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

欢迎!请参阅关于页面以了解更多关于此如何运作的信息。

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

重现步骤

运行命令:


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


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

抱歉让票据变得杂乱无章—原本认为提交后能够整理干净。

0

评论由: mfikes

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

0

评论由: thheller

附带的补丁仍然使用了{{deps}}映射,这只能保持插入顺序,直到达到数组映射阈值,一旦开始使用哈希映射,条目将不再排序。在shadow-cljs中,我通过维护一个附加的{{:deps}}向量来处理这个问题,除了常规的{{:requires}}映射。

0
_评论由: chancerussell_

这可能是最好的解决方案—I没有想要尝试修改现有代码中映射的类型。

我将在今晚查看这个问题—看起来CLJS-1453的补丁可能也存在同样的问题。

话虽如此,我想知道当ns形式既包含:require又包含:use时,排序的语义应该是什么?
0

评论由: thheller

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

0

评论由: chancerussell

托马斯是对的,依赖向量 {{:deps}} 存在且顺序正确,因此我已经提交了一个使用它的新补丁。

有一个问题——当两者同时存在时,{{cljs.core}} 和 {{cljs.core.constants}} 的顺序应该是什么?显然,之前代码的顺序没有得到保证,但我希望这次做得正确。

(测试 test-cljs-1882-constants-table-is-sorted 似乎表明 cljs.core 应该排在前面。)

0
by

评论由: mfikes

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

0
by

评论者:dnolen

{{cljs.core}} 必须在 {{cljs.core.constants}} 之前,因为没有定义关键字构造函数,{{cljs.core.contants}} 将无法工作。如果没有保证顺序,我们肯定会听到很多关于高级构建失败的大量投诉。

0
by

评论由: chancerussell

大卫,当你说“顺序绝对有保证”时,你是指仅仅 {{cljs.core}} 和 {{cljs.core.constants}},还是整个依赖关系?当前的代码是将元素添加到集合中,但这并不保证会维持任何顺序,据我所知。

0
by

评论由: mfikes

哦,我弄错了:CLJS-3056.patch 在 Windows CI 上失败 (x)

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

ERROR 在 (cljs-2077-test-loader) (core.clj:6077) 397未捕获的异常,不是在断言中。 398期望:nil 399实际:java.lang.StackOverflowError: null 400在 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 报告)
...