KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > bcel > generic > MethodGen


1 /*
2  * Copyright 2000-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17 package org.apache.bcel.generic;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Stack JavaDoc;
24 import org.apache.bcel.Constants;
25 import org.apache.bcel.classfile.Attribute;
26 import org.apache.bcel.classfile.Code;
27 import org.apache.bcel.classfile.CodeException;
28 import org.apache.bcel.classfile.ExceptionTable;
29 import org.apache.bcel.classfile.LineNumber;
30 import org.apache.bcel.classfile.LineNumberTable;
31 import org.apache.bcel.classfile.LocalVariable;
32 import org.apache.bcel.classfile.LocalVariableTable;
33 import org.apache.bcel.classfile.Method;
34 import org.apache.bcel.classfile.Utility;
35 import org.apache.bcel.util.BCELComparator;
36
37 /**
38  * Template class for building up a method. This is done by defining exception
39  * handlers, adding thrown exceptions, local variables and attributes, whereas
40  * the `LocalVariableTable' and `LineNumberTable' attributes will be set
41  * automatically for the code. Use stripAttributes() if you don't like this.
42  *
43  * While generating code it may be necessary to insert NOP operations. You can
44  * use the `removeNOPs' method to get rid off them.
45  * The resulting method object can be obtained via the `getMethod()' method.
46  *
47  * @version $Id: MethodGen.java 386056 2006-03-15 11:31:56Z tcurdt $
48  * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
49  * @author <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
50  * @see InstructionList
51  * @see Method
52  */

