使用错误的调用操作码进行Dalvik转换

时间:2013-07-11 20:46:31

标签: android dalvik jacoco

我遇到了dalvik dex转换器及其用于调用方法的操作码的问题。基本上我在我的类中定义了private final方法,并且在调用它时,不是生成invoke-direct操作码,而是生成invoke-super。因为它是私有方法,所以超类上不存在该方法,因此我在设备上遇到VFY违规。我能够找到触发这个的确切场景,它似乎发生在:

  1. 使用JaCoCo和
  2. 检测课程
  3. 使用--target 1.6
  4. 编译的类

    如果满足这两个条件,则生成的dex类具有invoke-super而不是invoke-direct。如果我禁用JaCoCo OR,如果我使用--target 1.5编译,则使用正确的invoke-direct操作码。

    在查看javap反汇编的类代码时,我可以看到导致dx假设超级而非直接的原因:

    没有检测,编译为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文件已经编译了java字节码,该字节码引用了this类的完全限定类名(注意“//Method waitForConnectivity:()V”vs“ //Method com/example/ClassName.waitForConnectivity:()V“)。 dx似乎自动假定如果方法名称是完全限定的,则必须使用invoke-super,但如果它不合格,则使用invoke-direct

    我的问题是:

    1. 这是Android dx中的错误,还是JaCoCo中的错误?
    2. 如何避免这种情况,以便JaCoCo检测的类可以在我的自动化测试版本中正常工作?
    3. 我目前的解决方法是拥有一个Maven“jacoco”配置文件,并在那里覆盖${java.version}属性,将其从默认的“1.6”更改为“1.5”。有没有更好的解决方案?

1 个答案:

答案 0 :(得分:2)

dx用于确定是否发出invoke-superinvoke-direct的规则之一是它是否认为方法调用是在与执行调用的类相同的类上进行的。请参阅此处包含的第912行中的RopperMachine.java以供参考:

        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