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自身可以抛出(在null锁定对象上)。Java字节码对这些情况进行了一些复杂的异常表处理以覆盖这些情况,这在afaict中无法在不修改Clojure编译器的情况下完成。
方法:一种可能的方法是让锁定在Java的synchronized块上下文中调用传入的body。这避免了通过交给Java处理来处理复杂的字节码问题,但可能对性能产生影响尚不清楚。
补丁: clj-1472-3.patch
参阅:将字节码与java synchronized块进行比较可以发现许多差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
审查: Alex Miller - 我认为这是一个可行的解决方案,可以修复问题,并且由于它的使用频率不高,我不会太担心这将成为性能问题。我认为另一种处理方法是使 locking 成为具有编译器支持的特形式,但我不确定这是否值得做,因此我将由Rich决定。