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