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

欢迎!请参阅关于页面了解更多关于它的信息。

+1
编译器
重新标记

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

复现问题

(以下代码也可见于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,其初始化器依赖于由可选第三方库提供的类。然而,即使bar从未引用该字段,初始化器也会运行并因为该库不存在而失败。

运行方式如下:

$ 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

选定
 
最佳答案

几乎可以肯定的是,编译器在某个地方发出的是 Class.forName(),而不是带有 initialize=false 变形的版本,导致静态初始化器在需要之前运行。

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

...