53 public class MethodGen extends FieldGenOrMethodGen {
54
55     private String JavaDoc class_name;
56     private Type[] arg_types;
57     private String JavaDoc[] arg_names;
58     private int max_locals;
59     private int max_stack;
60     private InstructionList il;
61     private boolean strip_attributes;
62     private List JavaDoc variable_vec = new ArrayList JavaDoc();
63     private List JavaDoc line_number_vec = new ArrayList JavaDoc();
64     private List JavaDoc exception_vec = new ArrayList JavaDoc();
65     private List JavaDoc throws_vec = new ArrayList JavaDoc();
66     private List JavaDoc code_attrs_vec = new ArrayList JavaDoc();
67     private static BCELComparator _cmp = new BCELComparator() {
68
69         public boolean equals( Object JavaDoc o1, Object JavaDoc o2 ) {
70             MethodGen THIS = (MethodGen) o1;
71             MethodGen THAT = (MethodGen) o2;
72             return THIS.getName().equals(THAT.getName())
73                     && THIS.getSignature().equals(THAT.getSignature());
74         }
75
76
77         public int hashCode( Object JavaDoc o ) {
78             MethodGen THIS = (MethodGen) o;
79             return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
80         }
81     };
82
83
84     /**
85      * Declare method. If the method is non-static the constructor
86      * automatically declares a local variable `$this' in slot 0. The
87      * actual code is contained in the `il' parameter, which may further
88      * manipulated by the user. But he must take care not to remove any
89      * instruction (handles) that are still referenced from this object.
90      *
91      * For example one may not add a local variable and later remove the
92      * instructions it refers to without causing havoc. It is safe
93      * however if you remove that local variable, too.
94      *
95      * @param access_flags access qualifiers
96      * @param return_type method type
97      * @param arg_types argument types
98      * @param arg_names argument names (if this is null, default names will be provided
99      * for them)
100      * @param method_name name of method
101      * @param class_name class name containing this method (may be null, if you don't care)
102      * @param il instruction list associated with this method, may be null only for
103      * abstract or native methods
104      * @param cp constant pool
105      */

106     public MethodGen(int access_flags, Type return_type, Type[] arg_types, String JavaDoc[] arg_names,
107             String JavaDoc method_name, String JavaDoc class_name, InstructionList il, ConstantPoolGen cp) {
108         setAccessFlags(access_flags);
109         setType(return_type);
110         setArgumentTypes(arg_types);
111         setArgumentNames(arg_names);
112         setName(method_name);
113         setClassName(class_name);
114         setInstructionList(il);
115         setConstantPool(cp);
116         boolean abstract_ = isAbstract() || isNative();
117         InstructionHandle start = null;
118         InstructionHandle end = null;
119         if (!abstract_) {
120             start = il.getStart();
121             end = il.getEnd();
122             /* Add local variables, namely the implicit `this' and the arguments
123              */

124             if (!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
125
addLocalVariable("this", new ObjectType(class_name), start, end);
126             }
127         }
128         if (arg_types != null) {
129             int size = arg_types.length;
130             for (int i = 0; i < size; i++) {
131                 if (Type.VOID == arg_types[i]) {
132                     throw new ClassGenException("'void' is an illegal argument type for a method");
133                 }
134             }
135             if (arg_names != null) { // Names for variables provided?
136
if (size != arg_names.length) {
137                     throw new ClassGenException("Mismatch in argument array lengths: " + size
138                             + " vs. " + arg_names.length);
139                 }
140             } else { // Give them dummy names
141
arg_names = new String JavaDoc[size];
142                 for (int i = 0; i < size; i++) {
143                     arg_names[i] = "arg" + i;
144                 }
145                 setArgumentNames(arg_names);
146             }
147             if (!abstract_) {
148                 for (int i = 0; i < size; i++) {
149                     addLocalVariable(arg_names[i], arg_types[i], start, end);
150                 }
151             }
152         }
153     }
154
155
156     /**
157      * Instantiate from existing method.
158      *
159      * @param m method
160      * @param class_name class name containing this method
161      * @param cp constant pool
162      */

163     public MethodGen(Method m, String JavaDoc class_name, ConstantPoolGen cp) {
164         this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m
165                 .getSignature()), null /* may be overridden anyway */
166         , m.getName(), class_name,
167                 ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)
168                         ? new InstructionList(m.getCode().getCode())
169                         : null, cp);
170         Attribute[] attributes = m.getAttributes();
171         for (int i = 0; i < attributes.length; i++) {
172             Attribute a = attributes[i];
173             if (a instanceof Code) {
174                 Code c = (Code) a;
175                 setMaxStack(c.getMaxStack());
176                 setMaxLocals(c.getMaxLocals());
177                 CodeException[] ces = c.getExceptionTable();
178                 if (ces != null) {
179                     for (int j = 0; j < ces.length; j++) {
180                         CodeException ce = ces[j];
181                         int type = ce.getCatchType();
182                         ObjectType c_type = null;
183                         if (type > 0) {
184                             String JavaDoc cen = m.getConstantPool().getConstantString(type,
185                                     Constants.CONSTANT_Class);
186                             c_type = new ObjectType(cen);
187                         }
188                         int end_pc = ce.getEndPC();
189                         int length = m.getCode().getCode().length;
190                         InstructionHandle end;
191                         if (length == end_pc) { // May happen, because end_pc is exclusive
192
end = il.getEnd();
193                         } else {
194                             end = il.findHandle(end_pc);
195                             end = end.getPrev(); // Make it inclusive
196
}
197                         addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce
198                                 .getHandlerPC()), c_type);
199                     }
200                 }
201                 Attribute[] c_attributes = c.getAttributes();
202                 for (int j = 0; j < c_attributes.length; j++) {
203                     a = c_attributes[j];
204                     if (a instanceof LineNumberTable) {
205                         LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable();
206                         for (int k = 0; k < ln.length; k++) {
207                             LineNumber l = ln[k];
208                             InstructionHandle ih = il.findHandle(l.getStartPC());
209                             if (ih != null) {
210                                 addLineNumber(ih, l.getLineNumber());
211                             }
212                         }
213                     } else if (a instanceof LocalVariableTable) {
214                         LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
215                         removeLocalVariables();
216                         for (int k = 0; k < lv.length; k++) {
217                             LocalVariable l = lv[k];
218                             InstructionHandle start = il.findHandle(l.getStartPC());
219                             InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
220                             // Repair malformed handles
221
if (null == start) {
222                                 start = il.getStart();
223                             }
224                             if (null == end) {
225                                 end = il.getEnd();
226                             }
227                             addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
228                                     .getIndex(), start, end);
229                         }
230                     } else {
231                         addCodeAttribute(a);
232                     }
233                 }
234             } else if (a instanceof ExceptionTable) {
235                 String JavaDoc[] names = ((ExceptionTable) a).getExceptionNames();
236                 for (int j = 0; j < names.length; j++) {
237                     addException(names[j]);
238                 }
239             } else {
240                 addAttribute(a);
241             }
242         }
243     }
244
245
246     /**
247      * Adds a local variable to this method.
248      *
249      * @param name variable name
250      * @param type variable type
251      * @param slot the index of the local variable, if type is long or double, the next available
252      * index is slot+2
253      * @param start from where the variable is valid
254      * @param end until where the variable is valid
255      * @return new local variable object
256      * @see LocalVariable
257      */

