Mercurial > hg > blitz_stable
view src/com/go/trove/classfile/InstructionList.java @ 32:243c74d599bf
Update ignores
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 12 Jun 2010 10:41:47 +0100 |
parents | 3dc0c5604566 |
children |
line wrap: on
line source
/* ==================================================================== * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group * ==================================================================== * The Tea Software License, Version 1.1 * * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Walt Disney Internet Group (http://opensource.go.com/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact opensource@dig.com. * * 5. Products derived from this software may not be called "Tea", * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior * written permission of the Walt Disney Internet Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * For more information about Tea, please see http://opensource.go.com/. */ package com.go.trove.classfile; import java.util.*; import java.io.*; /****************************************************************************** * The InstructionList class is used by the CodeBuilder to perform lower-level * bookkeeping operations and flow analysis. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> * @see CodeBuilder */ class InstructionList implements CodeBuffer { private static final boolean DEBUG = false; Instruction mFirst; Instruction mLast; boolean mResolved = false; private List mExceptionHandlers = new ArrayList(4); private List mLocalVariables = new ArrayList(); private int mMaxStack; private int mMaxLocals; private byte[] mByteCodes; private int mBufferLength; protected InstructionList() { super(); } /** * Returns an immutable collection of all the instructions in this * InstructionList. */ public Collection getInstructions() { return new AbstractCollection() { public Iterator iterator() { return new Iterator() { private Instruction mNext = mFirst; public boolean hasNext() { return mNext != null; } public Object 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 (ExceptionHandler[])mExceptionHandlers.toArray(handlers); } public void addExceptionHandler(ExceptionHandler handler) { mExceptionHandlers.add(handler); } public LocalVariable createLocalVariable(String name, TypeDescriptor type) { LocalVariable var = new LocalVariableImpl(name, type, -1); mLocalVariables.add(var); return var; } public LocalVariable createLocalParameter(String name, TypeDescriptor type, int number) { LocalVariableImpl var = new LocalVariableImpl(name, type, number); mLocalVariables.add(var); if (mFirst == null) { // Make sure there is an initial instruction. Create a pseudo one. LabelInstruction label = new LabelInstruction(); label.setLocation(); mFirst = label; } // Parameters are initialized first, so ensure flow analysis starts // at the beginning. var.addStoreInstruction(mFirst); return var; } private void resolve() { if (mResolved) { return; } if (!DEBUG) { resolve0(); } else { try { resolve0(); } finally { System.out.println("-- Instructions --"); Iterator it = getInstructions().iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } } private void resolve0() { mMaxStack = 0; mMaxLocals = 0; Instruction instr; // Sweep through the instructions, marking them as not being // visted by flow analysis and set fake locations. int instrCount = 0; for (instr = mFirst; instr != null; instr = instr.mNext) { instr.mStackDepth = -1; instr.mLocation = instrCount++; } // Assign variable numbers using the simplest technique. int size = mLocalVariables.size(); List activeLocationBits = new ArrayList(size); for (int i=0; i<size; i++) { LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(i); if (var.getNumber() < 0) { var.setNumber(mMaxLocals); } int max = var.getNumber() + (var.isDoubleWord() ? 2 : 1); if (max > mMaxLocals) { mMaxLocals = max; } } /* // Perform variable flow analysis for each local variable, in order to // determine which register it should be assigned. int size = mLocalVariables.size(); List activeLocationBits = new ArrayList(size); for (int i=0; i<size; i++) { LocalVariableImpl var = (LocalVariableImpl)mLocalVariables.get(i); BitList activeLocations = new BitList(instrCount); activeLocationBits.add(activeLocations); // Start flow analysis at store variable instructions. Iterator it = var.iterateStoreInstructions(); while (it.hasNext()) { Instruction enter = (Instruction)it.next(); variableResolve(var, enter, activeLocations, new BitList(instrCount), false); } // Continue flow analysis into all exception handlers that wrap // the active locations. boolean passAgain; do { passAgain = false; it = mExceptionHandlers.iterator(); while (it.hasNext()) { ExceptionHandler handler = (ExceptionHandler)it.next(); if (!isIntersecting(activeLocations, handler)) { continue; } Instruction enter = (Instruction)handler.getCatchLocation(); passAgain = variableResolve(var, enter, activeLocations, new BitList(instrCount), false); passAgain = false; } } while (passAgain); if (!var.isFixedNumber()) { var.setNumber(-1); // Assign variable number by checking first if it can be shared // with another variable of the same size. boolean[] conflicts = new boolean[mMaxLocals]; for (int j=0; j<i; j++) { LocalVariable av = (LocalVariable)mLocalVariables.get(j); if (av.getNumber() < 0 || av.isDoubleWord() != var.isDoubleWord()) { continue; } BitList alocs = (BitList)activeLocationBits.get(j); if (isIntersecting(alocs, activeLocations)) { conflicts[av.getNumber()] = true; if (av.getNumber() == var.getNumber()) { var.setNumber(-1); } } else if (conflicts[av.getNumber()]) { var.setNumber(-1); } else { var.setNumber(av.getNumber()); } } if (var.getNumber() < 0) { var.setNumber(mMaxLocals); } } int max = var.getNumber() + (var.isDoubleWord() ? 2 : 1); if (max > mMaxLocals) { mMaxLocals = max; } // TODO //var.setLocations(activeLocations); } activeLocationBits = null; */ // Perform stack flow analysis to determine the max stack size. // Start the flow analysis at the first instruction. Map subAdjustMap = new HashMap(11); stackResolve(0, mFirst, subAdjustMap); // Continue flow analysis into exception handler entry points. Iterator it = mExceptionHandlers.iterator(); while (it.hasNext()) { ExceptionHandler handler = (ExceptionHandler)it.next(); Instruction enter = (Instruction)handler.getCatchLocation(); 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[16]; mBufferLength = 0; for (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 boolean variableResolve(LocalVariableImpl var, Instruction instr, BitList activeLocations, BitList possibleLocations, boolean fork) { while (instr != null) { int instrLoc = instr.getLocation(); if (activeLocations.get(instrLoc)) { activeLocations.or(possibleLocations); if (possibleLocations.isAllClear()) { return false; } else { possibleLocations.clear(); return true; } } if (possibleLocations.get(instrLoc)) { return false; } possibleLocations.set(instrLoc); if (instr instanceof LocalOperandInstruction && ((LocalOperandInstruction)instr).getLocalVariable() == var) { if (instr instanceof StoreLocalInstruction) { activeLocations.set(instrLoc); if (fork) { possibleLocations.clear(instrLoc); } else { possibleLocations.clear(); } } else { activeLocations.or(possibleLocations); possibleLocations.clear(); } } // Determine the next instruction to flow down to. Instruction next = null; if (instr.isFlowThrough()) { if ((next = instr.mNext) == null) { throw new RuntimeException ("Execution flows through end of method"); } } Location[] targets = instr.getBranchTargets(); if (targets != null) { for (int i=0; i<targets.length; i++) { LabelInstruction targetInstr = (LabelInstruction)targets[i]; if (i == 0 && next == null) { // Flow to the first target if instruction doesn't // flow to its next instruction. next = targetInstr; continue; } variableResolve (var, targetInstr, activeLocations, possibleLocations, true); } } instr = next; } return true; } private boolean isIntersecting(BitList a, BitList b) { a = (BitList)a.clone(); a.and(b); return !a.isAllClear(); } private boolean isIntersecting(BitList bits, LocationRange range) { // TODO: how efficient is this? int start = range.getStartLocation().getLocation(); int end = range.getEndLocation().getLocation(); for (int i=start; i<end; i++) { if (bits.get(i)) { return true; } } return false; } */ private int stackResolve(int stackDepth, Instruction instr, Map 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 { if (instr.mStackDepth != stackDepth) { throw new RuntimeException ("Stack depth different at previously visited " + "instruction: " + instr.mStackDepth + " != " + stackDepth); } break; } // Determine the next instruction to flow down to. Instruction next = null; if (instr.isFlowThrough()) { if ((next = instr.mNext) == null) { throw new RuntimeException ("Execution flows through end of method"); } } stackDepth += instr.getStackAdjustment(); if (stackDepth > mMaxStack) { mMaxStack = stackDepth; } else if (stackDepth < 0) { throw new RuntimeException("Stack depth is negative: " + stackDepth); } Location[] targets = instr.getBranchTargets(); if (targets != null) { for (int i=0; i<targets.length; i++) { LabelInstruction targetInstr = (LabelInstruction)targets[i]; if (i == 0 && next == null) { // Flow to the first target if instruction doesn't // flow to its next instruction. next = targetInstr; continue; } if (!instr.isSubroutineCall()) { stackResolve (stackDepth, targetInstr, subAdjustMap); } else { Integer subAdjust = (Integer)subAdjustMap.get(targetInstr); if (subAdjust == null) { int newDepth = stackResolve(stackDepth, targetInstr, subAdjustMap); subAdjust = new Integer(newDepth - stackDepth); subAdjustMap.put(targetInstr, subAdjust); } stackDepth += subAdjust.intValue(); } } } instr = next; } return stackDepth; } private class LocalVariableImpl implements LocalVariable { private String mName; private TypeDescriptor mType; private boolean mIsDoubleWord; private int mNumber; private boolean mFixed; private List mStoreInstructions; private SortedSet mLocationRangeSet; public LocalVariableImpl(String name, TypeDescriptor type, int number) { mName = name; mType = type; Class clazz = type.getClassArg(); mIsDoubleWord = clazz == long.class || clazz == double.class; mNumber = number; if (number >= 0) { mFixed = true; } mStoreInstructions = new ArrayList(); } /** * May return null if this LocalVariable is unnamed. */ public String getName() { return mName; } public void setName(String name) { mName = name; } public TypeDescriptor getType() { return mType; } public boolean isDoubleWord() { return mIsDoubleWord; } public int getNumber() { return mNumber; } public void setNumber(int number) { mNumber = number; } public SortedSet getLocationRangeSet() { return mLocationRangeSet; } public void setLocations(Set locations) { /* TODO List sortedLocations = new ArrayList(locations); Collections.sort(sortedLocations); mLocationRangeSet = new TreeSet(); Iterator it = sortedLocations.iterator(); Instruction first = null; Instruction last = null; while (it.hasNext()) { Instruction instr = (Instruction)it.next(); if (first == null) { first = last = instr; } else if (last.mNext == instr) { last = instr; } else { if (last.mNext != null) { last = last.mNext; } mLocationRangeSet.add(new LocationRangeImpl(first, last)); first = last = instr; } } if (first != null && last != null) { if (last.mNext != null) { last = last.mNext; } mLocationRangeSet.add(new LocationRangeImpl(first, last)); } mLocationRangeSet = Collections.unmodifiableSortedSet(mLocationRangeSet); */ } public boolean isFixedNumber() { return mFixed; } public void addStoreInstruction(Instruction instr) { mStoreInstructions.add(instr); } public Iterator iterateStoreInstructions() { return mStoreInstructions.iterator(); } public String toString() { if (getName() != null) { return String.valueOf(getType()) + ' ' + getName(); } else { return String.valueOf(getType()); } } } ///////////////////////////////////////////////////////////////////////// // // Begin inner class definitions for instructions of the InstructionList. // ///////////////////////////////////////////////////////////////////////// /************************************************************************** * An Instruction is an element in an InstructionList, and represents a * Java byte code instruction. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ 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; /** * 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 all of the targets that this instruction may branch to. Not * all instructions support branching, and null is returned by default. */ public Location[] getBranchTargets() { return null; } /** * 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(Object obj) { if (this == obj) { return 0; } Location other = (Location)obj; 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(); } } /************************************************************************** * Defines a psuedo instruction for a label. No byte code is ever generated * from a label. Labels are not automatically added to the list. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class LabelInstruction extends Instruction implements Label { 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; } /** * Always returns null. */ public byte[] getBytes() { return null; } public boolean isResolved() { return getLocation() >= 0; } } /************************************************************************** * Defines a code instruction and has storage for byte codes. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class CodeInstruction extends Instruction { protected byte[] mBytes; public CodeInstruction(int stackAdjust) { super(stackAdjust); } protected CodeInstruction(int stackAdjust, boolean addInstruction) { super(stackAdjust, addInstruction); } public CodeInstruction(int stackAdjust, byte b) { super(stackAdjust); mBytes = new byte[] {b}; } public CodeInstruction(int stackAdjust, byte[] bytes) { super(stackAdjust); mBytes = bytes; } 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; } public byte[] getBytes() { return mBytes; } public boolean isResolved() { return true; } } /************************************************************************** * Defines a branch instruction, like a goto, jsr or any conditional * branch. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class BranchInstruction extends CodeInstruction { private Location mTarget; private boolean mHasShortHop = false; private boolean mIsSub = false; public BranchInstruction(int stackAdjust, byte opcode, Location target) { this(stackAdjust, true, opcode, target); } private BranchInstruction(int stackAdjust, boolean addInstruction, byte opcode, Location target) { super(stackAdjust, addInstruction); mTarget = target; switch (opcode) { case Opcode.GOTO_W: case Opcode.JSR_W: mIsSub = true; 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 Location[] getBranchTargets() { return new Location[] {mTarget}; } public boolean isSubroutineCall() { return mIsSub; } 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; mBytes[1] = (byte)0; mBytes[2] = (byte)8; // insert goto_w instruction after this one. insert (new BranchInstruction(0, false, Opcode.GOTO_W, mTarget)); } return mBytes; } public boolean isResolved() { return mTarget.getLocation() >= 0; } } /************************************************************************** * Defines an instruction that has a single operand which references a * constant in the constant pool. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class ConstantOperandInstruction extends CodeInstruction { private ConstantInfo mInfo; public ConstantOperandInstruction(int stackAdjust, byte[] bytes, ConstantInfo info) { super(stackAdjust, bytes); mInfo = info; } public byte[] getBytes() { int index = mInfo.getIndex(); if (index < 0) { throw new RuntimeException("Constant pool index not resolved"); } mBytes[1] = (byte)(index >> 8); mBytes[2] = (byte)index; return mBytes; } public boolean isResolved() { return mInfo.getIndex() >= 0; } } /************************************************************************** * Defines an instruction that loads a constant onto the stack from the * constant pool. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class LoadConstantInstruction extends CodeInstruction { private ConstantInfo mInfo; private boolean mWideOnly; public LoadConstantInstruction(int stackAdjust, ConstantInfo info) { this(stackAdjust, info, false); } public LoadConstantInstruction(int stackAdjust, ConstantInfo info, boolean wideOnly) { super(stackAdjust); mInfo = info; mWideOnly = wideOnly; } public boolean isFlowThrough() { return true; } public byte[] getBytes() { int index = mInfo.getIndex(); if (index < 0) { throw new RuntimeException("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; } } public boolean isResolved() { return mInfo.getIndex() >= 0; } } /************************************************************************** * Defines an instruction that contains an operand for referencing a * LocalVariable. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class LocalOperandInstruction extends CodeInstruction { protected LocalVariable mLocal; public LocalOperandInstruction(int stackAdjust, LocalVariable local) { super(stackAdjust); mLocal = local; } public boolean isResolved() { return mLocal.getNumber() >= 0; } public LocalVariable getLocalVariable() { return mLocal; } public int getVariableNumber() { int varNum = mLocal.getNumber(); if (varNum < 0) { throw new RuntimeException ("Local variable number not resolved"); } return varNum; } } /************************************************************************** * Defines an instruction that loads a local variable onto the stack. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class LoadLocalInstruction extends LocalOperandInstruction { public LoadLocalInstruction(int stackAdjust, LocalVariable local) { super(stackAdjust, local); } public boolean isFlowThrough() { return true; } public byte[] getBytes() { int varNum = getVariableNumber(); byte opcode; boolean writeIndex = false; TypeDescriptor type = mLocal.getType(); Class clazz; if (type.getDimensions() > 0) { clazz = null; } else { clazz = type.getClassArg(); } switch(varNum) { case 0: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ALOAD_0; } else if (clazz == long.class) { opcode = Opcode.LLOAD_0; } else if (clazz == float.class) { opcode = Opcode.FLOAD_0; } else if (clazz == double.class) { opcode = Opcode.DLOAD_0; } else { opcode = Opcode.ILOAD_0; } break; case 1: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ALOAD_1; } else if (clazz == long.class) { opcode = Opcode.LLOAD_1; } else if (clazz == float.class) { opcode = Opcode.FLOAD_1; } else if (clazz == double.class) { opcode = Opcode.DLOAD_1; } else { opcode = Opcode.ILOAD_1; } break; case 2: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ALOAD_2; } else if (clazz == long.class) { opcode = Opcode.LLOAD_2; } else if (clazz == float.class) { opcode = Opcode.FLOAD_2; } else if (clazz == double.class) { opcode = Opcode.DLOAD_2; } else { opcode = Opcode.ILOAD_2; } break; case 3: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ALOAD_3; } else if (clazz == long.class) { opcode = Opcode.LLOAD_3; } else if (clazz == float.class) { opcode = Opcode.FLOAD_3; } else if (clazz == double.class) { opcode = Opcode.DLOAD_3; } else { opcode = Opcode.ILOAD_3; } break; default: writeIndex = true; if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ALOAD; } else if (clazz == long.class) { opcode = Opcode.LLOAD; } else if (clazz == float.class) { opcode = Opcode.FLOAD; } else if (clazz == double.class) { opcode = Opcode.DLOAD; } else { opcode = Opcode.ILOAD; } 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; } } /************************************************************************** * Defines an instruction that stores a value from the stack into a local * variable. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class StoreLocalInstruction extends LocalOperandInstruction { public StoreLocalInstruction(int stackAdjust, LocalVariable local) { super(stackAdjust, local); ((LocalVariableImpl)local).addStoreInstruction(this); } public boolean isFlowThrough() { return true; } public byte[] getBytes() { int varNum = getVariableNumber(); byte opcode; boolean writeIndex = false; TypeDescriptor type = mLocal.getType(); Class clazz; if (type.getDimensions() > 0) { clazz = null; } else { clazz = type.getClassArg(); } switch(varNum) { case 0: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ASTORE_0; } else if (clazz == long.class) { opcode = Opcode.LSTORE_0; } else if (clazz == float.class) { opcode = Opcode.FSTORE_0; } else if (clazz == double.class) { opcode = Opcode.DSTORE_0; } else { opcode = Opcode.ISTORE_0; } break; case 1: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ASTORE_1; } else if (clazz == long.class) { opcode = Opcode.LSTORE_1; } else if (clazz == float.class) { opcode = Opcode.FSTORE_1; } else if (clazz == double.class) { opcode = Opcode.DSTORE_1; } else { opcode = Opcode.ISTORE_1; } break; case 2: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ASTORE_2; } else if (clazz == long.class) { opcode = Opcode.LSTORE_2; } else if (clazz == float.class) { opcode = Opcode.FSTORE_2; } else if (clazz == double.class) { opcode = Opcode.DSTORE_2; } else { opcode = Opcode.ISTORE_2; } break; case 3: if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ASTORE_3; } else if (clazz == long.class) { opcode = Opcode.LSTORE_3; } else if (clazz == float.class) { opcode = Opcode.FSTORE_3; } else if (clazz == double.class) { opcode = Opcode.DSTORE_3; } else { opcode = Opcode.ISTORE_3; } break; default: writeIndex = true; if (clazz == null || !clazz.isPrimitive()) { opcode = Opcode.ASTORE; } else if (clazz == long.class) { opcode = Opcode.LSTORE; } else if (clazz == float.class) { opcode = Opcode.FSTORE; } else if (clazz == double.class) { opcode = Opcode.DSTORE; } else { opcode = Opcode.ISTORE; } 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; } } /************************************************************************** * Defines a ret instruction for returning from a jsr call. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class RetInstruction extends LocalOperandInstruction { public RetInstruction(LocalVariable local) { super(0, local); } public boolean isFlowThrough() { return false; } 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; } } /************************************************************************** * Defines a specialized instruction that increments a local variable by * a signed 16-bit amount. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class ShortIncrementInstruction extends LocalOperandInstruction { private short mAmount; public ShortIncrementInstruction(LocalVariable local, short amount) { super(0, local); mAmount = amount; } public boolean isFlowThrough() { return true; } 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; } } /************************************************************************** * 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. * * @author Brian S O'Neill * @version * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> */ public class SwitchInstruction extends CodeInstruction { private int[] mCases; private Location[] mLocations; private Location mDefaultLocation; private byte mOpcode; private int mSmallest; private 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); mLocations = new Location[locationsParam.length]; System.arraycopy(locationsParam, 0, mLocations, 0, locationsParam.length); mDefaultLocation = defaultLocation; // 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 RuntimeException("Duplicate switch cases: " + lastCase); } lastCase = mCases[i]; } // Now determine which kind of switch to use. mSmallest = mCases[0]; mLargest = mCases[mCases.length - 1]; int tSize = 12 + 4 * (mLargest - mSmallest + 1); int lSize = 8 + 8 * mCases.length; if (tSize <= lSize) { mOpcode = Opcode.TABLESWITCH; } else { mOpcode = Opcode.LOOKUPSWITCH; } } public Location[] getBranchTargets() { Location[] targets = new Location[mLocations.length + 1]; System.arraycopy(mLocations, 0, targets, 0, mLocations.length); targets[targets.length - 1] = mDefaultLocation; return targets; } public boolean isFlowThrough() { return false; } 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; } 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; Location tempLocation = mLocations[i]; mLocations[i] = mLocations[j]; mLocations[j] = tempLocation; } } }