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 }