请分享您的想法,参与2024年Clojure调查!

欢迎!请参阅关于页面以获取更多关于此如何工作的信息。

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

复现步骤

运行以下命令


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


将会编译在{{a.cljs}}中定义的{{a}}命名空间。该命名空间通过ns宏的{{:require}}形式按字母顺序要求请注明——{{b}}至{{g}}命名空间。

编译后,观察以下情况

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


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中,我通过维护除了常用的{{:requires}}映射之外的一个额外的{{:deps}}向量来处理这个问题。

0
_评论者:chancerussell_

这可能是一个最好的解决方案——我不想尝试修改现有代码中映射的类型。

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

此外,我 wonder当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

评论者:mfikes

19/03/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}}集合,这并不保证保持任何顺序,据我所知。

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行在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报告)
...