0001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
0002:
0003: This file is part of the db4o open source object database.
0004:
0005: db4o is free software; you can redistribute it and/or modify it under
0006: the terms of version 2 of the GNU General Public License as published
0007: by the Free Software Foundation and as clarified by db4objects' GPL
0008: interpretation policy, available at
0009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
0010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
0011: Suite 350, San Mateo, CA 94403, USA.
0012:
0013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
0014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: for more details.
0017:
0018: You should have received a copy of the GNU General Public License along
0019: with this program; if not, write to the Free Software Foundation, Inc.,
0020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
0021: package EDU.purdue.cs.bloat.editor;
0022:
0023: import java.io.*;
0024: import java.util.*;
0025:
0026: import EDU.purdue.cs.bloat.reflect.*;
0027: import EDU.purdue.cs.bloat.tree.*;
0028: import EDU.purdue.cs.bloat.util.*;
0029:
0030: /**
0031: * <tt>MethodEditor</tt> provides a means to edit a method of a class. A
0032: * <tt>MethodEditor</tt> gathers information from a <tt>MethodInfo</tt>
0033: * object. It then goes through the bytecodes of the method and extracts
0034: * information about the method. Along the way it creates an array of
0035: * <tt>Instruction</tt> and <tt>Label</tt> objects that represent the code.
0036: * Additionally, it models the try-catch blocks in the method and their
0037: * associated exception handlers.
0038: *
0039: * @see EDU.purdue.cs.bloat.reflect.MethodInfo
0040: * @see Label
0041: * @see Instruction
0042: *
0043: * @author Nate Nystrom (<a
0044: * href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
0045: */
0046: public class MethodEditor implements Opcode {
0047: public static boolean PRESERVE_DEBUG = true;
0048:
0049: public static boolean UNIQUE_HANDLERS = false;
0050:
0051: public static boolean OPT_STACK_2 = false; // byte-code level stack opt
0052:
0053: private ClassEditor editor; // The editor that "owns" this MethodEditor
0054:
0055: private MethodInfo methodInfo; // Representation of this method
0056:
0057: private String name; // The name of this method
0058:
0059: private Type type; // Type variable representing the class's
0060:
0061: // descriptor
0062: private LinkedList code; // Label and Instruction objects representing
0063: // this
0064:
0065: // method's bytecode
0066: private LinkedList tryCatches; // Info about the try-catch blocks in this
0067: // method
0068:
0069: private LinkedList lineNumbers;
0070:
0071: private LocalVariable[] params; // The parameters to this method
0072:
0073: private int maxStack; // Max size of stack while running this method
0074:
0075: private int maxLabel; // Label pointing to the end of the code
0076:
0077: private int maxLocals; // Maximum number of local variables
0078:
0079: private boolean isDirty; // Has the method been modified?
0080:
0081: private Map locals; // Maps indices to that LocalVariable
0082:
0083: private Type[] paramTypes; // Types of parameters (accounts for wides)
0084:
0085: public UseMap uMap; // Structure for remembering use/def info
0086:
0087: private boolean isDeleted = false;
0088:
0089: public MethodEditor(final ClassEditor editor, final int modifiers,
0090: final Class returnType, final String methodName,
0091: final Class[] paramTypes, final Class[] exceptionTypes) {
0092:
0093: this (editor, modifiers, (returnType == null ? null : Type
0094: .getType(returnType)), methodName, MethodEditor
0095: .convertTypes(paramTypes), MethodEditor
0096: .convertTypes(exceptionTypes));
0097: }
0098:
0099: private static Type[] convertTypes(final Class[] classes) {
0100: if (classes == null) {
0101: return (null);
0102: }
0103:
0104: final Type[] types = new Type[classes.length];
0105: for (int i = 0; i < types.length; i++) {
0106: types[i] = Type.getType(classes[i]);
0107: }
0108: return (types);
0109: }
0110:
0111: /**
0112: * Creates a new <code>MethodEditor</code> for editing a method in a given
0113: * class with the given modifiers, return type, name, parameter types, and
0114: * exception types.
0115: *
0116: * @param modifiers
0117: * The {@link EDU.purdue.cs.bloat.reflect.Modifiers modifiers}
0118: * for the new method
0119: * @param returnType
0120: * The return type of the method. If, <code>returnType</code>
0121: * is null, the return type is assumed to be <code>void</code>.
0122: * @param methodName
0123: * The name of the method
0124: * @param paramTypes
0125: * The types of the parameters to the new method. If
0126: * <code>paramTypes</code> is <code>null</code>, then we
0127: * assume that there are no arguments.
0128: * @param exceptionTypes
0129: * The types of exceptions that may be thrown by the new method.
0130: * If <code>exceptionTypes</code> is <code>null</code>, then
0131: * we assume that no exceptions are declared.
0132: */
0133: public MethodEditor(final ClassEditor editor, final int modifiers,
0134: Type returnType, final String methodName,
0135: Type[] paramTypes, Type[] exceptionTypes) {
0136:
0137: // if(ClassEditor.DEBUG) {
0138: // System.out.println("Creating MethodEditor " +
0139: // System.identityHashCode(this));
0140: // Thread.dumpStack();
0141: // }
0142:
0143: this .editor = editor;
0144: this .name = methodName;
0145:
0146: if (returnType == null) {
0147: returnType = Type.VOID;
0148: }
0149:
0150: if (paramTypes == null) {
0151: paramTypes = new Type[0];
0152: }
0153:
0154: if (exceptionTypes == null) {
0155: exceptionTypes = new Type[0];
0156: }
0157:
0158: // Get the indices in the constant pool for all sorts of
0159: // interesting information
0160: final ConstantPool cp = editor.constants();
0161: final int nameIndex = cp.getUTF8Index(methodName);
0162: this .type = Type.getType(paramTypes, returnType);
0163: Assert.isTrue(this .type.isMethod(), "Method type not method: "
0164: + this .type);
0165: final int typeIndex = cp.getTypeIndex(this .type);
0166: final int exceptionIndex = cp.getUTF8Index("Exceptions");
0167:
0168: final int[] exceptionTypeIndices = new int[exceptionTypes.length];
0169: for (int i = 0; i < exceptionTypes.length; i++) {
0170: final Type eType = exceptionTypes[i];
0171: exceptionTypeIndices[i] = cp.getTypeIndex(eType);
0172: }
0173:
0174: final int codeIndex = cp.getUTF8Index("Code");
0175:
0176: final ClassInfo classInfo = editor.classInfo();
0177: this .methodInfo = classInfo.addNewMethod(modifiers, typeIndex,
0178: nameIndex, exceptionIndex, exceptionTypeIndices,
0179: codeIndex);
0180:
0181: // Initialize other parts of this MethodEditor as best we can
0182: this .code = new LinkedList();
0183: this .tryCatches = new LinkedList();
0184: this .lineNumbers = new LinkedList();
0185: this .locals = new HashMap();
0186:
0187: // Be sure to include space for the this pointer.
0188: if (!isStatic()) {
0189: this .params = new LocalVariable[type.stackHeight() + 1];
0190:
0191: } else {
0192: this .params = new LocalVariable[type.stackHeight()];
0193: }
0194:
0195: // Initalize the params to hold LocalVariables representing the
0196: // parameters
0197: this .paramTypes = new Type[this .params.length];
0198: final Type[] indexedParams = this .type().indexedParamTypes();
0199: if (!isStatic()) {
0200: // First parameter is the this pointer
0201: this .paramTypes[0] = this .declaringClass().type();
0202: for (int q = 1; q < this .paramTypes.length; q++) {
0203: this .paramTypes[q] = indexedParams[q - 1];
0204: }
0205:
0206: } else {
0207: for (int q = 0; q < this .paramTypes.length; q++) {
0208: this .paramTypes[q] = indexedParams[q];
0209: }
0210: }
0211:
0212: for (int q = 0; q < this .params.length; q++) {
0213: this .params[q] = new LocalVariable(null,
0214: this .paramTypes[q], q);
0215: }
0216:
0217: this .maxLocals = this .paramTypes.length;
0218:
0219: this .isDirty = true;
0220: }
0221:
0222: /**
0223: * Constructor.
0224: *
0225: * @param editor
0226: * The class containing the method.
0227: * @param methodInfo
0228: * The method to edit.
0229: *
0230: * @see ClassEditor
0231: * @see EDU.purdue.cs.bloat.reflect.MethodInfo MethodInfo
0232: */
0233: public MethodEditor(final ClassEditor editor,
0234: final MethodInfo methodInfo) {
0235: // if(ClassEditor.DEBUG) {
0236: // System.out.println("Creating MethodEditor " +
0237: // System.identityHashCode(this));
0238: // Thread.dumpStack();
0239: // }
0240:
0241: final ConstantPool cp = editor.constants();
0242:
0243: this .methodInfo = methodInfo;
0244: this .editor = editor;
0245: this .isDirty = false;
0246:
0247: maxLabel = 0;
0248: maxLocals = methodInfo.maxLocals();
0249: maxStack = methodInfo.maxStack();
0250: locals = new HashMap();
0251:
0252: int index;
0253: int i;
0254: int j;
0255:
0256: index = methodInfo.nameIndex();
0257: name = (String) cp.constantAt(index);
0258:
0259: index = methodInfo.typeIndex();
0260: final String typeName = (String) cp.constantAt(index);
0261: type = Type.getType(typeName);
0262:
0263: code = new LinkedList();
0264: tryCatches = new LinkedList();
0265: lineNumbers = new LinkedList();
0266:
0267: // Be sure to include space for the this pointer.
0268: if (!isStatic()) {
0269: params = new LocalVariable[type.stackHeight() + 1];
0270: } else {
0271: params = new LocalVariable[type.stackHeight()];
0272: }
0273:
0274: // Initalize the params to hold LocalVariables representing the
0275: // parameters
0276: paramTypes = new Type[params.length];
0277: final Type[] indexedParams = this .type().indexedParamTypes();
0278: if (!isStatic()) {
0279: // First parameter is the this pointer
0280: paramTypes[0] = this .declaringClass().type();
0281: for (int q = 1; q < paramTypes.length; q++) {
0282: paramTypes[q] = indexedParams[q - 1];
0283: }
0284:
0285: } else {
0286: for (int q = 0; q < paramTypes.length; q++) {
0287: paramTypes[q] = indexedParams[q];
0288: }
0289: }
0290: for (int q = 0; q < params.length; q++) {
0291: params[q] = new LocalVariable(null, paramTypes[q], q);
0292: }
0293:
0294: // Get the byte code for this method
0295: final byte[] array = methodInfo.code();
0296:
0297: if ((array == null) || (array.length == 0)) {
0298: return;
0299: }
0300:
0301: // Build the array of Instructions (and Labels).
0302: //
0303: // next[i] contains the index of the instruction following i.
0304: // targets[i] contains an array of the branch targets of i.
0305: // lookups[i] contains an array of the switch lookup values of i.
0306: // label[i] contains a label if a label should be inserted before i.
0307: // lines[i] contains the line number of instruction i (or 0).
0308: //
0309: final int[] next = new int[array.length];
0310: final int[][] targets = new int[array.length][];
0311: final int[][] lookups = new int[array.length][];
0312: final Label[] label = new Label[array.length + 1];
0313: LocalVariable[][] localVars;
0314:
0315: if (MethodEditor.PRESERVE_DEBUG && (array.length < 0x10000)) {
0316: // LocalDebugInfo maps a local variable in the generated code
0317: // back to the name of a local variable in the original Java
0318: // source file.
0319: final LocalDebugInfo[] locals = methodInfo.locals();
0320: int max = 0;
0321:
0322: // Find the maximum local variable index for the code.
0323: for (i = 0; i < locals.length; i++) {
0324: if (max <= locals[i].index()) {
0325: max = locals[i].index() + 1;
0326: }
0327: }
0328:
0329: // localVars[i][j] contains the a LocalVariable, j, for
0330: // instruction i
0331: localVars = new LocalVariable[array.length][max];
0332:
0333: // Create LocalVariables for those locals with debug info
0334: // and set the params array so the name and type will be returned
0335: // be paramAt.
0336: //
0337: for (i = 0; i < locals.length; i++) {
0338: final int start = locals[i].startPC();
0339: final int end = start + locals[i].length();
0340:
0341: final String localName = (String) cp
0342: .constantAt(locals[i].nameIndex());
0343: final String localType = (String) cp
0344: .constantAt(locals[i].typeIndex());
0345:
0346: final LocalVariable var = new LocalVariable(localName,
0347: Type.getType(localType), locals[i].index());
0348:
0349: for (int pc = start; pc <= end; pc++) {
0350: if (pc < localVars.length) {
0351: localVars[pc][locals[i].index()] = var;
0352: }
0353: }
0354:
0355: if ((start == 0) && (locals[i].index() < params.length)) {
0356: params[locals[i].index()] = var;
0357: }
0358: }
0359:
0360: // Create a list of line number entries and add a label at the
0361: // start PC for each entry.
0362: final LineNumberDebugInfo[] lineNumbers = methodInfo
0363: .lineNumbers();
0364:
0365: for (i = 0; i < lineNumbers.length; i++) {
0366: final int start = lineNumbers[i].startPC();
0367:
0368: if (label[start] == null) {
0369: label[start] = new Label(start, false);
0370: }
0371:
0372: addLineNumberEntry(label[start], lineNumbers[i]
0373: .lineNumber());
0374: }
0375: } else {
0376: // We're not preserving debugging information. So, we don't
0377: // need to worry about which local variables are live at
0378: // which instructions.
0379:
0380: localVars = new LocalVariable[array.length][0];
0381: }
0382:
0383: // Create a label for the beginning of the code and for each
0384: // branch target. Also set next[i] for all instructions i.
0385: //
0386: label[0] = new Label(0, true);
0387:
0388: int numInst = 0;
0389:
0390: for (i = 0; i < array.length; i = next[i]) {
0391: // Examine an instruction and extract its target labels
0392: // and switch lookups
0393: next[i] = munchCode(array, i, targets, lookups);
0394: numInst++;
0395:
0396: // Generate Labels for all the targets local to the code
0397: if (targets[i] != null) {
0398: for (j = 0; j < targets[i].length; j++) {
0399: if (targets[i][j] < array.length) {
0400: label[targets[i][j]] = new Label(targets[i][j],
0401: true);
0402: }
0403: }
0404: }
0405: }
0406:
0407: // Create a label for the beginning and end of protected blocks and the
0408: // beginning of catch blocks. Add a TryCatch entry for each
0409: // exception handler in the method.
0410: //
0411: final Catch[] exc = methodInfo.exceptionHandlers();
0412:
0413: for (i = 0; i < exc.length; i++) {
0414: final int start = exc[i].startPC();
0415: final int end = exc[i].endPC();
0416: final int handler = exc[i].handlerPC();
0417:
0418: label[start] = new Label(start, true);
0419: label[end] = new Label(end, true);
0420: label[handler] = new Label(handler, true);
0421:
0422: final Type catchType = (Type) cp.constantAt(exc[i]
0423: .catchTypeIndex());
0424:
0425: addTryCatch(new TryCatch(label[start], label[end],
0426: label[handler], catchType));
0427: }
0428:
0429: // Go through the bytecode and create Instructions and build the
0430: // code linked list.
0431: // Add a label for instructions following branches.
0432: for (i = 0; i < array.length; i = next[i]) {
0433: final Instruction inst = new Instruction(array, i,
0434: targets[i], lookups[i], localVars[i], cp);
0435:
0436: if (label[i] != null) {
0437: code.add(label[i]);
0438: }
0439:
0440: code.add(inst);
0441:
0442: if (inst.isJump() || inst.isReturn() || inst.isJsr()
0443: || inst.isRet() || inst.isThrow()
0444: || inst.isSwitch()) {
0445:
0446: // Add a label for the next instruction after a branch.
0447: if (next[i] < array.length) {
0448: label[next[i]] = new Label(next[i], true);
0449: }
0450: }
0451: }
0452:
0453: // Add a label at the end. This label must start a block.
0454: label[array.length] = new Label(array.length, true);
0455: code.add(label[array.length]);
0456:
0457: maxLabel = array.length + 1;
0458:
0459: if (ClassEditor.DEBUG) {
0460: System.out.println("Editing method " + name + " " + type);
0461: }
0462:
0463: if (MethodEditor.OPT_STACK_2) {
0464: uMap = new UseMap(); // structure for remembering use/def info.
0465: }
0466:
0467: this .setDirty(false);
0468: }
0469:
0470: /**
0471: * Returns the <tt>Type</tt>s of exceptions that this method may throw.
0472: */
0473: public Type[] exceptions() {
0474: final ConstantPool cp = editor.constants();
0475: final int[] indices = methodInfo.exceptionTypes();
0476: final Type[] types = new Type[indices.length];
0477:
0478: for (int i = 0; i < indices.length; i++) {
0479: types[i] = (Type) cp.constantAt(indices[i]);
0480: }
0481:
0482: return (types);
0483: }
0484:
0485: /**
0486: * Returns <tt>true</tt> if this method has been modified.
0487: */
0488: public boolean isDirty() {
0489: return (this .isDirty);
0490: }
0491:
0492: /**
0493: * Sets the dirty flag of this method. The dirty flag is <tt>true</tt> if
0494: * the method has been modified.
0495: */
0496: public void setDirty(final boolean dirty) {
0497: this .isDirty = dirty;
0498: if (isDirty == true) {
0499: this .editor.setDirty(true);
0500: }
0501: }
0502:
0503: /**
0504: * Marks this method for deletion. Once a method has been marked for
0505: * deletion all attempts to change it will throw an
0506: * <code>IllegalStateException</code>.
0507: */
0508: public void delete() {
0509: this .setDirty(true);
0510: this .isDeleted = true;
0511: }
0512:
0513: /**
0514: * Returns an array of <tt>Type</tt>s representing the types of the
0515: * parameters of this method. It's really used to figure out the type of the
0516: * local variables that hold the parameters. So, wide data is succeeded by
0517: * an empty slot. Also, for virtual methods, the first element in the array
0518: * is the receiver.
0519: */
0520: public Type[] paramTypes() {
0521: return (this .paramTypes);
0522: }
0523:
0524: /**
0525: * Get the LocalVariable for the parameter at the given index.
0526: *
0527: * @param index
0528: * The index into the params (0 is the this pointer or the first
0529: * argument, if static).
0530: * @return The LocalVariable for the parameter at the given index.
0531: *
0532: */
0533: public LocalVariable paramAt(final int index) {
0534: if ((index >= params.length) || (params[index] == null)) {
0535: final LocalVariable local = new LocalVariable(index);
0536: if (index < params.length) {
0537: params[index] = local;
0538: }
0539: return (local);
0540: }
0541:
0542: return params[index];
0543: }
0544:
0545: /**
0546: * Returns the raw MethodInfo of the method being edited.
0547: */
0548: public MethodInfo methodInfo() {
0549: return methodInfo;
0550: }
0551:
0552: /**
0553: * Returns the class which declared the method.
0554: */
0555: public ClassEditor declaringClass() {
0556: return editor;
0557: }
0558:
0559: /**
0560: * Returns the maximum number of locals used by the method.
0561: */
0562: public int maxLocals() {
0563: return maxLocals;
0564: }
0565:
0566: public boolean isPublic() {
0567: return (methodInfo.modifiers() & Modifiers.PUBLIC) != 0;
0568: }
0569:
0570: public boolean isPrivate() {
0571: return (methodInfo.modifiers() & Modifiers.PRIVATE) != 0;
0572: }
0573:
0574: public boolean isProtected() {
0575: return (methodInfo.modifiers() & Modifiers.PROTECTED) != 0;
0576: }
0577:
0578: /**
0579: * Returns true is the method has package level visibility
0580: */
0581: public boolean isPackage() {
0582: return (!isPublic() && !isPrivate() && !isProtected());
0583: }
0584:
0585: public boolean isStatic() {
0586: return (methodInfo.modifiers() & Modifiers.STATIC) != 0;
0587: }
0588:
0589: public boolean isFinal() {
0590: return (methodInfo.modifiers() & Modifiers.FINAL) != 0;
0591: }
0592:
0593: public boolean isSynchronized() {
0594: return (methodInfo.modifiers() & Modifiers.SYNCHRONIZED) != 0;
0595: }
0596:
0597: public boolean isNative() {
0598: return (methodInfo.modifiers() & Modifiers.NATIVE) != 0;
0599: }
0600:
0601: public boolean isAbstract() {
0602: return (methodInfo.modifiers() & Modifiers.ABSTRACT) != 0;
0603: }
0604:
0605: /**
0606: * Returns <tt>true</tt> if this method's class is an interface.
0607: */
0608: public boolean isInterface() {
0609: return (editor.isInterface());
0610: }
0611:
0612: // TODO: Only change the methodInfo at commit time.
0613: // TODO: Add similar methods to field and class editors.
0614: /**
0615: * @throws IllegalStateException This field has been marked for deletion
0616: */
0617: public void setPublic(final boolean flag) {
0618: if (this .isDeleted) {
0619: final String s = "Cannot change a field once it has been marked "
0620: + "for deletion";
0621: throw new IllegalStateException(s);
0622: }
0623:
0624: int mod = methodInfo.modifiers();
0625:
0626: if (flag) {
0627: mod |= Modifiers.PUBLIC;
0628: } else {
0629: mod &= ~Modifiers.PUBLIC;
0630: }
0631:
0632: methodInfo.setModifiers(mod);
0633: this .setDirty(true);
0634: }
0635:
0636: /**
0637: * @throws IllegalStateException This field has been marked for deletion
0638: */
0639: public void setPrivate(final boolean flag) {
0640: if (this .isDeleted) {
0641: final String s = "Cannot change a field once it has been marked "
0642: + "for deletion";
0643: throw new IllegalStateException(s);
0644: }
0645:
0646: int mod = methodInfo.modifiers();
0647:
0648: if (flag) {
0649: mod |= Modifiers.PRIVATE;
0650: } else {
0651: mod &= ~Modifiers.PRIVATE;
0652: }
0653:
0654: methodInfo.setModifiers(mod);
0655: this .setDirty(true);
0656: }
0657:
0658: /**
0659: * @throws IllegalStateException This field has been marked for deletion
0660: */
0661: public void setProtected(final boolean flag) {
0662: if (this .isDeleted) {
0663: final String s = "Cannot change a field once it has been marked "
0664: + "for deletion";
0665: throw new IllegalStateException(s);
0666: }
0667:
0668: int mod = methodInfo.modifiers();
0669:
0670: if (flag) {
0671: mod |= Modifiers.PROTECTED;
0672: } else {
0673: mod &= ~Modifiers.PROTECTED;
0674: }
0675:
0676: methodInfo.setModifiers(mod);
0677: this .setDirty(true);
0678: }
0679:
0680: /**
0681: * @throws IllegalStateException This field has been marked for deletion
0682: */
0683: public void setStatic(final boolean flag) {
0684: if (this .isDeleted) {
0685: final String s = "Cannot change a field once it has been marked "
0686: + "for deletion";
0687: throw new IllegalStateException(s);
0688: }
0689:
0690: int mod = methodInfo.modifiers();
0691:
0692: if (flag) {
0693: mod |= Modifiers.STATIC;
0694: } else {
0695: mod &= ~Modifiers.STATIC;
0696: }
0697:
0698: methodInfo.setModifiers(mod);
0699: this .setDirty(true);
0700: }
0701:
0702: /**
0703: * @throws IllegalStateException This field has been marked for deletion
0704: */
0705: public void setFinal(final boolean flag) {
0706: if (this .isDeleted) {
0707: final String s = "Cannot change a field once it has been marked "
0708: + "for deletion";
0709: throw new IllegalStateException(s);
0710: }
0711:
0712: int mod = methodInfo.modifiers();
0713:
0714: if (flag) {
0715: mod |= Modifiers.FINAL;
0716: } else {
0717: mod &= ~Modifiers.FINAL;
0718: }
0719:
0720: methodInfo.setModifiers(mod);
0721: this .setDirty(true);
0722: }
0723:
0724: /**
0725: * @throws IllegalStateException This field has been marked for deletion
0726: */
0727: public void setSynchronized(final boolean flag) {
0728: if (this .isDeleted) {
0729: final String s = "Cannot change a field once it has been marked "
0730: + "for deletion";
0731: throw new IllegalStateException(s);
0732: }
0733:
0734: int mod = methodInfo.modifiers();
0735:
0736: if (flag) {
0737: mod |= Modifiers.SYNCHRONIZED;
0738: } else {
0739: mod &= ~Modifiers.SYNCHRONIZED;
0740: }
0741:
0742: methodInfo.setModifiers(mod);
0743: this .setDirty(true);
0744: }
0745:
0746: /**
0747: * @throws IllegalStateException This field has been marked for deletion
0748: */
0749: public void setNative(final boolean flag) {
0750: if (this .isDeleted) {
0751: final String s = "Cannot change a field once it has been marked "
0752: + "for deletion";
0753: throw new IllegalStateException(s);
0754: }
0755:
0756: int mod = methodInfo.modifiers();
0757:
0758: if (flag) {
0759: mod |= Modifiers.NATIVE;
0760: } else {
0761: mod &= ~Modifiers.NATIVE;
0762: }
0763:
0764: methodInfo.setModifiers(mod);
0765: this .setDirty(true);
0766: }
0767:
0768: public void setAbstract(final boolean flag) {
0769: int mod = methodInfo.modifiers();
0770:
0771: if (flag) {
0772: mod |= Modifiers.ABSTRACT;
0773: } else {
0774: mod &= ~Modifiers.ABSTRACT;
0775: }
0776:
0777: methodInfo.setModifiers(mod);
0778: this .setDirty(true);
0779: }
0780:
0781: /**
0782: * Scan the raw bytes of a single instruction, saving the indices of branch
0783: * targets and the values of switch lookups. That is, gather information
0784: * needed for creating <tt>Instruction</tt> instances.
0785: *
0786: * @param code
0787: * The byte code array.
0788: * @param index
0789: * The index into the code array.
0790: * @param targets
0791: * Branch targets for the instruction scanned. This is set by the
0792: * method.
0793: * @param lookups
0794: * Switch lookups for the instruction scanned. This is set by the
0795: * method.
0796: * @return The index of the next instruction in the code array.
0797: */
0798: private int munchCode(final byte[] code, final int index,
0799: final int[][] targets, final int[][] lookups) {
0800: final int opcode = Instruction.toUByte(code[index]);
0801: int next = index + Opcode.opcSize[opcode];
0802:
0803: switch (opcode) {
0804: case opc_ifeq:
0805: case opc_ifne:
0806: case opc_iflt:
0807: case opc_ifge:
0808: case opc_ifgt:
0809: case opc_ifle:
0810: case opc_if_icmpeq:
0811: case opc_if_icmpne:
0812: case opc_if_icmplt:
0813: case opc_if_icmpge:
0814: case opc_if_icmpgt:
0815: case opc_if_icmple:
0816: case opc_if_acmpeq:
0817: case opc_if_acmpne:
0818: case opc_ifnull:
0819: case opc_ifnonnull: {
0820: // Branch target
0821: final int target = Instruction.toShort(code[index + 1],
0822: code[index + 2]);
0823: targets[index] = new int[1];
0824: targets[index][0] = index + target;
0825: break;
0826: }
0827: case opc_goto:
0828: case opc_jsr: {
0829: // Branch target
0830: final int target = Instruction.toShort(code[index + 1],
0831: code[index + 2]);
0832: targets[index] = new int[1];
0833: targets[index][0] = index + target;
0834: break;
0835: }
0836: case opc_goto_w:
0837: case opc_jsr_w: {
0838: // Branch target
0839: final int target = Instruction.toInt(code[index + 1],
0840: code[index + 2], code[index + 3], code[index + 4]);
0841: targets[index] = new int[1];
0842: targets[index][0] = index + target;
0843: break;
0844: }
0845: case opc_ret: {
0846: // Unconditional branch to the address in a local variable.
0847: // Work on finding branch targets later.
0848: break;
0849: }
0850: case opc_tableswitch: {
0851: int target;
0852: int lo;
0853: int hi;
0854: int j;
0855:
0856: // The targets and low and high values are aligned on
0857: // 4-byte boundaries.
0858: for (j = index + 1; j % 4 != 0; j++) {
0859: // Empty statement.
0860: }
0861:
0862: // Read the default target.
0863: target = Instruction.toInt(code[j], code[j + 1],
0864: code[j + 2], code[j + 3]);
0865: j += 4;
0866:
0867: lo = Instruction.toInt(code[j], code[j + 1], code[j + 2],
0868: code[j + 3]);
0869: j += 4;
0870:
0871: hi = Instruction.toInt(code[j], code[j + 1], code[j + 2],
0872: code[j + 3]);
0873: j += 4;
0874:
0875: lookups[index] = new int[2];
0876: lookups[index][0] = lo;
0877: lookups[index][1] = hi;
0878:
0879: targets[index] = new int[hi - lo + 2];
0880:
0881: int k = 0;
0882: targets[index][k++] = index + target;
0883:
0884: next = j + (hi - lo + 1) * 4;
0885:
0886: while (j < next) {
0887: target = Instruction.toInt(code[j], code[j + 1],
0888: code[j + 2], code[j + 3]);
0889: j += 4;
0890:
0891: targets[index][k++] = index + target;
0892: }
0893:
0894: break;
0895: }
0896: case opc_lookupswitch: {
0897: int target;
0898: int value;
0899: int npairs;
0900: int j;
0901:
0902: // The targets and pairs are aligned on 4-byte boundaries.
0903: for (j = index + 1; j % 4 != 0; j++) {
0904: // Empty statement.
0905: }
0906:
0907: // Read the default target.
0908: target = Instruction.toInt(code[j], code[j + 1],
0909: code[j + 2], code[j + 3]);
0910: j += 4;
0911:
0912: npairs = Instruction.toInt(code[j], code[j + 1],
0913: code[j + 2], code[j + 3]);
0914: j += 4;
0915:
0916: lookups[index] = new int[npairs];
0917: targets[index] = new int[npairs + 1];
0918:
0919: int k = 0;
0920: targets[index][k++] = index + target;
0921:
0922: next = j + npairs * 8;
0923:
0924: while (j < next) {
0925: value = Instruction.toInt(code[j], code[j + 1],
0926: code[j + 2], code[j + 3]);
0927: j += 4;
0928:
0929: target = Instruction.toInt(code[j], code[j + 1],
0930: code[j + 2], code[j + 3]);
0931: j += 4;
0932:
0933: lookups[index][k - 1] = value;
0934: targets[index][k++] = index + target;
0935: }
0936:
0937: break;
0938: }
0939: case opc_wide: {
0940: if (code[index + 1] == (byte) Opcode.opc_iinc) {
0941: next = index + 6;
0942: } else {
0943: next = index + 4;
0944: }
0945: break;
0946: }
0947: }
0948:
0949: return next;
0950: }
0951:
0952: /**
0953: * Remove all the instructions in preparation for the instructions being
0954: * added back after a control flow graph edit.
0955: *
0956: * @throws IllegalStateException This field has been marked for deletion
0957: */
0958: public void clearCode() {
0959: if (this .isDeleted) {
0960: final String s = "Cannot change a field once it has been marked "
0961: + "for deletion";
0962: throw new IllegalStateException(s);
0963: }
0964:
0965: if (ClassEditor.DEBUG) {
0966: System.out.println("Clearing code");
0967: Thread.dumpStack();
0968: }
0969:
0970: code.clear();
0971: tryCatches.clear();
0972: maxLocals = 0;
0973: maxStack = 0;
0974: this .setDirty(true);
0975: }
0976:
0977: /**
0978: * Like clear code, but doesn't reset the maxLocals. I'm not really sure why
0979: * this works, but it stops certain parts of code that is generated and then
0980: * re-cfg'd from being eliminated as dead
0981: */
0982:
0983: public void clearCode2() {
0984: code.clear();
0985: tryCatches.clear();
0986: maxStack = 0;
0987: this .setDirty(true);
0988: }
0989:
0990: /**
0991: * Returns the name of the method.
0992: */
0993: public String name() {
0994: return name;
0995: }
0996:
0997: /**
0998: * Returns <tt>true</tt> if the method being edited is a constructor.
0999: */
1000: public boolean isConstructor() {
1001: return (name.equals("<init>"));
1002: }
1003:
1004: /**
1005: * Returns the type of the method.
1006: */
1007: public Type type() {
1008: return type;
1009: }
1010:
1011: /**
1012: * Returns the <Tt>NameAndType</tt> of the method.
1013: */
1014: public NameAndType nameAndType() {
1015: return (new NameAndType(this .name(), this .type()));
1016: }
1017:
1018: /**
1019: * Returns a <tt>MemberRef</tt> for the method.
1020: */
1021: public MemberRef memberRef() {
1022: return (new MemberRef(this .declaringClass().type(), this
1023: .nameAndType()));
1024: }
1025:
1026: /**
1027: * Get the length of the code array.
1028: *
1029: * @return The length of the code array.
1030: */
1031: public int codeLength() {
1032: return code.size();
1033: }
1034:
1035: /**
1036: * @throws IllegalStateException This field has been marked for deletion
1037: */
1038: public void setCode(final List v) {
1039: if (this .isDeleted) {
1040: final String s = "Cannot change a field once it has been marked "
1041: + "for deletion";
1042: throw new IllegalStateException(s);
1043: }
1044:
1045: if (ClassEditor.DEBUG) {
1046: System.out.println("Setting code to " + v.size()
1047: + " instructions");
1048: Thread.dumpStack();
1049: }
1050: code = new LinkedList(v);
1051: this .setDirty(true);
1052: }
1053:
1054: /**
1055: * Returns the code (<tt>Instruction</tt>s and <Tt>Label</tt>s) in
1056: * the method.
1057: */
1058: public List code() {
1059: return code;
1060: }
1061:
1062: /**
1063: * Get the label of the first block.
1064: */
1065: public Label firstBlock() {
1066: final Iterator iter = code.iterator();
1067:
1068: while (iter.hasNext()) {
1069: final Object obj = iter.next();
1070:
1071: if (obj instanceof Label) {
1072: final Label l = (Label) obj;
1073: if (l.startsBlock()) {
1074: return l;
1075: }
1076: }
1077: }
1078:
1079: return null;
1080: }
1081:
1082: /**
1083: * Get the label of the next block after the parameter.
1084: *
1085: * @param label
1086: * The label at which to begin.
1087: * @return The label.
1088: */
1089: public Label nextBlock(final Label label) {
1090: boolean seen = false;
1091:
1092: final Iterator iter = code.iterator();
1093:
1094: while (iter.hasNext()) {
1095: final Object obj = iter.next();
1096:
1097: if (obj instanceof Label) {
1098: if (seen) {
1099: final Label l = (Label) obj;
1100: if (l.startsBlock()) {
1101: return l;
1102: }
1103: } else if (label.equals(obj)) {
1104: seen = true;
1105: }
1106: }
1107: }
1108:
1109: return null;
1110: }
1111:
1112: /**
1113: * Removes a Label or Instruction from the code array.
1114: *
1115: * @param i
1116: * The index of the element to remove.
1117: *
1118: * @throws IllegalStateException This field has been marked for deletion
1119: */
1120: public void removeCodeAt(final int i) {
1121: if (this .isDeleted) {
1122: final String s = "Cannot change a field once it has been marked "
1123: + "for deletion";
1124: throw new IllegalStateException(s);
1125: }
1126:
1127: code.remove(i);
1128: this .setDirty(true);
1129: }
1130:
1131: /**
1132: * Inserts a Label or Instruction into the code array.
1133: *
1134: * @param i
1135: * The index of the element to insert before.
1136: *
1137: * @throws IllegalStateException This field has been marked for deletion
1138: */
1139: public void insertCodeAt(final Object obj, final int i) {
1140: if (this .isDeleted) {
1141: final String s = "Cannot change a field once it has been marked "
1142: + "for deletion";
1143: throw new IllegalStateException(s);
1144: }
1145:
1146: code.add(i, obj);
1147: this .setDirty(true);
1148: }
1149:
1150: /**
1151: * Replace a Label or Instruction in the code array.
1152: *
1153: * @param obj
1154: * The new element.
1155: * @param i
1156: * The index of the element to replace
1157: *
1158: * @throws IllegalStateException This field has been marked for deletion
1159: */
1160: public void replaceCodeAt(final Object obj, final int i) {
1161: if (this .isDeleted) {
1162: final String s = "Cannot change a field once it has been marked "
1163: + "for deletion";
1164: throw new IllegalStateException(s);
1165: }
1166:
1167: code.set(i, obj);
1168: this .setDirty(true);
1169: }
1170:
1171: /**
1172: * Returns a Label or Instruction in the code array.
1173: *
1174: * @param i
1175: * The index into the code array.
1176: * @return The element at the index.
1177: */
1178: public Object codeElementAt(final int i) {
1179: return code.get(i);
1180: }
1181:
1182: /**
1183: * Add a line number entry.
1184: *
1185: * @param label
1186: * The label beginning the range of instructions for this line
1187: * number.
1188: * @param lineNumber
1189: * The line number.
1190: *
1191: * @throws IllegalStateException This field has been marked for deletion
1192: */
1193: public void addLineNumberEntry(final Label label,
1194: final int lineNumber) {
1195: if (this .isDeleted) {
1196: final String s = "Cannot change a field once it has been marked "
1197: + "for deletion";
1198: throw new IllegalStateException(s);
1199: }
1200:
1201: lineNumbers.add(new LineNumberEntry(label, lineNumber));
1202: this .setDirty(true);
1203: }
1204:
1205: /**
1206: * Returns the number of exception handlers in the method.
1207: */
1208: public int numTryCatches() {
1209: return tryCatches.size();
1210: }
1211:
1212: /**
1213: * Returns the exception handlers (<tt>TryCatch</tt>) in the method.
1214: */
1215: public Collection tryCatches() {
1216: return tryCatches;
1217: }
1218:
1219: /**
1220: * Add an exception handler.
1221: *
1222: * @param tryCatch
1223: * An exception handler.
1224: *
1225: * @throws IllegalStateException This field has been marked for deletion
1226: */
1227: public void addTryCatch(final TryCatch tryCatch) {
1228: if (this .isDeleted) {
1229: final String s = "Cannot change a field once it has been marked "
1230: + "for deletion";
1231: throw new IllegalStateException(s);
1232: }
1233:
1234: if (ClassEditor.DEBUG) {
1235: System.out.println("add " + tryCatch);
1236: }
1237:
1238: tryCatches.add(tryCatch);
1239: this .setDirty(true);
1240: }
1241:
1242: class LineNumberEntry {
1243: Label label;
1244:
1245: int lineNumber;
1246:
1247: public LineNumberEntry(final Label label, final int lineNumber) {
1248: this .label = label;
1249: this .lineNumber = lineNumber;
1250: }
1251: }
1252:
1253: class LocalInfo {
1254: LocalVariable var;
1255:
1256: Type type;
1257:
1258: public LocalInfo(final LocalVariable var, final Type type) {
1259: this .var = var;
1260: this .type = type;
1261: }
1262:
1263: public boolean equals(final Object obj) {
1264: return (obj != null) && (obj instanceof LocalInfo)
1265: && ((LocalInfo) obj).var.equals(var)
1266: && ((LocalInfo) obj).type.equals(type);
1267: }
1268:
1269: public int hashCode() {
1270: return var.hashCode() ^ type.hashCode();
1271: }
1272: }
1273:
1274: /**
1275: * Creates a new local variable.
1276: */
1277: public LocalVariable newLocal(final Type type) {
1278: final int index = maxLocals;
1279:
1280: maxLocals += type.stackHeight();
1281: this .setDirty(true);
1282:
1283: final LocalVariable local = new LocalVariable(index);
1284:
1285: locals.put(new Integer(index), local);
1286:
1287: return (local);
1288: }
1289:
1290: /**
1291: * Creates a new local variable of an undertermined type.
1292: *
1293: * @throws IllegalStateException This field has been marked for deletion
1294: */
1295: public LocalVariable newLocal(final boolean isWide) {
1296: if (this .isDeleted) {
1297: final String s = "Cannot change a field once it has been marked "
1298: + "for deletion";
1299: throw new IllegalStateException(s);
1300: }
1301:
1302: final int index = maxLocals;
1303:
1304: maxLocals += (isWide ? 2 : 1);
1305: this .setDirty(true);
1306:
1307: final LocalVariable local = new LocalVariable(index);
1308:
1309: locals.put(new Integer(index), local);
1310:
1311: return (local);
1312: }
1313:
1314: /**
1315: * Returns the <tt>LocalVariable</tt> with the given index. If there is no
1316: * local variable at that index, a new one is created at that index. We
1317: * assume that this variable is not wide.
1318: */
1319: public LocalVariable localAt(int index) {
1320: LocalVariable local = (LocalVariable) locals.get(new Integer(
1321: index));
1322:
1323: if (local == null) {
1324: local = new LocalVariable(index);
1325: locals.put(new Integer(index), local);
1326: if (index >= maxLocals) {
1327: maxLocals = index++; // Dangerous?
1328: }
1329: }
1330:
1331: return (local);
1332: }
1333:
1334: /**
1335: * Add an instruction.
1336: *
1337: * @param opcodeClass
1338: * The instruction to add.
1339: */
1340: public void addInstruction(final int opcodeClass) {
1341: addInstruction(new Instruction(opcodeClass));
1342: }
1343:
1344: /**
1345: * Add an instruction.
1346: *
1347: * @param opcodeClass
1348: * The instruction to add.
1349: */
1350: public void addInstruction(final int opcodeClass,
1351: final Object operand) {
1352: addInstruction(new Instruction(opcodeClass, operand));
1353: }
1354:
1355: /**
1356: * Add an instruction to the end of the code array.
1357: *
1358: * @param inst
1359: * The instruction to add.
1360: *
1361: * @throws IllegalStateException This field has been marked for deletion
1362: */
1363: public void addInstruction(final Instruction inst) {
1364: if (this .isDeleted) {
1365: final String s = "Cannot change a field once it has been marked "
1366: + "for deletion";
1367: throw new IllegalStateException(s);
1368: }
1369:
1370: if (ClassEditor.DEBUG) {
1371: System.out.println(" " + inst + " to "
1372: + System.identityHashCode(this ) + ":"
1373: + System.identityHashCode(this .code));
1374: }
1375:
1376: code.add(inst);
1377: this .setDirty(true);
1378: }
1379:
1380: /**
1381: * Get the next available label. That is the Label after the final
1382: * Instruction in the code array.
1383: *
1384: * @return A new label.
1385: *
1386: * @throws IllegalStateException This field has been marked for deletion
1387: */
1388: public Label newLabel() {
1389: if (this .isDeleted) {
1390: final String s = "Cannot change a field once it has been marked "
1391: + "for deletion";
1392: throw new IllegalStateException(s);
1393: }
1394:
1395: this .setDirty(true);
1396: return new Label(maxLabel++);
1397: }
1398:
1399: public Label newLabelTrue() {
1400: return new Label(maxLabel++, true);
1401: }
1402:
1403: /**
1404: * Add a label to the code array to the end of the code array.
1405: *
1406: * @param label
1407: * The label to add.
1408: *
1409: * @throws IllegalStateException This field has been marked for deletion
1410: */
1411: public void addLabel(final Label label) {
1412: if (this .isDeleted) {
1413: final String s = "Cannot change a field once it has been marked "
1414: + "for deletion";
1415: throw new IllegalStateException(s);
1416: }
1417:
1418: if (ClassEditor.DEBUG) {
1419: System.out.println(" " + label + " to "
1420: + System.identityHashCode(this ) + ":"
1421: + System.identityHashCode(this .code));
1422: }
1423:
1424: if (label.index() >= maxLabel) {
1425: maxLabel = label.index() + 1;
1426: }
1427:
1428: code.add(label);
1429: this .setDirty(true);
1430: }
1431:
1432: class LocalVarEntry {
1433: LocalVariable var;
1434:
1435: Label start;
1436:
1437: Label end;
1438:
1439: public LocalVarEntry(final LocalVariable var,
1440: final Label start, final Label end) {
1441: this .var = var;
1442: this .start = start;
1443: this .end = end;
1444: }
1445: }
1446:
1447: /**
1448: * Commits changes made to this MethodEditor back to the MethodInfo on which
1449: * it is based. Note that committal will take place regardless of whether or
1450: * not the method is dirty.
1451: */
1452: public void commit() {
1453: if (ClassEditor.DEBUG) {
1454: System.out.println("Committing method " + this .name + " "
1455: + this .type + ": " + this .code.size() + " insts "
1456: + System.identityHashCode(this ) + ":"
1457: + System.identityHashCode(this .code));
1458: }
1459:
1460: final ConstantPool cp = editor.constants();
1461:
1462: if (this .isDeleted) {
1463: final int nameIndex = cp.getUTF8Index(this .name);
1464: final int typeIndex = cp.getTypeIndex(this .type);
1465: this .editor.classInfo().deleteMethod(nameIndex, typeIndex);
1466:
1467: } else {
1468: methodInfo
1469: .setNameIndex(cp.addConstant(Constant.UTF8, name));
1470: methodInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type
1471: .descriptor()));
1472:
1473: if (isNative() || isAbstract()) {
1474: return;
1475: }
1476:
1477: final List vars = new ArrayList();
1478: final List copy = new LinkedList();
1479:
1480: Iterator iter = code.iterator();
1481:
1482: CODE: while (iter.hasNext()) {
1483: final Object ce = iter.next();
1484:
1485: if (ce instanceof Label) {
1486: copy.add(ce);
1487: continue CODE;
1488: }
1489:
1490: final Instruction inst = (Instruction) ce;
1491:
1492: LocalVariable var = null;
1493:
1494: if (inst.operand() instanceof LocalVariable) {
1495: var = (LocalVariable) inst.operand();
1496: } else if (inst.operand() instanceof IncOperand) {
1497: var = ((IncOperand) inst.operand()).var();
1498: }
1499:
1500: if ((var == null) || (var.name() == null)
1501: || (var.type() == null)) {
1502: copy.add(ce);
1503: continue CODE;
1504: }
1505:
1506: for (int j = vars.size() - 1; j >= 0; j--) {
1507: final LocalVarEntry v = (LocalVarEntry) vars.get(j);
1508:
1509: // Same variable, extend the range of the variable and
1510: // go to the next instruction.
1511: if (v.var.equals(var)) {
1512: v.end = newLabel();
1513:
1514: // Add a label after the instruction.
1515: copy.add(ce);
1516: copy.add(v.end);
1517: continue CODE;
1518: }
1519:
1520: // Different variable, same index. We have to add an entry
1521: // for the variable since we know the live range for this
1522: // index starts here.
1523: if (v.var.index() == var.index()) {
1524: break;
1525: }
1526: }
1527:
1528: final Label start = newLabel();
1529: final Label end = newLabel();
1530:
1531: vars.add(new LocalVarEntry(var, start, end));
1532:
1533: // Add labels before and after the instruction.
1534: copy.add(start);
1535: copy.add(ce);
1536: copy.add(end);
1537: }
1538:
1539: final HashSet seen = new HashSet();
1540: final ArrayList dup = new ArrayList();
1541:
1542: iter = tryCatches.iterator();
1543:
1544: while (iter.hasNext()) {
1545: final TryCatch tc = (TryCatch) iter.next();
1546:
1547: if (!seen.contains(tc.handler())) {
1548: if (ClassEditor.DEBUG) {
1549: System.out.println("See " + tc.handler());
1550: }
1551: seen.add(tc.handler());
1552: } else {
1553: if (ClassEditor.DEBUG) {
1554: System.out.println("See " + tc.handler()
1555: + " again");
1556: }
1557: dup.add(tc);
1558: }
1559: }
1560:
1561: if (dup.size() != 0) {
1562: final ListIterator liter = copy.listIterator();
1563:
1564: while (liter.hasNext()) {
1565: final Object ce = liter.next();
1566:
1567: if (ce instanceof Label) {
1568: final Iterator d = dup.iterator();
1569:
1570: while (d.hasNext()) {
1571: final TryCatch tc = (TryCatch) d.next();
1572:
1573: if (tc.handler().equals(ce)) {
1574: // Split the exception handler.
1575: //
1576: // Handler:
1577: // nop <-- nop needed to prevent TowerJ
1578: // goto L2 from removing the goto
1579: // Handler2:
1580: // nop
1581: // goto L2
1582: // Code:
1583: // handler code
1584:
1585: Instruction jump;
1586: Instruction nop;
1587:
1588: final Label handler2 = newLabel();
1589: final Label code = newLabel();
1590:
1591: nop = new Instruction(Opcode.opcx_nop);
1592: liter.add(nop);
1593:
1594: jump = new Instruction(
1595: Opcode.opcx_goto, code);
1596: liter.add(jump);
1597:
1598: liter.add(handler2);
1599:
1600: nop = new Instruction(Opcode.opcx_nop);
1601: liter.add(nop);
1602:
1603: jump = new Instruction(
1604: Opcode.opcx_goto, code);
1605: liter.add(jump);
1606:
1607: liter.add(code);
1608:
1609: if (ClassEditor.DEBUG) {
1610: System.out
1611: .println("Insert " + jump);
1612: System.out.println("Insert "
1613: + handler2);
1614: System.out
1615: .println("Insert " + jump);
1616: System.out
1617: .println("Insert " + code);
1618: }
1619:
1620: tc.setHandler(handler2);
1621:
1622: d.remove();
1623: }
1624: }
1625: }
1626: }
1627: }
1628:
1629: final CodeArray array = new CodeArray(this , cp, copy);
1630: final byte[] arr = array.array();
1631:
1632: methodInfo.setCode(arr);
1633:
1634: methodInfo.setMaxLocals(array.maxLocals());
1635: methodInfo.setMaxStack(array.maxStack());
1636:
1637: if (MethodEditor.PRESERVE_DEBUG && (arr.length < 0x10000)) {
1638: final LocalDebugInfo[] locals = new LocalDebugInfo[vars
1639: .size()];
1640:
1641: for (int i = 0; i < vars.size(); i++) {
1642: final LocalVarEntry entry = (LocalVarEntry) vars
1643: .get(i);
1644:
1645: final int start = array.labelIndex(entry.start);
1646: final int end = array.labelIndex(entry.end);
1647:
1648: if (start < end) {
1649: locals[i] = new LocalDebugInfo(start, end
1650: - start, cp.addConstant(Constant.UTF8,
1651: entry.var.name()), cp.addConstant(
1652: Constant.UTF8, entry.var.type()
1653: .descriptor()), entry.var
1654: .index());
1655: }
1656: }
1657:
1658: methodInfo.setLocals(locals);
1659:
1660: final LineNumberDebugInfo[] lines = new LineNumberDebugInfo[lineNumbers
1661: .size()];
1662: int i = 0;
1663:
1664: iter = lineNumbers.iterator();
1665:
1666: while (iter.hasNext()) {
1667: final LineNumberEntry line = (LineNumberEntry) iter
1668: .next();
1669: lines[i++] = new LineNumberDebugInfo(array
1670: .labelIndex(line.label), line.lineNumber);
1671: }
1672:
1673: methodInfo.setLineNumbers(lines);
1674: } else {
1675: methodInfo.setLineNumbers(null);
1676: methodInfo.setLocals(null);
1677: }
1678:
1679: final List c = new LinkedList();
1680:
1681: iter = tryCatches.iterator();
1682:
1683: while (iter.hasNext()) {
1684: final TryCatch tc = (TryCatch) iter.next();
1685:
1686: final int start = array.labelIndex(tc.start());
1687: final int end = array.labelIndex(tc.end());
1688:
1689: if (start < end) {
1690: c.add(new Catch(start, end, array.labelIndex(tc
1691: .handler()), cp.addConstant(Constant.CLASS,
1692: tc.type())));
1693: }
1694: }
1695:
1696: final Object[] a = c.toArray();
1697: final Catch[] catches = new Catch[a.length];
1698: System.arraycopy(a, 0, catches, 0, a.length);
1699:
1700: methodInfo.setExceptionHandlers(catches);
1701:
1702: }
1703:
1704: if (ClassEditor.DEBUG) {
1705: System.out
1706: .println("MethodInfo after commit: " + methodInfo);
1707: }
1708:
1709: // Method is no longer dirty
1710: this .isDirty = false;
1711: }
1712:
1713: /**
1714: * Print the method.
1715: *
1716: * @param out
1717: * Stream to which to print.
1718: */
1719: public void print(final PrintStream out) {
1720: out.println(name + "." + type + (isDirty ? " (dirty) " : "")
1721: + ":");
1722:
1723: Iterator iter;
1724:
1725: iter = code.iterator();
1726:
1727: while (iter.hasNext()) {
1728: out.println(" " + iter.next());
1729: }
1730:
1731: iter = tryCatches.iterator();
1732:
1733: while (iter.hasNext()) {
1734: out.println(" " + iter.next());
1735: }
1736: }
1737:
1738: /**
1739: * Two <tt>MethodEditor</tt>s are equal if they edit the same method in
1740: * the same class.
1741: */
1742: public boolean equals(final Object o) {
1743: if (o instanceof MethodEditor) {
1744: final MethodEditor other = (MethodEditor) o;
1745:
1746: if (!other.declaringClass().equals(this .declaringClass())) {
1747: return (false);
1748: }
1749: if (!other.name().equals(this .name())) {
1750: return (false);
1751: }
1752: if (!other.type().equals(this .type())) {
1753: return (false);
1754: }
1755:
1756: return (true);
1757: }
1758:
1759: return (false);
1760: }
1761:
1762: /**
1763: * A <tt>MethodEditor</tt>'s hash code is based on the hash codes for its
1764: * class, name, and type.
1765: */
1766: public int hashCode() {
1767: return (this .declaringClass().hashCode()
1768: + this .name().hashCode() + this .type().hashCode());
1769: }
1770:
1771: public String toString() {
1772: return (editor.type() + "." + name + type);
1773: }
1774:
1775: public UseMap uMap() {
1776: return uMap;
1777: }
1778:
1779: public void rememberDef(final LocalExpr e) {
1780: if (MethodEditor.OPT_STACK_2) {
1781: uMap.add(e, (Instruction) code.get(code.size() - 1));
1782: }
1783: }
1784:
1785: }
|