Версия JVM Spec 4.10.2.4 7, последний абзац, говорит
Действительная последовательность инструкций не должна иметь неинициализированный объект в стеке операндов или в локальной переменной в цели обратной ветви, если специальный тип неинициализированного объекта объединен со специальным типом, отличным от него самого.
Вот пример, отвергнутый верификатором — подозреваю, что его следует принять:
public scala.Tuple2<scala.runtime.Null$, scala.runtime.Null$> apply(boolean);
flags: ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: new #12 // class scala/Tuple2
3: dup
4: aconst_null
5: iload_1
6: ifne 5
9: aconst_null
10: invokespecial #16 // Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
13: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LC;
0 14 1 x Z
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 5
locals = [ class C, int ]
stack = [ uninitialized 0, uninitialized 0, null ]
Сообщение об ошибке жалуется на скачок назад ifne 5
java.lang.VerifyError: Uninitialized object exists on backward branch 5
Exception Details:
Location:
C.apply(Z)Lscala/Tuple2; @6: ifne
В стеке цели перехода действительно есть неинициализированный объект; однако мне кажется, что «особый тип неинициализированного объекта» объединен с самим собой, как того требует спецификация.
Я думаю, что есть только один кадр карты стека, поэтому его нельзя объединить ни с чем другим.
Интересно, что в JVM Spec версии 8.
Однако Verifier в виртуальной машине Java 8 по-прежнему отклоняет пример.
Я неправильно прочитал спецификацию JVM или пример действительно не прошел проверку? Пробовал версии 1.7.0_60-b19
и 1.8.0_05-b13
.
Общие исследования
Проблема проявляется в Scala (отчет об ошибке). Чтобы воспроизвести, возьмите scala 2.11.1, убедитесь, что вы используете JVM >= 1.7, и выполните следующее (обязательно передайте -target:jvm-1.7
в scala):
localhost:sandbox luc$ scala -target:jvm-1.7
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class C {
| def apply(x: Boolean) = new Tuple2(null, {
| while (x) { }
| null
| })
| }
defined class C
scala> new C
java.lang.VerifyError: Uninitialized object exists on backward branch 5
Exception Details:
Location:
C.apply(Z)Lscala/Tuple2; @6: ifne
Reason:
Error exists in the bytecode
Bytecode:
0000000: bb00 0959 011b 9aff ff01 b700 0db0
Stackmap Table:
full_frame(@5,{Object[#2],Integer},{Uninitialized[#0],Uninitialized[#0],Null})
... 32 elided
Как упоминалось выше - отчет об ошибке JDK здесь, я надеюсь получить там ответ.
Ошибка JDK была исправлена в JDK 8u25.
Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55
- попробую другие. - person Lukas Rytz   schedule 05.06.2014Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0)
- person ggovan   schedule 05.06.2014Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05
и получил тот же сбой. @ggovan, ты запускал интерпретатор scala, используя-target:jvm-1.7
? - person Lukas Rytz   schedule 05.06.2014-target:jvm-1.7
тоже ломает мой. - person ggovan   schedule 05.06.2014-target:jvm-1.7
в scala, то он создаст файл класса с версией 50 (версия для JDK 1.6) вместо 51. В этом случае верификатор не обеспечивает правильность кадров карты стека (см. спецификацию JVM 4.10 .1), ошибка не срабатывает. - person Lukas Rytz   schedule 06.06.2014StackMapTable
не удалась). Для файлов класса ›50StackMapTable
являются обязательными, и возврат к верификатору вывода типа не разрешен. Следовательно, только JVM Spec § Применяется 4.10.1 (см. первые два предложения). Обратите внимание, что §4.9.2 также содержит ограничение на обратную ветвь, и оно также было удалено для Java 8. Но кажется, что §4.10.1 никогда не имел этого ограничения. - person Holger   schedule 27.06.2014