• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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$
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      */
MethodGen(final int access_flags, final Type return_type, final Type[] arg_types, String[] arg_names, final String method_name, final String class_name, final InstructionList il, final ConstantPoolGen cp)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      */
MethodGen(final Method m, final String class_name, final ConstantPoolGen cp)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      */
addLocalVariable( final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end, final int orig_index )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      */
addLocalVariable( final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end )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      */
addLocalVariable( final String name, final Type type, final InstructionHandle start, final InstructionHandle end )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      */
removeLocalVariable( final LocalVariableGen l )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      */
removeLocalVariables()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      */
getLocalVariables()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      */
getLocalVariableTable( final ConstantPoolGen cp )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      */
getLocalVariableTypeTable()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      */
addLineNumber( final InstructionHandle ih, final int src_line )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      */
removeLineNumber( final LineNumberGen l )410     public void removeLineNumber( final LineNumberGen l ) {
411         line_number_vec.remove(l);
412     }
413 
414 
415     /**
416      * Remove all line numbers.
417      */
removeLineNumbers()418     public void removeLineNumbers() {
419         line_number_vec.clear();
420     }
421 
422 
423     /*
424      * @return array of line numbers
425      */
getLineNumbers()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      */
getLineNumberTable( final ConstantPoolGen cp )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      */
addExceptionHandler( final InstructionHandle start_pc, final InstructionHandle end_pc, final InstructionHandle handler_pc, final ObjectType catch_type )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      */
removeExceptionHandler( final CodeExceptionGen c )472     public void removeExceptionHandler( final CodeExceptionGen c ) {
473         exception_vec.remove(c);
474     }
475 
476 
477     /**
478      * Remove all line numbers.
479      */
removeExceptionHandlers()480     public void removeExceptionHandlers() {
481         exception_vec.clear();
482     }
483 
484 
485     /*
486      * @return array of declared exception handlers
487      */
getExceptionHandlers()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      */
getCodeExceptions()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      */
addException( final String class_name )514     public void addException( final String class_name ) {
515         throws_vec.add(class_name);
516     }
517 
518 
519     /**
520      * Remove an exception.
521      */
removeException( final String c )522     public void removeException( final String c ) {
523         throws_vec.remove(c);
524     }
525 
526 
527     /**
528      * Remove all exceptions.
529      */
removeExceptions()530     public void removeExceptions() {
531         throws_vec.clear();
532     }
533 
534 
535     /*
536      * @return array of thrown exceptions
537      */
getExceptions()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      */
getExceptionTable( final ConstantPoolGen cp )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      */
addCodeAttribute( final Attribute a )567     public void addCodeAttribute( final Attribute a ) {
568         code_attrs_vec.add(a);
569     }
570 
571 
572     /**
573      * Remove the LocalVariableTypeTable
574      */
removeLocalVariableTypeTable( )575     public void removeLocalVariableTypeTable( ) {
576         local_variable_type_table = null;
577     }
578 
579     /**
580      * Remove a code attribute.
581      */
removeCodeAttribute( final Attribute a )582     public void removeCodeAttribute( final Attribute a ) {
583         code_attrs_vec.remove(a);
584     }
585 
586 
587     /**
588      * Remove all code attributes.
589      */
removeCodeAttributes()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      */
getCodeAttributes()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      */
addAnnotationsAsAttribute(final ConstantPoolGen cp)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      */
addParameterAnnotationsAsAttribute(final ConstantPoolGen cp)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      */
getMethod()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 
updateLocalVariableTable(final LocalVariableTable a)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 
adjustLocalVariableTypeTable(final LocalVariableTable lvt)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      */
removeNOPs()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      */
setMaxLocals( final int m )784     public void setMaxLocals( final int m ) {
785         max_locals = m;
786     }
787 
788 
getMaxLocals()789     public int getMaxLocals() {
790         return max_locals;
791     }
792 
793 
794     /**
795      * Set maximum stack size for this method.
796      */
setMaxStack( final int m )797     public void setMaxStack( final int m ) { // TODO could be package-protected?
798         max_stack = m;
799     }
800 
801 
getMaxStack()802     public int getMaxStack() {
803         return max_stack;
804     }
805 
806 
807     /** @return class that contains this method
808      */
getClassName()809     public String getClassName() {
810         return class_name;
811     }
812 
813 
setClassName( final String class_name )814     public void setClassName( final String class_name ) { // TODO could be package-protected?
815         this.class_name = class_name;
816     }
817 
818 
setReturnType( final Type return_type )819     public void setReturnType( final Type return_type ) {
820         setType(return_type);
821     }
822 
823 
getReturnType()824     public Type getReturnType() {
825         return getType();
826     }
827 
828 
setArgumentTypes( final Type[] arg_types )829     public void setArgumentTypes( final Type[] arg_types ) {
830         this.arg_types = arg_types;
831     }
832 
833 
getArgumentTypes()834     public Type[] getArgumentTypes() {
835         return arg_types.clone();
836     }
837 
838 
setArgumentType( final int i, final Type type )839     public void setArgumentType( final int i, final Type type ) {
840         arg_types[i] = type;
841     }
842 
843 
getArgumentType( final int i )844     public Type getArgumentType( final int i ) {
845         return arg_types[i];
846     }
847 
848 
setArgumentNames( final String[] arg_names )849     public void setArgumentNames( final String[] arg_names ) {
850         this.arg_names = arg_names;
851     }
852 
853 
getArgumentNames()854     public String[] getArgumentNames() {
855         return arg_names.clone();
856     }
857 
858 
setArgumentName( final int i, final String name )859     public void setArgumentName( final int i, final String name ) {
860         arg_names[i] = name;
861     }
862 
863 
getArgumentName( final int i )864     public String getArgumentName( final int i ) {
865         return arg_names[i];
866     }
867 
868 
getInstructionList()869     public InstructionList getInstructionList() {
870         return il;
871     }
872 
873 
setInstructionList( final InstructionList il )874     public void setInstructionList( final InstructionList il ) { // TODO could be package-protected?
875         this.il = il;
876     }
877 
878 
879     @Override
getSignature()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      */
setMaxStack()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      */
setMaxLocals()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      */
stripAttributes( final boolean flag )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 
BranchTarget(final InstructionHandle target, final int stackDepth)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 
push( final InstructionHandle target, final int stackDepth )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 
pop()959         public BranchTarget pop() {
960             if (!branchTargets.empty()) {
961                 final BranchTarget bt = branchTargets.pop();
962                 return bt;
963             }
964             return null;
965         }
966 
967 
visit( final InstructionHandle target, final int stackDepth )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 
visited( final InstructionHandle target )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      */
getMaxStack( final ConstantPoolGen cp, final InstructionList il, final CodeExceptionGen[] et )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      */
addObserver( final MethodObserver o )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      */
removeObserver( final MethodObserver o )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      */
update()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
toString()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      */
copy( final String class_name, final ConstantPoolGen cp )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      */
getAnnotationsOnParameter(final int i)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      */
ensureExistingParameterAnnotationsUnpacked()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 
makeMutableVersion(final AnnotationEntry[] mutableArray)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 
addParameterAnnotation(final int parameterIndex, final AnnotationEntryGen annotation)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      */
getComparator()1244     public static BCELComparator getComparator() {
1245         return bcelComparator;
1246     }
1247 
1248 
1249     /**
1250      * @param comparator Comparison strategy object
1251      */
setComparator( final BCELComparator comparator )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
equals( final Object obj )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
hashCode()1277     public int hashCode() {
1278         return bcelComparator.hashCode(this);
1279     }
1280 }
1281