/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/** An internal structure that corresponds to the code attribute of
* methods in a classfile. The class also provides some utility operations to
* generate bytecode instructions.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Code {
public final boolean debugCode;
public final boolean needStackMap;
public enum StackMapFormat {
NONE,
CLDC {
}
},
JSR202 {
return names.StackMapTable;
}
};
}
}
/*---------- classfile fields: --------------- */
/** The maximum stack size.
*/
/** The maximum number of local variable slots.
*/
/** The code buffer.
*/
/** the current code pointer.
*/
/** Check the code against VM spec limits; if
* problems report them and return true.
*/
return true;
}
return true;
}
return true;
}
return false;
}
/** A buffer for expression catch data. Each enter is a vector
* of four unsigned shorts.
*/
/** A buffer for line number information. Each entry is a vector
* of two unsigned shorts.
*/
/** The CharacterRangeTable
*/
/*---------- internal fields: --------------- */
/** Are we generating code with jumps >= 32K?
*/
public boolean fatcode;
/** Code generation enabled?
*/
private boolean alive = true;
/** The current machine state (registers and stack).
*/
/** Is it forbidden to compactify code, because something is
* pointing to current location?
*/
private boolean fixedPc = false;
/** The next available register.
*/
/** A chain for jumps to be resolved before the next opcode is emitted.
* We do this lazily to avoid jumps to jumps.
*/
/** The position of the currently statement, if we are at the
* start of this statement, NOPOS otherwise.
* We need this to emit line numbers lazily, which we need to do
* because of jump-to-jump optimization.
*/
/** Set true when a stackMap is needed at the current PC. */
boolean pendingStackMap = false;
/** The stack map format to be generated. */
/** Switch: emit variable debug info.
*/
boolean varDebugInfo;
/** Switch: emit line number info.
*/
boolean lineDebugInfo;
/** Emit line number info if map supplied
*/
/** The constant pool of the current class.
*/
/** Construct a code object, given the settings of the fatcode,
* debugging info switches and the CharacterRangeTable.
*/
boolean fatcode,
boolean varDebugInfo,
boolean debugCode,
this.varDebugInfo = varDebugInfo;
switch (stackMap) {
case CLDC:
case JSR202:
this.needStackMap = true;
break;
default:
this.needStackMap = false;
}
}
/* **************************************************************************
* Typecodes & related stuff
****************************************************************************/
/** Given a type, return its type code (used implicitly in the
* JVM architecture).
*/
case DOUBLE: return DOUBLEcode;
case CLASS:
case ARRAY:
case METHOD:
case BOT:
case TYPEVAR:
case UNINITIALIZED_THIS:
case UNINITIALIZED_OBJECT:
return OBJECTcode;
}
}
/** Collapse type code for subtypes of int to INTcode.
*/
switch (tc) {
default: return tc;
}
}
/** The width in bytes of objects of the type.
*/
switch (typecode) {
case VOIDcode: return 0;
default: return 1;
}
}
}
/** The total width taken up by a vector of objects.
*/
int w = 0;
return w;
}
/** Given a type, return its code for allocating arrays of that type.
*/
case BYTE: return 8;
case BOOLEAN: return 4;
case SHORT: return 9;
case CHAR: return 5;
case INT: return 10;
case LONG: return 11;
case FLOAT: return 6;
case DOUBLE: return 7;
case CLASS: return 0;
case ARRAY: return 1;
}
}
/* **************************************************************************
* Emit code
****************************************************************************/
/** The current output code pointer.
*/
public int curPc() {
fixedPc = true;
return cp;
}
/** Emit a byte of code.
*/
if (!alive) return;
}
}
/** Emit two bytes of code.
*/
if (!alive) return;
} else {
}
}
/** Emit four bytes of code.
*/
if (!alive) return;
} else {
}
}
/** Emit an opcode.
*/
if (alive) {
if (pendingStackMap) {
pendingStackMap = false;
emitStackMap();
}
if (debugCode)
}
}
void postop() {
}
/** Emit a multinewarray instruction.
*/
if (!alive) return;
}
/** Emit newarray.
*/
if (!alive) return;
}
/** Emit anewarray.
*/
if (!alive) return;
}
/** Emit an invokeinterface instruction.
*/
if (!alive) return;
emit1(0);
}
/** Emit an invokespecial instruction.
*/
if (!alive) return;
if (sym.isConstructor())
}
/** Emit an invokestatic instruction.
*/
if (!alive) return;
}
/** Emit an invokevirtual instruction.
*/
if (!alive) return;
}
/** Emit an invokedynamic instruction.
*/
// N.B. this format is under consideration by the JSR 292 EG
if (!alive) return;
emit2(0);
}
/** Emit an opcode with no operand field.
*/
if (!alive) return;
switch (op) {
case aaload: {
//sometimes 'null type' is treated as a one-dimensional array type
//see Gen.visitLiteral - we should handle this case accordingly
break;
case goto_:
markDead();
break;
case nop:
case ineg:
case lneg:
case fneg:
case dneg:
break;
case aconst_null:
break;
case iconst_m1:
case iconst_0:
case iconst_1:
case iconst_2:
case iconst_3:
case iconst_4:
case iconst_5:
case iload_0:
case iload_1:
case iload_2:
case iload_3:
break;
case lconst_0:
case lconst_1:
case lload_0:
case lload_1:
case lload_2:
case lload_3:
break;
case fconst_0:
case fconst_1:
case fconst_2:
case fload_0:
case fload_1:
case fload_2:
case fload_3:
break;
case dconst_0:
case dconst_1:
case dload_0:
case dload_1:
case dload_2:
case dload_3:
break;
case aload_0:
break;
case aload_1:
break;
case aload_2:
break;
case aload_3:
break;
case iaload:
case baload:
case caload:
case saload:
break;
case laload:
break;
case faload:
break;
case daload:
break;
case istore_0:
case istore_1:
case istore_2:
case istore_3:
case fstore_0:
case fstore_1:
case fstore_2:
case fstore_3:
case astore_0:
case astore_1:
case astore_2:
case astore_3:
case pop:
case lshr:
case lshl:
case lushr:
break;
case areturn:
case ireturn:
case freturn:
markDead();
break;
case athrow:
markDead();
break;
case lstore_0:
case lstore_1:
case lstore_2:
case lstore_3:
case dstore_0:
case dstore_1:
case dstore_2:
case dstore_3:
case pop2:
break;
case lreturn:
case dreturn:
markDead();
break;
case dup:
break;
case return_:
markDead();
break;
case arraylength:
break;
case isub:
case iadd:
case imul:
case idiv:
case imod:
case ishl:
case ishr:
case iushr:
case iand:
case ior:
case ixor:
// state.pop(1);
// state.push(syms.intType);
break;
case aastore:
break;
case land:
case lor:
case lxor:
case lmod:
case ldiv:
case lmul:
case lsub:
case ladd:
break;
case lcmp:
break;
case l2i:
break;
case i2l:
break;
case i2f:
break;
case i2d:
break;
case l2f:
break;
case l2d:
break;
case f2i:
break;
case f2l:
break;
case f2d:
break;
case d2i:
break;
case d2l:
break;
case d2f:
break;
case tableswitch:
case lookupswitch:
// the caller is responsible for patching up the state
break;
case dup_x1: {
break;
}
case bastore:
break;
case int2byte:
case int2char:
case int2short:
break;
case fmul:
case fadd:
case fsub:
case fdiv:
case fmod:
break;
case castore:
case iastore:
case fastore:
case sastore:
break;
case lastore:
case dastore:
break;
case dup2:
} else {
}
break;
case dup2_x1:
} else {
}
break;
case dup2_x2:
// form 1
} else {
// form 3
}
} else {
// form 2
} else {
// form 4
}
}
break;
case dup_x2: {
// form 1
} else {
// form 2
}
}
break;
case fcmpl:
case fcmpg:
break;
case dcmpl:
case dcmpg:
break;
case swap: {
break;
}
case dadd:
case dsub:
case dmul:
case ddiv:
case dmod:
break;
case ret:
markDead();
break;
case wide:
// must be handled by the caller.
return;
case monitorenter:
case monitorexit:
break;
default:
}
postop();
}
/** Emit an opcode with a one-byte operand field.
*/
if (!alive) return;
switch (op) {
case bipush:
break;
case ldc1:
break;
default:
}
postop();
}
/** The type of a constant pool entry. */
throw new AssertionError(o);
}
/** Emit an opcode with a one-byte operand field;
* widen if field does not fit in a byte.
*/
if (od > 0xFF) {
} else {
}
if (!alive) return;
switch (op) {
case iload:
break;
case lload:
break;
case fload:
break;
case dload:
break;
case aload:
break;
case lstore:
case dstore:
break;
case istore:
case fstore:
case astore:
break;
case ret:
markDead();
break;
default:
}
postop();
}
/** Emit an opcode with two one-byte operand fields;
* widen if either field does not fit in a byte.
*/
} else {
}
if (!alive) return;
switch (op) {
case iinc:
break;
default:
}
}
/** Emit an opcode with a two-byte operand field.
*/
if (!alive) return;
switch (op) {
case getstatic:
break;
case putstatic:
break;
case new_:
break;
case sipush:
break;
case if_acmp_null:
case if_acmp_nonnull:
case ifeq:
case ifne:
case iflt:
case ifge:
case ifgt:
case ifle:
break;
case if_icmpeq:
case if_icmpne:
case if_icmplt:
case if_icmpge:
case if_icmpgt:
case if_icmple:
case if_acmpeq:
case if_acmpne:
break;
case goto_:
markDead();
break;
case putfield:
break;
case getfield:
break;
case checkcast: {
break; }
case ldc2w:
break;
case instanceof_:
break;
case ldc2:
break;
case jsr:
break;
default:
}
// postop();
}
/** Emit an opcode with a four-byte operand field.
*/
if (!alive) return;
switch (op) {
case goto_w:
markDead();
break;
case jsr_w:
break;
default:
}
// postop();
}
/** Align code pointer to next `incr' boundary.
*/
if (alive)
}
/** Place a byte into code at address pc. Pre: pc + 1 <= cp.
*/
}
/** Place two bytes into code at address pc. Pre: pc + 2 <= cp.
*/
// pre: pc + 2 <= cp
}
/** Place four bytes into code at address pc. Pre: pc + 4 <= cp.
*/
// pre: pc + 4 <= cp
}
/** Return code byte at position pc as an unsigned int.
*/
}
/** Return two code bytes at position pc as an unsigned int.
*/
}
/** Return four code bytes at position pc as an int.
*/
// pre: pc + 4 <= cp
return
}
/** Is code generation currently enabled?
*/
public boolean isAlive() {
}
*/
public void markDead() {
alive = false;
}
/** Declare an entry point; return current code pointer
*/
public int entryPoint() {
alive = true;
return pc;
}
/** Declare an entry point with initial state;
* return current code pointer
*/
alive = true;
return pc;
}
/** Declare an entry point with initial state plus a pushed value;
* return current code pointer
*/
alive = true;
return pc;
}
/**************************************************************************
* Stack map generation
*************************************************************************/
/** An entry in the stack map. */
static class StackMapFrame {
int pc;
}
/** A buffer of cldc stack map entries. */
/** A buffer of compressed StackMapTable entries. */
/** The last PC at which we generated a stack map. */
/** The last stack map frame in StackMapTable. */
/** The stack map frame before the last one. */
/** Emit a stack map entry. */
public void emitStackMap() {
if (!needStackMap) return;
switch (stackMap) {
case CLDC:
break;
case JSR202:
break;
default:
throw new AssertionError("Should have chosen a stackmap format");
}
// DEBUG code follows
}
private int getLocalsSize() {
int nextLocal = 0;
break;
}
}
return nextLocal;
}
/** Emit a CLDC stack map frame. */
if (lastStackMapPC == pc) {
// drop existing stackmap at this offset
}
lastStackMapPC = pc;
if (stackMapBuffer == null) {
0, stackMapBufferSize);
}
for (int i=0; i<localsSize; i++) {
if (!(vtype instanceof UninitializedType))
}
}
}
// first frame
lastFrame = getInitialFrame();
// drop existing stackmap at this offset
}
int localCount = 0;
if (!(vtype instanceof UninitializedType))
}
}
}
int stackCount = 0;
stackCount++;
}
}
stackCount = 0;
}
}
if (stackMapTableBuffer == null) {
0, stackMapBufferSize);
}
}
int count = 0;
} else {
}
} else {
}
}
return frame;
}
/**************************************************************************
* Operations having to do with jumps
*************************************************************************/
/** A chain represents a list of unresolved jumps. Jump locations
* are sorted in decreasing order.
*/
public static class Chain {
/** The position of the jump instruction.
*/
public final int pc;
/** The machine state after the jump instruction.
* Invariant: all elements of a chain list have the same stacksize
* and compatible stack and register contents.
*/
/** The next jump in the list.
*/
/** Construct a chain from its jump position, stacksize, previous
* chain, and machine state.
*/
}
}
/** Negate a branch opcode.
*/
}
/** Emit a jump instruction.
* Return code pointer of instruction to be patched.
*/
if (fatcode) {
} else {
alive = true;
}
return cp - 5;
} else {
return cp - 3;
}
}
/** Emit a branch with given opcode; return its chain.
* branch differs from jump in that jsr is treated as no-op.
*/
pendingJumps = null;
}
}
return result;
}
/** Resolve chain to point to given target.
*/
boolean changed = false;
}
// If goto the next instruction, the jump is not needed:
// compact the code.
// This is the only jump to the target. Exit the loop
// without setting new state. The code is reachable
// from the instruction before goto_.
alive = true;
break;
}
} else {
if (fatcode)
fatcode = true;
else
}
fixedPc = true;
changed = true;
if (debugCode)
if (alive) {
} else {
alive = true;
}
}
}
}
}
/** Resolve chain to point to current code pointer.
*/
!alive ||
}
/** Resolve any pending jumps.
*/
public void resolvePending() {
Chain x = pendingJumps;
pendingJumps = null;
}
/** Merge the jumps in of two chains into one.
*/
// recursive merge sort
return new Chain(
return new Chain(
}
/* **************************************************************************
* Catch clauses
****************************************************************************/
/** Add a catch clause to code.
*/
public void addCatch(
}
/* **************************************************************************
* Line numbers
****************************************************************************/
/** Add a line number entry.
*/
if (lineDebugInfo) {
}
}
/** Mark beginning of statement.
*/
}
}
/** Force stat begin eagerly
*/
public void markStatBegin() {
if (alive && lineDebugInfo) {
}
}
/* **************************************************************************
* Simulated VM machine state
****************************************************************************/
/** The set of registers containing values. */
/** The (types of the) contents of the machine stack. */
/** The first stack position currently unused. */
int stacksize;
/** The numbers of registers containing locked monitors. */
int[] locks;
int nlocks;
State() {
}
try {
if (debugCode) {
dump();
}
return state;
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
locks = new int[20];
}
nlocks++;
}
nlocks--;
}
switch (t.tag) {
return;
break;
default:
break;
}
}
switch (width(t)) {
case 1:
break;
case 2:
break;
default:
throw new AssertionError(t);
}
}
stacksize--;
return result;
}
}
stacksize -= 2;
return result;
}
void pop(int n) {
while (n > 0) {
n--;
}
}
}
/** Force the top of the stack to be treated as this supertype
* of its current type. */
if (!alive) return;
switch (t.tag) {
case CLASS:
case ARRAY:
break;
default:
}
}
for (int i=0; i<stacksize; i++)
// should the following be initialized to cp?
}
}
}
for (int i=0; i<stacksize; ) {
t==tother ? t :
error();
i += w;
}
return this;
}
throw new AssertionError("inconsistent stack types at join point");
}
void dump() {
dump(-1);
}
if (pc == -1)
else
for (int i=0; i<stacksize; i++)
int lastLocal = 0;
lastLocal = i;
break;
}
}
if (lastLocal >= 0)
for (int i=0; i<=lastLocal; i++) {
else
} else {
}
}
if (nlocks != 0) {
for (int i=0; i<nlocks; i++) {
}
}
}
}
/* **************************************************************************
* Local variables
****************************************************************************/
/** A live range of a local variable. */
static class LocalVar {
final char reg;
this.sym = v;
}
}
return "" + sym + " in register " + ((int)reg) + " starts at pc=" + ((int)start_pc) + " length=" + ((int)length);
}
};
/** Local variables, indexed by register. */
/** Add a new local variable. */
}
}
/** Set the current variable defined state. */
adr >= 0;
else
}
}
}
/** Mark a register as being (possibly) defined. */
if (v == null) {
} else {
}
}
}
/** Mark a register as being undefined. */
putVar(v);
} else {
}
}
}
/** End the scope of a variable. */
if (v != null) {
putVar(v);
}
}
}
}
/** Put a live variable range into the buffer to be output to the
* class file.
*/
if (!varDebugInfo) return;
}
}
/** Previously live local variables, to be put into the variable table. */
int varBufferSize;
/** Create a new local variable address and return it.
*/
return reg;
}
}
addLocalVar(v);
return reg;
}
/** Start a set of fresh registers.
*/
public void newRegSegment() {
}
/** End scopes of all variables with registers >= first.
*/
int prevNextReg = nextreg;
}
/**************************************************************************
* static tables
*************************************************************************/
}
private static class Mneumonics {
static {
}
}
}