android - 使用错误调用操作码的 Dalvik 转换

标签 android dalvik jacoco

我在使用 dalvik dex 转换器和它用来调用方法的操作码时遇到了问题。基本上我在类中定义了一个 private final 方法,在调用它时,dx 没有生成 invoke-direct 操作码,而是生成了 invoke-super。因为它是私有(private)方法,父类(super class)中不存在该方法,所以我在设备上遇到了 VFY 违规。我能够追踪到触发此事件的确切场景,并且它似乎发生在以下时间:

  1. 使用 JaCoCo 对类进行检测,以及
  2. 使用 --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

我的问题是:

  1. 这是 Android 的 dx 中的错误,还是 JaCoCo 中的错误?
  2. 如何避免这种情况,以便 JaCoCo 检测的类可以在我的自动化测试构建中正常工作?

我目前的解决方法是拥有一个 Maven“jacoco”配置文件,并在其中覆盖 ${java.version} 属性以将其从默认的“1.6”更改为“1.5”。有没有更好的解决方案?

最佳答案

dx 用来确定是否发出 invoke-superinvoke-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/

相关文章:

android - Dalvik 类加载器之谜

java - 替换/重建Android源中的framework.jar

java - Jacoco Gradle Plugin 及其对测试任务的依赖

java - 添加在 @PrepareForTest 中编写 Junit 的类可以使 Sonar 覆盖率为零

android - 如何从代码覆盖率报告中排除包?

android - 从 Android 提交到 Google 表单?

javascript - 在expo react-native中打开键盘时如何隐藏TabBar

android - .obj 到 .glb 转换

android - 单击时更新按钮的不透明度? flutter

Android dalvik虚拟机