Ответ 1
Теория
Спецификация Java VM §4.10.1 (Проверка по проверке типов) указывает, что когда требуется фрейм карты стека. Сначала это дает неофициальное описание:
Предполагается, что в начале каждого базового блока в методе должен отображаться кадр карты стека. Кадр карты стека определяет тип проверки каждой записи стека операндов и каждой локальной переменной в начале каждого базового блока.
Подробная спецификация приведена в §4.10.1.6 (Тип Методы проверки с кодом). Кадры карты стека требуются командой goto
:
Неправильно иметь код после безусловной ветки без фрейма карты стека.
и всех других команд ветвления:
Ветвление к цели является безопасным по типу, если у объекта есть связанный стек стека, Frame и текущий стек стека, StackFrame, назначается Frame.
Также для начала обработчика исключений нужен кадр карты стека:
Инструкция удовлетворяет обработчику исключений, если состояние исходящего типа команд - это ExcStackFrame, а цель обработчика (исходная команда кода обработчика) безопасна в соответствии с типом состояния входящего типа T.
Наконец, §4.10.1.9 (Инструкции по проверке типов) указывает, какие инструкции требуют целевого объекта ветвления с фреймом карты стека. Ищите targetIsTypeSafe
в правилах типа; инструкции goto
, if*
, lookupswitch
и tableswitch
имеют его.
Пример
Даже для следующего кода требуются фреймы стоп-кадров:
public static class GuineaPig {
public GuineaPig() {
int i = 1;
if (i > 0) {
// code branch to require stackmap frames
}
}
}
Если они отсутствуют, код завершится с ошибкой:
java.lang.VerifyError: Expecting a stackmap frame at branch target 10
Exception Details:
Location:
net/orfjackal/retrolambda/Java6MissingStackMapFrameFixerTest$GuineaPig.<init>()V @7: ifle
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: 2ab7 000c 043c 1b9e 0003 b1
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2658)
at java.lang.Class.getConstructor0(Class.java:2964)
at java.lang.Class.newInstance(Class.java:403)
Вот байт-код:
public net.orfjackal.retrolambda.Java6MissingStackMapFrameFixerTest$GuineaPig();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: iconst_1
5: istore_1
6: iload_1
7: ifle 10
10: return
LineNumberTable:
line 22: 0
line 23: 4
line 24: 6
line 27: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lnet/orfjackal/retrolambda/Java6MissingStackMapFrameFixerTest$GuineaPig;
6 5 1 i I
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 10
locals = [ class net/orfjackal/retrolambda/Java6MissingStackMapFrameFixerTest$GuineaPig, int ]
stack = []
P.S. Мне потребовалось некоторое время, чтобы понять это, потому что по умолчанию я запускаю свои модульные тесты с охватом кода, а инструмент покрытия кода IDEA, по-видимому, автоматически пересчитывает фреймы стека для всех классов, что уменьшает мои усилия по тестированию, чтобы удалить их.