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