258     public LocalVariableGen addLocalVariable( String JavaDoc name, Type type, int slot,
259             InstructionHandle start, InstructionHandle end ) {
260         byte t = type.getType();
261         if (t != Constants.T_ADDRESS) {
262             int add = type.getSize();
263             if (slot + add > max_locals) {
264                 max_locals = slot + add;
265             }
266             LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
267             int i;
268             if ((i = variable_vec.indexOf(l)) >= 0) {
269                 variable_vec.set(i, l);
270             } else {
271                 variable_vec.add(l);
272             }
273             return l;
274         } else {
275             throw new IllegalArgumentException JavaDoc("Can not use " + type
276                     + " as type for local variable");
277         }
278     }
279
280
281     /**
282      * Adds a local variable to this method and assigns an index automatically.
283      *
284      * @param name variable name
285      * @param type variable type
286      * @param start from where the variable is valid, if this is null,
287      * it is valid from the start
288      * @param end until where the variable is valid, if this is null,
289      * it is valid to the end
290      * @return new local variable object
291      * @see LocalVariable
292      */

293     public LocalVariableGen addLocalVariable( String JavaDoc name, Type type, InstructionHandle start,
294             InstructionHandle end ) {
295         return addLocalVariable(name, type, max_locals, start, end);
296     }
297
298
299     /**
300      * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
301      * with an explicit index argument.
302      */

303     public void removeLocalVariable( LocalVariableGen l ) {
304         variable_vec.remove(l);
305     }
306
307
308     /**
309      * Remove all local variables.
310      */

311     public void removeLocalVariables() {
312         variable_vec.clear();
313     }
314
315
316     /**
317      * Sort local variables by index
318      */

319     private static final void sort( LocalVariableGen[] vars, int l, int r ) {
320         int i = l, j = r;
321         int m = vars[(l + r) / 2].getIndex();
322         LocalVariableGen h;
323         do {
324             while (vars[i].getIndex() < m) {
325                 i++;
326             }
327             while (m < vars[j].getIndex()) {
328                 j--;
329             }
330             if (i <= j) {
331                 h = vars[i];
332                 vars[i] = vars[j];
333                 vars[j] = h; // Swap elements
334
i++;
335                 j--;
336             }
337         } while (i <= j);
338         if (l < j) {
339             sort(vars, l, j);
340         }
341         if (i < r) {
342             sort(vars, i, r);
343         }
344     }
345
346
347     /*
348      * If the range of the variable has not been set yet, it will be set to be valid from
349      * the start to the end of the instruction list.
350      *
351      * @return array of declared local variables sorted by index
352      */

353     public LocalVariableGen[] getLocalVariables() {
354         int size = variable_vec.size();
355         LocalVariableGen[] lg = new LocalVariableGen[size];
356         variable_vec.toArray(lg);
357         for (int i = 0; i < size; i++) {
358             if (lg[i].getStart() == null) {
359                 lg[i].setStart(il.getStart());
360             }
361             if (lg[i].getEnd() == null) {
362                 lg[i].setEnd(il.getEnd());
363             }
364         }
365         if (size > 1) {
366             sort(lg, 0, size - 1);
367         }
368         return lg;
369     }
370
371
372     /**
373      * @return `LocalVariableTable' attribute of all the local variables of this method.
374      */

