Transformación de Dalvik usando código de invocación incorrecto
Estoy corriendo en un problema con el convertidor dalvik dex y el opcode que está utilizando para invocar métodos. Básicamente tengo un método private final
definido en mi clase, y cuando lo llamo, en lugar de generar el código operativo invoke-direct
, dx está generando invoke-super
. Debido a que es un método privado, el método no existe en la superclase, por lo que obtengo una violación VFY en el dispositivo. Pude rastrear el escenario exacto que desencadena esto, y parece que sucede cuando:
- Instrumentación de las clases con JaCoCo, y
- Clases compiladas con
--target 1.6
Si se cumplen estas dos condiciones, la clase dex resultante tiene invoke-super
lugar de invoke-direct
. Si desactivo JaCoCo O si compile con --target 1.5
, usa el opcode correcto de invoke-direct
.
- Continúa el informe de cobertura de código jacoco después de un caso de prueba fallido
- Combine la cobertura jacoco de androidTest y pruebe
- ¿Cómo obtener el informe de cobertura de código para android test apk using jacoco?
- JaCoCo devuelve 0% de cobertura con Kotlin y Android 3.0
- Cobertura androide jacoco vacía con gradle
Al mirar el código de clase javap
desmontado, puedo ver lo que hace dx
asumir super en lugar de directo:
No instrumentado, compilado para 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
Instrumentado, compilado para 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
Instrumentado, compilado para 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
Así que la diferencia es que el archivo .class compilado ha compilado bytecode java que hace referencia al nombre de clase totalmente calificado de this
clase (aviso " //Method waitForConnectivity:()V
" vs " //Method com/example/ClassName.waitForConnectivity:()V
"). Parece que dx
asume automáticamente que si el nombre del método está completamente calificado, debe usar invoke-super
, pero si no está calificado, usa invoke-direct
.
Mis preguntas son:
- ¿Se trata de un error en el
dx
de Android, o un error en JaCoCo? - ¿Cómo puedo evitar esto, para que las clases instrumentadas con JaCoCo funcionen correctamente en mis compilaciones de prueba automatizadas?
Mi solución actual es tener un perfil "jacoco" de Maven, y en ese caso ${java.version}
propiedad ${java.version}
para cambiarla del valor predeterminado "1.6" a "1.5". ¿Hay alguna solución mejor?
- Android - La cobertura de código de Jacoco ignora las pruebas Robolectric
- Cobertura de código en Android Studio con Sonar & Gradle
- Cobertura del código de prueba de Android con JaCoCo Gradle plugin
- Java.lang.NoClassDefFoundError: Resolución fallida de: Lorg / jacoco / agent / rt / internal_773e439 / Offline;
- Cómo obtener informes de cobertura de código de google Firebase para Android Espresso tests
- AndroidStudio: Informe de cobertura de código JaCoCo 0% para cobertura
- Archivo Cobertura de cobertura.ec al informe de Jacoco
- Cobertura de código de Android en Jenkins CI
Una de las reglas que dx
utiliza para determinar si debe emitir invoke-super
o invoke-direct
es si cree que la llamada al método se está realizando en la misma clase que la que realiza la llamada. Vea RopperMachine.java
en la fuente, alrededor de la línea 912, incluida aquí como referencia:
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;
Sería interesante ver un volcado más completo de la clase que está siendo malconvertida. Creo que es probablemente el caso de que lo que está viendo desde javap
no es una imagen completa de la realidad. Tenga en cuenta que dx
sí tiene un descargador de archivos .class integrado en él que proporciona mucho más detalle que javap
. dx --dump --bytes path/to/Name.class
como dx --dump --bytes path/to/Name.class
.
- ¿Cómo actualizo el SSL cert en mis aplicaciones Android?
- Cargar la imagen en caché de WebView en un ImageView