0001: /*
0002: * Copyright 2004 Brian S O'Neill
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.cojen.classfile;
0018:
0019: import java.util.AbstractCollection;
0020: import java.util.ArrayList;
0021: import java.util.Collection;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027: import java.util.NoSuchElementException;
0028: import java.util.Set;
0029:
0030: /**
0031: * The InstructionList class is used by the CodeBuilder to perform lower-level
0032: * bookkeeping operations and flow analysis.
0033: *
0034: * @author Brian S O'Neill
0035: * @see CodeBuilder
0036: */
0037: class InstructionList implements CodeBuffer {
0038: private static final boolean DEBUG = false;
0039:
0040: Instruction mFirst;
0041: Instruction mLast;
0042:
0043: boolean mResolved = false;
0044:
0045: private List mExceptionHandlers = new ArrayList(4);
0046: private List mLocalVariables = new ArrayList();
0047: private int mNextFixedVariableNumber;
0048:
0049: private int mMaxStack;
0050: private int mMaxLocals;
0051:
0052: private byte[] mByteCodes;
0053: private int mBufferLength;
0054:
0055: protected InstructionList() {
0056: super ();
0057: }
0058:
0059: /**
0060: * Returns an immutable collection of all the instructions in this
0061: * InstructionList.
0062: */
0063: public Collection getInstructions() {
0064: return new AbstractCollection() {
0065: public Iterator iterator() {
0066: return new Iterator() {
0067: private Instruction mNext = mFirst;
0068:
0069: public boolean hasNext() {
0070: return mNext != null;
0071: }
0072:
0073: public Object next() {
0074: if (mNext == null) {
0075: throw new NoSuchElementException();
0076: }
0077:
0078: Instruction current = mNext;
0079: mNext = mNext.mNext;
0080: return current;
0081: }
0082:
0083: public void remove() {
0084: throw new UnsupportedOperationException();
0085: }
0086: };
0087: }
0088:
0089: public int size() {
0090: int count = 0;
0091: for (Instruction i = mFirst; i != null; i = i.mNext) {
0092: count++;
0093: }
0094: return count;
0095: }
0096: };
0097: }
0098:
0099: public int getMaxStackDepth() {
0100: resolve();
0101: return mMaxStack;
0102: }
0103:
0104: public int getMaxLocals() {
0105: resolve();
0106: return mMaxLocals;
0107: }
0108:
0109: public byte[] getByteCodes() {
0110: resolve();
0111: return mByteCodes;
0112: }
0113:
0114: public ExceptionHandler[] getExceptionHandlers() {
0115: resolve();
0116:
0117: ExceptionHandler[] handlers = new ExceptionHandler[mExceptionHandlers
0118: .size()];
0119: return (ExceptionHandler[]) mExceptionHandlers
0120: .toArray(handlers);
0121: }
0122:
0123: public void addExceptionHandler(ExceptionHandler handler) {
0124: mExceptionHandlers.add(handler);
0125: }
0126:
0127: public LocalVariable createLocalVariable(String name, TypeDesc type) {
0128: LocalVariable var = new LocalVariableImpl(mLocalVariables
0129: .size(), name, type, -1);
0130: mLocalVariables.add(var);
0131: return var;
0132: }
0133:
0134: /**
0135: * All parameters must be defined before adding instructions.
0136: */
0137: public LocalVariable createLocalParameter(String name, TypeDesc type) {
0138: LocalVariable var = new LocalVariableImpl(mLocalVariables
0139: .size(), name, type, mNextFixedVariableNumber);
0140: mLocalVariables.add(var);
0141: mNextFixedVariableNumber += type.isDoubleWord() ? 2 : 1;
0142: return var;
0143: }
0144:
0145: private void resolve() {
0146: if (mResolved) {
0147: return;
0148: }
0149:
0150: if (!DEBUG) {
0151: resolve0();
0152: } else {
0153: try {
0154: resolve0();
0155: } finally {
0156: System.out.println("-- Instructions --");
0157:
0158: Iterator it = getInstructions().iterator();
0159: while (it.hasNext()) {
0160: System.out.println(it.next());
0161: }
0162: }
0163: }
0164: }
0165:
0166: private void resolve0() {
0167: mMaxStack = 0;
0168: mMaxLocals = 0;
0169:
0170: Instruction instr;
0171:
0172: // Sweep through the instructions, preparing for flow analysis.
0173: int instrCount = 0;
0174: for (instr = mFirst; instr != null; instr = instr.mNext) {
0175: // Set address to instruction index.
0176: instr.reset(instrCount++);
0177: }
0178:
0179: // Make sure exception handlers are registered with all guarded
0180: // instructions.
0181: Iterator it = mExceptionHandlers.iterator();
0182: while (it.hasNext()) {
0183: ExceptionHandler handler = (ExceptionHandler) it.next();
0184: instr = (Instruction) handler.getStartLocation();
0185: Instruction end = (Instruction) handler.getEndLocation();
0186: for (; instr != null && instr != end; instr = instr.mNext) {
0187: instr.addExceptionHandler(handler);
0188: }
0189: }
0190:
0191: // Perform variable liveness flow analysis for each local variable, in
0192: // order to determine which register it should be assigned. Takes
0193: // advantage of the fact that instruction addresses are not yet
0194: // resolved to true addresses, but are instead indexes. This means the
0195: // liveness analysis operates on smaller BitLists, which makes some
0196: // operations (i.e. intersection) a bit faster.
0197: {
0198: int size = mLocalVariables.size();
0199: BitList[] liveIn = new BitList[size];
0200: BitList[] liveOut = new BitList[size];
0201: for (int v = 0; v < size; v++) {
0202: liveIn[v] = new BitList(instrCount);
0203: liveOut[v] = new BitList(instrCount);
0204: }
0205:
0206: livenessAnalysis(liveIn, liveOut);
0207:
0208: // Register number -> list of variables that use that register.
0209: List registerUsers = new ArrayList();
0210:
0211: // First fill up list with variables that have a fixed number.
0212: for (int v = 0; v < size; v++) {
0213: LocalVariableImpl var = (LocalVariableImpl) mLocalVariables
0214: .get(v);
0215: if (var.isFixedNumber()) {
0216: addRegisterUser(registerUsers, var);
0217: // Ensure that max locals is large enough to hold parameters.
0218: int num = var.getNumber();
0219: if (var.isDoubleWord()) {
0220: num++;
0221: }
0222: if (num >= mMaxLocals) {
0223: mMaxLocals = num + 1;
0224: }
0225: }
0226: }
0227:
0228: // Merge bit lists together.
0229: BitList[] live = liveIn;
0230: for (int v = 0; v < size; v++) {
0231: live[v].or(liveOut[v]);
0232: if (live[v].isAllClear()) {
0233: // Variable isn't needed.
0234: live[v] = null;
0235: }
0236: }
0237:
0238: for (int v = 0; v < size; v++) {
0239: if (live[v] == null) {
0240: continue;
0241: }
0242: LocalVariableImpl var = (LocalVariableImpl) mLocalVariables
0243: .get(v);
0244: if (var.isFixedNumber()) {
0245: continue;
0246: }
0247: int r = 0;
0248: while (true) {
0249: r = findAvailableRegister(registerUsers, r, live, v);
0250: if (var.isDoubleWord()) {
0251: if (findAvailableRegister(registerUsers, ++r,
0252: live, v) == r) {
0253: // Found consecutive registers, required for double word.
0254: r--;
0255: break;
0256: }
0257: } else {
0258: break;
0259: }
0260: }
0261: var.setNumber(r);
0262: addRegisterUser(registerUsers, var);
0263: }
0264:
0265: mMaxLocals = Math.max(mMaxLocals, registerUsers.size());
0266: } // end liveness analysis
0267:
0268: // Perform stack flow analysis to determine the max stack size.
0269: {
0270: // Start the flow analysis at the first instruction.
0271: Map subAdjustMap = new HashMap(11);
0272: stackResolve(0, mFirst, subAdjustMap);
0273:
0274: // Continue flow analysis into exception handler entry points.
0275: it = mExceptionHandlers.iterator();
0276: while (it.hasNext()) {
0277: ExceptionHandler handler = (ExceptionHandler) it.next();
0278: Instruction enter = (Instruction) handler
0279: .getCatchLocation();
0280: stackResolve(1, enter, subAdjustMap);
0281: }
0282: }
0283:
0284: // Okay, build up the byte code and set real instruction locations.
0285: // Multiple passes may be required because instructions may adjust
0286: // their size as locations are set. Changing size affects the
0287: // locations of other instructions, so that is why additional passes
0288: // are required.
0289:
0290: boolean passAgain;
0291: do {
0292: passAgain = false;
0293:
0294: mByteCodes = new byte[instrCount * 2]; // estimate
0295: mBufferLength = 0;
0296:
0297: for (instr = mFirst; instr != null; instr = instr.mNext) {
0298: if (!instr.isResolved()) {
0299: passAgain = true;
0300: }
0301:
0302: if (instr instanceof Label) {
0303: if (instr.mLocation != mBufferLength) {
0304: if (instr.mLocation >= 0) {
0305: // If the location of this label is not where it
0306: // should be, (most likely because an instruction
0307: // needed to expand in size) then do another pass.
0308: passAgain = true;
0309: }
0310: instr.mLocation = mBufferLength;
0311: }
0312: } else {
0313: instr.mLocation = mBufferLength;
0314:
0315: byte[] bytes = instr.getBytes();
0316: if (bytes != null) {
0317: if (passAgain) {
0318: // If there is going to be another pass, don't
0319: // bother collecting bytes into the array. Just
0320: // expand the the length variable.
0321: mBufferLength += bytes.length;
0322: } else {
0323: addBytes(bytes);
0324: }
0325: }
0326: }
0327: }
0328: } while (passAgain); // do {} while ();
0329:
0330: if (mBufferLength != mByteCodes.length) {
0331: byte[] newBytes = new byte[mBufferLength];
0332: System.arraycopy(mByteCodes, 0, newBytes, 0, mBufferLength);
0333: mByteCodes = newBytes;
0334: }
0335:
0336: // Set resolved at end because during resolution, this field gets
0337: // set false again while changes are being made to the list
0338: // of instructions.
0339: mResolved = true;
0340: }
0341:
0342: private void addBytes(byte[] code) {
0343: growBuffer(code.length);
0344: System.arraycopy(code, 0, mByteCodes, mBufferLength,
0345: code.length);
0346: mBufferLength += code.length;
0347: }
0348:
0349: private void growBuffer(int amount) {
0350: if ((mBufferLength + amount) > mByteCodes.length) {
0351: int newCapacity = mByteCodes.length * 2;
0352: if ((mBufferLength + amount) > newCapacity) {
0353: newCapacity = mBufferLength + amount;
0354: }
0355:
0356: byte[] newBuffer = new byte[newCapacity];
0357: System
0358: .arraycopy(mByteCodes, 0, newBuffer, 0,
0359: mBufferLength);
0360: mByteCodes = newBuffer;
0361: }
0362: }
0363:
0364: private void livenessAnalysis(BitList[] liveIn, BitList[] liveOut) {
0365: // Track stores to variables to see if the result is discarded.
0366: List[] localStores = new List[liveIn.length];
0367:
0368: int passCount = -1;
0369: boolean passAgain;
0370: do {
0371: passCount++;
0372: passAgain = false;
0373:
0374: for (Instruction instr = mLast; instr != null; instr = instr.mPrev) {
0375: int n = instr.getLocation();
0376:
0377: int useIndex = -1;
0378: int defIndex = -1;
0379:
0380: if (instr instanceof LocalOperandInstruction) {
0381: LocalOperandInstruction loi = (LocalOperandInstruction) instr;
0382: LocalVariableImpl var = loi.getLocalVariable();
0383: int varIndex = var.getIndex();
0384:
0385: if (loi.isLoad()) {
0386: useIndex = varIndex;
0387: }
0388:
0389: if (loi.isStore()) {
0390: defIndex = varIndex;
0391: if (passCount == 0
0392: && loi instanceof StoreLocalInstruction) {
0393: List stores = localStores[varIndex];
0394: if (stores == null) {
0395: stores = new ArrayList();
0396: localStores[varIndex] = stores;
0397: }
0398: stores.add(instr);
0399: }
0400: }
0401: }
0402:
0403: for (int v = liveIn.length; --v >= 0;) {
0404: boolean setLiveIn, setLiveOut;
0405:
0406: if (useIndex == v
0407: || (v != defIndex && liveOut[v].get(n))) {
0408: passAgain |= liveIn[v].set(n);
0409: setLiveIn = true;
0410: } else {
0411: setLiveIn = false;
0412: }
0413:
0414: setLiveOut = false;
0415:
0416: if (instr.isFlowThrough() && instr.mNext != null) {
0417: if (liveIn[v].get(instr.mNext.getLocation())) {
0418: setLiveOut = true;
0419: passAgain |= liveOut[v].set(n);
0420: }
0421: }
0422:
0423: Location[] targets = instr.getBranchTargets();
0424: if (targets != null) {
0425: for (int i = 0; i < targets.length; i++) {
0426: Instruction targetInstr = (Instruction) targets[i];
0427: if (liveIn[v]
0428: .get(targetInstr.getLocation())) {
0429: setLiveOut = true;
0430: passAgain |= liveOut[v].set(n);
0431: }
0432: }
0433: }
0434:
0435: Iterator handlers = instr.getExceptionHandlers();
0436: if (handlers != null) {
0437: while (handlers.hasNext()) {
0438: ExceptionHandler handler = (ExceptionHandler) handlers
0439: .next();
0440: Instruction catchInstr = (Instruction) handler
0441: .getCatchLocation();
0442: if (liveIn[v].get(catchInstr.getLocation())) {
0443: setLiveOut = true;
0444: passAgain |= liveOut[v].set(n);
0445: }
0446: }
0447: }
0448:
0449: if (!setLiveIn && setLiveOut && v != defIndex) {
0450: // Set liveIn entry now that liveOut has been
0451: // updated. This greatly reduces the number of full
0452: // passes required.
0453: passAgain |= liveIn[v].set(n);
0454: }
0455: }
0456: }
0457: } while (passAgain); // do {} while ();
0458:
0459: // See which local store instructions should discard their results.
0460: for (int v = localStores.length; --v >= 0;) {
0461: List stores = localStores[v];
0462: if (stores != null) {
0463: for (int i = stores.size(); --i >= 0;) {
0464: StoreLocalInstruction instr = (StoreLocalInstruction) stores
0465: .get(i);
0466: if (!liveOut[v].get(instr.getLocation())) {
0467: instr.discardResult();
0468: }
0469: }
0470: }
0471: }
0472: }
0473:
0474: private void addRegisterUser(List registerUsers, LocalVariable var) {
0475: int num = var.getNumber();
0476: if (num < 0) {
0477: throw new IllegalStateException(
0478: "Local variable number not resolved");
0479: }
0480: getRegisterUsers(registerUsers, num).add(var);
0481: if (var.isDoubleWord()) {
0482: getRegisterUsers(registerUsers, num + 1).add(var);
0483: }
0484: }
0485:
0486: private List getRegisterUsers(List registerUsers, int num) {
0487: while (registerUsers.size() <= num) {
0488: registerUsers.add(new ArrayList());
0489: }
0490: return (List) registerUsers.get(num);
0491: }
0492:
0493: /**
0494: * @param registerUsers
0495: * @param r index into registerUsers
0496: * @return index into registerUsers which is available, which may be equal
0497: * to r or equal to the size of registerUsers
0498: */
0499: private int findAvailableRegister(List registerUsers, int r,
0500: BitList[] live, int v) {
0501: registerScan: for (; r < registerUsers.size(); r++) {
0502: List users = getRegisterUsers(registerUsers, r);
0503: for (int i = 0; i < users.size(); i++) {
0504: int v2 = ((LocalVariableImpl) users.get(i)).getIndex();
0505: if (live[v].intersects(live[v2])) {
0506: continue registerScan;
0507: }
0508: }
0509: break;
0510: }
0511: return r;
0512: }
0513:
0514: private int stackResolve(int stackDepth, Instruction instr,
0515: Map subAdjustMap) {
0516: while (instr != null) {
0517: // Set the stack depth, marking this instruction as being visited.
0518: // If already visited, break out of this flow.
0519: if (instr.mStackDepth < 0) {
0520: instr.mStackDepth = stackDepth;
0521: } else {
0522: if (instr.mStackDepth != stackDepth) {
0523: throw new IllegalStateException(
0524: "Stack depth different at previously visited "
0525: + "instruction: "
0526: + instr.mStackDepth + " != "
0527: + stackDepth);
0528: }
0529:
0530: break;
0531: }
0532:
0533: // Determine the next instruction to flow down to.
0534: Instruction next = null;
0535:
0536: if (instr.isFlowThrough()) {
0537: if ((next = instr.mNext) == null) {
0538: throw new IllegalStateException(
0539: "Execution flows through end of method");
0540: }
0541: }
0542:
0543: stackDepth += instr.getStackAdjustment();
0544: if (stackDepth > mMaxStack) {
0545: mMaxStack = stackDepth;
0546: } else if (stackDepth < 0) {
0547: throw new IllegalStateException(
0548: "Stack depth is negative: " + stackDepth);
0549: }
0550:
0551: Location[] targets = instr.getBranchTargets();
0552:
0553: if (targets != null) {
0554: for (int i = 0; i < targets.length; i++) {
0555: LabelInstruction targetInstr = (LabelInstruction) targets[i];
0556:
0557: if (i == 0 && next == null) {
0558: // Flow to the first target if instruction doesn't
0559: // flow to its next instruction.
0560: next = targetInstr;
0561: continue;
0562: }
0563:
0564: if (!instr.isSubroutineCall()) {
0565: stackResolve(stackDepth, targetInstr,
0566: subAdjustMap);
0567: } else {
0568: Integer subAdjust = (Integer) subAdjustMap
0569: .get(targetInstr);
0570:
0571: if (subAdjust == null) {
0572: int newDepth = stackResolve(stackDepth,
0573: targetInstr, subAdjustMap);
0574: subAdjust = new Integer(newDepth
0575: - stackDepth);
0576: subAdjustMap.put(targetInstr, subAdjust);
0577: }
0578:
0579: stackDepth += subAdjust.intValue();
0580: }
0581: }
0582: }
0583:
0584: instr = next;
0585: }
0586:
0587: return stackDepth;
0588: }
0589:
0590: private class LocalVariableImpl implements LocalVariable {
0591: private final int mIndex;
0592:
0593: private String mName;
0594: private TypeDesc mType;
0595:
0596: private int mNumber;
0597: private boolean mFixed;
0598:
0599: public LocalVariableImpl(int index, String name, TypeDesc type,
0600: int number) {
0601: mIndex = index;
0602: mName = name;
0603: mType = type;
0604: mNumber = number;
0605: if (number >= 0) {
0606: mFixed = true;
0607: }
0608: }
0609:
0610: int getIndex() {
0611: return mIndex;
0612: }
0613:
0614: /**
0615: * May return null if this LocalVariable is unnamed.
0616: */
0617: public String getName() {
0618: return mName;
0619: }
0620:
0621: public void setName(String name) {
0622: mName = name;
0623: }
0624:
0625: public TypeDesc getType() {
0626: return mType;
0627: }
0628:
0629: public boolean isDoubleWord() {
0630: return mType.isDoubleWord();
0631: }
0632:
0633: public int getNumber() {
0634: return mNumber;
0635: }
0636:
0637: public Set getLocationRangeSet() {
0638: // TODO
0639: return null;
0640: }
0641:
0642: public void setNumber(int number) {
0643: mNumber = number;
0644: }
0645:
0646: public void setFixedNumber(int number) {
0647: mNumber = number;
0648: mFixed = true;
0649: }
0650:
0651: public boolean isFixedNumber() {
0652: return mFixed;
0653: }
0654:
0655: public String toString() {
0656: if (getName() != null) {
0657: return String.valueOf(getType()) + ' ' + getName();
0658: } else {
0659: return String.valueOf(getType());
0660: }
0661: }
0662: }
0663:
0664: /////////////////////////////////////////////////////////////////////////
0665: //
0666: // Begin inner class definitions for instructions of the InstructionList.
0667: //
0668: /////////////////////////////////////////////////////////////////////////
0669:
0670: /**
0671: * An Instruction is an element in an InstructionList, and represents a
0672: * Java byte code instruction.
0673: */
0674: public abstract class Instruction implements Location {
0675: private int mStackAdjust;
0676:
0677: Instruction mPrev;
0678: Instruction mNext;
0679:
0680: // Indicates what the stack depth is when this instruction is reached.
0681: // Is -1 if not reached. Flow analysis sets this value.
0682: int mStackDepth = -1;
0683:
0684: // Indicates the address of this instruction is, or -1 if not known.
0685: int mLocation = -1;
0686:
0687: private Set mExceptionHandlers;
0688:
0689: /**
0690: * Newly created instructions are automatically added to the
0691: * InstructionList.
0692: */
0693: public Instruction(int stackAdjust) {
0694: mStackAdjust = stackAdjust;
0695: add();
0696: }
0697:
0698: /**
0699: * This constructor allows sub-classes to disable auto-adding to the
0700: * InstructionList.
0701: */
0702: protected Instruction(int stackAdjust, boolean addInstruction) {
0703: mStackAdjust = stackAdjust;
0704:
0705: if (addInstruction) {
0706: add();
0707: }
0708: }
0709:
0710: /**
0711: * Add this instruction to the end of the InstructionList. If the
0712: * Instruction is already in the list, then it is moved to the end.
0713: */
0714: protected void add() {
0715: InstructionList.this .mResolved = false;
0716:
0717: if (mPrev != null) {
0718: mPrev.mNext = mNext;
0719: }
0720:
0721: if (mNext != null) {
0722: mNext.mPrev = mPrev;
0723: }
0724:
0725: mNext = null;
0726:
0727: if (InstructionList.this .mFirst == null) {
0728: mPrev = null;
0729: InstructionList.this .mFirst = this ;
0730: } else {
0731: mPrev = InstructionList.this .mLast;
0732: InstructionList.this .mLast.mNext = this ;
0733: }
0734:
0735: InstructionList.this .mLast = this ;
0736: }
0737:
0738: /**
0739: * Insert an Instruction immediately following this one.
0740: */
0741: public void insert(Instruction instr) {
0742: InstructionList.this .mResolved = false;
0743:
0744: instr.mPrev = this ;
0745: instr.mNext = mNext;
0746:
0747: mNext = instr;
0748:
0749: if (this == InstructionList.this .mLast) {
0750: InstructionList.this .mLast = instr;
0751: }
0752: }
0753:
0754: /**
0755: * Removes this Instruction from its parent InstructionList.
0756: */
0757: public void remove() {
0758: InstructionList.this .mResolved = false;
0759:
0760: if (mPrev != null) {
0761: mPrev.mNext = mNext;
0762: }
0763:
0764: if (mNext != null) {
0765: mNext.mPrev = mPrev;
0766: }
0767:
0768: if (this == InstructionList.this .mFirst) {
0769: InstructionList.this .mFirst = mNext;
0770: }
0771:
0772: if (this == InstructionList.this .mLast) {
0773: InstructionList.this .mLast = mPrev;
0774: }
0775:
0776: mPrev = null;
0777: mNext = null;
0778: }
0779:
0780: /**
0781: * Replace this Instruction with another one.
0782: */
0783: public void replace(Instruction replacement) {
0784: if (replacement == null) {
0785: remove();
0786: return;
0787: }
0788:
0789: InstructionList.this .mResolved = false;
0790:
0791: replacement.mPrev = mPrev;
0792: replacement.mNext = mNext;
0793:
0794: if (mPrev != null) {
0795: mPrev.mNext = replacement;
0796: }
0797:
0798: if (mNext != null) {
0799: mNext.mPrev = replacement;
0800: }
0801:
0802: if (this == InstructionList.this .mFirst) {
0803: InstructionList.this .mFirst = replacement;
0804: }
0805:
0806: if (this == InstructionList.this .mLast) {
0807: InstructionList.this .mLast = replacement;
0808: }
0809: }
0810:
0811: /**
0812: * Returns a positive, negative or zero value indicating what affect
0813: * this generated instruction has on the runtime stack.
0814: */
0815: public int getStackAdjustment() {
0816: return mStackAdjust;
0817: }
0818:
0819: /**
0820: * Returns the stack depth for when this instruction is reached. If the
0821: * value is negative, then this instruction is never reached.
0822: */
0823: public int getStackDepth() {
0824: return mStackDepth;
0825: }
0826:
0827: /**
0828: * Returns the address of this instruction or -1 if not known.
0829: */
0830: public int getLocation() {
0831: return mLocation;
0832: }
0833:
0834: /**
0835: * Returns all of the targets that this instruction may branch to. Not
0836: * all instructions support branching, and null is returned by default.
0837: */
0838: public Location[] getBranchTargets() {
0839: return null;
0840: }
0841:
0842: /**
0843: * Returns an all the exception handlers that wraps this instruction,
0844: * or null if none.
0845: */
0846: public Iterator getExceptionHandlers() {
0847: if (mExceptionHandlers == null) {
0848: return null;
0849: }
0850: return mExceptionHandlers.iterator();
0851: }
0852:
0853: /**
0854: * Adds an exception handler that wraps this instruction.
0855: */
0856: public void addExceptionHandler(ExceptionHandler handler) {
0857: if (mExceptionHandlers == null) {
0858: mExceptionHandlers = new HashSet(4);
0859: }
0860: mExceptionHandlers.add(handler);
0861: }
0862:
0863: /**
0864: * Returns true if execution flow may continue after this instruction.
0865: * It may be a goto, a method return, an exception throw or a
0866: * subroutine return. Default implementation returns true.
0867: */
0868: public boolean isFlowThrough() {
0869: return true;
0870: }
0871:
0872: public boolean isSubroutineCall() {
0873: return false;
0874: }
0875:
0876: /**
0877: * Returns null if this is a pseudo instruction and no bytes are
0878: * generated.
0879: */
0880: public abstract byte[] getBytes();
0881:
0882: /**
0883: * An instruction is resolved when it has all information needed to
0884: * generate correct byte code.
0885: */
0886: public abstract boolean isResolved();
0887:
0888: public int compareTo(Object obj) {
0889: if (this == obj) {
0890: return 0;
0891: }
0892: Location other = (Location) obj;
0893:
0894: int loca = getLocation();
0895: int locb = other.getLocation();
0896:
0897: if (loca < locb) {
0898: return -1;
0899: } else if (loca > locb) {
0900: return 1;
0901: } else {
0902: return 0;
0903: }
0904: }
0905:
0906: /**
0907: * Returns a string containing the type of this instruction, the stack
0908: * adjustment and the list of byte codes. Unvisted instructions are
0909: * marked with an asterisk.
0910: */
0911: public String toString() {
0912: String name = getClass().getName();
0913: int index = name.lastIndexOf('.');
0914: if (index >= 0) {
0915: name = name.substring(index + 1);
0916: }
0917: index = name.lastIndexOf('$');
0918: if (index >= 0) {
0919: name = name.substring(index + 1);
0920: }
0921:
0922: StringBuffer buf = new StringBuffer(name.length() + 20);
0923:
0924: int adjust = getStackAdjustment();
0925: int depth = getStackDepth();
0926:
0927: if (depth >= 0) {
0928: buf.append(' ');
0929: } else {
0930: buf.append('*');
0931: }
0932:
0933: buf.append('[');
0934: buf.append(mLocation);
0935: buf.append("] ");
0936:
0937: buf.append(name);
0938: buf.append(" (");
0939:
0940: if (depth >= 0) {
0941: buf.append(depth);
0942: buf.append(" + ");
0943: buf.append(adjust);
0944: buf.append(" = ");
0945: buf.append(depth + adjust);
0946: } else {
0947: buf.append(adjust);
0948: }
0949:
0950: buf.append(") ");
0951:
0952: try {
0953: byte[] bytes = getBytes();
0954: boolean wide = false;
0955: if (bytes != null) {
0956: for (int i = 0; i < bytes.length; i++) {
0957: if (i > 0) {
0958: buf.append(',');
0959: }
0960:
0961: byte code = bytes[i];
0962:
0963: if (i == 0 || wide) {
0964: buf.append(Opcode.getMnemonic(code));
0965: wide = code == Opcode.WIDE;
0966: } else {
0967: buf.append(code & 0xff);
0968: }
0969: }
0970: }
0971: } catch (Exception e) {
0972: }
0973:
0974: return buf.toString();
0975: }
0976:
0977: /**
0978: * Reset this instruction in preparation for flow analysis.
0979: */
0980: void reset(int instrCount) {
0981: mStackDepth = -1;
0982: // Start with a fake location.
0983: mLocation = instrCount;
0984: }
0985: }
0986:
0987: /**
0988: * Defines a pseudo instruction for a label. No byte code is ever generated
0989: * from a label. Labels are not automatically added to the list.
0990: */
0991: public class LabelInstruction extends Instruction implements Label {
0992: public LabelInstruction() {
0993: super (0, false);
0994: }
0995:
0996: /**
0997: * Set this label's branch location to be the current address
0998: * in this label's parent CodeBuilder or InstructionList.
0999: *
1000: * @return This Label.
1001: */
1002: public Label setLocation() {
1003: add();
1004: return this ;
1005: }
1006:
1007: /**
1008: * @return -1 when not resolved yet
1009: */
1010: public int getLocation() throws IllegalStateException {
1011: int loc;
1012: if ((loc = mLocation) < 0) {
1013: if (mPrev == null && mNext == null) {
1014: throw new IllegalStateException(
1015: "Label location is not set");
1016: }
1017: }
1018: return loc;
1019: }
1020:
1021: /**
1022: * Always returns null.
1023: */
1024: public byte[] getBytes() {
1025: return null;
1026: }
1027:
1028: public boolean isResolved() {
1029: return getLocation() >= 0;
1030: }
1031: }
1032:
1033: /**
1034: * Defines a code instruction and has storage for byte codes.
1035: */
1036: public class CodeInstruction extends Instruction {
1037: protected byte[] mBytes;
1038:
1039: private Set mExceptionHandlers;
1040:
1041: public CodeInstruction(int stackAdjust) {
1042: super (stackAdjust);
1043: }
1044:
1045: protected CodeInstruction(int stackAdjust,
1046: boolean addInstruction) {
1047: super (stackAdjust, addInstruction);
1048: }
1049:
1050: public CodeInstruction(int stackAdjust, byte b) {
1051: super (stackAdjust);
1052: mBytes = new byte[] { b };
1053: }
1054:
1055: public CodeInstruction(int stackAdjust, byte[] bytes) {
1056: super (stackAdjust);
1057: mBytes = bytes;
1058: }
1059:
1060: public boolean isFlowThrough() {
1061: if (mBytes != null && mBytes.length > 0) {
1062: switch (mBytes[0]) {
1063: case Opcode.GOTO:
1064: case Opcode.GOTO_W:
1065: case Opcode.IRETURN:
1066: case Opcode.LRETURN:
1067: case Opcode.FRETURN:
1068: case Opcode.DRETURN:
1069: case Opcode.ARETURN:
1070: case Opcode.RETURN:
1071: case Opcode.ATHROW:
1072: return false;
1073: }
1074: }
1075:
1076: return true;
1077: }
1078:
1079: public byte[] getBytes() {
1080: return mBytes;
1081: }
1082:
1083: public boolean isResolved() {
1084: return true;
1085: }
1086: }
1087:
1088: /**
1089: * Defines a branch instruction, like a goto, jsr or any conditional
1090: * branch.
1091: */
1092: public class BranchInstruction extends CodeInstruction {
1093: private Location mTarget;
1094: private boolean mHasShortHop = false;
1095: private boolean mIsSub = false;
1096:
1097: public BranchInstruction(int stackAdjust, byte opcode,
1098: Location target) {
1099: this (stackAdjust, true, opcode, target);
1100: }
1101:
1102: private BranchInstruction(int stackAdjust,
1103: boolean addInstruction, byte opcode, Location target) {
1104: super (stackAdjust, addInstruction);
1105:
1106: mTarget = target;
1107:
1108: switch (opcode) {
1109: case Opcode.JSR_W:
1110: mIsSub = true;
1111: // Flow through to next case.
1112: case Opcode.GOTO_W:
1113: mBytes = new byte[5];
1114: mBytes[0] = opcode;
1115: break;
1116: case Opcode.JSR:
1117: mIsSub = true;
1118: // Flow through to next case.
1119: case Opcode.GOTO:
1120: case Opcode.IF_ACMPEQ:
1121: case Opcode.IF_ACMPNE:
1122: case Opcode.IF_ICMPEQ:
1123: case Opcode.IF_ICMPNE:
1124: case Opcode.IF_ICMPLT:
1125: case Opcode.IF_ICMPGE:
1126: case Opcode.IF_ICMPGT:
1127: case Opcode.IF_ICMPLE:
1128: case Opcode.IFEQ:
1129: case Opcode.IFNE:
1130: case Opcode.IFLT:
1131: case Opcode.IFGE:
1132: case Opcode.IFGT:
1133: case Opcode.IFLE:
1134: case Opcode.IFNONNULL:
1135: case Opcode.IFNULL:
1136: mBytes = new byte[3];
1137: mBytes[0] = opcode;
1138: break;
1139: default:
1140: throw new IllegalArgumentException(
1141: "Opcode not a branch instruction: "
1142: + Opcode.getMnemonic(opcode));
1143: }
1144: }
1145:
1146: public Location[] getBranchTargets() {
1147: return new Location[] { mTarget };
1148: }
1149:
1150: public boolean isSubroutineCall() {
1151: return mIsSub;
1152: }
1153:
1154: public byte[] getBytes() {
1155: if (!isResolved() || mHasShortHop) {
1156: return mBytes;
1157: }
1158:
1159: int offset = mTarget.getLocation() - mLocation;
1160: byte opcode = mBytes[0];
1161:
1162: if (opcode == Opcode.GOTO_W || opcode == Opcode.JSR_W) {
1163: mBytes[1] = (byte) (offset >> 24);
1164: mBytes[2] = (byte) (offset >> 16);
1165: mBytes[3] = (byte) (offset >> 8);
1166: mBytes[4] = (byte) (offset >> 0);
1167: } else if (-32768 <= offset && offset <= 32767) {
1168: mBytes[1] = (byte) (offset >> 8);
1169: mBytes[2] = (byte) (offset >> 0);
1170: } else if (opcode == Opcode.GOTO || opcode == Opcode.JSR) {
1171: mBytes = new byte[5];
1172: if (opcode == Opcode.GOTO) {
1173: mBytes[0] = Opcode.GOTO_W;
1174: } else {
1175: mBytes[0] = Opcode.JSR_W;
1176: }
1177: mBytes[1] = (byte) (offset >> 24);
1178: mBytes[2] = (byte) (offset >> 16);
1179: mBytes[3] = (byte) (offset >> 8);
1180: mBytes[4] = (byte) (offset >> 0);
1181: } else {
1182: // The if branch requires a 32 bit offset.
1183:
1184: // Convert:
1185: //
1186: // if <cond> goto target
1187: // // reached if <cond> false
1188: // target: // reached if <cond> true
1189:
1190: // to this:
1191: //
1192: // if not <cond> goto shortHop
1193: // goto_w target
1194: // shortHop: // reached if <cond> false
1195: // target: // reached if <cond> true
1196:
1197: mHasShortHop = true;
1198:
1199: opcode = Opcode.reverseIfOpcode(opcode);
1200:
1201: mBytes[0] = opcode;
1202: mBytes[1] = (byte) 0;
1203: mBytes[2] = (byte) 8;
1204:
1205: // insert goto_w instruction after this one.
1206: insert(new BranchInstruction(0, false, Opcode.GOTO_W,
1207: mTarget));
1208: }
1209:
1210: return mBytes;
1211: }
1212:
1213: public boolean isResolved() {
1214: return mTarget.getLocation() >= 0;
1215: }
1216: }
1217:
1218: /**
1219: * Defines an instruction that has a single operand which references a
1220: * constant in the constant pool.
1221: */
1222: public class ConstantOperandInstruction extends CodeInstruction {
1223: private ConstantInfo mInfo;
1224:
1225: public ConstantOperandInstruction(int stackAdjust,
1226: byte[] bytes, ConstantInfo info) {
1227: super (stackAdjust, bytes);
1228: mInfo = info;
1229: }
1230:
1231: public byte[] getBytes() {
1232: int index = mInfo.getIndex();
1233:
1234: if (index < 0) {
1235: throw new IllegalStateException(
1236: "Constant pool index not resolved");
1237: }
1238:
1239: mBytes[1] = (byte) (index >> 8);
1240: mBytes[2] = (byte) index;
1241:
1242: return mBytes;
1243: }
1244:
1245: public boolean isResolved() {
1246: return mInfo.getIndex() >= 0;
1247: }
1248: }
1249:
1250: /**
1251: * Defines an instruction that loads a constant onto the stack from the
1252: * constant pool.
1253: */
1254: public class LoadConstantInstruction extends CodeInstruction {
1255: private ConstantInfo mInfo;
1256: private boolean mWideOnly;
1257:
1258: public LoadConstantInstruction(int stackAdjust,
1259: ConstantInfo info) {
1260: this (stackAdjust, info, false);
1261: }
1262:
1263: public LoadConstantInstruction(int stackAdjust,
1264: ConstantInfo info, boolean wideOnly) {
1265: super (stackAdjust);
1266: mInfo = info;
1267: mWideOnly = wideOnly;
1268: }
1269:
1270: public boolean isFlowThrough() {
1271: return true;
1272: }
1273:
1274: public byte[] getBytes() {
1275: int index = mInfo.getIndex();
1276:
1277: if (index < 0) {
1278: throw new IllegalStateException(
1279: "Constant pool index not resolved");
1280: }
1281:
1282: if (mWideOnly) {
1283: byte[] bytes = new byte[3];
1284: bytes[0] = Opcode.LDC2_W;
1285: bytes[1] = (byte) (index >> 8);
1286: bytes[2] = (byte) index;
1287: return bytes;
1288: } else if (index <= 255) {
1289: byte[] bytes = new byte[2];
1290: bytes[0] = Opcode.LDC;
1291: bytes[1] = (byte) index;
1292: return bytes;
1293: } else {
1294: byte[] bytes = new byte[3];
1295: bytes[0] = Opcode.LDC_W;
1296: bytes[1] = (byte) (index >> 8);
1297: bytes[2] = (byte) index;
1298: return bytes;
1299: }
1300: }
1301:
1302: public boolean isResolved() {
1303: return mInfo.getIndex() >= 0;
1304: }
1305: }
1306:
1307: /**
1308: * Defines an instruction that contains an operand for referencing a
1309: * LocalVariable.
1310: */
1311: public abstract class LocalOperandInstruction extends
1312: CodeInstruction {
1313: protected LocalVariableImpl mLocal;
1314:
1315: public LocalOperandInstruction(int stackAdjust,
1316: LocalVariable local) {
1317: super (stackAdjust);
1318: mLocal = (LocalVariableImpl) local;
1319: }
1320:
1321: public boolean isResolved() {
1322: return mLocal.getNumber() >= 0;
1323: }
1324:
1325: public LocalVariableImpl getLocalVariable() {
1326: return mLocal;
1327: }
1328:
1329: public int getVariableNumber() {
1330: int varNum = mLocal.getNumber();
1331:
1332: if (varNum < 0) {
1333: throw new IllegalStateException(
1334: "Local variable number not resolved");
1335: }
1336:
1337: return varNum;
1338: }
1339:
1340: public abstract boolean isLoad();
1341:
1342: public abstract boolean isStore();
1343: }
1344:
1345: /**
1346: * Defines an instruction that loads a local variable onto the stack.
1347: */
1348: public class LoadLocalInstruction extends LocalOperandInstruction {
1349: public LoadLocalInstruction(int stackAdjust, LocalVariable local) {
1350: super (stackAdjust, local);
1351: }
1352:
1353: public boolean isFlowThrough() {
1354: return true;
1355: }
1356:
1357: public byte[] getBytes() {
1358: int varNum = getVariableNumber();
1359: byte opcode;
1360: boolean writeIndex = false;
1361:
1362: int typeCode = mLocal.getType().getTypeCode();
1363:
1364: switch (varNum) {
1365: case 0:
1366: switch (typeCode) {
1367: default:
1368: opcode = Opcode.ALOAD_0;
1369: break;
1370: case TypeDesc.LONG_CODE:
1371: opcode = Opcode.LLOAD_0;
1372: break;
1373: case TypeDesc.FLOAT_CODE:
1374: opcode = Opcode.FLOAD_0;
1375: break;
1376: case TypeDesc.DOUBLE_CODE:
1377: opcode = Opcode.DLOAD_0;
1378: break;
1379: case TypeDesc.INT_CODE:
1380: case TypeDesc.BOOLEAN_CODE:
1381: case TypeDesc.BYTE_CODE:
1382: case TypeDesc.CHAR_CODE:
1383: case TypeDesc.SHORT_CODE:
1384: opcode = Opcode.ILOAD_0;
1385: break;
1386: }
1387: break;
1388: case 1:
1389: switch (typeCode) {
1390: default:
1391: opcode = Opcode.ALOAD_1;
1392: break;
1393: case TypeDesc.LONG_CODE:
1394: opcode = Opcode.LLOAD_1;
1395: break;
1396: case TypeDesc.FLOAT_CODE:
1397: opcode = Opcode.FLOAD_1;
1398: break;
1399: case TypeDesc.DOUBLE_CODE:
1400: opcode = Opcode.DLOAD_1;
1401: break;
1402: case TypeDesc.INT_CODE:
1403: case TypeDesc.BOOLEAN_CODE:
1404: case TypeDesc.BYTE_CODE:
1405: case TypeDesc.CHAR_CODE:
1406: case TypeDesc.SHORT_CODE:
1407: opcode = Opcode.ILOAD_1;
1408: break;
1409: }
1410: break;
1411: case 2:
1412: switch (typeCode) {
1413: default:
1414: opcode = Opcode.ALOAD_2;
1415: break;
1416: case TypeDesc.LONG_CODE:
1417: opcode = Opcode.LLOAD_2;
1418: break;
1419: case TypeDesc.FLOAT_CODE:
1420: opcode = Opcode.FLOAD_2;
1421: break;
1422: case TypeDesc.DOUBLE_CODE:
1423: opcode = Opcode.DLOAD_2;
1424: break;
1425: case TypeDesc.INT_CODE:
1426: case TypeDesc.BOOLEAN_CODE:
1427: case TypeDesc.BYTE_CODE:
1428: case TypeDesc.CHAR_CODE:
1429: case TypeDesc.SHORT_CODE:
1430: opcode = Opcode.ILOAD_2;
1431: break;
1432: }
1433: break;
1434: case 3:
1435: switch (typeCode) {
1436: default:
1437: opcode = Opcode.ALOAD_3;
1438: break;
1439: case TypeDesc.LONG_CODE:
1440: opcode = Opcode.LLOAD_3;
1441: break;
1442: case TypeDesc.FLOAT_CODE:
1443: opcode = Opcode.FLOAD_3;
1444: break;
1445: case TypeDesc.DOUBLE_CODE:
1446: opcode = Opcode.DLOAD_3;
1447: break;
1448: case TypeDesc.INT_CODE:
1449: case TypeDesc.BOOLEAN_CODE:
1450: case TypeDesc.BYTE_CODE:
1451: case TypeDesc.CHAR_CODE:
1452: case TypeDesc.SHORT_CODE:
1453: opcode = Opcode.ILOAD_3;
1454: break;
1455: }
1456: break;
1457: default:
1458: writeIndex = true;
1459:
1460: switch (typeCode) {
1461: default:
1462: opcode = Opcode.ALOAD;
1463: break;
1464: case TypeDesc.LONG_CODE:
1465: opcode = Opcode.LLOAD;
1466: break;
1467: case TypeDesc.FLOAT_CODE:
1468: opcode = Opcode.FLOAD;
1469: break;
1470: case TypeDesc.DOUBLE_CODE:
1471: opcode = Opcode.DLOAD;
1472: break;
1473: case TypeDesc.INT_CODE:
1474: case TypeDesc.BOOLEAN_CODE:
1475: case TypeDesc.BYTE_CODE:
1476: case TypeDesc.CHAR_CODE:
1477: case TypeDesc.SHORT_CODE:
1478: opcode = Opcode.ILOAD;
1479: break;
1480: }
1481: break;
1482: }
1483:
1484: if (!writeIndex) {
1485: mBytes = new byte[] { opcode };
1486: } else {
1487: if (varNum <= 255) {
1488: mBytes = new byte[] { opcode, (byte) varNum };
1489: } else {
1490: mBytes = new byte[] { Opcode.WIDE, opcode,
1491: (byte) (varNum >> 8), (byte) varNum };
1492: }
1493: }
1494:
1495: return mBytes;
1496: }
1497:
1498: public boolean isLoad() {
1499: return true;
1500: }
1501:
1502: public boolean isStore() {
1503: return false;
1504: }
1505: }
1506:
1507: /**
1508: * Defines an instruction that stores a value from the stack into a local
1509: * variable.
1510: */
1511: public class StoreLocalInstruction extends LocalOperandInstruction {
1512: private boolean mDiscardResult;
1513:
1514: public StoreLocalInstruction(int stackAdjust,
1515: LocalVariable local) {
1516: super (stackAdjust, local);
1517: }
1518:
1519: public boolean isFlowThrough() {
1520: return true;
1521: }
1522:
1523: public byte[] getBytes() {
1524: if (mDiscardResult) {
1525: // Liveness analysis discovered that the results of this store
1526: // are not needed so just pop if off the stack.
1527: return new byte[] { mLocal.isDoubleWord() ? Opcode.POP2
1528: : Opcode.POP };
1529: }
1530:
1531: int varNum = getVariableNumber();
1532:
1533: byte opcode;
1534: boolean writeIndex = false;
1535:
1536: int typeCode = mLocal.getType().getTypeCode();
1537:
1538: switch (varNum) {
1539: case 0:
1540: switch (typeCode) {
1541: default:
1542: opcode = Opcode.ASTORE_0;
1543: break;
1544: case TypeDesc.LONG_CODE:
1545: opcode = Opcode.LSTORE_0;
1546: break;
1547: case TypeDesc.FLOAT_CODE:
1548: opcode = Opcode.FSTORE_0;
1549: break;
1550: case TypeDesc.DOUBLE_CODE:
1551: opcode = Opcode.DSTORE_0;
1552: break;
1553: case TypeDesc.INT_CODE:
1554: case TypeDesc.BOOLEAN_CODE:
1555: case TypeDesc.BYTE_CODE:
1556: case TypeDesc.CHAR_CODE:
1557: case TypeDesc.SHORT_CODE:
1558: opcode = Opcode.ISTORE_0;
1559: break;
1560: }
1561: break;
1562: case 1:
1563: switch (typeCode) {
1564: default:
1565: opcode = Opcode.ASTORE_1;
1566: break;
1567: case TypeDesc.LONG_CODE:
1568: opcode = Opcode.LSTORE_1;
1569: break;
1570: case TypeDesc.FLOAT_CODE:
1571: opcode = Opcode.FSTORE_1;
1572: break;
1573: case TypeDesc.DOUBLE_CODE:
1574: opcode = Opcode.DSTORE_1;
1575: break;
1576: case TypeDesc.INT_CODE:
1577: case TypeDesc.BOOLEAN_CODE:
1578: case TypeDesc.BYTE_CODE:
1579: case TypeDesc.CHAR_CODE:
1580: case TypeDesc.SHORT_CODE:
1581: opcode = Opcode.ISTORE_1;
1582: break;
1583: }
1584: break;
1585: case 2:
1586: switch (typeCode) {
1587: default:
1588: opcode = Opcode.ASTORE_2;
1589: break;
1590: case TypeDesc.LONG_CODE:
1591: opcode = Opcode.LSTORE_2;
1592: break;
1593: case TypeDesc.FLOAT_CODE:
1594: opcode = Opcode.FSTORE_2;
1595: break;
1596: case TypeDesc.DOUBLE_CODE:
1597: opcode = Opcode.DSTORE_2;
1598: break;
1599: case TypeDesc.INT_CODE:
1600: case TypeDesc.BOOLEAN_CODE:
1601: case TypeDesc.BYTE_CODE:
1602: case TypeDesc.CHAR_CODE:
1603: case TypeDesc.SHORT_CODE:
1604: opcode = Opcode.ISTORE_2;
1605: break;
1606: }
1607: break;
1608: case 3:
1609: switch (typeCode) {
1610: default:
1611: opcode = Opcode.ASTORE_3;
1612: break;
1613: case TypeDesc.LONG_CODE:
1614: opcode = Opcode.LSTORE_3;
1615: break;
1616: case TypeDesc.FLOAT_CODE:
1617: opcode = Opcode.FSTORE_3;
1618: break;
1619: case TypeDesc.DOUBLE_CODE:
1620: opcode = Opcode.DSTORE_3;
1621: break;
1622: case TypeDesc.INT_CODE:
1623: case TypeDesc.BOOLEAN_CODE:
1624: case TypeDesc.BYTE_CODE:
1625: case TypeDesc.CHAR_CODE:
1626: case TypeDesc.SHORT_CODE:
1627: opcode = Opcode.ISTORE_3;
1628: break;
1629: }
1630: break;
1631: default:
1632: writeIndex = true;
1633:
1634: switch (typeCode) {
1635: default:
1636: opcode = Opcode.ASTORE;
1637: break;
1638: case TypeDesc.LONG_CODE:
1639: opcode = Opcode.LSTORE;
1640: break;
1641: case TypeDesc.FLOAT_CODE:
1642: opcode = Opcode.FSTORE;
1643: break;
1644: case TypeDesc.DOUBLE_CODE:
1645: opcode = Opcode.DSTORE;
1646: break;
1647: case TypeDesc.INT_CODE:
1648: case TypeDesc.BOOLEAN_CODE:
1649: case TypeDesc.BYTE_CODE:
1650: case TypeDesc.CHAR_CODE:
1651: case TypeDesc.SHORT_CODE:
1652: opcode = Opcode.ISTORE;
1653: break;
1654: }
1655: break;
1656: }
1657:
1658: if (!writeIndex) {
1659: mBytes = new byte[] { opcode };
1660: } else {
1661: if (varNum <= 255) {
1662: mBytes = new byte[] { opcode, (byte) varNum };
1663: } else {
1664: mBytes = new byte[] { Opcode.WIDE, opcode,
1665: (byte) (varNum >> 8), (byte) varNum };
1666: }
1667: }
1668:
1669: return mBytes;
1670: }
1671:
1672: public boolean isResolved() {
1673: return true;
1674: }
1675:
1676: public boolean isLoad() {
1677: return false;
1678: }
1679:
1680: public boolean isStore() {
1681: return true;
1682: }
1683:
1684: public void discardResult() {
1685: mDiscardResult = true;
1686: }
1687: }
1688:
1689: /**
1690: * Defines a ret instruction for returning from a jsr call.
1691: */
1692: public class RetInstruction extends LocalOperandInstruction {
1693: // Note: This instruction does not provide any branch targets. The
1694: // analysis for determining all possible return locations is
1695: // complicated. Instead, the stack flow analysis assumes that all jsr
1696: // calls are "well formed", and so it doesn't need to follow the ret
1697: // back to a "comes from" label. Liveness analysis could take advantage
1698: // of the branch targets, and reduce the set of variables used to
1699: // manage jsr return addresses. Since jsr/ret is used infrequently,
1700: // local variables used by ret are fixed and are not optimized.
1701:
1702: public RetInstruction(LocalVariable local) {
1703: super (0, local);
1704: ((LocalVariableImpl) local)
1705: .setFixedNumber(mNextFixedVariableNumber++);
1706: }
1707:
1708: public boolean isFlowThrough() {
1709: return false;
1710: }
1711:
1712: public byte[] getBytes() {
1713: int varNum = getVariableNumber();
1714:
1715: if (varNum <= 255) {
1716: mBytes = new byte[] { Opcode.RET, (byte) varNum };
1717: } else {
1718: mBytes = new byte[] { Opcode.WIDE, Opcode.RET,
1719: (byte) (varNum >> 8), (byte) varNum };
1720: }
1721:
1722: return mBytes;
1723: }
1724:
1725: public boolean isLoad() {
1726: return true;
1727: }
1728:
1729: public boolean isStore() {
1730: return false;
1731: }
1732: }
1733:
1734: /**
1735: * Defines a specialized instruction that increments a local variable by
1736: * a signed 16-bit amount.
1737: */
1738: public class ShortIncrementInstruction extends
1739: LocalOperandInstruction {
1740: private short mAmount;
1741:
1742: public ShortIncrementInstruction(LocalVariable local,
1743: short amount) {
1744: super (0, local);
1745: mAmount = amount;
1746: }
1747:
1748: public boolean isFlowThrough() {
1749: return true;
1750: }
1751:
1752: public byte[] getBytes() {
1753: int varNum = getVariableNumber();
1754:
1755: if ((-128 <= mAmount && mAmount <= 127) && varNum <= 255) {
1756: mBytes = new byte[] { Opcode.IINC, (byte) varNum,
1757: (byte) mAmount };
1758: } else {
1759: mBytes = new byte[] { Opcode.WIDE, Opcode.IINC,
1760: (byte) (varNum >> 8), (byte) varNum,
1761: (byte) (mAmount >> 8), (byte) mAmount };
1762: }
1763:
1764: return mBytes;
1765: }
1766:
1767: public boolean isLoad() {
1768: return true;
1769: }
1770:
1771: public boolean isStore() {
1772: return true;
1773: }
1774: }
1775:
1776: /**
1777: * Defines a switch instruction. The choice of which actual switch
1778: * implementation to use (table or lookup switch) is determined
1779: * automatically based on which generates to the smallest amount of bytes.
1780: */
1781: public class SwitchInstruction extends CodeInstruction {
1782: private int[] mCases;
1783: private Location[] mLocations;
1784: private Location mDefaultLocation;
1785:
1786: private byte mOpcode;
1787:
1788: private int mSmallest;
1789: private int mLargest;
1790:
1791: public SwitchInstruction(int[] casesParam,
1792: Location[] locationsParam, Location defaultLocation) {
1793: // A SwitchInstruction always adjusts the stack by -1 because it
1794: // pops the switch key off the stack.
1795: super (-1);
1796:
1797: if (casesParam.length != locationsParam.length) {
1798: throw new IllegalArgumentException(
1799: "Switch cases and locations sizes differ: "
1800: + casesParam.length + ", "
1801: + locationsParam.length);
1802: }
1803:
1804: mCases = new int[casesParam.length];
1805: System.arraycopy(casesParam, 0, mCases, 0,
1806: casesParam.length);
1807:
1808: mLocations = new Location[locationsParam.length];
1809: System.arraycopy(locationsParam, 0, mLocations, 0,
1810: locationsParam.length);
1811:
1812: mDefaultLocation = defaultLocation;
1813:
1814: // First sort the cases and locations.
1815: sort(0, mCases.length - 1);
1816:
1817: // Check for duplicate cases.
1818: int lastCase = 0;
1819: for (int i = 0; i < mCases.length; i++) {
1820: if (i > 0 && mCases[i] == lastCase) {
1821: throw new IllegalArgumentException(
1822: "Duplicate switch cases: " + lastCase);
1823: }
1824: lastCase = mCases[i];
1825: }
1826:
1827: // Now determine which kind of switch to use.
1828:
1829: mSmallest = mCases[0];
1830: mLargest = mCases[mCases.length - 1];
1831: int tSize = 12 + 4 * (mLargest - mSmallest + 1);
1832:
1833: int lSize = 8 + 8 * mCases.length;
1834:
1835: if (tSize <= lSize) {
1836: mOpcode = Opcode.TABLESWITCH;
1837: } else {
1838: mOpcode = Opcode.LOOKUPSWITCH;
1839: }
1840: }
1841:
1842: public Location[] getBranchTargets() {
1843: Location[] targets = new Location[mLocations.length + 1];
1844: System.arraycopy(mLocations, 0, targets, 0,
1845: mLocations.length);
1846: targets[targets.length - 1] = mDefaultLocation;
1847:
1848: return targets;
1849: }
1850:
1851: public boolean isFlowThrough() {
1852: return false;
1853: }
1854:
1855: public byte[] getBytes() {
1856: int length = 1;
1857: int pad = 3 - (mLocation & 3);
1858: length += pad;
1859:
1860: if (mOpcode == Opcode.TABLESWITCH) {
1861: length += 12 + 4 * (mLargest - mSmallest + 1);
1862: } else {
1863: length += 8 + 8 * mCases.length;
1864: }
1865:
1866: mBytes = new byte[length];
1867:
1868: if (!isResolved()) {
1869: return mBytes;
1870: }
1871:
1872: mBytes[0] = mOpcode;
1873: int cursor = pad + 1;
1874:
1875: int defaultOffset = mDefaultLocation.getLocation()
1876: - mLocation;
1877: mBytes[cursor++] = (byte) (defaultOffset >> 24);
1878: mBytes[cursor++] = (byte) (defaultOffset >> 16);
1879: mBytes[cursor++] = (byte) (defaultOffset >> 8);
1880: mBytes[cursor++] = (byte) (defaultOffset >> 0);
1881:
1882: if (mOpcode == Opcode.TABLESWITCH) {
1883: mBytes[cursor++] = (byte) (mSmallest >> 24);
1884: mBytes[cursor++] = (byte) (mSmallest >> 16);
1885: mBytes[cursor++] = (byte) (mSmallest >> 8);
1886: mBytes[cursor++] = (byte) (mSmallest >> 0);
1887:
1888: mBytes[cursor++] = (byte) (mLargest >> 24);
1889: mBytes[cursor++] = (byte) (mLargest >> 16);
1890: mBytes[cursor++] = (byte) (mLargest >> 8);
1891: mBytes[cursor++] = (byte) (mLargest >> 0);
1892:
1893: int index = 0;
1894: for (int case_ = mSmallest; case_ <= mLargest; case_++) {
1895: if (case_ == mCases[index]) {
1896: int offset = mLocations[index].getLocation()
1897: - mLocation;
1898: mBytes[cursor++] = (byte) (offset >> 24);
1899: mBytes[cursor++] = (byte) (offset >> 16);
1900: mBytes[cursor++] = (byte) (offset >> 8);
1901: mBytes[cursor++] = (byte) (offset >> 0);
1902:
1903: index++;
1904: } else {
1905: mBytes[cursor++] = (byte) (defaultOffset >> 24);
1906: mBytes[cursor++] = (byte) (defaultOffset >> 16);
1907: mBytes[cursor++] = (byte) (defaultOffset >> 8);
1908: mBytes[cursor++] = (byte) (defaultOffset >> 0);
1909: }
1910: }
1911: } else {
1912: mBytes[cursor++] = (byte) (mCases.length >> 24);
1913: mBytes[cursor++] = (byte) (mCases.length >> 16);
1914: mBytes[cursor++] = (byte) (mCases.length >> 8);
1915: mBytes[cursor++] = (byte) (mCases.length >> 0);
1916:
1917: for (int index = 0; index < mCases.length; index++) {
1918: int case_ = mCases[index];
1919:
1920: mBytes[cursor++] = (byte) (case_ >> 24);
1921: mBytes[cursor++] = (byte) (case_ >> 16);
1922: mBytes[cursor++] = (byte) (case_ >> 8);
1923: mBytes[cursor++] = (byte) (case_ >> 0);
1924:
1925: int offset = mLocations[index].getLocation()
1926: - mLocation;
1927: mBytes[cursor++] = (byte) (offset >> 24);
1928: mBytes[cursor++] = (byte) (offset >> 16);
1929: mBytes[cursor++] = (byte) (offset >> 8);
1930: mBytes[cursor++] = (byte) (offset >> 0);
1931: }
1932: }
1933:
1934: return mBytes;
1935: }
1936:
1937: public boolean isResolved() {
1938: if (mDefaultLocation.getLocation() >= 0) {
1939: for (int i = 0; i < mLocations.length; i++) {
1940: if (mLocations[i].getLocation() < 0) {
1941: break;
1942: }
1943: }
1944:
1945: return true;
1946: }
1947:
1948: return false;
1949: }
1950:
1951: private void sort(int left, int right) {
1952: if (left >= right) {
1953: return;
1954: }
1955:
1956: swap(left, (left + right) / 2); // move middle element to 0
1957:
1958: int last = left;
1959:
1960: for (int i = left + 1; i <= right; i++) {
1961: if (mCases[i] < mCases[left]) {
1962: swap(++last, i);
1963: }
1964: }
1965:
1966: swap(left, last);
1967: sort(left, last - 1);
1968: sort(last + 1, right);
1969: }
1970:
1971: private void swap(int i, int j) {
1972: int tempInt = mCases[i];
1973: mCases[i] = mCases[j];
1974: mCases[j] = tempInt;
1975:
1976: Location tempLocation = mLocations[i];
1977: mLocations[i] = mLocations[j];
1978: mLocations[j] = tempLocation;
1979: }
1980: }
1981: }
|