中分享您的想法!(2024年Clojure调查!)

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

+1
编译器

我在使用Clojure和RoboVM的项目上工作。我们使用AOT编译将Clojure编译成JVM类,然后使用RoboVM将JVM类编译成本地代码。在我们的Clojure代码中,我们调用RoboVM提供的Java API,这些API包装了本地的iOS API。

但是我们发现了一个与继承和类级别静态初始化代码的问题。许多iOS API要求继承自基对象并覆盖某些方法。目前,Clojure在使用":gen-class"或"proxy"创建子类时,都会在编译时运行超类的静态初始化代码。然而,RoboVM的基"ObjCObject"类(链接:1),大多数iOS特定类都继承自它,需要iOS运行时初始化,并提供错误,因为在编译时的代码没有在设备上运行。

CLJ-1315通过修改"import"以加载类而不运行静态初始化代码来解决了类似的问题。我已经编写了自己的补丁,将该行为扩展到":gen-class"和"proxy"。单元测试已经通过,我们正在iOS应用中使用这个代码成功。

补丁: clj-1743-2.patch

以下是一些可以用来演示当前行为的示例代码(完整演示项目可在(链接:https://github.com/figly/clojure-static-initialization)找到)

`
package clojure_static_initialization;

public class Demo {
static {

System.out.println("Running static initializers!");

}
public Demo () {
}
}
`

(ns clojure-static-initialization.gen-class-demo (:gen-class :extends clojure_static_initialization.Demo))

`
(ns clojure-static-initialization.proxy-demo)

(defn make-proxy []
(proxy [clojure_static_initialization.Demo] []))
`

(链接:1) https://github.com/robovm/robovm/blob/master/objc/src/main/java/org/robovm/objc/ObjCObject.java

14 答案

0

评论者:alexmiller

没有变化,只是更新到适用于1.7.0-RC2的master版本。

0

评论者:alexmiller

如果您有一个通过代理和gen-class进行测试的草图,这将非常有帮助。

0

评论者:abram

当然,您需要什么样的草图代码形式?一个小型的独立项目?单元测试?

0

评论者:alexmiller

这里在测试描述中有几行Java代码(一个具有静态初始化器的类和打印的Clojure代码,用于扩展gen-class和proxy),可以用于展示该问题。不应依赖于iOS或其他外部依赖。

0

评论者:abram

添加了示例代码,请告诉我是否还需要添加其他内容!

0

评论者:abram

出于好奇,这个功能被纳入1.8的可能性有多少?

0

评论者:alexmiller

未知。

0

评论者:notespin

我也受此错误影响。一个命名空间中的函数调用了一个静态Java变量,该变量在原地初始化。另一个genclass化的命名空间调用该函数。现在在编译时,将初始化静态Java,导致构建失败,因为该静态Java初始化需要build机器上不存在的资源。

0

评论由:michaelblume

刷新补丁以将其应用到master分支,没有更改,保持归因。

0

评论者:alexmiller

我对补丁在 RT.loadClassForName() 中进行的更改感到困惑,但在 Compiler 中的更改是调用 RT.classForNameNonLoading()?这是补丁偏移还是怎么回事?

0

评论由:sonicsmooth

感谢你提供这个补丁。静态初始化器的问题使得使用 AOT 和交互式开发进行 JavaFX 开发变得很困难。我今天克隆了 Clojure 1.9.0-master 源代码并应用了这个补丁,但示例 Clojure 项目仍然显示 "正在运行静态初始化!" 我已经通过我的实际使用案例验证了这一点。如果先启动 JFXPanel,错误就会消失。截至 2017 年 9 月,有没有其他解决方案,比如另一种定义代理或延迟到运行时的方式?谢谢。

$ lein clean;lein repl
正在编译 1 个源文件到 C:\dev\clojure\clojure-static-initialization\target\classes
正在编译 clojure-static-initialization.gen-class-demo
正在编译 clojure-static-initialization.proxy-demo
正在运行静态初始化!
Clojure 1.9.0-master-SNAPSHOT

user=> (def lcp (proxy (link: javafx.scene.control.ListCell) (link: )))

CompilationException java.lang.ExceptionInInitializerError, 编译:(C:\dev\clojure\clojure-static-initialization\target\f31ee90298a1be447b450330204c3c0806c08b96-init.clj:1:10)

$ lein clean;lein repl
正在编译 1 个源文件到 C:\dev\clojure\clojure-static-initialization\target\classes
正在编译 clojure-static-initialization.gen-class-demo
正在编译 clojure-static-initialization.proxy-demo
正在运行静态初始化!
Clojure 1.9.0-master-SNAPSHOT

user=> (def jfxpanel (javafx.embed.swing.JFXPanel.))

'user/jfxpanel

user=> (def lcp (proxy (link: javafx.scene.control.ListCell) (link: )))

'user/lcp

0

评论由:[email protected]

我不太确定修复是否只是简单地替换了一些调用 {{classForNameNonLoading}} 几处的问题。{{proxy}} 做了一些相当复杂的内省和类分析,这涉及到许多地方的自定义代码。

另一种解决方案是为代理函数提供一个代理 - 这个函数像 {{proxy}} 一样在运行时执行。
我目前有一个用于解决 JavaFX 的 ListCell 问题的解决方案...

0

评论由:[email protected]

工作原理:将宏评估从编译时延迟到运行时。
在这种情况下,我假设你已经将你的代理调用封装在函数中,并且在运行时这样做是安全的(因为你已经初始化了JavaFX或类似的库)
1. 使用“反引号”来防止宏在编译时评估。
2. 将反引号代码包裹在eval中

(defn make-thing [] (eval `(proxy ...

  1. 局部绑定和函数参数需要“gensym-ed” 1. .
  2. 隐式{{this}}需要以~'this的方式访问

`
(defn make-thing []
(eval
`(proxy [ListCell][]

 (updateItem [item# empty?#]
   (proxy-super updateItem item# empty?#)            
     (.setText ~'this nil)
     ...

`

  1. 函数传递的参数需要在eval外部动态绑定,也许在反引号代码内的let中重新绑定,以便在单独的线程中访问

`
(def ^:dynamic an-arg nil)

(defn make-thing [an-arg]
(binding [*an-arg* an-arg]

(eval
`(let [an-arg# *an-arg*]
  (proxy [ListCell][]
    (updateItem [item# empty?#]
      (proxy-super updateItem item# empty?#)            
       (.setText ~'this nil)
       (println "an-arg:" an-arg#)
       ...

`

这可以由宏来完成吗?例如{{proxyfn}}

0
by
...