1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.generic;
18
19 import java.util.ArrayList;
20 import java.util.Hashtable;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Stack;
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 class_name;
56 private Type[] arg_types;
57 private String[] arg_names;
58 private int max_locals;
59 private int max_stack;
60 private InstructionList il;
61 private boolean strip_attributes;
62 private List variable_vec = new ArrayList();
63 private List line_number_vec = new ArrayList();
64 private List exception_vec = new ArrayList();
65 private List throws_vec = new ArrayList();
66 private List code_attrs_vec = new ArrayList();
67 private static BCELComparator _cmp = new BCELComparator() {
68
69 public boolean equals( Object o1, Object 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 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[] arg_names,
107 String method_name, String 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
123
124 if (!isStatic() && (class_name != null)) {
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) {
136 if (size != arg_names.length) {
137 throw new ClassGenException("Mismatch in argument array lengths: " + size
138 + " vs. " + arg_names.length);
139 }
140 } else {
141 arg_names = new String[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 class_name, ConstantPoolGen cp) {
164 this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m
165 .getSignature()), null
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 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) {
192 end = il.getEnd();
193 } else {
194 end = il.findHandle(end_pc);
195 end = end.getPrev();
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
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[] 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 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("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 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;
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
349
350
351
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
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 e) {
438 }
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
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 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 class_name ) {
515 throws_vec.add(class_name);
516 }
517
518
519 /***
520 * Remove an exception.
521 */
522 public void removeException( String 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
537
538 public String[] getExceptions() {
539 String[] e = new String[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) throws_vec.get(i));
554 }
555 } catch (ArrayIndexOutOfBoundsException 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 signature = getSignature();
609 int name_index = cp.addUtf8(name);
610 int signature_index = cp.addUtf8(signature);
611
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
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
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;
636 Code code = null;
637 if ((il != null) && !isAbstract() && !isNative()) {
638
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 +
647 2 + exc_len +
648 2 + attrs_len,
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
656 }
657 Method m = new Method(access_flags, name_index, signature_index, getAttributes(), cp
658 .getConstantPool());
659
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
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 getClassName() {
735 return class_name;
736 }
737
738
739 public void setClassName( String 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[] arg_names ) {
775 this.arg_names = arg_names;
776 }
777
778
779 public String[] getArgumentNames() {
780 return (String[]) arg_names.clone();
781 }
782
783
784 public void setArgumentName( int i, String name ) {
785 arg_names[i] = name;
786 }
787
788
789 public String 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 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 branchTargets = new Stack();
872 Hashtable visitedTargets = new Hashtable();
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
913
914
915
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
934 if (instruction instanceof BranchInstruction) {
935 BranchInstruction branch = (BranchInstruction) instruction;
936 if (instruction instanceof Select) {
937
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
944 ih = null;
945 } else if (!(branch instanceof IfInstruction)) {
946
947
948 if (opcode == Constants.JSR || opcode == Constants.JSR_W) {
949 branchTargets.push(ih.getNext(), stackDepth - 1);
950 }
951 ih = null;
952 }
953
954
955
956 branchTargets.push(branch.getTarget(), stackDepth);
957 } else {
958
959 if (opcode == Constants.ATHROW || opcode == Constants.RET
960 || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
961 ih = null;
962 }
963 }
964
965 if (ih != null) {
966 ih = ih.getNext();
967 }
968
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 observers;
981
982
983 /*** Add observer for this object.
984 */
985 public void addObserver( MethodObserver o ) {
986 if (observers == null) {
987 observers = new ArrayList();
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 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 toString() {
1022 String access = Utility.accessToString(access_flags);
1023 String signature = Type.getMethodSignature(type, arg_types);
1024 signature = Utility.methodSignatureToString(signature, name, access, true,
1025 getLocalVariableTable(cp));
1026 StringBuffer buf = new StringBuffer(signature);
1027 if (throws_vec.size() > 0) {
1028 for (Iterator 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 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 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 }