375     public LocalVariableTable getLocalVariableTable( ConstantPoolGen cp ) {
376         LocalVariableGen[] lg = getLocalVariables();
377         int size = lg.length;
378         LocalVariable[] lv = new LocalVariable[size];
379         for (int i = 0; i < size; i++) {
380             lv[i] = lg[i].getLocalVariable(cp);
381         }
382         return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp
383                 .getConstantPool());
384     }
385
386
387     /**
388      * Give an instruction a line number corresponding to the source code line.
389      *
390      * @param ih instruction to tag
391      * @return new line number object
392      * @see LineNumber
393      */

394     public LineNumberGen addLineNumber( InstructionHandle ih, int src_line ) {
395         LineNumberGen l = new LineNumberGen(ih, src_line);
396         line_number_vec.add(l);
397         return l;
398     }
399
400
401     /**
402      * Remove a line number.
403      */

404     public void removeLineNumber( LineNumberGen l ) {
405         line_number_vec.remove(l);
406     }
407
408
409     /**
410      * Remove all line numbers.
411      */

412     public void removeLineNumbers() {
413         line_number_vec.clear();
414     }
415
416
417     /*
418      * @return array of line numbers
419      */

420     public LineNumberGen[] getLineNumbers() {
421         LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
422         line_number_vec.toArray(lg);
423         return lg;
424     }
425
426
427     /**
428      * @return `LineNumberTable' attribute of all the local variables of this method.
429      */

430     public LineNumberTable getLineNumberTable( ConstantPoolGen cp ) {
431         int size = line_number_vec.size();
432         LineNumber[] ln = new LineNumber[size];
433         try {
434             for (int i = 0; i < size; i++) {
435                 ln[i] = ((LineNumberGen) line_number_vec.get(i)).getLineNumber();
436             }
437         } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
438         } // Never occurs
439
return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp
440                 .getConstantPool());
441     }
442
443
444     /**
445      * Add an exception handler, i.e., specify region where a handler is active and an
446      * instruction where the actual handling is done.
447      *
448      * @param start_pc Start of region (inclusive)
449      * @param end_pc End of region (inclusive)
450      * @param handler_pc Where handling is done
451      * @param catch_type class type of handled exception or null if any
452      * exception is handled
453      * @return new exception handler object
454      */

455     public CodeExceptionGen addExceptionHandler( InstructionHandle start_pc,
456             InstructionHandle end_pc, InstructionHandle handler_pc, ObjectType catch_type ) {
457         if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) {
458             throw new ClassGenException("Exception handler target is null instruction");
459         }
460         CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type);
461         exception_vec.add(c);
462         return c;
463     }
464
465
466     /**
467      * Remove an exception handler.
468      */

469     public void removeExceptionHandler( CodeExceptionGen c ) {
470         exception_vec.remove(c);
471     }
472
473
474     /**
475      * Remove all line numbers.
476      */

477     public void removeExceptionHandlers() {
478         exception_vec.clear();
479     }
480
481
482     /*
483      * @return array of declared exception handlers
484      */

485     public CodeExceptionGen[] getExceptionHandlers() {
486         CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()];
487         exception_vec.toArray(cg);
488         return cg;
489     }
490
491
492     /**
493      * @return code exceptions for `Code' attribute
494      */

495     private CodeException[] getCodeExceptions() {
496         int size = exception_vec.size();
497         CodeException[] c_exc = new CodeException[size];
498         try {
499             for (int i = 0; i < size; i++) {
500                 CodeExceptionGen c = (CodeExceptionGen) exception_vec.get(i);
501                 c_exc[i] = c.getCodeException(cp);
502             }
503         } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
504         }
505         return c_exc;
506     }
507
508
509     /**
510      * Add an exception possibly thrown by this method.
511      *
512      * @param class_name (fully qualified) name of exception
513      */

514     public void addException( String JavaDoc class_name ) {
515         throws_vec.add(class_name);
516     }
517
518
519     /**
520      * Remove an exception.
521      */

