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