2013-07-11 15 views
5

Występuje problem z konwerterem dalvik dex i kodem operacyjnym używanym do wywoływania metod. Zasadniczo mam metoda private final zdefiniowana w mojej klasie, a podczas wywoływania, zamiast generowania kodu operacji invoke-direct, dx generuje invoke-super. Ponieważ jest to metoda prywatna, ta metoda nie istnieje w super klasie, więc dostaję naruszenie VFY na urządzeniu. Udało mi się wyśledzić dokładny scenariusz, który wyzwala to, i to wydaje się zdarzyć, gdy:Transformacja Dalvik przy użyciu niewłaściwego kodu wywołania

  1. instrumentacji klas z JaCoCo i
  2. klas skompilowane z --target 1.6

Jeśli te dwa warunki są spełnione, wynikowa klasa dex ma invoke-super zamiast invoke-direct. Jeśli wyłączysz JaCoCo LUB jeśli skompiluję z --target 1.5, użyje poprawnego kodu operacyjnego invoke-direct.

W patrząc na javap zdemontowanego kod klasy, widzę, co powoduje dx zakładać Super zamiast bezpośredniego:

Nie oprzyrządowanie, skompilowany do 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] 

oprzyrządowanego, opracowywane dla 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, skompilowany do 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 // [email protected] 

Więc różnica jest taka, że ​​skompilowany plik .class skompilowany kod bajtowy Java jest, że odwołuje się do pełną nazwę z this klasy (uprzedzenia "//Method waitForConnectivity:()V" class vs "//Method com/example/ClassName.waitForConnectivity:()V"). Wygląda na to, że dx automatycznie zakłada, że ​​jeśli nazwa metody jest w pełni kwalifikowana, musi używać invoke-super, ale jeśli nie jest kwalifikowana, używa invoke-direct.

Moje pytania są następujące:

  1. Jest to błąd w Android dx lub błąd w JaCoCo?
  2. Jak mogę tego uniknąć, aby przyrządy z instrumentem JaCoCo działały poprawnie w moich automatycznych testach?

Moje bieżące obejście jest mieć Maven „jacoco” profil, a tam zastąpić właściwość ${java.version} go zmienić z domyślnego „1.6” do „1.5”. Czy istnieje jakieś lepsze rozwiązanie?

Odpowiedz

2

Jedną z zasad używanych przez do określenia, czy należy emitować invoke-super czy invoke-direct, jest to, czy wywołanie metody jest wykonywane na tej samej klasie, co wywołanie.Zobacz RopperMachine.java w źródle wokół linii 912, dołączonego tutaj dla odniesienia:

 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; 

Byłoby interesujące zobaczyć pełniejszy zrzut klasy, która jest coraz misconverted. Myślę, że prawdopodobnie to, co widzisz z javap, nie jest kompletnym obrazem rzeczywistości. Zauważ, że sama dx ma wbudowaną wtyczkę .class, która zapewnia o wiele więcej szczegółów niż javap. Wywołaj go jako dx --dump --bytes path/to/Name.class.

+0

Oznacza to, że 'ref.getDefiningClass()! = Method.getDefiningClass()'. Postaram się dziś dla ciebie zrobić bardziej kompletny zrzut. Problem wydaje się jednak łatwy do odtworzenia dzięki instrumentacji JaCoCo offline i kompilatorowi Java 1.6. Co ciekawe, nie zawsze się tak łamało, więc prawdopodobnie masz rację, że dzieje się coś jeszcze. Po prostu nie wiem, kiedy to się zaczęło. – Joe

+0

Przyznam nagrodę, ponieważ spełniłeś wiarygodną część wymogu nagrody. Chociaż nie jest jeszcze rozwiązany lub jest śledzony z powrotem do przyczyny/przyczyny, na razie wydaje się, że sprawcą jest najprawdopodobniej instrumentator JaCoCo, chociaż wydaje mi się, że 'dx' powinien nadal być w stanie rozpoznać, że klasa definiująca dla wywołanej metody jest równy klasie definiującej wywołującego, co powoduje oczekiwane "INVOKE_DIRECT". Będzie wymagać dalszych badań zgodnie z sugestią. Być może będziemy mogli kontynuować to na czacie w późniejszym czasie, ponieważ znajduję czas, aby do tego wrócić. – Joe

+1

Dzięki! Jak już powiedziałem, "dx - dump" może pomóc w dokładnym określeniu charakteru różnicy. Nie zamierzam twierdzić, że 'dx' jest bezbłędny (chociaż chęć sprawia, że ​​chcę), ale tak, JaCoCo prawie na pewno robi coś co najmniej podejrzanego. – danfuzz

Powiązane problemy