我在使用 dalvik dex 转换器和它用来调用方法的操作码时遇到了问题。基本上我在类中定义了一个 private final
方法,在调用它时,dx 没有生成 invoke-direct
操作码,而是生成了 invoke-super
。因为它是私有(private)方法,父类(super class)中不存在该方法,所以我在设备上遇到了 VFY 违规。我能够追踪到触发此事件的确切场景,并且它似乎发生在以下时间:
- 使用 JaCoCo 对类进行检测,以及
- 使用
--target 1.6
编译的类
如果满足这两个条件,生成的 dex 类将具有 invoke-super
而不是 invoke-direct
。如果我禁用 JaCoCo 或者如果我使用 --target 1.5
进行编译,它会使用正确的 invoke-direct
操作码。
在查看 javap
反汇编类代码时,我可以看到是什么导致 dx
假设为 super 而不是 direct:
未检测,为 1.6 编译:
$ javap -d com.example.ClassName | grep waitForConnectivity
159: invokespecial #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
147ad8: 7010 6042 0200 |001e: invoke-direct {v2}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4260
针对 1.5(--target 1.5
)进行检测、编译:
$ javap -d com.example.ClassName | grep waitForConnectivity
235: invokespecial #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 7010 9242 0400 |0018: invoke-direct {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292
为 1.6 编译:
$ javap -d com.example.ClassName | grep waitForConnectivity
235: invokespecial #115; //Method com/example/ClassName.waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 6f10 9242 0400 |0018: invoke-super {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292
因此不同之处在于编译后的 .class 文件已经编译了引用 this
类的完全限定类名的 java 字节码(注意“//方法 waitForConnectivity:()V
"与 "//Method com/example/ClassName.waitForConnectivity:()V
")。似乎 dx
自动假设如果方法名是完全限定的,它必须使用 invoke-super
,但如果不是限定的,它使用 invoke-direct
。
我的问题是:
- 这是 Android 的
dx
中的错误,还是 JaCoCo 中的错误? - 如何避免这种情况,以便 JaCoCo 检测的类可以在我的自动化测试构建中正常工作?
我目前的解决方法是拥有一个 Maven“jacoco”配置文件,并在其中覆盖 ${java.version}
属性以将其从默认的“1.6”更改为“1.5”。有没有更好的解决方案?
最佳答案
dx
用来确定是否发出 invoke-super
或 invoke-direct
的规则之一是它是否相信方法调用正在与打电话的人在同一类(class)上制作。请参阅源代码中的 RopperMachine.java
,大约在第 912 行,包含在此处以供引用:
case ByteOps.INVOKESPECIAL: {
/*
* Determine whether the opcode should be
* INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
* on "invokespecial" as well as section 4.8.2 (7th
* bullet point) for the gory details.
*/
CstMethodRef ref = (CstMethodRef) cst;
if (ref.isInstanceInit() ||
(ref.getDefiningClass() == method.getDefiningClass()) ||
!method.getAccSuper()) {
return RegOps.INVOKE_DIRECT;
}
return RegOps.INVOKE_SUPER;
看到被错误转换的类的更完整的转储会很有趣。我认为您从 javap
中看到的可能不是现实的完整图景。请注意,dx
本身内置了一个 .class 文件转储器,它提供了比 javap
更多的细节。将其作为 dx --dump --bytes path/to/Name.class
调用。
关于android - 使用错误调用操作码的 Dalvik 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17603192/