// Copyright (c) 1997, 1998, 1999, 2001, 2003, 2004 Per M.A. Bothner.
// This is free software; for terms and warranty disclaimer see ./COPYING.
package gnu.bytecode;
import java.io.*;
/**
* Represents the contents of a standard "Code" attribute.
* <p>
* Most of the actual methods that generate bytecode operation
* are in this class (typically with names starting with <code>emit</code>),
* though there are also some in <code>Method</code>.
* <p>
* Note that a <code>CodeAttr</code> is an <code>Attribute</code>
* of a <code>Method</code>, and can in turn contain other
* <code>Attribute</code>s, such as a <code>LineNumbersAttr</code>.
*
* @author Per Bothner
*/
public class CodeAttr extends Attribute implements AttrContainer
{
Attribute attributes;
public final Attribute getAttributes () { return attributes; }
public final void setAttributes (Attribute attributes)
{ this.attributes = attributes; }
LineNumbersAttr lines;
public LocalVarsAttr locals;
SourceDebugExtAttr sourceDbgExt;
// In hindsight, maintaining stack_types is more hassle than it is worth.
// Instead, better to just keep track of SP, which should catch most
// stack errors, while being more general and less hassle. FIXME.
Type[] stack_types;
int SP; // Current stack size (in "words")
private int max_stack;
private int max_locals;
/** Current active length of code array.
* Note that processFixups may expand/contract the code array. */
int PC;
byte[] code;
/* The exception handler table, as a vector of quadruples
(start_pc, end_pc, handler_pc, catch_type).
Only the first exception_table_length quadruples are defined. */
short[] exception_table;
/* The number of (defined) exception handlers (i.e. quadruples)
in exception_table. */
int exception_table_length;
/** Not a fixup - a no-op. */
static final int FIXUP_NONE = 0;
/** The definition of a label. */
static final int FIXUP_DEFINE = 1;
/** The offset points to a tableswitch/lookupswitch - handle padding. */
static final int FIXUP_SWITCH = 2;
/** The offset contains a label relative to the previous FIXUP_SWITCH. */
static final int FIXUP_CASE = 3;
/** The offset points to a goto instruction.
* This case up to FIXUP_TRANSFER2 must be contiguous
* - see the jump-to-jump optimization in processFixups. */
static final int FIXUP_GOTO = 4;
/** The offset points to a jsr instruction. */
static final int FIXUP_JSR = 5;
/** The offset points to a conditional transfer (if_xxx) instruction. */
static final int FIXUP_TRANSFER = 6;
/** A FIXUP_GOTO_, FIXUP_JSR, or FIXUP_TRANSFER that uses a 2-byte offset. */
static final int FIXUP_TRANSFER2 = 7;
/** The offsets points to 3 bytes that should be deleted. */
static final int FIXUP_DELETE3 = 8;
/** The following instructions are moved to later in the code stream.
* Instead the instructions starting at the fixup label are patched here.
* (If the fixup label is null, we're done.)
* This allows re-arranging code to avoid unneeded gotos.
* The following instruction is the target of a later FIXUP_MOVE,
* and we'll insert then when we get to it. */
static final int FIXUP_MOVE = 9;
/** The following instructions are moved to the end of the code stream.
* Same as FIXUP_MOVE, but there is no explicit later FIXUP_MOVE that
* refers to the following instructions. Created by beginFragment.
* The fixup_offset points to the end of the fragment.
* (The first processFixups patches these to FIXUP_MOVE.) */
static final int FIXUP_MOVE_TO_END = 10;
/** FIXUP_TRY with the following FIXUP_CATCH marks an exception handler.
* The label is the start of the try clause;
* the current offset marks the exception handler. */
static final int FIXUP_TRY = 11;
/** Second half of a FIXUP_TRY/FIXUP_CATCH pair.
* The label is the ed of the try clause;
* the current offset is the exception type as a constant pool index. */
static final int FIXUP_CATCH = 12;
/** With following FIXUP_LINE_NUMBER associates an offset with a line number.
* The fixup_offset is the code location; the fixup_label is null. */
static final int FIXUP_LINE_PC = 13;
/** With preceding FIXUP_LINE_PC associates an offset with a line number.
* The fixup_offset is the line number; the fixup_label is null. */
static final int FIXUP_LINE_NUMBER = 14;
int[] fixup_offsets;
Label[] fixup_labels;
int fixup_count;
/** This causes a later processFixup to rearrange the code.
* The code at target comes here, instead of the following instructions.
* Fuctionally equivalent to: <code>goto target; here:</code>,
* but implemented by code re-arranging. Therefore there should be
* at some later point a <code>goto here; target:</code>.
*/
public final void fixupChain (Label here, Label target)
{
fixupAdd(CodeAttr.FIXUP_MOVE, 0, target);
here.define(this);
}
/** Add a fixup at this location.
* @param kind one of the FIXUP_xxx codes.
* @param label varies - typically the target of jump. */
public final void fixupAdd (int kind, Label label)
{
fixupAdd(kind, PC, label);
}
final void fixupAdd (int kind, int offset, Label label)
{
int count = fixup_count;
if (count == 0)
{
fixup_offsets = new int[30];
fixup_labels = new Label[30];
}
else if (fixup_count == fixup_offsets.length)
{
int new_length = 2 * count;
Label[] new_labels = new Label[new_length];
System.arraycopy (fixup_labels, 0, new_labels, 0, count);
fixup_labels = new_labels;
int[] new_offsets = new int[new_length];
System.arraycopy (fixup_offsets, 0, new_offsets, 0, count);
fixup_offsets = new_offsets;
}
fixup_offsets[count] = (offset << 4) | kind;
fixup_labels[count] = label;
fixup_count = count + 1;
}
private final int fixupOffset(int index)
{
return fixup_offsets[index] >> 4;
}
private final int fixupKind(int index)
{
return fixup_offsets[index] & 15;
}
/** If true we get a line number entry for each instruction.
* Normally false, but can be a convenient hack to allow instruction-level
* stepping/debugging and stacktraces. In this case {@code LINE==PC}. */
public static boolean instructionLineMode = false;
/** The stack of currently active conditionals. */
IfState if_stack;
/** The stack of currently active try statements. */
TryState try_stack;
public final Method getMethod() { return (Method) getContainer(); }
public final int getPC() { return PC; }
public final int getSP() { return SP; }
public final ConstantPool getConstants ()
{
return getMethod().classfile.constants;
}
/* True if we cannot fall through to bytes[PC] -
the previous instruction was an uncondition control transfer. */
private boolean unreachable_here;
/** True if control could reach here. */
public final boolean reachableHere () { return !unreachable_here; }
public final void setReachable(boolean val) { unreachable_here = !val; }
public final void setUnreachable() { unreachable_here = true; }
/** Get the maximum number of words on the operand stack in this method. */
public int getMaxStack() { return max_stack; }
/** Get the maximum number of local variable words in this method. */
public int getMaxLocals() { return max_locals; }
/** Set the maximum number of words on the operand stack in this method. */
public void setMaxStack(int n) { max_stack = n; }
/** Set the maximum number of local variable words in this method. */
public void setMaxLocals(int n) { max_locals = n; }
/** Get the code (instruction bytes) of this method.
* Does not make a copy. */
public byte[] getCode() { return code; }
/** Set the code (instruction bytes) of this method.
* @param code the code bytes (which are not copied).
* Implicitly calls setCodeLength(code.length). */
public void setCode(byte[] code) {
this.code = code; this.PC = code.length; }
/** Set the length the the code (instruction bytes) of this method.
* That is the number of current used bytes in getCode().
* (Any remaing bytes provide for future growth.) */
public void setCodeLength(int len) { PC = len;}
/** Set the current lengthof the code (instruction bytes) of this method. */
public int getCodeLength() { return PC; }
public CodeAttr (Method meth)
{
super ("Code");
addToFrontOf(meth);
meth.code = this;
}
public final void reserve (int bytes)
{
if (code == null)
code = new byte[100+bytes];
else if (PC + bytes > code.length)
{
byte[] new_code = new byte[2 * code.length + bytes];
System.arraycopy (code, 0, new_code, 0, PC);
code = new_code;
}
}
/** Get opcode that implements NOT (x OPCODE y). */
byte invert_opcode (byte opcode)
{
if ((opcode >= 153 && opcode <= 166)
|| (opcode >= 198 && opcode <= 199))
return (byte) (opcode ^ 1);
throw new Error("unknown opcode to invert_opcode");
}
/**
* Write an 8-bit byte to the current code-stream.
* @param i the byte to write
*/
public final void put1(int i)
{
code[PC++] = (byte) i;
unreachable_here = false;
}
/**
* Write a 16-bit short to the current code-stream
* @param i the value to write
*/
public final void put2(int i)
{
code[PC++] = (byte) (i >> 8);
code[PC++] = (byte) (i);
unreachable_here = false;
}
/**
* Write a 32-bit int to the current code-stream
* @param i the value to write
*/
public final void put4(int i)
{
code[PC++] = (byte) (i >> 24);
code[PC++] = (byte) (i >> 16);
code[PC++] = (byte) (i >> 8);
code[PC++] = (byte) (i);
unreachable_here = false;
}
public final void putIndex2 (CpoolEntry cnst)
{
put2(cnst.index);
}
public final void putLineNumber (String filename, int linenumber)
{
getMethod().classfile.setSourceFile(filename);
putLineNumber(linenumber);
}
public final void putLineNumber (int linenumber)
{
if (sourceDbgExt != null)
linenumber = sourceDbgExt.fixLine(linenumber);
fixupAdd(FIXUP_LINE_PC, null);
fixupAdd(FIXUP_LINE_NUMBER, linenumber, null);
}
public final void pushType(Type type)
{
if (type.size == 0)
throw new Error ("pushing void type onto stack");
if (stack_types == null)
stack_types = new Type[20];
else if (SP + 1 >= stack_types.length) {
Type[] new_array = new Type[2 * stack_types.length];
System.arraycopy (stack_types, 0, new_array, 0, SP);
stack_types = new_array;
}
if (type.size == 8)
stack_types[SP++] = Type.void_type;
stack_types[SP++] = type;
if (SP > max_stack)
max_stack = SP;
}
public final Type popType ()
{
if (SP <= 0)
throw new Error("popType called with empty stack "+getMethod());
Type type = stack_types[--SP];
if (type.size == 8)
if (! popType().isVoid())
throw new Error("missing void type on stack");
return type;
}
public final Type topType ()
{
return stack_types[SP - 1];
}
/** Compile code to pop values off the stack (and ignore them).
* @param nvalues the number of values (not words) to pop
*/
public void emitPop (int nvalues)
{
for ( ; nvalues > 0; --nvalues)
{
reserve(1);
Type type = popType();
if (type.size > 4)
put1(88); // pop2
else if (nvalues > 1)
{ // optimization: can we pop 2 4-byte words using a pop2
Type type2 = popType();
if (type2.size > 4)
{
put1(87); // pop
reserve(1);
}
put1(88); // pop2
--nvalues;
}
else
put1(87); // pop
}
}
/** Get a new Label for the current location.
* Unlike Label.define, Does not change reachableHere().
*/
public Label getLabel ()
{
boolean unreachable = unreachable_here;
Label label = new Label();
label.define(this);
unreachable_here = unreachable;
return label;
}
public void emitSwap ()
{
reserve(1);
Type type1 = popType();
Type type2 = popType();
if (type1.size > 4 || type2.size > 4)
{
// There is no swap instruction in the JVM for this case.
// Fall back to a more convoluted way.
pushType(type2);
pushType(type1);
emitDupX();
emitPop(1);
}
else
{
pushType(type1);
put1(95); // swap
pushType(type2);
}
}
/** Emit code to duplicate the top element of the stack. */
public void emitDup ()
{
reserve(1);
Type type = topType();
put1 (type.size <= 4 ? 89 : 92); // dup or dup2
pushType (type);
}
/** Emit code to duplicate the top element of the stack
and place the copy before the previous element. */
public void emitDupX ()
{
reserve(1);
Type type = popType();
Type skipedType = popType();
if (skipedType.size <= 4)
put1 (type.size <= 4 ? 90 : 93); // dup_x1 or dup2_x1
else
put1 (type.size <= 4 ? 91 : 94); // dup_x2 or dup2_x2
pushType (type);
pushType (skipedType);
pushType (type);
}
/** Compile code to duplicate with offset.
* @param size the size of the stack item to duplicate (1 or 2)
* @param offset where to insert the result (must be 0, 1, or 2)
* The new words get inserted at stack[SP-size-offset]
*/
public void emitDup (int size, int offset)
{
if (size == 0)
return;
reserve(1);
// copied1 and (optionally copied2) are the types of the duplicated words
Type copied1 = popType ();
Type copied2 = null;
if (size == 1)
{
if (copied1.size > 4)
throw new Error ("using dup for 2-word type");
}
else if (size != 2)
throw new Error ("invalid size to emitDup");
else if (copied1.size <= 4)
{
copied2 = popType();
if (copied2.size > 4)
throw new Error ("dup will cause invalid types on stack");
}
int kind;
// These are the types of the words (in any) that are "skipped":
Type skipped1 = null;
Type skipped2 = null;
if (offset == 0)
{
kind = size == 1 ? 89 : 92; // dup or dup2
}
else if (offset == 1)
{
kind = size == 1 ? 90 : 93; // dup_x1 or dup2_x1
skipped1 = popType ();
if (skipped1.size > 4)
throw new Error ("dup will cause invalid types on stack");
}
else if (offset == 2)
{
kind = size == 1 ? 91 : 94; // dup_x2 or dup2_x2
skipped1 = popType();
if (skipped1.size <= 4)
{
skipped2 = popType();
if (skipped2.size > 4)
throw new Error ("dup will cause invalid types on stack");
}
}
else
throw new Error ("emitDup: invalid offset");
put1(kind);
if (copied2 != null)
pushType(copied2);
pushType(copied1);
if (skipped2 != null)
pushType(skipped2);
if (skipped1 != null)
pushType(skipped1);
if (copied2 != null)
pushType(copied2);
pushType(copied1);
}
/**
* Compile code to duplicate the top 1 or 2 words.
* @param size number of words to duplicate
*/
public void emitDup (int size)
{
emitDup(size, 0);
}
public void emitDup (Type type)
{
emitDup(type.size > 4 ? 2 : 1, 0);
}
public void enterScope (Scope scope)
{
scope.setStartPC(this);
locals.enterScope(scope);
}
public Scope pushScope () {
Scope scope = new Scope ();
if (locals == null)
locals = new LocalVarsAttr(getMethod());
enterScope(scope);
if (locals.parameter_scope == null)
locals.parameter_scope = scope;
return scope;
}
public Scope getCurrentScope()
{
return locals.current_scope;
}
public Scope popScope () {
Scope scope = locals.current_scope;
locals.current_scope = scope.parent;
scope.freeLocals(this);
scope.end = getLabel();
return scope;
}
/** Get the index'th parameter. */
public Variable getArg (int index)
{
return locals.parameter_scope.getVariable(index);
}
/**
* Search by name for a Variable
* @param name name to search for
* @return the Variable, or null if not found (in any scope of this Method).
*/
public Variable lookup (String name)
{
Scope scope = locals.current_scope;
for (; scope != null; scope = scope.parent)
{
Variable var = scope.lookup (name);
if (var != null)
return var;
}
return null;
}
/** Add a new local variable (in the current scope).
* @param type type of the new Variable.
* @return the new Variable. */
public Variable addLocal (Type type)
{
return locals.current_scope.addVariable(this, type, null);
}
/** Add a new local variable (in the current scope).
* @param type type of the new Variable.
* @param name name of the new Variable.
* @return the new Variable. */
public Variable addLocal (Type type, String name)
{
return locals.current_scope.addVariable (this, type, name);
}
/** Call addLocal for parameters (as implied by method type). */
public void addParamLocals()
{
Method method = getMethod();
if ((method.access_flags & Access.STATIC) == 0)
addLocal(method.classfile).setParameter(true);
int arg_count = method.arg_types.length;
for (int i = 0; i < arg_count; i++)
addLocal(method.arg_types[i]).setParameter(true);
}
public final void emitPushConstant(int val, Type type)
{
switch (type.getSignature().charAt(0))
{
case 'B': case 'C': case 'I': case 'Z': case 'S':
emitPushInt(val); break;
case 'J':
emitPushLong((long)val); break;
case 'F':
emitPushFloat((float)val); break;
case 'D':
emitPushDouble((double)val); break;
default:
throw new Error("bad type to emitPushConstant");
}
}
/* Low-level method to pust a ConstantPool entry.
* Does not do the appropriatre <code>pushType</code>. */
public final void emitPushConstant (CpoolEntry cnst)
{
reserve(3);
int index = cnst.index;
if (cnst instanceof CpoolValue2)
{
put1 (20); // ldc2_w
put2 (index);
}
else if (index < 256)
{
put1(18); // ldc
put1(index);
}
else
{
put1(19); // ldc_w
put2(index);
}
}
public final void emitPushInt(int i)
{
reserve(3);
if (i >= -1 && i <= 5)
put1(i + 3); // iconst_m1 .. iconst_5
else if (i >= -128 && i < 128)
{
put1(16); // bipush
put1(i);
}
else if (i >= -32768 && i < 32768)
{
put1(17); // sipush
put2(i);
}
else
{
emitPushConstant(getConstants().addInt(i));
}
pushType(Type.int_type);
}
public void emitPushLong (long i)
{
if (i == 0 || i == 1)
{
reserve(1);
put1 (9 + (int) i); // lconst_0 .. lconst_1
}
else if ((long) (int) i == i)
{
emitPushInt ((int) i);
reserve(1);
popType();
put1 (133); // i2l
}
else
{
emitPushConstant(getConstants().addLong(i));
}
pushType(Type.long_type);
}
public void emitPushFloat (float x)
{
int xi = (int) x;
if ((float) xi == x && xi >= -128 && xi < 128)
{
if (xi >= 0 && xi <= 2)
{
reserve(1);
put1(11 + xi); // fconst_0 .. fconst_2
if (xi == 0 && Float.floatToIntBits(x) != 0) // x == -0.0
{
reserve(1);
put1(118); // fneg
}
}
else
{
// Saves space in the constant pool
// Probably faster, at least on modern CPUs.
emitPushInt (xi);
reserve(1);
popType();
put1 (134); // i2f
}
}
else
{
emitPushConstant(getConstants().addFloat(x));
}
pushType(Type.float_type);
}
public void emitPushDouble (double x)
{
int xi = (int) x;
if ((double) xi == x && xi >= -128 && xi < 128)
{
if (xi == 0 || xi == 1)
{
reserve(1);
put1(14+xi); // dconst_0 or dconst_1
if (xi == 0 && Double.doubleToLongBits(x) != 0L) // x == -0.0
{
reserve(1);
put1(119); // dneg
}
}
else
{
// Saves space in the constant pool
// Probably faster, at least on modern CPUs.
emitPushInt (xi);
reserve(1);
popType();
put1 (135); // i2d
}
}
else
{
emitPushConstant(getConstants().addDouble(x));
}
pushType(Type.double_type);
}
/** Calculate how many CONSTANT_String constants we need for a string.
* Each CONSTANT_String can be at most 0xFFFF bytes (as a UTF8 string).
* Returns a String, where each char, coerced to an int, is the length
* of a substring of the input that is at most 0xFFFF bytes.
*/
public static final String calculateSplit (String str)
{
int strLength = str.length();
StringBuffer sbuf = new StringBuffer(20);
// Where the current segments starts, as an index in 'str':
int segmentStart = 0;
int byteLength = 0; // Length in bytes of current segment so far.
for (int i = 0; i < strLength; i++)
{
char ch = str.charAt(i);
int bytes = ch >= 0x0800 ? 3 : ch >= 0x0080 || ch == 0 ? 2 : 1;
if (byteLength + bytes > 0xFFFF)
{
sbuf.append((char) (i - segmentStart));
segmentStart = i;
byteLength = 0;
}
byteLength += bytes;
}
sbuf.append((char) (strLength - segmentStart));
return sbuf.toString();
}
/** Emit code to push the value of a constant String.
* Uses CONSTANT_String and CONSTANT_Utf8 constant pool entries as needed.
* Can handle Strings whose UTF8 length is greates than 0xFFFF bytes
* (the limit of a CONSTANT_Utf8) by generating String concatenation.
*/
public final void emitPushString (String str)
{
if (str == null)
emitPushNull();
else
{
int length = str.length();
String segments = calculateSplit(str);
int numSegments = segments.length();
if (numSegments <= 1)
emitPushConstant(getConstants().addString(str));
else
{
if (numSegments == 2)
{
int firstSegment = (int) segments.charAt(0);
emitPushString(str.substring(0, firstSegment));
emitPushString(str.substring(firstSegment));
Method concatMethod
= Type.string_type.getDeclaredMethod("concat", 1);
emitInvokeVirtual(concatMethod);
}
else
{
ClassType sbufType = ClassType.make("java.lang.StringBuffer");
emitNew(sbufType);
emitDup(sbufType);
emitPushInt(length);
Type[] args1 = { Type.int_type };
emitInvokeSpecial(sbufType.getDeclaredMethod("<init>", args1));
Type[] args2 = { Type.string_type };
Method appendMethod
= sbufType.getDeclaredMethod("append", args2);
int segStart = 0;
for (int seg = 0; seg < numSegments; seg++)
{
emitDup(sbufType);
int segEnd = segStart + (int) segments.charAt(seg);
emitPushString(str.substring(segStart, segEnd));
emitInvokeVirtual(appendMethod);
segStart = segEnd;
}
emitInvokeVirtual(Type.toString_method);
}
if (str == str.intern())
emitInvokeVirtual(Type.string_type.getDeclaredMethod("intern", 0));
return;
}
pushType(Type.string_type);
}
}
/** Push a class constant pool entry.
* This is only supported by JDK 1.5 and later. */
public final void emitPushClass (String name)
{
emitPushConstant(getConstants().addClass(name));
pushType(Type.java_lang_Class_type);
}
public void emitPushNull ()
{
reserve(1);
put1(1); // aconst_null
pushType(Type.pointer_type);
}
public final void emitPushThis()
{
reserve(1);
put1(42); // aload_0
pushType(getMethod().getDeclaringClass());
}
/** Emit code to push a constant primitive array.
* @param value The array value that we want the emitted code to re-create.
* @param arrayType The ArrayType that matches value.
*/
public final void emitPushPrimArray(Object value, ArrayType arrayType)
{
Type elementType = arrayType.getComponentType();
int len = java.lang.reflect.Array.getLength(value);
emitPushInt(len);
emitNewArray(elementType);
char sig = elementType.getSignature().charAt(0);
for (int i = 0; i < len; i++)
{
long ival = 0; float fval = 0; double dval = 0;
switch (sig)
{
case 'J':
ival = ((long[]) value)[i];
if (ival == 0)
continue;
break;
case 'I':
ival = ((int[]) value)[i];
if (ival == 0)
continue;
break;
case 'S':
ival = ((short[]) value)[i];
if (ival == 0)
continue;
break;
case 'C':
ival = ((char[]) value)[i];
if (ival == 0)
continue;
break;
case 'B':
ival = ((byte[]) value)[i];
if (ival == 0)
continue;
break;
case 'Z':
ival = ((boolean[]) value)[i] ? 1 : 0;
if (ival == 0)
continue;
break;
case 'F':
fval = ((float[]) value)[i];
if (fval == 0.0)
continue;
break;
case 'D':
dval = ((double[]) value)[i];
if (dval == 0.0)
continue;
break;
}
emitDup(arrayType);
emitPushInt(i);
switch (sig)
{
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
emitPushInt((int) ival);
break;
case 'J':
emitPushLong(ival);
break;
case 'F':
emitPushFloat(fval);
break;
case 'D':
emitPushDouble(dval);
break;
}
emitArrayStore(elementType);
}
}
void emitNewArray (int type_code)
{
reserve(2);
put1(188); // newarray
put1(type_code);
}
public final void emitArrayLength ()
{
if (! (popType() instanceof ArrayType))
throw new Error( "non-array type in emitArrayLength" );
reserve(1);
put1(190); // arraylength
pushType(Type.int_type);
}
/* Returns an integer in the range 0 (for 'int') through 4 (for object
reference) to 7 (for 'short') which matches the pattern of how JVM
opcodes typically depend on the operand type. */
private int adjustTypedOp (char sig)
{
switch (sig)
{
case 'I': return 0; // int
case 'J': return 1; // long
case 'F': return 2; // float
case 'D': return 3; // double
default: return 4; // object
case 'B':
case 'Z': return 5; // byte or boolean
case 'C': return 6; // char
case 'S': return 7; // short
}
}
private int adjustTypedOp (Type type)
{
return adjustTypedOp(type.getSignature().charAt(0));
}
private void emitTypedOp (int op, Type type)
{
reserve(1);
put1(op + adjustTypedOp(type));
}
private void emitTypedOp (int op, char sig)
{
reserve(1);
put1(op + adjustTypedOp(sig));
}
/** Store into an element of an array.
* Must already have pushed the array reference, the index,
* and the new value (in that order).
* Stack: ..., array, index, value => ...
*/
public void emitArrayStore (Type element_type)
{
popType(); // Pop new value
popType(); // Pop index
popType(); // Pop array reference
emitTypedOp(79, element_type);
}
/** Load an element from an array.
* Must already have pushed the array and the index (in that order):
* Stack: ..., array, index => ..., value */
public void emitArrayLoad (Type element_type)
{
popType(); // Pop index
popType(); // Pop array reference
emitTypedOp(46, element_type);
pushType(element_type);
}
/**
* Invoke new on a class type.
* Does not call the constructor!
* @param type the desired new object type
*/
public void emitNew (ClassType type)
{
reserve(3);
put1(187); // new
putIndex2(getConstants().addClass(type));
pushType(type);
}
/** Compile code to allocate a new array.
* The size should have been already pushed on the stack.
* @param element_type type of the array elements
*/
public void emitNewArray (Type element_type, int dims)
{
if (popType ().promote () != Type.int_type)
throw new Error ("non-int dim. spec. in emitNewArray");
if (element_type instanceof PrimType)
{
int code;
switch (element_type.getSignature().charAt(0))
{
case 'B': code = 8; break;
case 'S': code = 9; break;
case 'I': code = 10; break;
case 'J': code = 11; break;
case 'F': code = 6; break;
case 'D': code = 7; break;
case 'Z': code = 4; break;
case 'C': code = 5; break;
default: throw new Error("bad PrimType in emitNewArray");
}
emitNewArray(code);
}
else if (element_type instanceof ObjectType)
{
reserve(3);
put1(189); // anewarray
putIndex2(getConstants().addClass((ObjectType) element_type));
}
else if (element_type instanceof ArrayType)
{
reserve(4);
put1(197); // multianewarray
putIndex2 (getConstants ().addClass (new ArrayType (element_type)));
if (dims < 1 || dims > 255)
throw new Error ("dims out of range in emitNewArray");
put1(dims);
while (-- dims > 0) // first dim already popped
if (popType ().promote () != Type.int_type)
throw new Error ("non-int dim. spec. in emitNewArray");
}
else
throw new Error ("unimplemented type in emitNewArray");
pushType (new ArrayType (element_type));
}
public void emitNewArray (Type element_type)
{
emitNewArray (element_type, 1);
}
// We may want to deprecate this, because it depends on popType.
private void emitBinop (int base_code)
{
Type type2 = popType().promote();
Type type1_raw = popType();
Type type1 = type1_raw.promote();
if (type1 != type2 || ! (type1 instanceof PrimType))
throw new Error ("non-matching or bad types in binary operation");
emitTypedOp(base_code, type1);
pushType(type1_raw);
}
private void emitBinop (int base_code, char sig)
{
popType();
popType();
emitTypedOp(base_code, sig);
pushType(Type.signatureToPrimitive(sig));
}
private void emitBinop (int base_code, Type type)
{
popType();
popType();
emitTypedOp(base_code, type);
pushType(type);
}
// public final void emitIntAdd () { put1(96); popType();}
// public final void emitLongAdd () { put1(97); popType();}
// public final void emitFloatAdd () { put1(98); popType();}
// public final void emitDoubleAdd () { put1(99); popType();}
public final void emitAdd(char sig) { emitBinop (96, sig); }
public final void emitAdd(PrimType type) { emitBinop (96, type); }
/** @deprecated */
public final void emitAdd () { emitBinop (96); }
public final void emitSub(char sig) { emitBinop (100, sig); }
public final void emitSub(PrimType type) { emitBinop (100, type); }
/** @deprecated */
public final void emitSub () { emitBinop (100); }
public final void emitMul () { emitBinop (104); }
public final void emitDiv () { emitBinop (108); }
public final void emitRem () { emitBinop (112); }
public final void emitAnd () { emitBinop (126); }
public final void emitIOr () { emitBinop (128); }
public final void emitXOr () { emitBinop (130); }
public final void emitShl () { emitShift (120); }
public final void emitShr () { emitShift (122); }
public final void emitUshr() { emitShift (124); }
private void emitShift (int base_code)
{
Type type2 = popType().promote();
Type type1_raw = popType();
Type type1 = type1_raw.promote();
if (type1 != Type.int_type && type1 != Type.long_type)
throw new Error ("the value shifted must be an int or a long");
if (type2 != Type.int_type)
throw new Error ("the amount of shift must be an int");
emitTypedOp(base_code, type1);
pushType(type1_raw);
}
public final void emitNot(Type type)
{
emitPushConstant(1, type);
emitAdd();
emitPushConstant(1, type);
emitAnd();
}
public void emitPrimop (int opcode, int arg_count, Type retType)
{
reserve(1);
while (-- arg_count >= 0)
popType();
put1(opcode);
pushType(retType);
}
void emitMaybeWide (int opcode, int index)
{
if (index >= 256)
{
put1(196); // wide
put1(opcode);
put2(index);
}
else
{
put1(opcode);
put1(index);
}
}
/**
* Compile code to push the contents of a local variable onto the statck.
* @param var The variable whose contents we want to push.
*/
public final void emitLoad (Variable var)
{
if (var.dead())
throw new Error("attempting to push dead variable");
int offset = var.offset;
if (offset < 0 || !var.isSimple())
throw new Error ("attempting to load from unassigned variable "+var
+" simple:"+var.isSimple()+", offset: "+offset);
Type type = var.getType().promote();
reserve(4);
int kind = adjustTypedOp(type);
if (offset <= 3)
put1(26 + 4 * kind + offset); // [ilfda]load_[0123]
else
emitMaybeWide(21 + kind, offset); // [ilfda]load
pushType(var.getType());
}
public void emitStore (Variable var)
{
if (var.dead ())
throw new Error("attempting to push dead variable");
int offset = var.offset;
if (offset < 0 || !var.isSimple ())
throw new Error ("attempting to store in unassigned "+var
+" simple:"+var.isSimple()+", offset: "+offset);
Type type = var.getType().promote ();
reserve(4);
popType();
int kind = adjustTypedOp(type);
if (offset <= 3)
put1(59 + 4 * kind + offset); // [ilfda]store_[0123]
else
emitMaybeWide(54 + kind, offset); // [ilfda]store
}
public void emitInc (Variable var, short inc)
{
if (var.dead ())
throw new Error ("attempting to increment dead variable");
int offset = var.offset;
if (offset < 0 || !var.isSimple ())
throw new Error ("attempting to increment unassigned variable"+var.getName()
+" simple:"+var.isSimple()+", offset: "+offset);
Type type = var.getType().promote ();
reserve(6);
if (type != Type.int_type)
throw new Error("attempting to increment non-int variable");
boolean wide = offset > 255 || inc > 255 || inc < -256;
if (wide)
{
put1(196); // wide
put1(132); // iinc
put2(offset);
put2(inc);
}
else
{
put1(132); // iinc
put1(offset);
put1(inc);
}
}
private final void emitFieldop (Field field, int opcode)
{
reserve(3);
put1(opcode);
putIndex2(getConstants().addFieldRef(field));
}
/** Compile code to get a static field value.
* Stack: ... => ..., value */
public final void emitGetStatic(Field field)
{
pushType(field.type);
emitFieldop (field, 178); // getstatic
}
/** Compile code to get a non-static field value.
* Stack: ..., objectref => ..., value */
public final void emitGetField(Field field)
{
popType();
pushType(field.type);
emitFieldop(field, 180); // getfield
}
/** Compile code to put a static field value.
* Stack: ..., value => ... */
public final void emitPutStatic (Field field)
{
popType();
emitFieldop(field, 179); // putstatic
}
/** Compile code to put a non-static field value.
* Stack: ..., objectref, value => ... */
public final void emitPutField (Field field)
{
popType();
popType();
emitFieldop(field, 181); // putfield
}
/** Comptes the number of stack words taken by a list of types. */
private int words(Type[] types)
{
int res = 0;
for (int i=types.length; --i >= 0; )
if (types[i].size > 4)
res+=2;
else
res++;
return res;
}
public void emitInvokeMethod (Method method, int opcode)
{
reserve(opcode == 185 ? 5 : 3);
int arg_count = method.arg_types.length;
boolean is_invokestatic = opcode == 184;
if (is_invokestatic != ((method.access_flags & Access.STATIC) != 0))
throw new Error
("emitInvokeXxx static flag mis-match method.flags="+method.access_flags);
if (!is_invokestatic)
arg_count++;
put1(opcode); // invokevirtual, invokespecial, or invokestatic
putIndex2(getConstants().addMethodRef(method));
if (opcode == 185) // invokeinterface
{
put1(words(method.arg_types)+1); // 1 word for 'this'
put1(0);
}
while (--arg_count >= 0)
popType();
if (method.return_type.size != 0)
pushType(method.return_type);
}
public void emitInvoke (Method method)
{
int opcode;
if ((method.access_flags & Access.STATIC) != 0)
opcode = 184; // invokestatic
else if (method.classfile.isInterface())
opcode = 185; // invokeinterface
else if ("<init>".equals(method.getName()))
opcode = 183; // invokespecial
else
opcode = 182; // invokevirtual
emitInvokeMethod(method, opcode);
}
/** Compile a virtual method call.
* The stack contains the 'this' object, followed by the arguments in order.
* @param method the method to invoke virtually
*/
public void emitInvokeVirtual (Method method)
{
emitInvokeMethod(method, 182); // invokevirtual
}
public void emitInvokeSpecial (Method method)
{
emitInvokeMethod(method, 183); // invokespecial
}
/** Compile a static method call.
* The stack contains the the arguments in order.
* @param method the static method to invoke
*/
public void emitInvokeStatic (Method method)
{
emitInvokeMethod(method, 184); // invokestatic
}
public void emitInvokeInterface (Method method)
{
emitInvokeMethod(method, 185); // invokeinterface
}
final void emitTransfer (Label label, int opcode)
{
fixupAdd(FIXUP_TRANSFER, label);
put1(opcode);
PC += 2;
}
/** Compile an unconditional branch (goto).
* @param label target of the branch (must be in this method).
*/
public final void emitGoto (Label label)
{
fixupAdd(FIXUP_GOTO, label);
reserve(3);
put1(167);
PC += 2;
setUnreachable();
}
public final void emitJsr (Label label)
{
fixupAdd(FIXUP_JSR, label);
reserve(3);
put1(168);
PC += 2;
}
public final void emitGotoIfCompare1 (Label label, int opcode)
{
popType();
reserve(3);
emitTransfer (label, opcode);
}
public final void emitGotoIfIntEqZero(Label label)
{ emitGotoIfCompare1(label, 153); }
public final void emitGotoIfIntNeZero(Label label)
{ emitGotoIfCompare1(label, 154); }
public final void emitGotoIfIntLtZero(Label label)
{ emitGotoIfCompare1(label, 155); }
public final void emitGotoIfIntGeZero(Label label)
{ emitGotoIfCompare1(label, 156); }
public final void emitGotoIfIntGtZero(Label label)
{ emitGotoIfCompare1(label, 157); }
public final void emitGotoIfIntLeZero(Label label)
{ emitGotoIfCompare1(label, 158); }
public final void emitGotoIfCompare2 (Label label, int logop)
{
if( logop < 153 || logop > 158 )
throw new Error ("emitGotoIfCompare2: logop must be one of ifeq...ifle");
Type type2 = popType().promote();
Type type1 = popType().promote();
reserve(4);
char sig1 = type1.getSignature().charAt(0);
char sig2 = type2.getSignature().charAt(0);
boolean cmpg = (logop == 155 || logop == 158); // iflt,ifle
if (sig1 == 'I' && sig2 == 'I')
logop += 6; // iflt -> if_icmplt etc.
else if (sig1 == 'J' && sig2 == 'J')
put1(148); // lcmp
else if (sig1 == 'F' && sig2 == 'F')
put1(cmpg ? 149 : 150); // fcmpl/fcmpg
else if (sig1 == 'D' && sig2 == 'D')
put1(cmpg ? 151 : 152); // dcmpl/dcmpg
else if ((sig1 == 'L' || sig1 == '[')
&& (sig2 == 'L' || sig2 == '[')
&& logop <= 154)
logop += 12; // ifeq->if_acmpeq, ifne->if_acmpne
else
throw new Error ("invalid types to emitGotoIfCompare2");
emitTransfer (label, logop);
}
// binary comparisons
/** @deprecated */
public final void emitGotoIfEq (Label label, boolean invert)
{
emitGotoIfCompare2(label, invert ? 154 : 153);
}
/** Compile a conditional transfer if 2 top stack elements are equal. */
public final void emitGotoIfEq (Label label)
{
emitGotoIfCompare2(label, 153);
}
/** Compile conditional transfer if 2 top stack elements are not equal. */
public final void emitGotoIfNE (Label label)
{
emitGotoIfCompare2(label, 154);
}
public final void emitGotoIfLt(Label label)
{ emitGotoIfCompare2(label, 155); }
public final void emitGotoIfGe(Label label)
{ emitGotoIfCompare2(label, 156); }
public final void emitGotoIfGt(Label label)
{ emitGotoIfCompare2(label, 157); }
public final void emitGotoIfLe(Label label)
{ emitGotoIfCompare2(label, 158); }
/** Compile start of a conditional:
* <tt>if (!(<var>x</var> opcode 0)) ...</tt>.
* The value of <var>x</var> must already have been pushed. */
public final void emitIfCompare1 (int opcode)
{
IfState new_if = new IfState(this);
if (popType().promote() != Type.int_type)
throw new Error ("non-int type to emitIfCompare1");
reserve(3);
emitTransfer (new_if.end_label, opcode);
new_if.start_stack_size = SP;
}
/** Compile start of conditional: <tt>if (x != 0) ...</tt>.
* Also use this if you have pushed a boolean value: if (b) ... */
public final void emitIfIntNotZero()
{
emitIfCompare1(153); // ifeq
}
/** Compile start of conditional: <tt>if (x == 0) ...</tt>.
* Also use this if you have pushed a boolean value: if (!b) ... */
public final void emitIfIntEqZero()
{
emitIfCompare1(154); // ifne
}
/** Compile start of conditional: <tt>if (x <= 0)</tt>. */
public final void emitIfIntLEqZero()
{
emitIfCompare1(157); // ifgt
}
/** Compile start of a conditional: <tt>if (!(x opcode null)) ...</tt>.
* The value of <tt>x</tt> must already have been pushed and must be of
* reference type. */
public final void emitIfRefCompare1 (int opcode)
{
IfState new_if = new IfState(this);
if (! (popType() instanceof ObjectType))
throw new Error ("non-ref type to emitIfRefCompare1");
reserve(3);
emitTransfer (new_if.end_label, opcode);
new_if.start_stack_size = SP;
}
/** Compile start of conditional: if (x != null) */
public final void emitIfNotNull()
{
emitIfRefCompare1(198); // ifnull
}
/** Compile start of conditional: if (x == null) */
public final void emitIfNull()
{
emitIfRefCompare1(199); // ifnonnull
}
/** Compile start of a conditional: if (!(x OPCODE y)) ...
* The value of x and y must already have been pushed. */
public final void emitIfIntCompare(int opcode)
{
IfState new_if = new IfState(this);
popType();
popType();
reserve(3);
emitTransfer(new_if.end_label, opcode);
new_if.start_stack_size = SP;
}
/* Compile start of a conditional: if (x < y) ... */
public final void emitIfIntLt()
{
emitIfIntCompare(162); // if_icmpge
}
/** Compile start of a conditional: if (x != y) ...
* The values of x and y must already have been pushed. */
public final void emitIfNEq ()
{
IfState new_if = new IfState (this);
emitGotoIfEq(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Compile start of a conditional: if (x == y) ...
* The values of x and y must already have been pushed. */
public final void emitIfEq ()
{
IfState new_if = new IfState (this);
emitGotoIfNE(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Compile start of a conditional: if (x < y) ...
* The values of x and y must already have been pushed. */
public final void emitIfLt ()
{
IfState new_if = new IfState (this);
emitGotoIfGe(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Compile start of a conditional: if (x >= y) ...
* The values of x and y must already have been pushed. */
public final void emitIfGe ()
{
IfState new_if = new IfState (this);
emitGotoIfLt(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Compile start of a conditional: if (x > y) ...
* The values of x and y must already have been pushed. */
public final void emitIfGt ()
{
IfState new_if = new IfState (this);
emitGotoIfLe(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Compile start of a conditional: if (x <= y) ...
* The values of x and y must already have been pushed. */
public final void emitIfLe ()
{
IfState new_if = new IfState (this);
emitGotoIfGt(new_if.end_label);
new_if.start_stack_size = SP;
}
/** Emit a 'ret' instruction.
* @param var the variable containing the return address */
public void emitRet (Variable var)
{
int offset = var.offset;
if (offset < 256)
{
reserve(2);
put1(169); // ret
put1(offset);
}
else
{
reserve(4);
put1(196); // wide
put1(169); // ret
put2(offset);
}
}
public final void emitThen()
{
if_stack.start_stack_size = SP;
}
public final void emitIfThen ()
{
new IfState(this, null);
}
/** Compile start of else clause. */
public final void emitElse ()
{
Label else_label = if_stack.end_label;
Label end_label = new Label (this);
if_stack.end_label = end_label;
if (reachableHere ())
{
int growth = SP-if_stack.start_stack_size;
if_stack.stack_growth = growth;
if (growth > 0)
{
if_stack.then_stacked_types = new Type[growth];
System.arraycopy (stack_types, if_stack.start_stack_size,
if_stack.then_stacked_types, 0, growth);
}
else
if_stack.then_stacked_types = new Type[0]; // ???
emitGoto (end_label);
}
while (SP > if_stack.start_stack_size)
popType();
SP = if_stack.start_stack_size;
if (else_label != null)
else_label.define (this);
if_stack.doing_else = true;
}
/** Compile end of conditional. */
public final void emitFi ()
{
boolean make_unreachable = false;
if (! if_stack.doing_else)
{ // There was no 'else' clause.
if (reachableHere ()
&& SP != if_stack.start_stack_size)
throw new Error("at PC "+PC+" then clause grows stack with no else clause");
}
else if (if_stack.then_stacked_types != null)
{
int then_clause_stack_size
= if_stack.start_stack_size + if_stack.stack_growth;
if (! reachableHere ())
{
if (if_stack.stack_growth > 0)
System.arraycopy (if_stack.then_stacked_types, 0,
stack_types, if_stack.start_stack_size,
if_stack.stack_growth);
SP = then_clause_stack_size;
}
else if (SP != then_clause_stack_size)
throw new Error("at PC "+PC+": SP at end of 'then' was " +
then_clause_stack_size
+ " while SP at end of 'else' was " + SP);
}
else if (unreachable_here)
make_unreachable = true;
if (if_stack.end_label != null)
if_stack.end_label.define (this);
if (make_unreachable)
setUnreachable();
// Pop the if_stack.
if_stack = if_stack.previous;
}
public final void emitConvert (Type from, Type to)
{
String to_sig = to.getSignature();
String from_sig = from.getSignature();
int op = -1;
if (to_sig.length() == 1 || from_sig.length() == 1)
{
char to_sig0 = to_sig.charAt(0);
char from_sig0 = from_sig.charAt(0);
if (from_sig0 == to_sig0)
return;
if (from.size < 4)
from_sig0 = 'I';
if (to.size < 4)
{
emitConvert(from, Type.int_type);
from_sig0 = 'I';
}
if (from_sig0 == to_sig0)
return;
switch (from_sig0)
{
case 'I':
switch (to_sig0)
{
case 'B': op = 145; break; // i2b
case 'C': op = 146; break; // i2c
case 'S': op = 147; break; // i2s
case 'J': op = 133; break; // i2l
case 'F': op = 134; break; // i2f
case 'D': op = 135; break; // i2d
}
break;
case 'J':
switch (to_sig0)
{
case 'I': op = 136; break; // l2i
case 'F': op = 137; break; // l2f
case 'D': op = 138; break; // l2d
}
break;
case 'F':
switch (to_sig0)
{
case 'I': op = 139; break; // f2i
case 'J': op = 140; break; // f2l
case 'D': op = 141; break; // f2d
}
break;
case 'D':
switch (to_sig0)
{
case 'I': op = 142; break; // d2i
case 'J': op = 143; break; // d2l
case 'F': op = 144; break; // d2f
}
break;
}
}
if (op < 0)
throw new Error ("unsupported CodeAttr.emitConvert");
reserve(1);
popType();
put1(op);
pushType(to);
}
private void emitCheckcast (Type type, int opcode)
{
reserve(3);
popType();
put1(opcode);
if (type instanceof ArrayType)
{
ArrayType atype = (ArrayType) type;
CpoolUtf8 name = getConstants().addUtf8(atype.signature);
putIndex2(getConstants().addClass(name));
}
else if (type instanceof ClassType)
{
putIndex2(getConstants().addClass((ClassType) type));
}
else
throw new Error ("unimplemented type " + type
+ " in emitCheckcast/emitInstanceof");
}
public static boolean castNeeded (Type top, Type required)
{
for (;;)
{
if (required instanceof ClassType
&& top instanceof ClassType
&& ((ClassType) top).isSubclass((ClassType) required))
return false;
else if (required instanceof ArrayType
&& top instanceof ArrayType)
{
required = ((ArrayType) required).getComponentType();
top = ((ArrayType) top).getComponentType();
continue;
}
return true;
}
}
public void emitCheckcast (Type type)
{
if (castNeeded(topType(), type))
{
emitCheckcast(type, 192);
pushType(type);
}
}
public void emitInstanceof (Type type)
{
emitCheckcast(type, 193);
pushType(Type.boolean_type);
}
public final void emitThrow ()
{
popType();
reserve(1);
put1 (191); // athrow
setUnreachable();
}
public final void emitMonitorEnter ()
{
popType();
reserve(1);
put1 (194); // monitorenter
}
public final void emitMonitorExit ()
{
popType();
reserve(1);
put1 (195); // monitorexit
}
/** Call pending finalizer functions.
* @param limit Only call finalizers more recent than this.
*/
public void doPendingFinalizers (TryState limit)
{
TryState stack = try_stack;
/* If a value is returned, it must be saved to a local variable,
to prevent a verification error because of inconsistent stack sizes.
*/
boolean saveResult = ! getMethod().getReturnType().isVoid();
Variable result = null;
while (stack != limit)
{
if (stack.finally_subr != null // there is a finally block
&& stack.finally_ret_addr == null) // 'return' is not inside it
{
if (saveResult && result == null)
{
result = addLocal(topType());
emitStore(result);
}
emitJsr(stack.finally_subr);
}
stack = stack.previous;
}
if (result != null)
emitLoad(result);
// We'd like to do freeLocal on the result Variable, but then we risk
// it being re-used in a finalizer, which would trash its value. We
// don't have any convenient way to to do that (the pending Scope
// mechanism is over-kill), we for now we just leak the Variable.
}
/**
* Compile a method return.
* If inside a 'catch' clause, first call 'finally' clauses.
* The return value (unless the return type is void) must be on the stack,
* and have the correct type.
*/
public final void emitReturn ()
{
doPendingFinalizers(null);
if (getMethod().getReturnType().size == 0)
{
reserve(1);
put1(177); // return
}
else
emitTypedOp (172, popType().promote());
setUnreachable();
}
/** Add an exception handler. */
public void addHandler (int start_pc, int end_pc,
int handler_pc, int catch_type)
{
int index = 4 * exception_table_length;
if (exception_table == null)
{
exception_table = new short[20];
}
else if (exception_table.length <= index)
{
short[] new_table = new short[2 * exception_table.length];
System.arraycopy(exception_table, 0, new_table, 0, index);
exception_table = new_table;
}
exception_table[index++] = (short) start_pc;
exception_table[index++] = (short) end_pc;
exception_table[index++] = (short) handler_pc;
exception_table[index++] = (short) catch_type;
exception_table_length++;
}
/** Add an exception handler. */
public void addHandler (Label start_try, Label end_try,
ClassType catch_type)
{
ConstantPool constants = getConstants();
int catch_type_index;
if (catch_type == null)
catch_type_index = 0;
else
catch_type_index = constants.addClass(catch_type).index;
fixupAdd(FIXUP_TRY, start_try);
fixupAdd(FIXUP_CATCH, catch_type_index, end_try);
}
/** Beginning of code that has a cleanup handler.
* This is similar to a try-finally, but the cleanup is only
* done in the case of an exception. Alternatively, the try clause
* has to manually do the cleanup with code duplication.
* Equivalent to: <code>try <var>body</var> catch (Throwable ex) { <var>cleanup</var>; throw ex; }</code>
* Call <code>emitWithCleanupStart</code> before the <code><var>body</var></code>.
*/
public void emitWithCleanupStart ()
{
int savedSP = SP;
SP = 0;
emitTryStart(false, null);
SP = savedSP;
}
/** Called after a <code><var>body</var></code> that has a <code><var>cleanup</var></code> clause.
* Followed by the <code><var>cleanup</var></code> code.
*/
public void emitWithCleanupCatch (Variable catchVar)
{
emitTryEnd();
Type[] savedTypes;
if (SP > 0)
{
savedTypes = new Type[SP];
System.arraycopy(stack_types, 0, savedTypes, 0, SP);
SP = 0;
}
else
savedTypes = null;
try_stack.savedTypes = savedTypes;
try_stack.saved_result = catchVar;
int save_SP = SP;
emitCatchStart(catchVar);
}
/** Called after generating a <code><var>cleanup</var></code> handler. */
public void emitWithCleanupDone ()
{
Variable catchVar = try_stack.saved_result;
try_stack.saved_result = null;
if (catchVar != null)
emitLoad(catchVar);
emitThrow();
emitCatchEnd();
Type[] savedTypes = try_stack.savedTypes;
emitTryCatchEnd();
if (savedTypes != null)
{
SP = savedTypes.length;
if (SP >= stack_types.length)
stack_types = savedTypes;
else
System.arraycopy(savedTypes, 0, stack_types, 0, SP);
}
else
SP = 0;
}
public void emitTryStart(boolean has_finally, Type result_type)
{
if (result_type != null && result_type.isVoid())
result_type = null;
Variable[] savedStack = null;
if (result_type != null || SP > 0)
pushScope();
if (SP > 0)
{
savedStack = new Variable[SP];
int i = 0;
while (SP > 0)
{
Variable var = addLocal(topType());
emitStore(var);
savedStack[i++] = var;
}
}
TryState try_state = new TryState(this);
try_state.savedStack = savedStack;
if (result_type != null)
try_state.saved_result = addLocal(result_type);
if (has_finally)
try_state.finally_subr = new Label();
}
public void emitTryEnd()
{
if (try_stack.end_label == null)
{
if (try_stack.saved_result != null && reachableHere())
emitStore(try_stack.saved_result);
try_stack.end_label = new Label();
if (reachableHere())
{
if (try_stack.finally_subr != null)
emitJsr(try_stack.finally_subr);
emitGoto(try_stack.end_label);
}
try_stack.end_try = getLabel();
}
}
public void emitCatchStart(Variable var)
{
emitTryEnd();
SP = 0;
if (try_stack.try_type != null)
emitCatchEnd();
ClassType type = var == null ? null : (ClassType) var.getType();
try_stack.try_type = type;
addHandler(try_stack.start_try, try_stack.end_try, type);
if (var != null)
{
pushType(type);
emitStore(var);
}
else
pushType(Type.throwable_type);
}
public void emitCatchEnd()
{
if (reachableHere())
{
if (try_stack.saved_result != null)
emitStore(try_stack.saved_result);
if (try_stack.finally_subr != null)
emitJsr(try_stack.finally_subr);
emitGoto(try_stack.end_label);
}
try_stack.try_type = null;
}
public void emitFinallyStart()
{
emitTryEnd();
if (try_stack.try_type != null)
emitCatchEnd();
SP = 0;
try_stack.end_try = getLabel();
pushScope();
Type except_type = Type.pointer_type;
Variable except = addLocal(except_type);
emitCatchStart(null);
emitStore(except);
emitJsr(try_stack.finally_subr);
emitLoad(except);
emitThrow();
try_stack.finally_subr.define(this);
Type ret_addr_type = Type.pointer_type;
try_stack.finally_ret_addr = addLocal(ret_addr_type);
pushType(ret_addr_type);
emitStore(try_stack.finally_ret_addr);
}
public void emitFinallyEnd()
{
emitRet(try_stack.finally_ret_addr);
setUnreachable();
popScope();
try_stack.finally_subr = null;
}
public void emitTryCatchEnd()
{
if (try_stack.finally_subr != null)
emitFinallyEnd();
try_stack.end_label.define(this);
Variable[] vars = try_stack.savedStack;
if (vars != null)
{
for (int i = vars.length; --i >= 0; )
{
Variable v = vars[i];
if (v != null) {
emitLoad(v);
}
}
}
if (try_stack.saved_result != null)
emitLoad(try_stack.saved_result);
if (try_stack.saved_result != null || vars != null)
popScope();
try_stack = try_stack.previous;
}
public final TryState getCurrentTry ()
{
return try_stack;
}
public final boolean isInTry()
{
// This also return true if we're in a catch clause, but that is
// good enough for now.
return try_stack != null;
}
/** Compile a tail-call to position 0 of the current procedure.
* @param pop_args if true, copy argument registers (except this) from stack.
* @param scope Scope whose start we jump back to. */
public void emitTailCall (boolean pop_args, Scope scope)
{
if (pop_args)
{
Method meth = getMethod();
int arg_slots = ((meth.access_flags & Access.STATIC) != 0) ? 0 : 1;
for (int i = meth.arg_types.length; --i >= 0; )
arg_slots += meth.arg_types[i].size > 4 ? 2 : 1;
for (int i = meth.arg_types.length; --i >= 0; )
{
arg_slots -= meth.arg_types[i].size > 4 ? 2 : 1;
emitStore(locals.used [arg_slots]);
}
}
emitGoto(scope.start);
}
public void processFixups ()
{
if (fixup_count == 0)
return;
// For each label, set it to its maximum limit, assuming all
// fixups causes the code the be expanded. We need a prepass
// for this, since FIXUP_MOVEs can cause code to be reordered.
// Also, convert each FIXUP_MOVE_TO_END to FIXUP_MOVE.
int delta = 0;
int instruction_tail = fixup_count;
fixupAdd(CodeAttr.FIXUP_MOVE, 0, null);
loop1:
for (int i = 0; ; )
{
int offset = fixup_offsets[i];
int kind = offset & 15;
offset >>= 4;
Label label = fixup_labels[i];
switch (kind)
{
case FIXUP_TRY:
case FIXUP_LINE_PC:
i++;
case FIXUP_NONE:
case FIXUP_CASE:
case FIXUP_DELETE3:
break;
case FIXUP_DEFINE:
label.position += delta;
break;
case FIXUP_SWITCH:
delta += 3; // May need to add up to 3 padding bytes.
break;
case FIXUP_GOTO:
// The first test fails in this case: GOTO L2; L1: L2: FIXME
if (label.first_fixup == i + 1
&& fixupOffset(i+1) == offset + 3)
{
// Optimize: GOTO L; L:
fixup_offsets[i] = (offset << 4) | FIXUP_DELETE3;
fixup_labels[i] = null;
delta -= 3;
break;
}
// ... else fall through ...
case FIXUP_JSR:
if (PC >= 0x8000)
delta += 2; // May need to convert goto->goto_w, jsr->jsr_w.
break;
case FIXUP_TRANSFER:
if (PC >= 0x8000)
delta += 5; // May need to add a goto_w.
break;
case FIXUP_MOVE_TO_END:
fixup_labels[instruction_tail] = fixup_labels[i+1];
instruction_tail = offset;
// ... fall through ...
case FIXUP_MOVE:
int cur_pc = ((i+1) >= fixup_count ? PC
: fixupOffset(fixup_labels[i+1].first_fixup));
fixup_offsets[i] = (cur_pc << 4) | FIXUP_MOVE;
if (label == null)
break loop1;
else
{
i = label.first_fixup;
int next_pc = fixupOffset(i);
delta = (cur_pc + delta) - next_pc;
continue;
}
default:
throw new Error("unexpected fixup");
}
i++;
}
// Next a loop to fix the position of each label, and calculate
// the exact number of code bytes.
// Number of bytes to be inserted or (if negative) removed, so far.
int new_size = PC;
// Current delta between final PC and offset in generate code array.
delta = 0;
loop2:
for (int i = 0; i < fixup_count; )
{
int offset = fixup_offsets[i];
int kind = offset & 15;
Label label = fixup_labels[i];
if (label != null && label.position < 0)
throw new Error ("undefined label "+label);
while (label != null
&& kind >= FIXUP_GOTO && kind <= FIXUP_TRANSFER2
&& label.first_fixup + 1 < fixup_count
&& (fixup_offsets[label.first_fixup + 1]
== ((fixup_offsets[label.first_fixup] & 15) | FIXUP_GOTO)))
{
// Optimize JUMP L; ... L: GOTO X
// (where the JUMP is any GOTO or other transfer)
// by changing the JUMP L to JUMP X.
label = fixup_labels[label.first_fixup + 1];
fixup_labels[i] = label;
}
offset = offset >> 4;
switch (kind)
{
case FIXUP_TRY:
case FIXUP_LINE_PC:
i++;
case FIXUP_NONE:
case FIXUP_CASE:
break;
case FIXUP_DELETE3:
delta -= 3;
new_size -= 3;
break;
case FIXUP_DEFINE:
label.position = offset + delta;
break;
case FIXUP_SWITCH:
int padding = 3 - (offset+delta) & 3;
delta += padding;
new_size += padding;
break;
case FIXUP_GOTO:
case FIXUP_JSR:
case FIXUP_TRANSFER:
int rel = label.position - (offset+delta);
if ((short) rel == rel)
{
fixup_offsets[i] = (offset << 4) | FIXUP_TRANSFER2;
}
else
{
delta += kind == FIXUP_TRANSFER ? 5 : 2; // need goto_w
new_size += kind == FIXUP_TRANSFER ? 5 : 2; // need goto_w
}
break;
case FIXUP_MOVE:
if (label == null)
break loop2;
else
{
i = label.first_fixup;
int next_pc = fixupOffset(i);
delta = (offset + delta) - next_pc;
continue;
}
default:
throw new Error("unexpected fixup");
}
i++;
}
byte[] new_code = new byte[new_size];
int new_pc = 0;
int next_fixup_index = 0;
int next_fixup_offset = fixupOffset(0);
loop3:
for (int old_pc = 0; ; )
{
if (old_pc < next_fixup_offset)
{
new_code[new_pc++] = code[old_pc++];
}
else
{
int kind = fixup_offsets[next_fixup_index] & 15;
Label label = fixup_labels[next_fixup_index];
switch (kind)
{
case FIXUP_NONE:
case FIXUP_DEFINE:
break;
case FIXUP_DELETE3:
old_pc += 3;
break;
case FIXUP_TRANSFER2:
delta = label.position - new_pc;
new_code[new_pc++] = code[old_pc];
new_code[new_pc++] = (byte) (delta >> 8);
new_code[new_pc++] = (byte) (delta & 0xFF);
old_pc += 3;
break;
case FIXUP_GOTO:
case FIXUP_JSR:
case FIXUP_TRANSFER:
delta = label.position - new_pc;
byte opcode = code[old_pc];
if (kind == FIXUP_TRANSFER)
{
// convert: IF_xxx L to IF_NOT_xxx Lt; GOTO L; Lt:
opcode = invert_opcode(opcode);
new_code[new_pc++] = opcode;
new_code[new_pc++] = 0;
new_code[new_pc++] = 8; // 8 byte offset to Lt.
opcode = (byte) 200; // goto_w
}
else
{
// Change goto to goto_w; jsr to jsr_w:
opcode = (byte) (opcode + (200-167));
}
new_code[new_pc++] = opcode;
new_code[new_pc++] = (byte) (delta >> 24);
new_code[new_pc++] = (byte) (delta >> 16);
new_code[new_pc++] = (byte) (delta >> 8);
new_code[new_pc++] = (byte) (delta & 0xFF);
old_pc += 3;
break;
case FIXUP_SWITCH:
int padding = 3 - new_pc & 3;
int switch_start = new_pc;
new_code[new_pc++] = code[old_pc++];
while (--padding >= 0)
new_code[new_pc++] = 0;
while (next_fixup_index < fixup_count
&& fixupKind(next_fixup_index + 1) == FIXUP_CASE)
{
next_fixup_index++;
int offset = fixupOffset(next_fixup_index);
while (old_pc < offset)
new_code[new_pc++] = code[old_pc++];
delta = (fixup_labels[next_fixup_index].position
- switch_start);
new_code[new_pc++] = (byte) (delta >> 24);
new_code[new_pc++] = (byte) (delta >> 16);
new_code[new_pc++] = (byte) (delta >> 8);
new_code[new_pc++] = (byte) (delta & 0xFF);
old_pc += 4;
}
break;
case FIXUP_TRY:
addHandler(fixup_labels[next_fixup_index].position,
fixup_labels[next_fixup_index+1].position,
new_pc,
fixupOffset(next_fixup_index+1));
next_fixup_index++;
break;
case FIXUP_LINE_PC:
if (lines == null)
lines = new LineNumbersAttr(this);
next_fixup_index++;
lines.put(fixupOffset(next_fixup_index), new_pc);
break;
case FIXUP_MOVE:
if (label == null)
break loop3;
else
{
next_fixup_index = label.first_fixup;
old_pc = fixupOffset(next_fixup_index);
next_fixup_offset = old_pc;
if (label.position != new_pc)
throw new Error("bad pc");
continue;
}
default:
throw new Error("unexpected fixup");
}
next_fixup_index++;
next_fixup_offset = fixupOffset(next_fixup_index);
}
}
if (new_size != new_pc)
throw new Error("PC confusion new_pc:"+new_pc+" new_size:"+new_size);
PC = new_size;
code = new_code;
fixup_count = 0;
fixup_labels = null;
fixup_offsets = null;
}
public void assignConstants (ClassType cl)
{
if (locals != null && locals.container == null && ! locals.isEmpty())
locals.addToFrontOf(this);
processFixups();
if (instructionLineMode)
{
// A kludge to low-level debugging:
// Define a "line number" for each instrction.
if (lines == null)
lines = new LineNumbersAttr(this);
lines.linenumber_count = 0;
int codeLen = getCodeLength();
for (int i = 0; i < codeLen; i++)
lines.put(i, i);
}
super.assignConstants(cl);
Attribute.assignConstants(this, cl);
}
public final int getLength()
{
return 12 + getCodeLength() + 8 * exception_table_length
+ Attribute.getLengthAll(this);
}
public void write (DataOutputStream dstr) throws java.io.IOException
{
dstr.writeShort (max_stack);
dstr.writeShort (max_locals);
dstr.writeInt (PC);
dstr.write (code, 0, PC);
dstr.writeShort (exception_table_length);
int count = exception_table_length;
for (int i = 0; --count >= 0; i += 4)
{
dstr.writeShort(exception_table[i]);
dstr.writeShort(exception_table[i+1]);
dstr.writeShort(exception_table[i+2]);
dstr.writeShort(exception_table[i+3]);
}
Attribute.writeAll(this, dstr);
}
public void print (ClassTypeWriter dst)
{
dst.print("Attribute \"");
dst.print(getName());
dst.print("\", length:");
dst.print(getLength());
dst.print(", max_stack:");
dst.print(max_stack);
dst.print(", max_locals:");
dst.print(max_locals);
dst.print(", code_length:");
int length = getCodeLength();
dst.println(length);
disAssemble(dst, 0, length);
if (exception_table_length > 0)
{
dst.print("Exceptions (count: ");
dst.print(exception_table_length);
dst.println("):");
int count = exception_table_length;
for (int i = 0; --count >= 0; i += 4)
{
dst.print(" start: ");
dst.print(exception_table[i] & 0xffff);
dst.print(", end: ");
dst.print(exception_table[i+1] & 0xffff);
dst.print(", handler: ");
dst.print(exception_table[i+2] & 0xffff);
dst.print(", type: ");
int catch_type_index = exception_table[i+3] & 0xffff;
if (catch_type_index == 0)
dst.print("0 /* finally */");
else
{
dst.printOptionalIndex(catch_type_index);
dst.printConstantTersely(catch_type_index, ConstantPool.CLASS);
}
dst.println();
}
}
dst.printAttributes(this);
}
/* DEBUGGING:
public void disAssembleWithFixups (ClassTypeWriter dst)
{
if (fixup_count == 0)
{
disAssemble(dst, 0, PC);
return;
}
int prev_pc = 0;
for (int i = 0; i < fixup_count; )
{
int offset = fixup_offsets[i];
int kind = offset & 15;
Label label = fixup_labels[i];
offset = offset >> 4;
int pc = offset;
if (kind == FIXUP_MOVE || kind == FIXUP_MOVE_TO_END)
pc = (i+1 >= fixup_count) ? PC : fixup_offsets[i+1] >> 4;
disAssemble(dst, prev_pc, offset);
prev_pc = pc;
dst.print("fixup#"); dst.print(i);
dst.print(" @"); dst.print(offset);
switch (kind)
{
case FIXUP_NONE:
dst.println(" NONE");
break;
case FIXUP_DEFINE:
dst.print(" DEFINE ");
dst.println(label);
break;
case FIXUP_SWITCH:
dst.println(" SWITCH");
break;
case FIXUP_CASE:
dst.println(" CASE");
break;
case FIXUP_GOTO:
dst.print(" GOTO ");
dst.println(label);
break;
case FIXUP_TRANSFER:
dst.print(" TRANSFER ");
dst.println(label);
break;
case FIXUP_TRANSFER2:
dst.print(" TRANSFER2 ");
dst.println(label);
break;
case FIXUP_DELETE3:
dst.println(" DELETE3");
break;
case FIXUP_MOVE:
dst.print(" MOVE ");
dst.println(label);
break;
case FIXUP_MOVE_TO_END:
dst.print(" MOVE_TO_END ");
dst.println(label);
break;
case FIXUP_TRY:
dst.print(" TRY start: ");
dst.println(label);
i++;
dst.print(" - end: ");
dst.print(fixup_labels[i]);
dst.print(" type: ");
dst.println(fixup_offsets[i] >> 4);
break;
case FIXUP_LINE_PC:
dst.print(" LINE ");
i++;
dst.println(fixup_offsets[i] >> 4);
break;
default:
dst.println(" kind:"+fixupKind(i)+" offset:"+fixupOffset(i)+" "+fixup_labels[i]);
}
i++;
}
}
*/
public void disAssemble (ClassTypeWriter dst, int start, int limit)
{
boolean wide = false;
for (int i = start; i < limit; )
{
int oldpc = i++;
int op = code[oldpc] & 0xff;
String str = Integer.toString(oldpc);
int printConstant = 0;
int j = str.length();
while (++j <= 3) dst.print(' ');
dst.print(str);
dst.print(": ");
// We do a rough binary partition of the opcodes.
if (op < 120)
{
if (op < 87)
{
if (op < 3) print("nop;aconst_null;iconst_m1;", op, dst);
else if (op < 9) { dst.print("iconst_"); dst.print(op-3); }
else if (op < 16) // op >= 9 [lconst_0] && op <= 15 [dconst_1]
{
char typ;
if (op < 11) { typ = 'l'; op -= 9; }
else if (op < 14) { typ = 'f'; op -= 11; }
else { typ = 'd'; op -= 14; }
dst.print(typ); dst.print("const_"); dst.print(op);
}
else if (op < 21)
{
if (op < 18) // op >= 16 [bipush] && op <= 17 [sipush]
{
print("bipush ;sipush ;", op-16, dst);
int constant;
if (op == 16) constant = code[i++];
else { constant = (short) readUnsignedShort(i); i+=2;}
dst.print(constant);
}
else // op >= 18 [ldc] && op <= 20 [ldc2_w]
{
printConstant = op == 18 ? 1 : 2;
print("ldc;ldc_w;ldc2_w;", op-18, dst);
}
}
else // op >= 21 && op < 87
{
String load_or_store;
if (op < 54) { load_or_store = "load"; }
else { load_or_store = "store"; op -=(54-21); }
int index; // -2 if array op; -1 if index follows
if (op < 26) { index = -1; op -= 21; }
else if (op < 46) { op -= 26; index = op % 4; op >>= 2; }
else { index = -2; op -= 46; }
dst.print("ilfdabcs".charAt(op));
if (index == -2) dst.write('a');
dst.print(load_or_store);
if (index >= 0) { dst.write('_'); dst.print(index); }
else if (index == -1)
{
if (wide) { index = readUnsignedShort(i); i += 2; }
else { index = code[i] & 0xff; i++; }
wide = false;
dst.print(' ');
dst.print(index);
}
}
}
else // op >= 87 && op < 120
{
if (op < 96)
print("pop;pop2;dup;dup_x1;dup_x2;dup2;dup2_x1;dup2_x2;swap;"
, op-87, dst);
else // op >= 96 [iadd] && op <= 119 [dneg]
{
dst.print("ilfda".charAt((op-96) % 4));
print("add;sub;mul;div;rem;neg;", (op-96)>>2, dst);
}
}
}
else // op >= 120
{
if (op < 170)
{
if (op < 132) // op >= 120 [ishl] && op <= 131 [lxor]
{
dst.print((op & 1) == 0 ? 'i' : 'l');
print("shl;shr;ushr;and;or;xor;", (op-120)>>1, dst);
}
else if (op == 132) // iinc
{
int var_index;
int constant;
dst.print("iinc");
if (! wide)
{
var_index = 0xff & code[i++];
constant = code[i++];
}
else
{
var_index = readUnsignedShort(i);
i += 2;
constant = (short) readUnsignedShort(i);
i += 2;
wide = false;
}
dst.print(' '); dst.print(var_index);
dst.print(' '); dst.print(constant);
}
else if (op < 148) // op >= 133 [i2l] && op <= 147 [i2s]
{
dst.print("ilfdi".charAt((op-133) / 3));
dst.print('2');
dst.print("lfdifdildilfbcs".charAt(op-133));
}
else if (op < 153) // op >= 148 [lcmp] && op <= 152 [dcmpg]
print("lcmp;fcmpl;fcmpg;dcmpl;dcmpg;", op-148, dst);
else if (op < 169)
{
if (op < 159)
{
dst.print("if");
print("eq;ne;lt;ge;gt;le;", op-153, dst);
}
else if (op < 167)
{
if (op < 165) { dst.print("if_icmp"); }
else { dst.print("if_acmp"); op -= 165-159; }
print("eq;ne;lt;ge;gt;le;", op-159, dst);
}
else
print("goto;jsr;", op-167, dst);
int delta = (short) readUnsignedShort(i);
i += 2;
dst.print(' '); dst.print(oldpc+delta);
}
else
{
int index;
dst.print("ret ");
if (wide) { index = readUnsignedShort(i); index += 2; }
else { index = code[i] & 0xff; i++; }
wide = false;
dst.print(index);
}
}
else
{
if (op < 172) // [tableswitch] or [lookupswitch]
{
if (fixup_count == 0)
i = (i + 3) & ~3; // skip 0-3 byte padding.
int code_offset = readInt(i); i += 4;
if (op == 170)
{
dst.print("tableswitch");
int low = readInt(i); i += 4;
int high = readInt(i); i += 4;
dst.print(" low: "); dst.print(low);
dst.print(" high: "); dst.print(high);
dst.print(" default: "); dst.print(oldpc+code_offset);
for (; low <= high; low++)
{
code_offset = readInt(i); i += 4;
dst.println();
dst.print(" "); dst.print(low);
dst.print(": "); dst.print(oldpc + code_offset);
}
}
else
{
dst.print("lookupswitch");
int npairs = readInt(i); i += 4;
dst.print(" npairs: "); dst.print(npairs);
dst.print(" default: "); dst.print(oldpc+code_offset);
while (--npairs >= 0)
{
int match = readInt(i); i += 4;
code_offset = readInt(i); i += 4;
dst.println();
dst.print(" "); dst.print(match);
dst.print(": "); dst.print(oldpc + code_offset);
}
}
}
else if (op < 178) // op >= 172 [ireturn] && op <= 177 [return]
{
if (op < 177) dst.print("ilfda".charAt(op-172));
dst.print("return");
}
else if (op < 182) // op >= 178 [getstatic] && op <= 181 [putfield]
{
print("getstatic;putstatic;getfield;putfield;", op-178, dst);
printConstant = 2;
}
else if (op < 185) // op >= 182 && op <= 185 [invoke*]
{
dst.print("invoke");
print("virtual;special;static;", op-182, dst);
printConstant = 2;
}
else if (op == 185) // invokeinterface
{
dst.print("invokeinterface (");
int index = readUnsignedShort(i);
i += 2;
int args = 0xff & code[i];
i += 2;
dst.print(args + " args)");
dst.printConstantOperand(index);
}
else if (op < 196)
{
print("186;new;newarray;anewarray;arraylength;athrow;checkcast;instanceof;monitorenter;monitorexit;", op-186, dst);
if (op == 187 || op == 189 || op == 192 || op == 193)
printConstant = 2;
else if (op == 188) // newarray
{
int type = code[i++];
dst.print(' ');
if (type >= 4 && type <= 11)
print("boolean;char;float;double;byte;short;int;long;",
type-4, dst);
else
dst.print(type);
}
}
else if (op == 196) // wide
{
dst.print("wide");
wide = true;
}
else if (op == 197)
{
dst.print("multianewarray");
int index = readUnsignedShort(i);
i += 2;
dst.printConstantOperand(index);
int dims = 0xff & code[i++];
dst.print(' ');
dst.print(dims);
}
else if (op < 200)
{
print("ifnull;ifnonnull;", op-198, dst);
int delta = (short) readUnsignedShort(i);
i += 2;
dst.print(' '); dst.print(oldpc+delta);
}
else if (op < 202)
{
print("goto_w;jsr_w;", op-200, dst);
int delta = readInt(i);
i += 4;
dst.print(' '); dst.print(oldpc+delta);
}
else
dst.print(op);
}
}
if (printConstant > 0)
{
int index;
if (printConstant == 1) index = 0xff & code[i++];
else { index = readUnsignedShort(i); i += 2; }
dst.printConstantOperand(index);
}
dst.println();
}
}
private int readUnsignedShort(int offset)
{
return ((0xff & code[offset]) << 8) | (0xff & code[offset+1]);
}
private int readInt(int offset)
{
return (readUnsignedShort(offset) << 16) | readUnsignedShort(offset+2);
}
/*
public saveStack (ClassType into)
{
Field[] flds = new Field[SP];
while (SP > 0)
{
Field fld = ?;
emitStore(fld);
flds[SP...]
}
}
*/
/* Print the i'th ';'-delimited substring of str on dst. */
private void print (String str, int i, PrintWriter dst)
{
int last = 0;
int pos = -1;
for (; i >= 0; i--)
{
last = ++pos;
pos = str.indexOf(';', last);
}
dst.write(str, last, pos-last);
}
/** Return an object encapsulating the type state of the JVM stack. */
public Type[] saveStackTypeState(boolean clear)
{
if (SP == 0)
return null;
Type[] typeState = new Type[SP];
System.arraycopy(stack_types, 0, typeState, 0, SP);
if (clear)
SP = 0;
return typeState;
}
/** Restore a type state as saved by saveStackTypeState. */
public void restoreStackTypeState (Type[] save)
{
if (save == null)
SP = 0;
else
{
SP = save.length;
System.arraycopy(save, 0, stack_types, 0, SP);
}
}
public int beginFragment (Label start, Label after)
{
int i = fixup_count;
fixupAdd(FIXUP_MOVE_TO_END, after);
start.define(this);
return i;
}
/** End a fragment.
* @param cookie the return value from the previous beginFragment.
*/
public void endFragment (int cookie)
{
fixup_offsets[cookie] = (fixup_count << 4) | FIXUP_MOVE_TO_END;
Label after = fixup_labels[cookie];
fixupAdd(FIXUP_MOVE, 0, null);
after.define(this);
}
}
|