Skip to content

Commit adeaa9b

Browse files
r0manswannodette
authored andcommitted
Recompile cljs.loader in REPL
The cljs.loader namespace has to be compiled last, because all inputs need to be known to populate the `MODULE_INFOS` and `MODULE_URIS` vars. Loading a file or evaluating a form in the REPL could trigger another compilation of the cljs.loader namespace, without the information for the `MODULE_INFOS` and `MODULE_URIS` available. This compilation erased the previously populated `MODULE_INFOS` and `MODULE_URIS` vars and corrupted the module loader. This patch modifies the loader compilation pass to swap and merge the `MODULE_INFOS` and `MODULE_URIS` into the compiler state, to make this information available to compilation stages happening later. The patch also changes the REPL to recompile and load the cljs.loader namespace when a file gets loaded, or a namespace form gets evaluated. A new module graph will be calculated and merged into the previously calculated one. Recompilation of the cljs.loader namespace will only happen if the file or form require the cljs.loader namespace. Since the :aot-cache is populated at a point where the information for the `MODULE_INFOS` and `MODULE_URIS` is not yet available, this patch also makes sure the cljs.loader namespace is never cached.
1 parent fb312cd commit adeaa9b

File tree

3 files changed

+77
-50
lines changed

3 files changed

+77
-50
lines changed

src/main/clojure/cljs/closure.clj

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,10 @@
634634
(util/mkdirs target)
635635
(spit target (slurp f))
636636
(.setLastModified target (util/last-modified jar-file))))))))
637+
;; Files that don't require compilation (cljs.loader for example)
638+
;; need to be copied from JAR to disk.
639+
(when-not (.exists out-file)
640+
(jar-file-to-disk jar-file (util/output-directory opts) opts))
637641
;; have to call compile-file as it includes more IJavaScript
638642
;; information than ana/parse-ns for now
639643
(compile-file
@@ -1140,6 +1144,11 @@
11401144
:depends-on #{:core}}})
11411145
)
11421146