522     public void removeException( String JavaDoc c ) {
523         throws_vec.remove(c);
524     }
525
526
527     /**
528      * Remove all exceptions.
529      */

530     public void removeExceptions() {
531         throws_vec.clear();
532     }
533
534
535     /*
536      * @return array of thrown exceptions
537      */

538     public String JavaDoc[] getExceptions() {
539         String JavaDoc[] e = new String JavaDoc[throws_vec.size()];
540         throws_vec.toArray(e);
541         return e;
542     }
543
544
545     /**
546      * @return `Exceptions' attribute of all the exceptions thrown by this method.
547      */

548     private ExceptionTable getExceptionTable( ConstantPoolGen cp ) {
549         int size = throws_vec.size();
550         int[] ex = new int[size];
551         try {
552             for (int i = 0; i < size; i++) {
553                 ex[i] = cp.addClass((String JavaDoc) throws_vec.get(i));
554             }
555         } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
556         }
557         return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool());
558     }
559
560
561     /**
562      * Add an attribute to the code. Currently, the JVM knows about the
563      * LineNumberTable, LocalVariableTable and StackMap attributes,
564      * where the former two will be generated automatically and the
565      * latter is used for the MIDP only. Other attributes will be
566      * ignored by the JVM but do no harm.
567      *
568      * @param a attribute to be added
569      */

570     public void addCodeAttribute( Attribute a ) {
571         code_attrs_vec.add(a);
572     }
573
574
575     /**
576      * Remove a code attribute.
577      */

578     public void removeCodeAttribute( Attribute a ) {
579         code_attrs_vec.remove(a);
580     }
581
582
583     /**
584      * Remove all code attributes.
585      */

586     public void removeCodeAttributes() {
587         code_attrs_vec.clear();
588     }
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     /**
602      * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
603      * before calling this method (the same applies for max locals).
604      *
605      * @return method object
606      */

607     public Method getMethod() {
608         String JavaDoc signature = getSignature();
609         int name_index = cp.addUtf8(name);
610         int signature_index = cp.addUtf8(signature);
611         /* Also updates positions of instructions, i.e., their indices
612          */

613         byte[] byte_code = null;
614         if (il != null) {
615             byte_code = il.getByteCode();
616         }
617         LineNumberTable lnt = null;
618         LocalVariableTable lvt = null;
619         /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
620          */

621         if ((variable_vec.size() > 0) && !strip_attributes) {
622             addCodeAttribute(lvt = getLocalVariableTable(cp));
623         }
624         if ((line_number_vec.size() > 0) && !strip_attributes) {
625             addCodeAttribute(lnt = getLineNumberTable(cp));
626         }
627         Attribute[] code_attrs = getCodeAttributes();
628         /* Each attribute causes 6 additional header bytes
629          */

630         int attrs_len = 0;
631         for (int i = 0; i < code_attrs.length; i++) {
632             attrs_len += (code_attrs[i].getLength() + 6);
633         }
634         CodeException[] c_exc = getCodeExceptions();
635         int exc_len = c_exc.length * 8; // Every entry takes 8 bytes
636
Code code = null;
637         if ((il != null) && !isAbstract() && !isNative()) {
638             // Remove any stale code attribute
639
Attribute[] attributes = getAttributes();
640             for (int i = 0; i < attributes.length; i++) {
641                 Attribute a = attributes[i];
642                 if (a instanceof Code) {
643                     removeAttribute(a);
644                 }
645             }
646             code = new Code(cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code
647
2 + exc_len + // exceptions
648
2 + attrs_len, // attributes
649
max_stack, max_locals, byte_code, c_exc, code_attrs, cp.getConstantPool());
650             addAttribute(code);
651         }
652         ExceptionTable et = null;
653         if (throws_vec.size() > 0) {
654             addAttribute(et = getExceptionTable(cp));
655             // Add `Exceptions' if there are "throws" clauses
656
}
657         Method m = new Method(access_flags, name_index, signature_index, getAttributes(), cp
658                 .getConstantPool());
659         // Undo effects of adding attributes
660
if (lvt != null) {
661             removeCodeAttribute(lvt);
662         }
663         if (lnt != null) {
664             removeCodeAttribute(lnt);
665         }
666         if (code != null) {
667             removeAttribute(code);
668         }
669         if (et != null) {
670             removeAttribute(et);
671         }
672         return m;
673     }
674
675
676     /**
677      * Remove all NOPs from the instruction list (if possible) and update every
678      * object refering to them, i.e., branch instructions, local variables and
679      * exception handlers.
680      */

