0001: /*
0002: Copyright (c) 2003-2005, Dennis M. Sosnoski
0003: All rights reserved.
0004:
0005: Redistribution and use in source and binary forms, with or without modification,
0006: are permitted provided that the following conditions are met:
0007:
0008: * Redistributions of source code must retain the above copyright notice, this
0009: list of conditions and the following disclaimer.
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: this list of conditions and the following disclaimer in the documentation
0012: and/or other materials provided with the distribution.
0013: * Neither the name of JiBX nor the names of its contributors may be used
0014: to endorse or promote products derived from this software without specific
0015: prior written permission.
0016:
0017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
0018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
0021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027: */
0028:
0029: package org.jibx.binding.classes;
0030:
0031: import java.util.ArrayList;
0032: import java.util.HashMap;
0033:
0034: import org.apache.bcel.Constants;
0035: import org.apache.bcel.classfile.Method;
0036: import org.apache.bcel.classfile.Utility;
0037: import org.apache.bcel.generic.ANEWARRAY;
0038: import org.apache.bcel.generic.BranchHandle;
0039: import org.apache.bcel.generic.CompoundInstruction;
0040: import org.apache.bcel.generic.GOTO;
0041: import org.apache.bcel.generic.IFEQ;
0042: import org.apache.bcel.generic.IFGE;
0043: import org.apache.bcel.generic.IFLT;
0044: import org.apache.bcel.generic.IFNE;
0045: import org.apache.bcel.generic.IFNONNULL;
0046: import org.apache.bcel.generic.IFNULL;
0047: import org.apache.bcel.generic.IF_ICMPNE;
0048: import org.apache.bcel.generic.IINC;
0049: import org.apache.bcel.generic.Instruction;
0050: import org.apache.bcel.generic.InstructionConstants;
0051: import org.apache.bcel.generic.InstructionFactory;
0052: import org.apache.bcel.generic.InstructionHandle;
0053: import org.apache.bcel.generic.InstructionList;
0054: import org.apache.bcel.generic.LocalVariableGen;
0055: import org.apache.bcel.generic.MULTIANEWARRAY;
0056: import org.apache.bcel.generic.MethodGen;
0057: import org.apache.bcel.generic.NEWARRAY;
0058: import org.apache.bcel.generic.ReferenceType;
0059: import org.apache.bcel.generic.Type;
0060: import org.jibx.binding.util.StringStack;
0061: import org.jibx.runtime.JiBXException;
0062:
0063: /**
0064: * Method builder. Organizes and tracks the creation of a method, providing
0065: * convenience methods for common operations. This is customized for the needs
0066: * of JiBX, with some predetermined settings as appropriate. It supplies hash
0067: * code and equality checking based on the method signature and actual byte
0068: * code of the generated method, ignoring the method name.
0069: *
0070: * @author Dennis M. Sosnoski
0071: * @version 1.0
0072: */
0073: public abstract class MethodBuilder extends BindingMethod {
0074: //
0075: // Constants for code generation.
0076:
0077: public static final String FRAMEWORK_EXCEPTION_CLASS = "org.jibx.runtime.JiBXException";
0078:
0079: public static final String EXCEPTION_CONSTRUCTOR_SIGNATURE1 = "(Ljava/lang/String;)V";
0080:
0081: public static final String EXCEPTION_CONSTRUCTOR_SIGNATURE2 = "(Ljava/lang/String;Ljava/lang/Throwable;)V";
0082:
0083: public static final int SYNTHETIC_ACCESS_FLAG = 0x1000;
0084:
0085: //
0086: // Static data.
0087:
0088: /** Table of argument name lists (generated as needed). */
0089: protected static ArrayList s_argNameLists = new ArrayList();
0090:
0091: /** Zero-length string array. */
0092: protected static String[] EMPTY_STRING_ARRAY = new String[0];
0093:
0094: //
0095: // Actual instance data
0096:
0097: /** Builder for class instructions. */
0098: protected InstructionBuilder m_instructionBuilder;
0099:
0100: /** List of instructions in method definition. */
0101: private InstructionList m_instructionList;
0102:
0103: /** List of types currently on stack. */
0104: private StringStack m_stackState;
0105:
0106: /** Generator for constructing method. */
0107: protected MethodGen m_generator;
0108:
0109: /** Actual generated method information. */
0110: protected Method m_method;
0111:
0112: /** Method class item information. */
0113: protected ClassItem m_item;
0114:
0115: /** Value types associated with local variable slots. */
0116: private ArrayList m_localTypes;
0117:
0118: /** Exceptions needing to be handled in method (lazy create,
0119: <code>null</code> if not used). */
0120: protected ArrayList m_exceptions;
0121:
0122: /** Accumulated hash code from adding instructions. */
0123: protected int m_hashCode;
0124:
0125: /** Branch to be aimed at next appended instruction. */
0126: protected BranchWrapper[] m_targetBranches;
0127:
0128: /** Map for initialized properties (lazy create, <code>null</code> if not
0129: used). */
0130: protected HashMap m_valueMap;
0131:
0132: /**
0133: * Constructor. This sets up for constructing a method with public access.
0134: *
0135: * @param name method name to be built
0136: * @param ret method return type
0137: * @param args types of arguments
0138: * @param cf owning class file information
0139: * @param access flags for method access
0140: * @throws JiBXException on error in constructing method
0141: */
0142: protected MethodBuilder(String name, Type ret, Type[] args,
0143: ClassFile cf, int access) throws JiBXException {
0144: super (cf);
0145:
0146: // make sure the dummy argument names are defined
0147: if (args.length >= s_argNameLists.size()) {
0148:
0149: // append to end of argument names list
0150: for (int i = s_argNameLists.size(); i <= args.length; i++) {
0151: String[] list = new String[i];
0152: if (i > 0) {
0153: Object last = s_argNameLists.get(i - 1);
0154: System.arraycopy(last, 0, list, 0, i - 1);
0155: list[i - 1] = "arg" + i;
0156: }
0157: s_argNameLists.add(list);
0158: }
0159:
0160: }
0161:
0162: // create the method generator with empty instruction list
0163: String[] names = (String[]) s_argNameLists.get(args.length);
0164: m_instructionList = new InstructionList();
0165: m_stackState = new StringStack();
0166: m_instructionBuilder = cf.getInstructionBuilder();
0167: m_generator = new MethodGen(access | SYNTHETIC_ACCESS_FLAG,
0168: ret, args, names, name, cf.getName(),
0169: m_instructionList, cf.getConstPoolGen());
0170:
0171: // initialize local variables for method parameters
0172: m_localTypes = new ArrayList();
0173: if ((access & Constants.ACC_STATIC) == 0) {
0174: m_localTypes.add(cf.getName());
0175: }
0176: for (int i = 0; i < args.length; i++) {
0177: m_localTypes.add(args[i].toString());
0178: if (args[i].getSize() > 1) {
0179: m_localTypes.add(null);
0180: }
0181: }
0182: }
0183:
0184: /**
0185: * Get name of method being constructed.
0186: *
0187: * @return name of method being constructed
0188: */
0189: public String getName() {
0190: return m_generator.getName();
0191: }
0192:
0193: /**
0194: * Get signature.
0195: *
0196: * @return signature for method
0197: */
0198: public String getSignature() {
0199: return m_generator.getSignature();
0200: }
0201:
0202: /**
0203: * Get access flags.
0204: *
0205: * @return flags for access type of method
0206: */
0207: public int getAccessFlags() {
0208: return m_generator.getAccessFlags();
0209: }
0210:
0211: /**
0212: * Set access flags.
0213: *
0214: * @param flags access type to be set
0215: */
0216: public void setAccessFlags(int flags) {
0217: m_generator.setAccessFlags(flags);
0218: }
0219:
0220: /**
0221: * Get the actual method. This can only be called once code generation is
0222: * completed (after the {@link #codeComplete(boolean)} method is called).
0223: *
0224: * @return constructed method information
0225: */
0226: public Method getMethod() {
0227: if (m_method == null) {
0228: throw new IllegalStateException(
0229: "Method still under construction");
0230: } else {
0231: return m_method;
0232: }
0233: }
0234:
0235: /**
0236: * Add keyed value to method definition.
0237: *
0238: * @param key retrieval key
0239: * @param value keyed value
0240: * @return prior value for key
0241: */
0242: public Object setKeyValue(Object key, Object value) {
0243: if (m_valueMap == null) {
0244: m_valueMap = new HashMap();
0245: }
0246: return m_valueMap.put(key, value);
0247: }
0248:
0249: /**
0250: * Get local variable for object.
0251: *
0252: * @param key object key for local variable
0253: * @return local variable
0254: */
0255: public Object getKeyValue(Object key) {
0256: return m_valueMap == null ? null : m_valueMap.get(key);
0257: }
0258:
0259: /**
0260: * Add exception to those needing handling.
0261: *
0262: * @param name fully qualified name of exception class
0263: */
0264: public void addException(String name) {
0265: if (m_exceptions == null) {
0266: m_exceptions = new ArrayList();
0267: }
0268: if (!m_exceptions.contains(name)) {
0269: m_exceptions.add(name);
0270: }
0271: }
0272:
0273: /**
0274: * Add exceptions thrown by called method to those needing handling.
0275: *
0276: * @param method information for method to be handled
0277: */
0278: public void addMethodExceptions(ClassItem method) {
0279: String[] excepts = method.getExceptions();
0280: if (excepts != null) {
0281: for (int i = 0; i < excepts.length; i++) {
0282: addException(excepts[i]);
0283: }
0284: }
0285: }
0286:
0287: /**
0288: * Get first instruction in method.
0289: *
0290: * @return handle for first instruction in method
0291: */
0292: protected InstructionHandle getFirstInstruction() {
0293: return m_instructionList.getStart();
0294: }
0295:
0296: /**
0297: * Get last instruction in method.
0298: *
0299: * @return handle for last instruction in method
0300: */
0301: protected InstructionHandle getLastInstruction() {
0302: return m_instructionList.getEnd();
0303: }
0304:
0305: /**
0306: * Target branches if pending. This implements setting the target of
0307: * branch instructions supplied using the {@link #targetNext} method.
0308: *
0309: * @param inst handle for appended instruction
0310: */
0311: protected final void setTarget(InstructionHandle inst) {
0312: if (m_targetBranches != null) {
0313:
0314: // TODO: fix this ugly kludge with code rewrite
0315: // adjust stack with POPs if need to match size
0316: String[] types = m_stackState.toArray();
0317: if (m_targetBranches.length > 0) {
0318: boolean match = true;
0319: int depth = m_targetBranches[0].getStackState().length;
0320: for (int i = 1; i < m_targetBranches.length; i++) {
0321: if (depth != m_targetBranches[i].getStackState().length) {
0322: match = false;
0323: break;
0324: }
0325: }
0326: if (match) {
0327: if (depth > types.length) {
0328: BranchWrapper merge = new BranchWrapper(
0329: m_instructionList.insert(inst,
0330: new GOTO(null)), types, this );
0331: String[] stack = m_targetBranches[0]
0332: .getStackState();
0333: m_stackState = new StringStack(stack);
0334: InstructionHandle poph = m_instructionList
0335: .insert(inst, InstructionConstants.POP);
0336: for (int i = 0; i < m_targetBranches.length; i++) {
0337: m_targetBranches[i].setTarget(poph, stack,
0338: this );
0339: }
0340: m_stackState.pop();
0341: while (m_stackState.size() > types.length) {
0342: m_instructionList.insert(inst,
0343: InstructionConstants.POP);
0344: m_stackState.pop();
0345: }
0346: merge.setTarget(inst, m_stackState.toArray(),
0347: this );
0348: m_targetBranches = null;
0349: return;
0350: } else {
0351: while (depth < types.length) {
0352: m_instructionList.insert(inst,
0353: InstructionConstants.POP);
0354: m_stackState.pop();
0355: types = m_stackState.toArray();
0356: }
0357: }
0358: }
0359: }
0360:
0361: // set all branch targets
0362: for (int i = 0; i < m_targetBranches.length; i++) {
0363: m_targetBranches[i].setTarget(inst, types, this );
0364: }
0365: m_targetBranches = null;
0366: }
0367: }
0368:
0369: /**
0370: * Generate description of current stack state.
0371: *
0372: * @return stack state description
0373: */
0374: private String describeStack() {
0375: StringBuffer buff = new StringBuffer();
0376: String[] types = m_stackState.toArray();
0377: for (int i = 0; i < types.length; i++) {
0378: buff.append(" ");
0379: buff.append(i);
0380: buff.append(": ");
0381: buff.append(types[i]);
0382: buff.append('\n');
0383: }
0384: return buff.toString();
0385: }
0386:
0387: /**
0388: * Verify that a pair of value types represent compatible types. This checks
0389: * for equal types or downcast object types.
0390: *
0391: * @param type actual known type of value
0392: * @param need type needed
0393: */
0394: private void verifyCompatible(String type, String need) {
0395: if (!need.equals(type)) {
0396: try {
0397: if ("<null>".equals(type)) {
0398: if (ClassItem.isPrimitive(need)) {
0399: throw new IllegalStateException(
0400: "Internal error: Expected " + need
0401: + " on stack , found null");
0402: }
0403: } else if ("java.lang.Object".equals(need)) {
0404: if (ClassItem.isPrimitive(type)) {
0405: throw new IllegalStateException(
0406: "Internal error: "
0407: + "Expected object reference on stack, found "
0408: + type + "\n full stack:\n"
0409: + describeStack());
0410: }
0411: } else {
0412: boolean match = false;
0413: if ("int".equals(need)) {
0414: match = "boolean".equals(type)
0415: || "short".equals(type)
0416: || "char".equals(type)
0417: || "byte".equals(type);
0418: } else if ("int".equals(type)) {
0419: match = "boolean".equals(need)
0420: || "short".equals(need)
0421: || "char".equals(need)
0422: || "byte".equals(need);
0423: }
0424: if (!match && !ClassItem.isAssignable(type, need)) {
0425: throw new IllegalStateException(
0426: "Internal error: Expected " + need
0427: + " on stack, found " + type
0428: + "\n full stack:\n"
0429: + describeStack());
0430: }
0431: }
0432: } catch (JiBXException e) {
0433: throw new RuntimeException(
0434: "Internal error: Attempting to compare types "
0435: + need + " and " + type);
0436: }
0437: }
0438: }
0439:
0440: /**
0441: * Verify that at least the specified number of items are present on the
0442: * stack.
0443: *
0444: * @param count minimum number of items required
0445: */
0446: private void verifyStackDepth(int count) {
0447: if (m_stackState.size() < count) {
0448: throw new IllegalStateException(
0449: "Internal error: Too few values on stack\n full stack:\n"
0450: + describeStack());
0451: }
0452: }
0453:
0454: /**
0455: * Verify the top value in the stack state resulting from the current
0456: * instruction list.
0457: *
0458: * @param t1 expected type for top item on stack
0459: */
0460: private void verifyStack(String t1) {
0461: verifyStackDepth(1);
0462: verifyCompatible(m_stackState.peek(), t1);
0463: }
0464:
0465: /**
0466: * Verify the top value in the stack state resulting from the current
0467: * instruction list is an array.
0468: *
0469: * @return array item type
0470: */
0471: private String verifyArray() {
0472: verifyStackDepth(1);
0473: String type = m_stackState.peek();
0474: if (type.endsWith("[]")) {
0475: return type.substring(0, type.length() - 2);
0476: } else {
0477: throw new IllegalStateException(
0478: "Internal error: Expected array type on stack , found "
0479: + type);
0480: }
0481: }
0482:
0483: /**
0484: * Verify the top value in the stack state resulting from the current
0485: * instruction list is an array of the specified type.
0486: *
0487: * @param type array item type
0488: */
0489: private void verifyArray(String type) {
0490: String atype = verifyArray();
0491: if (!atype.equals(type)) {
0492: throw new IllegalStateException(
0493: "Internal error: Expected array of " + type
0494: + " on stack , found array of " + atype);
0495: }
0496: }
0497:
0498: /**
0499: * Verify the top two values in the stack state resulting from the current
0500: * instruction list.
0501: *
0502: * @param t1 expected type for first item on stack
0503: * @param t2 expected type for second item on stack
0504: */
0505: private void verifyStack(String t1, String t2) {
0506: verifyStackDepth(2);
0507: verifyCompatible(m_stackState.peek(), t1);
0508: verifyCompatible(m_stackState.peek(1), t2);
0509: }
0510:
0511: /**
0512: * Verify the top values in the stack state resulting from the current
0513: * instruction list. This form checks only the actual call parameters.
0514: *
0515: * @param types expected parameter types on stack
0516: */
0517: private void verifyCallStack(String[] types) {
0518:
0519: // make sure there are enough items on stack
0520: int count = types.length;
0521: verifyStackDepth(count);
0522:
0523: // verify all parameter types for call
0524: for (int i = 0; i < count; i++) {
0525: int slot = count - i - 1;
0526: verifyCompatible(m_stackState.peek(slot), types[i]);
0527: }
0528: }
0529:
0530: /**
0531: * Verify the top values in the stack state resulting from the current
0532: * instruction list. This form checks both the object being called and the
0533: * actual call parameters.
0534: *
0535: * @param clas name of method class
0536: * @param types expected parameter types on stack
0537: */
0538: private void verifyCallStack(String clas, String[] types) {
0539:
0540: // start by verifying object reference
0541: int count = types.length;
0542: verifyStackDepth(count + 1);
0543: verifyCompatible(m_stackState.peek(count), clas);
0544:
0545: // check values for call parameters
0546: verifyCallStack(types);
0547: }
0548:
0549: /**
0550: * Verify that the top value in the stack state resulting from the current
0551: * instruction list is an object reference.
0552: */
0553: private void verifyStackObject() {
0554: verifyStackDepth(1);
0555: String top = m_stackState.peek();
0556: if (ClassItem.isPrimitive(top)) {
0557: throw new IllegalStateException("Internal error: "
0558: + "Expected object reference on stack , found "
0559: + m_stackState.peek() + "\n full stack:\n"
0560: + describeStack());
0561: }
0562: }
0563:
0564: /**
0565: * Append IFEQ branch instruction to method.
0566: *
0567: * @param src object responsible for generating branch
0568: * @return wrapper for appended conditional branch
0569: */
0570: public BranchWrapper appendIFEQ(Object src) {
0571: verifyStack("int");
0572: BranchHandle hand = m_instructionList.append(new IFEQ(null));
0573: setTarget(hand);
0574: m_stackState.pop();
0575: return new BranchWrapper(hand, m_stackState.toArray(), src);
0576: }
0577:
0578: /**
0579: * Append IFGE branch instruction to method.
0580: *
0581: * @param src object responsible for generating branch
0582: * @return wrapper for appended conditional branch
0583: */
0584: public BranchWrapper appendIFGE(Object src) {
0585: verifyStack("int");
0586: BranchHandle hand = m_instructionList.append(new IFGE(null));
0587: setTarget(hand);
0588: m_stackState.pop();
0589: return new BranchWrapper(hand, m_stackState.toArray(), src);
0590: }
0591:
0592: /**
0593: * Append IFLT branch instruction to method.
0594: *
0595: * @param src object responsible for generating branch
0596: * @return wrapper for appended conditional branch
0597: */
0598: public BranchWrapper appendIFLT(Object src) {
0599: verifyStack("int");
0600: BranchHandle hand = m_instructionList.append(new IFLT(null));
0601: setTarget(hand);
0602: m_stackState.pop();
0603: return new BranchWrapper(hand, m_stackState.toArray(), src);
0604: }
0605:
0606: /**
0607: * Append IFNE branch instruction to method.
0608: *
0609: * @param src object responsible for generating branch
0610: * @return wrapper for appended conditional branch
0611: */
0612: public BranchWrapper appendIFNE(Object src) {
0613: verifyStack("int");
0614: BranchHandle hand = m_instructionList.append(new IFNE(null));
0615: setTarget(hand);
0616: m_stackState.pop();
0617: return new BranchWrapper(hand, m_stackState.toArray(), src);
0618: }
0619:
0620: /**
0621: * Append IFNONNULL branch instruction to method.
0622: *
0623: * @param src object responsible for generating branch
0624: * @return wrapper for appended conditional branch
0625: */
0626: public BranchWrapper appendIFNONNULL(Object src) {
0627: verifyStackObject();
0628: BranchHandle hand = m_instructionList
0629: .append(new IFNONNULL(null));
0630: setTarget(hand);
0631: m_stackState.pop();
0632: return new BranchWrapper(hand, m_stackState.toArray(), src);
0633: }
0634:
0635: /**
0636: * Append IFNULL branch instruction to method.
0637: *
0638: * @param src object responsible for generating branch
0639: * @return wrapper for appended conditional branch
0640: */
0641: public BranchWrapper appendIFNULL(Object src) {
0642: verifyStackObject();
0643: BranchHandle hand = m_instructionList.append(new IFNULL(null));
0644: setTarget(hand);
0645: m_stackState.pop();
0646: return new BranchWrapper(hand, m_stackState.toArray(), src);
0647: }
0648:
0649: /**
0650: * Append IF_ICMPNE branch instruction to method.
0651: *
0652: * @param src object responsible for generating branch
0653: * @return wrapper for appended conditional branch
0654: */
0655: public BranchWrapper appendIF_ICMPNE(Object src) {
0656: verifyStack("int", "int");
0657: BranchHandle hand = m_instructionList
0658: .append(new IF_ICMPNE(null));
0659: setTarget(hand);
0660: m_stackState.pop(2);
0661: return new BranchWrapper(hand, m_stackState.toArray(), src);
0662: }
0663:
0664: /**
0665: * Append unconditional branch instruction to method.
0666: *
0667: * @param src object responsible for generating branch
0668: * @return wrapper for appended unconditional branch
0669: */
0670: public BranchWrapper appendUnconditionalBranch(Object src) {
0671: BranchHandle hand = m_instructionList.append(new GOTO(null));
0672: setTarget(hand);
0673: BranchWrapper wrapper = new BranchWrapper(hand, m_stackState
0674: .toArray(), src);
0675: m_stackState = null;
0676: return wrapper;
0677: }
0678:
0679: /**
0680: * Append compound instruction to method.
0681: *
0682: * @param ins instruction to be appended
0683: */
0684: private void append(CompoundInstruction ins) {
0685: setTarget(m_instructionList.append(ins));
0686: }
0687:
0688: /**
0689: * Append instruction to method.
0690: *
0691: * @param ins instruction to be appended
0692: */
0693: private void append(Instruction ins) {
0694: setTarget(m_instructionList.append(ins));
0695: }
0696:
0697: /**
0698: * Create load constant instruction and append to method. Builds the most
0699: * appropriate type of instruction for the value.
0700: *
0701: * @param value constant value to be loaded
0702: */
0703: public void appendLoadConstant(int value) {
0704: append(m_instructionBuilder.createLoadConstant(value));
0705: m_stackState.push("int");
0706: }
0707:
0708: /**
0709: * Create load constant instruction and append to method. Loads a
0710: * <code>String</code> reference from the constant pool.
0711: *
0712: * @param value constant value to be loaded
0713: */
0714: public void appendLoadConstant(String value) {
0715: append(m_instructionBuilder.createLoadConstant(value));
0716: m_stackState.push("java.lang.String");
0717: }
0718:
0719: /**
0720: * Create load constant instruction and append to method. Loads an
0721: * unwrapped primitive value from the constant pool.
0722: *
0723: * @param value constant value to be loaded
0724: */
0725: public void appendLoadConstant(Object value) {
0726: append(m_instructionBuilder.createLoadConstant(value));
0727: if (value instanceof Integer || value instanceof Character
0728: || value instanceof Short || value instanceof Boolean
0729: || value instanceof Byte) {
0730: m_stackState.push("int");
0731: } else if (value instanceof Long) {
0732: m_stackState.push("long");
0733: } else if (value instanceof Float) {
0734: m_stackState.push("float");
0735: } else if (value instanceof Double) {
0736: m_stackState.push("double");
0737: } else {
0738: throw new IllegalArgumentException("Unknown argument type");
0739: }
0740: }
0741:
0742: /**
0743: * Create getfield instruction and append to method. Uses the target field
0744: * information to generate the instruction.
0745: *
0746: * @param item information for field to be gotton
0747: */
0748: public void appendGetField(ClassItem item) {
0749: verifyStack(item.getClassFile().getName());
0750: append(m_instructionBuilder.createGetField(item));
0751: m_stackState.pop();
0752: m_stackState.push(item.getTypeName());
0753: }
0754:
0755: /**
0756: * Create getstatic instruction and append to method. Uses the target field
0757: * information to generate the instruction.
0758: *
0759: * @param item information for field to be set
0760: */
0761: public void appendGetStatic(ClassItem item) {
0762: append(m_instructionBuilder.createGetStatic(item));
0763: m_stackState.push(item.getTypeName());
0764: }
0765:
0766: /**
0767: * Create get instruction and append to method. This generates either a
0768: * getstatic or a getfield instruction, as appropriate.
0769: *
0770: * @param item information for field to be gotten
0771: */
0772: public void appendGet(ClassItem item) {
0773: if (item.isStatic()) {
0774: appendGetStatic(item);
0775: } else {
0776: appendGetField(item);
0777: }
0778: }
0779:
0780: /**
0781: * Create putfield instruction and append to method. Uses the target field
0782: * information to generate the instruction.
0783: *
0784: * @param item information for field to be set
0785: */
0786: public void appendPutField(ClassItem item) {
0787: String tname = item.getTypeName();
0788: verifyStack(tname, item.getClassFile().getName());
0789: append(m_instructionBuilder.createPutField(item));
0790: m_stackState.pop(2);
0791: }
0792:
0793: /**
0794: * Create putstatic instruction and append to method. Uses the target field
0795: * information to generate the instruction.
0796: *
0797: * @param item information for field to be set
0798: */
0799: public void appendPutStatic(ClassItem item) {
0800: verifyStack(item.getTypeName());
0801: append(m_instructionBuilder.createPutStatic(item));
0802: m_stackState.pop();
0803: }
0804:
0805: /**
0806: * Create put instruction and append to method. This generates either a
0807: * putstatic or a putfield instruction, as appropriate.
0808: *
0809: * @param item information for field to be gotten
0810: */
0811: public void appendPut(ClassItem item) {
0812: if (item.isStatic()) {
0813: appendPutStatic(item);
0814: } else {
0815: appendPutField(item);
0816: }
0817: }
0818:
0819: /**
0820: * Create invoke instruction for static, member, or interface method and
0821: * append to method. Uses the target method information to generate the
0822: * correct instruction.
0823: *
0824: * @param item information for method to be called
0825: */
0826: public void appendCall(ClassItem item) {
0827:
0828: // process based on call type
0829: String[] types = item.getArgumentTypes();
0830: int count = types.length;
0831: if (item.getClassFile().isInterface()) {
0832:
0833: // process parameters and object reference for interface call
0834: verifyCallStack(item.getClassFile().getName(), types);
0835: append(m_instructionBuilder.createCallInterface(item));
0836: m_stackState.pop(count + 1);
0837:
0838: } else if ((item.getAccessFlags() & Constants.ACC_STATIC) != 0) {
0839:
0840: // process only parameters for static call
0841: verifyCallStack(types);
0842: append(m_instructionBuilder.createCallStatic(item));
0843: if (count > 0) {
0844: m_stackState.pop(count);
0845: }
0846:
0847: } else {
0848:
0849: // process parameters and object reference for normal method call
0850: verifyCallStack(item.getClassFile().getName(), types);
0851: append(m_instructionBuilder.createCallVirtual(item));
0852: m_stackState.pop(count + 1);
0853: }
0854:
0855: // adjust stack state to reflect result of call
0856: if (!"void".equals(item.getTypeName())) {
0857: m_stackState.push(item.getTypeName());
0858: }
0859: }
0860:
0861: /**
0862: * Create invoke static method instruction from signature and append to
0863: * method.
0864: *
0865: * @param method fully qualified class and method name
0866: * @param signature method signature in standard form
0867: */
0868: public void appendCallStatic(String method, String signature) {
0869:
0870: // verify all call parameters on stack
0871: String[] types = ClassItem
0872: .getParametersFromSignature(signature);
0873: verifyCallStack(types);
0874:
0875: // generate the actual method call
0876: append(m_instructionBuilder.createCallStatic(method, signature));
0877:
0878: // change stack state to reflect result of call
0879: if (types.length > 0) {
0880: m_stackState.pop(types.length);
0881: }
0882: String result = ClassItem.getTypeFromSignature(signature);
0883: if (!"void".equals(result)) {
0884: m_stackState.push(result);
0885: }
0886: }
0887:
0888: /**
0889: * Create invoke virtual method instruction from signature and append to
0890: * method.
0891: *
0892: * @param method fully qualified class and method name
0893: * @param signature method signature in standard form
0894: */
0895: public void appendCallVirtual(String method, String signature) {
0896:
0897: // verify all call parameters and object reference on stack
0898: String[] types = ClassItem
0899: .getParametersFromSignature(signature);
0900: int split = method.lastIndexOf('.');
0901: if (split < 0) {
0902: throw new IllegalArgumentException(
0903: "Internal error: Missing class name on method "
0904: + method);
0905: }
0906: verifyCallStack(method.substring(0, split), types);
0907:
0908: // generate the actual method call
0909: append(m_instructionBuilder
0910: .createCallVirtual(method, signature));
0911:
0912: // change stack state to reflect result of call
0913: m_stackState.pop(types.length + 1);
0914: String result = ClassItem.getTypeFromSignature(signature);
0915: if (!"void".equals(result)) {
0916: m_stackState.push(result);
0917: }
0918: }
0919:
0920: /**
0921: * Create invoke interface method instruction from signature and append to
0922: * method.
0923: *
0924: * @param method fully qualified interface and method name
0925: * @param signature method signature in standard form
0926: */
0927: public void appendCallInterface(String method, String signature) {
0928:
0929: // verify all call parameters and object reference on stack
0930: String[] types = ClassItem
0931: .getParametersFromSignature(signature);
0932: int split = method.lastIndexOf('.');
0933: if (split < 0) {
0934: throw new IllegalArgumentException(
0935: "Internal error: Missing class name on method "
0936: + method);
0937: }
0938: verifyCallStack(method.substring(0, split), types);
0939:
0940: // generate the actual method call
0941: append(m_instructionBuilder.createCallInterface(method,
0942: signature));
0943:
0944: // change stack state to reflect result of call
0945: m_stackState.pop(types.length + 1);
0946: String result = ClassItem.getTypeFromSignature(signature);
0947: if (!"void".equals(result)) {
0948: m_stackState.push(result);
0949: }
0950: }
0951:
0952: /**
0953: * Append instruction to create instance of class.
0954: *
0955: * @param name fully qualified class name
0956: */
0957: public void appendCreateNew(String name) {
0958: append(m_instructionBuilder.createNew(name));
0959: m_stackState.push(name);
0960: }
0961:
0962: /**
0963: * Create invoke initializer instruction from signature and append to
0964: * method.
0965: *
0966: * @param name fully qualified class name
0967: * @param signature method signature in standard form
0968: */
0969: public void appendCallInit(String name, String signature) {
0970:
0971: // verify all call parameters and object reference on stack
0972: String[] types = ClassItem
0973: .getParametersFromSignature(signature);
0974: verifyCallStack(name, types);
0975:
0976: // generate the actual method call
0977: append(m_instructionBuilder.createCallInit(name, signature));
0978:
0979: // change stack state to reflect result of call
0980: m_stackState.pop(types.length + 1);
0981: }
0982:
0983: /**
0984: * Append instruction to create instance of array.
0985: *
0986: * @param type fully qualified type name of array elements
0987: */
0988: public void appendCreateArray(String type) {
0989: if (ClassItem.isPrimitive(type)) {
0990: String sig = Utility.getSignature(type);
0991: append(new NEWARRAY(Utility.typeOfSignature(sig)));
0992: } else if (type.endsWith("[]")) {
0993: String cname = Utility.getSignature(type + "[]");
0994: append(new MULTIANEWARRAY(m_instructionBuilder
0995: .getConstantPoolGen().addClass(cname), (short) 1));
0996: } else {
0997: append(new ANEWARRAY(m_instructionBuilder
0998: .getConstantPoolGen().addClass(type)));
0999: }
1000: m_stackState.pop();
1001: m_stackState.push(type + "[]");
1002: }
1003:
1004: /**
1005: * Append check cast instruction (if needed).
1006: *
1007: * @param from fully qualified name of current type
1008: * @param to fully qualified name of desired type
1009: */
1010: public void appendCreateCast(String from, String to) {
1011:
1012: // verify current top of stack
1013: verifyStack(from);
1014:
1015: // check if any change of type
1016: if (!from.equals(to)) {
1017:
1018: // generate instruction and change stack state to match
1019: append(m_instructionBuilder.createCast(ClassItem
1020: .typeFromName(from), ClassItem.typeFromName(to)));
1021: m_stackState.pop();
1022: m_stackState.push(to);
1023: }
1024: }
1025:
1026: /**
1027: * Append check cast instruction from object (if needed).
1028: *
1029: * @param to fully qualified name of desired type
1030: */
1031: public void appendCreateCast(String to) {
1032:
1033: // verify current top of stack
1034: verifyStackObject();
1035:
1036: // check if any change of type
1037: if (!m_stackState.peek().equals(to)) {
1038:
1039: // generate instruction and change stack state to match
1040: append(m_instructionBuilder.createCast(Type.OBJECT,
1041: ClassItem.typeFromName(to)));
1042: m_stackState.pop();
1043: m_stackState.push(to);
1044: }
1045: }
1046:
1047: /**
1048: * Append instanceof check instruction.
1049: *
1050: * @param to fully qualified name of type to check
1051: */
1052: public void appendInstanceOf(String to) {
1053:
1054: // make sure the stack has an object reference
1055: verifyStackObject();
1056:
1057: // see if anything actually needs to be checked
1058: if ("java.lang.Object".equals(to)) {
1059: append(InstructionConstants.POP);
1060: appendLoadConstant(1);
1061: } else {
1062: append(m_instructionBuilder
1063: .createInstanceOf((ReferenceType) ClassItem
1064: .typeFromName(to)));
1065: }
1066:
1067: // change stack state to reflect results of added code
1068: m_stackState.pop();
1069: m_stackState.push("int");
1070: }
1071:
1072: /**
1073: * Add local variable to method. The current code in the method must have
1074: * the initial value for the variable on the stack. The scope of the
1075: * variable is defined from the last instruction to the end of the
1076: * method unless otherwise modified.
1077: *
1078: * @param name local variable name (may be <code>null</code> to use default)
1079: * @param type variable type
1080: */
1081: protected LocalVariableGen createLocal(String name, Type type) {
1082:
1083: // verify top of stack
1084: verifyStack(type.toString());
1085:
1086: // create name if needed
1087: if (name == null) {
1088: name = "var" + m_generator.getLocalVariables().length;
1089: }
1090:
1091: // allocation local and store value
1092: LocalVariableGen var = m_generator.addLocalVariable(name, type,
1093: getLastInstruction(), null);
1094: append(InstructionFactory.createStore(type, var.getIndex()));
1095:
1096: // save type information for local variable slot
1097: int slot = var.getIndex();
1098: while (slot >= m_localTypes.size()) {
1099: m_localTypes.add(null);
1100: }
1101: m_localTypes.set(slot, type.toString());
1102:
1103: // change stack state to reflect result
1104: m_stackState.pop();
1105: return var;
1106: }
1107:
1108: /**
1109: * Add local variable to method. The current code in the method must have
1110: * the initial value for the variable on the stack. The scope of the
1111: * variable is defined from the preceding instruction to the end of the
1112: * method.
1113: *
1114: * @param name local variable name
1115: * @param type variable type
1116: * @return local variable slot number
1117: */
1118: public int addLocal(String name, Type type) {
1119: LocalVariableGen var = createLocal(name, type);
1120: return var.getIndex();
1121: }
1122:
1123: /**
1124: * Append instruction to load local variable.
1125: *
1126: * @param slot local variable slot to load
1127: */
1128: public void appendLoadLocal(int slot) {
1129: String type = (String) m_localTypes.get(slot);
1130: if (type == null) {
1131: throw new IllegalArgumentException(
1132: "Internal error: No variable defined at position "
1133: + slot);
1134: }
1135: append(InstructionFactory.createLoad(ClassItem
1136: .typeFromName(type), slot));
1137: m_stackState.push(type);
1138: }
1139:
1140: /**
1141: * Append instruction to store local variable.
1142: *
1143: * @param slot local variable slot to store
1144: */
1145: public void appendStoreLocal(int slot) {
1146: String type = (String) m_localTypes.get(slot);
1147: if (type == null) {
1148: throw new IllegalArgumentException(
1149: "Internal error: No variable defined at position "
1150: + slot);
1151: }
1152: verifyStack(type);
1153: append(InstructionFactory.createStore(ClassItem
1154: .typeFromName(type), slot));
1155: m_stackState.pop();
1156: }
1157:
1158: /**
1159: * Append instruction to increment local integer variable.
1160: *
1161: * @param inc amount of incrment
1162: * @param slot local variable slot to load
1163: */
1164: public void appendIncrementLocal(int inc, int slot) {
1165: String type = (String) m_localTypes.get(slot);
1166: if (type == null) {
1167: throw new IllegalArgumentException(
1168: "Internal error: No variable defined at position "
1169: + slot);
1170: } else if (!"int".equals(type)) {
1171: throw new IllegalArgumentException(
1172: "Internal error: Variable at " + slot + " is "
1173: + type + ", not int");
1174: }
1175: append(new IINC(slot, inc));
1176: }
1177:
1178: /**
1179: * Append simple return.
1180: */
1181: public void appendReturn() {
1182: append(InstructionConstants.RETURN);
1183: m_stackState = null;
1184: }
1185:
1186: /**
1187: * Append typed return.
1188: *
1189: * @param type returned type (may be <code>Type.VOID</code>)
1190: */
1191: public void appendReturn(Type type) {
1192:
1193: // verify and return the object reference
1194: if (type != Type.VOID) {
1195: verifyStack(type.toString());
1196: }
1197: append(InstructionFactory.createReturn(type));
1198:
1199: // set open stack state for potential continuation code
1200: m_stackState = null;
1201: }
1202:
1203: /**
1204: * Append typed return.
1205: *
1206: * @param type returned type (may be <code>void</code>)
1207: */
1208: public void appendReturn(String type) {
1209:
1210: // verify stack and generate return
1211: if ("void".equals(type)) {
1212: append(InstructionConstants.RETURN);
1213: } else {
1214: verifyStack(type);
1215: if (ClassItem.isPrimitive(type)) {
1216: if ("int".equals(type) || "char".equals(type)
1217: || "short".equals(type)
1218: || "boolean".equals(type)) {
1219: append(InstructionConstants.IRETURN);
1220: } else if ("long".equals(type)) {
1221: append(InstructionConstants.LRETURN);
1222: } else if ("float".equals(type)) {
1223: append(InstructionConstants.FRETURN);
1224: } else if ("double".equals(type)) {
1225: append(InstructionConstants.DRETURN);
1226: } else {
1227: throw new IllegalArgumentException(
1228: "Unknown argument type");
1229: }
1230: } else {
1231: append(InstructionConstants.ARETURN);
1232: }
1233: }
1234:
1235: // set open stack state for potential continuation code
1236: m_stackState = null;
1237: }
1238:
1239: /**
1240: * Append exception throw.
1241: */
1242: public void appendThrow() {
1243: append(InstructionConstants.ATHROW);
1244: m_stackState = null;
1245: }
1246:
1247: /**
1248: * Append appropriate array load to the instruction list.
1249: *
1250: * @param type array item type expected
1251: */
1252: public void appendALOAD(String type) {
1253: verifyStack("int");
1254: m_stackState.pop();
1255: verifyArray(type);
1256: if ("byte".equals(type) || "boolean".equals(type)) {
1257: append(InstructionConstants.BALOAD);
1258: } else if ("char".equals(type)) {
1259: append(InstructionConstants.CALOAD);
1260: } else if ("double".equals(type)) {
1261: append(InstructionConstants.DALOAD);
1262: } else if ("float".equals(type)) {
1263: append(InstructionConstants.FALOAD);
1264: } else if ("int".equals(type)) {
1265: append(InstructionConstants.IALOAD);
1266: } else if ("long".equals(type)) {
1267: append(InstructionConstants.LALOAD);
1268: } else if ("short".equals(type)) {
1269: append(InstructionConstants.SALOAD);
1270: } else {
1271: append(InstructionConstants.AALOAD);
1272: }
1273: m_stackState.pop();
1274: m_stackState.push(type);
1275: }
1276:
1277: /**
1278: * Append an AASTORE to the instruction list. Doesn't actually check the
1279: * types, just the count of items present.
1280: */
1281: public void appendAASTORE() {
1282: verifyStackDepth(3);
1283: String vtype = m_stackState.pop();
1284: verifyStack("int");
1285: m_stackState.pop();
1286: String atype = verifyArray();
1287: verifyCompatible(vtype, atype);
1288: m_stackState.pop();
1289: append(InstructionConstants.AASTORE);
1290: }
1291:
1292: /**
1293: * Append the appropriate array store to the instruction list.
1294: *
1295: * @param type array item type expected
1296: */
1297: public void appendASTORE(String type) {
1298: verifyStackDepth(3);
1299: String vtype = m_stackState.pop();
1300: verifyStack("int");
1301: m_stackState.pop();
1302: String atype = verifyArray();
1303: verifyCompatible(vtype, atype);
1304: m_stackState.pop();
1305: if ("byte".equals(type) || "boolean".equals(type)) {
1306: append(InstructionConstants.BASTORE);
1307: } else if ("char".equals(type)) {
1308: append(InstructionConstants.CASTORE);
1309: } else if ("double".equals(type)) {
1310: append(InstructionConstants.DASTORE);
1311: } else if ("float".equals(type)) {
1312: append(InstructionConstants.FASTORE);
1313: } else if ("int".equals(type)) {
1314: append(InstructionConstants.IASTORE);
1315: } else if ("long".equals(type)) {
1316: append(InstructionConstants.LASTORE);
1317: } else if ("short".equals(type)) {
1318: append(InstructionConstants.SASTORE);
1319: } else {
1320: append(InstructionConstants.AASTORE);
1321: }
1322: }
1323:
1324: /**
1325: * Append an ACONST_NULL to the instruction list.
1326: */
1327: public void appendACONST_NULL() {
1328: append(InstructionConstants.ACONST_NULL);
1329: m_stackState.push("<null>");
1330: }
1331:
1332: /**
1333: * Append an ARRAYLENGTH to the instruction list.
1334: */
1335: public void appendARRAYLENGTH() {
1336: verifyArray();
1337: append(InstructionConstants.ARRAYLENGTH);
1338: m_stackState.pop();
1339: m_stackState.push("int");
1340: }
1341:
1342: /**
1343: * Append an DCMPG to the instruction list.
1344: */
1345: public void appendDCMPG() {
1346: verifyStack("double", "double");
1347: append(InstructionConstants.DCMPG);
1348: m_stackState.pop(2);
1349: m_stackState.push("int");
1350: }
1351:
1352: /**
1353: * Append a DUP to the instruction list.
1354: */
1355: public void appendDUP() {
1356: verifyStackDepth(1);
1357: append(InstructionConstants.DUP);
1358: m_stackState.push(m_stackState.peek());
1359: }
1360:
1361: /**
1362: * Append a DUP2 to the instruction list.
1363: */
1364: public void appendDUP2() {
1365: verifyStackDepth(1);
1366: append(InstructionConstants.DUP2);
1367: m_stackState.push(m_stackState.peek());
1368: }
1369:
1370: /**
1371: * Append a DUP_X1 to the instruction list.
1372: */
1373: public void appendDUP_X1() {
1374: verifyStackDepth(2);
1375: append(InstructionConstants.DUP_X1);
1376: String hold0 = m_stackState.pop();
1377: String hold1 = m_stackState.pop();
1378: m_stackState.push(hold0);
1379: m_stackState.push(hold1);
1380: m_stackState.push(hold0);
1381: }
1382:
1383: /**
1384: * Append an FCMPG to the instruction list.
1385: */
1386: public void appendFCMPG() {
1387: verifyStack("float", "float");
1388: append(InstructionConstants.FCMPG);
1389: m_stackState.pop(2);
1390: m_stackState.push("int");
1391: }
1392:
1393: /**
1394: * Append an IASTORE to the instruction list. Doesn't actually check the
1395: * types, just the count of items present.
1396: */
1397: public void appendIASTORE() {
1398: verifyStackDepth(3);
1399: append(InstructionConstants.IASTORE);
1400: m_stackState.pop(3);
1401: }
1402:
1403: /**
1404: * Append an ICONST_0 to the instruction list.
1405: */
1406: public void appendICONST_0() {
1407: append(InstructionConstants.ICONST_0);
1408: m_stackState.push("int");
1409: }
1410:
1411: /**
1412: * Append an ICONST_1 to the instruction list.
1413: */
1414: public void appendICONST_1() {
1415: append(InstructionConstants.ICONST_1);
1416: m_stackState.push("int");
1417: }
1418:
1419: /**
1420: * Append an ISUB to the instruction list.
1421: */
1422: public void appendISUB() {
1423: verifyStack("int", "int");
1424: append(InstructionConstants.ISUB);
1425: m_stackState.pop(1);
1426: }
1427:
1428: /**
1429: * Append an IXOR to the instruction list.
1430: */
1431: public void appendIXOR() {
1432: verifyStack("int", "int");
1433: append(InstructionConstants.IXOR);
1434: m_stackState.pop(1);
1435: }
1436:
1437: /**
1438: * Append an LCMP to the instruction list.
1439: */
1440: public void appendLCMP() {
1441: verifyStack("long", "long");
1442: append(InstructionConstants.LCMP);
1443: m_stackState.pop(2);
1444: m_stackState.push("int");
1445: }
1446:
1447: /**
1448: * Append a POP to the instruction list.
1449: */
1450: public void appendPOP() {
1451: verifyStackDepth(1);
1452: String type = m_stackState.peek();
1453: if ("long".equals(type) || "double".equals(type)) {
1454: throw new IllegalStateException(
1455: "Internal error: POP splits long value");
1456: }
1457: append(InstructionConstants.POP);
1458: m_stackState.pop();
1459: }
1460:
1461: /**
1462: * Append a POP2 to the instruction list.
1463: */
1464: public void appendPOP2() {
1465: verifyStackDepth(1);
1466: String type = m_stackState.peek();
1467: if (!"long".equals(type) && !"double".equals(type)) {
1468: throw new IllegalStateException(
1469: "Internal error: POP2 requires long value");
1470: }
1471: append(InstructionConstants.POP2);
1472: m_stackState.pop();
1473: }
1474:
1475: /**
1476: * Append a SWAP to the instruction list.
1477: */
1478: public void appendSWAP() {
1479: verifyStackDepth(2);
1480: append(InstructionConstants.SWAP);
1481: String hold0 = m_stackState.pop();
1482: String hold1 = m_stackState.pop();
1483: m_stackState.push(hold0);
1484: m_stackState.push(hold1);
1485: }
1486:
1487: /**
1488: * Append instructions to exchange a single-word value on the top of the
1489: * stack with the double-word value below it on the stack.
1490: */
1491: public void appendSWAP1For2() {
1492: verifyStackDepth(2);
1493: append(InstructionConstants.DUP_X2);
1494: append(InstructionConstants.POP);
1495: String hold0 = m_stackState.pop();
1496: String hold1 = m_stackState.pop();
1497: m_stackState.push(hold0);
1498: m_stackState.push(hold1);
1499: }
1500:
1501: /**
1502: * Append a compound instruction to the list as a branch target.
1503: *
1504: * @param inst compound instruction to be appended as branch target
1505: * @return branch target information
1506: */
1507: private BranchTarget appendTargetInstruction(
1508: CompoundInstruction inst) {
1509: String[] types = m_stackState.toArray();
1510: InstructionHandle hand = m_instructionList.append(inst);
1511: return new BranchTarget(hand, types);
1512: }
1513:
1514: /**
1515: * Append an instruction to the list as a branch target.
1516: *
1517: * @param inst instruction to be appended as branch target
1518: * @return branch target information
1519: */
1520: private BranchTarget appendTargetInstruction(Instruction inst) {
1521: String[] types = m_stackState.toArray();
1522: InstructionHandle hand = m_instructionList.append(inst);
1523: return new BranchTarget(hand, types);
1524: }
1525:
1526: /**
1527: * Append a NOP to the instruction list as a branch target.
1528: *
1529: * @return branch target information
1530: */
1531: public BranchTarget appendTargetNOP() {
1532: return appendTargetInstruction(InstructionConstants.NOP);
1533: }
1534:
1535: /**
1536: * Append an ACONST_NULL to the instruction list as a branch target.
1537: *
1538: * @return branch target information
1539: */
1540: public BranchTarget appendTargetACONST_NULL() {
1541: BranchTarget target = appendTargetInstruction(InstructionConstants.ACONST_NULL);
1542: m_stackState.push("<null>");
1543: return target;
1544: }
1545:
1546: /**
1547: * Append a load constant instruction as a branch target. Builds the most
1548: * appropriate type of instruction for the value.
1549: *
1550: * @param value constant value to be loaded
1551: * @return branch target information
1552: */
1553: public BranchTarget appendTargetLoadConstant(int value) {
1554: BranchTarget target = appendTargetInstruction(m_instructionBuilder
1555: .createLoadConstant(value));
1556: m_stackState.push("int");
1557: return target;
1558: }
1559:
1560: /**
1561: * Append a load constant instruction as a branch target. Loads a
1562: * <code>String</code> reference from the constant pool.
1563: *
1564: * @param value constant value to be loaded
1565: * @return branch target information
1566: */
1567: public BranchTarget appendTargetLoadConstant(String value) {
1568: BranchTarget target = appendTargetInstruction(m_instructionBuilder
1569: .createLoadConstant(value));
1570: m_stackState.push("java.lang.String");
1571: return target;
1572: }
1573:
1574: /**
1575: * Append instruction to create instance of class as a branch target.
1576: *
1577: * @param name fully qualified class name
1578: * @return branch target information
1579: */
1580: public BranchTarget appendTargetCreateNew(String name) {
1581: BranchTarget target = appendTargetInstruction(m_instructionBuilder
1582: .createNew(name));
1583: m_stackState.push(name);
1584: return target;
1585: }
1586:
1587: /**
1588: * Internal append instruction to create instance of class. This is used by
1589: * subclasses when they need access to the actual instruction handle.
1590: *
1591: * @param name fully qualified class name
1592: */
1593: protected InstructionHandle internalAppendCreateNew(String name) {
1594: InstructionHandle handle = m_instructionList
1595: .append(m_instructionBuilder.createNew(name));
1596: m_stackState.push(name);
1597: return handle;
1598: }
1599:
1600: /**
1601: * Check if top item on stack is a long value.
1602: *
1603: * @return <code>true</code> if long value, <code>false</code> if not
1604: */
1605: public boolean isStackTopLong() {
1606: verifyStackDepth(1);
1607: String type = m_stackState.peek();
1608: return "long".equals(type) || "double".equals(type);
1609: }
1610:
1611: /**
1612: * Initialize stack state to match branch source. This can be used to set
1613: * the expected stack state following an unconditional transfer of control
1614: * instruction. The principle here is that the code to be generated must be
1615: * reached by a branch, so the stack state must match that of the branch
1616: * source.
1617: *
1618: * @param branch wrapper for branch to be for stack initialization
1619: */
1620: public void initStackState(BranchWrapper branch) {
1621: m_stackState = new StringStack(branch.getStackState());
1622: }
1623:
1624: /**
1625: * Initialize stack state to partially match branch source. This can be used
1626: * to set the expected stack state following an unconditional transfer of
1627: * control instruction. The specified number of items are removed from the
1628: * branch stack, with the assumption that code to add these items will be
1629: * appended before the branch join point is reached.
1630: *
1631: * @param branch wrapper for branch to be for stack initialization
1632: * @param pop number of items to be removed from branch source stack state
1633: */
1634: public void initStackState(BranchWrapper branch, int pop) {
1635: m_stackState = new StringStack(branch.getStackState());
1636: if (pop > 0) {
1637: m_stackState.pop(pop);
1638: }
1639: }
1640:
1641: /**
1642: * Initialize stack state to array of value types. This can be used to set
1643: * the expected stack state following an unconditional transfer of control
1644: * instruction.
1645: *
1646: * @param types array of type names on stack
1647: */
1648: protected void initStackState(String[] types) {
1649: m_stackState = new StringStack(types);
1650: }
1651:
1652: /**
1653: * Set branch target as next instruction added to method. This effectively
1654: * sets up a state trigger for the next append operation. The appended
1655: * instruction is set as the target for the branch. This requires that
1656: * instructions are only appended using the methods supplied in this class.
1657: *
1658: * @param branch wrapper for branch to be aimed at next instruction (may be
1659: * <code>null</code>, in which case nothing is done)
1660: */
1661: public void targetNext(BranchWrapper branch) {
1662: if (branch != null) {
1663: if (m_targetBranches == null) {
1664: m_targetBranches = new BranchWrapper[] { branch };
1665: if (m_stackState == null) {
1666: m_stackState = new StringStack(branch
1667: .getStackState());
1668: }
1669: } else {
1670: int length = m_targetBranches.length;
1671: BranchWrapper[] wrappers = new BranchWrapper[length + 1];
1672: System.arraycopy(m_targetBranches, 0, wrappers, 0,
1673: length);
1674: wrappers[length] = branch;
1675: m_targetBranches = wrappers;
1676: }
1677: }
1678: }
1679:
1680: /**
1681: * Set branch targets as next instruction added to method. This effectively
1682: * sets up a state trigger for the next append operation. The appended
1683: * instruction is set as the target for all the branches. This requires that
1684: * instructions are only appended using the methods supplied in this class.
1685: *
1686: * @param branches wrappers for branches to be aimed at next instruction
1687: * (may be <code>null</code>, in which case nothing is done)
1688: */
1689: public void targetNext(BranchWrapper[] branches) {
1690: if (branches != null && branches.length > 0) {
1691: if (m_targetBranches == null) {
1692: m_targetBranches = branches;
1693: if (m_stackState == null) {
1694: m_stackState = new StringStack(branches[0]
1695: .getStackState());
1696: }
1697: } else {
1698: int offset = m_targetBranches.length;
1699: int length = offset + branches.length;
1700: BranchWrapper[] wrappers = new BranchWrapper[length];
1701: System.arraycopy(m_targetBranches, 0, wrappers, 0,
1702: offset);
1703: System.arraycopy(branches, 0, wrappers, offset,
1704: branches.length);
1705: m_targetBranches = wrappers;
1706: }
1707: }
1708: }
1709:
1710: /**
1711: * Process accumulated exceptions. Each subclass must implement this
1712: * method to perform the appropriate handling of the checked exceptions
1713: * that may be thrown in the constructed method.
1714: *
1715: * @throws JiBXException on error in exception handling
1716: */
1717: protected abstract void handleExceptions() throws JiBXException;
1718:
1719: /**
1720: * Complete method construction. Finalizes the instruction list and
1721: * generates the byte code for the constructed method, then computes the
1722: * hash code based on the byte code. If requested, an appropriate suffix is
1723: * tacked on the end of the supplied name in order to make sure that it will
1724: * not be duplicated (even in a superclass or subclass).
1725: *
1726: * @param suffix add suffix to make method name unique
1727: * @throws JiBXException on error in finishing method construction
1728: */
1729: public void codeComplete(boolean suffix) throws JiBXException {
1730: if (m_targetBranches != null) {
1731: throw new IllegalStateException(
1732: "Method complete with pending branch target");
1733: }
1734: if (m_exceptions != null) {
1735: handleExceptions();
1736: }
1737: if (suffix) {
1738: m_generator.setName(getClassFile().makeUniqueMethodName(
1739: m_generator.getName()));
1740: }
1741: m_generator.setMaxStack();
1742: m_generator.setMaxLocals();
1743: m_instructionList.setPositions(true);
1744: m_method = m_generator.getMethod();
1745: m_instructionList.dispose();
1746: m_hashCode = computeMethodHash(m_method);
1747: }
1748:
1749: /**
1750: * Get the method item.
1751: *
1752: * @return method item information
1753: */
1754: public ClassItem getItem() {
1755: if (m_item == null) {
1756: throw new IllegalStateException("Method not added to class");
1757: } else {
1758: return m_item;
1759: }
1760: }
1761:
1762: /**
1763: * Get hash code. This is based only on the byte code in the method, and
1764: * is only valid after the {@link #codeComplete} method is called.
1765: *
1766: * @return hash code based on code sequence
1767: */
1768: public int hashCode() {
1769: if (m_method == null) {
1770: throw new IllegalStateException(
1771: "Method still under construction");
1772: } else {
1773: return m_hashCode;
1774: }
1775: }
1776:
1777: /**
1778: * Add constructed method to class. Makes the method callable, generating
1779: * the method information.
1780: *
1781: * @return added method information
1782: * @throws JiBXException on error in finishing method construction
1783: */
1784: public ClassItem addMethod() throws JiBXException {
1785: if (m_method == null) {
1786: throw new IllegalStateException("Method not finalized.");
1787: } else {
1788: m_item = getClassFile().addMethod(m_method);
1789: return m_item;
1790: }
1791: }
1792: }
|