001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 */
018 package org.apache.bcel.generic;
019
020 import java.util.ArrayList;
021 import java.util.Hashtable;
022 import java.util.List;
023 import java.util.Stack;
024 import org.apache.bcel.Constants;
025 import org.apache.bcel.classfile.AnnotationEntry;
026 import org.apache.bcel.classfile.Annotations;
027 import org.apache.bcel.classfile.Attribute;
028 import org.apache.bcel.classfile.Code;
029 import org.apache.bcel.classfile.CodeException;
030 import org.apache.bcel.classfile.ExceptionTable;
031 import org.apache.bcel.classfile.LineNumber;
032 import org.apache.bcel.classfile.LineNumberTable;
033 import org.apache.bcel.classfile.LocalVariable;
034 import org.apache.bcel.classfile.LocalVariableTable;
035 import org.apache.bcel.classfile.LocalVariableTypeTable;
036 import org.apache.bcel.classfile.Method;
037 import org.apache.bcel.classfile.ParameterAnnotationEntry;
038 import org.apache.bcel.classfile.ParameterAnnotations;
039 import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
040 import org.apache.bcel.classfile.Utility;
041 import org.apache.bcel.util.BCELComparator;
042
043 /**
044 * Template class for building up a method. This is done by defining exception
045 * handlers, adding thrown exceptions, local variables and attributes, whereas
046 * the `LocalVariableTable' and `LineNumberTable' attributes will be set
047 * automatically for the code. Use stripAttributes() if you don't like this.
048 *
049 * While generating code it may be necessary to insert NOP operations. You can
050 * use the `removeNOPs' method to get rid off them.
051 * The resulting method object can be obtained via the `getMethod()' method.
052 *
053 * @version $Id: MethodGen.java 1152077 2011-07-29 02:29:42Z dbrosius $
054 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
055 * @author <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
056 * @see InstructionList
057 * @see Method
058 */
059 public class MethodGen extends FieldGenOrMethodGen {
060
061 private static final long serialVersionUID = -3924667713338957720L;
062 private String class_name;
063 private Type[] arg_types;
064 private String[] arg_names;
065 private int max_locals;
066 private int max_stack;
067 private InstructionList il;
068 private boolean strip_attributes;
069 private List<LocalVariableGen> variable_vec = new ArrayList<LocalVariableGen>();
070 private List<LineNumberGen> line_number_vec = new ArrayList<LineNumberGen>();
071 private List<CodeExceptionGen> exception_vec = new ArrayList<CodeExceptionGen>();
072 private List<String> throws_vec = new ArrayList<String>();
073 private List<Attribute> code_attrs_vec = new ArrayList<Attribute>();
074
075 private List<AnnotationEntryGen>[] param_annotations; // Array of lists containing AnnotationGen objects
076 private boolean hasParameterAnnotations = false;
077 private boolean haveUnpackedParameterAnnotations = false;
078
079 private static BCELComparator _cmp = new BCELComparator() {
080
081 public boolean equals( Object o1, Object o2 ) {
082 MethodGen THIS = (MethodGen) o1;
083 MethodGen THAT = (MethodGen) o2;
084 return THIS.getName().equals(THAT.getName())
085 && THIS.getSignature().equals(THAT.getSignature());
086 }
087
088
089 public int hashCode( Object o ) {
090 MethodGen THIS = (MethodGen) o;
091 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
092 }
093 };
094
095
096 /**
097 * Declare method. If the method is non-static the constructor
098 * automatically declares a local variable `$this' in slot 0. The
099 * actual code is contained in the `il' parameter, which may further
100 * manipulated by the user. But he must take care not to remove any
101 * instruction (handles) that are still referenced from this object.
102 *
103 * For example one may not add a local variable and later remove the
104 * instructions it refers to without causing havoc. It is safe
105 * however if you remove that local variable, too.
106 *
107 * @param access_flags access qualifiers
108 * @param return_type method type
109 * @param arg_types argument types
110 * @param arg_names argument names (if this is null, default names will be provided
111 * for them)
112 * @param method_name name of method
113 * @param class_name class name containing this method (may be null, if you don't care)
114 * @param il instruction list associated with this method, may be null only for
115 * abstract or native methods
116 * @param cp constant pool
117 */
118 public MethodGen(int access_flags, Type return_type, Type[] arg_types, String[] arg_names,
119 String method_name, String class_name, InstructionList il, ConstantPoolGen cp) {
120 setAccessFlags(access_flags);
121 setType(return_type);
122 setArgumentTypes(arg_types);
123 setArgumentNames(arg_names);
124 setName(method_name);
125 setClassName(class_name);
126 setInstructionList(il);
127 setConstantPool(cp);
128 boolean abstract_ = isAbstract() || isNative();
129 InstructionHandle start = null;
130 InstructionHandle end = null;
131 if (!abstract_) {
132 start = il.getStart();
133 end = il.getEnd();
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 if (arg_types != null) {
141 int size = arg_types.length;
142 for (int i = 0; i < size; i++) {
143 if (Type.VOID == arg_types[i]) {
144 throw new ClassGenException("'void' is an illegal argument type for a method");
145 }
146 }
147 if (arg_names != null) { // Names for variables provided?
148 if (size != arg_names.length) {
149 throw new ClassGenException("Mismatch in argument array lengths: " + size
150 + " vs. " + arg_names.length);
151 }
152 } else { // Give them dummy names
153 arg_names = new String[size];
154 for (int i = 0; i < size; i++) {
155 arg_names[i] = "arg" + i;
156 }
157 setArgumentNames(arg_names);
158 }
159 if (!abstract_) {
160 for (int i = 0; i < size; i++) {
161 addLocalVariable(arg_names[i], arg_types[i], start, end);
162 }
163 }
164 }
165 }
166
167
168 /**
169 * Instantiate from existing method.
170 *
171 * @param m method
172 * @param class_name class name containing this method
173 * @param cp constant pool
174 */
175 public MethodGen(Method m, String class_name, ConstantPoolGen cp) {
176 this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m
177 .getSignature()), null /* may be overridden anyway */
178 , m.getName(), class_name,
179 ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)
180 ? new InstructionList(m.getCode().getCode())
181 : null, cp);
182 Attribute[] attributes = m.getAttributes();
183 for (int i = 0; i < attributes.length; i++) {
184 Attribute a = attributes[i];
185 if (a instanceof Code) {
186 Code c = (Code) a;
187 setMaxStack(c.getMaxStack());
188 setMaxLocals(c.getMaxLocals());
189 CodeException[] ces = c.getExceptionTable();
190 if (ces != null) {
191 for (int j = 0; j < ces.length; j++) {
192 CodeException ce = ces[j];
193 int type = ce.getCatchType();
194 ObjectType c_type = null;
195 if (type > 0) {
196 String cen = m.getConstantPool().getConstantString(type,
197 Constants.CONSTANT_Class);
198 c_type = new ObjectType(cen);
199 }
200 int end_pc = ce.getEndPC();
201 int length = m.getCode().getCode().length;
202 InstructionHandle end;
203 if (length == end_pc) { // May happen, because end_pc is exclusive
204 end = il.getEnd();
205 } else {
206 end = il.findHandle(end_pc);
207 end = end.getPrev(); // Make it inclusive
208 }
209 addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce
210 .getHandlerPC()), c_type);
211 }
212 }
213 Attribute[] c_attributes = c.getAttributes();
214 for (int j = 0; j < c_attributes.length; j++) {
215 a = c_attributes[j];
216 if (a instanceof LineNumberTable) {
217 LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable();
218 for (int k = 0; k < ln.length; k++) {
219 LineNumber l = ln[k];
220 InstructionHandle ih = il.findHandle(l.getStartPC());
221 if (ih != null) {
222 addLineNumber(ih, l.getLineNumber());
223 }
224 }
225 } else if (a instanceof LocalVariableTable) {
226 LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
227 removeLocalVariables();
228 for (int k = 0; k < lv.length; k++) {
229 LocalVariable l = lv[k];
230 InstructionHandle start = il.findHandle(l.getStartPC());
231 InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
232 // Repair malformed handles
233 if (null == start) {
234 start = il.getStart();
235 }
236 if (null == end) {
237 end = il.getEnd();
238 }
239 addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
240 .getIndex(), start, end);
241 }
242 } else if (a instanceof LocalVariableTypeTable) {
243 LocalVariable[] lv = ((LocalVariableTypeTable) a).getLocalVariableTypeTable();
244 removeLocalVariables();
245 for (int k = 0; k < lv.length; k++) {
246 LocalVariable l = lv[k];
247 InstructionHandle start = il.findHandle(l.getStartPC());
248 InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
249 // Repair malformed handles
250 if (null == start) {
251 start = il.getStart();
252 }
253 if (null == end) {
254 end = il.getEnd();
255 }
256 addLocalVariable(l.getName(), Type.getType(l.getSignature()), l
257 .getIndex(), start, end);
258 }
259 } else {
260 addCodeAttribute(a);
261 }
262 }
263 } else if (a instanceof ExceptionTable) {
264 String[] names = ((ExceptionTable) a).getExceptionNames();
265 for (int j = 0; j < names.length; j++) {
266 addException(names[j]);
267 }
268 } else if (a instanceof Annotations) {
269 Annotations runtimeAnnotations = (Annotations) a;
270 AnnotationEntry[] aes = runtimeAnnotations.getAnnotationEntries();
271 for (int k = 0; k < aes.length; k++) {
272 AnnotationEntry element = aes[k];
273 addAnnotationEntry(new AnnotationEntryGen(element, cp, false));
274 }
275 } else {
276 addAttribute(a);
277 }
278 }
279 }
280
281
282 /**
283 * Adds a local variable to this method.
284 *
285 * @param name variable name
286 * @param type variable type
287 * @param slot the index of the local variable, if type is long or double, the next available
288 * index is slot+2
289 * @param start from where the variable is valid
290 * @param end until where the variable is valid
291 * @return new local variable object
292 * @see LocalVariable
293 */
294 public LocalVariableGen addLocalVariable( String name, Type type, int slot,
295 InstructionHandle start, InstructionHandle end ) {
296 byte t = type.getType();
297 if (t != Constants.T_ADDRESS) {
298 int add = type.getSize();
299 if (slot + add > max_locals) {
300 max_locals = slot + add;
301 }
302 LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
303 int i;
304 if ((i = variable_vec.indexOf(l)) >= 0) {
305 variable_vec.set(i, l);
306 } else {
307 variable_vec.add(l);
308 }
309 return l;
310 } else {
311 throw new IllegalArgumentException("Can not use " + type
312 + " as type for local variable");
313 }
314 }
315
316
317 /**
318 * Adds a local variable to this method and assigns an index automatically.
319 *
320 * @param name variable name
321 * @param type variable type
322 * @param start from where the variable is valid, if this is null,
323 * it is valid from the start
324 * @param end until where the variable is valid, if this is null,
325 * it is valid to the end
326 * @return new local variable object
327 * @see LocalVariable
328 */
329 public LocalVariableGen addLocalVariable( String name, Type type, InstructionHandle start,
330 InstructionHandle end ) {
331 return addLocalVariable(name, type, max_locals, start, end);
332 }
333
334
335 /**
336 * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable
337 * with an explicit index argument.
338 */
339 public void removeLocalVariable( LocalVariableGen l ) {
340 variable_vec.remove(l);
341 }
342
343
344 /**
345 * Remove all local variables.
346 */
347 public void removeLocalVariables() {
348 variable_vec.clear();
349 }
350
351
352 /**
353 * Sort local variables by index
354 */
355 private static final void sort( LocalVariableGen[] vars, int l, int r ) {
356 int i = l, j = r;
357 int m = vars[(l + r) / 2].getIndex();
358 LocalVariableGen h;
359 do {
360 while (vars[i].getIndex() < m) {
361 i++;
362 }
363 while (m < vars[j].getIndex()) {
364 j--;
365 }
366 if (i <= j) {
367 h = vars[i];
368 vars[i] = vars[j];
369 vars[j] = h; // Swap elements
370 i++;
371 j--;
372 }
373 } while (i <= j);
374 if (l < j) {
375 sort(vars, l, j);
376 }
377 if (i < r) {
378 sort(vars, i, r);
379 }
380 }
381
382
383 /*
384 * If the range of the variable has not been set yet, it will be set to be valid from
385 * the start to the end of the instruction list.
386 *
387 * @return array of declared local variables sorted by index
388 */
389 public LocalVariableGen[] getLocalVariables() {
390 int size = variable_vec.size();
391 LocalVariableGen[] lg = new LocalVariableGen[size];
392 variable_vec.toArray(lg);
393 for (int i = 0; i < size; i++) {
394 if (lg[i].getStart() == null) {
395 lg[i].setStart(il.getStart());
396 }
397 if (lg[i].getEnd() == null) {
398 lg[i].setEnd(il.getEnd());
399 }
400 }
401 if (size > 1) {
402 sort(lg, 0, size - 1);
403 }
404 return lg;
405 }
406
407
408 /**
409 * @return `LocalVariableTable' attribute of all the local variables of this method.
410 */
411 public LocalVariableTable getLocalVariableTable( ConstantPoolGen cp ) {
412 LocalVariableGen[] lg = getLocalVariables();
413 int size = lg.length;
414 LocalVariable[] lv = new LocalVariable[size];
415 for (int i = 0; i < size; i++) {
416 lv[i] = lg[i].getLocalVariable(cp);
417 }
418 return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp
419 .getConstantPool());
420 }
421
422
423 /**
424 * Give an instruction a line number corresponding to the source code line.
425 *
426 * @param ih instruction to tag
427 * @return new line number object
428 * @see LineNumber
429 */
430 public LineNumberGen addLineNumber( InstructionHandle ih, int src_line ) {
431 LineNumberGen l = new LineNumberGen(ih, src_line);
432 line_number_vec.add(l);
433 return l;
434 }
435
436
437 /**
438 * Remove a line number.
439 */
440 public void removeLineNumber( LineNumberGen l ) {
441 line_number_vec.remove(l);
442 }
443
444
445 /**
446 * Remove all line numbers.
447 */
448 public void removeLineNumbers() {
449 line_number_vec.clear();
450 }
451
452
453 /*
454 * @return array of line numbers
455 */
456 public LineNumberGen[] getLineNumbers() {
457 LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
458 line_number_vec.toArray(lg);
459 return lg;
460 }
461
462
463 /**
464 * @return `LineNumberTable' attribute of all the local variables of this method.
465 */
466 public LineNumberTable getLineNumberTable( ConstantPoolGen cp ) {
467 int size = line_number_vec.size();
468 LineNumber[] ln = new LineNumber[size];
469 try {
470 for (int i = 0; i < size; i++) {
471 ln[i] = line_number_vec.get(i).getLineNumber();
472 }
473 } catch (ArrayIndexOutOfBoundsException e) {
474 } // Never occurs
475 return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp
476 .getConstantPool());
477 }
478
479
480 /**
481 * Add an exception handler, i.e., specify region where a handler is active and an
482 * instruction where the actual handling is done.
483 *
484 * @param start_pc Start of region (inclusive)
485 * @param end_pc End of region (inclusive)
486 * @param handler_pc Where handling is done
487 * @param catch_type class type of handled exception or null if any
488 * exception is handled
489 * @return new exception handler object
490 */
491 public CodeExceptionGen addExceptionHandler( InstructionHandle start_pc,
492 InstructionHandle end_pc, InstructionHandle handler_pc, ObjectType catch_type ) {
493 if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) {
494 throw new ClassGenException("Exception handler target is null instruction");
495 }
496 CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type);
497 exception_vec.add(c);
498 return c;
499 }
500
501
502 /**
503 * Remove an exception handler.
504 */
505 public void removeExceptionHandler( CodeExceptionGen c ) {
506 exception_vec.remove(c);
507 }
508
509
510 /**
511 * Remove all line numbers.
512 */
513 public void removeExceptionHandlers() {
514 exception_vec.clear();
515 }
516
517
518 /*
519 * @return array of declared exception handlers
520 */
521 public CodeExceptionGen[] getExceptionHandlers() {
522 CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()];
523 exception_vec.toArray(cg);
524 return cg;
525 }
526
527
528 /**
529 * @return code exceptions for `Code' attribute
530 */
531 private CodeException[] getCodeExceptions() {
532 int size = exception_vec.size();
533 CodeException[] c_exc = new CodeException[size];
534 try {
535 for (int i = 0; i < size; i++) {
536 CodeExceptionGen c = exception_vec.get(i);
537 c_exc[i] = c.getCodeException(cp);
538 }
539 } catch (ArrayIndexOutOfBoundsException e) {
540 }
541 return c_exc;
542 }
543
544
545 /**
546 * Add an exception possibly thrown by this method.
547 *
548 * @param class_name (fully qualified) name of exception
549 */
550 public void addException( String class_name ) {
551 throws_vec.add(class_name);
552 }
553
554
555 /**
556 * Remove an exception.
557 */
558 public void removeException( String c ) {
559 throws_vec.remove(c);
560 }
561
562
563 /**
564 * Remove all exceptions.
565 */
566 public void removeExceptions() {
567 throws_vec.clear();
568 }
569
570
571 /*
572 * @return array of thrown exceptions
573 */
574 public String[] getExceptions() {
575 String[] e = new String[throws_vec.size()];
576 throws_vec.toArray(e);
577 return e;
578 }
579
580
581 /**
582 * @return `Exceptions' attribute of all the exceptions thrown by this method.
583 */
584 private ExceptionTable getExceptionTable( ConstantPoolGen cp ) {
585 int size = throws_vec.size();
586 int[] ex = new int[size];
587 try {
588 for (int i = 0; i < size; i++) {
589 ex[i] = cp.addClass(throws_vec.get(i));
590 }
591 } catch (ArrayIndexOutOfBoundsException e) {
592 }
593 return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool());
594 }
595
596
597 /**
598 * Add an attribute to the code. Currently, the JVM knows about the
599 * LineNumberTable, LocalVariableTable and StackMap attributes,
600 * where the former two will be generated automatically and the
601 * latter is used for the MIDP only. Other attributes will be
602 * ignored by the JVM but do no harm.
603 *
604 * @param a attribute to be added
605 */
606 public void addCodeAttribute( Attribute a ) {
607 code_attrs_vec.add(a);
608 }
609
610
611 /**
612 * Remove a code attribute.
613 */
614 public void removeCodeAttribute( Attribute a ) {
615 code_attrs_vec.remove(a);
616 }
617
618
619 /**
620 * Remove all code attributes.
621 */
622 public void removeCodeAttributes() {
623 code_attrs_vec.clear();
624 }
625
626
627 /**
628 * @return all attributes of this method.
629 */
630 public Attribute[] getCodeAttributes() {
631 Attribute[] attributes = new Attribute[code_attrs_vec.size()];
632 code_attrs_vec.toArray(attributes);
633 return attributes;
634 }
635
636 public void addAnnotationsAsAttribute(ConstantPoolGen cp) {
637 Attribute[] attrs = Utility.getAnnotationAttributes(cp,annotation_vec);
638 for (int i = 0; i < attrs.length; i++) {
639 addAttribute(attrs[i]);
640 }
641 }
642
643 public void addParameterAnnotationsAsAttribute(ConstantPoolGen cp) {
644 if (!hasParameterAnnotations) return;
645 Attribute[] attrs = Utility.getParameterAnnotationAttributes(cp,param_annotations);
646 if (attrs!=null) {
647 for (int i = 0; i < attrs.length; i++) {
648 addAttribute(attrs[i]);
649 }
650 }
651 }
652
653
654 /**
655 * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively,
656 * before calling this method (the same applies for max locals).
657 *
658 * @return method object
659 */
660 public Method getMethod() {
661 String signature = getSignature();
662 int name_index = cp.addUtf8(name);
663 int signature_index = cp.addUtf8(signature);
664 /* Also updates positions of instructions, i.e., their indices
665 */
666 byte[] byte_code = null;
667 if (il != null) {
668 byte_code = il.getByteCode();
669 }
670 LineNumberTable lnt = null;
671 LocalVariableTable lvt = null;
672 /* Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
673 */
674 if ((variable_vec.size() > 0) && !strip_attributes) {
675 addCodeAttribute(lvt = getLocalVariableTable(cp));
676 }
677 if ((line_number_vec.size() > 0) && !strip_attributes) {
678 addCodeAttribute(lnt = getLineNumberTable(cp));
679 }
680 Attribute[] code_attrs = getCodeAttributes();
681 /* Each attribute causes 6 additional header bytes
682 */
683 int attrs_len = 0;
684 for (int i = 0; i < code_attrs.length; i++) {
685 attrs_len += (code_attrs[i].getLength() + 6);
686 }
687 CodeException[] c_exc = getCodeExceptions();
688 int exc_len = c_exc.length * 8; // Every entry takes 8 bytes
689 Code code = null;
690 if ((il != null) && !isAbstract() && !isNative()) {
691 // Remove any stale code attribute
692 Attribute[] attributes = getAttributes();
693 for (int i = 0; i < attributes.length; i++) {
694 Attribute a = attributes[i];
695 if (a instanceof Code) {
696 removeAttribute(a);
697 }
698 }
699 code = new Code(cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code
700 2 + exc_len + // exceptions
701 2 + attrs_len, // attributes
702 max_stack, max_locals, byte_code, c_exc, code_attrs, cp.getConstantPool());
703 addAttribute(code);
704 }
705 addAnnotationsAsAttribute(cp);
706 addParameterAnnotationsAsAttribute(cp);
707 ExceptionTable et = null;
708 if (throws_vec.size() > 0) {
709 addAttribute(et = getExceptionTable(cp));
710 // Add `Exceptions' if there are "throws" clauses
711 }
712 Method m = new Method(access_flags, name_index, signature_index, getAttributes(), cp
713 .getConstantPool());
714 // Undo effects of adding attributes
715 if (lvt != null) {
716 removeCodeAttribute(lvt);
717 }
718 if (lnt != null) {
719 removeCodeAttribute(lnt);
720 }
721 if (code != null) {
722 removeAttribute(code);
723 }
724 if (et != null) {
725 removeAttribute(et);
726 }
727 return m;
728 }
729
730
731 /**
732 * Remove all NOPs from the instruction list (if possible) and update every
733 * object refering to them, i.e., branch instructions, local variables and
734 * exception handlers.
735 */
736 public void removeNOPs() {
737 if (il != null) {
738 InstructionHandle next;
739 /* Check branch instructions.
740 */
741 for (InstructionHandle ih = il.getStart(); ih != null; ih = next) {
742 next = ih.next;
743 if ((next != null) && (ih.getInstruction() instanceof NOP)) {
744 try {
745 il.delete(ih);
746 } catch (TargetLostException e) {
747 InstructionHandle[] targets = e.getTargets();
748 for (int i = 0; i < targets.length; i++) {
749 InstructionTargeter[] targeters = targets[i].getTargeters();
750 for (int j = 0; j < targeters.length; j++) {
751 targeters[j].updateTarget(targets[i], next);
752 }
753 }
754 }
755 }
756 }
757 }
758 }
759
760
761 /**
762 * Set maximum number of local variables.
763 */
764 public void setMaxLocals( int m ) {
765 max_locals = m;
766 }
767
768
769 public int getMaxLocals() {
770 return max_locals;
771 }
772
773
774 /**
775 * Set maximum stack size for this method.
776 */
777 public void setMaxStack( int m ) {
778 max_stack = m;
779 }
780
781
782 public int getMaxStack() {
783 return max_stack;
784 }
785
786
787 /** @return class that contains this method
788 */
789 public String getClassName() {
790 return class_name;
791 }
792
793
794 public void setClassName( String class_name ) {
795 this.class_name = class_name;
796 }
797
798
799 public void setReturnType( Type return_type ) {
800 setType(return_type);
801 }
802
803
804 public Type getReturnType() {
805 return getType();
806 }
807
808
809 public void setArgumentTypes( Type[] arg_types ) {
810 this.arg_types = arg_types;
811 }
812
813
814 public Type[] getArgumentTypes() {
815 return arg_types.clone();
816 }
817
818
819 public void setArgumentType( int i, Type type ) {
820 arg_types[i] = type;
821 }
822
823
824 public Type getArgumentType( int i ) {
825 return arg_types[i];
826 }
827
828
829 public void setArgumentNames( String[] arg_names ) {
830 this.arg_names = arg_names;
831 }
832
833
834 public String[] getArgumentNames() {
835 return arg_names.clone();
836 }
837
838
839 public void setArgumentName( int i, String name ) {
840 arg_names[i] = name;
841 }
842
843
844 public String getArgumentName( int i ) {
845 return arg_names[i];
846 }
847
848
849 public InstructionList getInstructionList() {
850 return il;
851 }
852
853
854 public void setInstructionList( InstructionList il ) {
855 this.il = il;
856 }
857
858
859 @Override
860 public String getSignature() {
861 return Type.getMethodSignature(type, arg_types);
862 }
863
864
865 /**
866 * Computes max. stack size by performing control flow analysis.
867 */
868 public void setMaxStack() {
869 if (il != null) {
870 max_stack = getMaxStack(cp, il, getExceptionHandlers());
871 } else {
872 max_stack = 0;
873 }
874 }
875
876
877 /**
878 * Compute maximum number of local variables.
879 */
880 public void setMaxLocals() {
881 if (il != null) {
882 int max = isStatic() ? 0 : 1;
883 if (arg_types != null) {
884 for (int i = 0; i < arg_types.length; i++) {
885 max += arg_types[i].getSize();
886 }
887 }
888 for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
889 Instruction ins = ih.getInstruction();
890 if ((ins instanceof LocalVariableInstruction) || (ins instanceof RET)
891 || (ins instanceof IINC)) {
892 int index = ((IndexedInstruction) ins).getIndex()
893 + ((TypedInstruction) ins).getType(cp).getSize();
894 if (index > max) {
895 max = index;
896 }
897 }
898 }
899 max_locals = max;
900 } else {
901 max_locals = 0;
902 }
903 }
904
905
906 /** Do not/Do produce attributes code attributesLineNumberTable and
907 * LocalVariableTable, like javac -O
908 */
909 public void stripAttributes( boolean flag ) {
910 strip_attributes = flag;
911 }
912
913 static final class BranchTarget {
914
915 InstructionHandle target;
916 int stackDepth;
917
918
919 BranchTarget(InstructionHandle target, int stackDepth) {
920 this.target = target;
921 this.stackDepth = stackDepth;
922 }
923 }
924
925 static final class BranchStack {
926
927 Stack<BranchTarget> branchTargets = new Stack<BranchTarget>();
928 Hashtable<InstructionHandle, BranchTarget> visitedTargets = new Hashtable<InstructionHandle, BranchTarget>();
929
930
931 public void push( InstructionHandle target, int stackDepth ) {
932 if (visited(target)) {
933 return;
934 }
935 branchTargets.push(visit(target, stackDepth));
936 }
937
938
939 public BranchTarget pop() {
940 if (!branchTargets.empty()) {
941 BranchTarget bt = branchTargets.pop();
942 return bt;
943 }
944 return null;
945 }
946
947
948 private final BranchTarget visit( InstructionHandle target, int stackDepth ) {
949 BranchTarget bt = new BranchTarget(target, stackDepth);
950 visitedTargets.put(target, bt);
951 return bt;
952 }
953
954
955 private final boolean visited( InstructionHandle target ) {
956 return (visitedTargets.get(target) != null);
957 }
958 }
959
960
961 /**
962 * Computes stack usage of an instruction list by performing control flow analysis.
963 *
964 * @return maximum stack depth used by method
965 */
966 public static int getMaxStack( ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et ) {
967 BranchStack branchTargets = new BranchStack();
968 /* Initially, populate the branch stack with the exception
969 * handlers, because these aren't (necessarily) branched to
970 * explicitly. in each case, the stack will have depth 1,
971 * containing the exception object.
972 */
973 for (int i = 0; i < et.length; i++) {
974 InstructionHandle handler_pc = et[i].getHandlerPC();
975 if (handler_pc != null) {
976 branchTargets.push(handler_pc, 1);
977 }
978 }
979 int stackDepth = 0, maxStackDepth = 0;
980 InstructionHandle ih = il.getStart();
981 while (ih != null) {
982 Instruction instruction = ih.getInstruction();
983 short opcode = instruction.getOpcode();
984 int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
985 stackDepth += delta;
986 if (stackDepth > maxStackDepth) {
987 maxStackDepth = stackDepth;
988 }
989 // choose the next instruction based on whether current is a branch.
990 if (instruction instanceof BranchInstruction) {
991 BranchInstruction branch = (BranchInstruction) instruction;
992 if (instruction instanceof Select) {
993 // explore all of the select's targets. the default target is handled below.
994 Select select = (Select) branch;
995 InstructionHandle[] targets = select.getTargets();
996 for (int i = 0; i < targets.length; i++) {
997 branchTargets.push(targets[i], stackDepth);
998 }
999 // nothing to fall through to.
1000 ih = null;
1001 } else if (!(branch instanceof IfInstruction)) {
1002 // if an instruction that comes back to following PC,
1003 // push next instruction, with stack depth reduced by 1.
1004 if (opcode == Constants.JSR || opcode == Constants.JSR_W) {
1005 branchTargets.push(ih.getNext(), stackDepth - 1);
1006 }
1007 ih = null;
1008 }
1009 // for all branches, the target of the branch is pushed on the branch stack.
1010 // conditional branches have a fall through case, selects don't, and
1011 // jsr/jsr_w return to the next instruction.
1012 branchTargets.push(branch.getTarget(), stackDepth);
1013 } else {
1014 // check for instructions that terminate the method.
1015 if (opcode == Constants.ATHROW || opcode == Constants.RET
1016 || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
1017 ih = null;
1018 }
1019 }
1020 // normal case, go to the next instruction.
1021 if (ih != null) {
1022 ih = ih.getNext();
1023 }
1024 // if we have no more instructions, see if there are any deferred branches to explore.
1025 if (ih == null) {
1026 BranchTarget bt = branchTargets.pop();
1027 if (bt != null) {
1028 ih = bt.target;
1029 stackDepth = bt.stackDepth;
1030 }
1031 }
1032 }
1033 return maxStackDepth;
1034 }
1035
1036 private List<MethodObserver> observers;
1037
1038
1039 /** Add observer for this object.
1040 */
1041 public void addObserver( MethodObserver o ) {
1042 if (observers == null) {
1043 observers = new ArrayList<MethodObserver>();
1044 }
1045 observers.add(o);
1046 }
1047
1048
1049 /** Remove observer for this object.
1050 */
1051 public void removeObserver( MethodObserver o ) {
1052 if (observers != null) {
1053 observers.remove(o);
1054 }
1055 }
1056
1057
1058 /** Call notify() method on all observers. This method is not called
1059 * automatically whenever the state has changed, but has to be
1060 * called by the user after he has finished editing the object.
1061 */
1062 public void update() {
1063 if (observers != null) {
1064 for (MethodObserver observer : observers) {
1065 observer.notify(this);
1066 }
1067 }
1068 }
1069
1070
1071 /**
1072 * Return string representation close to declaration format,
1073 * `public static void main(String[]) throws IOException', e.g.
1074 *
1075 * @return String representation of the method.
1076 */
1077 @Override
1078 public final String toString() {
1079 String access = Utility.accessToString(access_flags);
1080 String signature = Type.getMethodSignature(type, arg_types);
1081 signature = Utility.methodSignatureToString(signature, name, access, true,
1082 getLocalVariableTable(cp));
1083 StringBuilder buf = new StringBuilder(signature);
1084 for (int i = 0; i < getAttributes().length; i++) {
1085 Attribute a = getAttributes()[i];
1086 if (!((a instanceof Code) || (a instanceof ExceptionTable))) {
1087 buf.append(" [").append(a.toString()).append("]");
1088 }
1089 }
1090
1091 if (throws_vec.size() > 0) {
1092 for (String throwsDescriptor : throws_vec) {
1093 buf.append("\n\t\tthrows ").append(throwsDescriptor);
1094 }
1095 }
1096 return buf.toString();
1097 }
1098
1099
1100 /** @return deep copy of this method
1101 */
1102 public MethodGen copy( String class_name, ConstantPoolGen cp ) {
1103 Method m = ((MethodGen) clone()).getMethod();
1104 MethodGen mg = new MethodGen(m, class_name, this.cp);
1105 if (this.cp != cp) {
1106 mg.setConstantPool(cp);
1107 mg.getInstructionList().replaceConstantPool(this.cp, cp);
1108 }
1109 return mg;
1110 }
1111
1112 //J5TODO: Should param_annotations be an array of arrays? Rather than an array of lists, this
1113 // is more likely to suggest to the caller it is readonly (which a List does not).
1114 /**
1115 * Return a list of AnnotationGen objects representing parameter annotations
1116 */
1117 public List<AnnotationEntryGen> getAnnotationsOnParameter(int i) {
1118 ensureExistingParameterAnnotationsUnpacked();
1119 if (!hasParameterAnnotations || i>arg_types.length) return null;
1120 return param_annotations[i];
1121 }
1122
1123 /**
1124 * Goes through the attributes on the method and identifies any that are
1125 * RuntimeParameterAnnotations, extracting their contents and storing them
1126 * as parameter annotations. There are two kinds of parameter annotation -
1127 * visible and invisible. Once they have been unpacked, these attributes are
1128 * deleted. (The annotations will be rebuilt as attributes when someone
1129 * builds a Method object out of this MethodGen object).
1130 */
1131 private void ensureExistingParameterAnnotationsUnpacked()
1132 {
1133 if (haveUnpackedParameterAnnotations)
1134 return;
1135 // Find attributes that contain parameter annotation data
1136 Attribute[] attrs = getAttributes();
1137 ParameterAnnotations paramAnnVisAttr = null;
1138 ParameterAnnotations paramAnnInvisAttr = null;
1139 for (int i = 0; i < attrs.length; i++)
1140 {
1141 Attribute attribute = attrs[i];
1142 if (attribute instanceof ParameterAnnotations)
1143 {
1144 // Initialize param_annotations
1145 if (!hasParameterAnnotations)
1146 {
1147 param_annotations = new List[arg_types.length];
1148 for (int j = 0; j < arg_types.length; j++)
1149 param_annotations[j] = new ArrayList<AnnotationEntryGen>();
1150 }
1151 hasParameterAnnotations = true;
1152 ParameterAnnotations rpa = (ParameterAnnotations) attribute;
1153 if (rpa instanceof RuntimeVisibleParameterAnnotations)
1154 paramAnnVisAttr = rpa;
1155 else
1156 paramAnnInvisAttr = rpa;
1157 for (int j = 0; j < arg_types.length; j++)
1158 {
1159 // This returns Annotation[] ...
1160 ParameterAnnotationEntry immutableArray = rpa
1161 .getParameterAnnotationEntries()[j];
1162 // ... which needs transforming into an AnnotationGen[] ...
1163 List<AnnotationEntryGen> mutable = makeMutableVersion(immutableArray.getAnnotationEntries());
1164 // ... then add these to any we already know about
1165 param_annotations[j].addAll(mutable);
1166 }
1167 }
1168 }
1169 if (paramAnnVisAttr != null)
1170 removeAttribute(paramAnnVisAttr);
1171 if (paramAnnInvisAttr != null)
1172 removeAttribute(paramAnnInvisAttr);
1173 haveUnpackedParameterAnnotations = true;
1174 }
1175
1176 private List<AnnotationEntryGen> makeMutableVersion(AnnotationEntry[] mutableArray)
1177 {
1178 List<AnnotationEntryGen> result = new ArrayList<AnnotationEntryGen>();
1179 for (int i = 0; i < mutableArray.length; i++)
1180 {
1181 result.add(new AnnotationEntryGen(mutableArray[i], getConstantPool(),
1182 false));
1183 }
1184 return result;
1185 }
1186
1187 public void addParameterAnnotation(int parameterIndex,
1188 AnnotationEntryGen annotation)
1189 {
1190 ensureExistingParameterAnnotationsUnpacked();
1191 if (!hasParameterAnnotations)
1192 {
1193 param_annotations = new List[arg_types.length];
1194 hasParameterAnnotations = true;
1195 }
1196 List<AnnotationEntryGen> existingAnnotations = param_annotations[parameterIndex];
1197 if (existingAnnotations != null)
1198 {
1199 existingAnnotations.add(annotation);
1200 }
1201 else
1202 {
1203 List<AnnotationEntryGen> l = new ArrayList<AnnotationEntryGen>();
1204 l.add(annotation);
1205 param_annotations[parameterIndex] = l;
1206 }
1207 }
1208
1209
1210
1211
1212 /**
1213 * @return Comparison strategy object
1214 */
1215 public static BCELComparator getComparator() {
1216 return _cmp;
1217 }
1218
1219
1220 /**
1221 * @param comparator Comparison strategy object
1222 */
1223 public static void setComparator( BCELComparator comparator ) {
1224 _cmp = comparator;
1225 }
1226
1227
1228 /**
1229 * Return value as defined by given BCELComparator strategy.
1230 * By default two MethodGen objects are said to be equal when
1231 * their names and signatures are equal.
1232 *
1233 * @see java.lang.Object#equals(java.lang.Object)
1234 */
1235 @Override
1236 public boolean equals( Object obj ) {
1237 return _cmp.equals(this, obj);
1238 }
1239
1240
1241 /**
1242 * Return value as defined by given BCELComparator strategy.
1243 * By default return the hashcode of the method's name XOR signature.
1244 *
1245 * @see java.lang.Object#hashCode()
1246 */
1247 @Override
1248 public int hashCode() {
1249 return _cmp.hashCode(this);
1250 }
1251 }