681     public void removeNOPs() {
682         if (il != null) {
683             InstructionHandle next;
684             /* Check branch instructions.
685              */

686             for (InstructionHandle ih = il.getStart(); ih != null; ih = next) {
687                 next = ih.next;
688                 if ((next != null) && (ih.getInstruction() instanceof NOP)) {
689                     try {
690                         il.delete(ih);
691                     } catch (TargetLostException e) {
692                         InstructionHandle[] targets = e.getTargets();
693                         for (int i = 0; i < targets.length; i++) {
694                             InstructionTargeter[] targeters = targets[i].getTargeters();
695                             for (int j = 0; j < targeters.length; j++) {
696                                 targeters[j].updateTarget(targets[i], next);
697                             }
698                         }
699                     }
700                 }
701             }
702         }
703     }
704
705
706     /**
707      * Set maximum number of local variables.
708      */

709     public void setMaxLocals( int m ) {
710         max_locals = m;
711     }
712
713
714     public int getMaxLocals() {
715         return max_locals;
716     }
717
718
719     /**
720      * Set maximum stack size for this method.
721      */

722     public void setMaxStack( int m ) {
723         max_stack = m;
724     }
725
726
727     public int getMaxStack() {
728         return max_stack;
729     }
730
731
732     /** @return class that contains this method
733      */

734     public String JavaDoc getClassName() {
735         return class_name;
736     }
737
738
739     public void setClassName( String JavaDoc class_name ) {
740         this.class_name = class_name;
741     }
742
743
744     public void setReturnType( Type return_type ) {
745         setType(return_type);
746     }
747
748
749     public Type getReturnType() {
750         return getType();
751     }
752
753
754     public void setArgumentTypes( Type[] arg_types ) {
755         this.arg_types = arg_types;
756     }
757
758
759     public Type[] getArgumentTypes() {
760         return (Type[]) arg_types.clone();
761     }
762
763
764     public void setArgumentType( int i, Type type ) {
765         arg_types[i] = type;
766     }
767
768
769     public Type getArgumentType( int i ) {
770         return arg_types[i];
771     }
772
773
774     public void setArgumentNames( String JavaDoc[] arg_names ) {
775         this.arg_names = arg_names;
776     }
777
778
779     public String JavaDoc[] getArgumentNames() {
780         return (String JavaDoc[]) arg_names.clone();
781     }
782
783
784     public void setArgumentName( int i, String JavaDoc name ) {
785         arg_names[i] = name;
786     }
787
788
789     public String JavaDoc getArgumentName( int i ) {
790         return arg_names[i];
791     }
792
793
794     public InstructionList getInstructionList() {
795         return il;
796     }
797
798
799     public void setInstructionList( InstructionList il ) {
800         this.il = il;
801     }
802
803
804     public String JavaDoc getSignature() {
805         return Type.getMethodSignature(type, arg_types);
806     }
807
808
809     /**
810      * Computes max. stack size by performing control flow analysis.
811      */

812     public void setMaxStack() {
813         if (il != null) {
814             max_stack = getMaxStack(cp, il, getExceptionHandlers());
815         } else {
816             max_stack = 0;
817         }
818     }
819
820
821     /**
822      * Compute maximum number of local variables.
823      */

