web-dev-qa-db-ja.com

ProguardでコンパイルするとSimException:「ローカル変数の型が一致しません」

AndroidアプリケーションをProguardを有効にしてコンパイルすると、次のエラーが発生します。

-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

この問題を解決するにはどうすればよいですか?

48
Catalin Morosan

実際のProguardパーツは終了しますが、dexは結果のバイトコードを変換できなくなります。 DexはLocalVariableTableを正しくないと見なします。エリック・ラフォーチュンが理由を説明するための優れた情報源です(彼の回答を参照してください)。

難読化を行わないだけでなく、最適化ステップ(-dontoptimize)もスキップすれば、問題はなくなります。しかし、あなたはサイズ縮小のためにこれを持ちたいです。これを解決する別の方法は、javacdexにデバッグフラグをドロップすることです。唯一の問題は、適切なスタックトレースがないことです。ソースファイル情報や次のような行番号のないスタックトレース行を取得します。

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

これを行うには、アリdebug="false"javacタグにmain-rules.xmlを追加します(最初にbuild.xmlにパーツをコピーすることをお勧めします)。フラグjavac -g:noneが設定されます。また、dexを構成する必要があります。これは、提供されたantテンプレートで行うのが困難です。 dex-helperマクロをコピーして、それが使用されていることを確認し、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>

それを行うのは--no-localsです。

行番号情報とクラス名およびメソッド名情報にそれぞれ使用できるスタックトレース情報の損失を軽減するには、次のようにします。

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

このようにして、部分的な難読化を行うことができ、同等の優れたスタックトレースを保持できます。ただし、リリース時にマッピングファイルを作成して保持することをお勧めします。

さらに、-keepattributes LocalVariableTable,LocalVariableTypeTableおよび-keepparameternamesを指定しないでください(難読化を行うと、これだけでも問題が発生する可能性があります)。名前からは属性に影響を与えることが明確でない場合でも、2番目は1番目を意味することに注意してください。

個人的に、またProguardの他の問題を考慮して、難読化を行うことを選択しましたが、スタックトレース情報の損失を軽減しました。 @plowmanの提案はまだ試していません。

詳細については、ここに私のバージョン管理されたプロジェクトファイルを見つけることができます:

15
pjv

Proguard.cfgファイルに-dontobfuscateフラグを追加した後、同じ問題に遭遇しました。

解決策は、これを私の最適化に追加する必要があるということでした:

!code/allocation/variable

これにより、完全な最適化文字列は次のようになります。

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

これはProGuardのバグです。その最適化手順では、クラスファイル内のオプションの "LocalVariableTable"および "LocalVariableTypeTable"デバッグ属性が完全に正しく更新されない場合があります。 Dalvik VMは、デバッグ属性を明示的にチェックし、一貫性がない場合はクラスファイルを拒否します。

ProGuardの最新バージョンで問題が解決するかどうかを確認する必要があります。それ以外の場合は、ローカル変数の名前と型をクラスファイルから削除する必要があります。 Javaコンパイラに生成しないように要求できます(例: "javac -g:none")。それらを保持しないようにProGuardに要求することもできます( "-keepattributes LocalVariableTable、LocalVariableTypeTable ")。

22
Eric Lafortune

私はこれをWindowsで再浮上させただけですAndroid Studioで、Instant Runを無効にすると、再び動作します。

6
JaviCasa