Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/cljs/cljs/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 21 additions & 15 deletions src/main/clojure/cljs/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 8 additions & 0 deletions src/test/cljs/cljs/core_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))))
9 changes: 9 additions & 0 deletions src/test/cljs_build/cljs_3452_str_optimizations/core.cljs
Original file line number Diff line number Diff line change
@@ -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))
18 changes: 18 additions & 0 deletions src/test/clojure/cljs/build_api_tests.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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))))