2024 Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面,了解更多此网站的详细信息。

+1
ClojureScript
命名空间在运行时加载的顺序与用户代码中指定命名空间要求顺序无关。这似乎影响所有优化级别。这意味着开发者不能依赖当依赖它的命名空间加载之前,某个命名空间中有副作用的代码被运行。

复现步骤

运行命令


clj --main cljs.main --compile-opts '{:target :nodejs :main a}' --compile a


将编译由={{a.cljs}}定义的{{a}}命名空间。该命名空间通过{{ns}}宏的{{:require}}形式按字母顺序要求命名空间{{b}}通过{{g}}。

编译后,请注意以下内容

在输出文件{{out/cljs_deps.js}}中,对{{a.js}}的{{goog.addDependency}}调用以任意顺序包括了其依赖项。例如,在我的机器上的一次运行结果显示


goog.addDependency("../a.js", ['a'], ['cljs.core', 'e', 'c', 'g', 'b', 'd', 'f']);


{{node out/main.js}}运行编译后的代码提供的控制台输出指示运行时加载顺序与{{out/cljs_deps.js}}中反映的顺序相同


{{e}}的内容

{{c}}的内容

{{g}}的内容

{{b}}的内容

{{d}}的内容

{{f}}的内容


分析

看起来编译时确保顺序的工作已经作为[CLJS-1453](https://dev.clojure.org/jira/browse/CLJS-1453)的一部分完成,但事情似乎在生成{{ cljs_deps.js }}时顺序被打乱。

看起来特定ClojureScript文件的{{ cljs_deps.js }}输出最终由{{ cljs.compiler/emit-source }}的输出决定。那里的代码以与CLJS-1453之前在{{ cljs.analyzer }}中的{{ 'ns }}解析方法相同的方式删除了相同的依赖项。

附带的补丁可以解决我狭义的使用案例的问题,但我认为底层函数将`:uses`和`:requires`作为映射传递,所以可能需要更多的工作才能找到完整的解决方案。

11 答案

0

由 chancerussell 发布的评论:

抱歉用 Markdown 代码片段弄乱了工单——原以为在提交后可以进行整理。

0

由 mfikes 发布的评论:

CLJS-3056.patch 通过了 CI 和 Canary 测试。

0

由 thheller 发布的评论:

附带的补丁仍然使用 {{deps}} 映射,这只有在达到数组映射阈值时才会保持插入顺序,一旦开始使用哈希映射,顺序就不再保证。在 shadow-cljs 中,我除了维护常用的 {{:requires}} 映射外,还维护了一个额外的 {{:deps}} 向量。

0
_由 chancerussell 发布的评论_

这可能最好——我不想尝试修改现有代码中的映射类型。

我今晚将看一下——好像 CLJS-1453 的补丁可能也有同样的问题。

话虽如此,我想知道当 ns 形式同时包含 :require 和 :use 时,排序的语义应该是什么?显然,之前的代码没有保证排序,但我想把它弄对。
0

由 thheller 发布的评论:

啊,看起来分析器解析 'ns 和 'ns** 已经维护了一个 {{:deps}} 向量。只需要在 cljs.compiler/emit-source 函数中使用该向量,而不是使用 requires/uses 映射的值。

0

由 chancerussell 发布的评论:

托马斯是对的,{{:deps}} 向量确实存在,并且处于预期的顺序,所以我提交了一个新补丁来使用它。

一个问题——当同时存在 {{cljs.core}} 和 {{cljs.core.constants}} 时,它们的顺序应该是什么?显然,之前的代码没有保证顺序,但我想把它弄对。

(test-cljs-1882-constants-table-is-sorted 测试似乎暗示 cljs.core 应该排在第一位。)

0

由 mfikes 发布的评论:

2019 年 3 月 24 日 12:58 PM 的 CLJS-3056.patch 通过 CI 和 Canary (/)

0

评论者:dnolen

{{cljs.core}}必须在{{cljs.core.constants}}之前,因为没有定义关键词结构,{{cljs.core.contants}}将无法工作。如果没有保证排序,我们就会听到很多关于失败的构建的投诉。

0

由 chancerussell 发布的评论:

David,当你说“排序肯定被保证”的时候,你是指{{cljs.core}}和{{cljs.core.constants}},还是整个依赖关系?当前的代码是{{conj}}ing到集合中,据我所知,这并不保证会维持任何排序。

0

由 mfikes 发布的评论:

哎呀,我搞错了:CLJS-3056.patch 失败于 Windows CI (x)

特别是,它导致在{{cljs-2077-test-loader}}中发生栈溢出

(cljs-2077-test-loader) 错误 (core.clj:6077) 397未在断言中捕获到异常。 398预期:nil 399实际:java.lang.StackOverflowError: null 400 at clojure.core$get_in.invokeStatic (core.clj:6077) 401 cljs.module_graph$deps_for.invokeStatic (module_graph.cljc:147) 402 cljs.module_graph$deps_for.invoke (module_graph.cljc:147) 403 clojure.lang.AFn.applyToHelper (AFn.java:160) 404 clojure.lang.AFn.applyTo (AFn.java:144) 405 clojure.core$apply.invokeStatic (core.clj:657) 406 clojure.core$memoize$fn__6665.doInvoke (core.clj:6288) 407 clojure.lang.RestFn.invoke (RestFn.java:436) 408 cljs.module_graph$deps_for$fn__7089.invoke (module_graph.cljc:151) 409 clojure.core$map$fn__5665.invoke (core.clj:2745) 410 clojure.lang.LazySeq.sval (LazySeq.java:40) 411 clojure.lang.LazySeq.seq (LazySeq.java:49) 412 clojure.lang.RT.seq (RT.java:528) 413 clojure.core$seq__5202.invokeStatic (core.clj:137) 414 clojure.core$apply.invokeStatic (core.clj:652) 415 clojure.core$mapcat.invokeStatic (core.clj:2775) 416 cljs.module_graph$deps_for.invokeStatic (module_graph.cljc:147) 417 cljs.module_graph$deps_for.invoke (module_graph.cljc:147) 418 clojure.lang.AFn.applyToHelper (AFn.java:160) 419 clojure.lang.AFn.applyTo (AFn.java:144) 420 clojure.core$apply.invokeStatic (core.clj:657)

CI 构建链接: https://ci.appveyor.com/project/mfikes/clojurescript/builds/24478525

0
参考:[https://clojure.atlassian.net/browse/CLJS-3056](https://clojure.atlassian.net/browse/CLJS-3056)(由 chancerussell 提出)
...