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

欢迎!请参阅关于页面以了解更多关于工作方式的信息。

0
编译器

编译引用未加载(或未初始化)类的函数会触发其初始化静态内容。当初始化静态内容加载Clojure代码时,一些常量(考虑为源代码)会泄漏到正在编译的函数的常量池中。

这阻止了在某些环境(Rational)中CCW的正常运行,因为结果函数的静态初始化超过64K。

复现步骤

加载leak.main命名空间并运行注释中的代码:第一个函数尽管与第二个函数相同,但有多达15个额外字段。

`
(ns leak.main)

(defn first-to-load []
leak.Klass/foo)

(defn second-to-load []
leak.Klass/foo)

(comment
=> (map (comp count #(.getFields %) class) [first-to-load second-to-load])
(16 1)
)
`

`
package leak;

import clojure.lang.IFn;
import clojure.lang.RT;
import clojure.lang.Symbol;

public class Klass {
static {

RT.var("clojure.core", "require").invoke(Symbol.intern("leak.leaky"));

}
public static IFn foo = RT.var("leak.leaky", "foo");
}
`

`
(ns leak.leaky)

(defn foo
"Some doc"
[]
"hello")

(def unrelated 42)
`

https://gist.github.com/cgrand/5dcb6fe5b269aecc6a5b#file-main-clj-L10

补丁: clj-1620-v5.patch

33 答案

0

评论者:alexmiller

Rich已经同意对1.7进行这个问题的大纲筛选,但是我感觉我需要在对其有更多的了解之后才能标记它通过筛选。此处描述、代码和原因信息不足以让我理解问题真正是什么,或者为什么修复是正确的。修复似乎解决了症状,但我担心这只是症状,而没有对实际原因的更好理解可能会导致不同的或更好的修复。

补丁的演变是由CLJ-1544(一个因为其他原因被撤回的补丁)中的错误驱动的。如果我们从全新的开始,那些修改是必要的且正确吗?

为什么这个变量的集合需要将干净的实现推送到绑定中?为什么不使用其他一些变量(如使用 load() 推送的那些)呢?这里选择的集合似乎与 ReifyParser 的一致,为什么?为什么只有在变量被绑定(也就是说,“未绑定”为什么不等同于“绑定但为空”)时才推送它们?我们在影响性能吗?

完全弹出的操作,CCW 所做的就是否是应该可行的事情?描述说“编译引用了未加载(或未初始化)的类的函数将触发其静态初始化” - 这种加载是否应该发生?我们能找到实际演示 CCW 原始操作的例子吗?

0

评论者:laurentpetit

Alex,我认为如果您在给定例子中回答了这个问题,就能回答“CCW 应该执行的操作是否可行”的问题。

“当类可以只加载时,类的初始化是否应该发生”是一个很好的问题。已有多篇关于这个问题的 Clojure 列表报告,我猜至少有一个 CLJ issue 是关于在某些地方将 classForName 转换为 classForNameNonLoading 的。
例如,它阻止了在运行时对初始化的功能做假设的情况下,立即引用具有代码的静态初始化器的 java 类。这是一个 Eclipse / SWT 的问题,这正如我回忆起,Colin 提到了类似的某个问题。并且可能是在尝试 AOT 编译与 java 类交互的 clojure 代码时,出现在静态初始化器(及其通过编译传递的任何依赖项)中的假设的问题之一。

我不知道如果从一开始就防止类静态方法的初始化(通过 interop 调用 - 构造函数、属性、方法、静态属性、静态方法)发生,是否足以消除这个错误及其提出的补丁试图解决的问题。我并不完全了解所发生的事情(Christophe 和 Nicolas 在分析该问题和创建补丁方面给予了极大的帮助),但据我理解,这是一个类似于“电影《盗梦空间》”一样的错误。编译一个 fn,它会触发另一个 fn 的编译(在这里是通过通过 java 初始化器加载 clojure 名称空间)。

如果当通过 interop 调用(构造函数、属性、方法、静态属性、静态方法)引用类时,这是造成“编译中的编译”场景的最后一个可能的问题,那么是的,像 Nicolas 尝试做的,保护编译过程可能不是必要的,仅仅修复不希望加载的补丁可能就足够了。

0
参考资料:https://clojure.atlassian.net/browse/CLJ-1620(报告:cgrand)
...