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

欢迎!请参阅 关于 页面了解该功能的更多详细信息。

+52
Clojure
关闭

Android ART 在字节码上进行编译时验证,并在锁定宏的任何使用上失败。错误看起来与 CLJ-1829 中的类似(在这种情况下 clojure.core.server/stop-server 调用锁定宏)

10-16 14:49:26.801 2008-2008/? E/AndroidRuntime: java.lang.VerifyError: Rejecting class clojure.core.server$stop_server because it failed compile-time verification (declaration of 'clojure.core.server$stop_server' appears in /data/app/com.clojure_on_android-1/base.apk)

原因:从 Android 问题讨论(https://code.google.com/p/android/issues/detail?id=80823)中可以看出,这是由于对 JVM 规范中“结构锁定”规定的更严格执行(《https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.11.10》)。

似乎 monitor-enter、monitor-exit 与 locking 中的 try/finally 块的混合创建了一些路径,ART 将其标记为没有平衡的 monitorenter/monitorexit 字节码。特别地,monitorenter 和 monitorexit 本身可能抛出异常(在一个空锁定对象上)。Java 字节码对这些情况做一些巧妙的异常表处理,这(根据我所知)不可能在不修改 Clojure 编译器的情况下完成。

方法:一种可能的方法是让锁定在 Java 的 synchronized 块上下文中调用传递的体。这样可以避免这个问题,但可能对性能有未知的影响。

补丁:clj-1472-3.patch

参见:将字节码与 java synchronized 块的比较显示了许多差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470

审查者:Alex Miller - 我将其标记为审查,因为我认为这是一种可行的解决方案,能够解决这个问题,并且由于它使用的频率不高,我并不真正担心它会引起性能问题。我将指出,我认为另一种处理方法是使 locking 成为一种特殊形式,具有编译器支持,但我不确定这是否值得做,因此我将把它留给 Rich 决定。

已关闭,备注为: 已修复

41 答案

0

评论人为:alexmiller

我添加了一个新的clj-1472.patch,修复了一些我之前版中不喜欢的细节。然而,它仍然是基本相同的更改,所以我保留了原始作者的归属。

0

评论人为:gshayban

alex,你上传对了补丁吗?

0

评论人为:alexmiller

啊,搞错了。我会修复。

0

评论人为:bronsa

Alex,可能值得将其改为^:once fn**

0

评论人为:gshayban

来自Android
{quote}
Android không chạy bytecode Java, mà chạy bytecode Dex. Output của dexdump từ điều mà lớp của bạn dịch sang rất thú vị.

Wyکننده (dvì jvms cũng không thú vị). Android không phải là Java Virtual Machine. Chúng ta tuân theo JLS, nhưng không tuân theo JVMS (nếu như chúng ta không chạy bytecode Java, thì làm thế nào chúng ta lại có thể). Do đó, tất cả các khiếu nại chống lại nó đều không có ý nghĩa. Chúng ta bạn thử tương thích với tinh thần của JVMS về bytecode Dex, nhưng nếu nguồn của bạn không phải là Java, thì không có không có bảo đảm.

Bây giờ, các nhà xác nhận (và có thể vẫn còn) bị lỗi, ngay cả đối với spec (yếu) của chúng ta, và đáng tiếc là chúng ta không rất nhất quán. Ví dụ, trong Marshmallow, một số mã mà chúng ta không thể xác nhận đúng về cấu trúc khóa bị từ chối như một VerifyError, điều này không phải là tinh thần của JVMS. Tuy nhiên, trong phiên bản tiếp theo, điều này sẽ được thả lỏng và hoãn lại để kiểm tra thực sự khi chạy mã.

很遗憾,我们无法解决旧版发布的问题,您必须解决所有问题。:-(: 我会尽量在找到时间的时候看看你的类。
{quote}

听起来在Clojure中创建一个解决方案是所有方案中最不坏的。

0
by

评论人为:alexmiller

添加了带有 ^:once 的 -2 补丁。

0
by

评论人为:alexmiller

我们目前认为这是Android造成的错误,所以先将其记录起来。

0
by

评论人为:gshayban

GraalVM/native-image 也对 monitorenter/exit 平衡表示不满。通过模仿javac的做法进行了修复:https://github.com/ghadishayban/clojure/commit/8acb995853761bc48b62190fe7005b70da692510

0
by

评论人为:alexmiller

Ghadi,如果这是一个可行的解决方案,我对一个修复此问题的补丁很感兴趣。

0
by

评论人为:gshayban

如果有人能协助我进行Android测试基础设施,我将非常感激。

在javac发射"sync"时,它安装了一个捕获异常类型为"任意"的处理程序。链接的提交捕获了Exception。如果需要,我们可以通过调用GeneratorAdapter.visitTryCatch并使用null作为目标类来发射任意处理程序(我还不知道这具体是什么意思——Throwable加上未来的任何其他东西?)。

0
by

评论者:adamclements

你尝试过应用现有的clj-1472-2.patch来查看它是否修复了GraalVM吗?我想我们最初达到了人们满意这个补丁的地方(如果我说错了,Alex请指正),但最终决定将JVM实现作为核心,所以问题是Android没有符合它,而不是Clojure的问题。也许如果同样的问题在其他地方再次出现,这个决定可能需要重新考虑(我仍然希望看到这个补丁)。

这件事已经过去很久了,但我记得尝试了所有可能的clojure try/catch/finally/throw组合,但无法生成符合规格的同步字节码,这需要修改clojure的代码生成,这太深了,实际上不可行——因此产生了上面的补丁,该补丁依赖于javac在同步周围生成字节码,而不是使用monitor-enter和monitor-exit。我担心你的链接实现即使在Clojure代码级别看起来完全正常且显然正确,在某些情况下仍然可能会生成错误的字节码。

0

评论人为:gshayban

我没有尝试过,但我确信从可行性角度看,clj-1472-2.patch应该可以工作。它有一个性能劣势:它可能导致从JVM的角度看出现“性能分析污染”,并混淆锁优化。(大多数锁都是无竞争的)

如果这对Android有效,我更愿意修复其生成,使其更像javac。

0

评论者:eraserhd

我尝试了Ghadi链接的差异,但有些问题(包括丢弃了锁定表达式的值)。我修复了这个问题,但通过研究,我意识到
1. 这没有覆盖由monitor-exit本身抛出的异常,这似乎是必要的。我们不能在catch中递归,也不能捕获'任何',因此我们不能仅用Clojure构造函数生成这种字节码。
2. Clojure的finally生成的字节码中包含异常处理中未覆盖的存储和加载操作,因此我们无法只在monitor-exit代码中添加异常处理。我相当肯定我们不能删除存储和加载操作。

所以,除非我遗漏了什么,否则通过结构化锁定检查的唯一方法是将锁定作为一个特殊形式。

0

评论者:adamclements

已附上的clj-1472-2.patch通过了结构化锁定测试。

在我于2014年进行了大量的测试和生成的字节码比较后,这是我能找到的唯一可行的方案,它不需要指令新的特殊形式来重新实现文档记载不良的规格,以精确地生成正确的字节码,这在我看来似乎是脆弱的。

我很想看到一些性能分析的数字,以了解声称的性能影响有多大,尤其是考虑到在Clojure中实际使用锁定宏的频率非常低(我在测试中在整个生态系统只能找到几个示例。大多数人使用其他并发原语或Java互操作)。

0
by

评论人为:alexmiller

-3 修复合并到 master 上,没有语义变更,归功保留

...