5

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

  1. 使用 JaCoCo 检测类,以及
  2. 用编译的类--target 1.6

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

在查看javap反汇编的类代码时,我可以看到dx假设 super 而不是直接的原因:

未检测,为 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 检测类可以在我的自动化测试构建中正常工作?

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

4

1 回答 1

2

dx用于确定是否发出invoke-superinvoke-direct是否认为方法调用是在与进行调用的类相同的类上进行的规则之一。请参阅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 文件转储器,它提供了比 .class 更多的细节javap。调用它作为dx --dump --bytes path/to/Name.class.

于 2013-07-19T02:04:18.993 回答