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