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 本身可以抛出异常(在 null 锁定对象上)。Java 字节码在处理这些情况时做一些巧妙的异常表处理,这在(afaict)没有修改 Clojure 编译器的情况下是不可能的。
方法:一个可能的方法是让锁定在 Java 同步块上下文中调用传入的主体。这通过将问题转交给 Java 避免了复杂的字节码问题,但性能影响是未知的。
补丁: clj-1472-3.patch
另请参阅:将字节码与 java 同步块比较可以看到一些差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
审核: Alex Miller - 我将此标记为审核已通过,因为这看起来是一个可行的解决方案,可以修复这个问题,而且由于其使用频率不高,我不太担心性能问题。我认为处理这个问题的方式是让 locking
成为一个特殊形式,具有编译器支持,但我不确定这是否值得一做,所以我会留给 Rich 决定。