diff --git a/project.clj b/project.clj index ef036a1..d59c75e 100644 --- a/project.clj +++ b/project.clj @@ -9,8 +9,8 @@ :auto-clean false :dependencies [[riddley "0.1.12"]] :profiles {:provided {:dependencies - [[org.clojure/clojure "1.6.0"] - [org.clojure/clojurescript "0.0-3211"]]} + [[org.clojure/clojure "1.7.0"] + [org.clojure/clojurescript "1.7.10"]]} :dev {:dependencies [[org.clojure/test.check "0.7.0"]] :plugins diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index f7cdba3..f178834 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -374,7 +374,7 @@ The input path may be parameterized, in which case the result of transformed will be parameterized in the order of which the parameterized navigators were declared." - [path update-fn] + [path ^:notpath update-fn] (fixed-pathed-nav [late path] (select* [this structure next-fn] (next-fn (compiled-transform late update-fn structure))) diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 21864ae..da194a9 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -857,6 +857,26 @@ 1 )) +(defn- variadic-arglist? [al] + (contains? (set al) '&)) + +(defn- arglist-for-params-count [arglists c code] + (let [ret (->> arglists + (filter + (fn [al] + (or (= (count al) c) + (variadic-arglist? al)) + )) + first) + len (count ret)] + (when-not ret + (throw-illegal "Invalid # arguments at " code)) + (if (variadic-arglist? ret) + (srange-transform ret (- len 2) len + (fn [_] (repeatedly (- c (- len 2)) gensym))) + ret + ))) + (defn- magic-precompilation* [p params-atom failed-atom] (let [magic-fail! (fn [& reason] (if (get-cell MUST-CACHE-PATHS) @@ -908,9 +928,37 @@ vv ) - (and (fn? vv) (-> vv meta :pathedfn)) - (let [subpath (mapv #(magic-precompilation* % params-atom failed-atom) - ps)] + (and (fn? vv) (-> v meta :pathedfn)) + ;;TODO: update this to ignore args that aren't symbols or have :nopath + ;;metadata on them (in the arglist) + (let [arglists (-> v meta :arglists) + al (arglist-for-params-count arglists (count ps) (:code p)) + subpath (vec + (map + (fn [pdecl p] + (if (and (symbol? pdecl) + (-> pdecl meta :notpath not)) + (magic-precompilation* p params-atom failed-atom) + + (cond (and (instance? VarUse p) + (-> p :var meta :dynamic not)) + (:val p) + + (and (not (instance? LocalSym p)) + (not (instance? VarUse p)) + (not (instance? SpecialFormUse p)) + (not (instance? FnInvocation p)) + (not (coll? p))) + p + + :else + (magic-fail! "Could not factor static param " + "of pathedfn because it's not a static var " + " or non-collection value: " + (extract-original-code p)) + ))) + al + ps))] (if @failed-atom nil (apply vv subpath) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index 15301de..6abcd7b 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -339,8 +339,9 @@ [(with-meta name attr) macro-args])) (defmacro defpathedfn [name & args] - (let [[name args] (name-with-attributes name args)] - `(def ~name (vary-meta (fn ~@args) assoc :pathedfn true)))) + (let [[name args] (name-with-attributes name args) + name (vary-meta name assoc :pathedfn true)] + `(defn ~name ~@args))) (defmacro defnavconstructor [name & args] (let [[name [[csym anav] & body-or-bodies]] (name-with-attributes name args) diff --git a/test/com/rpl/specter/core_test.cljx b/test/com/rpl/specter/core_test.cljx index 054f723..1a23b59 100644 --- a/test/com/rpl/specter/core_test.cljx +++ b/test/com/rpl/specter/core_test.cljx @@ -936,6 +936,14 @@ [{:a 1 :b 2} {:a 10 :b 6} {:c 7 :b 8} {:c 1 :d 9} {:c 3 :d -1}] [[:a :b] [:b :a] [:c :d] [:b :c]] ) + (ic-test + true + [] + [(s/transformed s/STAY inc)] + inc + 10 + [] + ) (s/must-cache-paths!) (is (thrown? #+clj Exception #+cljs js/Error @@ -947,6 +955,11 @@ (is (thrown? #+clj Exception #+cljs js/Error (select [:a (identity even?)] {:a 2}) )) + ;; this tests a bug that existed before ^:staticparam annotation + ;; for pathedfns + (is (thrown? #+clj Exception #+cljs js/Error + (select [(s/putval 10) (s/transformed s/STAY #(inc %))] 10) + )) (let [p :a] (is (thrown? #+clj Exception #+cljs js/Error (select [p even?] {:a 2}) @@ -991,3 +1004,10 @@ [1 "a" "b" 2 3 "c" 4 5 6 "d" "e" "f"] ))) ) + +;; there was a bug where the transform-fn was being factored by inline caching +;; this verifies that it doesn't do inline caching +(deftest transformed-inline-caching + (dotimes [i 10] + (is (= [(inc i)] (select (s/transformed s/STAY #(+ % i)) 1))) + ))