I’ve recently come across while developing a library that performs operations on JVM bytecode some opcodes on which there is no documentation (that I’ve found), yet which are recognized by the JVM reference implementation. I’ve found a list of these, and they are:
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;
They seem to be replacements for their other implementations, yet have different opcodes. After a long period of trawling page after page through Google, I came across a mention of the LDC*_QUICK opcodes in this document.
Quote from it on the LDC_QUICK opcode:
Operation Push item from constant pool
Forms
ldc_quick = 203 (0xcb)Stack … …, item
Description The index is an unsigned byte that must be a valid index into the constant pool of the current class (§3.6). The constant
pool item at index must have already been resolved and must be one
word wide. The item is fetched from the constant pool and pushed onto
the operand stack.Notes The opcode of this instruction was originally ldc. The operand of the ldc instruction is not modified.
Alright. Seemed interesting, and so I decided to try it out. LDC_QUICK seems to have the same format as LDC, so I proceeded into changing a LDC opcode to a LDC_QUICK one. This resulted in a failure, though the JVM obviously recognized it. After attempting to run the modified file, the JVM crashed with the following output:
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)
The above error gives mixed messages. Obviously, class file verification failed: java.lang.VerifyError: Bad instruction: cc. At the same time, the JVM recognized the opcode: @9: fast_bgetfield. Additionally, it seems to think that it is a different instruction, because fast_bgetfield does not imply constant pushing…
I think its fair to say I am quite confused. What are these illegal opcodes? Do JVM’s run them? Why am I receiving VerifyErrors? Deprecation? And do they have an advantage over their documented counterparts?
Any insight would be greatly appreciated.
Chapter 8 of Inside the Java Virtual Machine
Basically you cannot just put the opcode in the class file. Only the JVM can do that after it resolves the operands.