pluotsorbet/bytecodes.ts

1179 строки
48 KiB
TypeScript

/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
module J2ME.Bytecode {
import assert = Debug.assert;
export class Bytes {
/**
* Gets a signed 1-byte value.
*/
public static beS1(data: Uint8Array, bci: number) {
return (data[bci] << 24) >> 24;
}
/**
* Gets a signed 2-byte big-endian value.
*/
public static beS2(data: Uint8Array, bci: number) {
return ((data[bci] << 8) | (data[bci + 1] & 0xff)) << 16 >> 16;
}
/**
* Gets an unsigned 1-byte value.
*/
public static beU1(data: Uint8Array, bci: number) {
return data[bci] & 0xff;
}
/**
* Gets an unsigned 2-byte big-endian value.
*/
public static beU2(data: Uint8Array, bci: number) {
return ((data[bci] & 0xff) << 8) | (data[bci + 1] & 0xff);
}
/**
* Gets a signed 4-byte big-endian value.
*/
public static beS4(data: Uint8Array, bci: number) {
return (data[bci] << 24) | ((data[bci + 1] & 0xff) << 16) | ((data[bci + 2] & 0xff) << 8) | (data[bci + 3] & 0xff);
}
/**
* Gets either a signed 2-byte or a signed 4-byte big-endian value.
*/
public static beSVar(data: Uint8Array, bci: number, fourByte: boolean) {
if (fourByte) {
return Bytes.beS4(data, bci);
} else {
return Bytes.beS2(data, bci);
}
}
}
export enum Condition {
/**
* Equal.
*/
EQ,
/**
* Not equal.
*/
NE,
/**
* Signed less than.
*/
LT,
/**
* Signed less than or equal.
*/
LE,
/**
* Signed greater than.
*/
GT,
/**
* Signed greater than or equal.
*/
GE,
/**
* Unsigned greater than or equal ("above than or equal").
*/
AE,
/**
* Unsigned less than or equal ("below than or equal").
*/
BE,
/**
* Unsigned greater than ("above than").
*/
AT,
/**
* Unsigned less than ("below than").
*/
BT,
/**
* Operation produced an overflow.
*/
OF,
/**
* Operation did not produce an overflow.
*/
NOF
}
/**
* The definitions of the bytecodes that are valid input to the compiler and
* related utility methods. This comprises two groups: the standard Java
* bytecodes defined by <a href=
* "http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html">
* Java Virtual Machine Specification</a>, and a set of <i>extended</i>
* bytecodes that support low-level programming, for example, memory barriers.
*
* The extended bytecodes are one or three bytes in size. The one-byte bytecodes
* follow the values in the standard set, with no gap. The three-byte extended
* bytecodes share a common first byte and carry additional instruction-specific
* information in the second and third bytes.
*/
export enum Bytecodes {
NOP = 0, // 0x00
ACONST_NULL = 1, // 0x01
ICONST_M1 = 2, // 0x02
ICONST_0 = 3, // 0x03
ICONST_1 = 4, // 0x04
ICONST_2 = 5, // 0x05
ICONST_3 = 6, // 0x06
ICONST_4 = 7, // 0x07
ICONST_5 = 8, // 0x08
LCONST_0 = 9, // 0x09
LCONST_1 = 10, // 0x0A
FCONST_0 = 11, // 0x0B
FCONST_1 = 12, // 0x0C
FCONST_2 = 13, // 0x0D
DCONST_0 = 14, // 0x0E
DCONST_1 = 15, // 0x0F
BIPUSH = 16, // 0x10
SIPUSH = 17, // 0x11
LDC = 18, // 0x12
LDC_W = 19, // 0x13
LDC2_W = 20, // 0x14
ILOAD = 21, // 0x15
LLOAD = 22, // 0x16
FLOAD = 23, // 0x17
DLOAD = 24, // 0x18
ALOAD = 25, // 0x19
ILOAD_0 = 26, // 0x1A
ILOAD_1 = 27, // 0x1B
ILOAD_2 = 28, // 0x1C
ILOAD_3 = 29, // 0x1D
LLOAD_0 = 30, // 0x1E
LLOAD_1 = 31, // 0x1F
LLOAD_2 = 32, // 0x20
LLOAD_3 = 33, // 0x21
FLOAD_0 = 34, // 0x22
FLOAD_1 = 35, // 0x23
FLOAD_2 = 36, // 0x24
FLOAD_3 = 37, // 0x25
DLOAD_0 = 38, // 0x26
DLOAD_1 = 39, // 0x27
DLOAD_2 = 40, // 0x28
DLOAD_3 = 41, // 0x29
ALOAD_0 = 42, // 0x2A
ALOAD_1 = 43, // 0x2B
ALOAD_2 = 44, // 0x2C
ALOAD_3 = 45, // 0x2D
IALOAD = 46, // 0x2E
LALOAD = 47, // 0x2F
FALOAD = 48, // 0x30
DALOAD = 49, // 0x31
AALOAD = 50, // 0x32
BALOAD = 51, // 0x33
CALOAD = 52, // 0x34
SALOAD = 53, // 0x35
ISTORE = 54, // 0x36
LSTORE = 55, // 0x37
FSTORE = 56, // 0x38
DSTORE = 57, // 0x39
ASTORE = 58, // 0x3A
ISTORE_0 = 59, // 0x3B
ISTORE_1 = 60, // 0x3C
ISTORE_2 = 61, // 0x3D
ISTORE_3 = 62, // 0x3E
LSTORE_0 = 63, // 0x3F
LSTORE_1 = 64, // 0x40
LSTORE_2 = 65, // 0x41
LSTORE_3 = 66, // 0x42
FSTORE_0 = 67, // 0x43
FSTORE_1 = 68, // 0x44
FSTORE_2 = 69, // 0x45
FSTORE_3 = 70, // 0x46
DSTORE_0 = 71, // 0x47
DSTORE_1 = 72, // 0x48
DSTORE_2 = 73, // 0x49
DSTORE_3 = 74, // 0x4A
ASTORE_0 = 75, // 0x4B
ASTORE_1 = 76, // 0x4C
ASTORE_2 = 77, // 0x4D
ASTORE_3 = 78, // 0x4E
IASTORE = 79, // 0x4F
LASTORE = 80, // 0x50
FASTORE = 81, // 0x51
DASTORE = 82, // 0x52
AASTORE = 83, // 0x53
BASTORE = 84, // 0x54
CASTORE = 85, // 0x55
SASTORE = 86, // 0x56
POP = 87, // 0x57
POP2 = 88, // 0x58
DUP = 89, // 0x59
DUP_X1 = 90, // 0x5A
DUP_X2 = 91, // 0x5B
DUP2 = 92, // 0x5C
DUP2_X1 = 93, // 0x5D
DUP2_X2 = 94, // 0x5E
SWAP = 95, // 0x5F
IADD = 96, // 0x60
LADD = 97, // 0x61
FADD = 98, // 0x62
DADD = 99, // 0x63
ISUB = 100, // 0x64
LSUB = 101, // 0x65
FSUB = 102, // 0x66
DSUB = 103, // 0x67
IMUL = 104, // 0x68
LMUL = 105, // 0x69
FMUL = 106, // 0x6A
DMUL = 107, // 0x6B
IDIV = 108, // 0x6C
LDIV = 109, // 0x6D
FDIV = 110, // 0x6E
DDIV = 111, // 0x6F
IREM = 112, // 0x70
LREM = 113, // 0x71
FREM = 114, // 0x72
DREM = 115, // 0x73
INEG = 116, // 0x74
LNEG = 117, // 0x75
FNEG = 118, // 0x76
DNEG = 119, // 0x77
ISHL = 120, // 0x78
LSHL = 121, // 0x79
ISHR = 122, // 0x7A
LSHR = 123, // 0x7B
IUSHR = 124, // 0x7C
LUSHR = 125, // 0x7D
IAND = 126, // 0x7E
LAND = 127, // 0x7F
IOR = 128, // 0x80
LOR = 129, // 0x81
IXOR = 130, // 0x82
LXOR = 131, // 0x83
IINC = 132, // 0x84
I2L = 133, // 0x85
I2F = 134, // 0x86
I2D = 135, // 0x87
L2I = 136, // 0x88
L2F = 137, // 0x89
L2D = 138, // 0x8A
F2I = 139, // 0x8B
F2L = 140, // 0x8C
F2D = 141, // 0x8D
D2I = 142, // 0x8E
D2L = 143, // 0x8F
D2F = 144, // 0x90
I2B = 145, // 0x91
I2C = 146, // 0x92
I2S = 147, // 0x93
LCMP = 148, // 0x94
FCMPL = 149, // 0x95
FCMPG = 150, // 0x96
DCMPL = 151, // 0x97
DCMPG = 152, // 0x98
IFEQ = 153, // 0x99
IFNE = 154, // 0x9A
IFLT = 155, // 0x9B
IFGE = 156, // 0x9C
IFGT = 157, // 0x9D
IFLE = 158, // 0x9E
IF_ICMPEQ = 159, // 0x9F
IF_ICMPNE = 160, // 0xA0
IF_ICMPLT = 161, // 0xA1
IF_ICMPGE = 162, // 0xA2
IF_ICMPGT = 163, // 0xA3
IF_ICMPLE = 164, // 0xA4
IF_ACMPEQ = 165, // 0xA5
IF_ACMPNE = 166, // 0xA6
GOTO = 167, // 0xA7
JSR = 168, // 0xA8
RET = 169, // 0xA9
TABLESWITCH = 170, // 0xAA
LOOKUPSWITCH = 171, // 0xAB
IRETURN = 172, // 0xAC
LRETURN = 173, // 0xAD
FRETURN = 174, // 0xAE
DRETURN = 175, // 0xAF
ARETURN = 176, // 0xB0
RETURN = 177, // 0xB1
GETSTATIC = 178, // 0xB2
PUTSTATIC = 179, // 0xB3
GETFIELD = 180, // 0xB4
PUTFIELD = 181, // 0xB5
INVOKEVIRTUAL = 182, // 0xB6
INVOKESPECIAL = 183, // 0xB7
INVOKESTATIC = 184, // 0xB8
INVOKEINTERFACE = 185, // 0xB9
XXXUNUSEDXXX = 186, // 0xBA
NEW = 187, // 0xBB
NEWARRAY = 188, // 0xBC
ANEWARRAY = 189, // 0xBD
ARRAYLENGTH = 190, // 0xBE
ATHROW = 191, // 0xBF
CHECKCAST = 192, // 0xC0
INSTANCEOF = 193, // 0xC1
MONITORENTER = 194, // 0xC2
MONITOREXIT = 195, // 0xC3
WIDE = 196, // 0xC4
MULTIANEWARRAY = 197, // 0xC5
IFNULL = 198, // 0xC6
IFNONNULL = 199, // 0xC7
GOTO_W = 200, // 0xC8
JSR_W = 201, // 0xC9
BREAKPOINT = 202, // 0xCA
ALOAD_ILOAD = 210,
IINC_GOTO = 211,
ARRAYLENGTH_IF_ICMPGE = 212,
RESOLVED_GETFIELD = 213,
RESOLVED_PUTFIELD = 214,
RESOLVED_INVOKEVIRTUAL = 215,
ILLEGAL = 255,
END = 256,
/**
* The last opcode defined by the JVM specification. To iterate over all JVM bytecodes:
*/
LAST_JVM_OPCODE = Bytecodes.JSR_W
}
enum Flags {
/**
* Denotes an instruction that ends a basic block and does not let control flow fall through to its lexical successor.
*/
STOP = 0x00000001,
/**
* Denotes an instruction that ends a basic block and may let control flow fall through to its lexical successor.
* In practice this means it is a conditional branch.
*/
FALL_THROUGH = 0x00000002,
/**
* Denotes an instruction that has a 2 or 4 byte operand that is an offset to another instruction in the same method.
* This does not include the {@link Bytecodes#TABLESWITCH} or {@link Bytecodes#LOOKUPSWITCH} instructions.
*/
BRANCH = 0x00000004,
/**
* Denotes an instruction that reads the value of a static or instance field.
*/
FIELD_READ = 0x00000008,
/**
* Denotes an instruction that writes the value of a static or instance field.
*/
FIELD_WRITE = 0x00000010,
/**
* Denotes an instruction that is not defined in the JVM specification.
*/
EXTENSION = 0x00000020,
/**
* Denotes an instruction that can cause aFlags.TRAP.
*/
TRAP = 0x00000080,
/**
* Denotes an instruction that is commutative.
*/
COMMUTATIVE = 0x00000100,
/**
* Denotes an instruction that is associative.
*/
ASSOCIATIVE = 0x00000200,
/**
* Denotes an instruction that loads an operand.
*/
LOAD = 0x00000400,
/**
* Denotes an instruction that stores an operand.
*/
STORE = 0x00000800,
/**
* Denotes the 4 INVOKE* instructions.
*/
INVOKE = 0x00001000,
/**
* Denotes a return instruction that ends a basic block.
*/
RETURN = 0x00002000
}
/**
* A array that maps from a bytecode value to the set of {@link Flags} for the corresponding instruction.
*/
export var flags = new Uint32Array(256);
/**
* A array that maps from a bytecode value to the length in bytes for the corresponding instruction.
*/
export var length = new Uint32Array(256);
var writer = new IndentingWriter();
function define(opcode: number, name: string, format: string, flags_: Flags = 0) {
var instructionLength = format.length;
length[opcode] = instructionLength;
flags[opcode] = flags_;
release || assert (!isConditionalBranch(opcode) || isBranch(opcode), "a conditional branch must also be a branch");
}
/**
* Only call this before the compiler is used.
*/
export function defineBytecodes() {
define(Bytecodes.NOP , "nop" , "b" );
define(Bytecodes.ACONST_NULL , "aconst_null" , "b" );
define(Bytecodes.ICONST_M1 , "iconst_m1" , "b" );
define(Bytecodes.ICONST_0 , "iconst_0" , "b" );
define(Bytecodes.ICONST_1 , "iconst_1" , "b" );
define(Bytecodes.ICONST_2 , "iconst_2" , "b" );
define(Bytecodes.ICONST_3 , "iconst_3" , "b" );
define(Bytecodes.ICONST_4 , "iconst_4" , "b" );
define(Bytecodes.ICONST_5 , "iconst_5" , "b" );
define(Bytecodes.LCONST_0 , "lconst_0" , "b" );
define(Bytecodes.LCONST_1 , "lconst_1" , "b" );
define(Bytecodes.FCONST_0 , "fconst_0" , "b" );
define(Bytecodes.FCONST_1 , "fconst_1" , "b" );
define(Bytecodes.FCONST_2 , "fconst_2" , "b" );
define(Bytecodes.DCONST_0 , "dconst_0" , "b" );
define(Bytecodes.DCONST_1 , "dconst_1" , "b" );
define(Bytecodes.BIPUSH , "bipush" , "bc" );
define(Bytecodes.SIPUSH , "sipush" , "bcc" );
define(Bytecodes.LDC , "ldc" , "bi" , Flags.TRAP);
define(Bytecodes.LDC_W , "ldc_w" , "bii" , Flags.TRAP);
define(Bytecodes.LDC2_W , "ldc2_w" , "bii" , Flags.TRAP);
define(Bytecodes.ILOAD , "iload" , "bi" , Flags.LOAD);
define(Bytecodes.LLOAD , "lload" , "bi" , Flags.LOAD);
define(Bytecodes.FLOAD , "fload" , "bi" , Flags.LOAD);
define(Bytecodes.DLOAD , "dload" , "bi" , Flags.LOAD);
define(Bytecodes.ALOAD , "aload" , "bi" , Flags.LOAD);
define(Bytecodes.ILOAD_0 , "iload_0" , "b" , Flags.LOAD);
define(Bytecodes.ILOAD_1 , "iload_1" , "b" , Flags.LOAD);
define(Bytecodes.ILOAD_2 , "iload_2" , "b" , Flags.LOAD);
define(Bytecodes.ILOAD_3 , "iload_3" , "b" , Flags.LOAD);
define(Bytecodes.LLOAD_0 , "lload_0" , "b" , Flags.LOAD);
define(Bytecodes.LLOAD_1 , "lload_1" , "b" , Flags.LOAD);
define(Bytecodes.LLOAD_2 , "lload_2" , "b" , Flags.LOAD);
define(Bytecodes.LLOAD_3 , "lload_3" , "b" , Flags.LOAD);
define(Bytecodes.FLOAD_0 , "fload_0" , "b" , Flags.LOAD);
define(Bytecodes.FLOAD_1 , "fload_1" , "b" , Flags.LOAD);
define(Bytecodes.FLOAD_2 , "fload_2" , "b" , Flags.LOAD);
define(Bytecodes.FLOAD_3 , "fload_3" , "b" , Flags.LOAD);
define(Bytecodes.DLOAD_0 , "dload_0" , "b" , Flags.LOAD);
define(Bytecodes.DLOAD_1 , "dload_1" , "b" , Flags.LOAD);
define(Bytecodes.DLOAD_2 , "dload_2" , "b" , Flags.LOAD);
define(Bytecodes.DLOAD_3 , "dload_3" , "b" , Flags.LOAD);
define(Bytecodes.ALOAD_0 , "aload_0" , "b" , Flags.LOAD);
define(Bytecodes.ALOAD_1 , "aload_1" , "b" , Flags.LOAD);
define(Bytecodes.ALOAD_2 , "aload_2" , "b" , Flags.LOAD);
define(Bytecodes.ALOAD_3 , "aload_3" , "b" , Flags.LOAD);
define(Bytecodes.IALOAD , "iaload" , "b" , Flags.TRAP);
define(Bytecodes.LALOAD , "laload" , "b" , Flags.TRAP);
define(Bytecodes.FALOAD , "faload" , "b" , Flags.TRAP);
define(Bytecodes.DALOAD , "daload" , "b" , Flags.TRAP);
define(Bytecodes.AALOAD , "aaload" , "b" , Flags.TRAP);
define(Bytecodes.BALOAD , "baload" , "b" , Flags.TRAP);
define(Bytecodes.CALOAD , "caload" , "b" , Flags.TRAP);
define(Bytecodes.SALOAD , "saload" , "b" , Flags.TRAP);
define(Bytecodes.ISTORE , "istore" , "bi" , Flags.STORE);
define(Bytecodes.LSTORE , "lstore" , "bi" , Flags.STORE);
define(Bytecodes.FSTORE , "fstore" , "bi" , Flags.STORE);
define(Bytecodes.DSTORE , "dstore" , "bi" , Flags.STORE);
define(Bytecodes.ASTORE , "astore" , "bi" , Flags.STORE);
define(Bytecodes.ISTORE_0 , "istore_0" , "b" , Flags.STORE);
define(Bytecodes.ISTORE_1 , "istore_1" , "b" , Flags.STORE);
define(Bytecodes.ISTORE_2 , "istore_2" , "b" , Flags.STORE);
define(Bytecodes.ISTORE_3 , "istore_3" , "b" , Flags.STORE);
define(Bytecodes.LSTORE_0 , "lstore_0" , "b" , Flags.STORE);
define(Bytecodes.LSTORE_1 , "lstore_1" , "b" , Flags.STORE);
define(Bytecodes.LSTORE_2 , "lstore_2" , "b" , Flags.STORE);
define(Bytecodes.LSTORE_3 , "lstore_3" , "b" , Flags.STORE);
define(Bytecodes.FSTORE_0 , "fstore_0" , "b" , Flags.STORE);
define(Bytecodes.FSTORE_1 , "fstore_1" , "b" , Flags.STORE);
define(Bytecodes.FSTORE_2 , "fstore_2" , "b" , Flags.STORE);
define(Bytecodes.FSTORE_3 , "fstore_3" , "b" , Flags.STORE);
define(Bytecodes.DSTORE_0 , "dstore_0" , "b" , Flags.STORE);
define(Bytecodes.DSTORE_1 , "dstore_1" , "b" , Flags.STORE);
define(Bytecodes.DSTORE_2 , "dstore_2" , "b" , Flags.STORE);
define(Bytecodes.DSTORE_3 , "dstore_3" , "b" , Flags.STORE);
define(Bytecodes.ASTORE_0 , "astore_0" , "b" , Flags.STORE);
define(Bytecodes.ASTORE_1 , "astore_1" , "b" , Flags.STORE);
define(Bytecodes.ASTORE_2 , "astore_2" , "b" , Flags.STORE);
define(Bytecodes.ASTORE_3 , "astore_3" , "b" , Flags.STORE);
define(Bytecodes.IASTORE , "iastore" , "b" , Flags.TRAP);
define(Bytecodes.LASTORE , "lastore" , "b" , Flags.TRAP);
define(Bytecodes.FASTORE , "fastore" , "b" , Flags.TRAP);
define(Bytecodes.DASTORE , "dastore" , "b" , Flags.TRAP);
define(Bytecodes.AASTORE , "aastore" , "b" , Flags.TRAP);
define(Bytecodes.BASTORE , "bastore" , "b" , Flags.TRAP);
define(Bytecodes.CASTORE , "castore" , "b" , Flags.TRAP);
define(Bytecodes.SASTORE , "sastore" , "b" , Flags.TRAP);
define(Bytecodes.POP , "pop" , "b" );
define(Bytecodes.POP2 , "pop2" , "b" );
define(Bytecodes.DUP , "dup" , "b" );
define(Bytecodes.DUP_X1 , "dup_x1" , "b" );
define(Bytecodes.DUP_X2 , "dup_x2" , "b" );
define(Bytecodes.DUP2 , "dup2" , "b" );
define(Bytecodes.DUP2_X1 , "dup2_x1" , "b" );
define(Bytecodes.DUP2_X2 , "dup2_x2" , "b" );
define(Bytecodes.SWAP , "swap" , "b" );
define(Bytecodes.IADD , "iadd" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.LADD , "ladd" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.FADD , "fadd" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.DADD , "dadd" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.ISUB , "isub" , "b" );
define(Bytecodes.LSUB , "lsub" , "b" );
define(Bytecodes.FSUB , "fsub" , "b" );
define(Bytecodes.DSUB , "dsub" , "b" );
define(Bytecodes.IMUL , "imul" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.LMUL , "lmul" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.FMUL , "fmul" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.DMUL , "dmul" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.IDIV , "idiv" , "b" , Flags.TRAP);
define(Bytecodes.LDIV , "ldiv" , "b" , Flags.TRAP);
define(Bytecodes.FDIV , "fdiv" , "b" );
define(Bytecodes.DDIV , "ddiv" , "b" );
define(Bytecodes.IREM , "irem" , "b" , Flags.TRAP);
define(Bytecodes.LREM , "lrem" , "b" , Flags.TRAP);
define(Bytecodes.FREM , "frem" , "b" );
define(Bytecodes.DREM , "drem" , "b" );
define(Bytecodes.INEG , "ineg" , "b" );
define(Bytecodes.LNEG , "lneg" , "b" );
define(Bytecodes.FNEG , "fneg" , "b" );
define(Bytecodes.DNEG , "dneg" , "b" );
define(Bytecodes.ISHL , "ishl" , "b" );
define(Bytecodes.LSHL , "lshl" , "b" );
define(Bytecodes.ISHR , "ishr" , "b" );
define(Bytecodes.LSHR , "lshr" , "b" );
define(Bytecodes.IUSHR , "iushr" , "b" );
define(Bytecodes.LUSHR , "lushr" , "b" );
define(Bytecodes.IAND , "iand" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.LAND , "land" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.IOR , "ior" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.LOR , "lor" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.IXOR , "ixor" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.LXOR , "lxor" , "b" , Flags.COMMUTATIVE | Flags.ASSOCIATIVE);
define(Bytecodes.IINC , "iinc" , "bic" , Flags.LOAD | Flags.STORE);
define(Bytecodes.I2L , "i2l" , "b" );
define(Bytecodes.I2F , "i2f" , "b" );
define(Bytecodes.I2D , "i2d" , "b" );
define(Bytecodes.L2I , "l2i" , "b" );
define(Bytecodes.L2F , "l2f" , "b" );
define(Bytecodes.L2D , "l2d" , "b" );
define(Bytecodes.F2I , "f2i" , "b" );
define(Bytecodes.F2L , "f2l" , "b" );
define(Bytecodes.F2D , "f2d" , "b" );
define(Bytecodes.D2I , "d2i" , "b" );
define(Bytecodes.D2L , "d2l" , "b" );
define(Bytecodes.D2F , "d2f" , "b" );
define(Bytecodes.I2B , "i2b" , "b" );
define(Bytecodes.I2C , "i2c" , "b" );
define(Bytecodes.I2S , "i2s" , "b" );
define(Bytecodes.LCMP , "lcmp" , "b" );
define(Bytecodes.FCMPL , "fcmpl" , "b" );
define(Bytecodes.FCMPG , "fcmpg" , "b" );
define(Bytecodes.DCMPL , "dcmpl" , "b" );
define(Bytecodes.DCMPG , "dcmpg" , "b" );
define(Bytecodes.IFEQ , "ifeq" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFNE , "ifne" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFLT , "iflt" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFGE , "ifge" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFGT , "ifgt" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFLE , "ifle" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPEQ , "if_icmpeq" , "boo" , Flags.COMMUTATIVE | Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPNE , "if_icmpne" , "boo" , Flags.COMMUTATIVE | Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPLT , "if_icmplt" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPGE , "if_icmpge" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPGT , "if_icmpgt" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ICMPLE , "if_icmple" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ACMPEQ , "if_acmpeq" , "boo" , Flags.COMMUTATIVE | Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IF_ACMPNE , "if_acmpne" , "boo" , Flags.COMMUTATIVE | Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.GOTO , "goto" , "boo" , Flags.STOP | Flags.BRANCH);
define(Bytecodes.JSR , "jsr" , "boo" , Flags.STOP | Flags.BRANCH);
define(Bytecodes.RET , "ret" , "bi" , Flags.STOP);
define(Bytecodes.TABLESWITCH , "tableswitch" , "" , Flags.STOP);
define(Bytecodes.LOOKUPSWITCH , "lookupswitch" , "" , Flags.STOP);
define(Bytecodes.IRETURN , "ireturn" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.LRETURN , "lreturn" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.FRETURN , "freturn" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.DRETURN , "dreturn" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.ARETURN , "areturn" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.RETURN , "return" , "b" , Flags.TRAP | Flags.STOP | Flags.RETURN);
define(Bytecodes.GETSTATIC , "getstatic" , "bjj" , Flags.TRAP | Flags.FIELD_READ);
define(Bytecodes.PUTSTATIC , "putstatic" , "bjj" , Flags.TRAP | Flags.FIELD_WRITE);
define(Bytecodes.GETFIELD , "getfield" , "bjj" , Flags.TRAP | Flags.FIELD_READ);
define(Bytecodes.PUTFIELD , "putfield" , "bjj" , Flags.TRAP | Flags.FIELD_WRITE);
define(Bytecodes.RESOLVED_GETFIELD , "getfield" , "bjj" , Flags.TRAP | Flags.FIELD_READ);
define(Bytecodes.RESOLVED_PUTFIELD , "putfield" , "bjj" , Flags.TRAP | Flags.FIELD_WRITE);
define(Bytecodes.INVOKEVIRTUAL , "invokevirtual" , "bjj" , Flags.TRAP | Flags.INVOKE);
define(Bytecodes.RESOLVED_INVOKEVIRTUAL , "invokevirtual" , "bjj" , Flags.TRAP | Flags.INVOKE);
define(Bytecodes.INVOKESPECIAL , "invokespecial" , "bjj" , Flags.TRAP | Flags.INVOKE);
define(Bytecodes.INVOKESTATIC , "invokestatic" , "bjj" , Flags.TRAP | Flags.INVOKE);
define(Bytecodes.INVOKEINTERFACE , "invokeinterface" , "bjja_", Flags.TRAP | Flags.INVOKE);
define(Bytecodes.XXXUNUSEDXXX , "xxxunusedxxx" , "" );
define(Bytecodes.NEW , "new" , "bii" , Flags.TRAP);
define(Bytecodes.NEWARRAY , "newarray" , "bc" , Flags.TRAP);
define(Bytecodes.ANEWARRAY , "anewarray" , "bii" , Flags.TRAP);
define(Bytecodes.ARRAYLENGTH , "arraylength" , "b" , Flags.TRAP);
define(Bytecodes.ATHROW , "athrow" , "b" , Flags.TRAP | Flags.STOP);
define(Bytecodes.CHECKCAST , "checkcast" , "bii" , Flags.TRAP);
define(Bytecodes.INSTANCEOF , "instanceof" , "bii" , Flags.TRAP);
define(Bytecodes.MONITORENTER , "monitorenter" , "b" , Flags.TRAP);
define(Bytecodes.MONITOREXIT , "monitorexit" , "b" , Flags.TRAP);
define(Bytecodes.WIDE , "wide" , "" );
define(Bytecodes.MULTIANEWARRAY , "multianewarray" , "biic" , Flags.TRAP);
define(Bytecodes.IFNULL , "ifnull" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.IFNONNULL , "ifnonnull" , "boo" , Flags.FALL_THROUGH | Flags.BRANCH);
define(Bytecodes.GOTO_W , "goto_w" , "boooo", Flags.STOP | Flags.BRANCH);
define(Bytecodes.JSR_W , "jsr_w" , "boooo", Flags.STOP | Flags.BRANCH);
define(Bytecodes.BREAKPOINT , "breakpoint" , "b" , Flags.TRAP);
define(Bytecodes.ALOAD_ILOAD , "aload_iload" , "bi" , Flags.LOAD);
define(Bytecodes.IINC_GOTO , "iinc_goto" , "bic" , Flags.LOAD | Flags.STORE | Flags.STOP | Flags.BRANCH);
define(Bytecodes.ARRAYLENGTH_IF_ICMPGE , "arraylength_IF_ICMPGE" , "b" , Flags.COMMUTATIVE | Flags.FALL_THROUGH | Flags.BRANCH | Flags.TRAP);
}
defineBytecodes();
/**
* Gets the length of an instruction denoted by a given opcode.
*/
export function lengthOf(opcode: Bytecodes): number {
return length[opcode & 0xff];
}
export function lengthAt(code: Uint8Array, bci: number): number {
var opcode = Bytes.beU1(code, bci);
var _length = length[opcode & 0xff];
if (_length == 0) {
switch (opcode) {
case Bytecodes.TABLESWITCH: {
return new BytecodeTableSwitch(code, bci).size();
}
case Bytecodes.LOOKUPSWITCH: {
return new BytecodeLookupSwitch(code, bci).size();
}
case Bytecodes.WIDE: {
var opc = Bytes.beU1(code, bci + 1);
if (opc == Bytecodes.RET) {
return 4;
} else if (opc == Bytecodes.IINC) {
return 6;
} else {
return 4; // a load or store bytecode
}
}
default:
throw new Error("unknown variable-length bytecode: " + opcode);
}
}
return _length;
}
/**
* Determines if an opcode is commutative.
*/
function isCommutative(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.COMMUTATIVE) != 0;
}
/**
* Determines if a given opcode denotes an instruction that can cause an implicit exception.
*/
export function canTrap(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.TRAP) != 0;
}
/**
* Determines if a given opcode denotes an instruction that loads a local variable to the operand stack.
*/
function isLoad(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.LOAD) != 0;
}
/**
* Determines if a given opcode denotes an instruction that ends a basic block and does not let control flow fall
* through to its lexical successor.
*/
export function isStop(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.STOP) != 0;
}
/**
* Determines if a given opcode denotes an instruction that stores a value to a local variable
* after popping it from the operand stack.
*/
export function isInvoke(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.INVOKE) != 0;
}
/**
* Determines if a given opcode denotes an instruction that stores a value to a local variable
* after popping it from the operand stack.
*/
export function isStore(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.STORE) != 0;
}
/**
* Determines if a given opcode is an instruction that delimits a basic block.
*/
export function isBlockEnd(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & (Flags.STOP | Flags.FALL_THROUGH)) != 0;
}
/**
* Determines if a given opcode is an instruction that has a 2 or 4 byte operand that is an offset to another
* instruction in the same method. This does not include the {@linkplain #TABLESWITCH switch} instructions.
*/
function isBranch(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.BRANCH) != 0;
}
/**
* Determines if a given opcode denotes a conditional branch.
*/
function isConditionalBranch(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.FALL_THROUGH) != 0;
}
/**
* Determines if a given opcode denotes a standard bytecode. A standard bytecode is
* defined in the JVM specification.
*/
function isStandard(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.EXTENSION) == 0;
}
/**
* Determines if a given opcode denotes an extended bytecode.
*/
function isExtended(opcode: Bytecodes): boolean {
return (flags[opcode & 0xff] & Flags.EXTENSION) != 0;
}
/**
* Determines if a given opcode is a three-byte extended bytecode.
*/
function isThreeByteExtended(opcode: Bytecodes): boolean {
return (opcode & ~0xff) != 0;
}
/**
* Determines if a given opcode is a return bytecode.
*/
export function isReturn(opcode: Bytecodes): boolean {
return !!(flags[opcode & 0xff] & Flags.RETURN);
}
export class BytecodeSwitch {
/**
* The bytecode array or {@code null} if {@link #stream} is not {@code null}.
*/
private code: Uint8Array;
/**
* Index of start of switch instruction.
*/
protected bci: number;
/**
* Index of the start of the additional data for the switch instruction, aligned to a multiple of four from the method start.
*/
protected alignedBci: number;
/**
* Constructor for a bytecode array.
* @param code the bytecode array containing the switch instruction.
* @param bci the index in the array of the switch instruction
*/
constructor(code: Uint8Array, bci: number) {
this.alignedBci = (bci + 4) & 0xfffffffc;
this.code = code;
this.bci = bci;
}
/**
* Gets the index of the instruction denoted by the {@code i}'th switch target.
* @param i index of the switch target
* @return the index of the instruction denoted by the {@code i}'th switch target
*/
public targetAt(i: number): number {
return this.bci + this.offsetAt(i);
}
/**
* Gets the index of the instruction for the default switch target.
* @return the index of the instruction for the default switch target
*/
public defaultTarget(): number {
return this.bci + this.defaultOffset();
}
/**
* Gets the offset from the start of the switch instruction to the default switch target.
* @return the offset to the default switch target
*/
public defaultOffset(): number {
throw Debug.abstractMethod("defaultOffset");
}
/**
* Gets the key at {@code i}'th switch target index.
* @param i the switch target index
* @return the key at {@code i}'th switch target index
*/
public keyAt(i: number): number {
throw Debug.abstractMethod("keyAt");
}
/**
* Gets the offset from the start of the switch instruction for the {@code i}'th switch target.
* @param i the switch target index
* @return the offset to the {@code i}'th switch target
*/
public offsetAt(i: number): number {
throw Debug.abstractMethod("offsetAt");
}
/**
* Gets the number of switch targets.
* @return the number of switch targets
*/
public numberOfCases(): number {
throw Debug.abstractMethod("numberOfCases");
}
/**
* Gets the total size in bytes of the switch instruction.
* @return the total size in bytes of the switch instruction
*/
public size(): number {
throw Debug.abstractMethod("size");
}
/**
* Reads the signed value at given bytecode index.
* @param bci the start index of the value to retrieve
* @return the signed, 4-byte value in the bytecode array starting at {@code bci}
*/
protected readWord(bci: number): number {
return Bytes.beS4(this.code, bci);
}
}
export class BytecodeTableSwitch extends BytecodeSwitch {
private static OFFSET_TO_LOW_KEY = 4;
private static OFFSET_TO_HIGH_KEY = 8;
private static OFFSET_TO_FIRST_JUMP_OFFSET = 12;
private static JUMP_OFFSET_SIZE = 4;
/**
* Constructor for a bytecode array.
* @param code the bytecode array containing the switch instruction.
* @param bci the index in the array of the switch instruction
*/
constructor(code: Uint8Array, bci: number) {
super(code, bci);
}
/**
* Gets the low key of the table switch.
*/
public lowKey() {
return this.readWord(this.alignedBci + BytecodeTableSwitch.OFFSET_TO_LOW_KEY);
}
/**
* Gets the high key of the table switch.
*/
public highKey() {
return this.readWord(this.alignedBci + BytecodeTableSwitch.OFFSET_TO_HIGH_KEY);
}
public keyAt(i: number) {
return this.lowKey() + i;
}
public defaultOffset(): number {
return this.readWord(this.alignedBci);
}
public offsetAt(i: number): number {
return this.readWord(this.alignedBci + BytecodeTableSwitch.OFFSET_TO_FIRST_JUMP_OFFSET + BytecodeTableSwitch.JUMP_OFFSET_SIZE * i);
}
public numberOfCases(): number {
return this.highKey() - this.lowKey() + 1;
}
public size(): number {
return this.alignedBci + BytecodeTableSwitch.OFFSET_TO_FIRST_JUMP_OFFSET + BytecodeTableSwitch.JUMP_OFFSET_SIZE * this.numberOfCases() - this.bci;
}
}
export class BytecodeLookupSwitch extends BytecodeSwitch {
private static OFFSET_TO_NUMBER_PAIRS = 4;
private static OFFSET_TO_FIRST_PAIR_MATCH = 8;
private static OFFSET_TO_FIRST_PAIR_OFFSET = 12;
private static PAIR_SIZE = 8;
constructor(code: Uint8Array, bci: number) {
super(code, bci);
}
public defaultOffset(): number {
return this.readWord(this.alignedBci);
}
public offsetAt(i: number): number {
return this.readWord(this.alignedBci + BytecodeLookupSwitch.OFFSET_TO_FIRST_PAIR_OFFSET + BytecodeLookupSwitch.PAIR_SIZE * i);
}
public keyAt(i): number {
return this.readWord(this.alignedBci + BytecodeLookupSwitch.OFFSET_TO_FIRST_PAIR_MATCH + BytecodeLookupSwitch.PAIR_SIZE * i);
}
public numberOfCases(): number {
return this.readWord(this.alignedBci + BytecodeLookupSwitch.OFFSET_TO_NUMBER_PAIRS);
}
public size(): number {
return this.alignedBci + BytecodeLookupSwitch.OFFSET_TO_FIRST_PAIR_MATCH + BytecodeLookupSwitch.PAIR_SIZE * this.numberOfCases() - this.bci;
}
}
/**
* A utility class that makes iterating over bytecodes and reading operands
* simpler and less error prone. For example, it handles the {@link Bytecodes#WIDE} instruction
* and wide variants of instructions internally.
*/
export class BytecodeStream {
private _code: Uint8Array;
private _opcode: Bytecodes;
private _currentBCI: number;
private _nextBCI: number;
constructor(code: Uint8Array) {
assert (code, "No Code");
this._code = code;
this.setBCI(0);
}
/**
* Advances to the next bytecode.
*/
public next() {
this.setBCI(this.nextBCI);
}
/**
* Gets the bytecode index of the end of the code.
*/
public endBCI(): number {
return this._code.length;
}
/**
* Gets the next bytecode index (no side-effects).
*/
public get nextBCI(): number {
return this._nextBCI;
}
/**
* Gets the current bytecode index.
*/
public get currentBCI(): number {
return this._currentBCI;
}
/**
* Gets the current opcode. This method will never return the
* {@link Bytecodes#WIDE WIDE} opcode, but will instead
* return the opcode that is modified by the {@code WIDE} opcode.
* @return the current opcode; {@link Bytecodes#END} if at or beyond the end of the code
*/
public currentBC(): Bytecodes {
if (this._opcode === Bytecodes.WIDE) {
return Bytes.beU1(this._code, this._currentBCI + 1);
} else {
return this._opcode;
}
}
public rawCurrentBC(): Bytecodes {
return this._opcode;
}
/**
* Sets the current opcode.
*/
public writeCurrentBC(bc: Bytecodes) {
assert(lengthOf(this.currentBC()) === lengthOf(bc));
this._code[this._currentBCI] = bc;
}
/**
* Gets the next opcode.
* @return the next opcode; {@link Bytecodes#END} if at or beyond the end of the code
*/
public nextBC(): Bytecodes {
return Bytes.beU1(this._code, this._nextBCI);
}
/**
* Reads the index of a local variable for one of the load or store instructions.
* The WIDE modifier is handled internally.
*/
public readLocalIndex(): number {
// read local variable index for load/store
if (this._opcode == Bytecodes.WIDE) {
return Bytes.beU2(this._code, this._currentBCI + 2);
}
return Bytes.beU1(this._code, this._currentBCI + 1);
}
/**
* Read the delta for an {@link Bytecodes#IINC} bytecode.
*/
public readIncrement(): number {
// read the delta for the iinc bytecode
if (this._opcode == Bytecodes.WIDE) {
return Bytes.beS2(this._code, this._currentBCI + 4);
}
return Bytes.beS1(this._code, this._currentBCI + 2);
}
/**
* Read the destination of a {@link Bytecodes#GOTO} or {@code IF} instructions.
* @return the destination bytecode index
*/
public readBranchDest(): number {
// reads the destination for a branch bytecode
return this._currentBCI + Bytes.beS2(this._code, this._currentBCI + 1);
}
/**
* Read the destination of a {@link Bytecodes#GOTO_W} or {@link Bytecodes#JSR_W} instructions.
* @return the destination bytecode index
*/
public readFarBranchDest(): number {
// reads the destination for a wide branch bytecode
return this._currentBCI + Bytes.beS4(this._code, this._currentBCI + 1);
}
/**
* Read a signed 4-byte integer from the bytecode stream at the specified bytecode index.
* @param bci the bytecode index
* @return the integer value
*/
public readInt(bci: number): number {
// reads a 4-byte signed value
return Bytes.beS4(this._code, bci);
}
/**
* Reads an unsigned, 1-byte value from the bytecode stream at the specified bytecode index.
* @param bci the bytecode index
* @return the byte
*/
public readUByte(bci: number): number {
return Bytes.beU1(this._code, bci);
}
/**
* Reads a constant pool index for the current instruction.
* @return the constant pool index
*/
public readCPI(): number {
if (this._opcode == Bytecodes.LDC) {
return Bytes.beU1(this._code, this._currentBCI + 1);
}
return Bytes.beU2(this._code, this._currentBCI + 1) << 16 >> 16;
}
/**
* Reads a signed, 1-byte value for the current instruction (e.g. BIPUSH).
*/
public readByte(): number {
return this._code[this._currentBCI + 1] << 24 >> 24;
}
/**
* Reads a signed, 2-byte short for the current instruction (e.g. SIPUSH).
*/
public readShort() {
return Bytes.beS2(this._code, this._currentBCI + 1) << 16 >> 16;
}
/**
* Sets the bytecode index to the specified value.
* If {@code bci} is beyond the end of the array, {@link #currentBC} will return
* {@link Bytecodes#END} and other methods may throw {@link ArrayIndexOutOfBoundsException}.
* @param bci the new bytecode index
*/
public setBCI(bci: number) {
this._currentBCI = bci;
if (this._currentBCI < this._code.length) {
this._opcode = Bytes.beU1(this._code, bci);
this._nextBCI = bci + lengthAt(this._code, bci);
} else {
this._opcode = Bytecodes.END;
this._nextBCI = this._currentBCI;
}
}
public readTableSwitch(): BytecodeTableSwitch {
return new BytecodeTableSwitch(this._code, this._currentBCI);
}
public readLookupSwitch(): BytecodeLookupSwitch {
return new BytecodeLookupSwitch(this._code, this._currentBCI);
}
}
}