web-dev-qa-db-fra.com

Compiler avec Proguard donne à SimException: "incompatibilité de type de variable locale"

Lorsque je compile mon Android Application avec Proguard activé, j'obtiens l'erreur suivante:

-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

Comment puis-je résoudre ce problème?

48
Catalin Morosan

La partie Proguard réelle se termine, mais dex ne peut plus convertir le bytecode résultant. Dex considère que le LocalVariableTable est incorrect. Eric Lafortune est la meilleure source pour expliquer pourquoi (voir sa réponse).

Le problème disparaît si non seulement vous ne masquez pas, mais que vous sautez également l'étape d'optimisation (-dontoptimize). Mais vous voulez l'avoir pour la réduction de taille. Une autre façon de le résoudre consiste à supprimer les indicateurs de débogage dans javac et dans dex. Le seul problème est que vous n'auriez pas non plus de traces de pile appropriées. Vous obtiendrez des lignes de stacktrace sans informations sur le fichier source ou numéros de ligne tels que:

net.lp.collectionista.domain.items.book.BookItem.getCoverImageForFormField(Unkno‌​wn Source)

Vous pouvez le faire en ajoutant debug="false" dans la balise javac dans la fourmi main-rules.xml (vous voudrez peut-être copier la pièce dans un build.xml première). Cela définira un indicateur javac -g:none. Vous devez également configurer dex et c'est plus difficile à faire dans le modèle de fourmi fourni. J'ai copié le dex-helper macro, s'est assuré qu'il était utilisé et a ajouté une balise de condition entourant les appels 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>

C'est le --no-locals ça le fait.

Pour atténuer la perte d'informations de trace de pile que vous pouvez utiliser, respectivement pour les informations de numéro de ligne et les informations de noms de classe et de méthode:

-keepattributes SourceFile, LineNumberTable
-keep,allowshrinking,allowoptimization class * { <methods>; }

De cette façon, vous pouvez faire de l'obscurcissement partiel et avoir toujours de bonnes traces de pile équivalentes. Je vous suggère néanmoins de créer et de conserver les fichiers de mappage lors de leur publication.

De plus, vous ne devez pas spécifier -keepattributes LocalVariableTable,LocalVariableTypeTable et également -keepparameternames (si vous faites de l'obscurcissement, cela peut aussi vous causer des ennuis). Notez que le second implique le premier, même s'il n'est pas clair d'après son nom qu'il affecte les attributs.

Personnellement, et compte tenu d'autres problèmes avec Proguard, j'ai choisi de faire de l'obscurcissement mais d'atténuer la perte d'informations de stacktrace. Je n'ai pas encore essayé la proposition de @ ploughman.

Pour plus de détails, vous pouvez trouver mes fichiers de projet contrôlés par version ici:

15
pjv

J'ai rencontré le même problème après avoir ajouté l'indicateur -dontobfuscate à mon fichier proguard.cfg.

La solution a finalement été que je devais ajouter ceci à mes optimisations:

!code/allocation/variable

Cela rend ma chaîne d'optimisation complète ressembler à ceci:

-optimizations !field/removal/writeonly,!field/marking/private,!class/merging/*,!code/allocation/variable
98
plowman

Il s'agit d'un bogue dans ProGuard. Son étape d'optimisation ne met parfois pas à jour les attributs de débogage facultatifs "LocalVariableTable" et "LocalVariableTypeTable" à l'intérieur des fichiers de classe correctement. Le Dalvik VM vérifie explicitement les attributs de débogage et rejette les fichiers de classe s'ils sont incohérents.

Vous devez vérifier si la dernière version de ProGuard résout le problème. Sinon, vous devez supprimer les noms et types de variables locales des fichiers de classe. Vous pouvez demander au compilateur Java de ne pas les générer (par exemple "javac -g: none"). Vous pouvez également demander à ProGuard de ne pas les conserver (ne pas spécifier "-keepattributes LocalVariableTable, LocalVariableTypeTable ").

22
Eric Lafortune

Je viens de refaire surface sur Windows Android Studio, et la désactivation d'Instant Run a fait fonctionner à nouveau les choses.

6
JaviCasa