|
12 | 12 | #?(:cljs (:require-macros
|
13 | 13 | [cljs.analyzer.macros
|
14 | 14 | :refer [no-warn wrapping-errors
|
15 |
| - disallowing-recur allowing-redef]] |
| 15 | + disallowing-recur allowing-redef disallowing-ns*]] |
16 | 16 | [cljs.env.macros :refer [ensure]]))
|
17 | 17 | #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]]
|
18 | 18 | [clojure.java.io :as io]
|
|
36 | 36 | [java.util.regex Pattern]
|
37 | 37 | [java.net URL]
|
38 | 38 | [java.lang Throwable]
|
| 39 | + [java.security MessageDigest] |
| 40 | + [javax.xml.bind DatatypeConverter] |
39 | 41 | [clojure.lang Namespace Var LazySeq ArityException]
|
40 | 42 | [cljs.tagged_literals JSValue])))
|
41 | 43 |
|
|
878 | 880 | (declare analyze analyze-symbol analyze-seq)
|
879 | 881 |
|
880 | 882 | (def specials '#{if def fn* do let* loop* letfn* throw try recur new set!
|
881 |
| - ns deftype* defrecord* . js* & quote case* var}) |
| 883 | + ns deftype* defrecord* . js* & quote case* var ns*}) |
882 | 884 |
|
883 | 885 | (def ^:dynamic *recur-frames* nil)
|
884 | 886 | (def ^:dynamic *loop-lets* ())
|
885 | 887 | (def ^:dynamic *allow-redef* false)
|
| 888 | +(def ^:dynamic *allow-ns* true) |
886 | 889 |
|
887 | 890 | #?(:clj
|
888 | 891 | (defmacro disallowing-recur [& body]
|
|
892 | 895 | (defmacro allowing-redef [& body]
|
893 | 896 | `(binding [*allow-redef* true] ~@body)))
|
894 | 897 |
|
| 898 | +#?(:clj |
| 899 | + (defmacro disallowing-ns* [& body] |
| 900 | + `(binding [*allow-ns* false] ~@body))) |
| 901 | + |
895 | 902 | ;; TODO: move this logic out - David
|
896 | 903 | (defn analyze-keyword
|
897 | 904 | [env sym]
|
|
1213 | 1220 | (when dynamic {:dynamic true})
|
1214 | 1221 | (source-info var-name env)))
|
1215 | 1222 | (disallowing-recur
|
1216 |
| - (analyze (assoc env :context :expr) (:init args) sym))) |
| 1223 | + (disallowing-ns* |
| 1224 | + (analyze (assoc env :context :expr) (:init args) sym)))) |
1217 | 1225 | fn-var? (and init-expr (= (:op init-expr) :fn))
|
1218 | 1226 | tag (if fn-var?
|
1219 | 1227 | (or (:ret-tag init-expr) tag)
|
|
1404 | 1412 | menv (merge menv
|
1405 | 1413 | {:protocol-impl proto-impl
|
1406 | 1414 | :protocol-inline proto-inline})
|
1407 |
| - methods (map #(analyze-fn-method menv locals % type) meths) |
| 1415 | + methods (map #(disallowing-ns* (analyze-fn-method menv locals % type)) meths) |
1408 | 1416 | mfa (apply max (map :max-fixed-arity methods))
|
1409 | 1417 | variadic (boolean (some :variadic methods))
|
1410 | 1418 | locals (if-not (nil? name)
|
|
1418 | 1426 | methods (if-not (nil? name)
|
1419 | 1427 | ;; a second pass with knowledge of our function-ness/arity
|
1420 | 1428 | ;; lets us optimize self calls
|
1421 |
| - (analyze-fn-methods-pass2 menv locals type meths) |
| 1429 | + (disallowing-ns* (analyze-fn-methods-pass2 menv locals type meths)) |
1422 | 1430 | methods)
|
1423 | 1431 | form (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type)
|
1424 | 1432 | js-doc (when (true? variadic)
|
|
2076 | 2084 | (util/ns->source sym')))))))
|
2077 | 2085 |
|
2078 | 2086 | #?(:clj
|
2079 |
| - (defn rewrite-cljs-aliases |
2080 |
| - "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if |
2081 |
| - possible." |
2082 |
| - [args] |
| 2087 | + (defn process-rewrite-form [[k & specs :as form]] |
2083 | 2088 | (letfn [(process-spec [maybe-spec]
|
2084 | 2089 | (let [[lib & xs] (if (sequential? maybe-spec)
|
2085 | 2090 | maybe-spec
|
|
2088 | 2093 | (let [lib' (clj-ns->cljs-ns lib)
|
2089 | 2094 | spec (cons lib' xs)]
|
2090 | 2095 | (into (if xs [spec] []) [(list lib' :as lib)]))
|
2091 |
| - [maybe-spec]))) |
2092 |
| - (process-form [[k & specs :as form]] |
2093 |
| - (if (#{:use :require} k) |
2094 |
| - (cons k (mapcat process-spec specs)) |
2095 |
| - form))] |
2096 |
| - (map process-form args)))) |
| 2096 | + [maybe-spec])))] |
| 2097 | + (if (#{:use :require} k) |
| 2098 | + (cons k (mapcat process-spec specs)) |
| 2099 | + form)))) |
| 2100 | + |
| 2101 | +#?(:clj |
| 2102 | + (defn rewrite-cljs-aliases |
| 2103 | + "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if |
| 2104 | + possible." |
| 2105 | + [args] |
| 2106 | + (map process-rewrite-form args))) |
| 2107 | + |
| 2108 | +(defn canonicalize-specs [specs] |
| 2109 | + (letfn [(canonicalize [quoted-spec-or-kw] |
| 2110 | + (if (keyword? quoted-spec-or-kw) |
| 2111 | + quoted-spec-or-kw |
| 2112 | + (as-> (second quoted-spec-or-kw) spec |
| 2113 | + (if (vector? spec) spec [spec]))))] |
| 2114 | + (map canonicalize specs))) |
2097 | 2115 |
|
2098 | 2116 | (defn desugar-ns-specs
|
2099 | 2117 | "Given an original set of ns specs desugar :include-macros and :refer-macros
|
|
2166 | 2184 |
|
2167 | 2185 | (defmethod parse 'ns
|
2168 | 2186 | [_ env [_ name & args :as form] _ opts]
|
| 2187 | + (when-not *allow-ns* |
| 2188 | + (throw (error env "Namespace declarations must be at the top-level."))) |
2169 | 2189 | (when-not (symbol? name)
|
2170 | 2190 | (throw (error env "Namespaces must be named by a symbol.")))
|
2171 | 2191 | (let [name (cond-> name (:macros-ns opts) macro-ns-name)]
|
|
2285 | 2305 | (update-in [:requires]
|
2286 | 2306 | (fn [m] (with-meta m {(@reload :require) true})))))))))
|
2287 | 2307 |
|
| 2308 | +(defmethod parse 'ns* |
| 2309 | + [_ env [_ quoted-specs :as form] _ opts] |
| 2310 | + (when-let [not-quoted (->> (remove keyword? quoted-specs) |
| 2311 | + (filter #(not= 'quote (first %)) ) |
| 2312 | + first)] |
| 2313 | + (throw (error env (str "Arguments to " (name (first quoted-specs)) |
| 2314 | + " must be quoted. Offending spec: " not-quoted)))) |
| 2315 | + (when-not *allow-ns* |
| 2316 | + (throw (error env "Namespace declarations must be at the top-level."))) |
| 2317 | + (let [specs (canonicalize-specs quoted-specs) |
| 2318 | + name 'cljs.user |
| 2319 | + args (desugar-ns-specs |
| 2320 | + #?(:clj (list (process-rewrite-form |
| 2321 | + specs)) |
| 2322 | + :cljs (list specs))) |
| 2323 | + {excludes :excludes core-renames :renames} (parse-ns-excludes env args) |
| 2324 | + core-renames (reduce (fn [m [original renamed]] |
| 2325 | + (assoc m renamed (symbol "cljs.core" (str original)))) |
| 2326 | + {} core-renames) |
| 2327 | + deps (atom #{}) |
| 2328 | + aliases (atom {:fns {} :macros {}}) |
| 2329 | + spec-parsers {:require (partial parse-require-spec env false deps aliases) |
| 2330 | + :require-macros (partial parse-require-spec env true deps aliases) |
| 2331 | + :use (comp (partial parse-require-spec env false deps aliases) |
| 2332 | + (partial use->require env)) |
| 2333 | + :use-macros (comp (partial parse-require-spec env true deps aliases) |
| 2334 | + (partial use->require env)) |
| 2335 | + :import (partial parse-import-spec env deps)} |
| 2336 | + reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) |
| 2337 | + reloads (atom {}) |
| 2338 | + {uses :use requires :require renames :rename |
| 2339 | + use-macros :use-macros require-macros :require-macros |
| 2340 | + rename-macros :rename-macros imports :import :as params} |
| 2341 | + (reduce |
| 2342 | + (fn [m [k & libs]] |
| 2343 | + ;; check for spec type reloads |
| 2344 | + (when-not (= :import k) |
| 2345 | + (when (some #{:reload} libs) |
| 2346 | + (swap! reload assoc k :reload)) |
| 2347 | + (when (some #{:reload-all} libs) |
| 2348 | + (swap! reload assoc k :reload-all))) |
| 2349 | + ;; check for individual ns reloads from REPL interactions |
| 2350 | + (when-let [xs (seq (filter #(-> % meta :reload) libs))] |
| 2351 | + (swap! reloads assoc k |
| 2352 | + (zipmap (map first xs) (map #(-> % meta :reload) xs)))) |
| 2353 | + (apply merge-with merge m |
| 2354 | + (map (spec-parsers k) |
| 2355 | + (remove #{:reload :reload-all} libs)))) |
| 2356 | + {} (remove (fn [[r]] (= r :refer-clojure)) args))] |
| 2357 | + (set! *cljs-ns* 'cljs.user) |
| 2358 | + (let [require-info |
| 2359 | + {:name name |
| 2360 | + :excludes excludes |
| 2361 | + :use-macros use-macros |
| 2362 | + :require-macros require-macros |
| 2363 | + :rename-macros rename-macros |
| 2364 | + :uses uses |
| 2365 | + :requires requires |
| 2366 | + :renames (merge renames core-renames) |
| 2367 | + :imports imports} |
| 2368 | + ns-info |
| 2369 | + (let [ns-info' (get-in @env/*compiler* [::namespaces name])] |
| 2370 | + (if (pos? (count ns-info')) |
| 2371 | + (let [merge-keys |
| 2372 | + [:use-macros :require-macros :rename-macros |
| 2373 | + :uses :requires :renames :imports]] |
| 2374 | + (merge |
| 2375 | + ns-info' |
| 2376 | + (merge-with merge |
| 2377 | + (select-keys ns-info' merge-keys) |
| 2378 | + (select-keys require-info merge-keys)))) |
| 2379 | + require-info))] |
| 2380 | + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) |
| 2381 | + (merge {:op :ns* |
| 2382 | + :env env |
| 2383 | + :form form |
| 2384 | + :deps @deps |
| 2385 | + :reload @reload |
| 2386 | + :reloads @reloads} |
| 2387 | + (cond-> require-info |
| 2388 | + (@reload :use) |
| 2389 | + (update-in [:uses] |
| 2390 | + (fn [m] (with-meta m {(@reload :use) true}))) |
| 2391 | + (@reload :require) |
| 2392 | + (update-in [:requires] |
| 2393 | + (fn [m] (with-meta m {(@reload :require) true})))))))) |
| 2394 | + |
2288 | 2395 | (defn parse-type
|
2289 | 2396 | [op env [_ tsym fields pmasks body :as form]]
|
2290 | 2397 | (let [t (:name (resolve-var (dissoc env :locals) tsym))
|
|
2853 | 2960 | #?(:clj
|
2854 | 2961 | (defn ns-side-effects
|
2855 | 2962 | [env {:keys [op] :as ast} opts]
|
2856 |
| - (if (= :ns op) |
| 2963 | + (if (#{:ns :ns*} op) |
2857 | 2964 | (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast]
|
2858 | 2965 | (when (and *analyze-deps* (seq deps))
|
2859 | 2966 | (analyze-deps name deps env (dissoc opts :macros-ns)))
|
|
3035 | 3142 | [(forms-seq*) rdr]
|
3036 | 3143 | (forms-seq*))))))
|
3037 | 3144 |
|
| 3145 | +#?(:clj |
| 3146 | + (defn gen-user-ns [src] |
| 3147 | + (let [name (str src) |
| 3148 | + name (.substring name (inc (.lastIndexOf name "/")) (.lastIndexOf name ".")) |
| 3149 | + digest (MessageDigest/getInstance "SHA-1")] |
| 3150 | + (.reset digest) |
| 3151 | + (.update digest (.getBytes ^String name "utf8")) |
| 3152 | + (symbol |
| 3153 | + (str |
| 3154 | + "cljs.user$$gen_ns$$_" name |
| 3155 | + (->> (DatatypeConverter/printHexBinary (.digest digest)) |
| 3156 | + (take 7) |
| 3157 | + (apply str))))))) |
| 3158 | + |
3038 | 3159 | #?(:clj
|
3039 | 3160 | (defn parse-ns
|
3040 | 3161 | "Helper for parsing only the essential namespace information from a
|
|
3075 | 3196 | (try
|
3076 | 3197 | (loop [forms (if rdr
|
3077 | 3198 | (forms-seq* rdr (source-path src))
|
3078 |
| - src)] |
| 3199 | + src) |
| 3200 | + ret (merge |
| 3201 | + {:ns (gen-user-ns src) |
| 3202 | + :provides [(gen-user-ns src)] |
| 3203 | + :file dest |
| 3204 | + :source-file (when rdr src) |
| 3205 | + :source-forms (when-not rdr src) |
| 3206 | + :macros-ns (:macros-ns opts) |
| 3207 | + :requires (cond-> #{'cljs.core} |
| 3208 | + (get-in @env/*compiler* [:options :emit-constants]) |
| 3209 | + (conj 'constants-table))} |
| 3210 | + (when (and dest (.exists ^File dest)) |
| 3211 | + {:lines (with-open [reader (io/reader dest)] |
| 3212 | + (-> reader line-seq count))}))] |
3079 | 3213 | (if (seq forms)
|
3080 | 3214 | (let [env (empty-env)
|
3081 | 3215 | ast (no-warn (analyze env (first forms) nil opts))]
|
3082 |
| - (if (= :ns (:op ast)) |
| 3216 | + (cond |
| 3217 | + (= :ns (:op ast)) |
3083 | 3218 | (let [ns-name (:name ast)
|
3084 | 3219 | ns-name (if (and (= 'cljs.core ns-name)
|
3085 | 3220 | (= "cljc" (util/ext src)))
|
|
3103 | 3238 | (when (and dest (.exists ^File dest))
|
3104 | 3239 | {:lines (with-open [reader (io/reader dest)]
|
3105 | 3240 | (-> reader line-seq count))})))
|
3106 |
| - (recur (rest forms)))) |
3107 |
| - (throw (AssertionError. (str "No ns form found in " src))))) |
| 3241 | + |
| 3242 | + (= :ns* (:op ast)) |
| 3243 | + (let [deps (merge (:uses ast) (:requires ast))] |
| 3244 | + (recur (rest forms) |
| 3245 | + (update-in ret [:requires] into (set (vals deps))))) |
| 3246 | + |
| 3247 | + :else ret)) |
| 3248 | + ret)) |
3108 | 3249 | (finally
|
3109 | 3250 | (when rdr
|
3110 | 3251 | (.close ^Reader rdr))))))]
|
|
3225 | 3366 | (let [form (first forms)
|
3226 | 3367 | env (assoc env :ns (get-namespace *cljs-ns*))
|
3227 | 3368 | ast (analyze env form nil opts)]
|
3228 |
| - (if (= (:op ast) :ns) |
| 3369 | + (cond |
| 3370 | + (= (:op ast) :ns) |
3229 | 3371 | (recur (:name ast) (next forms))
|
| 3372 | + |
| 3373 | + (and (nil? ns) (= (:op ast) :ns*)) |
| 3374 | + (recur (gen-user-ns res) (next forms)) |
| 3375 | + |
| 3376 | + :else |
3230 | 3377 | (recur ns (next forms))))
|
3231 | 3378 | ns)))]
|
3232 | 3379 | (when (and cache (true? (:cache-analysis opts)))
|
|
0 commit comments