blob: 8b951600cd30177630004527fa08a5fc3b04a40f [file] [log] [blame]
/*
* Copyright 1999-2007 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.tools.javac.jvm;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import static com.sun.tools.javac.code.TypeTags.*;
import static com.sun.tools.javac.jvm.ByteCodes.*;
import static com.sun.tools.javac.jvm.UninitializedType.*;
import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame;
/** An internal structure that corresponds to the code attribute of
* methods in a classfile. The class also provides some utility operations to
* generate bytecode instructions.
*
* <p><b>This is NOT part of any API supported by Sun Microsystems. If
* you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Code {
public final boolean debugCode;
public final boolean needStackMap;
public enum StackMapFormat {
NONE,
CLDC {
Name getAttributeName(Name.Table names) {
return names.StackMap;
}
},
JSR202 {
Name getAttributeName(Name.Table names) {
return names.StackMapTable;
}
};
Name getAttributeName(Name.Table names) {
return names.empty;
}
}
final Types types;
final Symtab syms;
/*---------- classfile fields: --------------- */
/** The maximum stack size.
*/
public int max_stack = 0;
/** The maximum number of local variable slots.
*/
public int max_locals = 0;
/** The code buffer.
*/
public byte[] code = new byte[64];
/** the current code pointer.
*/
public int cp = 0;
/** Check the code against VM spec limits; if
* problems report them and return true.
*/
public boolean checkLimits(DiagnosticPosition pos, Log log) {
if (cp > ClassFile.MAX_CODE) {
log.error(pos, "limit.code");
return true;
}
if (max_locals > ClassFile.MAX_LOCALS) {
log.error(pos, "limit.locals");
return true;
}
if (max_stack > ClassFile.MAX_STACK) {
log.error(pos, "limit.stack");
return true;
}
return false;
}
/** A buffer for expression catch data. Each enter is a vector
* of four unsigned shorts.
*/
ListBuffer<char[]> catchInfo = new ListBuffer<char[]>();
/** A buffer for line number information. Each entry is a vector
* of two unsigned shorts.
*/
List<char[]> lineInfo = List.nil(); // handled in stack fashion
/** The CharacterRangeTable
*/
public CRTable crt;
/*---------- internal fields: --------------- */
/** Are we generating code with jumps >= 32K?
*/
public boolean fatcode;
/** Code generation enabled?
*/
private boolean alive = true;
/** The current machine state (registers and stack).
*/
State state;
/** Is it forbidden to compactify code, because something is
* pointing to current location?
*/
private boolean fixedPc = false;
/** The next available register.
*/
public int nextreg = 0;
/** A chain for jumps to be resolved before the next opcode is emitted.
* We do this lazily to avoid jumps to jumps.
*/
Chain pendingJumps = null;
/** The position of the currently statement, if we are at the
* start of this statement, NOPOS otherwise.
* We need this to emit line numbers lazily, which we need to do
* because of jump-to-jump optimization.
*/
int pendingStatPos = Position.NOPOS;
/** Set true when a stackMap is needed at the current PC. */
boolean pendingStackMap = false;
/** The stack map format to be generated. */
StackMapFormat stackMap;
/** Switch: emit variable debug info.
*/
boolean varDebugInfo;
/** Switch: emit line number info.
*/
boolean lineDebugInfo;
/** Emit line number info if map supplied
*/
Position.LineMap lineMap;
/** The constant pool of the current class.
*/
final Pool pool;
final MethodSymbol meth;
/** Construct a code object, given the settings of the fatcode,
* debugging info switches and the CharacterRangeTable.
*/
public Code(MethodSymbol meth,
boolean fatcode,
Position.LineMap lineMap,
boolean varDebugInfo,
StackMapFormat stackMap,
boolean debugCode,
CRTable crt,
Symtab syms,
Types types,
Pool pool) {
this.meth = meth;
this.fatcode = fatcode;
this.lineMap = lineMap;
this.lineDebugInfo = lineMap != null;
this.varDebugInfo = varDebugInfo;
this.crt = crt;
this.syms = syms;
this.types = types;
this.debugCode = debugCode;
this.stackMap = stackMap;
switch (stackMap) {
case CLDC:
case JSR202:
this.needStackMap = true;
break;
default:
this.needStackMap = false;
}
state = new State();
lvar = new LocalVar[20];
this.pool = pool;
}
/* **************************************************************************
* Typecodes & related stuff
****************************************************************************/
/** Given a type, return its type code (used implicitly in the
* JVM architecture).
*/
public static int typecode(Type type) {
switch (type.tag) {
case BYTE: return BYTEcode;
case SHORT: return SHORTcode;
case CHAR: return CHARcode;
case INT: return INTcode;
case LONG: return LONGcode;
case FLOAT: return FLOATcode;
case DOUBLE: return DOUBLEcode;
case BOOLEAN: return BYTEcode;
case VOID: return VOIDcode;
case CLASS:
case ARRAY:
case METHOD:
case BOT:
case TYPEVAR:
case UNINITIALIZED_THIS:
case UNINITIALIZED_OBJECT:
return OBJECTcode;
default: throw new AssertionError("typecode " + type.tag);
}
}
/** Collapse type code for subtypes of int to INTcode.
*/
public static int truncate(int tc) {
switch (tc) {
case BYTEcode: case SHORTcode: case CHARcode: return INTcode;
default: return tc;
}
}
/** The width in bytes of objects of the type.
*/
public static int width(int typecode) {
switch (typecode) {
case LONGcode: case DOUBLEcode: return 2;
case VOIDcode: return 0;
default: return 1;
}
}
public static int width(Type type) {
return type == null ? 1 : width(typecode(type));
}
/** The total width taken up by a vector of objects.
*/
public static int width(List<Type> types) {
int w = 0;
for (List<Type> l = types; l.nonEmpty(); l = l.tail)
w = w + width(l.head);
return w;
}
/** Given a type, return its code for allocating arrays of that type.
*/
public static int arraycode(Type type) {
switch (type.tag) {
case BYTE: return 8;
case BOOLEAN: return 4;
case SHORT: return 9;
case CHAR: return 5;
case INT: return 10;
case LONG: return 11;
case FLOAT: return 6;
case DOUBLE: return 7;
case CLASS: return 0;
case ARRAY: return 1;
default: throw new AssertionError("arraycode " + type);
}
}
/* **************************************************************************
* Emit code
****************************************************************************/
/** The current output code pointer.
*/
public int curPc() {
if (pendingJumps != null) resolvePending();
if (pendingStatPos != Position.NOPOS) markStatBegin();
fixedPc = true;
return cp;
}
/** Emit a byte of code.
*/
private void emit1(int od) {
if (!alive) return;
if (cp == code.length) {
byte[] newcode = new byte[cp * 2];
System.arraycopy(code, 0, newcode, 0, cp);
code = newcode;
}
code[cp++] = (byte)od;
}
/** Emit two bytes of code.
*/
private void emit2(int od) {
if (!alive) return;
if (cp + 2 > code.length) {
emit1(od >> 8);
emit1(od);
} else {
code[cp++] = (byte)(od >> 8);
code[cp++] = (byte)od;
}
}
/** Emit four bytes of code.
*/
public void emit4(int od) {
if (!alive) return;
if (cp + 4 > code.length) {
emit1(od >> 24);
emit1(od >> 16);
emit1(od >> 8);
emit1(od);
} else {
code[cp++] = (byte)(od >> 24);
code[cp++] = (byte)(od >> 16);
code[cp++] = (byte)(od >> 8);
code[cp++] = (byte)od;
}
}
/** Emit an opcode.
*/
private void emitop(int op) {
if (pendingJumps != null) resolvePending();
if (alive) {
if (pendingStatPos != Position.NOPOS)
markStatBegin();
if (pendingStackMap) {
pendingStackMap = false;
emitStackMap();
}
if (debugCode)
System.err.println("emit@" + cp + " stack=" +
state.stacksize + ": " +
mnem(op));
emit1(op);
}
}
void postop() {
assert alive || state.stacksize == 0;
}
/** Emit a multinewarray instruction.
*/
public void emitMultianewarray(int ndims, int type, Type arrayType) {
emitop(multianewarray);
if (!alive) return;
emit2(type);
emit1(ndims);
state.pop(ndims);
state.push(arrayType);
}
/** Emit newarray.
*/
public void emitNewarray(int elemcode, Type arrayType) {
emitop(newarray);
if (!alive) return;
emit1(elemcode);
state.pop(1); // count
state.push(arrayType);
}
/** Emit anewarray.
*/
public void emitAnewarray(int od, Type arrayType) {
emitop(anewarray);
if (!alive) return;
emit2(od);
state.pop(1);
state.push(arrayType);
}
/** Emit an invokeinterface instruction.
*/
public void emitInvokeinterface(int meth, Type mtype) {
int argsize = width(mtype.getParameterTypes());
emitop(invokeinterface);
if (!alive) return;
emit2(meth);
emit1(argsize + 1);
emit1(0);
state.pop(argsize + 1);
state.push(mtype.getReturnType());
}
/** Emit an invokespecial instruction.
*/
public void emitInvokespecial(int meth, Type mtype) {
int argsize = width(mtype.getParameterTypes());
emitop(invokespecial);
if (!alive) return;
emit2(meth);
Symbol sym = (Symbol)pool.pool[meth];
state.pop(argsize);
if (sym.isConstructor())
state.markInitialized((UninitializedType)state.peek());
state.pop(1);
state.push(mtype.getReturnType());
}
/** Emit an invokestatic instruction.
*/
public void emitInvokestatic(int meth, Type mtype) {
int argsize = width(mtype.getParameterTypes());
emitop(invokestatic);
if (!alive) return;
emit2(meth);
state.pop(argsize);
state.push(mtype.getReturnType());
}
/** Emit an invokevirtual instruction.
*/
public void emitInvokevirtual(int meth, Type mtype) {
int argsize = width(mtype.getParameterTypes());
emitop(invokevirtual);
if (!alive) return;
emit2(meth);
state.pop(argsize + 1);
state.push(mtype.getReturnType());
}
/** Emit an opcode with no operand field.
*/
public void emitop0(int op) {
emitop(op);
if (!alive) return;
switch (op) {
case aaload: {
state.pop(1);// index
Type a = state.stack[state.stacksize-1];
state.pop(1);
state.push(types.erasure(types.elemtype(a))); }
break;
case goto_:
markDead();
break;
case nop:
case ineg:
case lneg:
case fneg:
case dneg:
break;
case aconst_null:
state.push(syms.botType);
break;
case iconst_m1:
case iconst_0:
case iconst_1:
case iconst_2:
case iconst_3:
case iconst_4:
case iconst_5:
case iload_0:
case iload_1:
case iload_2:
case iload_3:
state.push(syms.intType);
break;
case lconst_0:
case lconst_1:
case lload_0:
case lload_1:
case lload_2:
case lload_3:
state.push(syms.longType);
break;
case fconst_0:
case fconst_1:
case fconst_2:
case fload_0:
case fload_1:
case fload_2:
case fload_3:
state.push(syms.floatType);
break;
case dconst_0:
case dconst_1:
case dload_0:
case dload_1:
case dload_2:
case dload_3:
state.push(syms.doubleType);
break;
case aload_0:
state.push(lvar[0].sym.type);
break;
case aload_1:
state.push(lvar[1].sym.type);
break;
case aload_2:
state.push(lvar[2].sym.type);
break;
case aload_3:
state.push(lvar[3].sym.type);
break;
case iaload:
case baload:
case caload:
case saload:
state.pop(2);
state.push(syms.intType);
break;
case laload:
state.pop(2);
state.push(syms.longType);
break;
case faload:
state.pop(2);
state.push(syms.floatType);
break;
case daload:
state.pop(2);
state.push(syms.doubleType);
break;
case istore_0:
case istore_1:
case istore_2:
case istore_3:
case fstore_0:
case fstore_1:
case fstore_2:
case fstore_3:
case astore_0:
case astore_1:
case astore_2:
case astore_3:
case pop:
case lshr:
case lshl:
case lushr:
state.pop(1);
break;
case areturn:
case ireturn:
case freturn:
assert state.nlocks == 0;
state.pop(1);
markDead();
break;
case athrow:
state.pop(1);
markDead();
break;
case lstore_0:
case lstore_1:
case lstore_2:
case lstore_3:
case dstore_0:
case dstore_1:
case dstore_2:
case dstore_3:
case pop2:
state.pop(2);
break;
case lreturn:
case dreturn:
assert state.nlocks == 0;
state.pop(2);
markDead();
break;
case dup:
state.push(state.stack[state.stacksize-1]);
break;
case return_:
assert state.nlocks == 0;
markDead();
break;
case arraylength:
state.pop(1);
state.push(syms.intType);
break;
case isub:
case iadd:
case imul:
case idiv:
case imod:
case ishl:
case ishr:
case iushr:
case iand:
case ior:
case ixor:
state.pop(1);
// state.pop(1);
// state.push(syms.intType);
break;
case aastore:
state.pop(3);
break;
case land:
case lor:
case lxor:
case lmod:
case ldiv:
case lmul:
case lsub:
case ladd:
state.pop(2);
break;
case lcmp:
state.pop(4);
state.push(syms.intType);
break;
case l2i:
state.pop(2);
state.push(syms.intType);
break;
case i2l:
state.pop(1);
state.push(syms.longType);
break;
case i2f:
state.pop(1);
state.push(syms.floatType);
break;
case i2d:
state.pop(1);
state.push(syms.doubleType);
break;
case l2f:
state.pop(2);
state.push(syms.floatType);
break;
case l2d:
state.pop(2);
state.push(syms.doubleType);
break;
case f2i:
state.pop(1);
state.push(syms.intType);
break;
case f2l:
state.pop(1);
state.push(syms.longType);
break;
case f2d:
state.pop(1);
state.push(syms.doubleType);
break;
case d2i:
state.pop(2);
state.push(syms.intType);
break;
case d2l:
state.pop(2);
state.push(syms.longType);
break;
case d2f:
state.pop(2);
state.push(syms.floatType);
break;
case tableswitch:
case lookupswitch:
state.pop(1);
// the caller is responsible for patching up the state
break;
case dup_x1: {
Type val1 = state.pop1();
Type val2 = state.pop1();
state.push(val1);
state.push(val2);
state.push(val1);
break;
}
case bastore:
state.pop(3);
break;
case int2byte:
case int2char:
case int2short:
break;
case fmul:
case fadd:
case fsub:
case fdiv:
case fmod:
state.pop(1);
break;
case castore:
case iastore:
case fastore:
case sastore:
state.pop(3);
break;
case lastore:
case dastore:
state.pop(4);
break;
case dup2:
if (state.stack[state.stacksize-1] != null) {
Type value1 = state.pop1();
Type value2 = state.pop1();
state.push(value2);
state.push(value1);
state.push(value2);
state.push(value1);
} else {
Type value = state.pop2();
state.push(value);
state.push(value);
}
break;
case dup2_x1:
if (state.stack[state.stacksize-1] != null) {
Type value1 = state.pop1();
Type value2 = state.pop1();
Type value3 = state.pop1();
state.push(value2);
state.push(value1);
state.push(value3);
state.push(value2);
state.push(value1);
} else {
Type value1 = state.pop2();
Type value2 = state.pop1();
state.push(value1);
state.push(value2);
state.push(value1);
}
break;
case dup2_x2:
if (state.stack[state.stacksize-1] != null) {
Type value1 = state.pop1();
Type value2 = state.pop1();
if (state.stack[state.stacksize-1] != null) {
// form 1
Type value3 = state.pop1();
Type value4 = state.pop1();
state.push(value2);
state.push(value1);
state.push(value4);
state.push(value3);
state.push(value2);
state.push(value1);
} else {
// form 3
Type value3 = state.pop2();
state.push(value2);
state.push(value1);
state.push(value3);
state.push(value2);
state.push(value1);
}
} else {
Type value1 = state.pop2();
if (state.stack[state.stacksize-1] != null) {
// form 2
Type value2 = state.pop1();
Type value3 = state.pop1();
state.push(value1);
state.push(value3);
state.push(value2);
state.push(value1);
} else {
// form 4
Type value2 = state.pop2();
state.push(value1);
state.push(value2);
state.push(value1);
}
}
break;
case dup_x2: {
Type value1 = state.pop1();
if (state.stack[state.stacksize-1] != null) {
// form 1
Type value2 = state.pop1();
Type value3 = state.pop1();
state.push(value1);
state.push(value3);
state.push(value2);
state.push(value1);
} else {
// form 2
Type value2 = state.pop2();
state.push(value1);
state.push(value2);
state.push(value1);
}
}
break;
case fcmpl:
case fcmpg:
state.pop(2);
state.push(syms.intType);
break;
case dcmpl:
case dcmpg:
state.pop(4);
state.push(syms.intType);
break;
case swap: {
Type value1 = state.pop1();
Type value2 = state.pop1();
state.push(value1);
state.push(value2);
break;
}
case dadd:
case dsub:
case dmul:
case ddiv:
case dmod:
state.pop(2);
break;
case ret:
markDead();
break;
case wide:
// must be handled by the caller.
return;
case monitorenter:
case monitorexit:
state.pop(1);
break;
default:
throw new AssertionError(mnem(op));
}
postop();
}
/** Emit an opcode with a one-byte operand field.
*/
public void emitop1(int op, int od) {
emitop(op);
if (!alive) return;
emit1(od);
switch (op) {
case bipush:
state.push(syms.intType);
break;
case ldc1:
state.push(typeForPool(pool.pool[od]));
break;
default:
throw new AssertionError(mnem(op));
}
postop();
}
/** The type of a constant pool entry. */
private Type typeForPool(Object o) {
if (o instanceof Integer) return syms.intType;
if (o instanceof Float) return syms.floatType;
if (o instanceof String) return syms.stringType;
if (o instanceof Long) return syms.longType;
if (o instanceof Double) return syms.doubleType;
if (o instanceof ClassSymbol) return syms.classType;
if (o instanceof Type.ArrayType) return syms.classType;
throw new AssertionError(o);
}
/** Emit an opcode with a one-byte operand field;
* widen if field does not fit in a byte.
*/
public void emitop1w(int op, int od) {
if (od > 0xFF) {
emitop(wide);
emitop(op);
emit2(od);
} else {
emitop(op);
emit1(od);
}
if (!alive) return;
switch (op) {
case iload:
state.push(syms.intType);
break;
case lload:
state.push(syms.longType);
break;
case fload:
state.push(syms.floatType);
break;
case dload:
state.push(syms.doubleType);
break;
case aload:
state.push(lvar[od].sym.type);
break;
case lstore:
case dstore:
state.pop(2);
break;
case istore:
case fstore:
case astore:
state.pop(1);
break;
case ret:
markDead();
break;
default:
throw new AssertionError(mnem(op));
}
postop();
}
/** Emit an opcode with two one-byte operand fields;
* widen if either field does not fit in a byte.
*/
public void emitop1w(int op, int od1, int od2) {
if (od1 > 0xFF || od2 < -128 || od2 > 127) {
emitop(wide);
emitop(op);
emit2(od1);
emit2(od2);
} else {
emitop(op);
emit1(od1);
emit1(od2);
}
if (!alive) return;
switch (op) {
case iinc:
break;
default:
throw new AssertionError(mnem(op));
}
}
/** Emit an opcode with a two-byte operand field.
*/
public void emitop2(int op, int od) {
emitop(op);
if (!alive) return;
emit2(od);
switch (op) {
case getstatic:
state.push(((Symbol)(pool.pool[od])).erasure(types));
break;
case putstatic:
state.pop(((Symbol)(pool.pool[od])).erasure(types));
break;
case new_:
state.push(uninitializedObject(((Symbol)(pool.pool[od])).erasure(types), cp-3));
break;
case sipush:
state.push(syms.intType);
break;
case if_acmp_null:
case if_acmp_nonnull:
case ifeq:
case ifne:
case iflt:
case ifge:
case ifgt:
case ifle:
state.pop(1);
break;
case if_icmpeq:
case if_icmpne:
case if_icmplt:
case if_icmpge:
case if_icmpgt:
case if_icmple:
case if_acmpeq:
case if_acmpne:
state.pop(2);
break;
case goto_:
markDead();
break;
case putfield:
state.pop(((Symbol)(pool.pool[od])).erasure(types));
state.pop(1); // object ref
break;
case getfield:
state.pop(1); // object ref
state.push(((Symbol)(pool.pool[od])).erasure(types));
break;
case checkcast: {
state.pop(1); // object ref
Object o = pool.pool[od];
Type t = (o instanceof Symbol)
? ((Symbol)o).erasure(types)
: types.erasure(((Type)o));
state.push(t);
break; }
case ldc2w:
state.push(typeForPool(pool.pool[od]));
break;
case instanceof_:
state.pop(1);
state.push(syms.intType);
break;
case ldc2:
state.push(typeForPool(pool.pool[od]));
break;
case jsr:
break;
default:
throw new AssertionError(mnem(op));
}
// postop();
}
/** Emit an opcode with a four-byte operand field.
*/
public void emitop4(int op, int od) {
emitop(op);
if (!alive) return;
emit4(od);
switch (op) {
case goto_w:
markDead();
break;
case jsr_w:
break;
default:
throw new AssertionError(mnem(op));
}
// postop();
}
/** Align code pointer to next `incr' boundary.
*/
public void align(int incr) {
if (alive)
while (cp % incr != 0) emitop0(nop);
}
/** Place a byte into code at address pc. Pre: pc + 1 <= cp.
*/
private void put1(int pc, int op) {
code[pc] = (byte)op;
}
/** Place two bytes into code at address pc. Pre: pc + 2 <= cp.
*/
private void put2(int pc, int od) {
// pre: pc + 2 <= cp
put1(pc, od >> 8);
put1(pc+1, od);
}
/** Place four bytes into code at address pc. Pre: pc + 4 <= cp.
*/
public void put4(int pc, int od) {
// pre: pc + 4 <= cp
put1(pc , od >> 24);
put1(pc+1, od >> 16);
put1(pc+2, od >> 8);
put1(pc+3, od);
}
/** Return code byte at position pc as an unsigned int.
*/
private int get1(int pc) {
return code[pc] & 0xFF;
}
/** Return two code bytes at position pc as an unsigned int.
*/
private int get2(int pc) {
return (get1(pc) << 8) | get1(pc+1);
}
/** Return four code bytes at position pc as an int.
*/
public int get4(int pc) {
// pre: pc + 4 <= cp
return
(get1(pc) << 24) |
(get1(pc+1) << 16) |
(get1(pc+2) << 8) |
(get1(pc+3));
}
/** Is code generation currently enabled?
*/
public boolean isAlive() {
return alive || pendingJumps != null;
}
/** Switch code generation on/off.
*/
public void markDead() {
alive = false;
}
/** Declare an entry point; return current code pointer
*/
public int entryPoint() {
int pc = curPc();
alive = true;
pendingStackMap = needStackMap;
return pc;
}
/** Declare an entry point with initial state;
* return current code pointer
*/
public int entryPoint(State state) {
int pc = curPc();
alive = true;
this.state = state.dup();
assert state.stacksize <= max_stack;
if (debugCode) System.err.println("entry point " + state);
pendingStackMap = needStackMap;
return pc;
}
/** Declare an entry point with initial state plus a pushed value;
* return current code pointer
*/
public int entryPoint(State state, Type pushed) {
int pc = curPc();
alive = true;
this.state = state.dup();
assert state.stacksize <= max_stack;
this.state.push(pushed);
if (debugCode) System.err.println("entry point " + state);
pendingStackMap = needStackMap;
return pc;
}
/**************************************************************************
* Stack map generation
*************************************************************************/
/** An entry in the stack map. */
static class StackMapFrame {
int pc;
Type[] locals;
Type[] stack;
}
/** A buffer of cldc stack map entries. */
StackMapFrame[] stackMapBuffer = null;
/** A buffer of compressed StackMapTable entries. */
StackMapTableFrame[] stackMapTableBuffer = null;
int stackMapBufferSize = 0;
/** The last PC at which we generated a stack map. */
int lastStackMapPC = -1;
/** The last stack map frame in StackMapTable. */
StackMapFrame lastFrame = null;
/** The stack map frame before the last one. */
StackMapFrame frameBeforeLast = null;
/** Emit a stack map entry. */
public void emitStackMap() {
int pc = curPc();
if (!needStackMap) return;
switch (stackMap) {
case CLDC:
emitCLDCStackMap(pc, getLocalsSize());
break;
case JSR202:
emitStackMapFrame(pc, getLocalsSize());
break;
default:
throw new AssertionError("Should have chosen a stackmap format");
}
// DEBUG code follows
if (debugCode) state.dump(pc);
}
private int getLocalsSize() {
int nextLocal = 0;
for (int i=max_locals-1; i>=0; i--) {
if (state.defined.isMember(i) && lvar[i] != null) {
nextLocal = i + width(lvar[i].sym.erasure(types));
break;
}
}
return nextLocal;
}
/** Emit a CLDC stack map frame. */
void emitCLDCStackMap(int pc, int localsSize) {
if (lastStackMapPC == pc) {
// drop existing stackmap at this offset
stackMapBuffer[--stackMapBufferSize] = null;
}
lastStackMapPC = pc;
if (stackMapBuffer == null) {
stackMapBuffer = new StackMapFrame[20];
} else if (stackMapBuffer.length == stackMapBufferSize) {
StackMapFrame[] newStackMapBuffer =
new StackMapFrame[stackMapBufferSize << 1];
System.arraycopy(stackMapBuffer, 0, newStackMapBuffer,
0, stackMapBufferSize);
stackMapBuffer = newStackMapBuffer;
}
StackMapFrame frame =
stackMapBuffer[stackMapBufferSize++] = new StackMapFrame();
frame.pc = pc;
frame.locals = new Type[localsSize];
for (int i=0; i<localsSize; i++) {
if (state.defined.isMember(i) && lvar[i] != null) {
Type vtype = lvar[i].sym.type;
if (!(vtype instanceof UninitializedType))
vtype = types.erasure(vtype);
frame.locals[i] = vtype;
}
}
frame.stack = new Type[state.stacksize];
for (int i=0; i<state.stacksize; i++)
frame.stack[i] = state.stack[i];
}
void emitStackMapFrame(int pc, int localsSize) {
if (lastFrame == null) {
// first frame
lastFrame = getInitialFrame();
} else if (lastFrame.pc == pc) {
// drop existing stackmap at this offset
stackMapTableBuffer[--stackMapBufferSize] = null;
lastFrame = frameBeforeLast;
frameBeforeLast = null;
}
StackMapFrame frame = new StackMapFrame();
frame.pc = pc;
int localCount = 0;
Type[] locals = new Type[localsSize];
for (int i=0; i<localsSize; i++, localCount++) {
if (state.defined.isMember(i) && lvar[i] != null) {
Type vtype = lvar[i].sym.type;
if (!(vtype instanceof UninitializedType))
vtype = types.erasure(vtype);
locals[i] = vtype;
if (width(vtype) > 1) i++;
}
}
frame.locals = new Type[localCount];
for (int i=0, j=0; i<localsSize; i++, j++) {
assert(j < localCount);
frame.locals[j] = locals[i];
if (width(locals[i]) > 1) i++;
}
int stackCount = 0;
for (int i=0; i<state.stacksize; i++) {
if (state.stack[i] != null) {
stackCount++;
}
}
frame.stack = new Type[stackCount];
stackCount = 0;
for (int i=0; i<state.stacksize; i++) {
if (state.stack[i] != null) {
frame.stack[stackCount++] = state.stack[i];
}
}
if (stackMapTableBuffer == null) {
stackMapTableBuffer = new StackMapTableFrame[20];
} else if (stackMapTableBuffer.length == stackMapBufferSize) {
StackMapTableFrame[] newStackMapTableBuffer =
new StackMapTableFrame[stackMapBufferSize << 1];
System.arraycopy(stackMapTableBuffer, 0, newStackMapTableBuffer,
0, stackMapBufferSize);
stackMapTableBuffer = newStackMapTableBuffer;
}
stackMapTableBuffer[stackMapBufferSize++] =
StackMapTableFrame.getInstance(frame, lastFrame.pc, lastFrame.locals, types);
frameBeforeLast = lastFrame;
lastFrame = frame;
}
StackMapFrame getInitialFrame() {
StackMapFrame frame = new StackMapFrame();
List<Type> arg_types = ((MethodType)meth.externalType(types)).argtypes;
int len = arg_types.length();
int count = 0;
if (!meth.isStatic()) {
Type thisType = meth.owner.type;
frame.locals = new Type[len+1];
if (meth.isConstructor() && thisType != syms.objectType) {
frame.locals[count++] = UninitializedType.uninitializedThis(thisType);
} else {
frame.locals[count++] = types.erasure(thisType);
}
} else {
frame.locals = new Type[len];
}
for (Type arg_type : arg_types) {
frame.locals[count++] = types.erasure(arg_type);
}
frame.pc = -1;
frame.stack = null;
return frame;
}
/**************************************************************************
* Operations having to do with jumps
*************************************************************************/
/** A chain represents a list of unresolved jumps. Jump locations
* are sorted in decreasing order.
*/
public static class Chain {
/** The position of the jump instruction.
*/
public final int pc;
/** The machine state after the jump instruction.
* Invariant: all elements of a chain list have the same stacksize
* and compatible stack and register contents.
*/
Code.State state;
/** The next jump in the list.
*/
public final Chain next;
/** Construct a chain from its jump position, stacksize, previous
* chain, and machine state.
*/
public Chain(int pc, Chain next, Code.State state) {
this.pc = pc;
this.next = next;
this.state = state;
}
}
/** Negate a branch opcode.
*/
public static int negate(int opcode) {
if (opcode == if_acmp_null) return if_acmp_nonnull;
else if (opcode == if_acmp_nonnull) return if_acmp_null;
else return ((opcode + 1) ^ 1) - 1;
}
/** Emit a jump instruction.
* Return code pointer of instruction to be patched.
*/
public int emitJump(int opcode) {
if (fatcode) {
if (opcode == goto_ || opcode == jsr) {
emitop4(opcode + goto_w - goto_, 0);
} else {
emitop2(negate(opcode), 8);
emitop4(goto_w, 0);
alive = true;
pendingStackMap = needStackMap;
}
return cp - 5;
} else {
emitop2(opcode, 0);
return cp - 3;
}
}
/** Emit a branch with given opcode; return its chain.
* branch differs from jump in that jsr is treated as no-op.
*/
public Chain branch(int opcode) {
Chain result = null;
if (opcode == goto_) {
result = pendingJumps;
pendingJumps = null;
}
if (opcode != dontgoto && isAlive()) {
result = new Chain(emitJump(opcode),
result,
state.dup());
fixedPc = fatcode;
if (opcode == goto_) alive = false;
}
return result;
}
/** Resolve chain to point to given target.
*/
public void resolve(Chain chain, int target) {
boolean changed = false;
State newState = state;
for (; chain != null; chain = chain.next) {
assert state != chain.state;
assert target > chain.pc || state.stacksize == 0;
if (target >= cp) {
target = cp;
} else if (get1(target) == goto_) {
if (fatcode) target = target + get4(target + 1);
else target = target + get2(target + 1);
}
if (get1(chain.pc) == goto_ &&
chain.pc + 3 == target && target == cp && !fixedPc) {
// If goto the next instruction, the jump is not needed:
// compact the code.
cp = cp - 3;
target = target - 3;
if (chain.next == null) {
// This is the only jump to the target. Exit the loop
// without setting new state. The code is reachable
// from the instruction before goto_.
alive = true;
break;
}
} else {
if (fatcode)
put4(chain.pc + 1, target - chain.pc);
else if (target - chain.pc < Short.MIN_VALUE ||
target - chain.pc > Short.MAX_VALUE)
fatcode = true;
else
put2(chain.pc + 1, target - chain.pc);
assert !alive ||
chain.state.stacksize == newState.stacksize &&
chain.state.nlocks == newState.nlocks;
}
fixedPc = true;
if (cp == target) {
changed = true;
if (debugCode)
System.err.println("resolving chain state=" + chain.state);
if (alive) {
newState = chain.state.join(newState);
} else {
newState = chain.state;
alive = true;
}
}
}
assert !changed || state != newState;
if (state != newState) {
setDefined(newState.defined);
state = newState;
pendingStackMap = needStackMap;
}
}
/** Resolve chain to point to current code pointer.
*/
public void resolve(Chain chain) {
assert
!alive ||
chain==null ||
state.stacksize == chain.state.stacksize &&
state.nlocks == chain.state.nlocks;
pendingJumps = mergeChains(chain, pendingJumps);
}
/** Resolve any pending jumps.
*/
public void resolvePending() {
Chain x = pendingJumps;
pendingJumps = null;
resolve(x, cp);
}
/** Merge the jumps in of two chains into one.
*/
public static Chain mergeChains(Chain chain1, Chain chain2) {
// recursive merge sort
if (chain2 == null) return chain1;
if (chain1 == null) return chain2;
assert
chain1.state.stacksize == chain2.state.stacksize &&
chain1.state.nlocks == chain2.state.nlocks;
if (chain1.pc < chain2.pc)
return new Chain(
chain2.pc,
mergeChains(chain1, chain2.next),
chain2.state);
return new Chain(
chain1.pc,
mergeChains(chain1.next, chain2),
chain1.state);
}
/* **************************************************************************
* Catch clauses
****************************************************************************/
/** Add a catch clause to code.
*/
public void addCatch(
char startPc, char endPc, char handlerPc, char catchType) {
catchInfo.append(new char[]{startPc, endPc, handlerPc, catchType});
}
/* **************************************************************************
* Line numbers
****************************************************************************/
/** Add a line number entry.
*/
public void addLineNumber(char startPc, char lineNumber) {
if (lineDebugInfo) {
if (lineInfo.nonEmpty() && lineInfo.head[0] == startPc)
lineInfo = lineInfo.tail;
if (lineInfo.isEmpty() || lineInfo.head[1] != lineNumber)
lineInfo = lineInfo.prepend(new char[]{startPc, lineNumber});
}
}
/** Mark beginning of statement.
*/
public void statBegin(int pos) {
if (pos != Position.NOPOS) {
pendingStatPos = pos;
}
}
/** Force stat begin eagerly
*/
public void markStatBegin() {
if (alive && lineDebugInfo) {
int line = lineMap.getLineNumber(pendingStatPos);
char cp1 = (char)cp;
char line1 = (char)line;
if (cp1 == cp && line1 == line)
addLineNumber(cp1, line1);
}
pendingStatPos = Position.NOPOS;
}
/* **************************************************************************
* Simulated VM machine state
****************************************************************************/
class State implements Cloneable {
/** The set of registers containing values. */
Bits defined;
/** The (types of the) contents of the machine stack. */
Type[] stack;
/** The first stack position currently unused. */
int stacksize;
/** The numbers of registers containing locked monitors. */
int[] locks;
int nlocks;
State() {
defined = new Bits();
stack = new Type[16];
}
State dup() {
try {
State state = (State)super.clone();
state.defined = defined.dup();
state.stack = stack.clone();
if (locks != null) state.locks = locks.clone();
if (debugCode) {
System.err.println("duping state " + this);
dump();
}
return state;
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
void lock(int register) {
if (locks == null) {
locks = new int[20];
} else if (locks.length == nlocks) {
int[] newLocks = new int[locks.length << 1];
System.arraycopy(locks, 0, newLocks, 0, locks.length);
locks = newLocks;
}
locks[nlocks] = register;
nlocks++;
}
void unlock(int register) {
nlocks--;
assert locks[nlocks] == register;
locks[nlocks] = -1;
}
void push(Type t) {
if (debugCode) System.err.println(" pushing " + t);
switch (t.tag) {
case TypeTags.VOID:
return;
case TypeTags.BYTE:
case TypeTags.CHAR:
case TypeTags.SHORT:
case TypeTags.BOOLEAN:
t = syms.intType;
break;
default:
break;
}
if (stacksize+2 >= stack.length) {
Type[] newstack = new Type[2*stack.length];
System.arraycopy(stack, 0, newstack, 0, stack.length);
stack = newstack;
}
stack[stacksize++] = t;
switch (width(t)) {
case 1:
break;
case 2:
stack[stacksize++] = null;
break;
default:
throw new AssertionError(t);
}
if (stacksize > max_stack)
max_stack = stacksize;
}
Type pop1() {
if (debugCode) System.err.println(" popping " + 1);
stacksize--;
Type result = stack[stacksize];
stack[stacksize] = null;
assert result != null && width(result) == 1;
return result;
}
Type peek() {
return stack[stacksize-1];
}
Type pop2() {
if (debugCode) System.err.println(" popping " + 2);
stacksize -= 2;
Type result = stack[stacksize];
stack[stacksize] = null;
assert stack[stacksize+1] == null;
assert result != null && width(result) == 2;
return result;
}
void pop(int n) {
if (debugCode) System.err.println(" popping " + n);
while (n > 0) {
stack[--stacksize] = null;
n--;
}
}
void pop(Type t) {
pop(width(t));
}
/** Force the top of the stack to be treated as this supertype
* of its current type. */
void forceStackTop(Type t) {
if (!alive) return;
switch (t.tag) {
case CLASS:
case ARRAY:
int width = width(t);
Type old = stack[stacksize-width];
assert types.isSubtype(types.erasure(old),
types.erasure(t));
stack[stacksize-width] = t;
break;
default:
}
}
void markInitialized(UninitializedType old) {
Type newtype = old.initializedType();
for (int i=0; i<stacksize; i++)
if (stack[i] == old) stack[i] = newtype;
for (int i=0; i<lvar.length; i++) {
LocalVar lv = lvar[i];
if (lv != null && lv.sym.type == old) {
VarSymbol sym = lv.sym;
sym = sym.clone(sym.owner);
sym.type = newtype;
LocalVar newlv = lvar[i] = new LocalVar(sym);
// should the following be initialized to cp?
newlv.start_pc = lv.start_pc;
}
}
}
State join(State other) {
defined = defined.andSet(other.defined);
assert stacksize == other.stacksize;
assert nlocks == other.nlocks;
for (int i=0; i<stacksize; ) {
Type t = stack[i];
Type tother = other.stack[i];
Type result =
t==tother ? t :
types.isSubtype(t, tother) ? tother :
types.isSubtype(tother, t) ? t :
error();
int w = width(result);
stack[i] = result;
if (w == 2) assert stack[i+1] == null;
i += w;
}
return this;
}
Type error() {
throw new AssertionError("inconsistent stack types at join point");
}
void dump() {
dump(-1);
}
void dump(int pc) {
System.err.print("stackMap for " + meth.owner + "." + meth);
if (pc == -1)
System.out.println();
else
System.out.println(" at " + pc);
System.err.println(" stack (from bottom):");
for (int i=0; i<stacksize; i++)
System.err.println(" " + i + ": " + stack[i]);
int lastLocal = 0;
for (int i=max_locals-1; i>=0; i--) {
if (defined.isMember(i)) {
lastLocal = i;
break;
}
}
if (lastLocal >= 0)
System.err.println(" locals:");
for (int i=0; i<=lastLocal; i++) {
System.err.print(" " + i + ": ");
if (defined.isMember(i)) {
LocalVar var = lvar[i];
if (var == null) {
System.err.println("(none)");
} else if (var.sym == null)
System.err.println("UNKNOWN!");
else
System.err.println("" + var.sym + " of type " +
var.sym.erasure(types));
} else {
System.err.println("undefined");
}
}
if (nlocks != 0) {
System.err.print(" locks:");
for (int i=0; i<nlocks; i++) {
System.err.print(" " + locks[i]);
}
System.err.println();
}
}
}
static Type jsrReturnValue = new Type(TypeTags.INT, null);
/* **************************************************************************
* Local variables
****************************************************************************/
/** A live range of a local variable. */
static class LocalVar {
final VarSymbol sym;
final char reg;
char start_pc = Character.MAX_VALUE;
char length = Character.MAX_VALUE;
LocalVar(VarSymbol v) {
this.sym = v;
this.reg = (char)v.adr;
}
public LocalVar dup() {
return new LocalVar(sym);
}
public String toString() {
return "" + sym + " in register " + ((int)reg) + " starts at pc=" + ((int)start_pc) + " length=" + ((int)length);
}
};
/** Local variables, indexed by register. */
LocalVar[] lvar;
/** Add a new local variable. */
private void addLocalVar(VarSymbol v) {
int adr = v.adr;
if (adr+1 >= lvar.length) {
int newlength = lvar.length << 1;
if (newlength <= adr) newlength = adr + 10;
LocalVar[] new_lvar = new LocalVar[newlength];
System.arraycopy(lvar, 0, new_lvar, 0, lvar.length);
lvar = new_lvar;
}
assert lvar[adr] == null;
if (pendingJumps != null) resolvePending();
lvar[adr] = new LocalVar(v);
state.defined.excl(adr);
}
/** Set the current variable defined state. */
public void setDefined(Bits newDefined) {
if (alive && newDefined != state.defined) {
Bits diff = state.defined.dup().xorSet(newDefined);
for (int adr = diff.nextBit(0);
adr >= 0;
adr = diff.nextBit(adr+1)) {
if (adr >= nextreg)
state.defined.excl(adr);
else if (state.defined.isMember(adr))
setUndefined(adr);
else
setDefined(adr);
}
}
}
/** Mark a register as being (possibly) defined. */
public void setDefined(int adr) {
LocalVar v = lvar[adr];
if (v == null) {
state.defined.excl(adr);
} else {
state.defined.incl(adr);
if (cp < Character.MAX_VALUE) {
if (v.start_pc == Character.MAX_VALUE)
v.start_pc = (char)cp;
}
}
}
/** Mark a register as being undefined. */
public void setUndefined(int adr) {
state.defined.excl(adr);
if (adr < lvar.length &&
lvar[adr] != null &&
lvar[adr].start_pc != Character.MAX_VALUE) {
LocalVar v = lvar[adr];
char length = (char)(curPc() - v.start_pc);
if (length > 0 && length < Character.MAX_VALUE) {
lvar[adr] = v.dup();
v.length = length;
putVar(v);
} else {
v.start_pc = Character.MAX_VALUE;
}
}
}
/** End the scope of a variable. */
private void endScope(int adr) {
LocalVar v = lvar[adr];
if (v != null) {
lvar[adr] = null;
if (v.start_pc != Character.MAX_VALUE) {
char length = (char)(curPc() - v.start_pc);
if (length < Character.MAX_VALUE) {
v.length = length;
putVar(v);
}
}
}
state.defined.excl(adr);
}
/** Put a live variable range into the buffer to be output to the
* class file.
*/
void putVar(LocalVar var) {
if (!varDebugInfo) return;
if ((var.sym.flags() & Flags.SYNTHETIC) != 0) return;
if (varBuffer == null)
varBuffer = new LocalVar[20];
else if (varBufferSize >= varBuffer.length) {
LocalVar[] newVarBuffer = new LocalVar[varBufferSize*2];
System.arraycopy(varBuffer, 0, newVarBuffer, 0, varBuffer.length);
varBuffer = newVarBuffer;
}
varBuffer[varBufferSize++] = var;
}
/** Previously live local variables, to be put into the variable table. */
LocalVar[] varBuffer;
int varBufferSize;
/** Create a new local variable address and return it.
*/
private int newLocal(int typecode) {
int reg = nextreg;
int w = width(typecode);
nextreg = reg + w;
if (nextreg > max_locals) max_locals = nextreg;
return reg;
}
private int newLocal(Type type) {
return newLocal(typecode(type));
}
public int newLocal(VarSymbol v) {
int reg = v.adr = newLocal(v.erasure(types));
addLocalVar(v);
return reg;
}
/** Start a set of fresh registers.
*/
public void newRegSegment() {
nextreg = max_locals;
}
/** End scopes of all variables with registers >= first.
*/
public void endScopes(int first) {
int prevNextReg = nextreg;
nextreg = first;
for (int i = nextreg; i < prevNextReg; i++) endScope(i);
}
/**************************************************************************
* static tables
*************************************************************************/
public static String mnem(int opcode) {
return Mneumonics.mnem[opcode];
}
private static class Mneumonics {
private final static String[] mnem = new String[ByteCodeCount];
static {
mnem[nop] = "nop";
mnem[aconst_null] = "aconst_null";
mnem[iconst_m1] = "iconst_m1";
mnem[iconst_0] = "iconst_0";
mnem[iconst_1] = "iconst_1";
mnem[iconst_2] = "iconst_2";
mnem[iconst_3] = "iconst_3";
mnem[iconst_4] = "iconst_4";
mnem[iconst_5] = "iconst_5";
mnem[lconst_0] = "lconst_0";
mnem[lconst_1] = "lconst_1";
mnem[fconst_0] = "fconst_0";
mnem[fconst_1] = "fconst_1";
mnem[fconst_2] = "fconst_2";
mnem[dconst_0] = "dconst_0";
mnem[dconst_1] = "dconst_1";
mnem[bipush] = "bipush";
mnem[sipush] = "sipush";
mnem[ldc1] = "ldc1";
mnem[ldc2] = "ldc2";
mnem[ldc2w] = "ldc2w";
mnem[iload] = "iload";
mnem[lload] = "lload";
mnem[fload] = "fload";
mnem[dload] = "dload";
mnem[aload] = "aload";
mnem[iload_0] = "iload_0";
mnem[lload_0] = "lload_0";
mnem[fload_0] = "fload_0";
mnem[dload_0] = "dload_0";
mnem[aload_0] = "aload_0";
mnem[iload_1] = "iload_1";
mnem[lload_1] = "lload_1";
mnem[fload_1] = "fload_1";
mnem[dload_1] = "dload_1";
mnem[aload_1] = "aload_1";
mnem[iload_2] = "iload_2";
mnem[lload_2] = "lload_2";
mnem[fload_2] = "fload_2";
mnem[dload_2] = "dload_2";
mnem[aload_2] = "aload_2";
mnem[iload_3] = "iload_3";
mnem[lload_3] = "lload_3";
mnem[fload_3] = "fload_3";
mnem[dload_3] = "dload_3";
mnem[aload_3] = "aload_3";
mnem[iaload] = "iaload";
mnem[laload] = "laload";
mnem[faload] = "faload";
mnem[daload] = "daload";
mnem[aaload] = "aaload";
mnem[baload] = "baload";
mnem[caload] = "caload";
mnem[saload] = "saload";
mnem[istore] = "istore";
mnem[lstore] = "lstore";
mnem[fstore] = "fstore";
mnem[dstore] = "dstore";
mnem[astore] = "astore";
mnem[istore_0] = "istore_0";
mnem[lstore_0] = "lstore_0";
mnem[fstore_0] = "fstore_0";
mnem[dstore_0] = "dstore_0";
mnem[astore_0] = "astore_0";
mnem[istore_1] = "istore_1";
mnem[lstore_1] = "lstore_1";
mnem[fstore_1] = "fstore_1";
mnem[dstore_1] = "dstore_1";
mnem[astore_1] = "astore_1";
mnem[istore_2] = "istore_2";
mnem[lstore_2] = "lstore_2";
mnem[fstore_2] = "fstore_2";
mnem[dstore_2] = "dstore_2";
mnem[astore_2] = "astore_2";
mnem[istore_3] = "istore_3";
mnem[lstore_3] = "lstore_3";
mnem[fstore_3] = "fstore_3";
mnem[dstore_3] = "dstore_3";
mnem[astore_3] = "astore_3";
mnem[iastore] = "iastore";
mnem[lastore] = "lastore";
mnem[fastore] = "fastore";
mnem[dastore] = "dastore";
mnem[aastore] = "aastore";
mnem[bastore] = "bastore";
mnem[castore] = "castore";
mnem[sastore] = "sastore";
mnem[pop] = "pop";
mnem[pop2] = "pop2";
mnem[dup] = "dup";
mnem[dup_x1] = "dup_x1";
mnem[dup_x2] = "dup_x2";
mnem[dup2] = "dup2";
mnem[dup2_x1] = "dup2_x1";
mnem[dup2_x2] = "dup2_x2";
mnem[swap] = "swap";
mnem[iadd] = "iadd";
mnem[ladd] = "ladd";
mnem[fadd] = "fadd";
mnem[dadd] = "dadd";
mnem[isub] = "isub";
mnem[lsub] = "lsub";
mnem[fsub] = "fsub";
mnem[dsub] = "dsub";
mnem[imul] = "imul";
mnem[lmul] = "lmul";
mnem[fmul] = "fmul";
mnem[dmul] = "dmul";
mnem[idiv] = "idiv";
mnem[ldiv] = "ldiv";
mnem[fdiv] = "fdiv";
mnem[ddiv] = "ddiv";
mnem[imod] = "imod";
mnem[lmod] = "lmod";
mnem[fmod] = "fmod";
mnem[dmod] = "dmod";
mnem[ineg] = "ineg";
mnem[lneg] = "lneg";
mnem[fneg] = "fneg";
mnem[dneg] = "dneg";
mnem[ishl] = "ishl";
mnem[lshl] = "lshl";
mnem[ishr] = "ishr";
mnem[lshr] = "lshr";
mnem[iushr] = "iushr";
mnem[lushr] = "lushr";
mnem[iand] = "iand";
mnem[land] = "land";
mnem[ior] = "ior";
mnem[lor] = "lor";
mnem[ixor] = "ixor";
mnem[lxor] = "lxor";
mnem[iinc] = "iinc";
mnem[i2l] = "i2l";
mnem[i2f] = "i2f";
mnem[i2d] = "i2d";
mnem[l2i] = "l2i";
mnem[l2f] = "l2f";
mnem[l2d] = "l2d";
mnem[f2i] = "f2i";
mnem[f2l] = "f2l";
mnem[f2d] = "f2d";
mnem[d2i] = "d2i";
mnem[d2l] = "d2l";
mnem[d2f] = "d2f";
mnem[int2byte] = "int2byte";
mnem[int2char] = "int2char";
mnem[int2short] = "int2short";
mnem[lcmp] = "lcmp";
mnem[fcmpl] = "fcmpl";
mnem[fcmpg] = "fcmpg";
mnem[dcmpl] = "dcmpl";
mnem[dcmpg] = "dcmpg";
mnem[ifeq] = "ifeq";
mnem[ifne] = "ifne";
mnem[iflt] = "iflt";
mnem[ifge] = "ifge";
mnem[ifgt] = "ifgt";
mnem[ifle] = "ifle";
mnem[if_icmpeq] = "if_icmpeq";
mnem[if_icmpne] = "if_icmpne";
mnem[if_icmplt] = "if_icmplt";
mnem[if_icmpge] = "if_icmpge";
mnem[if_icmpgt] = "if_icmpgt";
mnem[if_icmple] = "if_icmple";
mnem[if_acmpeq] = "if_acmpeq";
mnem[if_acmpne] = "if_acmpne";
mnem[goto_] = "goto_";
mnem[jsr] = "jsr";
mnem[ret] = "ret";
mnem[tableswitch] = "tableswitch";
mnem[lookupswitch] = "lookupswitch";
mnem[ireturn] = "ireturn";
mnem[lreturn] = "lreturn";
mnem[freturn] = "freturn";
mnem[dreturn] = "dreturn";
mnem[areturn] = "areturn";
mnem[return_] = "return_";
mnem[getstatic] = "getstatic";
mnem[putstatic] = "putstatic";
mnem[getfield] = "getfield";
mnem[putfield] = "putfield";
mnem[invokevirtual] = "invokevirtual";
mnem[invokespecial] = "invokespecial";
mnem[invokestatic] = "invokestatic";
mnem[invokeinterface] = "invokeinterface";
// mnem[___unused___] = "___unused___";
mnem[new_] = "new_";
mnem[newarray] = "newarray";
mnem[anewarray] = "anewarray";
mnem[arraylength] = "arraylength";
mnem[athrow] = "athrow";
mnem[checkcast] = "checkcast";
mnem[instanceof_] = "instanceof_";
mnem[monitorenter] = "monitorenter";
mnem[monitorexit] = "monitorexit";
mnem[wide] = "wide";
mnem[multianewarray] = "multianewarray";
mnem[if_acmp_null] = "if_acmp_null";
mnem[if_acmp_nonnull] = "if_acmp_nonnull";
mnem[goto_w] = "goto_w";
mnem[jsr_w] = "jsr_w";
mnem[breakpoint] = "breakpoint";
}
}
}