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 synchronized 块进行比较,发现存在一些差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
筛选:由 Alex Miller 筛选 - 我将其标记为已筛选,因为我认为这是一种可行的解决方案,可以修复问题,但由于其使用频率较低,我并不担心它会导致性能问题。我认为另一种处理方法是使 locking
成为具有编译器支持的特殊形式,但我不确定是否值得这样做,因此我将这个问题留给 Rich 决定。