Android ART 对字节码进行编译时验证,并在使用锁定宏时失败。错误看起来像 CLJ-1829 中的情况(在这种情况下,clojure.core.server/stop-server 调用了锁定宏)
10-16 14:49:26.801 2008-2008/? E/AndroidRuntime: java.lang.VerifyError: 拒绝类 clojure.core.server$stop_server,因为它在编译时验证失败(声明 'clojure.core.server$stop_server' 在 /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》)。
似乎在 locking
中的 monitor-enter、monitor-exit 和 try/finally 块的混合中,ART 正将该路径标记为不平衡的 monitorenter/monitorexit 字节码。特别是,monitorenter 和 monitorexit 自己可以抛出异常(在一个空的锁定对象上)。Java 字节码对这些情况进行了某些复杂的异常表处理,但据我所知,没有修改 Clojure 编译器是无法做到这一点的。
方法:一个可能的方法是在 Java 的 synchronized 块上下文中调用传递的 body,这样通过移交到 Java 就避免了复杂的字节码问题,但这会产生未知的表现影响。
补丁:clj-1472-3.patch
另请参阅:将字节码与 Java synchronized 块进行比较,显示了一些不同的差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
已审核:Alex Miller - 我认为这是一个可行的解决方案,可以修复问题,并且由于使用频率不高,我并不特别关注它是否是一个性能问题。我将标记我认为另一种处理方式是让 locking
成为一种特殊形式,并获得编译器的支持,但我不确定这样做是否值得,所以我将把这个决定留给 Rich。