MethodGen.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.generic;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.Comparator;
  22. import java.util.Hashtable;
  23. import java.util.List;
  24. import java.util.Objects;
  25. import java.util.Stack;
  26. import java.util.stream.Collectors;

  27. import org.apache.bcel.Const;
  28. import org.apache.bcel.classfile.AnnotationEntry;
  29. import org.apache.bcel.classfile.Annotations;
  30. import org.apache.bcel.classfile.Attribute;
  31. import org.apache.bcel.classfile.Code;
  32. import org.apache.bcel.classfile.CodeException;
  33. import org.apache.bcel.classfile.ExceptionTable;
  34. import org.apache.bcel.classfile.LineNumber;
  35. import org.apache.bcel.classfile.LineNumberTable;
  36. import org.apache.bcel.classfile.LocalVariable;
  37. import org.apache.bcel.classfile.LocalVariableTable;
  38. import org.apache.bcel.classfile.LocalVariableTypeTable;
  39. import org.apache.bcel.classfile.Method;
  40. import org.apache.bcel.classfile.ParameterAnnotationEntry;
  41. import org.apache.bcel.classfile.ParameterAnnotations;
  42. import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
  43. import org.apache.bcel.classfile.Utility;
  44. import org.apache.bcel.util.BCELComparator;
  45. import org.apache.commons.lang3.ArrayUtils;
  46. import org.apache.commons.lang3.stream.Streams;

  47. /**
  48.  * Template class for building up a method. This is done by defining exception handlers, adding thrown exceptions, local
  49.  * variables and attributes, whereas the 'LocalVariableTable' and 'LineNumberTable' attributes will be set automatically
  50.  * for the code. Use stripAttributes() if you don't like this.
  51.  *
  52.  * While generating code it may be necessary to insert NOP operations. You can use the 'removeNOPs' method to get rid
  53.  * off them. The resulting method object can be obtained via the 'getMethod()' method.
  54.  *
  55.  * @see InstructionList
  56.  * @see Method
  57.  */
  58. public class MethodGen extends FieldGenOrMethodGen {

  59.     static final class BranchStack {

  60.         private final Stack<BranchTarget> branchTargets = new Stack<>();
  61.         private final Hashtable<InstructionHandle, BranchTarget> visitedTargets = new Hashtable<>();

  62.         public BranchTarget pop() {
  63.             if (!branchTargets.empty()) {
  64.                 return branchTargets.pop();
  65.             }
  66.             return null;
  67.         }

  68.         public void push(final InstructionHandle target, final int stackDepth) {
  69.             if (visited(target)) {
  70.                 return;
  71.             }
  72.             branchTargets.push(visit(target, stackDepth));
  73.         }

  74.         private BranchTarget visit(final InstructionHandle target, final int stackDepth) {
  75.             final BranchTarget bt = new BranchTarget(target, stackDepth);
  76.             visitedTargets.put(target, bt);
  77.             return bt;
  78.         }

  79.         private boolean visited(final InstructionHandle target) {
  80.             return visitedTargets.get(target) != null;
  81.         }
  82.     }

  83.     static final class BranchTarget {

  84.         final InstructionHandle target;
  85.         final int stackDepth;

  86.         BranchTarget(final InstructionHandle target, final int stackDepth) {
  87.             this.target = target;
  88.             this.stackDepth = stackDepth;
  89.         }
  90.     }

  91.     private static BCELComparator<FieldGenOrMethodGen> bcelComparator = new BCELComparator<FieldGenOrMethodGen>() {

  92.         @Override
  93.         public boolean equals(final FieldGenOrMethodGen a, final FieldGenOrMethodGen b) {
  94.             return a == b || a != null && b != null && Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getSignature(), b.getSignature());
  95.         }

  96.         @Override
  97.         public int hashCode(final FieldGenOrMethodGen o) {
  98.             return o != null ? Objects.hash(o.getSignature(), o.getName()) : 0;
  99.         }
  100.     };

  101.     private static byte[] getByteCodes(final Method method) {
  102.         final Code code = method.getCode();
  103.         if (code == null) {
  104.             throw new IllegalStateException(String.format("The method '%s' has no code.", method));
  105.         }
  106.         return code.getCode();
  107.     }

  108.     /**
  109.      * @return Comparison strategy object.
  110.      */
  111.     public static BCELComparator<FieldGenOrMethodGen> getComparator() {
  112.         return bcelComparator;
  113.     }

  114.     /**
  115.      * Computes stack usage of an instruction list by performing control flow analysis.
  116.      *
  117.      * @return maximum stack depth used by method
  118.      */
  119.     public static int getMaxStack(final ConstantPoolGen cp, final InstructionList il, final CodeExceptionGen[] et) {
  120.         final BranchStack branchTargets = new BranchStack();
  121.         /*
  122.          * Initially, populate the branch stack with the exception handlers, because these aren't (necessarily) branched to
  123.          * explicitly. in each case, the stack will have depth 1, containing the exception object.
  124.          */
  125.         for (final CodeExceptionGen element : et) {
  126.             final InstructionHandle handlerPc = element.getHandlerPC();
  127.             if (handlerPc != null) {
  128.                 branchTargets.push(handlerPc, 1);
  129.             }
  130.         }
  131.         int stackDepth = 0;
  132.         int maxStackDepth = 0;
  133.         InstructionHandle ih = il.getStart();
  134.         while (ih != null) {
  135.             final Instruction instruction = ih.getInstruction();
  136.             final short opcode = instruction.getOpcode();
  137.             final int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
  138.             stackDepth += delta;
  139.             if (stackDepth > maxStackDepth) {
  140.                 maxStackDepth = stackDepth;
  141.             }
  142.             // choose the next instruction based on whether current is a branch.
  143.             if (instruction instanceof BranchInstruction) {
  144.                 final BranchInstruction branch = (BranchInstruction) instruction;
  145.                 if (instruction instanceof Select) {
  146.                     // explore all of the select's targets. the default target is handled below.
  147.                     final Select select = (Select) branch;
  148.                     final InstructionHandle[] targets = select.getTargets();
  149.                     for (final InstructionHandle target : targets) {
  150.                         branchTargets.push(target, stackDepth);
  151.                     }
  152.                     // nothing to fall through to.
  153.                     ih = null;
  154.                 } else if (!(branch instanceof IfInstruction)) {
  155.                     // if an instruction that comes back to following PC,
  156.                     // push next instruction, with stack depth reduced by 1.
  157.                     if (opcode == Const.JSR || opcode == Const.JSR_W) {
  158.                         branchTargets.push(ih.getNext(), stackDepth - 1);
  159.                     }
  160.                     ih = null;
  161.                 }
  162.                 // for all branches, the target of the branch is pushed on the branch stack.
  163.                 // conditional branches have a fall through case, selects don't, and
  164.                 // jsr/jsr_w return to the next instruction.
  165.                 branchTargets.push(branch.getTarget(), stackDepth);
  166.             } else // check for instructions that terminate the method.
  167.             if (opcode == Const.ATHROW || opcode == Const.RET || opcode >= Const.IRETURN && opcode <= Const.RETURN) {
  168.                 ih = null;
  169.             }
  170.             // normal case, go to the next instruction.
  171.             if (ih != null) {
  172.                 ih = ih.getNext();
  173.             }
  174.             // if we have no more instructions, see if there are any deferred branches to explore.
  175.             if (ih == null) {
  176.                 final BranchTarget bt = branchTargets.pop();
  177.                 if (bt != null) {
  178.                     ih = bt.target;
  179.                     stackDepth = bt.stackDepth;
  180.                 }
  181.             }
  182.         }
  183.         return maxStackDepth;
  184.     }

  185.     /**
  186.      * @param comparator Comparison strategy object.
  187.      */
  188.     public static void setComparator(final BCELComparator<FieldGenOrMethodGen> comparator) {
  189.         bcelComparator = comparator;
  190.     }

  191.     private String className;
  192.     private Type[] argTypes;
  193.     private String[] argNames;
  194.     private int maxLocals;
  195.     private int maxStack;
  196.     private InstructionList il;

  197.     private boolean stripAttributes;
  198.     private LocalVariableTypeTable localVariableTypeTable;
  199.     private final List<LocalVariableGen> variableList = new ArrayList<>();

  200.     private final List<LineNumberGen> lineNumberList = new ArrayList<>();

  201.     private final List<CodeExceptionGen> exceptionList = new ArrayList<>();

  202.     private final List<String> throwsList = new ArrayList<>();

  203.     private final List<Attribute> codeAttrsList = new ArrayList<>();

  204.     private List<AnnotationEntryGen>[] paramAnnotations; // Array of lists containing AnnotationGen objects

  205.     private boolean hasParameterAnnotations;

  206.     private boolean haveUnpackedParameterAnnotations;

  207.     private List<MethodObserver> observers;

  208.     /**
  209.      * Declare method. If the method is non-static the constructor automatically declares a local variable '$this' in slot
  210.      * 0. The actual code is contained in the 'il' parameter, which may further manipulated by the user. But they must take
  211.      * care not to remove any instruction (handles) that are still referenced from this object.
  212.      *
  213.      * For example one may not add a local variable and later remove the instructions it refers to without causing havoc. It
  214.      * is safe however if you remove that local variable, too.
  215.      *
  216.      * @param accessFlags access qualifiers
  217.      * @param returnType method type
  218.      * @param argTypes argument types
  219.      * @param argNames argument names (if this is null, default names will be provided for them)
  220.      * @param methodName name of method
  221.      * @param className class name containing this method (may be null, if you don't care)
  222.      * @param il instruction list associated with this method, may be null only for abstract or native methods
  223.      * @param cp constant pool
  224.      */
  225.     public MethodGen(final int accessFlags, final Type returnType, final Type[] argTypes, String[] argNames, final String methodName, final String className,
  226.         final InstructionList il, final ConstantPoolGen cp) {
  227.         super(accessFlags);
  228.         setType(returnType);
  229.         setArgumentTypes(argTypes);
  230.         setArgumentNames(argNames);
  231.         setName(methodName);
  232.         setClassName(className);
  233.         setInstructionList(il);
  234.         setConstantPool(cp);
  235.         final boolean abstract_ = isAbstract() || isNative();
  236.         InstructionHandle start = null;
  237.         final InstructionHandle end = null;
  238.         if (!abstract_) {
  239.             start = il.getStart();
  240.             // end == null => live to end of method
  241.             /*
  242.              * Add local variables, namely the implicit 'this' and the arguments
  243.              */
  244.             if (!isStatic() && className != null) { // Instance method -> 'this' is local var 0
  245.                 addLocalVariable("this", ObjectType.getInstance(className), start, end);
  246.             }
  247.         }
  248.         if (argTypes != null) {
  249.             final int size = argTypes.length;
  250.             for (final Type argType : argTypes) {
  251.                 if (Type.VOID == argType) {
  252.                     throw new ClassGenException("'void' is an illegal argument type for a method");
  253.                 }
  254.             }
  255.             if (argNames != null) { // Names for variables provided?
  256.                 if (size != argNames.length) {
  257.                     throw new ClassGenException("Mismatch in argument array lengths: " + size + " vs. " + argNames.length);
  258.                 }
  259.             } else { // Give them dummy names
  260.                 argNames = new String[size];
  261.                 for (int i = 0; i < size; i++) {
  262.                     argNames[i] = "arg" + i;
  263.                 }
  264.                 setArgumentNames(argNames);
  265.             }
  266.             if (!abstract_) {
  267.                 for (int i = 0; i < size; i++) {
  268.                     addLocalVariable(argNames[i], argTypes[i], start, end);
  269.                 }
  270.             }
  271.         }
  272.     }

  273.     /**
  274.      * Instantiate from existing method.
  275.      *
  276.      * @param method method
  277.      * @param className class name containing this method
  278.      * @param cp constant pool
  279.      */
  280.     public MethodGen(final Method method, final String className, final ConstantPoolGen cp) {
  281.         this(method.getAccessFlags(), Type.getReturnType(method.getSignature()), Type.getArgumentTypes(method.getSignature()),
  282.             null /* may be overridden anyway */
  283.             , method.getName(), className,
  284.             (method.getAccessFlags() & (Const.ACC_ABSTRACT | Const.ACC_NATIVE)) == 0 ? new InstructionList(getByteCodes(method)) : null, cp);
  285.         final Attribute[] attributes = method.getAttributes();
  286.         for (final Attribute attribute : attributes) {
  287.             Attribute a = attribute;
  288.             if (a instanceof Code) {
  289.                 final Code c = (Code) a;
  290.                 setMaxStack(c.getMaxStack());
  291.                 setMaxLocals(c.getMaxLocals());
  292.                 final CodeException[] ces = c.getExceptionTable();
  293.                 if (ces != null) {
  294.                     for (final CodeException ce : ces) {
  295.                         final int type = ce.getCatchType();
  296.                         ObjectType cType = null;
  297.                         if (type > 0) {
  298.                             final String cen = method.getConstantPool().getConstantString(type, Const.CONSTANT_Class);
  299.                             cType = ObjectType.getInstance(cen);
  300.                         }
  301.                         final int endPc = ce.getEndPC();
  302.                         final int length = getByteCodes(method).length;
  303.                         InstructionHandle end;
  304.                         if (length == endPc) { // May happen, because end_pc is exclusive
  305.                             end = il.getEnd();
  306.                         } else {
  307.                             end = il.findHandle(endPc);
  308.                             end = end.getPrev(); // Make it inclusive
  309.                         }
  310.                         addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce.getHandlerPC()), cType);
  311.                     }
  312.                 }
  313.                 final Attribute[] cAttributes = c.getAttributes();
  314.                 for (final Attribute cAttribute : cAttributes) {
  315.                     a = cAttribute;
  316.                     if (a instanceof LineNumberTable) {
  317.                         ((LineNumberTable) a).forEach(l -> {
  318.                             final InstructionHandle ih = il.findHandle(l.getStartPC());
  319.                             if (ih != null) {
  320.                                 addLineNumber(ih, l.getLineNumber());
  321.                             }
  322.                         });
  323.                     } else if (a instanceof LocalVariableTable) {
  324.                         updateLocalVariableTable((LocalVariableTable) a);
  325.                     } else if (a instanceof LocalVariableTypeTable) {
  326.                         this.localVariableTypeTable = (LocalVariableTypeTable) a.copy(cp.getConstantPool());
  327.                     } else {
  328.                         addCodeAttribute(a);
  329.                     }
  330.                 }
  331.             } else if (a instanceof ExceptionTable) {
  332.                 Collections.addAll(throwsList, ((ExceptionTable) a).getExceptionNames());
  333.             } else if (a instanceof Annotations) {
  334.                 final Annotations runtimeAnnotations = (Annotations) a;
  335.                 runtimeAnnotations.forEach(element -> addAnnotationEntry(new AnnotationEntryGen(element, cp, false)));
  336.             } else {
  337.                 addAttribute(a);
  338.             }
  339.         }
  340.     }

  341.     /**
  342.      * @since 6.0
  343.      */
  344.     public void addAnnotationsAsAttribute(final ConstantPoolGen cp) {
  345.         addAll(AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()));
  346.     }

  347.     /**
  348.      * Add an attribute to the code. Currently, the JVM knows about the LineNumberTable, LocalVariableTable and StackMap
  349.      * attributes, where the former two will be generated automatically and the latter is used for the MIDP only. Other
  350.      * attributes will be ignored by the JVM but do no harm.
  351.      *
  352.      * @param a attribute to be added
  353.      */
  354.     public void addCodeAttribute(final Attribute a) {
  355.         codeAttrsList.add(a);
  356.     }

  357.     /**
  358.      * Add an exception possibly thrown by this method.
  359.      *
  360.      * @param className (fully qualified) name of exception
  361.      */
  362.     public void addException(final String className) {
  363.         throwsList.add(className);
  364.     }

  365.     /**
  366.      * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling
  367.      * is done.
  368.      *
  369.      * @param startPc Start of region (inclusive)
  370.      * @param endPc End of region (inclusive)
  371.      * @param handlerPc Where handling is done
  372.      * @param catchType class type of handled exception or null if any exception is handled
  373.      * @return new exception handler object
  374.      */
  375.     public CodeExceptionGen addExceptionHandler(final InstructionHandle startPc, final InstructionHandle endPc, final InstructionHandle handlerPc,
  376.         final ObjectType catchType) {
  377.         if (startPc == null || endPc == null || handlerPc == null) {
  378.             throw new ClassGenException("Exception handler target is null instruction");
  379.         }
  380.         final CodeExceptionGen c = new CodeExceptionGen(startPc, endPc, handlerPc, catchType);
  381.         exceptionList.add(c);
  382.         return c;
  383.     }

  384.     /**
  385.      * Give an instruction a line number corresponding to the source code line.
  386.      *
  387.      * @param ih instruction to tag
  388.      * @return new line number object
  389.      * @see LineNumber
  390.      */
  391.     public LineNumberGen addLineNumber(final InstructionHandle ih, final int srcLine) {
  392.         final LineNumberGen l = new LineNumberGen(ih, srcLine);
  393.         lineNumberList.add(l);
  394.         return l;
  395.     }

  396.     /**
  397.      * Adds a local variable to this method and assigns an index automatically.
  398.      *
  399.      * @param name variable name
  400.      * @param type variable type
  401.      * @param start from where the variable is valid, if this is null, it is valid from the start
  402.      * @param end until where the variable is valid, if this is null, it is valid to the end
  403.      * @return new local variable object
  404.      * @see LocalVariable
  405.      */
  406.     public LocalVariableGen addLocalVariable(final String name, final Type type, final InstructionHandle start, final InstructionHandle end) {
  407.         return addLocalVariable(name, type, maxLocals, start, end);
  408.     }

  409.     /**
  410.      * Adds a local variable to this method.
  411.      *
  412.      * @param name variable name
  413.      * @param type variable type
  414.      * @param slot the index of the local variable, if type is long or double, the next available index is slot+2
  415.      * @param start from where the variable is valid
  416.      * @param end until where the variable is valid
  417.      * @return new local variable object
  418.      * @see LocalVariable
  419.      */
  420.     public LocalVariableGen addLocalVariable(final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end) {
  421.         return addLocalVariable(name, type, slot, start, end, slot);
  422.     }

  423.     /**
  424.      * Adds a local variable to this method.
  425.      *
  426.      * @param name variable name
  427.      * @param type variable type
  428.      * @param slot the index of the local variable, if type is long or double, the next available index is slot+2
  429.      * @param start from where the variable is valid
  430.      * @param end until where the variable is valid
  431.      * @param origIndex the index of the local variable prior to any modifications
  432.      * @return new local variable object
  433.      * @see LocalVariable
  434.      */
  435.     public LocalVariableGen addLocalVariable(final String name, final Type type, final int slot, final InstructionHandle start, final InstructionHandle end,
  436.         final int origIndex) {
  437.         final byte t = type.getType();
  438.         if (t != Const.T_ADDRESS) {
  439.             final int add = type.getSize();
  440.             if (slot + add > maxLocals) {
  441.                 maxLocals = slot + add;
  442.             }
  443.             final LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end, origIndex);
  444.             int i;
  445.             if ((i = variableList.indexOf(l)) >= 0) {
  446.                 variableList.set(i, l);
  447.             } else {
  448.                 variableList.add(l);
  449.             }
  450.             return l;
  451.         }
  452.         throw new IllegalArgumentException("Can not use " + type + " as type for local variable");
  453.     }

  454.     /**
  455.      * Add observer for this object.
  456.      */
  457.     public void addObserver(final MethodObserver o) {
  458.         if (observers == null) {
  459.             observers = new ArrayList<>();
  460.         }
  461.         observers.add(o);
  462.     }

  463.     public void addParameterAnnotation(final int parameterIndex, final AnnotationEntryGen annotation) {
  464.         ensureExistingParameterAnnotationsUnpacked();
  465.         if (!hasParameterAnnotations) {
  466.             @SuppressWarnings("unchecked") // OK
  467.             final List<AnnotationEntryGen>[] parmList = new List[argTypes.length];
  468.             paramAnnotations = parmList;
  469.             hasParameterAnnotations = true;
  470.         }
  471.         final List<AnnotationEntryGen> existingAnnotations = paramAnnotations[parameterIndex];
  472.         if (existingAnnotations != null) {
  473.             existingAnnotations.add(annotation);
  474.         } else {
  475.             final List<AnnotationEntryGen> l = new ArrayList<>();
  476.             l.add(annotation);
  477.             paramAnnotations[parameterIndex] = l;
  478.         }
  479.     }

  480.     /**
  481.      * @since 6.0
  482.      */
  483.     public void addParameterAnnotationsAsAttribute(final ConstantPoolGen cp) {
  484.         if (!hasParameterAnnotations) {
  485.             return;
  486.         }
  487.         final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp, paramAnnotations);
  488.         if (attrs != null) {
  489.             addAll(attrs);
  490.         }
  491.     }

  492.     private Attribute[] addRuntimeAnnotationsAsAttribute(final ConstantPoolGen cp) {
  493.         final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries());
  494.         addAll(attrs);
  495.         return attrs;
  496.     }

  497.     private Attribute[] addRuntimeParameterAnnotationsAsAttribute(final ConstantPoolGen cp) {
  498.         if (!hasParameterAnnotations) {
  499.             return Attribute.EMPTY_ARRAY;
  500.         }
  501.         final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp, paramAnnotations);
  502.         addAll(attrs);
  503.         return attrs;
  504.     }

  505.     private void adjustLocalVariableTypeTable(final LocalVariableTable lvt) {
  506.         final LocalVariable[] lv = lvt.getLocalVariableTable();
  507.         for (final LocalVariable element : localVariableTypeTable.getLocalVariableTypeTable()) {
  508.             for (final LocalVariable l : lv) {
  509.                 if (element.getName().equals(l.getName()) && element.getIndex() == l.getOrigIndex()) {
  510.                     element.setLength(l.getLength());
  511.                     element.setStartPC(l.getStartPC());
  512.                     element.setIndex(l.getIndex());
  513.                     break;
  514.                 }
  515.             }
  516.         }
  517.     }

  518.     /**
  519.      * @return deep copy of this method
  520.      */
  521.     public MethodGen copy(final String className, final ConstantPoolGen cp) {
  522.         final Method m = ((MethodGen) clone()).getMethod();
  523.         final MethodGen mg = new MethodGen(m, className, super.getConstantPool());
  524.         if (super.getConstantPool() != cp) {
  525.             mg.setConstantPool(cp);
  526.             mg.getInstructionList().replaceConstantPool(super.getConstantPool(), cp);
  527.         }
  528.         return mg;
  529.     }

  530.     /**
  531.      * Goes through the attributes on the method and identifies any that are RuntimeParameterAnnotations, extracting their
  532.      * contents and storing them as parameter annotations. There are two kinds of parameter annotation - visible and
  533.      * invisible. Once they have been unpacked, these attributes are deleted. (The annotations will be rebuilt as attributes
  534.      * when someone builds a Method object out of this MethodGen object).
  535.      */
  536.     private void ensureExistingParameterAnnotationsUnpacked() {
  537.         if (haveUnpackedParameterAnnotations) {
  538.             return;
  539.         }
  540.         // Find attributes that contain parameter annotation data
  541.         final Attribute[] attrs = getAttributes();
  542.         ParameterAnnotations paramAnnVisAttr = null;
  543.         ParameterAnnotations paramAnnInvisAttr = null;
  544.         for (final Attribute attribute : attrs) {
  545.             if (attribute instanceof ParameterAnnotations) {
  546.                 // Initialize paramAnnotations
  547.                 if (!hasParameterAnnotations) {
  548.                     @SuppressWarnings("unchecked") // OK
  549.                     final List<AnnotationEntryGen>[] parmList = new List[argTypes.length];
  550.                     paramAnnotations = parmList;
  551.                     Arrays.setAll(paramAnnotations, i -> new ArrayList<>());
  552.                 }
  553.                 hasParameterAnnotations = true;
  554.                 final ParameterAnnotations rpa = (ParameterAnnotations) attribute;
  555.                 if (rpa instanceof RuntimeVisibleParameterAnnotations) {
  556.                     paramAnnVisAttr = rpa;
  557.                 } else {
  558.                     paramAnnInvisAttr = rpa;
  559.                 }
  560.                 final ParameterAnnotationEntry[] parameterAnnotationEntries = rpa.getParameterAnnotationEntries();
  561.                 for (int j = 0; j < parameterAnnotationEntries.length; j++) {
  562.                     // This returns Annotation[] ...
  563.                     final ParameterAnnotationEntry immutableArray = rpa.getParameterAnnotationEntries()[j];
  564.                     // ... which needs transforming into an AnnotationGen[] ...
  565.                     final List<AnnotationEntryGen> mutable = makeMutableVersion(immutableArray.getAnnotationEntries());
  566.                     // ... then add these to any we already know about
  567.                     paramAnnotations[j].addAll(mutable);
  568.                 }
  569.             }
  570.         }
  571.         if (paramAnnVisAttr != null) {
  572.             removeAttribute(paramAnnVisAttr);
  573.         }
  574.         if (paramAnnInvisAttr != null) {
  575.             removeAttribute(paramAnnInvisAttr);
  576.         }
  577.         haveUnpackedParameterAnnotations = true;
  578.     }

  579.     /**
  580.      * Return value as defined by given BCELComparator strategy. By default two MethodGen objects are said to be equal when
  581.      * their names and signatures are equal.
  582.      *
  583.      * @see Object#equals(Object)
  584.      */
  585.     @Override
  586.     public boolean equals(final Object obj) {
  587.         return obj instanceof FieldGenOrMethodGen && bcelComparator.equals(this, (FieldGenOrMethodGen) obj);
  588.     }

  589.     // J5TODO: Should paramAnnotations be an array of arrays? Rather than an array of lists, this
  590.     // is more likely to suggest to the caller it is readonly (which a List does not).
  591.     /**
  592.      * Return a list of AnnotationGen objects representing parameter annotations
  593.      *
  594.      * @since 6.0
  595.      */
  596.     public List<AnnotationEntryGen> getAnnotationsOnParameter(final int i) {
  597.         ensureExistingParameterAnnotationsUnpacked();
  598.         if (!hasParameterAnnotations || i > argTypes.length) {
  599.             return null;
  600.         }
  601.         return paramAnnotations[i];
  602.     }

  603.     public String getArgumentName(final int i) {
  604.         return argNames[i];
  605.     }

  606.     public String[] getArgumentNames() {
  607.         return argNames.clone();
  608.     }

  609.     public Type getArgumentType(final int i) {
  610.         return argTypes[i];
  611.     }

  612.     public Type[] getArgumentTypes() {
  613.         return argTypes.clone();
  614.     }

  615.     /**
  616.      * @return class that contains this method
  617.      */
  618.     public String getClassName() {
  619.         return className;
  620.     }

  621.     /**
  622.      * @return all attributes of this method.
  623.      */
  624.     public Attribute[] getCodeAttributes() {
  625.         return codeAttrsList.toArray(Attribute.EMPTY_ARRAY);
  626.     }

  627.     /**
  628.      * @return code exceptions for 'Code' attribute
  629.      */
  630.     private CodeException[] getCodeExceptions() {
  631.         final int size = exceptionList.size();
  632.         final CodeException[] cExc = new CodeException[size];
  633.         Arrays.setAll(cExc, i -> exceptionList.get(i).getCodeException(super.getConstantPool()));
  634.         return cExc;
  635.     }

  636.     /*
  637.      * @return array of declared exception handlers
  638.      */
  639.     public CodeExceptionGen[] getExceptionHandlers() {
  640.         return exceptionList.toArray(CodeExceptionGen.EMPTY_ARRAY);
  641.     }

  642.     /*
  643.      * @return array of thrown exceptions
  644.      */
  645.     public String[] getExceptions() {
  646.         return throwsList.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
  647.     }

  648.     /**
  649.      * @return 'Exceptions' attribute of all the exceptions thrown by this method.
  650.      */
  651.     private ExceptionTable getExceptionTable(final ConstantPoolGen cp) {
  652.         final int size = throwsList.size();
  653.         final int[] ex = new int[size];
  654.         Arrays.setAll(ex, i -> cp.addClass(throwsList.get(i)));
  655.         return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool());
  656.     }

  657.     public InstructionList getInstructionList() {
  658.         return il;
  659.     }

  660.     /*
  661.      * @return array of line numbers
  662.      */
  663.     public LineNumberGen[] getLineNumbers() {
  664.         return lineNumberList.toArray(LineNumberGen.EMPTY_ARRAY);
  665.     }

  666.     /**
  667.      * @return 'LineNumberTable' attribute of all the local variables of this method.
  668.      */
  669.     public LineNumberTable getLineNumberTable(final ConstantPoolGen cp) {
  670.         final int size = lineNumberList.size();
  671.         final LineNumber[] ln = new LineNumber[size];
  672.         Arrays.setAll(ln, i -> lineNumberList.get(i).getLineNumber());
  673.         return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp.getConstantPool());
  674.     }

  675.     /*
  676.      * 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
  677.      * instruction list.
  678.      *
  679.      * @return array of declared local variables sorted by index
  680.      */
  681.     public LocalVariableGen[] getLocalVariables() {
  682.         final int size = variableList.size();
  683.         final LocalVariableGen[] lg = new LocalVariableGen[size];
  684.         variableList.toArray(lg);
  685.         for (int i = 0; i < size; i++) {
  686.             if (lg[i].getStart() == null && il != null) {
  687.                 lg[i].setStart(il.getStart());
  688.             }
  689.             if (lg[i].getEnd() == null && il != null) {
  690.                 lg[i].setEnd(il.getEnd());
  691.             }
  692.         }
  693.         if (size > 1) {
  694.             Arrays.sort(lg, Comparator.comparingInt(LocalVariableGen::getIndex));
  695.         }
  696.         return lg;
  697.     }

  698.     /**
  699.      * @return 'LocalVariableTable' attribute of all the local variables of this method.
  700.      */
  701.     public LocalVariableTable getLocalVariableTable(final ConstantPoolGen cp) {
  702.         final LocalVariableGen[] lg = getLocalVariables();
  703.         final int size = lg.length;
  704.         final LocalVariable[] lv = new LocalVariable[size];
  705.         Arrays.setAll(lv, i -> lg[i].getLocalVariable(cp));
  706.         return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp.getConstantPool());
  707.     }

  708.     /**
  709.      * @return 'LocalVariableTypeTable' attribute of this method.
  710.      */
  711.     public LocalVariableTypeTable getLocalVariableTypeTable() {
  712.         return localVariableTypeTable;
  713.     }

  714.     public int getMaxLocals() {
  715.         return maxLocals;
  716.     }

  717.     public int getMaxStack() {
  718.         return maxStack;
  719.     }

  720.     /**
  721.      * Gets method object. Never forget to call setMaxStack() or setMaxStack(max), respectively, before calling this method
  722.      * (the same applies for max locals).
  723.      *
  724.      * @return method object
  725.      */
  726.     public Method getMethod() {
  727.         final String signature = getSignature();
  728.         final ConstantPoolGen cp = super.getConstantPool();
  729.         final int nameIndex = cp.addUtf8(super.getName());
  730.         final int signatureIndex = cp.addUtf8(signature);
  731.         /*
  732.          * Also updates positions of instructions, i.e., their indices
  733.          */
  734.         final byte[] byteCode = il != null ? il.getByteCode() : null;
  735.         LineNumberTable lnt = null;
  736.         LocalVariableTable lvt = null;
  737.         /*
  738.          * Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
  739.          */
  740.         if (!variableList.isEmpty() && !stripAttributes) {
  741.             updateLocalVariableTable(getLocalVariableTable(cp));
  742.             addCodeAttribute(lvt = getLocalVariableTable(cp));
  743.         }
  744.         if (localVariableTypeTable != null) {
  745.             // LocalVariable length in LocalVariableTypeTable is not updated automatically. It's a difference with
  746.             // LocalVariableTable.
  747.             if (lvt != null) {
  748.                 adjustLocalVariableTypeTable(lvt);
  749.             }
  750.             addCodeAttribute(localVariableTypeTable);
  751.         }
  752.         if (!lineNumberList.isEmpty() && !stripAttributes) {
  753.             addCodeAttribute(lnt = getLineNumberTable(cp));
  754.         }
  755.         final Attribute[] codeAttrs = getCodeAttributes();
  756.         /*
  757.          * Each attribute causes 6 additional header bytes
  758.          */
  759.         int attrsLen = 0;
  760.         for (final Attribute codeAttr : codeAttrs) {
  761.             attrsLen += codeAttr.getLength() + 6;
  762.         }
  763.         final CodeException[] cExc = getCodeExceptions();
  764.         final int excLen = cExc.length * 8; // Every entry takes 8 bytes
  765.         Code code = null;
  766.         if (byteCode != null && !isAbstract() && !isNative()) {
  767.             // Remove any stale code attribute
  768.             final Attribute[] attributes = getAttributes();
  769.             for (final Attribute a : attributes) {
  770.                 if (a instanceof Code) {
  771.                     removeAttribute(a);
  772.                 }
  773.             }
  774.             code = new Code(cp.addUtf8("Code"), 8 + byteCode.length + // prologue byte code
  775.                 2 + excLen + // exceptions
  776.                 2 + attrsLen, // attributes
  777.                 maxStack, maxLocals, byteCode, cExc, codeAttrs, cp.getConstantPool());
  778.             addAttribute(code);
  779.         }
  780.         final Attribute[] annotations = addRuntimeAnnotationsAsAttribute(cp);
  781.         final Attribute[] parameterAnnotations = addRuntimeParameterAnnotationsAsAttribute(cp);
  782.         ExceptionTable et = null;
  783.         if (!throwsList.isEmpty()) {
  784.             addAttribute(et = getExceptionTable(cp));
  785.             // Add 'Exceptions' if there are "throws" clauses
  786.         }
  787.         final Method m = new Method(super.getAccessFlags(), nameIndex, signatureIndex, getAttributes(), cp.getConstantPool());
  788.         // Undo effects of adding attributes
  789.         if (lvt != null) {
  790.             removeCodeAttribute(lvt);
  791.         }
  792.         if (localVariableTypeTable != null) {
  793.             removeCodeAttribute(localVariableTypeTable);
  794.         }
  795.         if (lnt != null) {
  796.             removeCodeAttribute(lnt);
  797.         }
  798.         if (code != null) {
  799.             removeAttribute(code);
  800.         }
  801.         if (et != null) {
  802.             removeAttribute(et);
  803.         }
  804.         removeRuntimeAttributes(annotations);
  805.         removeRuntimeAttributes(parameterAnnotations);
  806.         return m;
  807.     }

  808.     public Type getReturnType() {
  809.         return getType();
  810.     }

  811.     @Override
  812.     public String getSignature() {
  813.         return Type.getMethodSignature(super.getType(), argTypes);
  814.     }

  815.     /**
  816.      * Return value as defined by given BCELComparator strategy. By default return the hash code of the method's name XOR
  817.      * signature.
  818.      *
  819.      * @see Object#hashCode()
  820.      */
  821.     @Override
  822.     public int hashCode() {
  823.         return bcelComparator.hashCode(this);
  824.     }

  825.     private List<AnnotationEntryGen> makeMutableVersion(final AnnotationEntry[] mutableArray) {
  826.         return Streams.of(mutableArray).map(ae -> new AnnotationEntryGen(ae, getConstantPool(), false)).collect(Collectors.toList());
  827.     }

  828.     /**
  829.      * Remove a code attribute.
  830.      */
  831.     public void removeCodeAttribute(final Attribute a) {
  832.         codeAttrsList.remove(a);
  833.     }

  834.     /**
  835.      * Remove all code attributes.
  836.      */
  837.     public void removeCodeAttributes() {
  838.         localVariableTypeTable = null;
  839.         codeAttrsList.clear();
  840.     }

  841.     /**
  842.      * Remove an exception.
  843.      */
  844.     public void removeException(final String c) {
  845.         throwsList.remove(c);
  846.     }

  847.     /**
  848.      * Remove an exception handler.
  849.      */
  850.     public void removeExceptionHandler(final CodeExceptionGen c) {
  851.         exceptionList.remove(c);
  852.     }

  853.     /**
  854.      * Remove all line numbers.
  855.      */
  856.     public void removeExceptionHandlers() {
  857.         exceptionList.clear();
  858.     }

  859.     /**
  860.      * Remove all exceptions.
  861.      */
  862.     public void removeExceptions() {
  863.         throwsList.clear();
  864.     }

  865.     /**
  866.      * Remove a line number.
  867.      */
  868.     public void removeLineNumber(final LineNumberGen l) {
  869.         lineNumberList.remove(l);
  870.     }

  871.     /**
  872.      * Remove all line numbers.
  873.      */
  874.     public void removeLineNumbers() {
  875.         lineNumberList.clear();
  876.     }

  877.     /**
  878.      * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable with an explicit index
  879.      * argument.
  880.      */
  881.     public void removeLocalVariable(final LocalVariableGen l) {
  882.         l.dispose();
  883.         variableList.remove(l);
  884.     }

  885.     /**
  886.      * Remove all local variables.
  887.      */
  888.     public void removeLocalVariables() {
  889.         variableList.forEach(LocalVariableGen::dispose);
  890.         variableList.clear();
  891.     }

  892.     /**
  893.      * Remove the LocalVariableTypeTable
  894.      */
  895.     public void removeLocalVariableTypeTable() {
  896.         localVariableTypeTable = null;
  897.     }

  898.     /**
  899.      * Remove all NOPs from the instruction list (if possible) and update every object referring to them, i.e., branch
  900.      * instructions, local variables and exception handlers.
  901.      */
  902.     public void removeNOPs() {
  903.         if (il != null) {
  904.             InstructionHandle next;
  905.             /*
  906.              * Check branch instructions.
  907.              */
  908.             for (InstructionHandle ih = il.getStart(); ih != null; ih = next) {
  909.                 next = ih.getNext();
  910.                 if (next != null && ih.getInstruction() instanceof NOP) {
  911.                     try {
  912.                         il.delete(ih);
  913.                     } catch (final TargetLostException e) {
  914.                         for (final InstructionHandle target : e.getTargets()) {
  915.                             for (final InstructionTargeter targeter : target.getTargeters()) {
  916.                                 targeter.updateTarget(target, next);
  917.                             }
  918.                         }
  919.                     }
  920.                 }
  921.             }
  922.         }
  923.     }

  924.     /**
  925.      * Remove observer for this object.
  926.      */
  927.     public void removeObserver(final MethodObserver o) {
  928.         if (observers != null) {
  929.             observers.remove(o);
  930.         }
  931.     }

  932.     /**
  933.      * Would prefer to make this private, but need a way to test if client is using BCEL version 6.5.0 or later that
  934.      * contains fix for BCEL-329.
  935.      *
  936.      * @since 6.5.0
  937.      */
  938.     public void removeRuntimeAttributes(final Attribute[] attributes) {
  939.         Streams.of(attributes).forEach(this::removeAttribute);
  940.     }

  941.     public void setArgumentName(final int i, final String name) {
  942.         argNames[i] = name;
  943.     }

  944.     public void setArgumentNames(final String[] argNames) {
  945.         this.argNames = ArrayUtils.nullToEmpty(argNames);
  946.     }

  947.     public void setArgumentType(final int i, final Type type) {
  948.         argTypes[i] = type;
  949.     }

  950.     public void setArgumentTypes(final Type[] argTypes) {
  951.         this.argTypes = argTypes != null ? argTypes : Type.NO_ARGS;
  952.     }

  953.     public void setClassName(final String className) { // TODO could be package-protected?
  954.         this.className = className;
  955.     }

  956.     public void setInstructionList(final InstructionList il) { // TODO could be package-protected?
  957.         this.il = il;
  958.     }

  959.     /**
  960.      * Compute maximum number of local variables.
  961.      */
  962.     public void setMaxLocals() { // TODO could be package-protected? (some tests would need repackaging)
  963.         if (il != null) {
  964.             int max = isStatic() ? 0 : 1;
  965.             for (final Type argType : argTypes) {
  966.                 max += argType.getSize();
  967.             }
  968.             for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
  969.                 final Instruction ins = ih.getInstruction();
  970.                 if (ins instanceof LocalVariableInstruction || ins instanceof RET || ins instanceof IINC) {
  971.                     final int index = ((IndexedInstruction) ins).getIndex() + ((TypedInstruction) ins).getType(super.getConstantPool()).getSize();
  972.                     if (index > max) {
  973.                         max = index;
  974.                     }
  975.                 }
  976.             }
  977.             maxLocals = max;
  978.         } else {
  979.             maxLocals = 0;
  980.         }
  981.     }

  982.     /**
  983.      * Sets maximum number of local variables.
  984.      */
  985.     public void setMaxLocals(final int m) {
  986.         maxLocals = m;
  987.     }

  988.     /**
  989.      * Computes max. stack size by performing control flow analysis.
  990.      */
  991.     public void setMaxStack() { // TODO could be package-protected? (some tests would need repackaging)
  992.         if (il != null) {
  993.             maxStack = getMaxStack(super.getConstantPool(), il, getExceptionHandlers());
  994.         } else {
  995.             maxStack = 0;
  996.         }
  997.     }

  998.     /**
  999.      * Sets maximum stack size for this method.
  1000.      */
  1001.     public void setMaxStack(final int m) { // TODO could be package-protected?
  1002.         maxStack = m;
  1003.     }

  1004.     public void setReturnType(final Type returnType) {
  1005.         setType(returnType);
  1006.     }

  1007.     /**
  1008.      * Do not/Do produce attributes code attributesLineNumberTable and LocalVariableTable, like javac -O
  1009.      */
  1010.     public void stripAttributes(final boolean flag) {
  1011.         stripAttributes = flag;
  1012.     }

  1013.     /**
  1014.      * Return string representation close to declaration format, 'public static void main(String[]) throws IOException',
  1015.      * e.g.
  1016.      *
  1017.      * @return String representation of the method.
  1018.      */
  1019.     @Override
  1020.     public final String toString() {
  1021.         final String access = Utility.accessToString(super.getAccessFlags());
  1022.         String signature = Type.getMethodSignature(super.getType(), argTypes);
  1023.         signature = Utility.methodSignatureToString(signature, super.getName(), access, true, getLocalVariableTable(super.getConstantPool()));
  1024.         final StringBuilder buf = new StringBuilder(signature);
  1025.         for (final Attribute a : getAttributes()) {
  1026.             if (!(a instanceof Code || a instanceof ExceptionTable)) {
  1027.                 buf.append(" [").append(a).append("]");
  1028.             }
  1029.         }

  1030.         if (!throwsList.isEmpty()) {
  1031.             for (final String throwsDescriptor : throwsList) {
  1032.                 buf.append("\n\t\tthrows ").append(throwsDescriptor);
  1033.             }
  1034.         }
  1035.         return buf.toString();
  1036.     }

  1037.     /**
  1038.      * Call notify() method on all observers. This method is not called automatically whenever the state has changed, but
  1039.      * has to be called by the user after they have finished editing the object.
  1040.      */
  1041.     public void update() {
  1042.         if (observers != null) {
  1043.             for (final MethodObserver observer : observers) {
  1044.                 observer.notify(this);
  1045.             }
  1046.         }
  1047.     }

  1048.     private void updateLocalVariableTable(final LocalVariableTable a) {
  1049.         removeLocalVariables();
  1050.         for (final LocalVariable l : a.getLocalVariableTable()) {
  1051.             InstructionHandle start = il.findHandle(l.getStartPC());
  1052.             final InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
  1053.             // Repair malformed handles
  1054.             if (null == start) {
  1055.                 start = il.getStart();
  1056.             }
  1057.             // end == null => live to end of method
  1058.             // Since we are recreating the LocalVaraible, we must
  1059.             // propagate the orig_index to new copy.
  1060.             addLocalVariable(l.getName(), Type.getType(l.getSignature()), l.getIndex(), start, end, l.getOrigIndex());
  1061.         }
  1062.     }
  1063. }