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