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