View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.generic;
    18
    19  import java.util.ArrayList;
    20  import java.util.Hashtable;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Stack;
    24  import org.apache.bcel.Constants;
    25  import org.apache.bcel.classfile.Attribute;
    26  import org.apache.bcel.classfile.Code;
    27  import org.apache.bcel.classfile.CodeException;
    28  import org.apache.bcel.classfile.ExceptionTable;
    29  import org.apache.bcel.classfile.LineNumber;
    30  import org.apache.bcel.classfile.LineNumberTable;
    31  import org.apache.bcel.classfile.LocalVariable;
    32  import org.apache.bcel.classfile.LocalVariableTable;
    33  import org.apache.bcel.classfile.Method;
    34  import org.apache.bcel.classfile.Utility;
    35  import org.apache.bcel.util.BCELComparator;
    36
    37  /***
    38   * Template class for building up a method. This is done by defining exception
    39   * handlers, adding thrown exceptions, local variables and attributes, whereas
    40   * the `LocalVariableTable' and `LineNumberTable' attributes will be set
    41   * automatically for the code. Use stripAttributes() if you don't like this.
    42   *
    43   * While generating code it may be necessary to insert NOP operations. You can
    44   * use the `removeNOPs' method to get rid off them.
    45   * The resulting method object can be obtained via the `getMethod()' method.
    46   *
    47   * @version $Id: MethodGen.java 386056 2006-03-15 11:31:56Z tcurdt $
    48   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
    49   * @author  <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
    50   * @see     InstructionList
    51   * @see     Method
    52   */
    53  public class MethodGen extends FieldGenOrMethodGen {
    54
    55      private String class_name;
    56      private Type[] arg_types;
    57      private String[] arg_names;
    58      private int max_locals;
    59      private int max_stack;
    60      private InstructionList il;
    61      private boolean strip_attributes;
    62      private List variable_vec = new ArrayList();
    63      private List line_number_vec = new ArrayList();
    64      private List exception_vec = new ArrayList();
    65      private List throws_vec = new ArrayList();
    66      private List code_attrs_vec = new ArrayList();
    67      private static BCELComparator _cmp = new BCELComparator() {
    68
    69          public boolean equals( Object o1, Object o2 ) {
    70              MethodGen THIS = (MethodGen) o1;
    71              MethodGen THAT = (MethodGen) o2;
    72              return THIS.getName().equals(THAT.getName())
    73                      && THIS.getSignature().equals(THAT.getSignature());
    74          }
    75
    76
    77          public int hashCode( Object o ) {
    78              MethodGen THIS = (MethodGen) o;
    79              return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
    80          }
    81      };
    82
    83
    84      /***
    85       * Declare method. If the method is non-static the constructor
    86       * automatically declares a local variable `$this' in slot 0. The
    87       * actual code is contained in the `il' parameter, which may further
    88       * manipulated by the user. But he must take care not to remove any
    89       * instruction (handles) that are still referenced from this object.
    90       *
    91       * For example one may not add a local variable and later remove the
    92       * instructions it refers to without causing havoc. It is safe
    93       * however if you remove that local variable, too.
    94       *
    95       * @param access_flags access qualifiers
    96       * @param return_type  method type
    97       * @param arg_types argument types
    98       * @param arg_names argument names (if this is null, default names will be provided
    99       * for them)
    100      * @param method_name name of method
    101      * @param class_name class name containing this method (may be null, if you don't care)
    102      * @param il instruction list associated with this method, may be null only for
    103      * abstract or native methods
    104      * @param cp constant pool
    105      */
    106     public MethodGen(int access_flags, Type return_type, Type[] arg_types, String[] arg_names,
    107             String method_name, String class_name, InstructionList il, ConstantPoolGen cp) {
    108         setAccessFlags(access_flags);
    109         setType(return_type);
    110         setArgumentTypes(arg_types);
    111         setArgumentNames(arg_names);
    112         setName(method_name);
    113         setClassName(class_name);
    114         setInstructionList(il);
    115         setConstantPool(cp);
    116         boolean abstract_ = isAbstract() || isNative();
    117         InstructionHandle start = null;
    118         InstructionHandle end = null;
    119         if (!abstract_) {
    120             start = il.getStart();
    121             end = il.getEnd();
    122             /* Add local variables, namely the implicit `this' and the arguments
    123              */
    124             if (!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
    125                 addLocalVariable("this", new ObjectType(class_name), start, end);
    126             }
    127         }
    128         if (arg_types != null) {
    129             int size = arg_types.length;
    130             for (int i = 0; i < size; i++) {
    131                 if (Type.VOID == arg_types[i]) {
    132                     throw new ClassGenException("'void' is an illegal argument type for a method");
    133                 }
    134             }
    135             if (arg_names != null) { // Names for variables provided?
    136                 if (size != arg_names.length) {
    137                     throw new ClassGenException("Mismatch in argument array lengths: " + size
    138                             + " vs. " + arg_names.length);
    139                 }
    140             } else { // Give them dummy names
    141                 arg_names = new String[size];
    142                 for (int i = 0; i < size; i++) {
    143                     arg_names[i] = "arg" + i;
    144                 }
    145                 setArgumentNames(arg_names);
    146             }
    147             if (!abstract_) {
    148                 for (int i = 0; i < size; i++) {
    149                     addLocalVariable(arg_names[i], arg_types[i], start, end);
    150                 }
    151             }
    152         }
    153     }
    154
    155
    156     /***
    157      * Instantiate from existing method.
    158      *
    159      * @param m method
    160      * @param class_name class name containing this method
    161      * @param cp constant pool
    162      */
    163     public MethodGen(Method m, String class_name, ConstantPoolGen cp) {
    164         this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m
    165                 .getSignature()), null /* may be overridden anyway */
    166         , m.getName(), class_name,
    167                 ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)
    168                         ? new InstructionList(m.getCode().getCode())
    169                         : null, cp);
    170         Attribute[] attributes = m.getAttributes();
    171         for (int i = 0; i < attributes.length; i++) {
    172             Attribute a = attributes[i];
    173             if (a instanceof Code) {
    174                 Code c = (Code) a;
    175                 setMaxStack(c.getMaxStack());
    176                 setMaxLocals(c.getMaxLocals());
    177                 CodeException[] ces = c.getExceptionTable();
    178                 if (ces != null) {
    179                     for (int j = 0; j < ces.length; j++) {
    180                         CodeException ce = ces[j];
    181                         int type = ce.getCatchType();
    182                         ObjectType c_type = null;
    183                         if (type > 0) {
    184                             String cen = m.getConstantPool().getConstantString(type,
    185                                     Constants.CONSTANT_Class);
    186                             c_type = new ObjectType(cen);
    187                         }
    188                         int end_pc = ce.getEndPC();
    189                         int length = m.getCode().getCode().length;
    190                         InstructionHandle end;
    191                         if (length == end_pc) { // May happen, because end_pc is exclusive
    192                             end = il.getEnd();
    193                         } else {
    194                             end = il.findHandle(end_pc);
    195                             end = end.getPrev(); // Make it inclusive
    196                         }
    197                         addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce
    198                                 .getHandlerPC()), c_type);
    199                     }
    200                 }
    201                 Attribute[] c_attributes = c.getAttributes();
    202                 for (int j = 0; j < c_attributes.length; j++) {
    203                     a = c_attributes[j];
    204                     if (a instanceof LineNumberTable) {
    205                         LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable();
    206                         for (int k = 0; k < ln.length; k++) {
    207                             LineNumber l = ln[k];
    208                             InstructionHandle ih = il.findHandle(l.getStartPC());
    209                             if (ih != null) {
    210                                 addLineNumber(ih, l.getLineNumber());
    211                             }
    212                         }
    213                     } else if (a instanceof LocalVariableTable) {
    214                         LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
    215                         removeLocalVariables();
    216                         for (int k = 0; k < lv.length; k++) {
    217                             LocalVariable l = lv[k];
    218                             InstructionHandle start = il.findHandle(l.getStartPC());
    219                             InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
    220                             // Repair malformed handles
    221                             if (null == start) {
    222                                 start = il.getStart();
    223                             }
    224                             if (null == end) {
    225                                 end = il.getEnd();
    226                             }
    227                             addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
    228                                     .getIndex(), start, end);
    229                         }
    230                     } else {
    231                         addCodeAttribute(a);
    232                     }
    233                 }
    234             } else if (a instanceof ExceptionTable) {
    235                 String[] names = ((ExceptionTable) a).getExceptionNames();
    236                 for (int j = 0; j < names.length; j++) {
    237                     addException(names[j]);
    238                 }
    239             } else {
    240                 addAttribute(a);
    241             }
    242         }
    243     }
    244
    245
    246     /***
    247      * Adds a local variable to this method.
    248      *
    249      * @param name variable name
    250      * @param type variable type
    251      * @param slot the index of the local variable, if type is long or double, the next available
    252      * index is slot+2
    253      * @param start from where the variable is valid
    254      * @param end until where the variable is valid
    255      * @return new local variable object
    256      * @see LocalVariable
    257      */
    258     public LocalVariableGen addLocalVariable( String name, Type type, int slot,
    259             InstructionHandle start, InstructionHandle end ) {
    260         byte t = type.getType();
    261         if (t != Constants.T_ADDRESS) {
    262             int add = type.getSize();
    263             if (slot + add > max_locals) {
    264                 max_locals = slot + add;
    265             }
    266             LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
    267             int i;
    268             if ((i = variable_vec.indexOf(l)) >= 0) {
    269                 variable_vec.set(i, l);
    270             } else {
    271                 variable_vec.add(l);
    272             }
    273             return l;
    274         } else {
    275             throw new IllegalArgumentException("Can not use " + type
    276                     + " as type for local variable");
    277         }
    278     }
    279
    280
    281     /***
    282      * Adds a local variable to this method and assigns an index automatically.
    283      *
    284      * @param name variable name
    285      * @param type variable type
    286      * @param start from where the variable is valid, if this is null,
    287      * it is valid from the start
    288      * @param end until where the variable is valid, if this is null,
    289      * it is valid to the end
    290      * @return new local variable object
    291      * @see LocalVariable
    292      */
    293     public LocalVariableGen addLocalVariable( String name, Type type, InstructionHandle start,
    294             InstructionHandle end ) {
    295         return addLocalVariable(name, type, max_locals, start, end);
    296     }
    297
    298
    299     /***
    300      * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
    301      * with an explicit index argument.
    302      */
    303     public void removeLocalVariable( LocalVariableGen l ) {
    304         variable_vec.remove(l);
    305     }
    306
    307
    308     /***
    309      * Remove all local variables.
    310      */
    311     public void removeLocalVariables() {
    312         variable_vec.clear();
    313     }
    314
    315
    316     /***
    317      * Sort local variables by index
    318      */
    319     private static final void sort( LocalVariableGen[] vars, int l, int r ) {
    320         int i = l, j = r;
    321         int m = vars[(l + r) / 2].getIndex();
    322         LocalVariableGen h;
    323         do {
    324             while (vars[i].getIndex() < m) {
    325                 i++;
    326             }
    327             while (m < vars[j].getIndex()) {
    328                 j--;
    329             }
    330             if (i <= j) {
    331                 h = vars[i];
    332                 vars[i] = vars[j];
    333                 vars[j] = h; // Swap elements
    334                 i++;
    335                 j--;
    336             }
    337         } while (i <= j);
    338         if (l < j) {
    339             sort(vars, l, j);
    340         }
    341         if (i < r) {
    342             sort(vars, i, r);
    343         }
    344     }
    345
    346
    347     /*
    348      * If the range of the variable has not been set yet, it will be set to be valid from
    349      * the start to the end of the instruction list.
    350      *
    351      * @return array of declared local variables sorted by index
    352      */
    353     public LocalVariableGen[] getLocalVariables() {
    354         int size = variable_vec.size();
    355         LocalVariableGen[] lg = new LocalVariableGen[size];
    356         variable_vec.toArray(lg);
    357         for (int i = 0; i < size; i++) {
    358             if (lg[i].getStart() == null) {
    359                 lg[i].setStart(il.getStart());
    360             }
    361             if (lg[i].getEnd() == null) {
    362                 lg[i].setEnd(il.getEnd());
    363             }
    364         }
    365         if (size > 1) {
    366             sort(lg, 0, size - 1);
    367         }
    368         return lg;
    369     }
    370
    371
    372     /***
    373      * @return `LocalVariableTable' attribute of all the local variables of this method.
    374      */
    375     public LocalVariableTable getLocalVariableTable( ConstantPoolGen cp ) {
    376         LocalVariableGen[] lg = getLocalVariables();
    377         int size = lg.length;
    378         LocalVariable[] lv = new LocalVariable[size];
    379         for (int i = 0; i < size; i++) {
    380             lv[i] = lg[i].getLocalVariable(cp);
    381         }
    382         return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp
    383                 .getConstantPool());
    384     }
    385
    386
    387     /***
    388      * Give an instruction a line number corresponding to the source code line.
    389      *
    390      * @param ih instruction to tag
    391      * @return new line number object
    392      * @see LineNumber
    393      */
    394     public LineNumberGen addLineNumber( InstructionHandle ih, int src_line ) {
    395         LineNumberGen l = new LineNumberGen(ih, src_line);
    396         line_number_vec.add(l);
    397         return l;
    398     }
    399
    400
    401     /***
    402      * Remove a line number.
    403      */
    404     public void removeLineNumber( LineNumberGen l ) {
    405         line_number_vec.remove(l);
    406     }
    407
    408
    409     /***
    410      * Remove all line numbers.
    411      */
    412     public void removeLineNumbers() {
    413         line_number_vec.clear();
    414     }
    415
    416
    417     /*
    418      * @return array of line numbers
    419      */
    420     public LineNumberGen[] getLineNumbers() {
    421         LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
    422         line_number_vec.toArray(lg);
    423         return lg;
    424     }
    425
    426
    427     /***
    428      * @return `LineNumberTable' attribute of all the local variables of this method.
    429      */
    430     public LineNumberTable getLineNumberTable( ConstantPoolGen cp ) {
    431         int size = line_number_vec.size();
    432         LineNumber[] ln = new LineNumber[size];
    433         try {
    434             for (int i = 0; i < size; i++) {
    435                 ln[i] = ((LineNumberGen) line_number_vec.get(i)).getLineNumber();
    436             }
    437         } catch (ArrayIndexOutOfBoundsException e) {
    438         } // Never occurs
    439         return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp
    440                 .getConstantPool());
    441     }
    442
    443
    444     /***
    445      * Add an exception handler, i.e., specify region where a handler is active and an
    446      * instruction where the actual handling is done.
    447      *
    448      * @param start_pc Start of region (inclusive)
    449      * @param end_pc End of region (inclusive)
    450      * @param handler_pc Where handling is done
    451      * @param catch_type class type of handled exception or null if any
    452      * exception is handled
    453      * @return new exception handler object
    454      */
    455     public CodeExceptionGen addExceptionHandler( InstructionHandle start_pc,
    456             InstructionHandle end_pc, InstructionHandle handler_pc, ObjectType catch_type ) {
    457         if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) {
    458             throw new ClassGenException("Exception handler target is null instruction");
    459         }
    460         CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type);
    461         exception_vec.add(c);
    462         return c;
    463     }
    464
    465
    466     /***
    467      * Remove an exception handler.
    468      */
    469     public void removeExceptionHandler( CodeExceptionGen c ) {
    470         exception_vec.remove(c);
    471     }
    472
    473
    474     /***
    475      * Remove all line numbers.
    476      */
    477     public void removeExceptionHandlers() {
    478         exception_vec.clear();
    479     }
    480
    481
    482     /*
    483      * @return array of declared exception handlers
    484      */
    485     public CodeExceptionGen[] getExceptionHandlers() {
    486         CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()];
    487         exception_vec.toArray(cg);
    488         return cg;
    489     }
    490
    491
    492     /***
    493      * @return code exceptions for `Code' attribute
    494      */
    495     private CodeException[] getCodeExceptions() {
    496         int size = exception_vec.size();
    497         CodeException[] c_exc = new CodeException[size];
    498         try {
    499             for (int i = 0; i < size; i++) {
    500                 CodeExceptionGen c = (CodeExceptionGen) exception_vec.get(i);
    501                 c_exc[i] = c.getCodeException(cp);
    502             }
    503         } catch (ArrayIndexOutOfBoundsException e) {
    504         }
    505         return c_exc;
    506     }
    507
    508
    509     /***
    510      * Add an exception possibly thrown by this method.
    511      *
    512      * @param class_name (fully qualified) name of exception
    513      */
    514     public void addException( String class_name ) {
    515         throws_vec.add(class_name);
    516     }
    517
    518
    519     /***
    520      * Remove an exception.
    521      */
    522     public void removeException( String c ) {
    523         throws_vec.remove(c);
    524     }
    525
    526
    527     /***
    528      * Remove all exceptions.
    529      */
    530     public void removeExceptions() {
    531         throws_vec.clear();
    532     }
    533
    534
    535     /*
    536      * @return array of thrown exceptions
    537      */
    538     public String[] getExceptions() {
    539         String[] e = new String[throws_vec.size()];
    540         throws_vec.toArray(e);
    541         return e;
    542     }
    543
    544
    545     /***
    546      * @return `Exceptions' attribute of all the exceptions thrown by this method.
    547      */
    548     private ExceptionTable getExceptionTable( ConstantPoolGen cp ) {
    549         int size = throws_vec.size();
    550         int[] ex = new int[size];
    551         try {
    552             for (int i = 0; i < size; i++) {
    553                 ex[i] = cp.addClass((String) throws_vec.get(i));
    554             }
    555         } catch (ArrayIndexOutOfBoundsException e) {
    556         }
    557         return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool());
    558     }
    559
    560
    561     /***
    562      * Add an attribute to the code. Currently, the JVM knows about the
    563      * LineNumberTable, LocalVariableTable and StackMap attributes,
    564      * where the former two will be generated automatically and the
    565      * latter is used for the MIDP only. Other attributes will be
    566      * ignored by the JVM but do no harm.
    567      *
    568      * @param a attribute to be added
    569      */
    570     public void addCodeAttribute( Attribute a ) {
    571         code_attrs_vec.add(a);
    572     }
    573
    574
    575     /***
    576      * Remove a code attribute.
    577      */
    578     public void removeCodeAttribute( Attribute a ) {
    579         code_attrs_vec.remove(a);
    580     }
    581
    582
    583     /***
    584      * Remove all code attributes.
    585      */
    586     public void removeCodeAttributes() {
    587         code_attrs_vec.clear();
    588     }
    589
    590
    591     /***
    592      * @return all attributes of this method.
    593      */
    594     public Attribute[] getCodeAttributes() {
    595         Attribute[] attributes = new Attribute[code_attrs_vec.size()];
    596         code_attrs_vec.toArray(attributes);
    597         return attributes;
    598     }
    599
    600
    601     /***
    602      * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
    603      * before calling this method (the same applies for max locals).
    604      *
    605      * @return method object
    606      */
    607     public Method getMethod() {
    608         String signature = getSignature();
    609         int name_index = cp.addUtf8(name);
    610         int signature_index = cp.addUtf8(signature);
    611         /* Also updates positions of instructions, i.e., their indices
    612          */
    613         byte[] byte_code = null;
    614         if (il != null) {
    615             byte_code = il.getByteCode();
    616         }
    617         LineNumberTable lnt = null;
    618         LocalVariableTable lvt = null;
    619         /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
    620          */
    621         if ((variable_vec.size() > 0) && !strip_attributes) {
    622             addCodeAttribute(lvt = getLocalVariableTable(cp));
    623         }
    624         if ((line_number_vec.size() > 0) && !strip_attributes) {
    625             addCodeAttribute(lnt = getLineNumberTable(cp));
    626         }
    627         Attribute[] code_attrs = getCodeAttributes();
    628         /* Each attribute causes 6 additional header bytes
    629          */
    630         int attrs_len = 0;
    631         for (int i = 0; i < code_attrs.length; i++) {
    632             attrs_len += (code_attrs[i].getLength() + 6);
    633         }
    634         CodeException[] c_exc = getCodeExceptions();
    635         int exc_len = c_exc.length * 8; // Every entry takes 8 bytes
    636         Code code = null;
    637         if ((il != null) && !isAbstract() && !isNative()) {
    638             // Remove any stale code attribute
    639             Attribute[] attributes = getAttributes();
    640             for (int i = 0; i < attributes.length; i++) {
    641                 Attribute a = attributes[i];
    642                 if (a instanceof Code) {
    643                     removeAttribute(a);
    644                 }
    645             }
    646             code = new Code(cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code
    647                     2 + exc_len + // exceptions
    648                     2 + attrs_len, // attributes
    649                     max_stack, max_locals, byte_code, c_exc, code_attrs, cp.getConstantPool());
    650             addAttribute(code);
    651         }
    652         ExceptionTable et = null;
    653         if (throws_vec.size() > 0) {
    654             addAttribute(et = getExceptionTable(cp));
    655             // Add `Exceptions' if there are "throws" clauses
    656         }
    657         Method m = new Method(access_flags, name_index, signature_index, getAttributes(), cp
    658                 .getConstantPool());
    659         // Undo effects of adding attributes
    660         if (lvt != null) {
    661             removeCodeAttribute(lvt);
    662         }
    663         if (lnt != null) {
    664             removeCodeAttribute(lnt);
    665         }
    666         if (code != null) {
    667             removeAttribute(code);
    668         }
    669         if (et != null) {
    670             removeAttribute(et);
    671         }
    672         return m;
    673     }
    674
    675
    676     /***
    677      * Remove all NOPs from the instruction list (if possible) and update every
    678      * object refering to them, i.e., branch instructions, local variables and
    679      * exception handlers.
    680      */
    681     public void removeNOPs() {
    682         if (il != null) {
    683             InstructionHandle next;
    684             /* Check branch instructions.
    685              */
    686             for (InstructionHandle ih = il.getStart(); ih != null; ih = next) {
    687                 next = ih.next;
    688                 if ((next != null) && (ih.getInstruction() instanceof NOP)) {
    689                     try {
    690                         il.delete(ih);
    691                     } catch (TargetLostException e) {
    692                         InstructionHandle[] targets = e.getTargets();
    693                         for (int i = 0; i < targets.length; i++) {
    694                             InstructionTargeter[] targeters = targets[i].getTargeters();
    695                             for (int j = 0; j < targeters.length; j++) {
    696                                 targeters[j].updateTarget(targets[i], next);
    697                             }
    698                         }
    699                     }
    700                 }
    701             }
    702         }
    703     }
    704
    705
    706     /***
    707      * Set maximum number of local variables.
    708      */
    709     public void setMaxLocals( int m ) {
    710         max_locals = m;
    711     }
    712
    713
    714     public int getMaxLocals() {
    715         return max_locals;
    716     }
    717
    718
    719     /***
    720      * Set maximum stack size for this method.
    721      */
    722     public void setMaxStack( int m ) {
    723         max_stack = m;
    724     }
    725
    726
    727     public int getMaxStack() {
    728         return max_stack;
    729     }
    730
    731
    732     /*** @return class that contains this method
    733      */
    734     public String getClassName() {
    735         return class_name;
    736     }
    737
    738
    739     public void setClassName( String class_name ) {
    740         this.class_name = class_name;
    741     }
    742
    743
    744     public void setReturnType( Type return_type ) {
    745         setType(return_type);
    746     }
    747
    748
    749     public Type getReturnType() {
    750         return getType();
    751     }
    752
    753
    754     public void setArgumentTypes( Type[] arg_types ) {
    755         this.arg_types = arg_types;
    756     }
    757
    758
    759     public Type[] getArgumentTypes() {
    760         return (Type[]) arg_types.clone();
    761     }
    762
    763
    764     public void setArgumentType( int i, Type type ) {
    765         arg_types[i] = type;
    766     }
    767
    768
    769     public Type getArgumentType( int i ) {
    770         return arg_types[i];
    771     }
    772
    773
    774     public void setArgumentNames( String[] arg_names ) {
    775         this.arg_names = arg_names;
    776     }
    777
    778
    779     public String[] getArgumentNames() {
    780         return (String[]) arg_names.clone();
    781     }
    782
    783
    784     public void setArgumentName( int i, String name ) {
    785         arg_names[i] = name;
    786     }
    787
    788
    789     public String getArgumentName( int i ) {
    790         return arg_names[i];
    791     }
    792
    793
    794     public InstructionList getInstructionList() {
    795         return il;
    796     }
    797
    798
    799     public void setInstructionList( InstructionList il ) {
    800         this.il = il;
    801     }
    802
    803
    804     public String getSignature() {
    805         return Type.getMethodSignature(type, arg_types);
    806     }
    807
    808
    809     /***
    810      * Computes max. stack size by performing control flow analysis.
    811      */
    812     public void setMaxStack() {
    813         if (il != null) {
    814             max_stack = getMaxStack(cp, il, getExceptionHandlers());
    815         } else {
    816             max_stack = 0;
    817         }
    818     }
    819
    820
    821     /***
    822      * Compute maximum number of local variables.
    823      */
    824     public void setMaxLocals() {
    825         if (il != null) {
    826             int max = isStatic() ? 0 : 1;
    827             if (arg_types != null) {
    828                 for (int i = 0; i < arg_types.length; i++) {
    829                     max += arg_types[i].getSize();
    830                 }
    831             }
    832             for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
    833                 Instruction ins = ih.getInstruction();
    834                 if ((ins instanceof LocalVariableInstruction) || (ins instanceof RET)
    835                         || (ins instanceof IINC)) {
    836                     int index = ((IndexedInstruction) ins).getIndex()
    837                             + ((TypedInstruction) ins).getType(cp).getSize();
    838                     if (index > max) {
    839                         max = index;
    840                     }
    841                 }
    842             }
    843             max_locals = max;
    844         } else {
    845             max_locals = 0;
    846         }
    847     }
    848
    849
    850     /*** Do not/Do produce attributes code attributesLineNumberTable and
    851      * LocalVariableTable, like javac -O
    852      */
    853     public void stripAttributes( boolean flag ) {
    854         strip_attributes = flag;
    855     }
    856
    857     static final class BranchTarget {
    858
    859         InstructionHandle target;
    860         int stackDepth;
    861
    862
    863         BranchTarget(InstructionHandle target, int stackDepth) {
    864             this.target = target;
    865             this.stackDepth = stackDepth;
    866         }
    867     }
    868
    869     static final class BranchStack {
    870
    871         Stack branchTargets = new Stack();
    872         Hashtable visitedTargets = new Hashtable();
    873
    874
    875         public void push( InstructionHandle target, int stackDepth ) {
    876             if (visited(target)) {
    877                 return;
    878             }
    879             branchTargets.push(visit(target, stackDepth));
    880         }
    881
    882
    883         public BranchTarget pop() {
    884             if (!branchTargets.empty()) {
    885                 BranchTarget bt = (BranchTarget) branchTargets.pop();
    886                 return bt;
    887             }
    888             return null;
    889         }
    890
    891
    892         private final BranchTarget visit( InstructionHandle target, int stackDepth ) {
    893             BranchTarget bt = new BranchTarget(target, stackDepth);
    894             visitedTargets.put(target, bt);
    895             return bt;
    896         }
    897
    898
    899         private final boolean visited( InstructionHandle target ) {
    900             return (visitedTargets.get(target) != null);
    901         }
    902     }
    903
    904
    905     /***
    906      * Computes stack usage of an instruction list by performing control flow analysis.
    907      *
    908      * @return maximum stack depth used by method
    909      */
    910     public static int getMaxStack( ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et ) {
    911         BranchStack branchTargets = new BranchStack();
    912         /* Initially, populate the branch stack with the exception
    913          * handlers, because these aren't (necessarily) branched to
    914          * explicitly. in each case, the stack will have depth 1,
    915          * containing the exception object.
    916          */
    917         for (int i = 0; i < et.length; i++) {
    918             InstructionHandle handler_pc = et[i].getHandlerPC();
    919             if (handler_pc != null) {
    920                 branchTargets.push(handler_pc, 1);
    921             }
    922         }
    923         int stackDepth = 0, maxStackDepth = 0;
    924         InstructionHandle ih = il.getStart();
    925         while (ih != null) {
    926             Instruction instruction = ih.getInstruction();
    927             short opcode = instruction.getOpcode();
    928             int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
    929             stackDepth += delta;
    930             if (stackDepth > maxStackDepth) {
    931                 maxStackDepth = stackDepth;
    932             }
    933             // choose the next instruction based on whether current is a branch.
    934             if (instruction instanceof BranchInstruction) {
    935                 BranchInstruction branch = (BranchInstruction) instruction;
    936                 if (instruction instanceof Select) {
    937                     // explore all of the select's targets. the default target is handled below.
    938                     Select select = (Select) branch;
    939                     InstructionHandle[] targets = select.getTargets();
    940                     for (int i = 0; i < targets.length; i++) {
    941                         branchTargets.push(targets[i], stackDepth);
    942                     }
    943                     // nothing to fall through to.
    944                     ih = null;
    945                 } else if (!(branch instanceof IfInstruction)) {
    946                     // if an instruction that comes back to following PC,
    947                     // push next instruction, with stack depth reduced by 1.
    948                     if (opcode == Constants.JSR || opcode == Constants.JSR_W) {
    949                         branchTargets.push(ih.getNext(), stackDepth - 1);
    950                     }
    951                     ih = null;
    952                 }
    953                 // for all branches, the target of the branch is pushed on the branch stack.
    954                 // conditional branches have a fall through case, selects don't, and
    955                 // jsr/jsr_w return to the next instruction.
    956                 branchTargets.push(branch.getTarget(), stackDepth);
    957             } else {
    958                 // check for instructions that terminate the method.
    959                 if (opcode == Constants.ATHROW || opcode == Constants.RET
    960                         || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
    961                     ih = null;
    962                 }
    963             }
    964             // normal case, go to the next instruction.
    965             if (ih != null) {
    966                 ih = ih.getNext();
    967             }
    968             // if we have no more instructions, see if there are any deferred branches to explore.
    969             if (ih == null) {
    970                 BranchTarget bt = branchTargets.pop();
    971                 if (bt != null) {
    972                     ih = bt.target;
    973                     stackDepth = bt.stackDepth;
    974                 }
    975             }
    976         }
    977         return maxStackDepth;
    978     }
    979
    980     private List observers;
    981
    982
    983     /*** Add observer for this object.
    984      */
    985     public void addObserver( MethodObserver o ) {
    986         if (observers == null) {
    987             observers = new ArrayList();
    988         }
    989         observers.add(o);
    990     }
    991
    992
    993     /*** Remove observer for this object.
    994      */
    995     public void removeObserver( MethodObserver o ) {
    996         if (observers != null) {
    997             observers.remove(o);
    998         }
    999     }
    1000
    1001
    1002     /*** Call notify() method on all observers. This method is not called
    1003      * automatically whenever the state has changed, but has to be
    1004      * called by the user after he has finished editing the object.
    1005      */
    1006     public void update() {
    1007         if (observers != null) {
    1008             for (Iterator e = observers.iterator(); e.hasNext();) {
    1009                 ((MethodObserver) e.next()).notify(this);
    1010             }
    1011         }
    1012     }
    1013
    1014
    1015     /***
    1016      * Return string representation close to declaration format,
    1017      * `public static void main(String[]) throws IOException', e.g.
    1018      *
    1019      * @return String representation of the method.
    1020      */
    1021     public final String toString() {
    1022         String access = Utility.accessToString(access_flags);
    1023         String signature = Type.getMethodSignature(type, arg_types);
    1024         signature = Utility.methodSignatureToString(signature, name, access, true,
    1025                 getLocalVariableTable(cp));
    1026         StringBuffer buf = new StringBuffer(signature);
    1027         if (throws_vec.size() > 0) {
    1028             for (Iterator e = throws_vec.iterator(); e.hasNext();) {
    1029                 buf.append("\n\t\tthrows ").append(e.next());
    1030             }
    1031         }
    1032         return buf.toString();
    1033     }
    1034
    1035
    1036     /*** @return deep copy of this method
    1037      */
    1038     public MethodGen copy( String class_name, ConstantPoolGen cp ) {
    1039         Method m = ((MethodGen) clone()).getMethod();
    1040         MethodGen mg = new MethodGen(m, class_name, this.cp);
    1041         if (this.cp != cp) {
    1042             mg.setConstantPool(cp);
    1043             mg.getInstructionList().replaceConstantPool(this.cp, cp);
    1044         }
    1045         return mg;
    1046     }
    1047
    1048
    1049     /***
    1050      * @return Comparison strategy object
    1051      */
    1052     public static BCELComparator getComparator() {
    1053         return _cmp;
    1054     }
    1055
    1056
    1057     /***
    1058      * @param comparator Comparison strategy object
    1059      */
    1060     public static void setComparator( BCELComparator comparator ) {
    1061         _cmp = comparator;
    1062     }
    1063
    1064
    1065     /***
    1066      * Return value as defined by given BCELComparator strategy.
    1067      * By default two MethodGen objects are said to be equal when
    1068      * their names and signatures are equal.
    1069      *
    1070      * @see java.lang.Object#equals(java.lang.Object)
    1071      */
    1072     public boolean equals( Object obj ) {
    1073         return _cmp.equals(this, obj);
    1074     }
    1075
    1076
    1077     /***
    1078      * Return value as defined by given BCELComparator strategy.
    1079      * By default return the hashcode of the method's name XOR signature.
    1080      *
    1081      * @see java.lang.Object#hashCode()
    1082      */
    1083     public int hashCode() {
    1084         return _cmp.hashCode(this);
    1085     }
    1086 }