ASM - странный индекс localVar с использованием newLocal из LocalVariableSorter

Я добавляю новых местных жителей через newLocal из LocalVariableSorter. Метод, к которому я добавляю локальные переменные, является методом экземпляра с длинным параметром. Я добавляю двух местных жителей; один длинный, один объект. В примере кода нет других локальных переменных.

В результате я бы ожидал следующие слоты/индексы:

0 - this
1 - the long param
3 - my 1st local added via `newLocal` - using two slots as it is a long
5 - my 2nd local added via `newLocal`

То, что я получаю в качестве возврата от newLocal, это 3 и 7. Почему такой большой разрыв?

И что еще более странно, когда я добавляю инструкции xSTORE, используя эти индексы, и проверяю результат с помощью javap, он показывает мне:

LSTORE 5
ASTORE 8

Примечание. Мало того, что значения отличаются от тех, которые я передал в инструкцию xSTORE, так еще и разрыв между ними теперь равен 3, а не 4, как раньше.

Однако полученный код работает. Я просто хотел бы понять, что здесь происходит магия и почему.

Спасибо


person Haasip Satang    schedule 02.05.2018    source источник
comment
Не могли бы вы опубликовать свой код? Кроме того, вы уверены, что не вызываете ничего между ними, что неявно добавило бы местных жителей? Попробуйте создать подкласс LocalVariableSorter и добавить оператор печати в newLocal.   -  person Antimony    schedule 03.05.2018


Ответы (1)


Класс LocalVariableSorter имеет такой дизайн, что его очень легко использовать неправильно.

При вызове на нем методов, определенных MethodVisitor API, локальные переменные подвергаются перенумерации, указанной в документация класса.

Таким образом, при использовании с ClassReader посещенный старый код преобразуется. Поскольку вы не хотите, чтобы внедренный новый код подвергался этому преобразованию, а чтобы использовать вновь определенные переменные, вы должны обойти методы LocalVariableSorter и вызвать базовую цель MethodVisitor.

Когда вы вызываете visitVarInsn(LSTORE, 3) для LocalVariableSorter, она обрабатывается как старая инструкция, относящаяся к индексу 3, и поскольку вы вводите новую переменную, занимающую индексы 3 и 4, «старая переменная» с индексом 3 переназначается на следующий свободный индекс, т.е. 56). Затем, когда вы определяете свою следующую новую переменную, она получает индекс 7, а вызов visitVarInsn(ASTORE, 7) для LocalVariableSorter обрабатывается как старая переменная, которая конфликтует с вашей новой переменной, поэтому она переназначается на 8.

Это поведение точно соответствует тому, что говорится в первом предложении документации класса:

LocalVariablesSorter

MethodVisitor, который перенумеровывает локальные переменные в порядке их появления.

Таким образом, хотя вам нужно вызвать newLocal для LocalVariableSorter, чтобы создать новую переменную, которая не будет переназначена, вы должны вызвать методы visit… для исходной, обернутой MethodVisitor, чтобы использовать ее. Когда вы используете подкласс GeneratorAdapter, вы можете использовать его недавно определенные методы (те, которые не начинаются с visit…) для создания новых инструкций, которые не преобразуются, но для меня это сделало бы дело еще хуже, имея методы для преобразования инструкций и создания непреобразованные инструкции для одного и того же класса и всегда нужно помнить, что префикс visit… имеет значение. Для некоторых методов вам все равно потребуется доступ к исходному посетителю метода, как описано в этом ответе, в котором рассматривается visitLocalVariable для создания отладочная информация для созданной переменной.

person Holger    schedule 03.05.2018
comment
Ах, кстати: вы видите какие-либо проблемы с тем, чтобы не настроить мой код для вызова методов visit... в исходном, обернутом MethodVisitor, но оставить пробелы в локальном подсчете? Хотя это и некрасиво, но работает. Просто интересно, могут ли быть побочные эффекты. - person Haasip Satang; 03.05.2018
comment
Это работает только до тех пор, пока код, который вы преобразуете, не использует эти переменные. Дело в том, что вы зарезервировали определенные индексы, и LocalVariablesSorter переназначит старый код, чтобы он не использовал эти индексы. Когда вы используете разные индексы, вы не только теряете эту защиту, вы делаете бессмысленным использование LocalVariablesSorter. Когда вы заранее знаете, какие индексы определенно не используются, например. при создании совершенно нового кода вам вообще не нужно использовать LocalVariablesSorter, вы можете просто использовать эти индексы с MethodVisitor, возвращаемым ClassWriter - person Holger; 03.05.2018