--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2178 @@
+/*
+ * 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";
+ }
+ }
+}