Компиляция с 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
Как я могу исправить эту проблему?
Ответы
Ответ 1
Фактическая часть Proguard заканчивается, но затем dex больше не может преобразовать полученный байт-код. Dex считает неверным LocalVariableTable
. Эрик Лафортунэ - лучший источник объяснения, почему (см. Его ответ).
Проблема уходит, если вы не только не запутываете, но и пропустите шаг оптимизации (-dontoptimize
). Но вы хотите иметь это для уменьшения размера. Другой способ решить это - сбросить флаги отладки в javac
и в dex
. Единственная проблема заключается в том, что тогда у вас тоже не было бы правильных стеков. Вы получите строки stacktrace без информации о файле или номерах строк, например:
net.lp.collectionista.domain.items.book.BookItem.getCoverImageForFormField(Unknown Source)
Вы можете сделать это, добавив debug="false"
в тег javac
в ant 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
(если вы обфускатируете, это само по себе может также вызвать у вас проблемы). Обратите внимание, что второе подразумевает первое, даже если из его имени может быть неясно, что оно влияет на атрибуты.
Лично, и с учетом других проблем с Proguard я решил сделать обфускацию, но уменьшил потерю информации о стеке. Я еще не пробовал предложение @plowman.
Для получения более подробной информации вы можете найти файлы с контролируемыми версиями здесь:
Ответ 2
Я столкнулся с той же проблемой после добавления флага -dontobfuscate в файл proguard.cfg.
В итоге оказалось, что мне нужно добавить это к моим оптимизации:
!code/allocation/variable
Это делает мою полную строку оптимизации такой:
-optimizations !field/removal/writeonly,!field/marking/private,!class/merging/*,!code/allocation/variable
Ответ 3
Это ошибка в ProGuard. Его шаг оптимизации иногда не полностью корректно обновляет необязательные атрибуты отладки LocalVariableTable и LocalVariableTypeTable внутри файлов классов. Далвик VM явно проверяет атрибуты отладки и отклоняет файлы классов, если они несовместимы.
Вы должны проверить, исправлена ли последняя версия ProGuard. В противном случае вы должны удалить локальные имена переменных и типы из файлов классов. Вы можете попросить java-компилятор не генерировать их (например, "javac -g: none" ). Вы также можете попросить ProGuard не хранить их (не указывать "-keepattributes LocalVariableTable, LocalVariableTypeTable" ).
Ответ 4
У меня только что появилось это на Windows "Android Studio", и отключение Instant Run заставило все работать снова.