2013-07-11 29 views
5

Sto riscontrando un problema con il convertitore dalvik dex e l'opcode che sta utilizzando per richiamare i metodi. Fondamentalmente ho un metodo private final definito nella mia classe, e quando lo si chiama, invece di generare l'opcode invoke-direct, dx sta generando invoke-super. Poiché si tratta di un metodo privato, il metodo non esiste sulla super classe, quindi ricevo una violazione VFY sul dispositivo. Sono stato in grado di rintracciare lo scenario esatto che attiva questo, e sembra accadere quando:Trasformazione Dalvik tramite codice di invocazione errato

  1. strumentazione classi con JaCoCo e
  2. classi compilate con --target 1.6

Se queste due condizioni sono soddisfatti, la classe di dex risultante ha invoke-super anziché invoke-direct. Se disattivo JaCoCo OR se compilo con --target 1.5, utilizza l'opcode invoke-direct corretto.

Nel guardare il codice della classe smontato javap, posso vedere che cosa provoca dx ad assumere eccellente invece di diretta:

Non strumentato, compilato per 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] 

strumentati, compilato per 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, compilato per 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] 

Quindi la differenza è che il bytecode Java compilato .class file è compilato che fa riferimento alla completo nome del this di classe (avviso "//Method waitForConnectivity:()V" class vs "//Method com/example/ClassName.waitForConnectivity:()V"). Sembra che dx assuma automaticamente che se il nome del metodo è pienamente qualificato, deve utilizzare invoke-super, ma se non è qualificato, utilizza invoke-direct.

Le mie domande sono:

  1. È questo un bug in Android di dx, o di un bug nel JaCoCo?
  2. Come evitare questo, in modo che le classi JaCoCo-instrumented possano funzionare correttamente nei miei build di test automatici?

mia soluzione attuale è quella di avere un Maven "jacoco" profilo, e lì ho sovrascrivere la proprietà ${java.version} di cambiarlo dal default "1.6" a "1.5". C'è una soluzione migliore?

risposta

2

Una delle regole che dx utilizza per determinare se emettere o invoke-superinvoke-direct è se crede che la chiamata al metodo è stato fatto sulla stessa classe di quello che fa la chiamata.Vedere RopperMachine.java nella fonte, attorno alla riga 912, incluso qui per riferimento:

 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; 

Sarebbe interessante vedere un dump più completo della classe che sta ottenendo misconverted. Penso che sia probabilmente il caso che ciò che vedi da javap non sia un'immagine abbastanza completa della realtà. Notare che lo stesso dx ha un dumper del file .class incorporato che fornisce molti più dettagli di javap. Richiamalo come dx --dump --bytes path/to/Name.class.

+0

Quindi questo implicherebbe che ref.getDefiningClass()! = Method.getDefiningClass() '. Cercherò di ottenere una discarica più completa per te da qualche tempo oggi. Ma il problema sembra essere facilmente riproducibile usando la strumentazione offline JaCoCo e il compilatore Java 1.6. È interessante notare però che non si è sempre rotto così, quindi probabilmente hai ragione che c'è anche qualcos'altro. Solo non so quando è iniziato. – Joe

+0

Concedo il premio perché hai soddisfatto la parte credibile del requisito di taglia. Sebbene non sia stato ancora risolto o rintracciato per un motivo/causa, per ora sembra che il colpevole sia probabilmente lo strumento JaCoCo, sebbene ritenga che 'dx' dovrebbe essere ancora in grado di riconoscere che la classe che definisce il metodo invocato è uguale alla classe che definisce l'invocatore, risultante in "INVOKE_DIRECT" come previsto. Richiederà ulteriori indagini come suggerito. Forse potremo continuare in una chat in un secondo momento, poiché trovo il tempo per tornare a questo. – Joe

+1

Grazie! Come ho detto, 'dx --dump' può probabilmente aiutare a individuare la natura esatta della differenza. Non ho intenzione di affermare che 'dx' è impeccabile (anche se l'orgoglio mi fa desiderare di farlo), ma sì, JaCoCo sta quasi certamente facendo qualcosa almeno in qualche modo sospetto. – danfuzz

Problemi correlati