android - studio - proguard xamarin
La compilación con Proguard da SimException: "discrepancia del tipo de variable local" (4)
Acabo de tener esta superficie en el Android Studio de Windows, y la desactivación de Instant Run hizo que las cosas funcionaran nuevamente.
Cuando compilo mi aplicación para Android con Proguard habilitada, recibo el siguiente error:
-dex:
[echo] Converting compiled files and external libraries into /home/ka/dev/workspace/ImPress/build/classes.dex...
[apply]
[apply] UNEXPECTED TOP-LEVEL EXCEPTION:
[apply] com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.io.File using a local variable of type java.lang.Object[]. This is symptomatic of .class transformation tools that ignore local variable information.
[apply] at com.android.dx.cf.code.BaseMachine.throwLocalMismatch(BaseMachine.java:550)
[apply] at com.android.dx.cf.code.BaseMachine.getLocalTarget(BaseMachine.java:405)
[apply] at com.android.dx.cf.code.BaseMachine.storeResults(BaseMachine.java:532)
[apply] at com.android.dx.cf.code.ValueAwareMachine.run(ValueAwareMachine.java:197)
[apply] at com.android.dx.cf.code.RopperMachine.run(RopperMachine.java:291)
[apply] at com.android.dx.cf.code.Simulator$SimVisitor.visitLocal(Simulator.java:608)
[apply] at com.android.dx.cf.code.BytecodeArray.parseInstruction(BytecodeArray.java:526)
[apply] at com.android.dx.cf.code.Simulator.simulate(Simulator.java:99)
[apply] at com.android.dx.cf.code.Ropper.processBlock(Ropper.java:684)
[apply] at com.android.dx.cf.code.Ropper.doit(Ropper.java:639)
[apply] at com.android.dx.cf.code.Ropper.convert(Ropper.java:252)
[apply] at com.android.dx.dex.cf.CfTranslator.processMethods(CfTranslator.java:252)
[apply] at com.android.dx.dex.cf.CfTranslator.translate0(CfTranslator.java:131)
[apply] at com.android.dx.dex.cf.CfTranslator.translate(CfTranslator.java:85)
[apply] at com.android.dx.command.dexer.Main.processClass(Main.java:369)
[apply] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:346)
[apply] at com.android.dx.command.dexer.Main.access$400(Main.java:59)
[apply] at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:294)
[apply] at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:244)
[apply] at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:130)
[apply] at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:108)
[apply] at com.android.dx.command.dexer.Main.processOne(Main.java:313)
[apply] at com.android.dx.command.dexer.Main.processAllFiles(Main.java:233)
[apply] at com.android.dx.command.dexer.Main.run(Main.java:185)
[apply] at com.android.dx.command.dexer.Main.main(Main.java:166)
[apply] at com.android.dx.command.Main.main(Main.java:90)
[apply] ...at bytecode offset 00000006
[apply] locals[0000]: Lcom/officemax/impress/ui/library/task/DocumentBrowserTask;
[apply] locals[0001]: [Ljava/lang/Object;
[apply] locals[0002]: <invalid>
[apply] ...while working on block 0006
[apply] ...while working on method doTaskJob:([Ljava/lang/Object;)Lcom/kaciula/utils/ui/BasicTaskResponse;
[apply] ...while processing doTaskJob ([Ljava/lang/Object;)Lcom/kaciula/utils/ui/BasicTaskResponse;
[apply] ...while processing com/officemax/impress/ui/library/task/DocumentBrowserTask.class
[apply]
[apply] 1 error; aborting
¿Como puedo solucionar este problema?
Este es un error en ProGuard. Su paso de optimización a veces no actualiza los atributos de depuración opcionales "LocalVariableTable" y "LocalVariableTypeTable" dentro de los archivos de clase de manera completamente correcta. Dalvik VM verifica explícitamente los atributos de depuración y rechaza los archivos de clase si son inconsistentes.
Debería verificar si la última versión de ProGuard soluciona el problema. De lo contrario, debe eliminar los nombres y tipos de variables locales de los archivos de clase. Puede solicitar al compilador de java que no los genere (p. Ej., "Javac -g: none"). También puede pedirle a ProGuard que no los guarde (no especifique "-keepattributes LocalVariableTable, LocalVariableTypeTable").
La parte real de Proguard finaliza, pero dex ya no puede convertir el bytecode resultante. Dex considera que LocalVariableTable
incorrecta. Eric Lafortune es la mejor fuente para explicar por qué (ver su respuesta).
El problema desaparece si no solo no se ofusca, sino que también omite el paso de optimización ( -dontoptimize
). Pero quieres tener esto para la reducción de tamaño. Otra forma de resolverlo es soltar los indicadores de depuración en javac
y en dex
. El único problema es que tampoco tendrías stacktraces adecuados. Obtendrá líneas stacktrace sin información de archivo fuente o números de línea como:
net.lp.collectionista.domain.items.book.BookItem.getCoverImageForFormField(Unknown Source)
Puede hacer esto agregando debug="false"
en la etiqueta javac
en ant main-rules.xml
(es posible que desee copiar la parte en un build.xml
primero). Esto establecerá una bandera javac -g:none
. También tiene que configurar dex y esto es más difícil de hacer en la plantilla de hormiga proporcionada. Copié la macro dex-helper
, me aseguré de que se estaba utilizando y agregué una etiqueta de condición que rodea las llamadas dex:
<echo>Converting compiled files and external libraries into ${intermediate.dex.file}...</echo>
<if condition="debug">
<then>
<apply executable="${dx}" failonerror="true" parallel="true">
<arg value="--dex" />
<arg value="--output=${intermediate.dex.file}" />
<extra-parameters />
<arg line="${verbose.option}" />
<arg path="${out.dex.input.absolute.dir}" />
<path refid="out.dex.jar.input.ref" />
<external-libs />
</apply>
</then>
<else>
<apply executable="${dx}" failonerror="true" parallel="true">
<arg value="--dex" />
<arg value="--output=${intermediate.dex.file}" />
<arg value="--no-locals" /><!-- otherwise dex fails on the proguard bytecode -->
<extra-parameters />
<arg line="${verbose.option}" />
<arg path="${out.dex.input.absolute.dir}" />
<path refid="out.dex.jar.input.ref" />
<external-libs />
</apply>
</else>
</if>
Son los --no-locals
que lo hacen.
Para mitigar la pérdida de información de stack, puede usar, respectivamente, la información del número de línea y la información de nombres de clases y métodos:
-keepattributes SourceFile, LineNumberTable
-keep,allowshrinking,allowoptimization class * { <methods>; }
De esta forma puedes hacer una ofuscación parcial y aún tener buenas stacktraces equivalentes. Sin embargo, sigo sugiriendo que crees y guardes los archivos de mapeo una vez publicados.
Además de todo esto, no debería especificar -keepattributes LocalVariableTable,LocalVariableTypeTable
e igualmente -keepparameternames
(si se ofusca, esto por sí solo podría -keepparameternames
problemas). Tenga en cuenta que el segundo implica el primero, aunque puede no estar claro por su nombre que afecte a los atributos.
Personalmente, y en vista de otros problemas con Proguard, elegí hacer la ofuscación pero mitigar la pérdida de información de stack. No he intentado la propuesta de @plowman todavía.
Para más detalles, puede encontrar mis archivos de proyecto controlados por la versión aquí:
Me encontré con el mismo problema después de agregar el indicador -dontobfuscate a mi archivo proguard.cfg.
La solución terminó siendo que necesitaba agregar esto a mis optimizaciones:
!code/allocation/variable
Esto hace que mi cadena de optimización completa se vea así:
-optimizations !field/removal/writeonly,!field/marking/private,!class/merging/*,!code/allocation/variable