From 3ce7d0c8172dca364a742c4283dc3fcac85cbe5a Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 3 Jun 2012 17:08:21 -0700 Subject: [PATCH] Eliminates :meta, :set, :vector, and :map ops. These four operations can be defined more simply in terms of calls to with-meta, set, vector, and hash-map respectively. The compiler was optimizing construction of vectors and maps. Now, those optimizations are implemented as macros. Additionally, sets are optimized in much the same way. --- src/clj/cljs/compiler.clj | 98 +++----------------------------------------- src/clj/cljs/core.clj | 42 ++++++++++++++++--- src/cljs/cljs/core.cljs | 11 +++++- 3 files changed, 52 insertions(+), 99 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a7b90ae..7fff802 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -336,55 +336,6 @@ [{:keys [info env] :as arg}] (emit-wrap env (emits (munge (:name info))))) -(defmethod emit :meta - [{:keys [expr meta env]}] - (emit-wrap env - (emits "cljs.core.with_meta(" expr "," meta ")"))) - -(def ^:private array-map-threshold 16) -(def ^:private obj-map-threshold 32) - -(defmethod emit :map - [{:keys [env simple-keys? keys vals]}] - (emit-wrap env - (cond - (and simple-keys? (<= (count keys) obj-map-threshold)) - (emits "cljs.core.ObjMap.fromObject([" - (comma-sep keys) ; keys - "],{" - (comma-sep (map (fn [k v] - (with-out-str (emit k) (print ":") (emit v))) - keys vals)) ; js obj - "})") - - (<= (count keys) array-map-threshold) - (emits "cljs.core.PersistentArrayMap.fromArrays([" - (comma-sep keys) - "],[" - (comma-sep vals) - "])") - - :else - (emits "cljs.core.PersistentHashMap.fromArrays([" - (comma-sep keys) - "],[" - (comma-sep vals) - "])")))) - -(defmethod emit :vector - [{:keys [items env]}] - (emit-wrap env - (if (empty? items) - (emits "cljs.core.PersistentVector.EMPTY") - (emits "cljs.core.PersistentVector.fromArray([" - (comma-sep items) "], true)")))) - -(defmethod emit :set - [{:keys [items env]}] - (emit-wrap env - (emits "cljs.core.set([" - (comma-sep items) "])"))) - (defmethod emit :constant [{:keys [form env]}] (when-not (= :statement (:context env)) @@ -1457,42 +1408,6 @@ (parse-invoke env form)) (analyze env mform name)))))) -(declare analyze-wrap-meta) - -(defn analyze-map - [env form name] - (let [expr-env (assoc env :context :expr) - simple-keys? (every? #(or (string? %) (keyword? %)) - (keys form)) - ks (disallowing-recur (vec (map #(analyze expr-env % name) (keys form)))) - vs (disallowing-recur (vec (map #(analyze expr-env % name) (vals form))))] - (analyze-wrap-meta {:op :map :env env :form form - :keys ks :vals vs :simple-keys? simple-keys? - :children (vec (interleave ks vs))} - name))) - -(defn analyze-vector - [env form name] - (let [expr-env (assoc env :context :expr) - items (disallowing-recur (vec (map #(analyze expr-env % name) form)))] - (analyze-wrap-meta {:op :vector :env env :form form :items items :children items} name))) - -(defn analyze-set - [env form name] - (let [expr-env (assoc env :context :expr) - items (disallowing-recur (vec (map #(analyze expr-env % name) form)))] - (analyze-wrap-meta {:op :set :env env :form form :items items :children items} name))) - -(defn analyze-wrap-meta [expr name] - (let [form (:form expr)] - (if (meta form) - (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) (meta form) name)] - {:op :meta :env env :form form - :meta meta-expr :expr expr :children [meta-expr expr]}) - expr))) - (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 @@ -1506,12 +1421,13 @@ (or (seq form) ()) form)] (cond - (symbol? form) (analyze-symbol env form) - (and (seq? form) (seq form)) (analyze-seq env form name) - (map? form) (analyze-map env form name) - (vector? form) (analyze-vector env form name) - (set? form) (analyze-set env form name) - :else {:op :constant :env env :form form})))) + (symbol? form) (analyze-symbol env form) + (and (seq? form) (seq form)) (analyze-seq env form name) + (meta form) (analyze-seq env `(cljs.core/with-meta ~(with-meta form nil) ~(meta form)) name) + (map? form) (analyze-seq env `(cljs.core/hash-map ~@(mapcat seq form)) name) + (vector? form) (analyze-seq env `(cljs.core/vector ~@(seq form)) name) + (set? form) (analyze-seq env `(cljs.core/set* ~@(seq form)) name) + :else {:op :constant :env env :form form})))) (defn analyze-file [f] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 1220ce1..9d03f04 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -16,6 +16,7 @@ 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? nil? str get + vector hash-map set aget aset + - * / < <= > >= == zero? pos? neg? inc dec max min mod @@ -344,7 +345,7 @@ (core/str "WARNING: Symbol " % " is not a protocol"))) (cljs.compiler/warning &env (core/str "WARNING: Can't resolve protocol symbol " %))))) - skip-flag (set (-> tsym meta :skip-protocol-flag))] + skip-flag (core/set (-> tsym meta :skip-protocol-flag))] (if (base-type tsym) (let [t (base-type tsym) assign-impls (fn [[p sigs]] @@ -640,8 +641,8 @@ (let [names (take-nth 2 bindings) vals (take-nth 2 (drop 1 bindings)) tempnames (map (comp gensym name) names) - binds (map vector names vals) - resets (reverse (map vector names tempnames))] + binds (map core/vector names vals) + resets (reverse (map core/vector names tempnames))] (cljs.compiler/confirm-bindings &env names) `(let [~@(interleave tempnames names)] (try @@ -927,8 +928,8 @@ (when (seq (apply disj (apply hash-set (keys options)) valid-keys)) (throw (apply core/str "Only these options are valid: " - (first valid-keys) - (map #(core/str ", " %) (rest valid-keys)))))) + (first valid-keys) + (map #(core/str ", " %) (rest valid-keys)))))) (defmacro defmulti "Creates a new multimethod with the associated dispatch function. @@ -961,7 +962,7 @@ m)] (when (= (count options) 1) (throw "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")) - (let [options (apply hash-map options) + (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) @@ -1029,4 +1030,31 @@ (if (zero? ~'argc) (~'f) ~(gen-apply-to-helper)))) - (set! ~'*unchecked-if* false))) \ No newline at end of file + (set! ~'*unchecked-if* false))) + +(defmacro vector [& items] + (if (empty? items) + 'cljs.core.PersistentVector/EMPTY + `(cljs.core.PersistentVector/fromArray (cljs.core/array ~@items) true))) + +(def ^:private obj-map-threshold 32) +(def ^:private array-map-threshold 16) + +(defmacro hash-map [& keyvals] + (let [keys (take-nth 2 keyvals) + vals (take-nth 2 (rest keyvals)) + simple-keys? (every? #(or (string? %) (keyword? %)) keys)] + (cond + (core/= (count keys) 0) + `cljs.core.ObjMap/EMPTY + (and simple-keys? (core/<= (count keys) obj-map-threshold)) + `(cljs.core.ObjMap/fromObject (array ~@keys) (js-obj ~@keyvals)) + (core/<= (count keys) array-map-threshold) + `(cljs.core.PersistentArrayMap/fromArrays (array ~@keys) (array ~@vals)) + :else + `(cljs.core.PersistentHashMap/fromArrays (array ~@keys) (array ~@vals))))) + +(defmacro set* [& items] + (if (empty? items) + `cljs.core.PersistentHashSet/EMPTY + `(cljs.core.PersistentHashSet/fromArray (array ~@items)))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e963c6e..da6643a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3615,7 +3615,7 @@ reduces them without incurring seq initialization" IEditableCollection (-as-transient [coll] - (transient (into (hash-map) coll)))) + (transient (into cljs.core.PersistentHashMap/EMPTY coll)))) (set! cljs.core.ObjMap/EMPTY (ObjMap. nil (array) (js-obj) 0 0)) @@ -5571,6 +5571,15 @@ reduces them without incurring seq initialization" (set! cljs.core.PersistentHashSet/EMPTY (PersistentHashSet. nil (hash-map) 0)) +(set! cljs.core.PersistentHashSet/fromArray + (fn [items] + (let [len (count items)] + (loop [i 0 + out (transient cljs.core.PersistentHashSet/EMPTY)] + (if (< i len) + (recur (inc i) (conj! out (aget items i))) + (persistent! out)))))) + (deftype TransientHashSet [^:mutable transient-map] ITransientCollection (-conj! [tcoll o] -- 1.7.9.1