824     public void setMaxLocals() {
825         if (il != null) {
826             int max = isStatic() ? 0 : 1;
827             if (arg_types != null) {
828                 for (int i = 0; i < arg_types.length; i++) {
829                     max += arg_types[i].getSize();
830                 }
831             }
832             for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
833                 Instruction ins = ih.getInstruction();
834                 if ((ins instanceof LocalVariableInstruction) || (ins instanceof RET)
835                         || (ins instanceof IINC)) {
836                     int index = ((IndexedInstruction) ins).getIndex()
837                             + ((TypedInstruction) ins).getType(cp).getSize();
838                     if (index > max) {
839                         max = index;
840                     }
841                 }
842             }
843             max_locals = max;
844         } else {
845             max_locals = 0;
846         }
847     }
848
849
850     /** Do not/Do produce attributes code attributesLineNumberTable and
851      * LocalVariableTable, like javac -O
852      */

853     public void stripAttributes( boolean flag ) {
854         strip_attributes = flag;
855     }
856
857     static final class BranchTarget {
858
859         InstructionHandle target;
860         int stackDepth;
861
862
863         BranchTarget(InstructionHandle target, int stackDepth) {
864             this.target = target;
865             this.stackDepth = stackDepth;
866         }
867     }
868
869     static final class BranchStack {
870
871         Stack JavaDoc branchTargets = new Stack JavaDoc();
872         Hashtable JavaDoc visitedTargets = new Hashtable JavaDoc();
873
874
875         public void push( InstructionHandle target, int stackDepth ) {
876             if (visited(target)) {
877                 return;
878             }
879             branchTargets.push(visit(target, stackDepth));
880         }
881
882
883         public BranchTarget pop() {
884             if (!branchTargets.empty()) {
885                 BranchTarget bt = (BranchTarget) branchTargets.pop();
886                 return bt;
887             }
888             return null;
889         }
890
891
892         private final BranchTarget visit( InstructionHandle target, int stackDepth ) {
893             BranchTarget bt = new BranchTarget(target, stackDepth);
894             visitedTargets.put(target, bt);
895             return bt;
896         }
897
898
899         private final boolean visited( InstructionHandle target ) {
900             return (visitedTargets.get(target) != null);
901         }
902     }
903
904
905     /**
906      * Computes stack usage of an instruction list by performing control flow analysis.
907      *
908      * @return maximum stack depth used by method
909      */

910     public static int getMaxStack( ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et ) {
911         BranchStack branchTargets = new BranchStack();
912         /* Initially, populate the branch stack with the exception
913          * handlers, because these aren't (necessarily) branched to
914          * explicitly. in each case, the stack will have depth 1,
915          * containing the exception object.
916          */

917         for (int i = 0; i < et.length; i++) {
918             InstructionHandle handler_pc = et[i].getHandlerPC();
919             if (handler_pc != null) {
920                 branchTargets.push(handler_pc, 1);
921             }
922         }
923         int stackDepth = 0, maxStackDepth = 0;
924         InstructionHandle ih = il.getStart();
925         while (ih != null) {
926             Instruction instruction = ih.getInstruction();
927             short opcode = instruction.getOpcode();
928             int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
929             stackDepth += delta;
930             if (stackDepth > maxStackDepth) {
931                 maxStackDepth = stackDepth;
932             }
933             // choose the next instruction based on whether current is a branch.
934
if (instruction instanceof BranchInstruction) {
935                 BranchInstruction branch = (BranchInstruction) instruction;
936                 if (instruction instanceof Select) {
937                     // explore all of the select's targets. the default target is handled below.
938
Select select = (Select) branch;
939                     InstructionHandle[] targets = select.getTargets();
940                     for (int i = 0; i < targets.length; i++) {
941                         branchTargets.push(targets[i], stackDepth);
942                     }
943                     // nothing to fall through to.
944
ih = null;
945                 } else if (!(branch instanceof IfInstruction)) {
946                     // if an instruction that comes back to following PC,
947
// push next instruction, with stack depth reduced by 1.
948
if (opcode == Constants.JSR || opcode == Constants.JSR_W) {
949                         branchTargets.push(ih.getNext(), stackDepth - 1);
950                     }
951                     ih = null;
952                 }
953                 // for all branches, the target of the branch is pushed on the branch stack.
954
// conditional branches have a fall through case, selects don't, and
955
// jsr/jsr_w return to the next instruction.
956
branchTargets.push(branch.getTarget(), stackDepth);
957             } else {
958                 // check for instructions that terminate the method.
959
if (opcode == Constants.ATHROW || opcode == Constants.RET
960                         || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
961                     ih = null;
962                 }
963             }
964             // normal case, go to the next instruction.
965
if (ih != null) {
966                 ih = ih.getNext();
967             }
968             // if we have no more instructions, see if there are any deferred branches to explore.
969
if (ih == null) {
970                 BranchTarget bt = branchTargets.pop();
971                 if (bt != null) {
972                     ih = bt.target;
973                     stackDepth = bt.stackDepth;
974                 }
975             }
976         }
977         return maxStackDepth;
978     }
979
980     private List JavaDoc observers;
981
982
983     /** Add observer for this object.
984      */

