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