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》)。
在 locking
中的 monitor-enter,monitor-exit 和 try/finally 块的组合似乎产生了 ART 指示不平衡 monitorenter/monitorexit 字节码的路径。特别是,monitorenter 和 monitorexit 本身可能会抛出异常(在 null 锁定对象上)。Java 字节码对这些情况进行了复杂的异常表处理,这(据我所知)除非修改 Clojure 编译器,否则无法实现。
方法: 一种可能的方法是在 Java 的同步块上下文中调用传递的代码体。这避免了复杂的字节码问题,但可能会对性能产生影响,尽管其影响是不确定的。
补丁: clj-1472-3.patch
另请参阅:与 java synchronized 块相比,字节码的检查显示了一些差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
已审查: Alex Miller - 由于我认为这是一个可行的解决方案,它能修复问题,并且由于其使用频率不高,我不是那么担心它会引起性能问题。我认为处理这种问题的另一种方法是将 locking
设为一个特殊形式,并得到编译器的支持,但我不确定这样做是否值得,所以我会留给 Rich 来决定。