Наскоро се натъкнах, докато разработвах библиотека, която извършва операции върху 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 изпълняват ли ги? Защо получавам VerifyError
s? Отписване? И имат ли предимство пред документираните си аналози?
Всяко прозрение ще бъде високо оценено.