La comstackción con Proguard da SimException: “discrepancia del tipo de variable local”

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]:  [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?

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(Unkno‌​wn 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 ant 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:

  Converting compiled files and external libraries into ${intermediate.dex.file}...                          

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 * { ; } 

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í:

  • proguard.cfg

  • build.xml

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 

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 comstackdor 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”).

Acabo de tener esta superficie en el Android Studio de Windows, y la desactivación de Instant Run hizo que las cosas funcionaran nuevamente.