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
- instrumentacji klas z JaCoCo i
- 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:
- Jest to błąd w Android
dx
lub błąd w JaCoCo? - 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?
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
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
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