View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   package com.sun.org.apache.bcel.internal.generic;
6   
7   /* ====================================================================
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 2001 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution,
26   *    if any, must include the following acknowledgment:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowledgment may appear in the software itself,
30   *    if and wherever such third-party acknowledgments normally appear.
31   *
32   * 4. The names "Apache" and "Apache Software Foundation" and
33   *    "Apache BCEL" must not be used to endorse or promote products
34   *    derived from this software without prior written permission. For
35   *    written permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache",
38   *    "Apache BCEL", nor may "Apache" appear in their name, without
39   *    prior written permission of the Apache Software Foundation.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   */
60  
61  import com.sun.org.apache.bcel.internal.Constants;
62  import com.sun.org.apache.bcel.internal.classfile.*;
63  import java.util.*;
64  
65  /**
66   * Template class for building up a method. This is done by defining exception
67   * handlers, adding thrown exceptions, local variables and attributes, whereas
68   * the `LocalVariableTable' and `LineNumberTable' attributes will be set
69   * automatically for the code. Use stripAttributes() if you don't like this.
70   *
71   * While generating code it may be necessary to insert NOP operations. You can
72   * use the `removeNOPs' method to get rid off them.
73   * The resulting method object can be obtained via the `getMethod()' method.
74   *
75   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
76   * @author  <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
77   * @see     InstructionList
78   * @see     Method
79   */
80  public class MethodGen extends FieldGenOrMethodGen {
81    private String          class_name;
82    private Type[]          arg_types;
83    private String[]        arg_names;
84    private int             max_locals;
85    private int             max_stack;
86    private InstructionList il;
87    private boolean         strip_attributes;
88  
89    private ArrayList       variable_vec    = new ArrayList();
90    private ArrayList       line_number_vec = new ArrayList();
91    private ArrayList       exception_vec   = new ArrayList();
92    private ArrayList       throws_vec      = new ArrayList();
93    private ArrayList       code_attrs_vec  = new ArrayList();
94  
95    /**
96     * Declare method. If the method is non-static the constructor
97     * automatically declares a local variable `$this' in slot 0. The
98     * actual code is contained in the `il' parameter, which may further
99     * manipulated by the user. But he must take care not to remove any
100    * instruction (handles) that are still referenced from this object.
101    *
102    * For example one may not add a local variable and later remove the
103    * instructions it refers to without causing havoc. It is safe
104    * however if you remove that local variable, too.
105    *
106    * @param access_flags access qualifiers
107    * @param return_type  method type
108    * @param arg_types argument types
109    * @param arg_names argument names (if this is null, default names will be provided
110    * for them)
111    * @param method_name name of method
112    * @param class_name class name containing this method (may be null, if you don't care)
113    * @param il instruction list associated with this method, may be null only for
114    * abstract or native methods
115    * @param cp constant pool
116    */
117   public MethodGen(int access_flags, Type return_type, Type[] arg_types,
118                    String[] arg_names, String method_name, String class_name,
119                    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 
129     boolean abstract_ = isAbstract() || isNative();
130     InstructionHandle start = null;
131     InstructionHandle end   = null;
132 
133     if(!abstract_) {
134       start = il.getStart();
135       end   = il.getEnd();
136 
137       /* Add local variables, namely the implicit `this' and the arguments
138        */
139       if(!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
140         addLocalVariable("this", new ObjectType(class_name), start, end);
141       }
142     }
143 
144     if(arg_types != null) {
145       int size = arg_types.length;
146 
147       for(int i=0; i < size; i++) {
148         if(Type.VOID == arg_types[i]) {
149           throw new ClassGenException("'void' is an illegal argument type for a method");
150         }
151       }
152 
153       if(arg_names != null) { // Names for variables provided?
154         if(size != arg_names.length)
155           throw new ClassGenException("Mismatch in argument array lengths: " +
156                                       size + " vs. " + arg_names.length);
157       } else { // Give them dummy names
158         arg_names = new String[size];
159 
160         for(int i=0; i < size; i++)
161           arg_names[i] = "arg" + i;
162 
163         setArgumentNames(arg_names);
164       }
165 
166       if(!abstract_) {
167         for(int i=0; i < size; i++) {
168           addLocalVariable(arg_names[i], arg_types[i], start, end);
169         }
170       }
171     }
172   }
173 
174   /**
175    * Instantiate from existing method.
176    *
177    * @param m method
178    * @param class_name class name containing this method
179    * @param cp constant pool
180    */
181   public MethodGen(Method m, String class_name, ConstantPoolGen cp) {
182     this(m.getAccessFlags(), Type.getReturnType(m.getSignature()),
183          Type.getArgumentTypes(m.getSignature()), null /* may be overridden anyway */,
184          m.getName(), class_name,
185          ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)?
186          new InstructionList(m.getCode().getCode()) : null,
187          cp);
188 
189     Attribute[] attributes = m.getAttributes();
190     for(int i=0; i < attributes.length; i++) {
191       Attribute a = attributes[i];
192 
193       if(a instanceof Code) {
194         Code c = (Code)a;
195         setMaxStack(c.getMaxStack());
196         setMaxLocals(c.getMaxLocals());
197 
198         CodeException[] ces = c.getExceptionTable();
199 
200         if(ces != null) {
201           for(int j=0; j < ces.length; j++) {
202             CodeException ce     = ces[j];
203             int           type   = ce.getCatchType();
204             ObjectType    c_type = null;
205 
206             if(type > 0) {
207               String cen = m.getConstantPool().getConstantString(type, Constants.CONSTANT_Class);
208               c_type = new ObjectType(cen);
209             }
210 
211             int end_pc = ce.getEndPC();
212             int length = m.getCode().getCode().length;
213 
214             InstructionHandle end;
215 
216             if(length == end_pc) { // May happen, because end_pc is exclusive
217               end = il.getEnd();
218             } else {
219               end = il.findHandle(end_pc);
220               end = end.getPrev(); // Make it inclusive
221             }
222 
223             addExceptionHandler(il.findHandle(ce.getStartPC()), end,
224                                 il.findHandle(ce.getHandlerPC()), c_type);
225           }
226         }
227 
228         Attribute[] c_attributes = c.getAttributes();
229         for(int j=0; j < c_attributes.length; j++) {
230           a = c_attributes[j];
231 
232           if(a instanceof LineNumberTable) {
233             LineNumber[] ln = ((LineNumberTable)a).getLineNumberTable();
234 
235             for(int k=0; k < ln.length; k++) {
236               LineNumber l = ln[k];
237               addLineNumber(il.findHandle(l.getStartPC()), l.getLineNumber());
238             }
239           } else if(a instanceof LocalVariableTable) {
240             LocalVariable[] lv = ((LocalVariableTable)a).getLocalVariableTable();
241 
242             removeLocalVariables();
243 
244             for(int k=0; k < lv.length; k++) {
245               LocalVariable     l     = lv[k];
246               InstructionHandle start = il.findHandle(l.getStartPC());
247               InstructionHandle end   = il.findHandle(l.getStartPC() + l.getLength());
248 
249               // Repair malformed handles
250               if(null == start) {
251                 start = il.getStart();
252               }
253 
254               if(null == end) {
255                 end = il.getEnd();
256               }
257 
258               addLocalVariable(l.getName(), Type.getType(l.getSignature()),
259                                l.getIndex(), start, end);
260             }
261           } else if (a instanceof LocalVariableTypeTable) {
262              LocalVariable[] lv = ((LocalVariableTypeTable) a).getLocalVariableTypeTable();
263              removeLocalVariables();
264              for (int k = 0; k < lv.length; k++) {
265                  LocalVariable l = lv[k];
266                  InstructionHandle start = il.findHandle(l.getStartPC());
267                  InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
268                  // Repair malformed handles
269                  if (null == start) {
270                      start = il.getStart();
271                  }
272                  if (null == end) {
273                      end = il.getEnd();
274                  }
275                  addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
276                          .getIndex(), start, end);
277               }
278           } else
279             addCodeAttribute(a);
280         }
281       } else if(a instanceof ExceptionTable) {
282         String[] names = ((ExceptionTable)a).getExceptionNames();
283         for(int j=0; j < names.length; j++)
284           addException(names[j]);
285       } else
286         addAttribute(a);
287     }
288   }
289 
290   /**
291    * Adds a local variable to this method.
292    *
293    * @param name variable name
294    * @param type variable type
295    * @param slot the index of the local variable, if type is long or double, the next available
296    * index is slot+2
297    * @param start from where the variable is valid
298    * @param end until where the variable is valid
299    * @return new local variable object
300    * @see LocalVariable
301    */
302   public LocalVariableGen addLocalVariable(String name, Type type, int slot,
303                                            InstructionHandle start,
304                                            InstructionHandle end) {
305     byte t = type.getType();
306 
307     if(t != Constants.T_ADDRESS) {
308       int  add = type.getSize();
309 
310       if(slot + add > max_locals)
311         max_locals = slot + add;
312 
313       LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
314       int i;
315 
316       if((i = variable_vec.indexOf(l)) >= 0) // Overwrite if necessary
317         variable_vec.set(i, l);
318       else
319         variable_vec.add(l);
320 
321       return l;
322     } else {
323       throw new IllegalArgumentException("Can not use " + type +
324                                          " as type for local variable");
325 
326     }
327   }
328 
329   /**
330    * Adds a local variable to this method and assigns an index automatically.
331    *
332    * @param name variable name
333    * @param type variable type
334    * @param start from where the variable is valid, if this is null,
335    * it is valid from the start
336    * @param end until where the variable is valid, if this is null,
337    * it is valid to the end
338    * @return new local variable object
339    * @see LocalVariable
340    */
341   public LocalVariableGen addLocalVariable(String name, Type type,
342                                            InstructionHandle start,
343                                            InstructionHandle end) {
344     return addLocalVariable(name, type, max_locals, start, end);
345   }
346 
347   /**
348    * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
349    * with an explicit index argument.
350    */
351   public void removeLocalVariable(LocalVariableGen l) {
352     variable_vec.remove(l);
353   }
354 
355   /**
356    * Remove all local variables.
357    */
358   public void removeLocalVariables() {
359     variable_vec.clear();
360   }
361 
362   /**
363    * Sort local variables by index
364    */
365   private static final void sort(LocalVariableGen[] vars, int l, int r) {
366     int i = l, j = r;
367     int m = vars[(l + r) / 2].getIndex();
368     LocalVariableGen h;
369 
370     do {
371       while(vars[i].getIndex() < m) i++;
372       while(m < vars[j].getIndex()) j--;
373 
374       if(i <= j) {
375         h=vars[i]; vars[i]=vars[j]; vars[j]=h; // Swap elements
376         i++; j--;
377       }
378     } while(i <= j);
379 
380     if(l < j) sort(vars, l, j);
381     if(i < r) sort(vars, i, r);
382   }
383 
384   /*
385    * If the range of the variable has not been set yet, it will be set to be valid from
386    * the start to the end of the instruction list.
387    *
388    * @return array of declared local variables sorted by index
389    */
390   public LocalVariableGen[] getLocalVariables() {
391     int                size = variable_vec.size();
392     LocalVariableGen[] lg   = new LocalVariableGen[size];
393     variable_vec.toArray(lg);
394 
395     for(int i=0; i < size; i++) {
396       if(lg[i].getStart() == null)
397         lg[i].setStart(il.getStart());
398 
399       if(lg[i].getEnd() == null)
400         lg[i].setEnd(il.getEnd());
401     }
402 
403     if(size > 1)
404       sort(lg, 0, size - 1);
405 
406     return lg;
407   }
408 
409   /**
410    * @return `LocalVariableTable' attribute of all the local variables of this method.
411    */
412   public LocalVariableTable getLocalVariableTable(ConstantPoolGen cp) {
413     LocalVariableGen[] lg   = getLocalVariables();
414     int                size = lg.length;
415     LocalVariable[]    lv   = new LocalVariable[size];
416 
417     for(int i=0; i < size; i++)
418       lv[i] = lg[i].getLocalVariable(cp);
419 
420     return new LocalVariableTable(cp.addUtf8("LocalVariableTable"),
421                                   2 + lv.length * 10, lv, cp.getConstantPool());
422   }
423 
424   /**
425    * Give an instruction a line number corresponding to the source code line.
426    *
427    * @param ih instruction to tag
428    * @return new line number object
429    * @see LineNumber
430    */
431   public LineNumberGen addLineNumber(InstructionHandle ih, int src_line) {
432     LineNumberGen l = new LineNumberGen(ih, src_line);
433     line_number_vec.add(l);
434     return l;
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    * Remove all line numbers.
446    */
447   public void removeLineNumbers() {
448     line_number_vec.clear();
449   }
450 
451   /*
452    * @return array of line numbers
453    */
454   public LineNumberGen[] getLineNumbers() {
455     LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
456     line_number_vec.toArray(lg);
457     return lg;
458   }
459 
460   /**
461    * @return `LineNumberTable' attribute of all the local variables of this method.
462    */
463   public LineNumberTable getLineNumberTable(ConstantPoolGen cp) {
464     int          size = line_number_vec.size();
465     LineNumber[] ln   = new LineNumber[size];
466 
467     try {
468       for(int i=0; i < size; i++)
469         ln[i] = ((LineNumberGen)line_number_vec.get(i)).getLineNumber();
470     } catch(ArrayIndexOutOfBoundsException e) {} // Never occurs
471 
472     return new LineNumberTable(cp.addUtf8("LineNumberTable"),
473                                2 + ln.length * 4, ln, cp.getConstantPool());
474   }
475 
476   /**
477    * Add an exception handler, i.e., specify region where a handler is active and an
478    * instruction where the actual handling is done.
479    *
480    * @param start_pc Start of region (inclusive)
481    * @param end_pc End of region (inclusive)
482    * @param handler_pc Where handling is done
483    * @param catch_type class type of handled exception or null if any
484    * exception is handled
485    * @return new exception handler object
486    */
487   public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc,
488                                               InstructionHandle end_pc,
489                                               InstructionHandle handler_pc,
490                                               ObjectType catch_type) {
491     if((start_pc == null) || (end_pc == null) || (handler_pc == null))
492       throw new ClassGenException("Exception handler target is null instruction");
493 
494     CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc,
495                                               handler_pc, catch_type);
496     exception_vec.add(c);
497     return c;
498   }
499 
500   /**
501    * Remove an exception handler.
502    */
503   public void removeExceptionHandler(CodeExceptionGen c) {
504     exception_vec.remove(c);
505   }
506 
507   /**
508    * Remove all line numbers.
509    */
510   public void removeExceptionHandlers() {
511     exception_vec.clear();
512   }
513 
514   /*
515    * @return array of declared exception handlers
516    */
517   public CodeExceptionGen[] getExceptionHandlers() {
518     CodeExceptionGen[] cg   = new CodeExceptionGen[exception_vec.size()];
519     exception_vec.toArray(cg);
520     return cg;
521   }
522 
523   /**
524    * @return code exceptions for `Code' attribute
525    */
526   private CodeException[] getCodeExceptions() {
527     int             size  = exception_vec.size();
528     CodeException[] c_exc = new CodeException[size];
529 
530     try {
531       for(int i=0; i < size; i++) {
532         CodeExceptionGen c = (CodeExceptionGen)exception_vec.get(i);
533         c_exc[i] = c.getCodeException(cp);
534       }
535     } catch(ArrayIndexOutOfBoundsException e) {}
536 
537     return c_exc;
538   }
539 
540   /**
541    * Add an exception possibly thrown by this method.
542    *
543    * @param class_name (fully qualified) name of exception
544    */
545   public void addException(String class_name) {
546     throws_vec.add(class_name);
547   }
548 
549   /**
550    * Remove an exception.
551    */
552   public void removeException(String c) {
553     throws_vec.remove(c);
554   }
555 
556   /**
557    * Remove all exceptions.
558    */
559   public void removeExceptions() {
560     throws_vec.clear();
561   }
562 
563   /*
564    * @return array of thrown exceptions
565    */
566   public String[] getExceptions() {
567     String[] e = new String[throws_vec.size()];
568     throws_vec.toArray(e);
569     return e;
570   }
571 
572   /**
573    * @return `Exceptions' attribute of all the exceptions thrown by this method.
574    */
575   private ExceptionTable getExceptionTable(ConstantPoolGen cp) {
576     int   size = throws_vec.size();
577     int[] ex   = new int[size];
578 
579     try {
580       for(int i=0; i < size; i++)
581         ex[i] = cp.addClass((String)throws_vec.get(i));
582     } catch(ArrayIndexOutOfBoundsException e) {}
583 
584     return new ExceptionTable(cp.addUtf8("Exceptions"),
585                               2 + 2 * size, ex, cp.getConstantPool());
586   }
587 
588   /**
589    * Add an attribute to the code. Currently, the JVM knows about the
590    * LineNumberTable, LocalVariableTable and StackMap attributes,
591    * where the former two will be generated automatically and the
592    * latter is used for the MIDP only. Other attributes will be
593    * ignored by the JVM but do no harm.
594    *
595    * @param a attribute to be added
596    */
597   public void addCodeAttribute(Attribute a) { code_attrs_vec.add(a); }
598 
599   /**
600    * Remove a code attribute.
601    */
602   public void removeCodeAttribute(Attribute a) { code_attrs_vec.remove(a); }
603 
604   /**
605    * Remove all code attributes.
606    */
607   public void removeCodeAttributes() {
608     code_attrs_vec.clear();
609   }
610 
611   /**
612    * @return all attributes of this method.
613    */
614   public Attribute[] getCodeAttributes() {
615     Attribute[] attributes = new Attribute[code_attrs_vec.size()];
616     code_attrs_vec.toArray(attributes);
617     return attributes;
618   }
619 
620   /**
621    * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
622    * before calling this method (the same applies for max locals).
623    *
624    * @return method object
625    */
626   public Method getMethod() {
627     String signature       = getSignature();
628     int    name_index      = cp.addUtf8(name);
629     int    signature_index = cp.addUtf8(signature);
630 
631     /* Also updates positions of instructions, i.e., their indices
632      */
633     byte[] byte_code = null;
634 
635     if(il != null)
636       byte_code = il.getByteCode();
637 
638     LineNumberTable    lnt = null;
639     LocalVariableTable lvt = null;
640 
641     /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
642      */
643     if((variable_vec.size() > 0) && !strip_attributes)
644       addCodeAttribute(lvt = getLocalVariableTable(cp));
645 
646     if((line_number_vec.size() > 0) && !strip_attributes)
647       addCodeAttribute(lnt = getLineNumberTable(cp));
648 
649     Attribute[] code_attrs = getCodeAttributes();
650 
651     /* Each attribute causes 6 additional header bytes
652      */
653     int                attrs_len  = 0;
654     for(int i=0; i < code_attrs.length; i++)
655       attrs_len += (code_attrs[i].getLength() + 6);
656 
657     CodeException[] c_exc   = getCodeExceptions();
658     int             exc_len = c_exc.length * 8; // Every entry takes 8 bytes
659 
660     Code code = null;
661 
662     if((il != null) && !isAbstract()) {
663       // Remove any stale code attribute
664       Attribute[] attributes = getAttributes();
665       for(int i=0; i < attributes.length; i++) {
666         Attribute a = attributes[i];
667 
668         if(a instanceof Code)
669           removeAttribute(a);
670       }
671 
672       code = new Code(cp.addUtf8("Code"),
673                       8 + byte_code.length + // prologue byte code
674                       2 + exc_len +          // exceptions
675                       2 + attrs_len,         // attributes
676                       max_stack, max_locals,
677                       byte_code, c_exc,
678                       code_attrs,
679                       cp.getConstantPool());
680 
681       addAttribute(code);
682     }
683 
684     ExceptionTable et = null;
685 
686     if(throws_vec.size() > 0)
687       addAttribute(et = getExceptionTable(cp)); // Add `Exceptions' if there are "throws" clauses
688 
689     Method m = new Method(access_flags, name_index, signature_index,
690                           getAttributes(), cp.getConstantPool());
691 
692     // Undo effects of adding attributes
693     if(lvt != null)  removeCodeAttribute(lvt);
694     if(lnt != null)  removeCodeAttribute(lnt);
695     if(code != null) removeAttribute(code);
696     if(et != null)   removeAttribute(et);
697 
698     return m;
699   }
700 
701   /**
702    * Remove all NOPs from the instruction list (if possible) and update every
703    * object refering to them, i.e., branch instructions, local variables and
704    * exception handlers.
705    */
706   public void removeNOPs() {
707     if(il != null) {
708       InstructionHandle next;
709       /* Check branch instructions.
710        */
711       for(InstructionHandle ih = il.getStart(); ih != null; ih = next) {
712         next = ih.next;
713 
714         if((next != null) && (ih.getInstruction() instanceof NOP)) {
715           try {
716             il.delete(ih);
717           } catch(TargetLostException e) {
718             InstructionHandle[] targets = e.getTargets();
719 
720             for(int i=0; i < targets.length; i++) {
721               InstructionTargeter[] targeters = targets[i].getTargeters();
722 
723               for(int j=0; j < targeters.length; j++)
724                 targeters[j].updateTarget(targets[i], next);
725             }
726           }
727         }
728       }
729     }
730   }
731 
732   /**
733    * Set maximum number of local variables.
734    */
735   public void   setMaxLocals(int m)  { max_locals = m; }
736   public int    getMaxLocals()       { return max_locals; }
737 
738   /**
739    * Set maximum stack size for this method.
740    */
741   public void   setMaxStack(int m)  { max_stack = m; }
742   public int    getMaxStack()       { return max_stack; }
743 
744   /** @return class that contains this method
745    */
746   public String getClassName()                     { return class_name; }
747   public void   setClassName(String class_name)    { this.class_name = class_name; }
748 
749   public void   setReturnType(Type return_type)    { setType(return_type); }
750   public Type   getReturnType()                    { return getType(); }
751 
752   public void   setArgumentTypes(Type[] arg_types)  { this.arg_types = arg_types; }
753   public Type[] getArgumentTypes()                  { return (Type[])arg_types.clone(); }
754   public void   setArgumentType(int i, Type type)       { arg_types[i] = type; }
755   public Type   getArgumentType(int i)                  { return arg_types[i]; }
756 
757   public void     setArgumentNames(String[] arg_names) { this.arg_names = arg_names; }
758   public String[] getArgumentNames()                   { return (String[])arg_names.clone(); }
759   public void     setArgumentName(int i, String name)     { arg_names[i] = name; }
760   public String   getArgumentName(int i)                  { return arg_names[i]; }
761 
762   public InstructionList getInstructionList()                    { return il; }
763   public void            setInstructionList(InstructionList il)  { this.il = il; }
764 
765   public String getSignature() {
766     return Type.getMethodSignature(type, arg_types);
767   }
768 
769   /**
770    * Computes max. stack size by performing control flow analysis.
771    */
772   public void setMaxStack() {
773     if(il != null)
774       max_stack = getMaxStack(cp, il, getExceptionHandlers());
775     else
776       max_stack = 0;
777   }
778 
779   /**
780    * Compute maximum number of local variables.
781    */
782   public void setMaxLocals() {
783     if(il != null) {
784       int max = isStatic()? 0 : 1;
785 
786       if(arg_types != null)
787         for(int i=0; i < arg_types.length; i++)
788           max += arg_types[i].getSize();
789 
790       for(InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
791         Instruction ins = ih.getInstruction();
792 
793         if((ins instanceof LocalVariableInstruction) ||
794            (ins instanceof RET) || (ins instanceof IINC))
795         {
796           int index = ((IndexedInstruction)ins).getIndex() +
797             ((TypedInstruction)ins).getType(cp).getSize();
798 
799           if(index > max)
800             max = index;
801         }
802       }
803 
804       max_locals = max;
805     } else
806       max_locals = 0;
807   }
808 
809   /** Do not/Do produce attributes code attributesLineNumberTable and
810    * LocalVariableTable, like javac -O
811    */
812   public void stripAttributes(boolean flag) { strip_attributes = flag; }
813 
814   static final class BranchTarget {
815     InstructionHandle target;
816     int               stackDepth;
817 
818     BranchTarget(InstructionHandle target, int stackDepth) {
819       this.target = target;
820       this.stackDepth = stackDepth;
821     }
822   }
823 
824   static final class BranchStack {
825     Stack     branchTargets  = new Stack();
826     Hashtable visitedTargets = new Hashtable();
827 
828     public void push(InstructionHandle target, int stackDepth) {
829       if(visited(target))
830         return;
831 
832       branchTargets.push(visit(target, stackDepth));
833     }
834 
835     public BranchTarget pop() {
836       if(!branchTargets.empty()) {
837         BranchTarget bt = (BranchTarget) branchTargets.pop();
838         return bt;
839       }
840 
841       return null;
842     }
843 
844     private final BranchTarget visit(InstructionHandle target, int stackDepth) {
845       BranchTarget bt = new BranchTarget(target, stackDepth);
846       visitedTargets.put(target, bt);
847 
848       return bt;
849     }
850 
851     private final boolean visited(InstructionHandle target) {
852       return (visitedTargets.get(target) != null);
853     }
854   }
855 
856   /**
857    * Computes stack usage of an instruction list by performing control flow analysis.
858    *
859    * @return maximum stack depth used by method
860    */
861   public static int getMaxStack(ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et) {
862     BranchStack branchTargets = new BranchStack();
863 
864     /* Initially, populate the branch stack with the exception
865      * handlers, because these aren't (necessarily) branched to
866      * explicitly. in each case, the stack will have depth 1,
867      * containing the exception object.
868      */
869     for (int i = 0; i < et.length; i++) {
870       InstructionHandle handler_pc = et[i].getHandlerPC();
871       if (handler_pc != null)
872         branchTargets.push(handler_pc, 1);
873     }
874 
875     int               stackDepth = 0, maxStackDepth = 0;
876     InstructionHandle ih         = il.getStart();
877 
878     while(ih != null) {
879       Instruction instruction = ih.getInstruction();
880       short opcode = instruction.getOpcode();
881       int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
882 
883       stackDepth += delta;
884       if(stackDepth > maxStackDepth)
885         maxStackDepth = stackDepth;
886 
887       // choose the next instruction based on whether current is a branch.
888       if(instruction instanceof BranchInstruction) {
889         BranchInstruction branch = (BranchInstruction) instruction;
890         if(instruction instanceof Select) {
891           // explore all of the select's targets. the default target is handled below.
892           Select select = (Select) branch;
893           InstructionHandle[] targets = select.getTargets();
894           for (int i = 0; i < targets.length; i++)
895             branchTargets.push(targets[i], stackDepth);
896           // nothing to fall through to.
897           ih = null;
898         } else if(!(branch instanceof IfInstruction)) {
899           // if an instruction that comes back to following PC,
900           // push next instruction, with stack depth reduced by 1.
901           if(opcode == Constants.JSR || opcode == Constants.JSR_W)
902             branchTargets.push(ih.getNext(), stackDepth - 1);
903           ih = null;
904         }
905         // for all branches, the target of the branch is pushed on the branch stack.
906         // conditional branches have a fall through case, selects don't, and
907         // jsr/jsr_w return to the next instruction.
908         branchTargets.push(branch.getTarget(), stackDepth);
909       } else {
910         // check for instructions that terminate the method.
911         if(opcode == Constants.ATHROW || opcode == Constants.RET ||
912            (opcode >= Constants.IRETURN && opcode <= Constants.RETURN))
913           ih = null;
914       }
915       // normal case, go to the next instruction.
916       if(ih != null)
917         ih = ih.getNext();
918       // if we have no more instructions, see if there are any deferred branches to explore.
919       if(ih == null) {
920         BranchTarget bt = branchTargets.pop();
921         if (bt != null) {
922           ih = bt.target;
923           stackDepth = bt.stackDepth;
924         }
925       }
926     }
927 
928     return maxStackDepth;
929   }
930 
931   private ArrayList observers;
932 
933   /** Add observer for this object.
934    */
935   public void addObserver(MethodObserver o) {
936     if(observers == null)
937       observers = new ArrayList();
938 
939     observers.add(o);
940   }
941 
942   /** Remove observer for this object.
943    */
944   public void removeObserver(MethodObserver o) {
945     if(observers != null)
946       observers.remove(o);
947   }
948 
949   /** Call notify() method on all observers. This method is not called
950    * automatically whenever the state has changed, but has to be
951    * called by the user after he has finished editing the object.
952    */
953   public void update() {
954     if(observers != null)
955       for(Iterator e = observers.iterator(); e.hasNext(); )
956         ((MethodObserver)e.next()).notify(this);
957   }
958 
959   /**
960    * Return string representation close to declaration format,
961    * `public static void _main(String[]) throws IOException', e.g.
962    *
963    * @return String representation of the method.
964    */
965   public final String toString() {
966     String access    = Utility.accessToString(access_flags);
967     String signature = Type.getMethodSignature(type, arg_types);
968 
969     signature = Utility.methodSignatureToString(signature, name, access,
970                                                 true, getLocalVariableTable(cp));
971 
972     StringBuffer buf = new StringBuffer(signature);
973 
974     if(throws_vec.size() > 0) {
975       for(Iterator e = throws_vec.iterator(); e.hasNext(); )
976         buf.append("\n\t\tthrows " + e.next());
977     }
978 
979     return buf.toString();
980   }
981 
982   /** @return deep copy of this method
983    */
984   public MethodGen copy(String class_name, ConstantPoolGen cp) {
985     Method    m  = ((MethodGen)clone()).getMethod();
986     MethodGen mg = new MethodGen(m, class_name, this.cp);
987 
988     if(this.cp != cp) {
989       mg.setConstantPool(cp);
990       mg.getInstructionList().replaceConstantPool(this.cp, cp);
991     }
992 
993     return mg;
994   }
995 }