985     public void addObserver( MethodObserver o ) {
986         if (observers == null) {
987             observers = new ArrayList JavaDoc();
988         }
989         observers.add(o);
990     }
991
992
993     /** Remove observer for this object.
994      */

995     public void removeObserver( MethodObserver o ) {
996         if (observers != null) {
997             observers.remove(o);
998         }
999     }
1000
1001
1002    /** Call notify() method on all observers. This method is not called
1003     * automatically whenever the state has changed, but has to be
1004     * called by the user after he has finished editing the object.
1005     */

1006    public void update() {
1007        if (observers != null) {
1008            for (Iterator JavaDoc e = observers.iterator(); e.hasNext();) {
1009                ((MethodObserver) e.next()).notify(this);
1010            }
1011        }
1012    }
1013
1014
1015    /**
1016     * Return string representation close to declaration format,
1017     * `public static void main(String[]) throws IOException', e.g.
1018     *
1019     * @return String representation of the method.
1020     */

1021    public final String JavaDoc toString() {
1022        String JavaDoc access = Utility.accessToString(access_flags);
1023        String JavaDoc signature = Type.getMethodSignature(type, arg_types);
1024        signature = Utility.methodSignatureToString(signature, name, access, true,
1025                getLocalVariableTable(cp));
1026        StringBuffer JavaDoc buf = new StringBuffer JavaDoc(signature);
1027        if (throws_vec.size() > 0) {
1028            for (Iterator JavaDoc e = throws_vec.iterator(); e.hasNext();) {
1029                buf.append("\n\t\tthrows ").append(e.next());
1030            }
1031        }
1032        return buf.toString();
1033    }
1034
1035
1036    /** @return deep copy of this method
1037     */

1038    public MethodGen copy( String JavaDoc class_name, ConstantPoolGen cp ) {
1039        Method m = ((MethodGen) clone()).getMethod();
1040        MethodGen mg = new MethodGen(m, class_name, this.cp);
1041        if (this.cp != cp) {
1042            mg.setConstantPool(cp);
1043            mg.getInstructionList().replaceConstantPool(this.cp, cp);
1044        }
1045        return mg;
1046    }
1047
1048
1049    /**
1050     * @return Comparison strategy object
1051     */

1052    public static BCELComparator getComparator() {
1053        return _cmp;
1054    }
1055
1056
1057    /**
1058     * @param comparator Comparison strategy object
1059     */

1060    public static void setComparator( BCELComparator comparator ) {
1061        _cmp = comparator;
1062    }
1063
1064
1065    /**
1066     * Return value as defined by given BCELComparator strategy.
1067     * By default two MethodGen objects are said to be equal when
1068     * their names and signatures are equal.
1069     *
1070     * @see java.lang.Object#equals(java.lang.Object)
1071     */

1072    public boolean equals( Object JavaDoc obj ) {
1073        return _cmp.equals(this, obj);
1074    }
1075
1076
1077    /**
1078     * Return value as defined by given BCELComparator strategy.
1079     * By default return the hashcode of the method's name XOR signature.
1080     *
1081     * @see java.lang.Object#hashCode()
1082     */

1083    public int hashCode() {
1084        return _cmp.hashCode(this);
1085    }
1086}
1087
Popular Tags