/*
* Copyright 2004-2010 Brian S O'Neill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cojen.classfile;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.cojen.classfile.constant.ConstantClassInfo;
/**
* The InstructionList class is used by the CodeBuilder to perform lower-level
* bookkeeping operations and flow analysis.
*
* @author Brian S O'Neill
* @see CodeBuilder
*/
class InstructionList implements CodeBuffer {
private static final boolean DEBUG = false;
private final boolean mSaveLocalVariableInfo;
Instruction mFirst;
Instruction mLast;
boolean mResolved = false;
private List<ExceptionHandler<LabelInstruction>> mExceptionHandlers =
new ArrayList<ExceptionHandler<LabelInstruction>>(4);
private List<LocalVariable> mLocalVariables = new ArrayList<LocalVariable>();
private int mNextFixedVariableNumber;
private int mMaxStack;
private int mMaxLocals;
private byte[] mByteCodes;
private int mBufferLength;
protected InstructionList(boolean saveLocalVariableInfo) {
mSaveLocalVariableInfo = saveLocalVariableInfo;
}
/**
* Returns an immutable collection of all the instructions in this
* InstructionList.
*/
public Collection<Instruction> getInstructions() {
return new AbstractCollection<Instruction>() {
public Iterator<Instruction> iterator() {
return new Iterator<Instruction>() {
private Instruction mNext = mFirst;
public boolean hasNext() {
return mNext != null;
}
public Instruction next() {
if (mNext == null) {
throw new NoSuchElementException();
}
Instruction current = mNext;
mNext = mNext.mNext;
return current;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public int size() {
int count = 0;
for (Instruction i = mFirst; i != null; i = i.mNext) {
count++;
}
return count;
}
};
}
public int getMaxStackDepth() {
resolve();
return mMaxStack;
}
public int getMaxLocals() {
resolve();
return mMaxLocals;
}
public byte[] getByteCodes() {
resolve();
return mByteCodes;
}
public ExceptionHandler[] getExceptionHandlers() {
resolve();
ExceptionHandler[] handlers = new ExceptionHandler[mExceptionHandlers.size()];
return mExceptionHandlers.toArray(handlers);
}
public void addExceptionHandler(ExceptionHandler<LabelInstruction> handler) {
mExceptionHandlers.add(handler);
}
public LocalVariable createLocalVariable(String name, TypeDesc type) {
LocalVariable var = new LocalVariableImpl(mLocalVariables.size(), name, type, -1);
mLocalVariables.add(var);
return var;
}
/**
* All parameters must be defined before adding instructions.
*/
public LocalVariable createLocalParameter(String name, TypeDesc type) {
LocalVariable var = new LocalVariableImpl
(mLocalVariables.size(), name, type, mNextFixedVariableNumber);
mLocalVariables.add(var);
mNextFixedVariableNumber += type.isDoubleWord() ? 2 : 1;
return var;
}
private void resolve() {
if (mResolved) {
return;
}
if (!DEBUG) {
resolve0();
} else {
try {
resolve0();
} finally {
System.out.println("-- Instructions --");
for (Instruction i : getInstructions()) {
System.out.println(i);
}
}
}
}
private void resolve0() {
mMaxStack = 0;
mMaxLocals = 0;
// Sweep through the instructions, preparing for flow analysis.
int instrCount = 0;
for (Instruction instr = mFirst; instr != null; instr = instr.mNext) {
// Set address to instruction index.
instr.reset(instrCount++);
}
// Make sure exception handlers are registered with all guarded
// instructions.
for (ExceptionHandler<LabelInstruction> handler : mExceptionHandlers) {
LabelInstruction start = handler.getStartLocation();
start.markBranchTarget();
Instruction instr = start;
Instruction end = handler.getEndLocation();
for ( ; instr != null && instr != end; instr = instr.mNext) {
instr.addExceptionHandler(handler);
}
}
// Perform variable liveness flow analysis for each local variable, in
// order to determine which register it should be assigned. Takes
// advantage of the fact that instruction addresses are not yet
// resolved to true addresses, but are instead indexes. This means the
// liveness analysis operates on smaller BitLists, which makes some
// operations (i.e. intersection) a bit faster.
{
int size = mLocalVariables.size();
BitList[] liveIn = new BitList[size];
BitList[] liveOut = new BitList[size];
for (int v=0; v<size; v++) {
liveIn[v] = new BitList(instrCount);
liveOut[v] = new BitList(instrCount);
}
livenessAnalysis(liveIn, liveOut);
// Register number -> list of variables that use that register.
List<List<LocalVariable>> registerUsers = new ArrayList<List<LocalVariable>>();
// First fill up list with variables that have a fixed number.
for (int v=0; v<size; v++) {
LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(v);
if (var.isFixedNumber()) {
addRegisterUser(registerUsers, var);
// Ensure that max locals is large enough to hold parameters.
int num = var.getNumber();
if (var.isDoubleWord()) {
num++;
}
if (num >= mMaxLocals) {
mMaxLocals = num + 1;
}
}
}
// Merge bit lists together.
BitList[] live = liveIn;
for (int v=0; v<size; v++) {
live[v].or(liveOut[v]);
if (live[v].isAllClear()) {
// Variable isn't needed.
live[v] = null;
}
}
if (mSaveLocalVariableInfo) {
// Create indexable list of instructions.
List<Instruction> instrList = new ArrayList<Instruction>(instrCount);
instrList.addAll(getInstructions());
for (int v=0; v<size; v++) {
BitList list = live[v];
if (list == null) {
continue;
}
LocationRange firstRange = null;
Set<LocationRange> rangeSet = null;
int end = -1;
do {
int start = list.nextSetBit(end + 1);
if (start < 0) {
break;
}
end = list.nextClearBit(start + 1);
Location startLoc = instrList.get(start);
Location endLoc = instrList.get(end < instrCount ? end : instrCount - 1);
LocationRange range = new LocationRangeImpl(startLoc, endLoc);
if (firstRange == null) {
firstRange = range;
} else {
if (rangeSet == null) {
rangeSet = new HashSet<LocationRange>(5);
rangeSet.add(firstRange);
}
rangeSet.add(range);
}
} while (end < instrCount);
LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(v);
if (firstRange == null) {
var.setLocationRangeSet(null);
} else if (rangeSet == null || rangeSet.size() == 1) {
var.setLocationRangeSet(Collections.singleton(firstRange));
} else {
var.setLocationRangeSet(Collections.unmodifiableSet(rangeSet));
}
}
}
for (int v=0; v<size; v++) {
if (live[v] == null) {
continue;
}
LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(v);
if (var.isFixedNumber()) {
continue;
}
int r = 0;
while (true) {
r = findAvailableRegister(registerUsers, r, live, v);
if (var.isDoubleWord()) {
if (findAvailableRegister(registerUsers, ++r, live, v) == r) {
// Found consecutive registers, required for double word.
r--;
break;
}
} else {
break;
}
}
var.setNumber(r);
addRegisterUser(registerUsers, var);
}
mMaxLocals = Math.max(mMaxLocals, registerUsers.size());
} // end liveness analysis
// Perform stack flow analysis to determine the max stack size.
{
// Start the flow analysis at the first instruction.
Map<LabelInstruction, Integer> subAdjustMap =
new HashMap<LabelInstruction, Integer>(11);
stackResolve(0, mFirst, subAdjustMap);
// Continue flow analysis into exception handler entry points.
for (ExceptionHandler<LabelInstruction> handler : mExceptionHandlers) {
Instruction enter = handler.getCatchLocation();
// Initial stack depth is one because caught exception is on the stack.
stackResolve(1, enter, subAdjustMap);
}
}
// Okay, build up the byte code and set real instruction locations.
// Multiple passes may be required because instructions may adjust
// their size as locations are set. Changing size affects the
// locations of other instructions, so that is why additional passes
// are required.
boolean passAgain;
do {
passAgain = false;
mByteCodes = new byte[instrCount * 2]; // estimate
mBufferLength = 0;
for (Instruction instr = mFirst; instr != null; instr = instr.mNext) {
if (!instr.isResolved()) {
passAgain = true;
}
if (instr instanceof Label) {
if (instr.mLocation != mBufferLength) {
if (instr.mLocation >= 0) {
// If the location of this label is not where it
// should be, (most likely because an instruction
// needed to expand in size) then do another pass.
passAgain = true;
}
instr.mLocation = mBufferLength;
}
} else {
instr.mLocation = mBufferLength;
byte[] bytes = instr.getBytes();
if (bytes != null) {
if (passAgain) {
// If there is going to be another pass, don't
// bother collecting bytes into the array. Just
// expand the the length variable.
mBufferLength += bytes.length;
} else {
addBytes(bytes);
}
}
}
}
} while (passAgain); // do {} while ();
if (mBufferLength != mByteCodes.length) {
byte[] newBytes = new byte[mBufferLength];
System.arraycopy(mByteCodes, 0, newBytes, 0, mBufferLength);
mByteCodes = newBytes;
}
// Set resolved at end because during resolution, this field gets
// set false again while changes are being made to the list
// of instructions.
mResolved = true;
}
private void addBytes(byte[] code) {
growBuffer(code.length);
System.arraycopy(code, 0, mByteCodes, mBufferLength, code.length);
mBufferLength += code.length;
}
private void growBuffer(int amount) {
if ((mBufferLength + amount) > mByteCodes.length) {
int newCapacity = mByteCodes.length * 2;
if ((mBufferLength + amount) > newCapacity) {
newCapacity = mBufferLength + amount;
}
byte[] newBuffer = new byte[newCapacity];
System.arraycopy(mByteCodes, 0, newBuffer, 0, mBufferLength);
mByteCodes = newBuffer;
}
}
private void livenessAnalysis(BitList[] liveIn, BitList[] liveOut) {
// Track stores to variables to see if the result is discarded.
List<StoreLocalInstruction>[] localStores = new List[liveIn.length];
int passCount = -1;
boolean passAgain;
do {
passCount++;
passAgain = false;
for (Instruction instr = mLast; instr != null; instr = instr.mPrev) {
int n = instr.getLocation();
int useIndex = -1;
int defIndex = -1;
if (instr instanceof LocalOperandInstruction) {
LocalOperandInstruction loi = (LocalOperandInstruction)instr;
LocalVariableImpl var = loi.getLocalVariable();
int varIndex = var.getIndex();
if (loi.isLoad()) {
useIndex = varIndex;
}
if (loi.isStore()) {
defIndex = varIndex;
if (passCount == 0 && loi instanceof StoreLocalInstruction) {
List<StoreLocalInstruction> stores = localStores[varIndex];
if (stores == null) {
stores = new ArrayList<StoreLocalInstruction>();
localStores[varIndex] = stores;
}
stores.add((StoreLocalInstruction)loi);
}
}
}
for (int v=liveIn.length; --v>=0; ) {
boolean setLiveIn, setLiveOut;
if (useIndex == v || (v != defIndex && liveOut[v].get(n))) {
passAgain |= liveIn[v].set(n);
setLiveIn = true;
} else {
setLiveIn = false;
}
setLiveOut = false;
if (instr.isFlowThrough() && instr.mNext != null) {
if (liveIn[v].get(instr.mNext.getLocation())) {
setLiveOut = true;
passAgain |= liveOut[v].set(n);
}
}
LabelInstruction[] targets = instr.getBranchTargets();
if (targets != null) {
for (int i=0; i<targets.length; i++) {
if (liveIn[v].get(targets[i].getLocation())) {
setLiveOut = true;
passAgain |= liveOut[v].set(n);
}
}
}
Collection<ExceptionHandler<LabelInstruction>> handlers =
instr.getExceptionHandlers();
if (handlers != null) {
for (ExceptionHandler<LabelInstruction> handler : handlers) {
Instruction catchInstr = handler.getCatchLocation();
if (liveIn[v].get(catchInstr.getLocation())) {
setLiveOut = true;
passAgain |= liveOut[v].set(n);
}
}
}
if (!setLiveIn && setLiveOut && v != defIndex) {
// Set liveIn entry now that liveOut has been
// updated. This greatly reduces the number of full
// passes required.
passAgain |= liveIn[v].set(n);
}
}
}
} while (passAgain); // do {} while ();
// See which local store instructions should discard their results.
for (int v=localStores.length; --v>=0; ) {
List<StoreLocalInstruction> stores = localStores[v];
if (stores != null) {
for (int i=stores.size(); --i>=0; ) {
StoreLocalInstruction instr = stores.get(i);
if (!liveOut[v].get(instr.getLocation())) {
instr.discardResult();
}
}
}
}
}
private void addRegisterUser(List<List<LocalVariable>> registerUsers, LocalVariable var) {
int num = var.getNumber();
if (num < 0) {
throw new IllegalStateException("Local variable number not resolved: " + var);
}
getRegisterUsers(registerUsers, num).add(var);
if (var.isDoubleWord()) {
getRegisterUsers(registerUsers, num + 1).add(var);
}
}
private List<LocalVariable> getRegisterUsers(List<List<LocalVariable>> registerUsers,int num) {
while (registerUsers.size() <= num) {
registerUsers.add(new ArrayList<LocalVariable>());
}
return registerUsers.get(num);
}
/**
* @param registerUsers
* @param r index into registerUsers
* @return index into registerUsers which is available, which may be equal
* to r or equal to the size of registerUsers
*/
private int findAvailableRegister(List<List<LocalVariable>> registerUsers,
int r, BitList[] live, int v) {
registerScan:
for (; r<registerUsers.size(); r++) {
List users = getRegisterUsers(registerUsers, r);
for (int i=0; i<users.size(); i++) {
int v2 = ((LocalVariableImpl)users.get(i)).getIndex();
if (live[v].intersects(live[v2])) {
continue registerScan;
}
}
break;
}
return r;
}
/**
* @param stackDepth initial operand stack depth
* @param instr flow analysis start instruction
* @param subAdjustMap cache of stack adjustments for subroutine blocks;
* key is first instruction of subroutine (jsr target)
* @return updated stack depth, which may increment or decrement
*/
private int stackResolve(int stackDepth,
Instruction instr,
Map<LabelInstruction, Integer> subAdjustMap) {
while (instr != null) {
// Set the stack depth, marking this instruction as being visited.
// If already visited, break out of this flow.
if (instr.mStackDepth < 0) {
instr.mStackDepth = stackDepth;
} else {
/* Let verifier detect this illegal state.
if (instr.mStackDepth != stackDepth) {
throw new IllegalStateException
("Stack depth different at previously visited " +
"instruction: " + instr.mStackDepth +
" != " + stackDepth);
}
*/
break;
}
// Determine the next instruction to flow down to.
Instruction next = instr.isFlowThrough() ? instr.mNext : null;
stackDepth += instr.getStackAdjustment();
if (stackDepth > mMaxStack) {
mMaxStack = stackDepth;
} else if (stackDepth < 0) {
// Negative stack depth is illegal, but let verifier detect this.
stackDepth = 0;
}
LabelInstruction[] targets = instr.getBranchTargets();
if (targets != null) {
for (int i=0; i<targets.length; i++) {
LabelInstruction target = targets[i];
target.markBranchTarget();
if (i == 0 && next == null) {
// Simply flow to the first target if instruction
// doesn't flow to its next instruction.
next = target;
continue;
}
if (!instr.isSubroutineCall()) {
stackResolve(stackDepth, target, subAdjustMap);
} else {
Integer subAdjust = subAdjustMap.get(target);
if (subAdjust == null) {
int newDepth = stackResolve(stackDepth, target, subAdjustMap);
subAdjust = new Integer(newDepth - stackDepth);
subAdjustMap.put(target, subAdjust);
}
stackDepth += subAdjust.intValue();
}
}
}
instr = next;
}
return stackDepth;
}
private static class LocalVariableImpl implements LocalVariable {
private final int mIndex;
private String mName;
private TypeDesc mType;
private int mNumber;
private boolean mFixed;
private Set<LocationRange> mLocationRangeSet;
public LocalVariableImpl(int index, String name, TypeDesc type, int number) {
mIndex = index;
mName = name;
mType = type;
mNumber = number;
if (number >= 0) {
mFixed = true;
}
}
int getIndex() {
return mIndex;
}
/**
* May return null if this LocalVariable is unnamed.
*/
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public TypeDesc getType() {
return mType;
}
public boolean isDoubleWord() {
return mType.isDoubleWord();
}
public int getNumber() {
return mNumber;
}
public Set<LocationRange> getLocationRangeSet() {
return mLocationRangeSet;
}
void setLocationRangeSet(Set<LocationRange> set) {
mLocationRangeSet = set;
}
public void setNumber(int number) {
mNumber = number;
}
public void setFixedNumber(int number) {
mNumber = number;
mFixed = true;
}
public boolean isFixedNumber() {
return mFixed;
}
public String toString() {
return "variable {type=" + getType() + ", name=" + getName() + '}';
}
}
/////////////////////////////////////////////////////////////////////////
//
// Begin inner class definitions for instructions of the InstructionList.
//
/////////////////////////////////////////////////////////////////////////
/**
* An Instruction is an element in an InstructionList, and represents a
* Java byte code instruction.
*/
public abstract class Instruction implements Location {
private int mStackAdjust;
Instruction mPrev;
Instruction mNext;
// Indicates what the stack depth is when this instruction is reached.
// Is -1 if not reached. Flow analysis sets this value.
int mStackDepth = -1;
// Indicates the address of this instruction is, or -1 if not known.
int mLocation = -1;
private Set<ExceptionHandler<LabelInstruction>> mExceptionHandlers;
/**
* Newly created instructions are automatically added to the
* InstructionList.
*/
public Instruction(int stackAdjust) {
mStackAdjust = stackAdjust;
add();
}
/**
* This constructor allows sub-classes to disable auto-adding to the
* InstructionList.
*/
protected Instruction(int stackAdjust, boolean addInstruction) {
mStackAdjust = stackAdjust;
if (addInstruction) {
add();
}
}
/**
* Add this instruction to the end of the InstructionList. If the
* Instruction is already in the list, then it is moved to the end.
*/
protected void add() {
InstructionList.this.mResolved = false;
if (mPrev != null) {
mPrev.mNext = mNext;
}
if (mNext != null) {
mNext.mPrev = mPrev;
}
mNext = null;
if (InstructionList.this.mFirst == null) {
mPrev = null;
InstructionList.this.mFirst = this;
} else {
mPrev = InstructionList.this.mLast;
InstructionList.this.mLast.mNext = this;
}
InstructionList.this.mLast = this;
}
/**
* Insert an Instruction immediately following this one.
*/
public void insert(Instruction instr) {
InstructionList.this.mResolved = false;
instr.mPrev = this;
instr.mNext = mNext;
mNext = instr;
if (this == InstructionList.this.mLast) {
InstructionList.this.mLast = instr;
}
}
/**
* Removes this Instruction from its parent InstructionList.
*/
public void remove() {
InstructionList.this.mResolved = false;
if (mPrev != null) {
mPrev.mNext = mNext;
}
if (mNext != null) {
mNext.mPrev = mPrev;
}
if (this == InstructionList.this.mFirst) {
InstructionList.this.mFirst = mNext;
}
if (this == InstructionList.this.mLast) {
InstructionList.this.mLast = mPrev;
}
mPrev = null;
mNext = null;
}
/**
* Replace this Instruction with another one.
*/
public void replace(Instruction replacement) {
if (replacement == null) {
remove();
return;
}
InstructionList.this.mResolved = false;
replacement.mPrev = mPrev;
replacement.mNext = mNext;
if (mPrev != null) {
mPrev.mNext = replacement;
}
if (mNext != null) {
mNext.mPrev = replacement;
}
if (this == InstructionList.this.mFirst) {
InstructionList.this.mFirst = replacement;
}
if (this == InstructionList.this.mLast) {
InstructionList.this.mLast = replacement;
}
}
/**
* Returns a positive, negative or zero value indicating what affect
* this generated instruction has on the runtime stack.
*/
public int getStackAdjustment() {
return mStackAdjust;
}
/**
* Returns the stack depth for when this instruction is reached. If the
* value is negative, then this instruction is never reached.
*/
public int getStackDepth() {
return mStackDepth;
}
/**
* Returns the address of this instruction or -1 if not known.
*/
public int getLocation() {
return mLocation;
}
/**
* Returns true if instruction is a known branch target or exception
* handler entry point.
*/
public boolean isBranchTarget() {
return false;
}
/**
* Returns all of the targets that this instruction may branch to. Not
* all instructions support branching, and null is returned by default.
*/
public LabelInstruction[] getBranchTargets() {
return null;
}
/**
* Returns an all the exception handlers that wraps this instruction,
* or null if none.
*/
public Collection<ExceptionHandler<LabelInstruction>> getExceptionHandlers() {
return mExceptionHandlers;
}
/**
* Adds an exception handler that wraps this instruction.
*/
public void addExceptionHandler(ExceptionHandler<LabelInstruction> handler) {
if (mExceptionHandlers == null) {
mExceptionHandlers = new HashSet<ExceptionHandler<LabelInstruction>>(4);
}
mExceptionHandlers.add(handler);
}
/**
* Returns true if execution flow may continue after this instruction.
* It may be a goto, a method return, an exception throw or a
* subroutine return. Default implementation returns true.
*/
public boolean isFlowThrough() {
return true;
}
public boolean isSubroutineCall() {
return false;
}
/**
* Returns null if this is a pseudo instruction and no bytes are
* generated.
*/
public abstract byte[] getBytes();
/**
* An instruction is resolved when it has all information needed to
* generate correct byte code.
*/
public abstract boolean isResolved();
public int compareTo(Location other) {
if (this == other) {
return 0;
}
int loca = getLocation();
int locb = other.getLocation();
if (loca < locb) {
return -1;
} else if (loca > locb) {
return 1;
} else {
return 0;
}
}
/**
* Returns a string containing the type of this instruction, the stack
* adjustment and the list of byte codes. Unvisted instructions are
* marked with an asterisk.
*/
public String toString() {
String name = getClass().getName();
int index = name.lastIndexOf('.');
if (index >= 0) {
name = name.substring(index + 1);
}
index = name.lastIndexOf('$');
if (index >= 0) {
name = name.substring(index + 1);
}
StringBuffer buf = new StringBuffer(name.length() + 20);
int adjust = getStackAdjustment();
int depth = getStackDepth();
if (depth >= 0) {
buf.append(' ');
} else {
buf.append('*');
}
buf.append('[');
buf.append(mLocation);
buf.append("] ");
buf.append(name);
buf.append(" (");
if (depth >= 0) {
buf.append(depth);
buf.append(" + ");
buf.append(adjust);
buf.append(" = ");
buf.append(depth + adjust);
} else {
buf.append(adjust);
}
buf.append(") ");
try {
byte[] bytes = getBytes();
boolean wide = false;
if (bytes != null) {
for (int i=0; i<bytes.length; i++) {
if (i > 0) {
buf.append(',');
}
byte code = bytes[i];
if (i == 0 || wide) {
buf.append(Opcode.getMnemonic(code));
wide = code == Opcode.WIDE;
} else {
buf.append(code & 0xff);
}
}
}
} catch (Exception e) {
}
return buf.toString();
}
/**
* Reset this instruction in preparation for flow analysis.
*/
void reset(int instrCount) {
mStackDepth = -1;
// Start with a fake location.
mLocation = instrCount;
}
}
/**
* Defines a pseudo instruction for a label. No byte code is ever generated
* from a label. Labels are not automatically added to the list.
*/
public class LabelInstruction extends Instruction implements Label {
private boolean mIsTarget;
public LabelInstruction() {
super(0, false);
}
/**
* Set this label's branch location to be the current address
* in this label's parent CodeBuilder or InstructionList.
*
* @return This Label.
*/
public Label setLocation() {
add();
return this;
}
/**
* @return -1 when not resolved yet
*/
public int getLocation() throws IllegalStateException {
int loc;
if ((loc = mLocation) < 0) {
if (mPrev == null && mNext == null) {
throw new IllegalStateException("Label location is not set");
}
}
return loc;
}
@Override
public boolean isBranchTarget() {
return mIsTarget;
}
void markBranchTarget() {
mIsTarget = true;
}
/**
* Always returns null.
*/
public byte[] getBytes() {
return null;
}
public boolean isResolved() {
return getLocation() >= 0;
}
@Override
void reset(int instrCount) {
super.reset(instrCount);
mIsTarget = false;
}
}
/**
* Defines a code instruction and has storage for byte codes.
*/
public abstract class CodeInstruction extends Instruction {
protected byte[] mBytes;
protected CodeInstruction(int stackAdjust) {
super(stackAdjust);
}
protected CodeInstruction(int stackAdjust, boolean addInstruction) {
super(stackAdjust, addInstruction);
}
protected CodeInstruction(int stackAdjust, byte[] bytes) {
super(stackAdjust);
mBytes = bytes;
}
@Override
public boolean isFlowThrough() {
if (mBytes != null && mBytes.length > 0) {
switch (mBytes[0]) {
case Opcode.GOTO:
case Opcode.GOTO_W:
case Opcode.IRETURN:
case Opcode.LRETURN:
case Opcode.FRETURN:
case Opcode.DRETURN:
case Opcode.ARETURN:
case Opcode.RETURN:
case Opcode.ATHROW:
return false;
}
}
return true;
}
@Override
public byte[] getBytes() {
return mBytes;
}
@Override
public boolean isResolved() {
return true;
}
}
public class SimpleInstruction extends CodeInstruction {
/**
* @param pushed type of argument pushed to operand stack after
* instruction executes; pass TypeDesc.VOID if nothing
*/
public SimpleInstruction(int stackAdjust, TypeDesc pushed, byte[] bytes) {
super(stackAdjust, bytes);
// FIXME: pushed
}
}
/**
* Defines an instruction that has a single operand which references a
* constant in the constant pool.
*/
public class ConstantOperandInstruction extends SimpleInstruction {
private final ConstantInfo mInfo;
public ConstantOperandInstruction(int stackAdjust,
TypeDesc pushed,
byte[] bytes,
ConstantInfo info) {
super(stackAdjust, pushed, bytes);
if (bytes.length < 3) {
throw new IllegalArgumentException("Byte for instruction is too small");
}
mInfo = info;
}
@Override
public byte[] getBytes() {
int index = mInfo.getIndex();
if (index < 0) {
throw new IllegalStateException("Constant pool index not resolved");
}
mBytes[1] = (byte)(index >> 8);
mBytes[2] = (byte)index;
return mBytes;
}
@Override
public boolean isResolved() {
return mInfo.getIndex() >= 0;
}
}
/**
* Defines an instruction which create a new object.
*/
public class NewObjectInstruction extends ConstantOperandInstruction {
public NewObjectInstruction(ConstantClassInfo newType) {
super(1, TypeDesc.OBJECT, new byte[] {Opcode.NEW, 0, 0}, newType);
}
@Override
public boolean isFlowThrough() {
return true;
}
}
/**
* Defines an instruction which invokes a method.
*/
public class InvokeInstruction extends ConstantOperandInstruction {
public InvokeInstruction(byte opcode,
ConstantInfo method, TypeDesc ret, TypeDesc[] params) {
super(calcInvokeAdjust(opcode, ret, params),
ret,
createInvokeBytes(opcode, params),
method);
}
@Override
public boolean isFlowThrough() {
return true;
}
}
static int calcInvokeAdjust(byte opcode, TypeDesc ret, TypeDesc[] params) {
int stackAdjust = returnSize(ret) - argSize(params);
switch (opcode) {
case Opcode.INVOKESTATIC:
break;
case Opcode.INVOKEVIRTUAL:
case Opcode.INVOKEINTERFACE:
case Opcode.INVOKESPECIAL:
// Consume "this".
stackAdjust -= 1;
break;
default:
throw new IllegalArgumentException("Not an invoke operation: " + opcode);
}
return stackAdjust;
}
static byte[] createInvokeBytes(byte opcode, TypeDesc[] params) {
byte[] bytes;
if (opcode == Opcode.INVOKEINTERFACE) {
bytes = new byte[5];
bytes[3] = (byte)(1 + argSize(params));
} else {
bytes = new byte[3];
}
bytes[0] = opcode;
return bytes;
}
private static int returnSize(TypeDesc ret) {
if (ret == null || ret == TypeDesc.VOID) {
return 0;
}
if (ret.isDoubleWord()) {
return 2;
}
return 1;
}
private static int argSize(TypeDesc[] params) {
int size = 0;
if (params != null) {
for (int i=0; i<params.length; i++) {
size += returnSize(params[i]);
}
}
return size;
}
/**
* Defines an instruction which calls the constructor of a new object.
*/
public class InvokeConstructorInstruction extends InvokeInstruction {
public InvokeConstructorInstruction(ConstantInfo ctor, TypeDesc type, TypeDesc[] params) {
super(Opcode.INVOKESPECIAL, ctor, null, params);
}
}
/**
* Defines an instruction that loads a constant onto the stack from the
* constant pool.
*/
public class LoadConstantInstruction extends CodeInstruction {
private final ConstantInfo mInfo;
private final boolean mWideOnly;
/**
* @param pushed type of argument pushed to operand stack after
* instruction executes
*/
public LoadConstantInstruction(int stackAdjust,
TypeDesc pushed,
ConstantInfo info) {
this(stackAdjust, pushed, info, false);
}
/**
* @param pushed type of argument pushed to operand stack after
* instruction executes
*/
public LoadConstantInstruction(int stackAdjust,
TypeDesc pushed,
ConstantInfo info,
boolean wideOnly) {
super(stackAdjust);
// FIXME: pushed
mInfo = info;
mWideOnly = wideOnly;
}
@Override
public boolean isFlowThrough() {
return true;
}
@Override
public byte[] getBytes() {
int index = mInfo.getIndex();
if (index < 0) {
throw new IllegalStateException("Constant pool index not resolved");
}
if (mWideOnly) {
byte[] bytes = new byte[3];
bytes[0] = Opcode.LDC2_W;
bytes[1] = (byte)(index >> 8);
bytes[2] = (byte)index;
return bytes;
} else if (index <= 255) {
byte[] bytes = new byte[2];
bytes[0] = Opcode.LDC;
bytes[1] = (byte)index;
return bytes;
} else {
byte[] bytes = new byte[3];
bytes[0] = Opcode.LDC_W;
bytes[1] = (byte)(index >> 8);
bytes[2] = (byte)index;
return bytes;
}
}
@Override
public boolean isResolved() {
return mInfo.getIndex() >= 0;
}
}
/**
* Defines a branch instruction, like a goto, jsr or any conditional
* branch.
*/
public class BranchInstruction extends CodeInstruction {
private final LabelInstruction mTarget;
private boolean mHasShortHop = false;
private boolean mIsSub = false;
public BranchInstruction(int stackAdjust,
byte opcode, LabelInstruction target) {
this(stackAdjust, true, opcode, target);
}
private BranchInstruction(int stackAdjust, boolean addInstruction,
byte opcode, LabelInstruction target) {
super(stackAdjust, addInstruction);
mTarget = target;
switch (opcode) {
case Opcode.JSR_W:
mIsSub = true;
// Flow through to next case.
case Opcode.GOTO_W:
mBytes = new byte[5];
mBytes[0] = opcode;
break;
case Opcode.JSR:
mIsSub = true;
// Flow through to next case.
case Opcode.GOTO:
case Opcode.IF_ACMPEQ:
case Opcode.IF_ACMPNE:
case Opcode.IF_ICMPEQ:
case Opcode.IF_ICMPNE:
case Opcode.IF_ICMPLT:
case Opcode.IF_ICMPGE:
case Opcode.IF_ICMPGT:
case Opcode.IF_ICMPLE:
case Opcode.IFEQ:
case Opcode.IFNE:
case Opcode.IFLT:
case Opcode.IFGE:
case Opcode.IFGT:
case Opcode.IFLE:
case Opcode.IFNONNULL:
case Opcode.IFNULL:
mBytes = new byte[3];
mBytes[0] = opcode;
break;
default:
throw new IllegalArgumentException
("Opcode not a branch instruction: " +
Opcode.getMnemonic(opcode));
}
}
public LabelInstruction[] getBranchTargets() {
return new LabelInstruction[] {mTarget};
}
public boolean isSubroutineCall() {
return mIsSub;
}
@Override
public byte[] getBytes() {
if (!isResolved() || mHasShortHop) {
return mBytes;
}
int offset = mTarget.getLocation() - mLocation;
byte opcode = mBytes[0];
if (opcode == Opcode.GOTO_W || opcode == Opcode.JSR_W) {
mBytes[1] = (byte)(offset >> 24);
mBytes[2] = (byte)(offset >> 16);
mBytes[3] = (byte)(offset >> 8);
mBytes[4] = (byte)(offset >> 0);
} else if (-32768 <= offset && offset <= 32767) {
mBytes[1] = (byte)(offset >> 8);
mBytes[2] = (byte)(offset >> 0);
} else if (opcode == Opcode.GOTO || opcode == Opcode.JSR) {
mBytes = new byte[5];
if (opcode == Opcode.GOTO) {
mBytes[0] = Opcode.GOTO_W;
} else {
mBytes[0] = Opcode.JSR_W;
}
mBytes[1] = (byte)(offset >> 24);
mBytes[2] = (byte)(offset >> 16);
mBytes[3] = (byte)(offset >> 8);
mBytes[4] = (byte)(offset >> 0);
} else {
// The if branch requires a 32 bit offset.
// Convert:
//
// if <cond> goto target
// // reached if <cond> false
// target: // reached if <cond> true
// to this:
//
// if not <cond> goto shortHop
// goto_w target
// shortHop: // reached if <cond> false
// target: // reached if <cond> true
mHasShortHop = true;
opcode = Opcode.reverseIfOpcode(opcode);
mBytes[0] = opcode;
// Specify offset to jump to shortHop.
mBytes[1] = (byte)0;
mBytes[2] = (byte)(3 + 5); // 3: if statement size; 5: goto_w size
// insert goto_w instruction after this one.
insert(new BranchInstruction(0, false, Opcode.GOTO_W, mTarget));
}
return mBytes;
}
@Override
public boolean isResolved() {
return mTarget.getLocation() >= 0;
}
}
/**
* Defines an instruction that contains an operand for referencing a
* LocalVariable.
*/
public abstract class LocalOperandInstruction extends CodeInstruction {
protected final LocalVariableImpl mLocal;
public LocalOperandInstruction(int stackAdjust, LocalVariable local) {
super(stackAdjust);
mLocal = (LocalVariableImpl)local;
}
@Override
public boolean isResolved() {
return mLocal.getNumber() >= 0;
}
public LocalVariableImpl getLocalVariable() {
return mLocal;
}
public int getVariableNumber() {
int varNum = mLocal.getNumber();
if (varNum < 0) {
throw new IllegalStateException("Local variable number not resolved: " + mLocal);
}
return varNum;
}
public abstract boolean isLoad();
public abstract boolean isStore();
}
/**
* Defines an instruction that loads a local variable onto the stack.
*/
public class LoadLocalInstruction extends LocalOperandInstruction {
public LoadLocalInstruction(int stackAdjust, LocalVariable local) {
super(stackAdjust, local);
}
@Override
public boolean isFlowThrough() {
return true;
}
@Override
public byte[] getBytes() {
int varNum = getVariableNumber();
byte opcode;
boolean writeIndex = false;
int typeCode = mLocal.getType().getTypeCode();
switch(varNum) {
case 0:
switch (typeCode) {
default:
opcode = Opcode.ALOAD_0;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LLOAD_0;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FLOAD_0;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DLOAD_0;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ILOAD_0;
break;
}
break;
case 1:
switch (typeCode) {
default:
opcode = Opcode.ALOAD_1;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LLOAD_1;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FLOAD_1;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DLOAD_1;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ILOAD_1;
break;
}
break;
case 2:
switch (typeCode) {
default:
opcode = Opcode.ALOAD_2;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LLOAD_2;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FLOAD_2;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DLOAD_2;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ILOAD_2;
break;
}
break;
case 3:
switch (typeCode) {
default:
opcode = Opcode.ALOAD_3;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LLOAD_3;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FLOAD_3;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DLOAD_3;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ILOAD_3;
break;
}
break;
default:
writeIndex = true;
switch (typeCode) {
default:
opcode = Opcode.ALOAD;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LLOAD;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FLOAD;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DLOAD;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ILOAD;
break;
}
break;
}
if (!writeIndex) {
mBytes = new byte[] { opcode };
} else {
if (varNum <= 255) {
mBytes = new byte[] { opcode, (byte)varNum };
} else {
mBytes = new byte[]
{
Opcode.WIDE,
opcode,
(byte)(varNum >> 8),
(byte)varNum
};
}
}
return mBytes;
}
public boolean isLoad() {
return true;
}
public boolean isStore() {
return false;
}
}
/**
* Defines an instruction that stores a value from the stack into a local
* variable.
*/
public class StoreLocalInstruction extends LocalOperandInstruction {
private boolean mDiscardResult;
public StoreLocalInstruction(int stackAdjust, LocalVariable local) {
super(stackAdjust, local);
}
@Override
public boolean isFlowThrough() {
return true;
}
@Override
public byte[] getBytes() {
if (mDiscardResult) {
// Liveness analysis discovered that the results of this store
// are not needed so just pop if off the stack.
return new byte[] { mLocal.isDoubleWord() ? Opcode.POP2 : Opcode.POP };
}
int varNum = getVariableNumber();
byte opcode;
boolean writeIndex = false;
int typeCode = mLocal.getType().getTypeCode();
switch(varNum) {
case 0:
switch (typeCode) {
default:
opcode = Opcode.ASTORE_0;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LSTORE_0;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FSTORE_0;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DSTORE_0;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ISTORE_0;
break;
}
break;
case 1:
switch (typeCode) {
default:
opcode = Opcode.ASTORE_1;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LSTORE_1;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FSTORE_1;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DSTORE_1;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ISTORE_1;
break;
}
break;
case 2:
switch (typeCode) {
default:
opcode = Opcode.ASTORE_2;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LSTORE_2;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FSTORE_2;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DSTORE_2;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ISTORE_2;
break;
}
break;
case 3:
switch (typeCode) {
default:
opcode = Opcode.ASTORE_3;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LSTORE_3;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FSTORE_3;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DSTORE_3;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ISTORE_3;
break;
}
break;
default:
writeIndex = true;
switch (typeCode) {
default:
opcode = Opcode.ASTORE;
break;
case TypeDesc.LONG_CODE:
opcode = Opcode.LSTORE;
break;
case TypeDesc.FLOAT_CODE:
opcode = Opcode.FSTORE;
break;
case TypeDesc.DOUBLE_CODE:
opcode = Opcode.DSTORE;
break;
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
opcode = Opcode.ISTORE;
break;
}
break;
}
if (!writeIndex) {
mBytes = new byte[] { opcode };
} else {
if (varNum <= 255) {
mBytes = new byte[] { opcode, (byte)varNum };
} else {
mBytes = new byte[]
{
Opcode.WIDE,
opcode,
(byte)(varNum >> 8),
(byte)varNum
};
}
}
return mBytes;
}
@Override
public boolean isResolved() {
return true;
}
public boolean isLoad() {
return false;
}
public boolean isStore() {
return true;
}
public void discardResult() {
mDiscardResult = true;
}
}
/**
* Defines a ret instruction for returning from a jsr call.
*/
public class RetInstruction extends LocalOperandInstruction {
// Note: This instruction does not provide any branch targets. The
// analysis for determining all possible return locations is
// complicated. Instead, the stack flow analysis assumes that all jsr
// calls are "well formed", and so it doesn't need to follow the ret
// back to a "comes from" label. Liveness analysis could take advantage
// of the branch targets, and reduce the set of variables used to
// manage jsr return addresses. Since jsr/ret is used infrequently,
// local variables used by ret are fixed and are not optimized.
public RetInstruction(LocalVariable local) {
super(0, local);
((LocalVariableImpl)local).setFixedNumber(mNextFixedVariableNumber++);
}
@Override
public boolean isFlowThrough() {
return false;
}
@Override
public byte[] getBytes() {
int varNum = getVariableNumber();
if (varNum <= 255) {
mBytes = new byte[] { Opcode.RET, (byte)varNum };
} else {
mBytes = new byte[]
{
Opcode.WIDE,
Opcode.RET,
(byte)(varNum >> 8),
(byte)varNum
};
}
return mBytes;
}
public boolean isLoad() {
return true;
}
public boolean isStore() {
return false;
}
}
/**
* Defines a specialized instruction that increments a local variable by
* a signed 16-bit amount.
*/
public class ShortIncrementInstruction extends LocalOperandInstruction {
private final short mAmount;
public ShortIncrementInstruction(LocalVariable local, short amount) {
super(0, local);
mAmount = amount;
}
@Override
public boolean isFlowThrough() {
return true;
}
@Override
public byte[] getBytes() {
int varNum = getVariableNumber();
if ((-128 <= mAmount && mAmount <= 127) && varNum <= 255) {
mBytes = new byte[] {
Opcode.IINC,
(byte)varNum,
(byte)mAmount
};
} else {
mBytes = new byte[] {
Opcode.WIDE,
Opcode.IINC,
(byte)(varNum >> 8),
(byte)varNum,
(byte)(mAmount >> 8),
(byte)mAmount
};
}
return mBytes;
}
public boolean isLoad() {
return true;
}
public boolean isStore() {
return true;
}
}
/**
* Defines a switch instruction. The choice of which actual switch
* implementation to use (table or lookup switch) is determined
* automatically based on which generates to the smallest amount of bytes.
*/
public class SwitchInstruction extends CodeInstruction {
private final int[] mCases;
private final LabelInstruction[] mLocations;
private final LabelInstruction mDefaultLocation;
private final byte mOpcode;
private final int mSmallest;
private final int mLargest;
public SwitchInstruction(int[] casesParam,
Location[] locationsParam,
Location defaultLocation)
{
// A SwitchInstruction always adjusts the stack by -1 because it
// pops the switch key off the stack.
super(-1);
if (casesParam.length != locationsParam.length) {
throw new IllegalArgumentException
("Switch cases and locations sizes differ: " +
casesParam.length + ", " + locationsParam.length);
}
mCases = new int[casesParam.length];
System.arraycopy(casesParam, 0, mCases, 0, casesParam.length);
LabelInstruction[] locations = new LabelInstruction[locationsParam.length];
for (int i=0; i<locations.length; i++) {
LabelInstruction location;
try {
location = (LabelInstruction)locationsParam[i];
} catch (ClassCastException e) {
throw new IllegalArgumentException
("Switch location is not a label instruction");
}
locations[i] = location;
}
mLocations = locations;
try {
mDefaultLocation = (LabelInstruction)defaultLocation;
} catch (ClassCastException e) {
throw new IllegalArgumentException("Default location is not a label instruction");
}
// First sort the cases and locations.
sort(0, mCases.length - 1);
// Check for duplicate cases.
int lastCase = 0;
for (int i=0; i<mCases.length; i++) {
if (i > 0 && mCases[i] == lastCase) {
throw new IllegalArgumentException("Duplicate switch cases: " + lastCase);
}
lastCase = mCases[i];
}
// Now determine which kind of switch to use.
mSmallest = mCases[0];
mLargest = mCases[mCases.length - 1];
long tSize = 12 + 4 * ((long) mLargest - mSmallest + 1);
int lSize = 8 + 8 * mCases.length;
if (tSize <= lSize) {
mOpcode = Opcode.TABLESWITCH;
} else {
mOpcode = Opcode.LOOKUPSWITCH;
}
}
public LabelInstruction[] getBranchTargets() {
LabelInstruction[] targets = new LabelInstruction[mLocations.length + 1];
System.arraycopy(mLocations, 0, targets, 0, mLocations.length);
targets[targets.length - 1] = mDefaultLocation;
return targets;
}
@Override
public boolean isFlowThrough() {
return false;
}
@Override
public byte[] getBytes() {
int length = 1;
int pad = 3 - (mLocation & 3);
length += pad;
if (mOpcode == Opcode.TABLESWITCH) {
length += 12 + 4 * (mLargest - mSmallest + 1);
} else {
length += 8 + 8 * mCases.length;
}
mBytes = new byte[length];
if (!isResolved()) {
return mBytes;
}
mBytes[0] = mOpcode;
int cursor = pad + 1;
int defaultOffset = mDefaultLocation.getLocation() - mLocation;
mBytes[cursor++] = (byte)(defaultOffset >> 24);
mBytes[cursor++] = (byte)(defaultOffset >> 16);
mBytes[cursor++] = (byte)(defaultOffset >> 8);
mBytes[cursor++] = (byte)(defaultOffset >> 0);
if (mOpcode == Opcode.TABLESWITCH) {
mBytes[cursor++] = (byte)(mSmallest >> 24);
mBytes[cursor++] = (byte)(mSmallest >> 16);
mBytes[cursor++] = (byte)(mSmallest >> 8);
mBytes[cursor++] = (byte)(mSmallest >> 0);
mBytes[cursor++] = (byte)(mLargest >> 24);
mBytes[cursor++] = (byte)(mLargest >> 16);
mBytes[cursor++] = (byte)(mLargest >> 8);
mBytes[cursor++] = (byte)(mLargest >> 0);
int index = 0;
for (int case_ = mSmallest; case_ <= mLargest; case_++) {
if (case_ == mCases[index]) {
int offset =
mLocations[index].getLocation() - mLocation;
mBytes[cursor++] = (byte)(offset >> 24);
mBytes[cursor++] = (byte)(offset >> 16);
mBytes[cursor++] = (byte)(offset >> 8);
mBytes[cursor++] = (byte)(offset >> 0);
index++;
} else {
mBytes[cursor++] = (byte)(defaultOffset >> 24);
mBytes[cursor++] = (byte)(defaultOffset >> 16);
mBytes[cursor++] = (byte)(defaultOffset >> 8);
mBytes[cursor++] = (byte)(defaultOffset >> 0);
}
}
} else {
mBytes[cursor++] = (byte)(mCases.length >> 24);
mBytes[cursor++] = (byte)(mCases.length >> 16);
mBytes[cursor++] = (byte)(mCases.length >> 8);
mBytes[cursor++] = (byte)(mCases.length >> 0);
for (int index = 0; index < mCases.length; index++) {
int case_ = mCases[index];
mBytes[cursor++] = (byte)(case_ >> 24);
mBytes[cursor++] = (byte)(case_ >> 16);
mBytes[cursor++] = (byte)(case_ >> 8);
mBytes[cursor++] = (byte)(case_ >> 0);
int offset = mLocations[index].getLocation() - mLocation;
mBytes[cursor++] = (byte)(offset >> 24);
mBytes[cursor++] = (byte)(offset >> 16);
mBytes[cursor++] = (byte)(offset >> 8);
mBytes[cursor++] = (byte)(offset >> 0);
}
}
return mBytes;
}
@Override
public boolean isResolved() {
if (mDefaultLocation.getLocation() >= 0) {
for (int i=0; i<mLocations.length; i++) {
if (mLocations[i].getLocation() < 0) {
break;
}
}
return true;
}
return false;
}
private void sort(int left, int right) {
if (left >= right) {
return;
}
swap(left, (left + right) / 2); // move middle element to 0
int last = left;
for (int i = left + 1; i <= right; i++) {
if (mCases[i] < mCases[left]) {
swap(++last, i);
}
}
swap(left, last);
sort(left, last-1);
sort(last + 1, right);
}
private void swap(int i, int j) {
int tempInt = mCases[i];
mCases[i] = mCases[j];
mCases[j] = tempInt;
LabelInstruction tempLocation = mLocations[i];
mLocations[i] = mLocations[j];
mLocations[j] = tempLocation;
}
}
/**
* Defines an instruction which manipulates the operand stack.
*/
public class StackOperationInstruction extends CodeInstruction {
public StackOperationInstruction(byte opcode) {
super(calcStackOperationAdjust(opcode), new byte[] {opcode});
}
@Override
public boolean isFlowThrough() {
return true;
}
}
static int calcStackOperationAdjust(byte opcode) {
switch (opcode) {
case Opcode.DUP:
return 1;
case Opcode.DUP_X1:
return 1;
case Opcode.DUP_X2:
return 1;
case Opcode.DUP2:
return 2;
case Opcode.DUP2_X1:
return 2;
case Opcode.DUP2_X2:
return 2;
case Opcode.POP:
return -1;
case Opcode.POP2:
return -2;
case Opcode.SWAP:
return 0;
default:
throw new IllegalArgumentException("Not a stack operation: " + opcode);
}
}
}