如果您尝试将指令操作码映射到指令大小,您将得到以下令人沮丧的表格:
0 - 15 1 bytes
16 2 bytes
17 3 bytes
18 2 bytes
19 - 20 3 bytes
21 - 25 2 bytes
26 - 53 1 bytes
54 - 58 2 bytes
59 - 131 1 bytes
132 3 bytes
133 - 152 1 bytes
153 - 168 3 bytes
169 2 bytes
170 - 171 special handling
172 - 177 1 bytes
178 - 184 3 bytes
185 - 186 5 bytes
187 3 bytes
188 2 bytes
189 3 bytes
190 - 191 1 bytes
192 - 193 3 bytes
194 - 195 1 bytes
196 special handling
197 4 bytes
198 - 199 3 bytes
200 - 201 5 bytes
换句话说,指令的数值和位模式中没有编码大小信息,但是还有另一个属性,您可以考虑某种模式:在大约 200 条定义的指令中,大约 150 条指令的大小为一个字节,只剩下大约 50 条需要任何处理的指令。甚至这一小组指令也可以进一步细分为逻辑组,大多数占三个字节,第二大组占两个字节。
因此,快速执行指令的方法代码可能如下所示:
static void readByteCode(ByteBuffer bb) {
while(bb.hasRemaining()) {
switch(bb.get()&0xff) {
case BIPUSH: // one byte embedded constant
case LDC: // one byte embedded constant pool index
// follow-up: one byte embedded local variable index
case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD:
case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: case RET:
case NEWARRAY: // one byte embedded array type
bb.get();
break;
case IINC: // one byte local variable index, another one for the constant
case SIPUSH: // two bytes embedded constant
case LDC_W: case LDC2_W: // two bytes embedded constant pool index
// follow-up: two bytes embedded branch offset
case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE:
case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE:
case IF_ICMPGT: case IF_ICMPLE: case IF_ACMPEQ: case IF_ACMPNE:
case GOTO: case JSR: case IFNULL: case IFNONNULL:
// follow-up: two bytes embedded constant pool index to member or type
case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD:
case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case NEW:
case ANEWARRAY: case CHECKCAST: case INSTANCEOF:
bb.getShort();
break;
case MULTIANEWARRAY:// two bytes pool index, one byte dimension
bb.getShort();
bb.get();
break;
// follow-up: two bytes embedded constant pool index to member, two reserved
case INVOKEINTERFACE: case INVOKEDYNAMIC:
bb.getShort();
bb.getShort();
break;
case GOTO_W: case JSR_W:// four bytes embedded branch offset
bb.getInt();
break;
case LOOKUPSWITCH:
// special handling left as an exercise for the reader...
break;
case TABLESWITCH:
// special handling left as an exercise for the reader...
break;
case WIDE:
int widened=bb.get()&0xff;
bb.getShort(); // local variable index
if(widened==IINC) {
bb.getShort(); // constant offset value
}
break;
default: // one of the ~150 instructions taking one byte
}
}
}
我故意将一些指令分开,具有相同数量的后续字节,但含义不同。毕竟,我猜你想在某些地方插入一些实际的逻辑。
请注意,这两个switch
字节码指令的处理被忽略了,它们需要填充,其实现需要了解缓冲区内的代码对齐情况,这由调用者控制。所以这取决于你的具体应用。lookupswitch
请参阅和的文档tableswitch
。
当然,处理所有单字节指令default
意味着代码不会捕获未知或无效指令。如果你想要安全,你必须插入箱子……</p>