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