请分享您的想法,参加2024 Clojure状态调查!

欢迎!有关如何使用此功能,请参阅关于页面获取更多信息。

+1投票
ClojureScript
运行时命名空间加载的顺序与用户代码中命名空间被require的顺序无关。这似乎影响了所有优化级别。这意味着开发者不能依赖于在特定命名空间中的副作用代码在依赖该副作用的另一个命名空间加载之前执行。

复现步骤

运行以下命令


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


将编译在{{a.cljs}}中定义的{{a}}命名空间。该命名空间通过在ns宏的{{:require}}形式中按字母顺序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}}中反映的顺序相同


body of e

body of c

body of g

body of b

body of d

body of 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的补丁可能也有相同的问题。

话虽如此,我 wonder什么是命名空间形式中同时包含:require和:use时的顺序语义?显然,以前代码中的顺序没有保证,但我希望把它搞定。
0 投票

评论者:thheller

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

0 投票

评论者:chancerussell

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

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

(测试 test-cljs-1882-constants-table-is-sorted 也许意味着 cljs.core 应该排在第一位。)

0 投票

评论者:mfikes

2019年3月24日下午12:58的 CLJS-3056.patch 版本通过了 CI 和 Canary 测试 (/)

0 投票

评论者:dnolen

{{cljs.core}} 必须在 {{cljs.core.constants}} 之前,因为如果没有定义关键字结构,{{cljs.core.constants}} 将不会工作。否则,我们会听到很多关于构建失败的高级构建的抱怨,因为顺序一定是保证了的。

0 投票

评论者:chancerussell

David,当你说“顺序一定是保证了的”,你是指 {{cljs.core}} 和 {{cljs.core.constants}},还是整个依赖项?当前代码是通过 {{conj}} 将其添加到集合中,据我所知,这并不保证保持任何顺序。

0 投票

评论者:mfikes

哎呀,我搞错了:CLJS-3056.patch 在 Windows CI 中失败(X)

特别是,它在 {{cljs-2077-test-loader}} 中引发堆栈溢出。

ERROR in (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(由 chancerussell 报告)
...