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

欢迎!请查看关于页面以获取关于此工作方式的一些更多信息。

+1个投票
编译器
重新标记

给定一个引用类的 Clojure 函数,如果该类有一个在运行时失败的静态字段初始化器,该函数将无法编译,因为编译器(不需要)即使相应的字段未访问也要运行初始化器。值得注意的是,当将类存储在变量中然后通过该变量引用它时,这种情况不会发生(见下面的解决方案)。编译器能否支持这里的直接引用?

重现

(以下代码也可在https://github.com/bevuta/clojure-fn-compilation-issue-repro找到)

deps.edn:

{:deps {io.netty/netty-codec {:mvn/version "4.1.108.Final"}}}

src/repro/fail.clj:

(ns repro.fail
  (:import io.netty.handler.codec.compression.BrotliOptions))

(defn bar []
  BrotliOptions)

(defn -main [& args]
  (prn (bar)))

在此处,bar 函数引用了 io.netty.handler.codec.compression.BrotliOptions 类。该类有一个名为 DEFAULT 的包私有静态字段,有一个初始化器。该初始化器依赖于由可选第三方库提供的类。然而,即使用户从未引用过该字段,该初始化器也会运行并失败,因为该库不存在。

运行方式如下:

$ clojure -M -m repro.fail
Execution error (ClassNotFoundException) at jdk.internal.loader.BuiltinClassLoader/loadClass (BuiltinClassLoader.java:641).
com.aayushatharva.brotli4j.encoder.Encoder$Parameters

解决方案

可以通过将类值放置在单独的 def 中,并在函数中使用该值来解决这个问题。

src/repro/ok.clj:

(ns repro.ok
  (:import io.netty.handler.codec.compression.BrotliOptions))

(def foo BrotliOptions)

(defn bar []
  foo)

(defn -main [& args]
  (prn (bar)))

运行方式如下:

$ clojure -M -m repro.ok
io.netty.handler.codec.compression.BrotliOptions

相关性

https://github.com/clj-commons/aleph/issues/703的背景下遇到了该问题。

1 个答案

0个投票
by
选定 by
 
最佳答案

几乎可以确定编译器在某些地方发出了 Class.forName(),而不是 initialize=false 的变体,从而导致在需要之前静态初始化器执行。

记录为 https://clojure.atlassian.net/browse/CLJ-2842

...