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