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