2013-07-11 11 views
5

Ich stoße auf ein Problem mit dem Dalvik Dex-Konverter und dem Opcode, der verwendet wird, um Methoden aufzurufen. Grundsätzlich habe ich eine private final Methode in meiner Klasse definiert, und wenn Sie es aufrufen, anstatt invoke-direct Opcode zu generieren, generiert dx invoke-super. Da es sich um eine private Methode handelt, existiert die Methode nicht in der Superklasse, daher erhalte ich eine VFY-Verletzung auf dem Gerät. Ich war in der Lage, die genaue Szenario auf die Spur, die dies auslöst, und es scheint zu geschehen, wenn:Dalvik-Transformation mit falschem Invoke-Opcode

  1. die Klassen mit JaCoCo instrumentiert und
  2. Klassen zusammengestellt mit --target 1.6

Wenn diese beiden Bedingungen erfüllt sind, hat die resultierende Dex-Klasse invoke-super statt invoke-direct. Wenn ich JaCoCo ODER deaktivieren, wenn ich mit --target 1.5 kompiliere, verwendet es den korrekten invoke-direct Opcode.

am javap zerlegten Klassencode In suchen, kann ich sehen, was dx Super statt direkt zu übernehmen Ursachen:

nicht instrumentiert, zusammengestellt für 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 // [email protected] 

instrumentiert, zusammengestellt für 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 // [email protected] 

Instrumented, für 1.6 zusammengestellt:

$ 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 // [email protected] 

So ist der Unterschied besteht darin, dass die kompilierte Class-Datei hat Bytecode kompiliert Java, die die vollständig qualifizierten Klassennamen der this Klasse (Hinweis "//Method waitForConnectivity:()V" verweist vs "//Method com/example/ClassName.waitForConnectivity:()V"). Es scheint, dass dx automatisch davon ausgeht, dass, wenn der Methodenname vollständig qualifiziert ist, es invoke-super verwenden muss, aber wenn es nicht qualifiziert ist, verwendet es invoke-direct.

Meine Fragen sind:

  1. Ist das ein Bug in Android dx oder einen Fehler in JaCoCo?
  2. Wie kann ich dies vermeiden, damit die mit JaCoCo instrumentierten Klassen in meinen automatisierten Testaufbauten ordnungsgemäß funktionieren?

Meine aktuelle Problemumgehung ist ein Maven „jacoco“ Profil haben, und ich überschreiben die ${java.version} Eigenschaft in dort sind es von der Standardeinstellung zu ändern „1,6“ bis „1,5“. Gibt es eine bessere Lösung?

Antwort

2

Eine der Regeln, die dx verwendet, um zu bestimmen, ob invoke-super oder invoke-direct zu emittieren ist, ob es glaubt, dass der Methodenaufruf auf der gleichen Klasse wie der Anruf ausgeführt wird.Siehe RopperMachine.java in der Quelle, um die Linie 912, die hier enthalten als Referenz:

 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; 

Es wäre interessant, eine Müllhalde der Klasse zu sehen, die misconverted wird immer. Ich denke, es ist wahrscheinlich der Fall, dass das, was Sie von javap sehen, nicht ein vollständiges Bild der Realität ist. Beachten Sie, dass dx selbst einen .class Dateikipper enthält, der viel mehr Details als javap bietet. Rufen Sie es als dx --dump --bytes path/to/Name.class auf.

+0

Dies würde bedeuten, dass 'ref.getDefiningClass()! = Method.getDefiningClass()'. Ich werde heute versuchen, eine vollständigere Dump für Sie zu bekommen. Aber das Problem scheint mit der Offline-Instrumentierung von JaCoCo und dem Java 1.6 Compiler leicht reproduzierbar zu sein. Interessanterweise brach es nicht immer so, also hast du wahrscheinlich recht, dass da noch etwas anderes vor sich geht. Ich weiß nur nicht wann es angefangen hat. – Joe

+0

Ich gewähre das Kopfgeld, weil Sie den glaubwürdigen Teil der Bounty-Anforderung erfüllt haben. Obwohl es noch nicht aufgelöst oder auf einen Grund zurückverfolgt wurde, scheint es, dass der Schuldige höchstwahrscheinlich der JaCoCo-Instrumentierer ist, obwohl ich denke, dass "dx" immer noch in der Lage sein sollte, die definierende Klasse für die aufgerufene Methode zu erkennen ist gleich der definierenden Klasse des Aufrufers, was wie erwartet zu 'INVOKE_DIRECT' führt. Wird wie vorgeschlagen weitere Untersuchungen erfordern. Vielleicht können wir das später in einem Chat fortsetzen, da ich Zeit finde, darauf zurückzukommen. – Joe

+1

Danke! Wie ich schon sagte, kann 'dx - dump' wahrscheinlich helfen, die genaue Art des Unterschieds zu bestimmen. Ich werde nicht behaupten, dass "dx" fehlerfrei ist (obwohl Stolz mich dazu bringen will), aber ja, JaCoCo macht mit Sicherheit etwas, das zumindest etwas verdächtig ist. – danfuzz

Verwandte Themen