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