Недопустимые коды операций в JVM

Недавно при разработке библиотеки, которая выполняет операции с байт-кодом JVM, я столкнулся с некоторыми кодами операций, по которым нет документации (которую я нашел), но которые распознаются эталонной реализацией JVM. Я нашел их список, и они следующие:

BREAKPOINT = 202;
LDC_QUICK = 203;
LDC_W_QUICK = 204;
LDC2_W_QUICK = 205;
GETFIELD_QUICK = 206;
PUTFIELD_QUICK = 207;
GETFIELD2_QUICK = 208;
PUTFIELD2_QUICK = 209;
GETSTATIC_QUICK = 210;
PUTSTATIC_QUICK = 211;
GETSTATIC2_QUICK = 212;
PUTSTATIC2_QUICK = 213;
INVOKEVIRTUAL_QUICK = 214;
INVOKENONVIRTUAL_QUICK = 215;
INVOKESUPER_QUICK = 216;
INVOKESTATIC_QUICK = 217;
INVOKEINTERFACE_QUICK = 218;
INVOKEVIRTUALOBJECT_QUICK = 219;
NEW_QUICK = 221;
ANEWARRAY_QUICK = 222;
MULTIANEWARRAY_QUICK = 223;
CHECKCAST_QUICK = 224;
INSTANCEOF_QUICK = 225;
INVOKEVIRTUAL_QUICK_W = 226;
GETFIELD_QUICK_W = 227;
PUTFIELD_QUICK_W = 228;
IMPDEP1 = 254;
IMPDEP2 = 255;

Они кажутся заменой другим своим реализациям, но имеют разные коды операций. После долгого пролистывания страницы за страницей в Google я наткнулся на упоминание кодов операций LDC*_QUICK в этот документ.

Цитата из него по опкоду LDC_QUICK:

Операция Отправка элемента из постоянного пула

Формы ldc_quick = 203 (0xcb)

Стопка ... ..., элемент

Описание Индекс — это байт без знака, который должен быть допустимым индексом в пуле констант текущего класса (§3.6). Постоянный элемент пула по индексу уже должен быть разрешен и должен иметь ширину в одно слово. Элемент извлекается из пула констант и помещается в стек операндов.

Примечания Код операции этой инструкции изначально был ldc. Операнд инструкции ldc не изменяется.

Хорошо. Мне это показалось интересным, и я решил попробовать. LDC_QUICK имеет тот же формат, что и LDC, поэтому я изменил код операции LDC на код операции LDC_QUICK. Это привело к сбою, хотя JVM явно его распознала. После попытки запустить измененный файл JVM аварийно завершилась со следующим выводом:

