/* * Copyright (c) 1999, 2018, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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.code.Types.UniqueType; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import static com.sun.tools.javac.code.TypeTag.BOT; import static com.sun.tools.javac.code.TypeTag.INT; 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. * *

This is NOT part of any supported API. * 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. */ public class Code { public final boolean debugCode; public final boolean needStackMap; public enum StackMapFormat { NONE, CLDC { Name getAttributeName(Names names) { return names.StackMap; } }, JSR202 { Name getAttributeName(Names names) { return names.StackMapTable; } }; Name getAttributeName(Names 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, Errors.LimitCode); return true; } if (max_locals > ClassFile.MAX_LOCALS) { log.error(pos, Errors.LimitLocals); return true; } if (max_stack > ClassFile.MAX_STACK) { log.error(pos, Errors.LimitStack); return true; } return false; } /** A buffer for expression catch data. Each enter is a vector * of four unsigned shorts. */ ListBuffer catchInfo = new ListBuffer<>(); /** A buffer for line number information. Each entry is a vector * of two unsigned shorts. */ List 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; private int letExprStackPos = 0; /** 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.getTag()) { 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.getTag()); } } /** 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 types) { int w = 0; for (List 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.getTag()) { 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 curCP() { /* * This method has side-effects because calling it can indirectly provoke * extra code generation, like goto instructions, depending on the context * where it's called. * Use with care or even better avoid using it. */ 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; code = ArrayUtils.ensureCapacity(code, cp); 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.check(alive || isStatementStart()); } /** Emit a ldc (or ldc_w) instruction, taking into account operand size */ public void emitLdc(int od) { if (od <= 255) { emitop1(ldc1, od); } else { emitop2(ldc2, od); } } /** 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 invokedynamic instruction. */ public void emitInvokedynamic(int desc, Type mtype) { int argsize = width(mtype.getParameterTypes()); emitop(invokedynamic); if (!alive) return; emit2(desc); emit2(0); state.pop(argsize); 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]; Assert.check(!a.hasTag(BOT)); // null type as is cannot be indexed. 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.check(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.check(state.nlocks == 0); state.pop(2); markDead(); break; case dup: state.push(state.stack[state.stacksize-1]); break; case return_: Assert.check(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 Pool.MethodHandle) return syms.methodHandleType; if (o instanceof UniqueType) return typeForPool(((UniqueType)o).type); if (o instanceof Type) { Type ty = (Type) o; if (ty instanceof Type.ArrayType) return syms.classType; if (ty instanceof Type.MethodType) return syms.methodTypeType; } throw new AssertionError("Invalid type of constant pool entry: " + o.getClass()); } /** 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_: Symbol sym; if (pool.pool[od] instanceof UniqueType) { // Required by change in Gen.makeRef to allow // annotated types. // TODO: is this needed anywhere else? sym = ((UniqueType)(pool.pool[od])).type.tsym; } else { sym = (Symbol)(pool.pool[od]); } state.push(uninitializedObject(sym.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((((UniqueType)o).type)); 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: {@literal pc + 1 <= cp }. */ private void put1(int pc, int op) { code[pc] = (byte)op; } /** Place two bytes into code at address pc. * Pre: {@literal 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: {@literal 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 = curCP(); alive = true; pendingStackMap = needStackMap; return pc; } /** Declare an entry point with initial state; * return current code pointer */ public int entryPoint(State state) { int pc = curCP(); alive = true; State newState = state.dup(); setDefined(newState.defined); this.state = newState; Assert.check(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 = curCP(); alive = true; State newState = state.dup(); setDefined(newState.defined); this.state = newState; Assert.check(state.stacksize <= max_stack); this.state.push(pushed); if (debugCode) System.err.println("entry point " + state); pendingStackMap = needStackMap; return pc; } public int setLetExprStackPos(int pos) { int res = letExprStackPos; letExprStackPos = pos; return res; } public boolean isStatementStart() { return state.stacksize == letExprStackPos; } /************************************************************************** * 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 = curCP(); 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 { stackMapBuffer = ArrayUtils.ensureCapacity(stackMapBuffer, stackMapBufferSize); } StackMapFrame frame = stackMapBuffer[stackMapBufferSize++] = new StackMapFrame(); frame.pc = pc; frame.locals = new Type[localsSize]; for (int i=0; i 1) i++; } } frame.locals = new Type[localCount]; for (int i=0, j=0; i 1) i++; } int stackCount = 0; for (int i=0; i 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.check(state != chain.state && (target > chain.pc || isStatementStart())); 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. if (varDebugInfo) { adjustAliveRanges(cp, -3); } 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.check(!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.check(!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.check( !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.check( 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}); } public void compressCatchTable() { ListBuffer compressedCatchInfo = new ListBuffer<>(); List handlerPcs = List.nil(); for (char[] catchEntry : catchInfo) { handlerPcs = handlerPcs.prepend((int)catchEntry[2]); } for (char[] catchEntry : catchInfo) { int startpc = catchEntry[0]; int endpc = catchEntry[1]; if (startpc == endpc || (startpc == (endpc - 1) && handlerPcs.contains(startpc))) { continue; } else { compressedCatchInfo.append(catchEntry); } } catchInfo = compressedCatchInfo; } /* ************************************************************************** * 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 = new Bits(defined); 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 { locks = ArrayUtils.ensureCapacity(locks, nlocks); } locks[nlocks] = register; nlocks++; } void unlock(int register) { nlocks--; Assert.check(locks[nlocks] == register); locks[nlocks] = -1; } void push(Type t) { if (debugCode) System.err.println(" pushing " + t); switch (t.getTag()) { case VOID: return; case BYTE: case CHAR: case SHORT: case BOOLEAN: t = syms.intType; break; default: break; } stack = ArrayUtils.ensureCapacity(stack, stacksize+2); 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.check(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.check(stack[stacksize+1] == null && 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.getTag()) { case CLASS: case ARRAY: int width = width(t); Type old = stack[stacksize-width]; Assert.check(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=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 aliveRanges = new java.util.ArrayList<>(); LocalVar(VarSymbol v) { this.sym = v; this.reg = (char)v.adr; } public LocalVar dup() { return new LocalVar(sym); } Range firstRange() { return aliveRanges.isEmpty() ? null : aliveRanges.get(0); } Range lastRange() { return aliveRanges.isEmpty() ? null : aliveRanges.get(aliveRanges.size() - 1); } void removeLastRange() { Range lastRange = lastRange(); if (lastRange != null) { aliveRanges.remove(lastRange); } } @Override public String toString() { if (aliveRanges == null) { return "empty local var"; } StringBuilder sb = new StringBuilder().append(sym) .append(" in register ").append((int)reg).append(" \n"); for (Range r : aliveRanges) { sb.append(" starts at pc=").append(Integer.toString(((int)r.start_pc))) .append(" length=").append(Integer.toString(((int)r.length))) .append("\n"); } return sb.toString(); } public void openRange(char start) { if (!hasOpenRange()) { aliveRanges.add(new Range(start)); } } public void closeRange(char length) { if (isLastRangeInitialized() && length > 0) { Range range = lastRange(); if (range != null) { if (range.length == Character.MAX_VALUE) { range.length = length; } } } else { removeLastRange(); } } public boolean hasOpenRange() { if (aliveRanges.isEmpty()) { return false; } return lastRange().length == Character.MAX_VALUE; } public boolean isLastRangeInitialized() { if (aliveRanges.isEmpty()) { return false; } return lastRange().start_pc != Character.MAX_VALUE; } public Range getWidestRange() { if (aliveRanges.isEmpty()) { return new Range(); } else { Range firstRange = firstRange(); Range lastRange = lastRange(); char length = (char)(lastRange.length + (lastRange.start_pc - firstRange.start_pc)); return new Range(firstRange.start_pc, length); } } } /** Local variables, indexed by register. */ LocalVar[] lvar; /** Add a new local variable. */ private void addLocalVar(VarSymbol v) { int adr = v.adr; lvar = ArrayUtils.ensureCapacity(lvar, adr+1); Assert.checkNull(lvar[adr]); if (pendingJumps != null) { resolvePending(); } lvar[adr] = new LocalVar(v); state.defined.excl(adr); } void adjustAliveRanges(int oldCP, int delta) { for (LocalVar localVar: lvar) { if (localVar != null) { for (LocalVar.Range range: localVar.aliveRanges) { if (range.closed() && range.start_pc + range.length >= oldCP) { range.length += delta; } } } } } /** * Calculates the size of the LocalVariableTable. */ public int getLVTSize() { int result = varBufferSize; for (int i = 0; i < varBufferSize; i++) { LocalVar var = varBuffer[i]; result += var.aliveRanges.size() - 1; } return result; } /** Set the current variable defined state. */ public void setDefined(Bits newDefined) { if (alive && newDefined != state.defined) { Bits diff = new Bits(state.defined).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) { v.openRange((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].isLastRangeInitialized()) { LocalVar v = lvar[adr]; char length = (char)(curCP() - v.lastRange().start_pc); if (length < Character.MAX_VALUE) { lvar[adr] = v.dup(); v.closeRange(length); putVar(v); } else { v.removeLastRange(); } } } /** End the scope of a variable. */ private void endScope(int adr) { LocalVar v = lvar[adr]; if (v != null) { if (v.isLastRangeInitialized()) { char length = (char)(curCP() - v.lastRange().start_pc); if (length < Character.MAX_VALUE) { v.closeRange(length); putVar(v); fillLocalVarPosition(v); } } /** the call to curCP() can implicitly adjust the current cp, if so * the alive range of local variables may be modified. Thus we need * all of them. For this reason assigning null to the given address * should be the last action to do. */ lvar[adr] = null; } state.defined.excl(adr); } private void fillLocalVarPosition(LocalVar lv) { if (lv == null || lv.sym == null || lv.sym.isExceptionParameter()|| !lv.sym.hasTypeAnnotations()) return; LocalVar.Range widestRange = lv.getWidestRange(); for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { TypeAnnotationPosition p = ta.position; if (widestRange.closed() && widestRange.length > 0) { p.lvarOffset = new int[] { (int)widestRange.start_pc }; p.lvarLength = new int[] { (int)widestRange.length }; p.lvarIndex = new int[] { (int)lv.reg }; p.isValidOffset = true; } else { p.isValidOffset = false; } } } // Method to be called after compressCatchTable to // fill in the exception table index for type // annotations on exception parameters. public void fillExceptionParameterPositions() { for (int i = 0; i < varBufferSize; ++i) { LocalVar lv = varBuffer[i]; if (lv == null || lv.sym == null || !lv.sym.hasTypeAnnotations() || !lv.sym.isExceptionParameter()) continue; for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { TypeAnnotationPosition p = ta.position; if (p.hasCatchType()) { final int idx = findExceptionIndex(p); if (idx == -1) Assert.error("Could not find exception index for type annotation " + ta + " on exception parameter"); p.setExceptionIndex(idx); } } } } private int findExceptionIndex(TypeAnnotationPosition p) { final int catchType = p.getCatchType(); final int startPos = p.getStartPos(); final int len = catchInfo.length(); List iter = catchInfo.toList(); for (int i = 0; i < len; ++i) { char[] catchEntry = iter.head; iter = iter.tail; int ct = catchEntry[3]; int sp = catchEntry[0]; if (catchType == ct && sp == startPos) { return i; } } return -1; } /** Put a live variable range into the buffer to be output to the * class file. */ void putVar(LocalVar var) { // Keep local variables if // 1) we need them for debug information // 2) it is an exception type and it contains type annotations boolean keepLocalVariables = varDebugInfo || (var.sym.isExceptionParameter() && var.sym.hasTypeAnnotations()); if (!keepLocalVariables) return; //don't keep synthetic vars, unless they are lambda method parameters boolean ignoredSyntheticVar = (var.sym.flags() & Flags.SYNTHETIC) != 0 && ((var.sym.owner.flags() & Flags.LAMBDA_METHOD) == 0 || (var.sym.flags() & Flags.PARAMETER) == 0); if (ignoredSyntheticVar) return; if (varBuffer == null) varBuffer = new LocalVar[20]; else varBuffer = ArrayUtils.ensureCapacity(varBuffer, varBufferSize); 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[invokedynamic] = "invokedynamic"; 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"; } } }