Если вы попытаетесь сопоставить коды операций инструкций с размерами инструкций, вы получите следующую обескураживающую таблицу:
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
< /a> и tableswitch
а>.
Конечно, обработка всех однобайтовых инструкций как default
подразумевает, что код не будет перехватывать неизвестные или недопустимые инструкции. Если вы хотите безопасности, вам придется вставить чехлы…
person
Holger
schedule
27.06.2016