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