5

dalvik dex コンバーターとメソッドの呼び出しに使用しているオペコードに問題があります。基本的にprivate final、クラスで定義されたメソッドがあり、それを呼び出すと、invoke-directオペコードを生成する代わりに、dx が生成されinvoke-superます。プライベート メソッドであるため、スーパー クラスにはメソッドが存在しないため、デバイスで VFY 違反が発生します。これをトリガーする正確なシナリオを突き止めることができました。次の場合に発生するようです。

  1. JaCoCo を使用してクラスをインストルメント化し、
  2. でコンパイルされたクラス--target 1.6

これら 2 つの条件が満たされている場合、結果の dex クラスにはinvoke-super代わりにinvoke-direct. JaCoCo を無効にすると、または でコンパイルすると--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//Method com/example/ClassName.waitForConnectivity:()Vdxinvoke-superinvoke-direct

私の質問は次のとおりです。

  1. これは Android のdxバグですか、それとも JaCoCo のバグですか?
  2. 自動化されたテスト ビルドで JaCoCo がインストルメント化されたクラスを適切に動作させるには、どうすればこれを回避できますか?

私の現在の回避策は、Maven の「jacoco」プロファイルを使用することです。そこで${java.version}プロパティをオーバーライドして、デフォルトの「1.6」から「1.5」に変更します。より良い解決策はありますか?

4

1 に答える 1

2

エミットするかdxどうかを決定するために使用するルールの 1 つは、呼び出しを行っているクラスと同じクラスでメソッド呼び出しが行われていると見なすかどうかです。ソースの 912 行付近を参照してください。参照用にここに含まれています。invoke-superinvoke-directRopperMachine.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

于 2013-07-19T02:04:18.993 に答える