From 9e662c3ea5b9f536f303e9084415e988bf5d0878 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 27 Jul 2015 18:41:59 +0300 Subject: [PATCH] CLJS-1367: Better api call arguments This change adds additional arity for api calls which need compiler-env. Old arities continue to use the dynamic var. Also, api calls which take options map as argument, now use :warning-handlers options to set value for dynamic var cljs.analyzer/*cljs-warning-handlers*. --- src/main/clojure/cljs/analyzer/api.clj | 87 ++++++++++++-------- src/main/clojure/cljs/build/api.clj | 63 ++++++++------ src/main/clojure/cljs/compiler/api.clj | 52 +++++++----- src/test/clojure/cljs/analyzer_api_tests.clj | 28 +++++++ src/test/clojure/cljs/build_api_tests.clj | 4 +- 5 files changed, 153 insertions(+), 81 deletions(-) create mode 100644 src/test/clojure/cljs/analyzer_api_tests.clj diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 18cb8adcbd..3917c99c30 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -42,8 +42,9 @@ (defn get-options "Return the compiler options from compiler state." - [] - (get @env/*compiler* :options)) + ([] (get-options env/*compiler*)) + ([state] + (get @state :options))) (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context @@ -52,9 +53,13 @@ containing at least :form, :op and :env keys). If expr has any (immediately) nested exprs, must have :children [exprs...] entry. This will facilitate code walking without knowing the details of the op set." - ([env form] (ana/analyze env form nil)) - ([env form name] (ana/analyze env form name nil)) - ([env form name opts] (ana/analyze env form name opts))) + ([env form] (analyze env form nil)) + ([env form name] (analyze env form name nil)) + ([env form name opts] (analyze env/*compiler* env form name opts)) + ([state env form name opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze env form name opts))))) (defn forms-seq "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally @@ -72,9 +77,13 @@ be used for *analyze-deps* and *load-macros* bindings respectively. This function does _not_ side-effect the ambient compilation environment unless requested via opts where :restore is false." - ([src] (ana/parse-ns src nil nil)) - ([src opts] (ana/parse-ns src nil opts)) - ([src dest opts] (ana/parse-ns src dest opts))) + ([src] (parse-ns src nil nil)) + ([src opts] (parse-ns src nil opts)) + ([src dest opts] (parse-ns env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/parse-ns src dest opts))))) (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the @@ -85,8 +94,12 @@ compiler options, if :cache-analysis true will cache analysis to \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." - ([f] (ana/analyze-file f nil)) - ([f opts] (ana/analyze-file f opts))) + ([f] (analyze-file f nil)) + ([f opts] (analyze-file env/*compiler* f opts)) + ([state f opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze-file f opts))))) ;; ============================================================================= ;; Main API @@ -105,48 +118,54 @@ (defn all-ns "Return all namespaces. Analagous to clojure.core/all-ns but returns symbols identifying namespaces not Namespace instances." - [] - (keys (get @env/*compiler* ::ana/namespaces))) + ([] (all-ns env/*compiler*)) + ([state] + (keys (get @state ::ana/namespaces)))) (defn find-ns "Given a namespace return the corresponding namespace analysis map. Analagous to clojure.core/find-ns." - [sym] - {:pre [(symbol? sym)]} - (get-in @env/*compiler* [::ana/namespaces sym])) + ([sym] (find-ns env/*compiler* sym)) + ([state sym] + {:pre [(symbol? sym)]} + (get-in @state [::ana/namespaces sym]))) (defn ns-interns "Given a namespace return all the var analysis maps. Analagous to clojure.core/ns-interns but returns var analysis maps not vars." - [ns] - {:pre [(symbol? ns)]} - (merge - (get-in @env/*compiler* [::ana/namespaces ns :macros]) - (get-in @env/*compiler* [::ana/namespaces ns :defs]))) + ([ns] (ns-interns env/*compiler*)) + ([state ns] + {:pre [(symbol? ns)]} + (merge + (get-in @state [::ana/namespaces ns :macros]) + (get-in @state [::ana/namespaces ns :defs])))) (defn ns-publics "Given a namespace return all the public var analysis maps. Analagous to clojure.core/ns-publics but returns var analysis maps not vars." - [ns] - {:pre [(symbol? ns)]} - (->> (merge - (get-in @env/*compiler* [::ana/namespaces ns :macros]) - (get-in @env/*compiler* [::ana/namespaces ns :defs])) - (remove (fn [[k v]] (:private v))) - (into {}))) + ([ns] (ns-publics env/*compiler*)) + ([state ns] + {:pre [(symbol? ns)]} + (->> (merge + (get-in @state [::ana/namespaces ns :macros]) + (get-in @state [::ana/namespaces ns :defs])) + (remove (fn [[k v]] (:private v))) + (into {})))) (defn ns-resolve "Given a namespace and a symbol return the corresponding var analysis map. Analagous to clojure.core/ns-resolve but returns var analysis map not Var." - [ns sym] - {:pre [(symbol? ns) (symbol? sym)]} - (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) + ([ns sym] (ns-resolve env/*compiler* ns sym)) + ([state ns sym] + {:pre [(symbol? ns) (symbol? sym)]} + (get-in @state [::ana/namespaces ns :defs sym]))) (defn remove-ns "Removes the namespace named by the symbol." - [ns] - {:pre [(symbol? ns)]} - (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) + ([ns] (remove-ns env/*compiler* ns)) + ([state ns] + {:pre [(symbol? ns)]} + (swap! state update-in [::ana/namespaces] dissoc ns))) (defmacro in-cljs-user "Binds cljs.analyzer/*cljs-ns* to 'cljs.user and uses the given compilation @@ -154,4 +173,4 @@ [env & body] `(binding [cljs.analyzer/*cljs-ns* 'cljs.user] (cljs.env/with-compiler-env ~env - ~@body))) \ No newline at end of file + ~@body))) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 204067ebac..a31c37173d 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -56,23 +56,23 @@ ClojureScript namespaces that require and use the macros from 'example.macros : (cljs-dependents-for-macro-namespaces 'example.macros) -> - ('example.core 'example.util) - - This must be called when cljs.env/*compiler* is bound to the - compile env that you are inspecting. See cljs.env/with-compile-env." - [namespaces] - (map :name - (let [namespaces-set (set namespaces)] - (filter (fn [x] (not-empty - (intersection namespaces-set (-> x :require-macros vals set)))) - (vals (:cljs.analyzer/namespaces @env/*compiler*)))))) + ('example.core 'example.util)" + ([namespaces] (cljs-dependents-for-macro-namespaces env/*compiler* namespaces)) + ([state namespaces] + (map :name + (let [namespaces-set (set namespaces)] + (filter (fn [x] (not-empty + (intersection namespaces-set (-> x :require-macros vals set)))) + (vals (:cljs.analyzer/namespaces @state))))))) (defn cljs-ns-dependents "Given a namespace symbol return a seq of all dependent namespaces sorted in dependency order. Will include transient dependents." - [ns] - (ana/ns-dependents ns)) + ([ns] (cljs-ns-dependents env/*compiler* ns)) + ([state ns] + (env/with-compiler-env state + (ana/ns-dependents ns)))) (defn parse-js-ns "Given a Google Closure style JavaScript file or resource return the namespace @@ -84,15 +84,22 @@ (defn ^File src-file->target-file "Given a ClojureScript source file return the target file. May optionally provide build options with :output-dir specified." - ([src] (closure/src-file->target-file src)) - ([src opts] (closure/src-file->target-file src opts))) + ([src] (src-file->target-file src nil)) + ([src opts] (src-file->target-file env/*compiler* src opts)) + ([state src opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/src-file->target-file src opts))))) (defn ^String src-file->goog-require "Given a ClojureScript or Google Closure style JavaScript source file return the goog.require statement for it." - ([src] (closure/src-file->goog-require src)) - ([src options] - (closure/src-file->goog-require src options))) + ([src] (src-file->goog-require src nil)) + ([src options] (src-file->goog-require env/*compiler* src options)) + ([state src options] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers options ana/*cljs-warning-handlers*)] + (closure/src-file->goog-require src options))))) ;; ============================================================================= ;; Main API @@ -156,8 +163,10 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." - [opts compilable] - (closure/compile compilable opts)) + ([opts compilable] (compile env/*compiler* opts compilable)) + ([state opts compilable] + (env/with-compiler-env state + (closure/compile compilable opts)))) (defn output-unoptimized "Ensure that all JavaScript source files are on disk (not in jars), @@ -172,18 +181,22 @@ (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] - (closure/build source opts)) + (build source opts nil)) ([source opts compiler-env] - (closure/build source opts compiler-env))) + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/build source opts compiler-env)))) (defn watch "Given a source which can be compiled, watch it for changes to produce." ([source opts] - (closure/watch source opts)) + (watch source opts (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) ([source opts compiler-env] - (closure/watch source opts compiler-env)) + (watch source opts compiler-env nil)) ([source opts compiler-env stop] - (closure/watch source opts compiler-env stop))) + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/watch source opts compiler-env stop)))) (comment @@ -204,4 +217,4 @@ #(target-file-for-cljs-ns % "out-dev") (env/with-compiler-env test-cenv (ns-dependents 'clojure.string))) - ) \ No newline at end of file + ) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index c708c59470..019a59d16a 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -19,22 +19,30 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." - [ast] - (with-out-str - (comp/emit ast))) + ([ast] (emit env/*compiler* ast)) + ([state ast] + (env/with-compiler-env state + (with-out-str + (comp/emit ast))))) (defn with-core-cljs "Ensure that core.cljs has been loaded." ([] (comp/with-core-cljs nil)) - ([opts] (comp/with-core-cljs opts (fn []))) - ([opts body] (comp/with-core-cljs opts body))) + ([opts] (with-core-cljs opts (fn []))) + ([opts body] (with-core-cljs env/*compiler* opts body)) + ([state opts body] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/with-core-cljs opts body))))) (defn requires-compilation? "Return true if the src file requires compilation." - ([src dest] - (comp/requires-compilation? src dest nil)) - ([src dest opts] - (comp/requires-compilation? src dest opts))) + ([src dest] (requires-compilation? src dest nil)) + ([src dest opts] (requires-compilation? env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/requires-compilation? src dest opts))))) (defn compile-file "Compiles src to a file of the same name, but with a .js extension, @@ -49,12 +57,13 @@ Returns a map containing {:ns .. :provides .. :requires .. :file ..}. If the file was not compiled returns only {:file ...}" - ([src] - (comp/compile-file src)) - ([src dest] - (comp/compile-file src dest)) - ([src dest opts] - (comp/compile-file src dest opts))) + ([src] (compile-file src)) + ([src dest] (compile-file src dest)) + ([src dest opts] (compile-file env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/compile-file src dest opts))))) (defn cljs-files-in "Return a sequence of all .cljs and .cljc files in the given directory." @@ -67,9 +76,10 @@ directory mirroring the source directory structure. Returns a list of maps containing information about each file which was compiled in dependency order." - ([src-dir] - (comp/compile-root src-dir "out")) - ([src-dir target-dir] - (comp/compile-root src-dir target-dir nil)) - ([src-dir target-dir opts] - (comp/compile-root src-dir target-dir opts))) \ No newline at end of file + ([src-dir] (compile-root src-dir "out")) + ([src-dir target-dir] (compile-root src-dir target-dir nil)) + ([src-dir target-dir opts] (compile-root env/*compiler* src-dir target-dir opts)) + ([state src-dir target-dir opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (with-bindings (api-opts opts) (comp/compile-root src-dir target-dir opts)))))) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj new file mode 100644 index 0000000000..a4e71eae82 --- /dev/null +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -0,0 +1,28 @@ +(ns cljs.analyzer-api-tests + (:require [cljs.analyzer.api :as ana-api]) + (:use clojure.test)) + +(def warning-form + '(do (defn x [a b] (+ a b)) + (x 1 2 3 4))) + +(defn warning-handler [counter] + (fn [warning-type env extra] + (when (ana-api/warning-enabled? warning-type) + (swap! counter inc)))) + +(def test-cenv (atom {})) +(def test-env (ana-api/empty-env)) + +(deftest with-warning-handlers-test + (let [counter (atom 0)] + (ana-api/analyze test-cenv test-env warning-form nil + {:warning-handlers [(warning-handler counter)]}) + (is (= 1 @counter)))) + +(deftest vary-warning-handlers-test + (let [counter (atom 0)] + (cljs.analyzer/all-warn + (ana-api/analyze test-cenv test-env warning-form nil + {:warning-handlers [(warning-handler counter)]})) + (is (= 1 @counter)))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 17106cd9af..51d326c1d7 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -111,4 +111,6 @@ '(bar.core baz.core))) (is (= (env/with-compiler-env test-cenv (cljs-ns-dependents 'graph.foo.core)) - '(graph.bar.core graph.baz.core)))) \ No newline at end of file + '(graph.bar.core graph.baz.core))) + (is (= (cljs-ns-dependents test-cenv 'graph.foo.core) + '(graph.bar.core graph.baz.core))))