View Javadoc

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