0001: /*
0002: * Copyright 2000-2004 The Apache Software Foundation
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.apache.bcel.generic;
0018:
0019: import java.util.ArrayList;
0020: import java.util.Hashtable;
0021: import java.util.Iterator;
0022: import java.util.List;
0023: import java.util.Stack;
0024: import org.apache.bcel.Constants;
0025: import org.apache.bcel.classfile.Attribute;
0026: import org.apache.bcel.classfile.Code;
0027: import org.apache.bcel.classfile.CodeException;
0028: import org.apache.bcel.classfile.ExceptionTable;
0029: import org.apache.bcel.classfile.LineNumber;
0030: import org.apache.bcel.classfile.LineNumberTable;
0031: import org.apache.bcel.classfile.LocalVariable;
0032: import org.apache.bcel.classfile.LocalVariableTable;
0033: import org.apache.bcel.classfile.Method;
0034: import org.apache.bcel.classfile.Utility;
0035: import org.apache.bcel.util.BCELComparator;
0036:
0037: /**
0038: * Template class for building up a method. This is done by defining exception
0039: * handlers, adding thrown exceptions, local variables and attributes, whereas
0040: * the `LocalVariableTable' and `LineNumberTable' attributes will be set
0041: * automatically for the code. Use stripAttributes() if you don't like this.
0042: *
0043: * While generating code it may be necessary to insert NOP operations. You can
0044: * use the `removeNOPs' method to get rid off them.
0045: * The resulting method object can be obtained via the `getMethod()' method.
0046: *
0047: * @version $Id: MethodGen.java 386056 2006-03-15 11:31:56Z tcurdt $
0048: * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
0049: * @author <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
0050: * @see InstructionList
0051: * @see Method
0052: */
0053: public class MethodGen extends FieldGenOrMethodGen {
0054:
0055: private String class_name;
0056: private Type[] arg_types;
0057: private String[] arg_names;
0058: private int max_locals;
0059: private int max_stack;
0060: private InstructionList il;
0061: private boolean strip_attributes;
0062: private List variable_vec = new ArrayList();
0063: private List line_number_vec = new ArrayList();
0064: private List exception_vec = new ArrayList();
0065: private List throws_vec = new ArrayList();
0066: private List code_attrs_vec = new ArrayList();
0067: private static BCELComparator _cmp = new BCELComparator() {
0068:
0069: public boolean equals(Object o1, Object o2) {
0070: MethodGen THIS = (MethodGen) o1;
0071: MethodGen THAT = (MethodGen) o2;
0072: return THIS.getName().equals(THAT.getName())
0073: && THIS.getSignature().equals(THAT.getSignature());
0074: }
0075:
0076: public int hashCode(Object o) {
0077: MethodGen THIS = (MethodGen) o;
0078: return THIS.getSignature().hashCode()
0079: ^ THIS.getName().hashCode();
0080: }
0081: };
0082:
0083: /**
0084: * Declare method. If the method is non-static the constructor
0085: * automatically declares a local variable `$this' in slot 0. The
0086: * actual code is contained in the `il' parameter, which may further
0087: * manipulated by the user. But he must take care not to remove any
0088: * instruction (handles) that are still referenced from this object.
0089: *
0090: * For example one may not add a local variable and later remove the
0091: * instructions it refers to without causing havoc. It is safe
0092: * however if you remove that local variable, too.
0093: *
0094: * @param access_flags access qualifiers
0095: * @param return_type method type
0096: * @param arg_types argument types
0097: * @param arg_names argument names (if this is null, default names will be provided
0098: * for them)
0099: * @param method_name name of method
0100: * @param class_name class name containing this method (may be null, if you don't care)
0101: * @param il instruction list associated with this method, may be null only for
0102: * abstract or native methods
0103: * @param cp constant pool
0104: */
0105: public MethodGen(int access_flags, Type return_type,
0106: Type[] arg_types, String[] arg_names, String method_name,
0107: String class_name, InstructionList il, ConstantPoolGen cp) {
0108: setAccessFlags(access_flags);
0109: setType(return_type);
0110: setArgumentTypes(arg_types);
0111: setArgumentNames(arg_names);
0112: setName(method_name);
0113: setClassName(class_name);
0114: setInstructionList(il);
0115: setConstantPool(cp);
0116: boolean abstract_ = isAbstract() || isNative();
0117: InstructionHandle start = null;
0118: InstructionHandle end = null;
0119: if (!abstract_) {
0120: start = il.getStart();
0121: end = il.getEnd();
0122: /* Add local variables, namely the implicit `this' and the arguments
0123: */
0124: if (!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
0125: addLocalVariable("this", new ObjectType(class_name),
0126: start, end);
0127: }
0128: }
0129: if (arg_types != null) {
0130: int size = arg_types.length;
0131: for (int i = 0; i < size; i++) {
0132: if (Type.VOID == arg_types[i]) {
0133: throw new ClassGenException(
0134: "'void' is an illegal argument type for a method");
0135: }
0136: }
0137: if (arg_names != null) { // Names for variables provided?
0138: if (size != arg_names.length) {
0139: throw new ClassGenException(
0140: "Mismatch in argument array lengths: "
0141: + size + " vs. " + arg_names.length);
0142: }
0143: } else { // Give them dummy names
0144: arg_names = new String[size];
0145: for (int i = 0; i < size; i++) {
0146: arg_names[i] = "arg" + i;
0147: }
0148: setArgumentNames(arg_names);
0149: }
0150: if (!abstract_) {
0151: for (int i = 0; i < size; i++) {
0152: addLocalVariable(arg_names[i], arg_types[i], start,
0153: end);
0154: }
0155: }
0156: }
0157: }
0158:
0159: /**
0160: * Instantiate from existing method.
0161: *
0162: * @param m method
0163: * @param class_name class name containing this method
0164: * @param cp constant pool
0165: */
0166: public MethodGen(Method m, String class_name, ConstantPoolGen cp) {
0167: this (
0168: m.getAccessFlags(),
0169: Type.getReturnType(m.getSignature()),
0170: Type.getArgumentTypes(m.getSignature()),
0171: null /* may be overridden anyway */
0172: ,
0173: m.getName(),
0174: class_name,
0175: ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0) ? new InstructionList(
0176: m.getCode().getCode())
0177: : null, cp);
0178: Attribute[] attributes = m.getAttributes();
0179: for (int i = 0; i < attributes.length; i++) {
0180: Attribute a = attributes[i];
0181: if (a instanceof Code) {
0182: Code c = (Code) a;
0183: setMaxStack(c.getMaxStack());
0184: setMaxLocals(c.getMaxLocals());
0185: CodeException[] ces = c.getExceptionTable();
0186: if (ces != null) {
0187: for (int j = 0; j < ces.length; j++) {
0188: CodeException ce = ces[j];
0189: int type = ce.getCatchType();
0190: ObjectType c_type = null;
0191: if (type > 0) {
0192: String cen = m.getConstantPool()
0193: .getConstantString(type,
0194: Constants.CONSTANT_Class);
0195: c_type = new ObjectType(cen);
0196: }
0197: int end_pc = ce.getEndPC();
0198: int length = m.getCode().getCode().length;
0199: InstructionHandle end;
0200: if (length == end_pc) { // May happen, because end_pc is exclusive
0201: end = il.getEnd();
0202: } else {
0203: end = il.findHandle(end_pc);
0204: end = end.getPrev(); // Make it inclusive
0205: }
0206: addExceptionHandler(il.findHandle(ce
0207: .getStartPC()), end, il.findHandle(ce
0208: .getHandlerPC()), c_type);
0209: }
0210: }
0211: Attribute[] c_attributes = c.getAttributes();
0212: for (int j = 0; j < c_attributes.length; j++) {
0213: a = c_attributes[j];
0214: if (a instanceof LineNumberTable) {
0215: LineNumber[] ln = ((LineNumberTable) a)
0216: .getLineNumberTable();
0217: for (int k = 0; k < ln.length; k++) {
0218: LineNumber l = ln[k];
0219: InstructionHandle ih = il.findHandle(l
0220: .getStartPC());
0221: if (ih != null) {
0222: addLineNumber(ih, l.getLineNumber());
0223: }
0224: }
0225: } else if (a instanceof LocalVariableTable) {
0226: LocalVariable[] lv = ((LocalVariableTable) a)
0227: .getLocalVariableTable();
0228: removeLocalVariables();
0229: for (int k = 0; k < lv.length; k++) {
0230: LocalVariable l = lv[k];
0231: InstructionHandle start = il.findHandle(l
0232: .getStartPC());
0233: InstructionHandle end = il.findHandle(l
0234: .getStartPC()
0235: + l.getLength());
0236: // Repair malformed handles
0237: if (null == start) {
0238: start = il.getStart();
0239: }
0240: if (null == end) {
0241: end = il.getEnd();
0242: }
0243: addLocalVariable(l.getName(), Type
0244: .getType(l.getSignature()), l
0245: .getIndex(), start, end);
0246: }
0247: } else {
0248: addCodeAttribute(a);
0249: }
0250: }
0251: } else if (a instanceof ExceptionTable) {
0252: String[] names = ((ExceptionTable) a)
0253: .getExceptionNames();
0254: for (int j = 0; j < names.length; j++) {
0255: addException(names[j]);
0256: }
0257: } else {
0258: addAttribute(a);
0259: }
0260: }
0261: }
0262:
0263: /**
0264: * Adds a local variable to this method.
0265: *
0266: * @param name variable name
0267: * @param type variable type
0268: * @param slot the index of the local variable, if type is long or double, the next available
0269: * index is slot+2
0270: * @param start from where the variable is valid
0271: * @param end until where the variable is valid
0272: * @return new local variable object
0273: * @see LocalVariable
0274: */
0275: public LocalVariableGen addLocalVariable(String name, Type type,
0276: int slot, InstructionHandle start, InstructionHandle end) {
0277: byte t = type.getType();
0278: if (t != Constants.T_ADDRESS) {
0279: int add = type.getSize();
0280: if (slot + add > max_locals) {
0281: max_locals = slot + add;
0282: }
0283: LocalVariableGen l = new LocalVariableGen(slot, name, type,
0284: start, end);
0285: int i;
0286: if ((i = variable_vec.indexOf(l)) >= 0) {
0287: variable_vec.set(i, l);
0288: } else {
0289: variable_vec.add(l);
0290: }
0291: return l;
0292: } else {
0293: throw new IllegalArgumentException("Can not use " + type
0294: + " as type for local variable");
0295: }
0296: }
0297:
0298: /**
0299: * Adds a local variable to this method and assigns an index automatically.
0300: *
0301: * @param name variable name
0302: * @param type variable type
0303: * @param start from where the variable is valid, if this is null,
0304: * it is valid from the start
0305: * @param end until where the variable is valid, if this is null,
0306: * it is valid to the end
0307: * @return new local variable object
0308: * @see LocalVariable
0309: */
0310: public LocalVariableGen addLocalVariable(String name, Type type,
0311: InstructionHandle start, InstructionHandle end) {
0312: return addLocalVariable(name, type, max_locals, start, end);
0313: }
0314:
0315: /**
0316: * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
0317: * with an explicit index argument.
0318: */
0319: public void removeLocalVariable(LocalVariableGen l) {
0320: variable_vec.remove(l);
0321: }
0322:
0323: /**
0324: * Remove all local variables.
0325: */
0326: public void removeLocalVariables() {
0327: variable_vec.clear();
0328: }
0329:
0330: /**
0331: * Sort local variables by index
0332: */
0333: private static final void sort(LocalVariableGen[] vars, int l, int r) {
0334: int i = l, j = r;
0335: int m = vars[(l + r) / 2].getIndex();
0336: LocalVariableGen h;
0337: do {
0338: while (vars[i].getIndex() < m) {
0339: i++;
0340: }
0341: while (m < vars[j].getIndex()) {
0342: j--;
0343: }
0344: if (i <= j) {
0345: h = vars[i];
0346: vars[i] = vars[j];
0347: vars[j] = h; // Swap elements
0348: i++;
0349: j--;
0350: }
0351: } while (i <= j);
0352: if (l < j) {
0353: sort(vars, l, j);
0354: }
0355: if (i < r) {
0356: sort(vars, i, r);
0357: }
0358: }
0359:
0360: /*
0361: * If the range of the variable has not been set yet, it will be set to be valid from
0362: * the start to the end of the instruction list.
0363: *
0364: * @return array of declared local variables sorted by index
0365: */
0366: public LocalVariableGen[] getLocalVariables() {
0367: int size = variable_vec.size();
0368: LocalVariableGen[] lg = new LocalVariableGen[size];
0369: variable_vec.toArray(lg);
0370: for (int i = 0; i < size; i++) {
0371: if (lg[i].getStart() == null) {
0372: lg[i].setStart(il.getStart());
0373: }
0374: if (lg[i].getEnd() == null) {
0375: lg[i].setEnd(il.getEnd());
0376: }
0377: }
0378: if (size > 1) {
0379: sort(lg, 0, size - 1);
0380: }
0381: return lg;
0382: }
0383:
0384: /**
0385: * @return `LocalVariableTable' attribute of all the local variables of this method.
0386: */
0387: public LocalVariableTable getLocalVariableTable(ConstantPoolGen cp) {
0388: LocalVariableGen[] lg = getLocalVariables();
0389: int size = lg.length;
0390: LocalVariable[] lv = new LocalVariable[size];
0391: for (int i = 0; i < size; i++) {
0392: lv[i] = lg[i].getLocalVariable(cp);
0393: }
0394: return new LocalVariableTable(cp.addUtf8("LocalVariableTable"),
0395: 2 + lv.length * 10, lv, cp.getConstantPool());
0396: }
0397:
0398: /**
0399: * Give an instruction a line number corresponding to the source code line.
0400: *
0401: * @param ih instruction to tag
0402: * @return new line number object
0403: * @see LineNumber
0404: */
0405: public LineNumberGen addLineNumber(InstructionHandle ih,
0406: int src_line) {
0407: LineNumberGen l = new LineNumberGen(ih, src_line);
0408: line_number_vec.add(l);
0409: return l;
0410: }
0411:
0412: /**
0413: * Remove a line number.
0414: */
0415: public void removeLineNumber(LineNumberGen l) {
0416: line_number_vec.remove(l);
0417: }
0418:
0419: /**
0420: * Remove all line numbers.
0421: */
0422: public void removeLineNumbers() {
0423: line_number_vec.clear();
0424: }
0425:
0426: /*
0427: * @return array of line numbers
0428: */
0429: public LineNumberGen[] getLineNumbers() {
0430: LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
0431: line_number_vec.toArray(lg);
0432: return lg;
0433: }
0434:
0435: /**
0436: * @return `LineNumberTable' attribute of all the local variables of this method.
0437: */
0438: public LineNumberTable getLineNumberTable(ConstantPoolGen cp) {
0439: int size = line_number_vec.size();
0440: LineNumber[] ln = new LineNumber[size];
0441: try {
0442: for (int i = 0; i < size; i++) {
0443: ln[i] = ((LineNumberGen) line_number_vec.get(i))
0444: .getLineNumber();
0445: }
0446: } catch (ArrayIndexOutOfBoundsException e) {
0447: } // Never occurs
0448: return new LineNumberTable(cp.addUtf8("LineNumberTable"),
0449: 2 + ln.length * 4, ln, cp.getConstantPool());
0450: }
0451:
0452: /**
0453: * Add an exception handler, i.e., specify region where a handler is active and an
0454: * instruction where the actual handling is done.
0455: *
0456: * @param start_pc Start of region (inclusive)
0457: * @param end_pc End of region (inclusive)
0458: * @param handler_pc Where handling is done
0459: * @param catch_type class type of handled exception or null if any
0460: * exception is handled
0461: * @return new exception handler object
0462: */
0463: public CodeExceptionGen addExceptionHandler(
0464: InstructionHandle start_pc, InstructionHandle end_pc,
0465: InstructionHandle handler_pc, ObjectType catch_type) {
0466: if ((start_pc == null) || (end_pc == null)
0467: || (handler_pc == null)) {
0468: throw new ClassGenException(
0469: "Exception handler target is null instruction");
0470: }
0471: CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc,
0472: handler_pc, catch_type);
0473: exception_vec.add(c);
0474: return c;
0475: }
0476:
0477: /**
0478: * Remove an exception handler.
0479: */
0480: public void removeExceptionHandler(CodeExceptionGen c) {
0481: exception_vec.remove(c);
0482: }
0483:
0484: /**
0485: * Remove all line numbers.
0486: */
0487: public void removeExceptionHandlers() {
0488: exception_vec.clear();
0489: }
0490:
0491: /*
0492: * @return array of declared exception handlers
0493: */
0494: public CodeExceptionGen[] getExceptionHandlers() {
0495: CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec
0496: .size()];
0497: exception_vec.toArray(cg);
0498: return cg;
0499: }
0500:
0501: /**
0502: * @return code exceptions for `Code' attribute
0503: */
0504: private CodeException[] getCodeExceptions() {
0505: int size = exception_vec.size();
0506: CodeException[] c_exc = new CodeException[size];
0507: try {
0508: for (int i = 0; i < size; i++) {
0509: CodeExceptionGen c = (CodeExceptionGen) exception_vec
0510: .get(i);
0511: c_exc[i] = c.getCodeException(cp);
0512: }
0513: } catch (ArrayIndexOutOfBoundsException e) {
0514: }
0515: return c_exc;
0516: }
0517:
0518: /**
0519: * Add an exception possibly thrown by this method.
0520: *
0521: * @param class_name (fully qualified) name of exception
0522: */
0523: public void addException(String class_name) {
0524: throws_vec.add(class_name);
0525: }
0526:
0527: /**
0528: * Remove an exception.
0529: */
0530: public void removeException(String c) {
0531: throws_vec.remove(c);
0532: }
0533:
0534: /**
0535: * Remove all exceptions.
0536: */
0537: public void removeExceptions() {
0538: throws_vec.clear();
0539: }
0540:
0541: /*
0542: * @return array of thrown exceptions
0543: */
0544: public String[] getExceptions() {
0545: String[] e = new String[throws_vec.size()];
0546: throws_vec.toArray(e);
0547: return e;
0548: }
0549:
0550: /**
0551: * @return `Exceptions' attribute of all the exceptions thrown by this method.
0552: */
0553: private ExceptionTable getExceptionTable(ConstantPoolGen cp) {
0554: int size = throws_vec.size();
0555: int[] ex = new int[size];
0556: try {
0557: for (int i = 0; i < size; i++) {
0558: ex[i] = cp.addClass((String) throws_vec.get(i));
0559: }
0560: } catch (ArrayIndexOutOfBoundsException e) {
0561: }
0562: return new ExceptionTable(cp.addUtf8("Exceptions"),
0563: 2 + 2 * size, ex, cp.getConstantPool());
0564: }
0565:
0566: /**
0567: * Add an attribute to the code. Currently, the JVM knows about the
0568: * LineNumberTable, LocalVariableTable and StackMap attributes,
0569: * where the former two will be generated automatically and the
0570: * latter is used for the MIDP only. Other attributes will be
0571: * ignored by the JVM but do no harm.
0572: *
0573: * @param a attribute to be added
0574: */
0575: public void addCodeAttribute(Attribute a) {
0576: code_attrs_vec.add(a);
0577: }
0578:
0579: /**
0580: * Remove a code attribute.
0581: */
0582: public void removeCodeAttribute(Attribute a) {
0583: code_attrs_vec.remove(a);
0584: }
0585:
0586: /**
0587: * Remove all code attributes.
0588: */
0589: public void removeCodeAttributes() {
0590: code_attrs_vec.clear();
0591: }
0592:
0593: /**
0594: * @return all attributes of this method.
0595: */
0596: public Attribute[] getCodeAttributes() {
0597: Attribute[] attributes = new Attribute[code_attrs_vec.size()];
0598: code_attrs_vec.toArray(attributes);
0599: return attributes;
0600: }
0601:
0602: /**
0603: * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
0604: * before calling this method (the same applies for max locals).
0605: *
0606: * @return method object
0607: */
0608: public Method getMethod() {
0609: String signature = getSignature();
0610: int name_index = cp.addUtf8(name);
0611: int signature_index = cp.addUtf8(signature);
0612: /* Also updates positions of instructions, i.e., their indices
0613: */
0614: byte[] byte_code = null;
0615: if (il != null) {
0616: byte_code = il.getByteCode();
0617: }
0618: LineNumberTable lnt = null;
0619: LocalVariableTable lvt = null;
0620: /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
0621: */
0622: if ((variable_vec.size() > 0) && !strip_attributes) {
0623: addCodeAttribute(lvt = getLocalVariableTable(cp));
0624: }
0625: if ((line_number_vec.size() > 0) && !strip_attributes) {
0626: addCodeAttribute(lnt = getLineNumberTable(cp));
0627: }
0628: Attribute[] code_attrs = getCodeAttributes();
0629: /* Each attribute causes 6 additional header bytes
0630: */
0631: int attrs_len = 0;
0632: for (int i = 0; i < code_attrs.length; i++) {
0633: attrs_len += (code_attrs[i].getLength() + 6);
0634: }
0635: CodeException[] c_exc = getCodeExceptions();
0636: int exc_len = c_exc.length * 8; // Every entry takes 8 bytes
0637: Code code = null;
0638: if ((il != null) && !isAbstract() && !isNative()) {
0639: // Remove any stale code attribute
0640: Attribute[] attributes = getAttributes();
0641: for (int i = 0; i < attributes.length; i++) {
0642: Attribute a = attributes[i];
0643: if (a instanceof Code) {
0644: removeAttribute(a);
0645: }
0646: }
0647: code = new Code(cp.addUtf8("Code"),
0648: 8 + byte_code.length + // prologue byte code
0649: 2 + exc_len + // exceptions
0650: 2 + attrs_len, // attributes
0651: max_stack, max_locals, byte_code, c_exc,
0652: code_attrs, cp.getConstantPool());
0653: addAttribute(code);
0654: }
0655: ExceptionTable et = null;
0656: if (throws_vec.size() > 0) {
0657: addAttribute(et = getExceptionTable(cp));
0658: // Add `Exceptions' if there are "throws" clauses
0659: }
0660: Method m = new Method(access_flags, name_index,
0661: signature_index, getAttributes(), cp.getConstantPool());
0662: // Undo effects of adding attributes
0663: if (lvt != null) {
0664: removeCodeAttribute(lvt);
0665: }
0666: if (lnt != null) {
0667: removeCodeAttribute(lnt);
0668: }
0669: if (code != null) {
0670: removeAttribute(code);
0671: }
0672: if (et != null) {
0673: removeAttribute(et);
0674: }
0675: return m;
0676: }
0677:
0678: /**
0679: * Remove all NOPs from the instruction list (if possible) and update every
0680: * object refering to them, i.e., branch instructions, local variables and
0681: * exception handlers.
0682: */
0683: public void removeNOPs() {
0684: if (il != null) {
0685: InstructionHandle next;
0686: /* Check branch instructions.
0687: */
0688: for (InstructionHandle ih = il.getStart(); ih != null; ih = next) {
0689: next = ih.next;
0690: if ((next != null)
0691: && (ih.getInstruction() instanceof NOP)) {
0692: try {
0693: il.delete(ih);
0694: } catch (TargetLostException e) {
0695: InstructionHandle[] targets = e.getTargets();
0696: for (int i = 0; i < targets.length; i++) {
0697: InstructionTargeter[] targeters = targets[i]
0698: .getTargeters();
0699: for (int j = 0; j < targeters.length; j++) {
0700: targeters[j].updateTarget(targets[i],
0701: next);
0702: }
0703: }
0704: }
0705: }
0706: }
0707: }
0708: }
0709:
0710: /**
0711: * Set maximum number of local variables.
0712: */
0713: public void setMaxLocals(int m) {
0714: max_locals = m;
0715: }
0716:
0717: public int getMaxLocals() {
0718: return max_locals;
0719: }
0720:
0721: /**
0722: * Set maximum stack size for this method.
0723: */
0724: public void setMaxStack(int m) {
0725: max_stack = m;
0726: }
0727:
0728: public int getMaxStack() {
0729: return max_stack;
0730: }
0731:
0732: /** @return class that contains this method
0733: */
0734: public String getClassName() {
0735: return class_name;
0736: }
0737:
0738: public void setClassName(String class_name) {
0739: this .class_name = class_name;
0740: }
0741:
0742: public void setReturnType(Type return_type) {
0743: setType(return_type);
0744: }
0745:
0746: public Type getReturnType() {
0747: return getType();
0748: }
0749:
0750: public void setArgumentTypes(Type[] arg_types) {
0751: this .arg_types = arg_types;
0752: }
0753:
0754: public Type[] getArgumentTypes() {
0755: return (Type[]) arg_types.clone();
0756: }
0757:
0758: public void setArgumentType(int i, Type type) {
0759: arg_types[i] = type;
0760: }
0761:
0762: public Type getArgumentType(int i) {
0763: return arg_types[i];
0764: }
0765:
0766: public void setArgumentNames(String[] arg_names) {
0767: this .arg_names = arg_names;
0768: }
0769:
0770: public String[] getArgumentNames() {
0771: return (String[]) arg_names.clone();
0772: }
0773:
0774: public void setArgumentName(int i, String name) {
0775: arg_names[i] = name;
0776: }
0777:
0778: public String getArgumentName(int i) {
0779: return arg_names[i];
0780: }
0781:
0782: public InstructionList getInstructionList() {
0783: return il;
0784: }
0785:
0786: public void setInstructionList(InstructionList il) {
0787: this .il = il;
0788: }
0789:
0790: public String getSignature() {
0791: return Type.getMethodSignature(type, arg_types);
0792: }
0793:
0794: /**
0795: * Computes max. stack size by performing control flow analysis.
0796: */
0797: public void setMaxStack() {
0798: if (il != null) {
0799: max_stack = getMaxStack(cp, il, getExceptionHandlers());
0800: } else {
0801: max_stack = 0;
0802: }
0803: }
0804:
0805: /**
0806: * Compute maximum number of local variables.
0807: */
0808: public void setMaxLocals() {
0809: if (il != null) {
0810: int max = isStatic() ? 0 : 1;
0811: if (arg_types != null) {
0812: for (int i = 0; i < arg_types.length; i++) {
0813: max += arg_types[i].getSize();
0814: }
0815: }
0816: for (InstructionHandle ih = il.getStart(); ih != null; ih = ih
0817: .getNext()) {
0818: Instruction ins = ih.getInstruction();
0819: if ((ins instanceof LocalVariableInstruction)
0820: || (ins instanceof RET)
0821: || (ins instanceof IINC)) {
0822: int index = ((IndexedInstruction) ins).getIndex()
0823: + ((TypedInstruction) ins).getType(cp)
0824: .getSize();
0825: if (index > max) {
0826: max = index;
0827: }
0828: }
0829: }
0830: max_locals = max;
0831: } else {
0832: max_locals = 0;
0833: }
0834: }
0835:
0836: /** Do not/Do produce attributes code attributesLineNumberTable and
0837: * LocalVariableTable, like javac -O
0838: */
0839: public void stripAttributes(boolean flag) {
0840: strip_attributes = flag;
0841: }
0842:
0843: static final class BranchTarget {
0844:
0845: InstructionHandle target;
0846: int stackDepth;
0847:
0848: BranchTarget(InstructionHandle target, int stackDepth) {
0849: this .target = target;
0850: this .stackDepth = stackDepth;
0851: }
0852: }
0853:
0854: static final class BranchStack {
0855:
0856: Stack branchTargets = new Stack();
0857: Hashtable visitedTargets = new Hashtable();
0858:
0859: public void push(InstructionHandle target, int stackDepth) {
0860: if (visited(target)) {
0861: return;
0862: }
0863: branchTargets.push(visit(target, stackDepth));
0864: }
0865:
0866: public BranchTarget pop() {
0867: if (!branchTargets.empty()) {
0868: BranchTarget bt = (BranchTarget) branchTargets.pop();
0869: return bt;
0870: }
0871: return null;
0872: }
0873:
0874: private final BranchTarget visit(InstructionHandle target,
0875: int stackDepth) {
0876: BranchTarget bt = new BranchTarget(target, stackDepth);
0877: visitedTargets.put(target, bt);
0878: return bt;
0879: }
0880:
0881: private final boolean visited(InstructionHandle target) {
0882: return (visitedTargets.get(target) != null);
0883: }
0884: }
0885:
0886: /**
0887: * Computes stack usage of an instruction list by performing control flow analysis.
0888: *
0889: * @return maximum stack depth used by method
0890: */
0891: public static int getMaxStack(ConstantPoolGen cp,
0892: InstructionList il, CodeExceptionGen[] et) {
0893: BranchStack branchTargets = new BranchStack();
0894: /* Initially, populate the branch stack with the exception
0895: * handlers, because these aren't (necessarily) branched to
0896: * explicitly. in each case, the stack will have depth 1,
0897: * containing the exception object.
0898: */
0899: for (int i = 0; i < et.length; i++) {
0900: InstructionHandle handler_pc = et[i].getHandlerPC();
0901: if (handler_pc != null) {
0902: branchTargets.push(handler_pc, 1);
0903: }
0904: }
0905: int stackDepth = 0, maxStackDepth = 0;
0906: InstructionHandle ih = il.getStart();
0907: while (ih != null) {
0908: Instruction instruction = ih.getInstruction();
0909: short opcode = instruction.getOpcode();
0910: int delta = instruction.produceStack(cp)
0911: - instruction.consumeStack(cp);
0912: stackDepth += delta;
0913: if (stackDepth > maxStackDepth) {
0914: maxStackDepth = stackDepth;
0915: }
0916: // choose the next instruction based on whether current is a branch.
0917: if (instruction instanceof BranchInstruction) {
0918: BranchInstruction branch = (BranchInstruction) instruction;
0919: if (instruction instanceof Select) {
0920: // explore all of the select's targets. the default target is handled below.
0921: Select select = (Select) branch;
0922: InstructionHandle[] targets = select.getTargets();
0923: for (int i = 0; i < targets.length; i++) {
0924: branchTargets.push(targets[i], stackDepth);
0925: }
0926: // nothing to fall through to.
0927: ih = null;
0928: } else if (!(branch instanceof IfInstruction)) {
0929: // if an instruction that comes back to following PC,
0930: // push next instruction, with stack depth reduced by 1.
0931: if (opcode == Constants.JSR
0932: || opcode == Constants.JSR_W) {
0933: branchTargets
0934: .push(ih.getNext(), stackDepth - 1);
0935: }
0936: ih = null;
0937: }
0938: // for all branches, the target of the branch is pushed on the branch stack.
0939: // conditional branches have a fall through case, selects don't, and
0940: // jsr/jsr_w return to the next instruction.
0941: branchTargets.push(branch.getTarget(), stackDepth);
0942: } else {
0943: // check for instructions that terminate the method.
0944: if (opcode == Constants.ATHROW
0945: || opcode == Constants.RET
0946: || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
0947: ih = null;
0948: }
0949: }
0950: // normal case, go to the next instruction.
0951: if (ih != null) {
0952: ih = ih.getNext();
0953: }
0954: // if we have no more instructions, see if there are any deferred branches to explore.
0955: if (ih == null) {
0956: BranchTarget bt = branchTargets.pop();
0957: if (bt != null) {
0958: ih = bt.target;
0959: stackDepth = bt.stackDepth;
0960: }
0961: }
0962: }
0963: return maxStackDepth;
0964: }
0965:
0966: private List observers;
0967:
0968: /** Add observer for this object.
0969: */
0970: public void addObserver(MethodObserver o) {
0971: if (observers == null) {
0972: observers = new ArrayList();
0973: }
0974: observers.add(o);
0975: }
0976:
0977: /** Remove observer for this object.
0978: */
0979: public void removeObserver(MethodObserver o) {
0980: if (observers != null) {
0981: observers.remove(o);
0982: }
0983: }
0984:
0985: /** Call notify() method on all observers. This method is not called
0986: * automatically whenever the state has changed, but has to be
0987: * called by the user after he has finished editing the object.
0988: */
0989: public void update() {
0990: if (observers != null) {
0991: for (Iterator e = observers.iterator(); e.hasNext();) {
0992: ((MethodObserver) e.next()).notify(this );
0993: }
0994: }
0995: }
0996:
0997: /**
0998: * Return string representation close to declaration format,
0999: * `public static void main(String[]) throws IOException', e.g.
1000: *
1001: * @return String representation of the method.
1002: */
1003: public final String toString() {
1004: String access = Utility.accessToString(access_flags);
1005: String signature = Type.getMethodSignature(type, arg_types);
1006: signature = Utility.methodSignatureToString(signature, name,
1007: access, true, getLocalVariableTable(cp));
1008: StringBuffer buf = new StringBuffer(signature);
1009: if (throws_vec.size() > 0) {
1010: for (Iterator e = throws_vec.iterator(); e.hasNext();) {
1011: buf.append("\n\t\tthrows ").append(e.next());
1012: }
1013: }
1014: return buf.toString();
1015: }
1016:
1017: /** @return deep copy of this method
1018: */
1019: public MethodGen copy(String class_name, ConstantPoolGen cp) {
1020: Method m = ((MethodGen) clone()).getMethod();
1021: MethodGen mg = new MethodGen(m, class_name, this .cp);
1022: if (this .cp != cp) {
1023: mg.setConstantPool(cp);
1024: mg.getInstructionList().replaceConstantPool(this .cp, cp);
1025: }
1026: return mg;
1027: }
1028:
1029: /**
1030: * @return Comparison strategy object
1031: */
1032: public static BCELComparator getComparator() {
1033: return _cmp;
1034: }
1035:
1036: /**
1037: * @param comparator Comparison strategy object
1038: */
1039: public static void setComparator(BCELComparator comparator) {
1040: _cmp = comparator;
1041: }
1042:
1043: /**
1044: * Return value as defined by given BCELComparator strategy.
1045: * By default two MethodGen objects are said to be equal when
1046: * their names and signatures are equal.
1047: *
1048: * @see java.lang.Object#equals(java.lang.Object)
1049: */
1050: public boolean equals(Object obj) {
1051: return _cmp.equals(this , obj);
1052: }
1053:
1054: /**
1055: * Return value as defined by given BCELComparator strategy.
1056: * By default return the hashcode of the method's name XOR signature.
1057: *
1058: * @see java.lang.Object#hashCode()
1059: */
1060: public int hashCode() {
1061: return _cmp.hashCode(this);
1062: }
1063: }
|