Exception in thread "main" java.lang.VerifyError: Bad instruction: cc
Exception Details:
  Location:
    Test.main([Ljava/lang/String;)V @9: fast_bgetfield
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: bb00 0559 b700 064c 2bcc 07b6 0008 572b
    0000010: b200 09b6 000a 5710 0ab8 000b 08b8 000c
    0000020: 8860 aa00 0000 0032 0000 0001 0000 0003
    0000030: 0000 001a 0000 0022 0000 002a b200 0d12
    0000040: 0eb6 000f b200 0d12 10b6 000f b200 0d12
    0000050: 11b6 000f bb00 1259 2bb6 0013 b700 14b8
    0000060: 0015 a700 104d 2cb6 0016 b200 0d12 17b6
    0000070: 000f b1
  Exception Handler Table:
    bci [84, 98] => handler: 101
  Stackmap Table:
    append_frame(@60,Object[#41])
    same_frame(@68)
    same_frame(@76)
    same_frame(@84)
    same_locals_1_stack_item_frame(@101,Object[#42])
    same_frame(@114)

        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

Приведенная выше ошибка дает смешанные сообщения. Очевидно, проверка файла класса не удалась: java.lang.VerifyError: Bad instruction: cc. При этом JVM распознала код операции: @9: fast_bgetfield. Кроме того, кажется, что это другая инструкция, потому что fast_bgetfield не подразумевает постоянное нажатие...

Думаю, будет справедливо сказать, что я совершенно сбит с толку. Что это за незаконные коды операций? Запускают ли их JVM? Почему я получаю VerifyErrors? Устаревание? И есть ли у них преимущество перед документально оформленными аналогами?

Мы будем очень признательны за любое понимание.


person Xyene    schedule 11.02.2013    source источник


Ответы (3)


В первом издании спецификации виртуальной машины Java описан метод, используемый одной из ранних реализаций виртуальной машины Java от Sun для ускорения интерпретации байт-кодов. В этой схеме коды операций, которые относятся к записям пула констант, заменяются кодом операции _quick при разрешении записи пула констант. Когда виртуальная машина встречает инструкцию _quick, она знает, что запись пула констант уже разрешена, и поэтому может выполнить инструкцию быстрее.

Основной набор инструкций виртуальной машины Java состоит из 200 однобайтовых кодов операций. Эти 200 кодов операций — единственные коды операций, которые вы когда-либо увидите в файлах классов. Реализации виртуальных машин, использующие метод _quick, используют внутри себя еще 25 однобайтовых кодов операций, коды операций _quick.

Например, когда виртуальная машина, использующая метод _quick, разрешает запись пула констант, на которую ссылается инструкция ldc (значение кода операции 0x12), она заменяет байт кода операции ldc в потоке байт-кода инструкцией ldc_quick (значение кода операции 0xcb). Этот метод является частью процесса замены символической ссылки прямой ссылкой в ​​ранней виртуальной машине Sun.

Для некоторых инструкций, помимо перезаписи обычного кода операции кодом операции _quick, виртуальная машина, использующая технику _quick, перезаписывает операнды инструкции данными, представляющими прямую ссылку. Например, в дополнение к замене кода операции invokevirtual на invokevirtual_quick виртуальная машина также помещает смещение таблицы методов и количество аргументов в два байта операнда, которые следуют за каждой инструкцией invokevirtual. Размещение смещения таблицы методов в потоке байт-кода после кода операции invokevirtual_quick экономит виртуальной машине время, необходимое для поиска смещения в разрешенной записи пула констант.

Глава 8 внутренней части виртуальной машины Java

По сути, вы не можете просто поместить код операции в файл класса. Только JVM может сделать это после разрешения операндов.

person jdb    schedule 12.02.2013

Я не знаю обо всех кодах операций, которые вы перечислили, но три из них — breakpoint, impdep1 и impdep2 — являются зарезервированными кодами операций. документировано в разделе 6.2 Java Virtual Спецификация машины. В нем говорится, в частности:

Два зарезервированных кода операции с номерами 254 (0xfe) и 255 (0xff) имеют мнемоники impdep1 и impdep2 соответственно. Эти инструкции предназначены для обеспечения лазеек или ловушек для функций, специфичных для реализации, реализованных в программном и аппаратном обеспечении соответственно. Третий зарезервированный код операции, номер 202 (0xca), имеет мнемоническое обозначение точка останова и предназначен для использования отладчиками для реализации точек останова.

Хотя эти коды операций зарезервированы, их можно использовать только внутри реализации виртуальной машины Java. Они не могут появляться в допустимых файлах классов. . . .

Я подозреваю (из их названий), что остальные другие коды операций являются частью механизма JIT и также не могут появляться в допустимом файле класса.

person Ted Hopp    schedule 11.02.2013

Эти коды операций зарезервированы и не могут отображаться в допустимом файле класса, поэтому VerifyError. Однако JVM использует их внутри. Поэтому представление некоторого байт-кода в памяти может содержать эти коды операций после модификации виртуальной машиной. Однако это чисто детали реализации.

person Antimony    schedule 11.02.2013