Почему Java 7 Bytecode Verifier жалуется на этот стековый фрейм?
У меня есть метод, который я изменил в классе Java 7 (основная версия 51). С помощью javap
Я посмотрел на байт-код и карту стека кадров. Все выглядит хорошо
public int addOne(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: invokestatic #50 // Method isSomething:(I)Z
5: ifeq 12
8: iconst_0
9: goto 13
12: iconst_1
13: iadd
14: ireturn
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 12
locals = [ class test/Target, int ]
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class test/Target, int ]
stack = [ int, int ]
Этот верификатор выдает это исключение:
java.lang.VerifyError: Expecting a stackmap frame at branch target 12
Exception Details:
Location:
test/Target.addOne(I)I @5: ifeq
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: 1b03 b800 3299 0007 03a7 0004 0460 ac
Что сводит меня с ума, так это то, что компилятор сгенерировал тот же код из исходного кода Java, и он выглядит так:
public int addOne(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: invokestatic #16 // Method isSomething:(I)Z
5: ifeq 12
8: iconst_0
9: goto 13
12: iconst_1
13: iadd
14: ireturn
StackMapTable: number_of_entries = 2
frame_type = 76 /* same_locals_1_stack_item */
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class test/Target, int ]
stack = [ int, int ]
Обратите внимание, что единственная разница в карте стековых кадров состоит в том, что синтетическая карта имеет все полные кадры, но это не должно вызывать различий. Кто-нибудь знает, почему верфьеру может не понравиться моя синтетическая карта?
3 ответа
Я не могу воспроизвести эту проблему. Возможно, вы создаете стековые фреймы таким образом, что javap все равно будет читать, но на самом деле это не так? Потому что мой отредактированный класс имеет тот же вывод javap, но он проверяет просто отлично. Если вы опубликуете фактический файл класса, я смогу увидеть, смогу ли я найти проблему, потому что я не думаю, что есть что-то еще, что я могу сделать только с выводом JavaScript.
Источник:
public class ArrayTest {
public int addOne(int x){
return x + (isSomething(0) ? 0 : 1);
}
public static boolean isSomething(int z) {return true;}
}
Javap-вывод метода в оригинальном классе
public int addOne(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: invokestatic #2 // Method isSomething:(I)Z
5: ifeq 12
8: iconst_0
9: goto 13
12: iconst_1
13: iadd
14: ireturn
StackMapTable: number_of_entries = 2
frame_type = 76 /* same_locals_1_stack_item */
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class ArrayTest, int ]
stack = [ int, int ]
Вывод Javap метода в редактируемый класс
public int addOne(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: invokestatic #15 // Method ArrayTest.isSomething:(
)Z
5: ifeq 12
8: iconst_0
9: goto 13
12: iconst_1
13: iadd
14: ireturn
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 12
locals = [ class ArrayTest2, int ]
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class ArrayTest2, int ]
stack = [ int, int ]
Как видите, у меня тот же вывод Javap, но мой класс работает просто отлично.
Ответ в том, что javassist отстой, и я глубоко сожалею об этом.
StackMapTable
атрибут получен из вызова CodeAttribute.getAttribute(String tag)
, Даже при том, как вы получаете к нему доступ, нет никакого API, чтобы добавить его обратно, если это не тип StackMapTable
, Единственный API, который принимает ваниль AttributeInfo
в качестве параметра на MethodInfo
учебный класс.
В тех случаях, когда метод не нуждался (или не имел) фрейма стека, вы получаете null
, Если вы создаете AttributeInfo
структура для новой карты стека кадров, вы не должны добавлять MethodInfo
(где addAttribute
API есть), но CodeAttribute
где это принадлежит.
Это то, что я делал:
MethodInfo mi ...
AttributeInfo attr ...
mi.addAttribute(attr);
Вот что мне нужно было сделать:
CodeAttribute ca ...
ca.getAttributes().add(attr);
(Конечно, ca.getAttributes()
возвращает нетипизированный List
потому что мы все скучаем по 2004 году.)
Я копался в методе, который позволяет добавить тип StackFramMap
к CodeAttribute
и разобрался с этой работой из-за отсутствия универсального API.
Результатом использования верхней конструкции является то, что javap
создаст впечатление, что у вас есть надлежащий StackMapTable. Вы делаете, но он прикреплен к неправильному объекту, и вы не можете увидеть это из javap
выход.
Я не использовал ASM для своего проекта, потому что навязчивое использование шаблона посетителя показалось мне раздражающим. Теперь я признаю, что это было плохое решение. Поскольку у javassist нет обновлений с 2012 года, мне интересно, если проект мертв. У меня, конечно, есть куча ревизий, которые я бы отправил. Это бардак.
РЕДАКТИРОВАТЬ О вау. Внутренний код Javassist предполагает, что любой StackMapTable
приписать это свой внутренний StackMapTable
типа (потому что, как еще вы бы добавить StackMapTable
атрибуты). Я думаю, я мог бы создать свой собственный StackMapTable
Например, за исключением того, что конструкторы SFM защищены пакетами без видимой причины. Становится все хуже и хуже...
http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/html/javassist/bytecode/MethodInfo.html
Теперь вы можете позвонить:
mi.rebuildStackMapIf6(pool, cf);
Это восстановит карту стека, и не будет использовать или нуждаться -XX:-UseSplitVerifier