aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.jvm; aoqi@0: aoqi@0: import com.sun.tools.javac.code.*; aoqi@0: import com.sun.tools.javac.code.Symbol.*; aoqi@0: import com.sun.tools.javac.code.Types.UniqueType; aoqi@0: import com.sun.tools.javac.tree.JCTree; aoqi@0: import com.sun.tools.javac.util.*; aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; aoqi@0: aoqi@0: import static com.sun.tools.javac.code.TypeTag.BOT; aoqi@0: import static com.sun.tools.javac.code.TypeTag.INT; aoqi@0: import static com.sun.tools.javac.jvm.ByteCodes.*; aoqi@0: import static com.sun.tools.javac.jvm.UninitializedType.*; aoqi@0: import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame; aoqi@0: aoqi@0: /** An internal structure that corresponds to the code attribute of aoqi@0: * methods in a classfile. The class also provides some utility operations to aoqi@0: * generate bytecode instructions. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class Code { aoqi@0: aoqi@0: public final boolean debugCode; aoqi@0: public final boolean needStackMap; aoqi@0: aoqi@0: public enum StackMapFormat { aoqi@0: NONE, aoqi@0: CLDC { aoqi@0: Name getAttributeName(Names names) { aoqi@0: return names.StackMap; aoqi@0: } aoqi@0: }, aoqi@0: JSR202 { aoqi@0: Name getAttributeName(Names names) { aoqi@0: return names.StackMapTable; aoqi@0: } aoqi@0: }; aoqi@0: Name getAttributeName(Names names) { aoqi@0: return names.empty; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: final Types types; aoqi@0: final Symtab syms; aoqi@0: aoqi@0: /*---------- classfile fields: --------------- */ aoqi@0: aoqi@0: /** The maximum stack size. aoqi@0: */ aoqi@0: public int max_stack = 0; aoqi@0: aoqi@0: /** The maximum number of local variable slots. aoqi@0: */ aoqi@0: public int max_locals = 0; aoqi@0: aoqi@0: /** The code buffer. aoqi@0: */ aoqi@0: public byte[] code = new byte[64]; aoqi@0: aoqi@0: /** the current code pointer. aoqi@0: */ aoqi@0: public int cp = 0; aoqi@0: aoqi@0: /** Check the code against VM spec limits; if aoqi@0: * problems report them and return true. aoqi@0: */ aoqi@0: public boolean checkLimits(DiagnosticPosition pos, Log log) { aoqi@0: if (cp > ClassFile.MAX_CODE) { aoqi@0: log.error(pos, "limit.code"); aoqi@0: return true; aoqi@0: } aoqi@0: if (max_locals > ClassFile.MAX_LOCALS) { aoqi@0: log.error(pos, "limit.locals"); aoqi@0: return true; aoqi@0: } aoqi@0: if (max_stack > ClassFile.MAX_STACK) { aoqi@0: log.error(pos, "limit.stack"); aoqi@0: return true; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** A buffer for expression catch data. Each enter is a vector aoqi@0: * of four unsigned shorts. aoqi@0: */ aoqi@0: ListBuffer catchInfo = new ListBuffer(); aoqi@0: aoqi@0: /** A buffer for line number information. Each entry is a vector aoqi@0: * of two unsigned shorts. aoqi@0: */ aoqi@0: List lineInfo = List.nil(); // handled in stack fashion aoqi@0: aoqi@0: /** The CharacterRangeTable aoqi@0: */ aoqi@0: public CRTable crt; aoqi@0: aoqi@0: /*---------- internal fields: --------------- */ aoqi@0: aoqi@0: /** Are we generating code with jumps ≥ 32K? aoqi@0: */ aoqi@0: public boolean fatcode; aoqi@0: aoqi@0: /** Code generation enabled? aoqi@0: */ aoqi@0: private boolean alive = true; aoqi@0: aoqi@0: /** The current machine state (registers and stack). aoqi@0: */ aoqi@0: State state; aoqi@0: aoqi@0: /** Is it forbidden to compactify code, because something is aoqi@0: * pointing to current location? aoqi@0: */ aoqi@0: private boolean fixedPc = false; aoqi@0: aoqi@0: /** The next available register. aoqi@0: */ aoqi@0: public int nextreg = 0; aoqi@0: aoqi@0: /** A chain for jumps to be resolved before the next opcode is emitted. aoqi@0: * We do this lazily to avoid jumps to jumps. aoqi@0: */ aoqi@0: Chain pendingJumps = null; aoqi@0: aoqi@0: /** The position of the currently statement, if we are at the aoqi@0: * start of this statement, NOPOS otherwise. aoqi@0: * We need this to emit line numbers lazily, which we need to do aoqi@0: * because of jump-to-jump optimization. aoqi@0: */ aoqi@0: int pendingStatPos = Position.NOPOS; aoqi@0: aoqi@0: /** Set true when a stackMap is needed at the current PC. */ aoqi@0: boolean pendingStackMap = false; aoqi@0: aoqi@0: /** The stack map format to be generated. */ aoqi@0: StackMapFormat stackMap; aoqi@0: aoqi@0: /** Switch: emit variable debug info. aoqi@0: */ aoqi@0: boolean varDebugInfo; aoqi@0: aoqi@0: /** Switch: emit line number info. aoqi@0: */ aoqi@0: boolean lineDebugInfo; aoqi@0: aoqi@0: /** Emit line number info if map supplied aoqi@0: */ aoqi@0: Position.LineMap lineMap; aoqi@0: aoqi@0: /** The constant pool of the current class. aoqi@0: */ aoqi@0: final Pool pool; aoqi@0: aoqi@0: final MethodSymbol meth; aoqi@0: aoqi@0: final LVTRanges lvtRanges; aoqi@0: aoqi@0: /** Construct a code object, given the settings of the fatcode, aoqi@0: * debugging info switches and the CharacterRangeTable. aoqi@0: */ aoqi@0: public Code(MethodSymbol meth, aoqi@0: boolean fatcode, aoqi@0: Position.LineMap lineMap, aoqi@0: boolean varDebugInfo, aoqi@0: StackMapFormat stackMap, aoqi@0: boolean debugCode, aoqi@0: CRTable crt, aoqi@0: Symtab syms, aoqi@0: Types types, aoqi@0: Pool pool, aoqi@0: LVTRanges lvtRanges) { aoqi@0: this.meth = meth; aoqi@0: this.fatcode = fatcode; aoqi@0: this.lineMap = lineMap; aoqi@0: this.lineDebugInfo = lineMap != null; aoqi@0: this.varDebugInfo = varDebugInfo; aoqi@0: this.crt = crt; aoqi@0: this.syms = syms; aoqi@0: this.types = types; aoqi@0: this.debugCode = debugCode; aoqi@0: this.stackMap = stackMap; aoqi@0: switch (stackMap) { aoqi@0: case CLDC: aoqi@0: case JSR202: aoqi@0: this.needStackMap = true; aoqi@0: break; aoqi@0: default: aoqi@0: this.needStackMap = false; aoqi@0: } aoqi@0: state = new State(); aoqi@0: lvar = new LocalVar[20]; aoqi@0: this.pool = pool; aoqi@0: this.lvtRanges = lvtRanges; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /* ************************************************************************** aoqi@0: * Typecodes & related stuff aoqi@0: ****************************************************************************/ aoqi@0: aoqi@0: /** Given a type, return its type code (used implicitly in the aoqi@0: * JVM architecture). aoqi@0: */ aoqi@0: public static int typecode(Type type) { aoqi@0: switch (type.getTag()) { aoqi@0: case BYTE: return BYTEcode; aoqi@0: case SHORT: return SHORTcode; aoqi@0: case CHAR: return CHARcode; aoqi@0: case INT: return INTcode; aoqi@0: case LONG: return LONGcode; aoqi@0: case FLOAT: return FLOATcode; aoqi@0: case DOUBLE: return DOUBLEcode; aoqi@0: case BOOLEAN: return BYTEcode; aoqi@0: case VOID: return VOIDcode; aoqi@0: case CLASS: aoqi@0: case ARRAY: aoqi@0: case METHOD: aoqi@0: case BOT: aoqi@0: case TYPEVAR: aoqi@0: case UNINITIALIZED_THIS: aoqi@0: case UNINITIALIZED_OBJECT: aoqi@0: return OBJECTcode; aoqi@0: default: throw new AssertionError("typecode " + type.getTag()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Collapse type code for subtypes of int to INTcode. aoqi@0: */ aoqi@0: public static int truncate(int tc) { aoqi@0: switch (tc) { aoqi@0: case BYTEcode: case SHORTcode: case CHARcode: return INTcode; aoqi@0: default: return tc; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** The width in bytes of objects of the type. aoqi@0: */ aoqi@0: public static int width(int typecode) { aoqi@0: switch (typecode) { aoqi@0: case LONGcode: case DOUBLEcode: return 2; aoqi@0: case VOIDcode: return 0; aoqi@0: default: return 1; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static int width(Type type) { aoqi@0: return type == null ? 1 : width(typecode(type)); aoqi@0: } aoqi@0: aoqi@0: /** The total width taken up by a vector of objects. aoqi@0: */ aoqi@0: public static int width(List types) { aoqi@0: int w = 0; aoqi@0: for (List l = types; l.nonEmpty(); l = l.tail) aoqi@0: w = w + width(l.head); aoqi@0: return w; aoqi@0: } aoqi@0: aoqi@0: /** Given a type, return its code for allocating arrays of that type. aoqi@0: */ aoqi@0: public static int arraycode(Type type) { aoqi@0: switch (type.getTag()) { aoqi@0: case BYTE: return 8; aoqi@0: case BOOLEAN: return 4; aoqi@0: case SHORT: return 9; aoqi@0: case CHAR: return 5; aoqi@0: case INT: return 10; aoqi@0: case LONG: return 11; aoqi@0: case FLOAT: return 6; aoqi@0: case DOUBLE: return 7; aoqi@0: case CLASS: return 0; aoqi@0: case ARRAY: return 1; aoqi@0: default: throw new AssertionError("arraycode " + type); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /* ************************************************************************** aoqi@0: * Emit code aoqi@0: ****************************************************************************/ aoqi@0: aoqi@0: /** The current output code pointer. aoqi@0: */ aoqi@0: public int curCP() { aoqi@0: /* aoqi@0: * This method has side-effects because calling it can indirectly provoke aoqi@0: * extra code generation, like goto instructions, depending on the context aoqi@0: * where it's called. aoqi@0: * Use with care or even better avoid using it. aoqi@0: */ aoqi@0: if (pendingJumps != null) { aoqi@0: resolvePending(); aoqi@0: } aoqi@0: if (pendingStatPos != Position.NOPOS) { aoqi@0: markStatBegin(); aoqi@0: } aoqi@0: fixedPc = true; aoqi@0: return cp; aoqi@0: } aoqi@0: aoqi@0: /** Emit a byte of code. aoqi@0: */ aoqi@0: private void emit1(int od) { aoqi@0: if (!alive) return; aoqi@0: code = ArrayUtils.ensureCapacity(code, cp); aoqi@0: code[cp++] = (byte)od; aoqi@0: } aoqi@0: aoqi@0: /** Emit two bytes of code. aoqi@0: */ aoqi@0: private void emit2(int od) { aoqi@0: if (!alive) return; aoqi@0: if (cp + 2 > code.length) { aoqi@0: emit1(od >> 8); aoqi@0: emit1(od); aoqi@0: } else { aoqi@0: code[cp++] = (byte)(od >> 8); aoqi@0: code[cp++] = (byte)od; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Emit four bytes of code. aoqi@0: */ aoqi@0: public void emit4(int od) { aoqi@0: if (!alive) return; aoqi@0: if (cp + 4 > code.length) { aoqi@0: emit1(od >> 24); aoqi@0: emit1(od >> 16); aoqi@0: emit1(od >> 8); aoqi@0: emit1(od); aoqi@0: } else { aoqi@0: code[cp++] = (byte)(od >> 24); aoqi@0: code[cp++] = (byte)(od >> 16); aoqi@0: code[cp++] = (byte)(od >> 8); aoqi@0: code[cp++] = (byte)od; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode. aoqi@0: */ aoqi@0: private void emitop(int op) { aoqi@0: if (pendingJumps != null) resolvePending(); aoqi@0: if (alive) { aoqi@0: if (pendingStatPos != Position.NOPOS) aoqi@0: markStatBegin(); aoqi@0: if (pendingStackMap) { aoqi@0: pendingStackMap = false; aoqi@0: emitStackMap(); aoqi@0: } aoqi@0: if (debugCode) aoqi@0: System.err.println("emit@" + cp + " stack=" + aoqi@0: state.stacksize + ": " + aoqi@0: mnem(op)); aoqi@0: emit1(op); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void postop() { aoqi@0: Assert.check(alive || state.stacksize == 0); aoqi@0: } aoqi@0: aoqi@0: /** Emit a ldc (or ldc_w) instruction, taking into account operand size aoqi@0: */ aoqi@0: public void emitLdc(int od) { aoqi@0: if (od <= 255) { aoqi@0: emitop1(ldc1, od); aoqi@0: } aoqi@0: else { aoqi@0: emitop2(ldc2, od); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Emit a multinewarray instruction. aoqi@0: */ aoqi@0: public void emitMultianewarray(int ndims, int type, Type arrayType) { aoqi@0: emitop(multianewarray); aoqi@0: if (!alive) return; aoqi@0: emit2(type); aoqi@0: emit1(ndims); aoqi@0: state.pop(ndims); aoqi@0: state.push(arrayType); aoqi@0: } aoqi@0: aoqi@0: /** Emit newarray. aoqi@0: */ aoqi@0: public void emitNewarray(int elemcode, Type arrayType) { aoqi@0: emitop(newarray); aoqi@0: if (!alive) return; aoqi@0: emit1(elemcode); aoqi@0: state.pop(1); // count aoqi@0: state.push(arrayType); aoqi@0: } aoqi@0: aoqi@0: /** Emit anewarray. aoqi@0: */ aoqi@0: public void emitAnewarray(int od, Type arrayType) { aoqi@0: emitop(anewarray); aoqi@0: if (!alive) return; aoqi@0: emit2(od); aoqi@0: state.pop(1); aoqi@0: state.push(arrayType); aoqi@0: } aoqi@0: aoqi@0: /** Emit an invokeinterface instruction. aoqi@0: */ aoqi@0: public void emitInvokeinterface(int meth, Type mtype) { aoqi@0: int argsize = width(mtype.getParameterTypes()); aoqi@0: emitop(invokeinterface); aoqi@0: if (!alive) return; aoqi@0: emit2(meth); aoqi@0: emit1(argsize + 1); aoqi@0: emit1(0); aoqi@0: state.pop(argsize + 1); aoqi@0: state.push(mtype.getReturnType()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an invokespecial instruction. aoqi@0: */ aoqi@0: public void emitInvokespecial(int meth, Type mtype) { aoqi@0: int argsize = width(mtype.getParameterTypes()); aoqi@0: emitop(invokespecial); aoqi@0: if (!alive) return; aoqi@0: emit2(meth); aoqi@0: Symbol sym = (Symbol)pool.pool[meth]; aoqi@0: state.pop(argsize); aoqi@0: if (sym.isConstructor()) aoqi@0: state.markInitialized((UninitializedType)state.peek()); aoqi@0: state.pop(1); aoqi@0: state.push(mtype.getReturnType()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an invokestatic instruction. aoqi@0: */ aoqi@0: public void emitInvokestatic(int meth, Type mtype) { aoqi@0: int argsize = width(mtype.getParameterTypes()); aoqi@0: emitop(invokestatic); aoqi@0: if (!alive) return; aoqi@0: emit2(meth); aoqi@0: state.pop(argsize); aoqi@0: state.push(mtype.getReturnType()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an invokevirtual instruction. aoqi@0: */ aoqi@0: public void emitInvokevirtual(int meth, Type mtype) { aoqi@0: int argsize = width(mtype.getParameterTypes()); aoqi@0: emitop(invokevirtual); aoqi@0: if (!alive) return; aoqi@0: emit2(meth); aoqi@0: state.pop(argsize + 1); aoqi@0: state.push(mtype.getReturnType()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an invokedynamic instruction. aoqi@0: */ aoqi@0: public void emitInvokedynamic(int desc, Type mtype) { aoqi@0: int argsize = width(mtype.getParameterTypes()); aoqi@0: emitop(invokedynamic); aoqi@0: if (!alive) return; aoqi@0: emit2(desc); aoqi@0: emit2(0); aoqi@0: state.pop(argsize); aoqi@0: state.push(mtype.getReturnType()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with no operand field. aoqi@0: */ aoqi@0: public void emitop0(int op) { aoqi@0: emitop(op); aoqi@0: if (!alive) return; aoqi@0: switch (op) { aoqi@0: case aaload: { aoqi@0: state.pop(1);// index aoqi@0: Type a = state.stack[state.stacksize-1]; aoqi@0: state.pop(1); aoqi@0: //sometimes 'null type' is treated as a one-dimensional array type aoqi@0: //see Gen.visitLiteral - we should handle this case accordingly aoqi@0: Type stackType = a.hasTag(BOT) ? aoqi@0: syms.objectType : aoqi@0: types.erasure(types.elemtype(a)); aoqi@0: state.push(stackType); } aoqi@0: break; aoqi@0: case goto_: aoqi@0: markDead(); aoqi@0: break; aoqi@0: case nop: aoqi@0: case ineg: aoqi@0: case lneg: aoqi@0: case fneg: aoqi@0: case dneg: aoqi@0: break; aoqi@0: case aconst_null: aoqi@0: state.push(syms.botType); aoqi@0: break; aoqi@0: case iconst_m1: aoqi@0: case iconst_0: aoqi@0: case iconst_1: aoqi@0: case iconst_2: aoqi@0: case iconst_3: aoqi@0: case iconst_4: aoqi@0: case iconst_5: aoqi@0: case iload_0: aoqi@0: case iload_1: aoqi@0: case iload_2: aoqi@0: case iload_3: aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case lconst_0: aoqi@0: case lconst_1: aoqi@0: case lload_0: aoqi@0: case lload_1: aoqi@0: case lload_2: aoqi@0: case lload_3: aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case fconst_0: aoqi@0: case fconst_1: aoqi@0: case fconst_2: aoqi@0: case fload_0: aoqi@0: case fload_1: aoqi@0: case fload_2: aoqi@0: case fload_3: aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case dconst_0: aoqi@0: case dconst_1: aoqi@0: case dload_0: aoqi@0: case dload_1: aoqi@0: case dload_2: aoqi@0: case dload_3: aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case aload_0: aoqi@0: state.push(lvar[0].sym.type); aoqi@0: break; aoqi@0: case aload_1: aoqi@0: state.push(lvar[1].sym.type); aoqi@0: break; aoqi@0: case aload_2: aoqi@0: state.push(lvar[2].sym.type); aoqi@0: break; aoqi@0: case aload_3: aoqi@0: state.push(lvar[3].sym.type); aoqi@0: break; aoqi@0: case iaload: aoqi@0: case baload: aoqi@0: case caload: aoqi@0: case saload: aoqi@0: state.pop(2); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case laload: aoqi@0: state.pop(2); aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case faload: aoqi@0: state.pop(2); aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case daload: aoqi@0: state.pop(2); aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case istore_0: aoqi@0: case istore_1: aoqi@0: case istore_2: aoqi@0: case istore_3: aoqi@0: case fstore_0: aoqi@0: case fstore_1: aoqi@0: case fstore_2: aoqi@0: case fstore_3: aoqi@0: case astore_0: aoqi@0: case astore_1: aoqi@0: case astore_2: aoqi@0: case astore_3: aoqi@0: case pop: aoqi@0: case lshr: aoqi@0: case lshl: aoqi@0: case lushr: aoqi@0: state.pop(1); aoqi@0: break; aoqi@0: case areturn: aoqi@0: case ireturn: aoqi@0: case freturn: aoqi@0: Assert.check(state.nlocks == 0); aoqi@0: state.pop(1); aoqi@0: markDead(); aoqi@0: break; aoqi@0: case athrow: aoqi@0: state.pop(1); aoqi@0: markDead(); aoqi@0: break; aoqi@0: case lstore_0: aoqi@0: case lstore_1: aoqi@0: case lstore_2: aoqi@0: case lstore_3: aoqi@0: case dstore_0: aoqi@0: case dstore_1: aoqi@0: case dstore_2: aoqi@0: case dstore_3: aoqi@0: case pop2: aoqi@0: state.pop(2); aoqi@0: break; aoqi@0: case lreturn: aoqi@0: case dreturn: aoqi@0: Assert.check(state.nlocks == 0); aoqi@0: state.pop(2); aoqi@0: markDead(); aoqi@0: break; aoqi@0: case dup: aoqi@0: state.push(state.stack[state.stacksize-1]); aoqi@0: break; aoqi@0: case return_: aoqi@0: Assert.check(state.nlocks == 0); aoqi@0: markDead(); aoqi@0: break; aoqi@0: case arraylength: aoqi@0: state.pop(1); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case isub: aoqi@0: case iadd: aoqi@0: case imul: aoqi@0: case idiv: aoqi@0: case imod: aoqi@0: case ishl: aoqi@0: case ishr: aoqi@0: case iushr: aoqi@0: case iand: aoqi@0: case ior: aoqi@0: case ixor: aoqi@0: state.pop(1); aoqi@0: // state.pop(1); aoqi@0: // state.push(syms.intType); aoqi@0: break; aoqi@0: case aastore: aoqi@0: state.pop(3); aoqi@0: break; aoqi@0: case land: aoqi@0: case lor: aoqi@0: case lxor: aoqi@0: case lmod: aoqi@0: case ldiv: aoqi@0: case lmul: aoqi@0: case lsub: aoqi@0: case ladd: aoqi@0: state.pop(2); aoqi@0: break; aoqi@0: case lcmp: aoqi@0: state.pop(4); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case l2i: aoqi@0: state.pop(2); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case i2l: aoqi@0: state.pop(1); aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case i2f: aoqi@0: state.pop(1); aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case i2d: aoqi@0: state.pop(1); aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case l2f: aoqi@0: state.pop(2); aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case l2d: aoqi@0: state.pop(2); aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case f2i: aoqi@0: state.pop(1); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case f2l: aoqi@0: state.pop(1); aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case f2d: aoqi@0: state.pop(1); aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case d2i: aoqi@0: state.pop(2); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case d2l: aoqi@0: state.pop(2); aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case d2f: aoqi@0: state.pop(2); aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case tableswitch: aoqi@0: case lookupswitch: aoqi@0: state.pop(1); aoqi@0: // the caller is responsible for patching up the state aoqi@0: break; aoqi@0: case dup_x1: { aoqi@0: Type val1 = state.pop1(); aoqi@0: Type val2 = state.pop1(); aoqi@0: state.push(val1); aoqi@0: state.push(val2); aoqi@0: state.push(val1); aoqi@0: break; aoqi@0: } aoqi@0: case bastore: aoqi@0: state.pop(3); aoqi@0: break; aoqi@0: case int2byte: aoqi@0: case int2char: aoqi@0: case int2short: aoqi@0: break; aoqi@0: case fmul: aoqi@0: case fadd: aoqi@0: case fsub: aoqi@0: case fdiv: aoqi@0: case fmod: aoqi@0: state.pop(1); aoqi@0: break; aoqi@0: case castore: aoqi@0: case iastore: aoqi@0: case fastore: aoqi@0: case sastore: aoqi@0: state.pop(3); aoqi@0: break; aoqi@0: case lastore: aoqi@0: case dastore: aoqi@0: state.pop(4); aoqi@0: break; aoqi@0: case dup2: aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: Type value1 = state.pop1(); aoqi@0: Type value2 = state.pop1(); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } else { aoqi@0: Type value = state.pop2(); aoqi@0: state.push(value); aoqi@0: state.push(value); aoqi@0: } aoqi@0: break; aoqi@0: case dup2_x1: aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: Type value1 = state.pop1(); aoqi@0: Type value2 = state.pop1(); aoqi@0: Type value3 = state.pop1(); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: state.push(value3); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } else { aoqi@0: Type value1 = state.pop2(); aoqi@0: Type value2 = state.pop1(); aoqi@0: state.push(value1); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } aoqi@0: break; aoqi@0: case dup2_x2: aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: Type value1 = state.pop1(); aoqi@0: Type value2 = state.pop1(); aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: // form 1 aoqi@0: Type value3 = state.pop1(); aoqi@0: Type value4 = state.pop1(); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: state.push(value4); aoqi@0: state.push(value3); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } else { aoqi@0: // form 3 aoqi@0: Type value3 = state.pop2(); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: state.push(value3); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } aoqi@0: } else { aoqi@0: Type value1 = state.pop2(); aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: // form 2 aoqi@0: Type value2 = state.pop1(); aoqi@0: Type value3 = state.pop1(); aoqi@0: state.push(value1); aoqi@0: state.push(value3); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } else { aoqi@0: // form 4 aoqi@0: Type value2 = state.pop2(); aoqi@0: state.push(value1); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } aoqi@0: } aoqi@0: break; aoqi@0: case dup_x2: { aoqi@0: Type value1 = state.pop1(); aoqi@0: if (state.stack[state.stacksize-1] != null) { aoqi@0: // form 1 aoqi@0: Type value2 = state.pop1(); aoqi@0: Type value3 = state.pop1(); aoqi@0: state.push(value1); aoqi@0: state.push(value3); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } else { aoqi@0: // form 2 aoqi@0: Type value2 = state.pop2(); aoqi@0: state.push(value1); aoqi@0: state.push(value2); aoqi@0: state.push(value1); aoqi@0: } aoqi@0: } aoqi@0: break; aoqi@0: case fcmpl: aoqi@0: case fcmpg: aoqi@0: state.pop(2); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case dcmpl: aoqi@0: case dcmpg: aoqi@0: state.pop(4); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case swap: { aoqi@0: Type value1 = state.pop1(); aoqi@0: Type value2 = state.pop1(); aoqi@0: state.push(value1); aoqi@0: state.push(value2); aoqi@0: break; aoqi@0: } aoqi@0: case dadd: aoqi@0: case dsub: aoqi@0: case dmul: aoqi@0: case ddiv: aoqi@0: case dmod: aoqi@0: state.pop(2); aoqi@0: break; aoqi@0: case ret: aoqi@0: markDead(); aoqi@0: break; aoqi@0: case wide: aoqi@0: // must be handled by the caller. aoqi@0: return; aoqi@0: case monitorenter: aoqi@0: case monitorexit: aoqi@0: state.pop(1); aoqi@0: break; aoqi@0: aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: postop(); aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with a one-byte operand field. aoqi@0: */ aoqi@0: public void emitop1(int op, int od) { aoqi@0: emitop(op); aoqi@0: if (!alive) return; aoqi@0: emit1(od); aoqi@0: switch (op) { aoqi@0: case bipush: aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case ldc1: aoqi@0: state.push(typeForPool(pool.pool[od])); aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: postop(); aoqi@0: } aoqi@0: aoqi@0: /** The type of a constant pool entry. */ aoqi@0: private Type typeForPool(Object o) { aoqi@0: if (o instanceof Integer) return syms.intType; aoqi@0: if (o instanceof Float) return syms.floatType; aoqi@0: if (o instanceof String) return syms.stringType; aoqi@0: if (o instanceof Long) return syms.longType; aoqi@0: if (o instanceof Double) return syms.doubleType; aoqi@0: if (o instanceof ClassSymbol) return syms.classType; aoqi@0: if (o instanceof Pool.MethodHandle) return syms.methodHandleType; aoqi@0: if (o instanceof UniqueType) return typeForPool(((UniqueType)o).type); aoqi@0: if (o instanceof Type) { aoqi@0: Type ty = ((Type)o).unannotatedType(); aoqi@0: aoqi@0: if (ty instanceof Type.ArrayType) return syms.classType; aoqi@0: if (ty instanceof Type.MethodType) return syms.methodTypeType; aoqi@0: } aoqi@0: throw new AssertionError("Invalid type of constant pool entry: " + o.getClass()); aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with a one-byte operand field; aoqi@0: * widen if field does not fit in a byte. aoqi@0: */ aoqi@0: public void emitop1w(int op, int od) { aoqi@0: if (od > 0xFF) { aoqi@0: emitop(wide); aoqi@0: emitop(op); aoqi@0: emit2(od); aoqi@0: } else { aoqi@0: emitop(op); aoqi@0: emit1(od); aoqi@0: } aoqi@0: if (!alive) return; aoqi@0: switch (op) { aoqi@0: case iload: aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case lload: aoqi@0: state.push(syms.longType); aoqi@0: break; aoqi@0: case fload: aoqi@0: state.push(syms.floatType); aoqi@0: break; aoqi@0: case dload: aoqi@0: state.push(syms.doubleType); aoqi@0: break; aoqi@0: case aload: aoqi@0: state.push(lvar[od].sym.type); aoqi@0: break; aoqi@0: case lstore: aoqi@0: case dstore: aoqi@0: state.pop(2); aoqi@0: break; aoqi@0: case istore: aoqi@0: case fstore: aoqi@0: case astore: aoqi@0: state.pop(1); aoqi@0: break; aoqi@0: case ret: aoqi@0: markDead(); aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: postop(); aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with two one-byte operand fields; aoqi@0: * widen if either field does not fit in a byte. aoqi@0: */ aoqi@0: public void emitop1w(int op, int od1, int od2) { aoqi@0: if (od1 > 0xFF || od2 < -128 || od2 > 127) { aoqi@0: emitop(wide); aoqi@0: emitop(op); aoqi@0: emit2(od1); aoqi@0: emit2(od2); aoqi@0: } else { aoqi@0: emitop(op); aoqi@0: emit1(od1); aoqi@0: emit1(od2); aoqi@0: } aoqi@0: if (!alive) return; aoqi@0: switch (op) { aoqi@0: case iinc: aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with a two-byte operand field. aoqi@0: */ aoqi@0: public void emitop2(int op, int od) { aoqi@0: emitop(op); aoqi@0: if (!alive) return; aoqi@0: emit2(od); aoqi@0: switch (op) { aoqi@0: case getstatic: aoqi@0: state.push(((Symbol)(pool.pool[od])).erasure(types)); aoqi@0: break; aoqi@0: case putstatic: aoqi@0: state.pop(((Symbol)(pool.pool[od])).erasure(types)); aoqi@0: break; aoqi@0: case new_: aoqi@0: Symbol sym; aoqi@0: if (pool.pool[od] instanceof UniqueType) { aoqi@0: // Required by change in Gen.makeRef to allow aoqi@0: // annotated types. aoqi@0: // TODO: is this needed anywhere else? aoqi@0: sym = ((UniqueType)(pool.pool[od])).type.tsym; aoqi@0: } else { aoqi@0: sym = (Symbol)(pool.pool[od]); aoqi@0: } aoqi@0: state.push(uninitializedObject(sym.erasure(types), cp-3)); aoqi@0: break; aoqi@0: case sipush: aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case if_acmp_null: aoqi@0: case if_acmp_nonnull: aoqi@0: case ifeq: aoqi@0: case ifne: aoqi@0: case iflt: aoqi@0: case ifge: aoqi@0: case ifgt: aoqi@0: case ifle: aoqi@0: state.pop(1); aoqi@0: break; aoqi@0: case if_icmpeq: aoqi@0: case if_icmpne: aoqi@0: case if_icmplt: aoqi@0: case if_icmpge: aoqi@0: case if_icmpgt: aoqi@0: case if_icmple: aoqi@0: case if_acmpeq: aoqi@0: case if_acmpne: aoqi@0: state.pop(2); aoqi@0: break; aoqi@0: case goto_: aoqi@0: markDead(); aoqi@0: break; aoqi@0: case putfield: aoqi@0: state.pop(((Symbol)(pool.pool[od])).erasure(types)); aoqi@0: state.pop(1); // object ref aoqi@0: break; aoqi@0: case getfield: aoqi@0: state.pop(1); // object ref aoqi@0: state.push(((Symbol)(pool.pool[od])).erasure(types)); aoqi@0: break; aoqi@0: case checkcast: { aoqi@0: state.pop(1); // object ref aoqi@0: Object o = pool.pool[od]; aoqi@0: Type t = (o instanceof Symbol) aoqi@0: ? ((Symbol)o).erasure(types) aoqi@0: : types.erasure((((UniqueType)o).type)); aoqi@0: state.push(t); aoqi@0: break; } aoqi@0: case ldc2w: aoqi@0: state.push(typeForPool(pool.pool[od])); aoqi@0: break; aoqi@0: case instanceof_: aoqi@0: state.pop(1); aoqi@0: state.push(syms.intType); aoqi@0: break; aoqi@0: case ldc2: aoqi@0: state.push(typeForPool(pool.pool[od])); aoqi@0: break; aoqi@0: case jsr: aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: // postop(); aoqi@0: } aoqi@0: aoqi@0: /** Emit an opcode with a four-byte operand field. aoqi@0: */ aoqi@0: public void emitop4(int op, int od) { aoqi@0: emitop(op); aoqi@0: if (!alive) return; aoqi@0: emit4(od); aoqi@0: switch (op) { aoqi@0: case goto_w: aoqi@0: markDead(); aoqi@0: break; aoqi@0: case jsr_w: aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(mnem(op)); aoqi@0: } aoqi@0: // postop(); aoqi@0: } aoqi@0: aoqi@0: /** Align code pointer to next `incr' boundary. aoqi@0: */ aoqi@0: public void align(int incr) { aoqi@0: if (alive) aoqi@0: while (cp % incr != 0) emitop0(nop); aoqi@0: } aoqi@0: aoqi@0: /** Place a byte into code at address pc. aoqi@0: * Pre: {@literal pc + 1 <= cp }. aoqi@0: */ aoqi@0: private void put1(int pc, int op) { aoqi@0: code[pc] = (byte)op; aoqi@0: } aoqi@0: aoqi@0: /** Place two bytes into code at address pc. aoqi@0: * Pre: {@literal pc + 2 <= cp }. aoqi@0: */ aoqi@0: private void put2(int pc, int od) { aoqi@0: // pre: pc + 2 <= cp aoqi@0: put1(pc, od >> 8); aoqi@0: put1(pc+1, od); aoqi@0: } aoqi@0: aoqi@0: /** Place four bytes into code at address pc. aoqi@0: * Pre: {@literal pc + 4 <= cp }. aoqi@0: */ aoqi@0: public void put4(int pc, int od) { aoqi@0: // pre: pc + 4 <= cp aoqi@0: put1(pc , od >> 24); aoqi@0: put1(pc+1, od >> 16); aoqi@0: put1(pc+2, od >> 8); aoqi@0: put1(pc+3, od); aoqi@0: } aoqi@0: aoqi@0: /** Return code byte at position pc as an unsigned int. aoqi@0: */ aoqi@0: private int get1(int pc) { aoqi@0: return code[pc] & 0xFF; aoqi@0: } aoqi@0: aoqi@0: /** Return two code bytes at position pc as an unsigned int. aoqi@0: */ aoqi@0: private int get2(int pc) { aoqi@0: return (get1(pc) << 8) | get1(pc+1); aoqi@0: } aoqi@0: aoqi@0: /** Return four code bytes at position pc as an int. aoqi@0: */ aoqi@0: public int get4(int pc) { aoqi@0: // pre: pc + 4 <= cp aoqi@0: return aoqi@0: (get1(pc) << 24) | aoqi@0: (get1(pc+1) << 16) | aoqi@0: (get1(pc+2) << 8) | aoqi@0: (get1(pc+3)); aoqi@0: } aoqi@0: aoqi@0: /** Is code generation currently enabled? aoqi@0: */ aoqi@0: public boolean isAlive() { aoqi@0: return alive || pendingJumps != null; aoqi@0: } aoqi@0: aoqi@0: /** Switch code generation on/off. aoqi@0: */ aoqi@0: public void markDead() { aoqi@0: alive = false; aoqi@0: } aoqi@0: aoqi@0: /** Declare an entry point; return current code pointer aoqi@0: */ aoqi@0: public int entryPoint() { aoqi@0: int pc = curCP(); aoqi@0: alive = true; aoqi@0: pendingStackMap = needStackMap; aoqi@0: return pc; aoqi@0: } aoqi@0: aoqi@0: /** Declare an entry point with initial state; aoqi@0: * return current code pointer aoqi@0: */ aoqi@0: public int entryPoint(State state) { aoqi@0: int pc = curCP(); aoqi@0: alive = true; aoqi@0: this.state = state.dup(); aoqi@0: Assert.check(state.stacksize <= max_stack); aoqi@0: if (debugCode) System.err.println("entry point " + state); aoqi@0: pendingStackMap = needStackMap; aoqi@0: return pc; aoqi@0: } aoqi@0: aoqi@0: /** Declare an entry point with initial state plus a pushed value; aoqi@0: * return current code pointer aoqi@0: */ aoqi@0: public int entryPoint(State state, Type pushed) { aoqi@0: int pc = curCP(); aoqi@0: alive = true; aoqi@0: this.state = state.dup(); aoqi@0: Assert.check(state.stacksize <= max_stack); aoqi@0: this.state.push(pushed); aoqi@0: if (debugCode) System.err.println("entry point " + state); aoqi@0: pendingStackMap = needStackMap; aoqi@0: return pc; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /************************************************************************** aoqi@0: * Stack map generation aoqi@0: *************************************************************************/ aoqi@0: aoqi@0: /** An entry in the stack map. */ aoqi@0: static class StackMapFrame { aoqi@0: int pc; aoqi@0: Type[] locals; aoqi@0: Type[] stack; aoqi@0: } aoqi@0: aoqi@0: /** A buffer of cldc stack map entries. */ aoqi@0: StackMapFrame[] stackMapBuffer = null; aoqi@0: aoqi@0: /** A buffer of compressed StackMapTable entries. */ aoqi@0: StackMapTableFrame[] stackMapTableBuffer = null; aoqi@0: int stackMapBufferSize = 0; aoqi@0: aoqi@0: /** The last PC at which we generated a stack map. */ aoqi@0: int lastStackMapPC = -1; aoqi@0: aoqi@0: /** The last stack map frame in StackMapTable. */ aoqi@0: StackMapFrame lastFrame = null; aoqi@0: aoqi@0: /** The stack map frame before the last one. */ aoqi@0: StackMapFrame frameBeforeLast = null; aoqi@0: aoqi@0: /** Emit a stack map entry. */ aoqi@0: public void emitStackMap() { aoqi@0: int pc = curCP(); aoqi@0: if (!needStackMap) return; aoqi@0: aoqi@0: aoqi@0: aoqi@0: switch (stackMap) { aoqi@0: case CLDC: aoqi@0: emitCLDCStackMap(pc, getLocalsSize()); aoqi@0: break; aoqi@0: case JSR202: aoqi@0: emitStackMapFrame(pc, getLocalsSize()); aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError("Should have chosen a stackmap format"); aoqi@0: } aoqi@0: // DEBUG code follows aoqi@0: if (debugCode) state.dump(pc); aoqi@0: } aoqi@0: aoqi@0: private int getLocalsSize() { aoqi@0: int nextLocal = 0; aoqi@0: for (int i=max_locals-1; i>=0; i--) { aoqi@0: if (state.defined.isMember(i) && lvar[i] != null) { aoqi@0: nextLocal = i + width(lvar[i].sym.erasure(types)); aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: return nextLocal; aoqi@0: } aoqi@0: aoqi@0: /** Emit a CLDC stack map frame. */ aoqi@0: void emitCLDCStackMap(int pc, int localsSize) { aoqi@0: if (lastStackMapPC == pc) { aoqi@0: // drop existing stackmap at this offset aoqi@0: stackMapBuffer[--stackMapBufferSize] = null; aoqi@0: } aoqi@0: lastStackMapPC = pc; aoqi@0: aoqi@0: if (stackMapBuffer == null) { aoqi@0: stackMapBuffer = new StackMapFrame[20]; aoqi@0: } else { aoqi@0: stackMapBuffer = ArrayUtils.ensureCapacity(stackMapBuffer, stackMapBufferSize); aoqi@0: } aoqi@0: StackMapFrame frame = aoqi@0: stackMapBuffer[stackMapBufferSize++] = new StackMapFrame(); aoqi@0: frame.pc = pc; aoqi@0: aoqi@0: frame.locals = new Type[localsSize]; aoqi@0: for (int i=0; i 1) i++; aoqi@0: } aoqi@0: } aoqi@0: frame.locals = new Type[localCount]; aoqi@0: for (int i=0, j=0; i 1) i++; aoqi@0: } aoqi@0: aoqi@0: int stackCount = 0; aoqi@0: for (int i=0; i arg_types = ((MethodType)meth.externalType(types)).argtypes; aoqi@0: int len = arg_types.length(); aoqi@0: int count = 0; aoqi@0: if (!meth.isStatic()) { aoqi@0: Type thisType = meth.owner.type; aoqi@0: frame.locals = new Type[len+1]; aoqi@0: if (meth.isConstructor() && thisType != syms.objectType) { aoqi@0: frame.locals[count++] = UninitializedType.uninitializedThis(thisType); aoqi@0: } else { aoqi@0: frame.locals[count++] = types.erasure(thisType); aoqi@0: } aoqi@0: } else { aoqi@0: frame.locals = new Type[len]; aoqi@0: } aoqi@0: for (Type arg_type : arg_types) { aoqi@0: frame.locals[count++] = types.erasure(arg_type); aoqi@0: } aoqi@0: frame.pc = -1; aoqi@0: frame.stack = null; aoqi@0: return frame; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /************************************************************************** aoqi@0: * Operations having to do with jumps aoqi@0: *************************************************************************/ aoqi@0: aoqi@0: /** A chain represents a list of unresolved jumps. Jump locations aoqi@0: * are sorted in decreasing order. aoqi@0: */ aoqi@0: public static class Chain { aoqi@0: aoqi@0: /** The position of the jump instruction. aoqi@0: */ aoqi@0: public final int pc; aoqi@0: aoqi@0: /** The machine state after the jump instruction. aoqi@0: * Invariant: all elements of a chain list have the same stacksize aoqi@0: * and compatible stack and register contents. aoqi@0: */ aoqi@0: Code.State state; aoqi@0: aoqi@0: /** The next jump in the list. aoqi@0: */ aoqi@0: public final Chain next; aoqi@0: aoqi@0: /** Construct a chain from its jump position, stacksize, previous aoqi@0: * chain, and machine state. aoqi@0: */ aoqi@0: public Chain(int pc, Chain next, Code.State state) { aoqi@0: this.pc = pc; aoqi@0: this.next = next; aoqi@0: this.state = state; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Negate a branch opcode. aoqi@0: */ aoqi@0: public static int negate(int opcode) { aoqi@0: if (opcode == if_acmp_null) return if_acmp_nonnull; aoqi@0: else if (opcode == if_acmp_nonnull) return if_acmp_null; aoqi@0: else return ((opcode + 1) ^ 1) - 1; aoqi@0: } aoqi@0: aoqi@0: /** Emit a jump instruction. aoqi@0: * Return code pointer of instruction to be patched. aoqi@0: */ aoqi@0: public int emitJump(int opcode) { aoqi@0: if (fatcode) { aoqi@0: if (opcode == goto_ || opcode == jsr) { aoqi@0: emitop4(opcode + goto_w - goto_, 0); aoqi@0: } else { aoqi@0: emitop2(negate(opcode), 8); aoqi@0: emitop4(goto_w, 0); aoqi@0: alive = true; aoqi@0: pendingStackMap = needStackMap; aoqi@0: } aoqi@0: return cp - 5; aoqi@0: } else { aoqi@0: emitop2(opcode, 0); aoqi@0: return cp - 3; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Emit a branch with given opcode; return its chain. aoqi@0: * branch differs from jump in that jsr is treated as no-op. aoqi@0: */ aoqi@0: public Chain branch(int opcode) { aoqi@0: Chain result = null; aoqi@0: if (opcode == goto_) { aoqi@0: result = pendingJumps; aoqi@0: pendingJumps = null; aoqi@0: } aoqi@0: if (opcode != dontgoto && isAlive()) { aoqi@0: result = new Chain(emitJump(opcode), aoqi@0: result, aoqi@0: state.dup()); aoqi@0: fixedPc = fatcode; aoqi@0: if (opcode == goto_) alive = false; aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** Resolve chain to point to given target. aoqi@0: */ aoqi@0: public void resolve(Chain chain, int target) { aoqi@0: boolean changed = false; aoqi@0: State newState = state; aoqi@0: for (; chain != null; chain = chain.next) { aoqi@0: Assert.check(state != chain.state aoqi@0: && (target > chain.pc || state.stacksize == 0)); aoqi@0: if (target >= cp) { aoqi@0: target = cp; aoqi@0: } else if (get1(target) == goto_) { aoqi@0: if (fatcode) target = target + get4(target + 1); aoqi@0: else target = target + get2(target + 1); aoqi@0: } aoqi@0: if (get1(chain.pc) == goto_ && aoqi@0: chain.pc + 3 == target && target == cp && !fixedPc) { aoqi@0: // If goto the next instruction, the jump is not needed: aoqi@0: // compact the code. aoqi@0: if (varDebugInfo) { aoqi@0: adjustAliveRanges(cp, -3); aoqi@0: } aoqi@0: cp = cp - 3; aoqi@0: target = target - 3; aoqi@0: if (chain.next == null) { aoqi@0: // This is the only jump to the target. Exit the loop aoqi@0: // without setting new state. The code is reachable aoqi@0: // from the instruction before goto_. aoqi@0: alive = true; aoqi@0: break; aoqi@0: } aoqi@0: } else { aoqi@0: if (fatcode) aoqi@0: put4(chain.pc + 1, target - chain.pc); aoqi@0: else if (target - chain.pc < Short.MIN_VALUE || aoqi@0: target - chain.pc > Short.MAX_VALUE) aoqi@0: fatcode = true; aoqi@0: else aoqi@0: put2(chain.pc + 1, target - chain.pc); aoqi@0: Assert.check(!alive || aoqi@0: chain.state.stacksize == newState.stacksize && aoqi@0: chain.state.nlocks == newState.nlocks); aoqi@0: } aoqi@0: fixedPc = true; aoqi@0: if (cp == target) { aoqi@0: changed = true; aoqi@0: if (debugCode) aoqi@0: System.err.println("resolving chain state=" + chain.state); aoqi@0: if (alive) { aoqi@0: newState = chain.state.join(newState); aoqi@0: } else { aoqi@0: newState = chain.state; aoqi@0: alive = true; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: Assert.check(!changed || state != newState); aoqi@0: if (state != newState) { aoqi@0: setDefined(newState.defined); aoqi@0: state = newState; aoqi@0: pendingStackMap = needStackMap; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Resolve chain to point to current code pointer. aoqi@0: */ aoqi@0: public void resolve(Chain chain) { aoqi@0: Assert.check( aoqi@0: !alive || aoqi@0: chain==null || aoqi@0: state.stacksize == chain.state.stacksize && aoqi@0: state.nlocks == chain.state.nlocks); aoqi@0: pendingJumps = mergeChains(chain, pendingJumps); aoqi@0: } aoqi@0: aoqi@0: /** Resolve any pending jumps. aoqi@0: */ aoqi@0: public void resolvePending() { aoqi@0: Chain x = pendingJumps; aoqi@0: pendingJumps = null; aoqi@0: resolve(x, cp); aoqi@0: } aoqi@0: aoqi@0: /** Merge the jumps in of two chains into one. aoqi@0: */ aoqi@0: public static Chain mergeChains(Chain chain1, Chain chain2) { aoqi@0: // recursive merge sort aoqi@0: if (chain2 == null) return chain1; aoqi@0: if (chain1 == null) return chain2; aoqi@0: Assert.check( aoqi@0: chain1.state.stacksize == chain2.state.stacksize && aoqi@0: chain1.state.nlocks == chain2.state.nlocks); aoqi@0: if (chain1.pc < chain2.pc) aoqi@0: return new Chain( aoqi@0: chain2.pc, aoqi@0: mergeChains(chain1, chain2.next), aoqi@0: chain2.state); aoqi@0: return new Chain( aoqi@0: chain1.pc, aoqi@0: mergeChains(chain1.next, chain2), aoqi@0: chain1.state); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /* ************************************************************************** aoqi@0: * Catch clauses aoqi@0: ****************************************************************************/ aoqi@0: aoqi@0: /** Add a catch clause to code. aoqi@0: */ aoqi@0: public void addCatch( aoqi@0: char startPc, char endPc, char handlerPc, char catchType) { aoqi@0: catchInfo.append(new char[]{startPc, endPc, handlerPc, catchType}); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public void compressCatchTable() { aoqi@0: ListBuffer compressedCatchInfo = new ListBuffer<>(); aoqi@0: List handlerPcs = List.nil(); aoqi@0: for (char[] catchEntry : catchInfo) { aoqi@0: handlerPcs = handlerPcs.prepend((int)catchEntry[2]); aoqi@0: } aoqi@0: for (char[] catchEntry : catchInfo) { aoqi@0: int startpc = catchEntry[0]; aoqi@0: int endpc = catchEntry[1]; aoqi@0: if (startpc == endpc || aoqi@0: (startpc == (endpc - 1) && aoqi@0: handlerPcs.contains(startpc))) { aoqi@0: continue; aoqi@0: } else { aoqi@0: compressedCatchInfo.append(catchEntry); aoqi@0: } aoqi@0: } aoqi@0: catchInfo = compressedCatchInfo; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /* ************************************************************************** aoqi@0: * Line numbers aoqi@0: ****************************************************************************/ aoqi@0: aoqi@0: /** Add a line number entry. aoqi@0: */ aoqi@0: public void addLineNumber(char startPc, char lineNumber) { aoqi@0: if (lineDebugInfo) { aoqi@0: if (lineInfo.nonEmpty() && lineInfo.head[0] == startPc) aoqi@0: lineInfo = lineInfo.tail; aoqi@0: if (lineInfo.isEmpty() || lineInfo.head[1] != lineNumber) aoqi@0: lineInfo = lineInfo.prepend(new char[]{startPc, lineNumber}); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Mark beginning of statement. aoqi@0: */ aoqi@0: public void statBegin(int pos) { aoqi@0: if (pos != Position.NOPOS) { aoqi@0: pendingStatPos = pos; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Force stat begin eagerly aoqi@0: */ aoqi@0: public void markStatBegin() { aoqi@0: if (alive && lineDebugInfo) { aoqi@0: int line = lineMap.getLineNumber(pendingStatPos); aoqi@0: char cp1 = (char)cp; aoqi@0: char line1 = (char)line; aoqi@0: if (cp1 == cp && line1 == line) aoqi@0: addLineNumber(cp1, line1); aoqi@0: } aoqi@0: pendingStatPos = Position.NOPOS; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /* ************************************************************************** aoqi@0: * Simulated VM machine state aoqi@0: ****************************************************************************/ aoqi@0: aoqi@0: class State implements Cloneable { aoqi@0: /** The set of registers containing values. */ aoqi@0: Bits defined; aoqi@0: aoqi@0: /** The (types of the) contents of the machine stack. */ aoqi@0: Type[] stack; aoqi@0: aoqi@0: /** The first stack position currently unused. */ aoqi@0: int stacksize; aoqi@0: aoqi@0: /** The numbers of registers containing locked monitors. */ aoqi@0: int[] locks; aoqi@0: int nlocks; aoqi@0: aoqi@0: State() { aoqi@0: defined = new Bits(); aoqi@0: stack = new Type[16]; aoqi@0: } aoqi@0: aoqi@0: State dup() { aoqi@0: try { aoqi@0: State state = (State)super.clone(); aoqi@0: state.defined = new Bits(defined); aoqi@0: state.stack = stack.clone(); aoqi@0: if (locks != null) state.locks = locks.clone(); aoqi@0: if (debugCode) { aoqi@0: System.err.println("duping state " + this); aoqi@0: dump(); aoqi@0: } aoqi@0: return state; aoqi@0: } catch (CloneNotSupportedException ex) { aoqi@0: throw new AssertionError(ex); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void lock(int register) { aoqi@0: if (locks == null) { aoqi@0: locks = new int[20]; aoqi@0: } else { aoqi@0: locks = ArrayUtils.ensureCapacity(locks, nlocks); aoqi@0: } aoqi@0: locks[nlocks] = register; aoqi@0: nlocks++; aoqi@0: } aoqi@0: aoqi@0: void unlock(int register) { aoqi@0: nlocks--; aoqi@0: Assert.check(locks[nlocks] == register); aoqi@0: locks[nlocks] = -1; aoqi@0: } aoqi@0: aoqi@0: void push(Type t) { aoqi@0: if (debugCode) System.err.println(" pushing " + t); aoqi@0: switch (t.getTag()) { aoqi@0: case VOID: aoqi@0: return; aoqi@0: case BYTE: aoqi@0: case CHAR: aoqi@0: case SHORT: aoqi@0: case BOOLEAN: aoqi@0: t = syms.intType; aoqi@0: break; aoqi@0: default: aoqi@0: break; aoqi@0: } aoqi@0: stack = ArrayUtils.ensureCapacity(stack, stacksize+2); aoqi@0: stack[stacksize++] = t; aoqi@0: switch (width(t)) { aoqi@0: case 1: aoqi@0: break; aoqi@0: case 2: aoqi@0: stack[stacksize++] = null; aoqi@0: break; aoqi@0: default: aoqi@0: throw new AssertionError(t); aoqi@0: } aoqi@0: if (stacksize > max_stack) aoqi@0: max_stack = stacksize; aoqi@0: } aoqi@0: aoqi@0: Type pop1() { aoqi@0: if (debugCode) System.err.println(" popping " + 1); aoqi@0: stacksize--; aoqi@0: Type result = stack[stacksize]; aoqi@0: stack[stacksize] = null; aoqi@0: Assert.check(result != null && width(result) == 1); aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: Type peek() { aoqi@0: return stack[stacksize-1]; aoqi@0: } aoqi@0: aoqi@0: Type pop2() { aoqi@0: if (debugCode) System.err.println(" popping " + 2); aoqi@0: stacksize -= 2; aoqi@0: Type result = stack[stacksize]; aoqi@0: stack[stacksize] = null; aoqi@0: Assert.check(stack[stacksize+1] == null aoqi@0: && result != null && width(result) == 2); aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: void pop(int n) { aoqi@0: if (debugCode) System.err.println(" popping " + n); aoqi@0: while (n > 0) { aoqi@0: stack[--stacksize] = null; aoqi@0: n--; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void pop(Type t) { aoqi@0: pop(width(t)); aoqi@0: } aoqi@0: aoqi@0: /** Force the top of the stack to be treated as this supertype aoqi@0: * of its current type. */ aoqi@0: void forceStackTop(Type t) { aoqi@0: if (!alive) return; aoqi@0: switch (t.getTag()) { aoqi@0: case CLASS: aoqi@0: case ARRAY: aoqi@0: int width = width(t); aoqi@0: Type old = stack[stacksize-width]; aoqi@0: Assert.check(types.isSubtype(types.erasure(old), aoqi@0: types.erasure(t))); aoqi@0: stack[stacksize-width] = t; aoqi@0: break; aoqi@0: default: aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void markInitialized(UninitializedType old) { aoqi@0: Type newtype = old.initializedType(); aoqi@0: for (int i=0; i=0; i--) { aoqi@0: if (defined.isMember(i)) { aoqi@0: lastLocal = i; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: if (lastLocal >= 0) aoqi@0: System.err.println(" locals:"); aoqi@0: for (int i=0; i<=lastLocal; i++) { aoqi@0: System.err.print(" " + i + ": "); aoqi@0: if (defined.isMember(i)) { aoqi@0: LocalVar var = lvar[i]; aoqi@0: if (var == null) { aoqi@0: System.err.println("(none)"); aoqi@0: } else if (var.sym == null) aoqi@0: System.err.println("UNKNOWN!"); aoqi@0: else aoqi@0: System.err.println("" + var.sym + " of type " + aoqi@0: var.sym.erasure(types)); aoqi@0: } else { aoqi@0: System.err.println("undefined"); aoqi@0: } aoqi@0: } aoqi@0: if (nlocks != 0) { aoqi@0: System.err.print(" locks:"); aoqi@0: for (int i=0; i aliveRanges = new java.util.ArrayList<>(); aoqi@0: aoqi@0: LocalVar(VarSymbol v) { aoqi@0: this.sym = v; aoqi@0: this.reg = (char)v.adr; aoqi@0: } aoqi@0: public LocalVar dup() { aoqi@0: return new LocalVar(sym); aoqi@0: } aoqi@0: aoqi@0: Range firstRange() { aoqi@0: return aliveRanges.isEmpty() ? null : aliveRanges.get(0); aoqi@0: } aoqi@0: aoqi@0: Range lastRange() { aoqi@0: return aliveRanges.isEmpty() ? null : aliveRanges.get(aliveRanges.size() - 1); aoqi@0: } aoqi@0: aoqi@0: void removeLastRange() { aoqi@0: Range lastRange = lastRange(); aoqi@0: if (lastRange != null) { aoqi@0: aliveRanges.remove(lastRange); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String toString() { aoqi@0: if (aliveRanges == null) { aoqi@0: return "empty local var"; aoqi@0: } aoqi@0: StringBuilder sb = new StringBuilder().append(sym) aoqi@0: .append(" in register ").append((int)reg).append(" \n"); aoqi@0: for (Range r : aliveRanges) { aoqi@0: sb.append(" starts at pc=").append(Integer.toString(((int)r.start_pc))) aoqi@0: .append(" length=").append(Integer.toString(((int)r.length))) aoqi@0: .append("\n"); aoqi@0: } aoqi@0: return sb.toString(); aoqi@0: } aoqi@0: aoqi@0: public void openRange(char start) { aoqi@0: if (!hasOpenRange()) { aoqi@0: aliveRanges.add(new Range(start)); aoqi@0: } aoqi@0: } aoqi@0: vromero@2534: public void closeRange(char length) { vromero@2534: if (isLastRangeInitialized() && length > 0) { aoqi@0: Range range = lastRange(); aoqi@0: if (range != null) { aoqi@0: if (range.length == Character.MAX_VALUE) { vromero@2534: range.length = length; aoqi@0: } aoqi@0: } aoqi@0: } else { aoqi@0: removeLastRange(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public boolean hasOpenRange() { aoqi@0: if (aliveRanges.isEmpty()) { aoqi@0: return false; aoqi@0: } aoqi@0: return lastRange().length == Character.MAX_VALUE; aoqi@0: } aoqi@0: aoqi@0: public boolean isLastRangeInitialized() { aoqi@0: if (aliveRanges.isEmpty()) { aoqi@0: return false; aoqi@0: } aoqi@0: return lastRange().start_pc != Character.MAX_VALUE; aoqi@0: } aoqi@0: aoqi@0: public Range getWidestRange() { aoqi@0: if (aliveRanges.isEmpty()) { aoqi@0: return new Range(); aoqi@0: } else { aoqi@0: Range firstRange = firstRange(); aoqi@0: Range lastRange = lastRange(); aoqi@0: char length = (char)(lastRange.length + (lastRange.start_pc - firstRange.start_pc)); aoqi@0: return new Range(firstRange.start_pc, length); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }; aoqi@0: aoqi@0: /** Local variables, indexed by register. */ aoqi@0: LocalVar[] lvar; aoqi@0: aoqi@0: /** Add a new local variable. */ aoqi@0: private void addLocalVar(VarSymbol v) { aoqi@0: int adr = v.adr; aoqi@0: lvar = ArrayUtils.ensureCapacity(lvar, adr+1); aoqi@0: Assert.checkNull(lvar[adr]); aoqi@0: if (pendingJumps != null) { aoqi@0: resolvePending(); aoqi@0: } aoqi@0: lvar[adr] = new LocalVar(v); aoqi@0: state.defined.excl(adr); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public void closeAliveRanges(JCTree tree) { aoqi@0: closeAliveRanges(tree, cp); aoqi@0: } aoqi@0: aoqi@0: public void closeAliveRanges(JCTree tree, int closingCP) { aoqi@0: List locals = lvtRanges.getVars(meth, tree); aoqi@0: for (LocalVar localVar: lvar) { aoqi@0: for (VarSymbol aliveLocal : locals) { vromero@2595: if (localVar != null) { vromero@2595: if (localVar.sym == aliveLocal && localVar.lastRange() != null) { vromero@2595: char length = (char)(closingCP - localVar.lastRange().start_pc); vromero@2595: if (length < Character.MAX_VALUE) { vromero@2595: localVar.closeRange(length); vromero@2595: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void adjustAliveRanges(int oldCP, int delta) { aoqi@0: for (LocalVar localVar: lvar) { vromero@2572: if (localVar != null) { vromero@2572: for (LocalVar.Range range: localVar.aliveRanges) { vromero@2572: if (range.closed() && range.start_pc + range.length >= oldCP) { vromero@2572: range.length += delta; vromero@2572: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Calculates the size of the LocalVariableTable. aoqi@0: */ aoqi@0: public int getLVTSize() { aoqi@0: int result = varBufferSize; aoqi@0: for (int i = 0; i < varBufferSize; i++) { aoqi@0: LocalVar var = varBuffer[i]; aoqi@0: result += var.aliveRanges.size() - 1; aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** Set the current variable defined state. */ aoqi@0: public void setDefined(Bits newDefined) { aoqi@0: if (alive && newDefined != state.defined) { aoqi@0: Bits diff = new Bits(state.defined).xorSet(newDefined); aoqi@0: for (int adr = diff.nextBit(0); aoqi@0: adr >= 0; aoqi@0: adr = diff.nextBit(adr+1)) { aoqi@0: if (adr >= nextreg) aoqi@0: state.defined.excl(adr); aoqi@0: else if (state.defined.isMember(adr)) aoqi@0: setUndefined(adr); aoqi@0: else aoqi@0: setDefined(adr); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Mark a register as being (possibly) defined. */ aoqi@0: public void setDefined(int adr) { aoqi@0: LocalVar v = lvar[adr]; aoqi@0: if (v == null) { aoqi@0: state.defined.excl(adr); aoqi@0: } else { aoqi@0: state.defined.incl(adr); aoqi@0: if (cp < Character.MAX_VALUE) { aoqi@0: v.openRange((char)cp); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Mark a register as being undefined. */ aoqi@0: public void setUndefined(int adr) { aoqi@0: state.defined.excl(adr); aoqi@0: if (adr < lvar.length && aoqi@0: lvar[adr] != null && aoqi@0: lvar[adr].isLastRangeInitialized()) { aoqi@0: LocalVar v = lvar[adr]; aoqi@0: char length = (char)(curCP() - v.lastRange().start_pc); vromero@2534: if (length < Character.MAX_VALUE) { aoqi@0: lvar[adr] = v.dup(); aoqi@0: v.closeRange(length); aoqi@0: putVar(v); aoqi@0: } else { aoqi@0: v.removeLastRange(); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** End the scope of a variable. */ aoqi@0: private void endScope(int adr) { aoqi@0: LocalVar v = lvar[adr]; aoqi@0: if (v != null) { aoqi@0: if (v.isLastRangeInitialized()) { aoqi@0: char length = (char)(curCP() - v.lastRange().start_pc); aoqi@0: if (length < Character.MAX_VALUE) { aoqi@0: v.closeRange(length); aoqi@0: putVar(v); aoqi@0: fillLocalVarPosition(v); aoqi@0: } aoqi@0: } aoqi@0: /** the call to curCP() can implicitly adjust the current cp, if so aoqi@0: * the alive range of local variables may be modified. Thus we need aoqi@0: * all of them. For this reason assigning null to the given address aoqi@0: * should be the last action to do. aoqi@0: */ aoqi@0: lvar[adr] = null; aoqi@0: } aoqi@0: state.defined.excl(adr); aoqi@0: } aoqi@0: aoqi@0: private void fillLocalVarPosition(LocalVar lv) { aoqi@0: if (lv == null || lv.sym == null || !lv.sym.hasTypeAnnotations()) aoqi@0: return; aoqi@0: for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { aoqi@0: TypeAnnotationPosition p = ta.position; aoqi@0: LocalVar.Range widestRange = lv.getWidestRange(); aoqi@0: p.lvarOffset = new int[] { (int)widestRange.start_pc }; aoqi@0: p.lvarLength = new int[] { (int)widestRange.length }; aoqi@0: p.lvarIndex = new int[] { (int)lv.reg }; aoqi@0: p.isValidOffset = true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Method to be called after compressCatchTable to aoqi@0: // fill in the exception table index for type aoqi@0: // annotations on exception parameters. aoqi@0: public void fillExceptionParameterPositions() { aoqi@0: for (int i = 0; i < varBufferSize; ++i) { aoqi@0: LocalVar lv = varBuffer[i]; aoqi@0: if (lv == null || lv.sym == null aoqi@0: || !lv.sym.hasTypeAnnotations() aoqi@0: || !lv.sym.isExceptionParameter()) aoqi@0: continue; aoqi@0: aoqi@0: for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { aoqi@0: TypeAnnotationPosition p = ta.position; aoqi@0: // At this point p.type_index contains the catch type index. aoqi@0: // Use that index to determine the exception table index. aoqi@0: // We can afterwards discard the type_index. aoqi@0: // A TA position is shared for all type annotations in the aoqi@0: // same location; updating one is enough. aoqi@0: // Use -666 as a marker that the exception_index was already updated. aoqi@0: if (p.type_index != -666) { aoqi@0: p.exception_index = findExceptionIndex(p.type_index); aoqi@0: p.type_index = -666; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private int findExceptionIndex(int catchType) { aoqi@0: if (catchType == Integer.MIN_VALUE) { aoqi@0: // We didn't set the catch type index correctly. aoqi@0: // This shouldn't happen. aoqi@0: // TODO: issue error? aoqi@0: return -1; aoqi@0: } aoqi@0: List iter = catchInfo.toList(); aoqi@0: int len = catchInfo.length(); aoqi@0: for (int i = 0; i < len; ++i) { aoqi@0: char[] catchEntry = iter.head; aoqi@0: iter = iter.tail; aoqi@0: char ct = catchEntry[3]; aoqi@0: if (catchType == ct) { aoqi@0: return i; aoqi@0: } aoqi@0: } aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: /** Put a live variable range into the buffer to be output to the aoqi@0: * class file. aoqi@0: */ aoqi@0: void putVar(LocalVar var) { aoqi@0: // Keep local variables if aoqi@0: // 1) we need them for debug information aoqi@0: // 2) it is an exception type and it contains type annotations aoqi@0: boolean keepLocalVariables = varDebugInfo || aoqi@0: (var.sym.isExceptionParameter() && var.sym.hasTypeAnnotations()); aoqi@0: if (!keepLocalVariables) return; aoqi@0: if ((var.sym.flags() & Flags.SYNTHETIC) != 0) return; aoqi@0: if (varBuffer == null) aoqi@0: varBuffer = new LocalVar[20]; aoqi@0: else aoqi@0: varBuffer = ArrayUtils.ensureCapacity(varBuffer, varBufferSize); aoqi@0: varBuffer[varBufferSize++] = var; aoqi@0: } aoqi@0: aoqi@0: /** Previously live local variables, to be put into the variable table. */ aoqi@0: LocalVar[] varBuffer; aoqi@0: int varBufferSize; aoqi@0: aoqi@0: /** Create a new local variable address and return it. aoqi@0: */ aoqi@0: private int newLocal(int typecode) { aoqi@0: int reg = nextreg; aoqi@0: int w = width(typecode); aoqi@0: nextreg = reg + w; aoqi@0: if (nextreg > max_locals) max_locals = nextreg; aoqi@0: return reg; aoqi@0: } aoqi@0: aoqi@0: private int newLocal(Type type) { aoqi@0: return newLocal(typecode(type)); aoqi@0: } aoqi@0: aoqi@0: public int newLocal(VarSymbol v) { aoqi@0: int reg = v.adr = newLocal(v.erasure(types)); aoqi@0: addLocalVar(v); aoqi@0: return reg; aoqi@0: } aoqi@0: aoqi@0: /** Start a set of fresh registers. aoqi@0: */ aoqi@0: public void newRegSegment() { aoqi@0: nextreg = max_locals; aoqi@0: } aoqi@0: aoqi@0: /** End scopes of all variables with registers ≥ first. aoqi@0: */ aoqi@0: public void endScopes(int first) { aoqi@0: int prevNextReg = nextreg; aoqi@0: nextreg = first; aoqi@0: for (int i = nextreg; i < prevNextReg; i++) endScope(i); aoqi@0: } aoqi@0: aoqi@0: /************************************************************************** aoqi@0: * static tables aoqi@0: *************************************************************************/ aoqi@0: aoqi@0: public static String mnem(int opcode) { aoqi@0: return Mneumonics.mnem[opcode]; aoqi@0: } aoqi@0: aoqi@0: private static class Mneumonics { aoqi@0: private final static String[] mnem = new String[ByteCodeCount]; aoqi@0: static { aoqi@0: mnem[nop] = "nop"; aoqi@0: mnem[aconst_null] = "aconst_null"; aoqi@0: mnem[iconst_m1] = "iconst_m1"; aoqi@0: mnem[iconst_0] = "iconst_0"; aoqi@0: mnem[iconst_1] = "iconst_1"; aoqi@0: mnem[iconst_2] = "iconst_2"; aoqi@0: mnem[iconst_3] = "iconst_3"; aoqi@0: mnem[iconst_4] = "iconst_4"; aoqi@0: mnem[iconst_5] = "iconst_5"; aoqi@0: mnem[lconst_0] = "lconst_0"; aoqi@0: mnem[lconst_1] = "lconst_1"; aoqi@0: mnem[fconst_0] = "fconst_0"; aoqi@0: mnem[fconst_1] = "fconst_1"; aoqi@0: mnem[fconst_2] = "fconst_2"; aoqi@0: mnem[dconst_0] = "dconst_0"; aoqi@0: mnem[dconst_1] = "dconst_1"; aoqi@0: mnem[bipush] = "bipush"; aoqi@0: mnem[sipush] = "sipush"; aoqi@0: mnem[ldc1] = "ldc1"; aoqi@0: mnem[ldc2] = "ldc2"; aoqi@0: mnem[ldc2w] = "ldc2w"; aoqi@0: mnem[iload] = "iload"; aoqi@0: mnem[lload] = "lload"; aoqi@0: mnem[fload] = "fload"; aoqi@0: mnem[dload] = "dload"; aoqi@0: mnem[aload] = "aload"; aoqi@0: mnem[iload_0] = "iload_0"; aoqi@0: mnem[lload_0] = "lload_0"; aoqi@0: mnem[fload_0] = "fload_0"; aoqi@0: mnem[dload_0] = "dload_0"; aoqi@0: mnem[aload_0] = "aload_0"; aoqi@0: mnem[iload_1] = "iload_1"; aoqi@0: mnem[lload_1] = "lload_1"; aoqi@0: mnem[fload_1] = "fload_1"; aoqi@0: mnem[dload_1] = "dload_1"; aoqi@0: mnem[aload_1] = "aload_1"; aoqi@0: mnem[iload_2] = "iload_2"; aoqi@0: mnem[lload_2] = "lload_2"; aoqi@0: mnem[fload_2] = "fload_2"; aoqi@0: mnem[dload_2] = "dload_2"; aoqi@0: mnem[aload_2] = "aload_2"; aoqi@0: mnem[iload_3] = "iload_3"; aoqi@0: mnem[lload_3] = "lload_3"; aoqi@0: mnem[fload_3] = "fload_3"; aoqi@0: mnem[dload_3] = "dload_3"; aoqi@0: mnem[aload_3] = "aload_3"; aoqi@0: mnem[iaload] = "iaload"; aoqi@0: mnem[laload] = "laload"; aoqi@0: mnem[faload] = "faload"; aoqi@0: mnem[daload] = "daload"; aoqi@0: mnem[aaload] = "aaload"; aoqi@0: mnem[baload] = "baload"; aoqi@0: mnem[caload] = "caload"; aoqi@0: mnem[saload] = "saload"; aoqi@0: mnem[istore] = "istore"; aoqi@0: mnem[lstore] = "lstore"; aoqi@0: mnem[fstore] = "fstore"; aoqi@0: mnem[dstore] = "dstore"; aoqi@0: mnem[astore] = "astore"; aoqi@0: mnem[istore_0] = "istore_0"; aoqi@0: mnem[lstore_0] = "lstore_0"; aoqi@0: mnem[fstore_0] = "fstore_0"; aoqi@0: mnem[dstore_0] = "dstore_0"; aoqi@0: mnem[astore_0] = "astore_0"; aoqi@0: mnem[istore_1] = "istore_1"; aoqi@0: mnem[lstore_1] = "lstore_1"; aoqi@0: mnem[fstore_1] = "fstore_1"; aoqi@0: mnem[dstore_1] = "dstore_1"; aoqi@0: mnem[astore_1] = "astore_1"; aoqi@0: mnem[istore_2] = "istore_2"; aoqi@0: mnem[lstore_2] = "lstore_2"; aoqi@0: mnem[fstore_2] = "fstore_2"; aoqi@0: mnem[dstore_2] = "dstore_2"; aoqi@0: mnem[astore_2] = "astore_2"; aoqi@0: mnem[istore_3] = "istore_3"; aoqi@0: mnem[lstore_3] = "lstore_3"; aoqi@0: mnem[fstore_3] = "fstore_3"; aoqi@0: mnem[dstore_3] = "dstore_3"; aoqi@0: mnem[astore_3] = "astore_3"; aoqi@0: mnem[iastore] = "iastore"; aoqi@0: mnem[lastore] = "lastore"; aoqi@0: mnem[fastore] = "fastore"; aoqi@0: mnem[dastore] = "dastore"; aoqi@0: mnem[aastore] = "aastore"; aoqi@0: mnem[bastore] = "bastore"; aoqi@0: mnem[castore] = "castore"; aoqi@0: mnem[sastore] = "sastore"; aoqi@0: mnem[pop] = "pop"; aoqi@0: mnem[pop2] = "pop2"; aoqi@0: mnem[dup] = "dup"; aoqi@0: mnem[dup_x1] = "dup_x1"; aoqi@0: mnem[dup_x2] = "dup_x2"; aoqi@0: mnem[dup2] = "dup2"; aoqi@0: mnem[dup2_x1] = "dup2_x1"; aoqi@0: mnem[dup2_x2] = "dup2_x2"; aoqi@0: mnem[swap] = "swap"; aoqi@0: mnem[iadd] = "iadd"; aoqi@0: mnem[ladd] = "ladd"; aoqi@0: mnem[fadd] = "fadd"; aoqi@0: mnem[dadd] = "dadd"; aoqi@0: mnem[isub] = "isub"; aoqi@0: mnem[lsub] = "lsub"; aoqi@0: mnem[fsub] = "fsub"; aoqi@0: mnem[dsub] = "dsub"; aoqi@0: mnem[imul] = "imul"; aoqi@0: mnem[lmul] = "lmul"; aoqi@0: mnem[fmul] = "fmul"; aoqi@0: mnem[dmul] = "dmul"; aoqi@0: mnem[idiv] = "idiv"; aoqi@0: mnem[ldiv] = "ldiv"; aoqi@0: mnem[fdiv] = "fdiv"; aoqi@0: mnem[ddiv] = "ddiv"; aoqi@0: mnem[imod] = "imod"; aoqi@0: mnem[lmod] = "lmod"; aoqi@0: mnem[fmod] = "fmod"; aoqi@0: mnem[dmod] = "dmod"; aoqi@0: mnem[ineg] = "ineg"; aoqi@0: mnem[lneg] = "lneg"; aoqi@0: mnem[fneg] = "fneg"; aoqi@0: mnem[dneg] = "dneg"; aoqi@0: mnem[ishl] = "ishl"; aoqi@0: mnem[lshl] = "lshl"; aoqi@0: mnem[ishr] = "ishr"; aoqi@0: mnem[lshr] = "lshr"; aoqi@0: mnem[iushr] = "iushr"; aoqi@0: mnem[lushr] = "lushr"; aoqi@0: mnem[iand] = "iand"; aoqi@0: mnem[land] = "land"; aoqi@0: mnem[ior] = "ior"; aoqi@0: mnem[lor] = "lor"; aoqi@0: mnem[ixor] = "ixor"; aoqi@0: mnem[lxor] = "lxor"; aoqi@0: mnem[iinc] = "iinc"; aoqi@0: mnem[i2l] = "i2l"; aoqi@0: mnem[i2f] = "i2f"; aoqi@0: mnem[i2d] = "i2d"; aoqi@0: mnem[l2i] = "l2i"; aoqi@0: mnem[l2f] = "l2f"; aoqi@0: mnem[l2d] = "l2d"; aoqi@0: mnem[f2i] = "f2i"; aoqi@0: mnem[f2l] = "f2l"; aoqi@0: mnem[f2d] = "f2d"; aoqi@0: mnem[d2i] = "d2i"; aoqi@0: mnem[d2l] = "d2l"; aoqi@0: mnem[d2f] = "d2f"; aoqi@0: mnem[int2byte] = "int2byte"; aoqi@0: mnem[int2char] = "int2char"; aoqi@0: mnem[int2short] = "int2short"; aoqi@0: mnem[lcmp] = "lcmp"; aoqi@0: mnem[fcmpl] = "fcmpl"; aoqi@0: mnem[fcmpg] = "fcmpg"; aoqi@0: mnem[dcmpl] = "dcmpl"; aoqi@0: mnem[dcmpg] = "dcmpg"; aoqi@0: mnem[ifeq] = "ifeq"; aoqi@0: mnem[ifne] = "ifne"; aoqi@0: mnem[iflt] = "iflt"; aoqi@0: mnem[ifge] = "ifge"; aoqi@0: mnem[ifgt] = "ifgt"; aoqi@0: mnem[ifle] = "ifle"; aoqi@0: mnem[if_icmpeq] = "if_icmpeq"; aoqi@0: mnem[if_icmpne] = "if_icmpne"; aoqi@0: mnem[if_icmplt] = "if_icmplt"; aoqi@0: mnem[if_icmpge] = "if_icmpge"; aoqi@0: mnem[if_icmpgt] = "if_icmpgt"; aoqi@0: mnem[if_icmple] = "if_icmple"; aoqi@0: mnem[if_acmpeq] = "if_acmpeq"; aoqi@0: mnem[if_acmpne] = "if_acmpne"; aoqi@0: mnem[goto_] = "goto_"; aoqi@0: mnem[jsr] = "jsr"; aoqi@0: mnem[ret] = "ret"; aoqi@0: mnem[tableswitch] = "tableswitch"; aoqi@0: mnem[lookupswitch] = "lookupswitch"; aoqi@0: mnem[ireturn] = "ireturn"; aoqi@0: mnem[lreturn] = "lreturn"; aoqi@0: mnem[freturn] = "freturn"; aoqi@0: mnem[dreturn] = "dreturn"; aoqi@0: mnem[areturn] = "areturn"; aoqi@0: mnem[return_] = "return_"; aoqi@0: mnem[getstatic] = "getstatic"; aoqi@0: mnem[putstatic] = "putstatic"; aoqi@0: mnem[getfield] = "getfield"; aoqi@0: mnem[putfield] = "putfield"; aoqi@0: mnem[invokevirtual] = "invokevirtual"; aoqi@0: mnem[invokespecial] = "invokespecial"; aoqi@0: mnem[invokestatic] = "invokestatic"; aoqi@0: mnem[invokeinterface] = "invokeinterface"; aoqi@0: mnem[invokedynamic] = "invokedynamic"; aoqi@0: mnem[new_] = "new_"; aoqi@0: mnem[newarray] = "newarray"; aoqi@0: mnem[anewarray] = "anewarray"; aoqi@0: mnem[arraylength] = "arraylength"; aoqi@0: mnem[athrow] = "athrow"; aoqi@0: mnem[checkcast] = "checkcast"; aoqi@0: mnem[instanceof_] = "instanceof_"; aoqi@0: mnem[monitorenter] = "monitorenter"; aoqi@0: mnem[monitorexit] = "monitorexit"; aoqi@0: mnem[wide] = "wide"; aoqi@0: mnem[multianewarray] = "multianewarray"; aoqi@0: mnem[if_acmp_null] = "if_acmp_null"; aoqi@0: mnem[if_acmp_nonnull] = "if_acmp_nonnull"; aoqi@0: mnem[goto_w] = "goto_w"; aoqi@0: mnem[jsr_w] = "jsr_w"; aoqi@0: mnem[breakpoint] = "breakpoint"; aoqi@0: } aoqi@0: } aoqi@0: }