1147+
(defn- const-expr-form
1148+
"Returns the :const-expr form for `sym` from `compiler-state`."
1149+
[compiler-state sym]
1150+
(get-in compiler-state [::ana/namespaces (symbol (namespace sym)) :defs (symbol (name sym)) :const-expr :form]))
1151+
11431152
(defn compile-loader
11441153
"Special compilation pass for cljs.loader namespace. cljs.loader must be
11451154
compiled last after all inputs. This is because all inputs must be known and
@@ -1153,14 +1162,15 @@
11531162
first)]
11541163
(let [module-uris (module-graph/modules->module-uris modules inputs opts)
11551164
module-infos (module-graph/modules->module-infos modules)]
1156-
(env/with-compiler-env
1157-
(ana/add-consts @env/*compiler*
1158-
{'cljs.core/MODULE_URIS module-uris
1159-
'cljs.core/MODULE_INFOS module-infos})
1160-
(-compile (:source-file loader)
1161-
(merge opts
1162-
{:cache-key (util/content-sha (pr-str module-uris))
1163-
:output-file (comp/rename-to-js (util/ns->relpath (:ns loader)))})))))
1165+
(swap! env/*compiler* ana/add-consts
1166+
{'cljs.core/MODULE_INFOS
1167+
(merge (const-expr-form @env/*compiler* 'cljs.core/MODULE_INFOS) module-infos)
1168+
'cljs.core/MODULE_URIS
1169+
(merge (const-expr-form @env/*compiler* 'cljs.core/MODULE_URIS) module-uris)})
1170+
(-compile (:source-file loader)
1171+
(merge opts
1172+
{:cache-key (util/content-sha (pr-str module-uris))
1173+
:output-file (comp/rename-to-js (util/ns->relpath (:ns loader)))}))))
11641174
inputs)
11651175

11661176
(defn build-modules

src/main/clojure/cljs/compiler.cljc

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,22 +1480,24 @@
14801480
(:options @env/*compiler*))))
14811481
([^File src ^File dest opts]
14821482
(let [{:keys [ns requires]} (ana/parse-ns src)]
1483-
(ensure
1484-
(or (not (.exists dest))
1485-
(util/changed? src dest)
1486-
(let [version' (util/compiled-by-version dest)
1487-
version (util/clojurescript-version)]
1488-
(and version (not= version version')))
1489-
(and opts
1490-
(not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
1491-
(not= (ana/build-affecting-options opts)
1492-
(ana/build-affecting-options (util/build-options dest))))
1493-
(and opts (:source-map opts)
1494-
(if (= (:optimizations opts) :none)
1495-
(not (.exists (io/file (str (.getPath dest) ".map"))))
1496-
(not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
1497-
(when-let [recompiled' (and *recompiled* @*recompiled*)]
1498-
(some requires recompiled'))))))))
1483+
(if (and (= 'cljs.loader ns) (not (contains? opts :cache-key)))
1484+
false
1485+
(ensure
1486+
(or (not (.exists dest))
1487+
(util/changed? src dest)
1488+
(let [version' (util/compiled-by-version dest)
1489+
version (util/clojurescript-version)]
1490+
(and version (not= version version')))
1491+
(and opts
1492+
(not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
1493+
(not= (ana/build-affecting-options opts)
1494+
(ana/build-affecting-options (util/build-options dest))))
1495+
(and opts (:source-map opts)
1496+
(if (= (:optimizations opts) :none)
1497+
(not (.exists (io/file (str (.getPath dest) ".map"))))
1498+
(not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
1499+
(when-let [recompiled' (and *recompiled* @*recompiled*)]
1500+
(some requires recompiled')))))))))
14991501

15001502
#?(:clj
15011503
(defn compile-file

src/main/clojure/cljs/repl.cljc

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,34 @@
206206
(defn compilable? [input]
207207
(contains? input :source-file))
208208

209+
(defn- load-sources
210+
"Load the compiled `sources` into the REPL."
211+
[repl-env sources opts]
212+
(if (:output-dir opts)
213+
;; REPLs that read from :output-dir just need to add deps,
214+
;; environment will handle actual loading - David
215+
(let [sb (StringBuffer.)]
216+
(doseq [source sources]
217+
(with-open [rdr (io/reader (:url source))]
218+
(.append sb (cljsc/add-dep-string opts source))))
219+
(when (:repl-verbose opts)
220+
(println (.toString sb)))
221+
(-evaluate repl-env "<cljs repl>" 1 (.toString sb)))
222+
;; REPLs that stream must manually load each dep - David
223+
(doseq [{:keys [url provides]} sources]
224+
(-load repl-env provides url))))
225+
226+
(defn- load-cljs-loader
227+
"Compile and load the cljs.loader namespace if it's present in `sources`."
228+
[repl-env sources opts]
229+
(when-let [source (first (filter #(= (:ns %) 'cljs.loader) sources))]
230+
(cljsc/compile-loader sources opts)
231+
(load-sources repl-env [source] opts)))
232+
209233
(defn load-namespace
210234
"Load a namespace and all of its dependencies into the evaluation environment.
211-
The environment is responsible for ensuring that each namespace is loaded once and
212-
only once."
235+
The environment is responsible for ensuring that each namespace is
236+
loaded once and only once. Returns the compiled sources."
213237
([repl-env ns] (load-namespace repl-env ns nil))
214238
([repl-env ns opts]
215239
(let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
@@ -221,29 +245,18 @@
221245
(merge (env->opts repl-env) opts))
222246
(remove (comp #{["goog"]} :provides)))
223247
(map #(cljsc/source-on-disk opts %)
224-
(cljsc/add-js-sources [input] opts))))))]
248+
(cljsc/add-js-sources [input] opts))))))]
225249
(when (:repl-verbose opts)
226250
(println (str "load-namespace " ns " , compiled:") (map :provides sources)))
227-
(if (:output-dir opts)
228-
;; REPLs that read from :output-dir just need to add deps,
229-
;; environment will handle actual loading - David
230-
(let [sb (StringBuffer.)]
231-
(doseq [source sources]
232-
(with-open [rdr (io/reader (:url source))]
233-
(.append sb
234-
(cljsc/add-dep-string opts source))))
235-
(when (:repl-verbose opts)
236-
(println (.toString sb)))
237-
(-evaluate repl-env "<cljs repl>" 1 (.toString sb)))
238-
;; REPLs that stream must manually load each dep - David
239-
(doseq [{:keys [url provides]} sources]
240-
(-load repl-env provides url))))))
251+
(load-sources repl-env sources opts)
252+
sources)))
241253

242254
(defn- load-dependencies
243-
([repl-env requires] (load-dependencies repl-env requires nil))
255+
"Compile and load the given `requires` and return the compiled sources."
256+
([repl-env requires]
257+
(load-dependencies repl-env requires nil))
244258
([repl-env requires opts]
245-
(doseq [ns (distinct requires)]
246-
(load-namespace repl-env ns opts))))
259+
(doall (mapcat #(load-namespace repl-env % opts) (distinct requires)))))
247260

248261
(defn ^File js-src->cljs-src
249262
"Map a JavaScript output file back to the original ClojureScript source
@@ -544,11 +557,12 @@
544557
(ana/no-warn (ana/analyze env form nil opts))
545558
(catch #?(:clj Exception :cljs js/Error) e
546559
(reset! env/*compiler* backup-comp)
547-
(throw e)))]
548-
(load-dependencies repl-env
549-
(into (vals (:requires ast))
550-
(distinct (vals (:uses ast))))
551-
opts)))
560+
(throw e)))
561+
sources (load-dependencies repl-env
562+
(into (vals (:requires ast))
563+
(distinct (vals (:uses ast))))
564+
opts)]
565+
(load-cljs-loader repl-env sources opts)))
552566
(when *cljs-verbose*
553567
(err-out (println wrap-js)))
554568
(let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)]
@@ -596,7 +610,8 @@
596610
(util/ns->relpath ns (util/ext (:source-url compiled))))
597611
(slurp src)))
598612
;; need to load dependencies first
599-
(load-dependencies repl-env (:requires compiled) opts)
613+
(let [sources (load-dependencies repl-env (:requires compiled) opts)]
614+
(load-cljs-loader repl-env (conj sources compiled) opts))
600615
(-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled))
601616
(-evaluate repl-env f 1
602617
(cljsc/src-file->goog-require src

0 commit comments

Comments
 (0)