Как избежать ошибки VerifyError: «Ожидается найти унифицированный объект в стеке» для уже инициализированных объектов

Я разрабатываю механизм инструментирования с помощью ASM, и мне нужно перехватить вызов методов, которые получают параметры типа массива. Для этой цели я реализовал MethodVisitor и в его visitMethodInsn я проверяю, указывает ли параметр desc какой-либо параметр типа массива. Если у целевого метода нет параметров типа массива, то мне делать нечего и я вставляю исходный вызов метода: super.visitMethodInsn(opcode, owner, name, desc);

С другой стороны, если целевой метод имеет параметры типа массива, то я должен выполнить определенное действие для его аргументов. Самым простым решением, которое я получил для доступа к каждому аргументу, было вызвать метод посредника с тем же дескриптором целевого метода, и в этом посреднике я могу легко получить доступ к его параметрам (соответствующим аргументам, переданным целевому методу).

Однако возникает проблема, когда целевой метод является конструктором экземпляра (<init>). В Java new XXX() переводится в байт-код следующим образом:

  NEW XXX 
  DUP 
  INVOKESPECIAL XXXX.<init>()

В соответствии с подходом, который я объяснил выше, я перемещаю вызов INVOKESPECIAL в метод посредника, и вновь созданный экземпляр объекта является первым аргументом этого посредника. Тем не менее, верификатор выдает ошибку для этого посредника, сообщая, что первый параметр посредника (который будет первым аргументом целевого метода — <init>) не является «унифицированным объектом». Точнее я получил ошибку: Exception in thread "main" java.lang.VerifyError: Expecting to find unitialized object on stack”.

Как только я разделяю байт-коды NEW и INVOKESPECIAL двумя разными методами, верификатор утверждает, что аргумент, переданный INVOKESPECIAL, уже инициализирован.

Любое предложение обойти эту проблему? (пожалуйста, не отвечайте мне, чтобы избежать посредника и получить прямой доступ к аргументам в стеке, потому что не так просто дублировать и заменять аргументы, которые занимают произвольную позицию в стеке.)


person Miguel Gamboa    schedule 05.11.2012    source источник
comment
что вы подразумеваете под методом посредника?   -  person vijay    schedule 06.11.2012
comment
Представьте, что в какой-то момент мой инструментарий перехватывает вызов целевого метода: obj.foo(x). Итак, на данный момент я заменяю этот вызов: obj.foo(x) на: mediator$foo(obj, x) — в данном случае mediator$foo — это новый статический метод в вызывающем классе. Поэтому mediator$foo(obj, x) делает то, что ему нужно, а затем вызывает цель: obj.foo(x).   -  person Miguel Gamboa    schedule 07.11.2012
comment
Является ли метод посредника статическим или методом экземпляра?   -  person vijay    schedule 08.11.2012
comment
Медиатор всегда является статическим методом.   -  person Miguel Gamboa    schedule 08.11.2012


Ответы (1)


Верификатор прав, чтобы отклонить ваш код (см. спецификацию JVM). Невозможно обойти проверку байт-кода.

Один из способов — встроить код посредника в момент вызова конструктора. Вы по-прежнему можете вызывать части посредника как вызовы методов до или после вызова конструктора, но сам вызов конструктора должен быть в том же методе, что и новая инструкция.

Другой способ — создать специальные посредники для каждого экземпляра класса, чтобы посредники сами вызывали инструкцию new вместе с вызовом конструктора.

Вы также можете взглянуть на существующие библиотеки АОП, если они могут правильно выполнять требуемую работу.

person Alexei Kaigorodov    schedule 05.11.2012