diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 2c36661..0edd11f 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -41,6 +41,8 @@ (defn do-macroexpand-all [form] (riddley/macroexpand-all form)) +;;this is not a secure way to generate uuids – the `path` implementation +;;for cljs uses an alternative strategy #+cljs (defn gen-uuid-str [] (apply str (repeatedly 50 #(rand-int 9))) @@ -723,12 +725,8 @@ params-maker ; can be null ]) -(defonce PATH-CACHE - #+clj (java.util.concurrent.ConcurrentHashMap.) - ;;TODO: according to @dnolen, can forgo this for instead doing - ;;inline defs at runtime - #+cljs (atom {}) - ) +#+clj +(defonce PATH-CACHE (java.util.concurrent.ConcurrentHashMap.)) (def MUST-CACHE-PATHS (mutable-cell false)) @@ -744,14 +742,6 @@ (defn get-path-cache [^String k] (.get ^java.util.concurrent.ConcurrentHashMap PATH-CACHE k)) -#+cljs -(defn add-path-cache! [k v] - (swap! PATH-CACHE (fn [m] (assoc m k v)))) - -#+cljs -(defn get-path-cache [k] - (get @PATH-CACHE k)) - (defn- extract-original-code [p] (cond (instance? LocalSym p) (:sym p) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index 61ac9a6..ead6dd5 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -414,7 +414,12 @@ ))) ;; still possible to mess this up with alter-var-root -(defmacro path [& path] ; "inline cache" +(defmacro path + "Same as calling comp-paths, except it caches the composition of the static part + of the path for later re-use (when possible). For almost all idiomatic uses + of Specter provides huge speedup. This macro is automatically used by the + select/transform/setval/replace-in/etc. macros." + [& path] (let [;;this is a hack, but the composition of &env is considered stable for cljs platform (if (contains? &env :locals) :cljs :clj) local-syms (if (= platform :cljs) @@ -439,7 +444,18 @@ ;; - with invokedynamic here, could go directly to the code ;; to invoke and/or parameterize the precompiled path without ;; a bunch of checks beforehand - cache-id (i/gen-uuid-str) + cache-id (i/gen-uuid-str) ;; used for clj + cache-sym (gensym "pathcache") ;; used for cljs + info-sym (gensym "info") + + get-cache-code (if (= platform :clj) + `(i/get-path-cache ~cache-id) + cache-sym + ) + add-cache-code (if (= platform :clj) + `(i/add-path-cache! ~cache-id ~info-sym) + `(def ~cache-sym ~info-sym)) + precompiled-sym (gensym "precompiled") params-maker-sym (gensym "params-maker") @@ -453,20 +469,23 @@ ~(mapv (fn [p] `(fn [] ~p)) possible-params) )) ] - `(let [info# (i/get-path-cache ~cache-id) + `(let [;;TODO: for cljs, use the dynamic def strategy + exists for a direct + ;; inline cache + + info# ~get-cache-code ^com.rpl.specter.impl.CachedPathInfo info# (if (nil? info#) - (let [info# (i/magic-precompilation - ~prepared-path - ~(str *ns*) - (quote ~used-locals) - ;;possible-params is wrong atm - ;;as is used-locals in cljs... - (quote ~possible-params) - )] - (i/add-path-cache! ~cache-id info#) - info# + (let [~info-sym (i/magic-precompilation + ~prepared-path + ~(str *ns*) + (quote ~used-locals) + ;;possible-params is wrong atm + ;;as is used-locals in cljs... + (quote ~possible-params) + )] + ~add-cache-code + ~info-sym ) info# )