diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 79a8fe96d..5514c3425 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -181,13 +181,6 @@
     :jsdoc ["@type {*}"]}
   *loaded-libs* nil)
 
-(defn- pr-opts []
-  {:flush-on-newline *flush-on-newline*
-   :readably *print-readably*
-   :meta *print-meta*
-   :dup *print-dup*
-   :print-length *print-length*})
-
 (declare into-array)
 
 (defn enable-console-print!
@@ -896,6 +889,46 @@
 
 ;; Printing support
 
+(defn- pr-opts*
+  [flush-on-newline readably meta dup print-length]
+  (reify
+    ILookup
+    (-lookup [this k]
+      (-lookup this k nil))
+    (-lookup [this k not-found]
+      (case k
+        :flush-on-newline flush-on-newline
+        :readably readably
+        :meta meta
+        :dup dup
+        :print-length print-length
+        not-found))
+    IAssociative
+    (-contains-key? [coll k]
+      (case k
+        :flush-on-newline true
+        :readably true
+        :meta true
+        :dup true
+        :print-length true
+        false))
+    (-assoc [this k v]
+      (case k
+        :flush-on-newline (pr-opts* v readably meta dup print-length)
+        :readably (pr-opts* flush-on-newline v meta dup print-length)
+        :meta (pr-opts* flush-on-newline readably v dup print-length)
+        :dup (pr-opts* flush-on-newline readably meta v print-length)
+        :print-length (pr-opts* flush-on-newline readably meta dup v)
+        this))))
+
+(defn pr-opts []
+  (pr-opts*
+    *flush-on-newline*
+    *print-readably*
+    *print-meta*
+    *print-dup*
+    *print-length*))
+
 (deftype StringBufferWriter [sb]
   IWriter
   (-write [_ s] (.append sb s))
@@ -10442,18 +10475,18 @@ reduces them without incurring seq initialization"
       (do
         (-write writer begin)
         (if (zero? (:print-length opts))
-          (when (seq coll)
+          (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))]
+            (when-let [coll (-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))
+                  (print-one (-first coll) writer opts)
+                  (recur (-next coll) (dec n)))
+                (when (and (-seq coll) (zero? n))
                   (-write writer sep)
                   (-write writer (or (:more-marker opts) "...")))))))
         (-write writer end)))))
@@ -10462,6 +10495,13 @@ reduces them without incurring seq initialization"
   (doseq [s ss]
     (-write writer s)))
 
+(defn write-all-array [writer arr]
+  (let [len (alength arr)]
+    (loop [i 0]
+      (when (< i len)
+        (-write writer (aget arr i))
+        (recur (inc i))))))
+
 (defn string-print [x]
   (when (nil? *print-fn*)
     (throw (js/Error. "No *print-fn* fn set for evaluation environment")))
@@ -10483,10 +10523,9 @@ reduces them without incurring seq initialization"
 
 (defn ^:private quote-string
   [s]
-  (str \"
-       (.replace s (js/RegExp "[\\\\\"\b\f\n\r\t]" "g")
-         (fn [match] (unchecked-get char-escapes match)))
-       \"))
+  (let [s' ^string (.replace s (js/RegExp "[\\\\\"\b\f\n\r\t]" "g")
+                     (fn [match] (unchecked-get char-escapes match)))]
+    (str \" s' \")))
 
 (declare print-map)
 
@@ -10515,7 +10554,7 @@ reduces them without incurring seq initialization"
         (-pr-writer obj writer opts)
 
         (or (true? obj) (false? obj))
-        (-write writer (str obj))
+        (-write writer (.toString obj))
 
         (number? obj)
         (-write writer
@@ -10523,15 +10562,19 @@ 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 (.toString obj)))
 
         (object? obj)
         (do
           (-write writer "#js ")
           (print-map
-            (map (fn [k]
-                   (MapEntry. (cond-> k (some? (re-matches #"[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k) nil))
-              (js-keys obj))
+            (prim-seq
+              (.map
+                (fn [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))
 
         (array? obj)
@@ -10547,45 +10590,48 @@ reduces them without incurring seq initialization"
               name (if (or (nil? name) (gstring/isEmpty name))
                      "Function"
                      name)]
-          (write-all writer "#object[" name
-            (if *print-fn-bodies*
-              (str " \"" (str obj) "\"")
-              "")
-            "]"))
+          (write-all-array writer
+            (array
+              "#object[" name
+              (if *print-fn-bodies*
+                (str " \"" ^string (.toString obj) "\"")
+                "")
+              "]")))
 
         (instance? js/Date obj)
         (let [normalize (fn [n len]
-                          (loop [ns (str n)]
+                          (loop [ns (.toString n)]
                             (if (< (count ns) len)
-                              (recur (str "0" ns))
+                              (recur (str "0" ^string ns))
                               ns)))]
-          (write-all writer
-            "#inst \""
-            (normalize (.getUTCFullYear obj) 4)     "-"
-            (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) "\"")
-
-        (js-symbol? obj) (write-all writer "#object[" (.toString obj) "]" )
+          (write-all-array writer
+            (array
+              "#inst \""
+              (normalize (.getUTCFullYear obj) 4)     "-"
+              (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-array writer (array "#\"" (.-source obj) "\""))
+
+        (js-symbol? obj) (write-all-array writer (array "#object[" (.toString obj) "]"))
 
         :else
         (if (some-> obj .-constructor .-cljs$lang$ctorStr)
-          (write-all writer
-            "#object[" (.replace (.. obj -constructor -cljs$lang$ctorStr)
-                         (js/RegExp. "/" "g") ".") "]")
+          (write-all-array writer
+            (array "#object[" (.replace (.. obj -constructor -cljs$lang$ctorStr)
+                                (js/RegExp. "/" "g") ".") "]"))
           (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) "]"))))))))
+              (write-all-array writer (array "#object[" name "]"))
+              (write-all-array writer (array "#object[" name " " (.toString obj) "]")))))))))
 
 (defn- pr-writer
   "Prefer this to pr-seq, because it makes the printing function
@@ -10711,7 +10757,7 @@ reduces them without incurring seq initialization"
               (recur new-ns entries (assoc lm (strip-ns k) v)))))
         [ns lm]))))
 
-(defn print-prefix-map [prefix m print-one writer opts]
+(defn print-prefix-map [^string prefix m print-one writer opts]
   (pr-sequential-writer
     writer
     (fn [e w opts]
@@ -10719,7 +10765,7 @@ reduces them without incurring seq initialization"
           (-write w \space)
           (print-one (val e) w opts)))
     (str prefix "{") ", " "}"
-    opts (seq m)))
+    opts (-seq m)))
 
 (defn print-map [m print-one writer opts]
   (let [[ns lift-map] (when (map? m)
@@ -11795,6 +11841,15 @@ reduces them without incurring seq initialization"
 
 (set! (.. ExceptionInfo -prototype -__proto__) js/Error.prototype)
 
+(extend-protocol ISeqable
+  nil
+  (-seq [this] nil)
+
+  array
+  (-seq [this]
+    (when-not (zero? (alength this))
+      (IndexedSeq. this 0 nil))))
+
 (extend-type ExceptionInfo
   IPrintWithWriter
   (-pr-writer [obj writer opts]