Source code: com/techtrader/modules/tools/bytecode/BCMethod.java
1 package com.techtrader.modules.tools.bytecode;
2
3
4 import java.io.IOException;
5 import java.io.DataInput;
6 import java.io.DataOutput;
7
8 import com.techtrader.modules.tools.bytecode.lowlevel.ConstantPool;
9 import com.techtrader.modules.tools.bytecode.lowlevel.MethodEntry;
10 import com.techtrader.modules.tools.bytecode.lowlevel.InterfaceMethodEntry;
11 import com.techtrader.modules.tools.bytecode.visitor.BCVisitor;
12
13
14 /**
15 * Representation of a bytecode method of a class; a BCMethod can only
16 * be obtained from a BCClass. Note that this class has method to manipulate
17 * its declared excptions and code for convenience only; they can be
18 * manipulated directly through the ATTR_EXCEPTIONS and ATTR_CODE attributes.
19 *
20 * @author Abe White
21 */
22 public class BCMethod
23 extends BCEntity
24 implements Constants
25 {
26 private BCClass _owner = null;
27 private int _access = ACCESS_PUBLIC;
28 private int _nameIndex = 0;
29 private int _descriptorIndex = 0;
30
31
32 /**
33 * Protected constructor.
34 */
35 protected BCMethod (BCClass owner)
36 {
37 _owner = owner;
38 }
39
40
41 /**
42 * Used when this field is deleted from its class.
43 */
44 protected void invalidate ()
45 {
46 _owner = null;
47 }
48
49
50 /**
51 * Get the BCClass that owns this method.
52 */
53 public BCClass getOwner ()
54 {
55 return _owner;
56 }
57
58
59 /**
60 * Return the access flags for this class as a bit array of
61 * ACCESS_XXX constants. This can be used to transfer access flags
62 * between methods without getting/setting each possible access flag.
63 */
64 public int getAccessFlags ()
65 {
66 return _access;
67 }
68
69
70 /**
71 * Set the access flags for this class as a bit array of
72 * ACCESS_XXX constants. This can be used to transfer access flags
73 * between methods without getting/setting each possible access flag.
74 */
75 public void setAccessFlags (int access)
76 {
77 _access = access;
78 }
79
80
81 /**
82 * Manipulate the method access flags.
83 */
84 public boolean isPublic ()
85 {
86 return BCHelper.hasFlag (_access, ACCESS_PUBLIC);
87 }
88
89
90 /**
91 * Manipulate the method access flags.
92 */
93 public void makePublic ()
94 {
95 _access = BCHelper.setFlag (_access, ACCESS_PUBLIC, true);
96 _access = BCHelper.setFlag (_access, ACCESS_PRIVATE, false);
97 _access = BCHelper.setFlag (_access, ACCESS_PROTECTED, false);
98 }
99
100
101 /**
102 * Manipulate the method access flags.
103 */
104 public boolean isProtected ()
105 {
106 return BCHelper.hasFlag (_access, ACCESS_PROTECTED);
107 }
108
109
110 /**
111 * Manipulate the method access flags.
112 */
113 public void makeProtected ()
114 {
115 _access = BCHelper.setFlag (_access, ACCESS_PUBLIC, false);
116 _access = BCHelper.setFlag (_access, ACCESS_PRIVATE, false);
117 _access = BCHelper.setFlag (_access, ACCESS_PROTECTED, true);
118 }
119
120
121 /**
122 * Manipulate the method access flags.
123 */
124 public boolean isPrivate ()
125 {
126 return BCHelper.hasFlag (_access, ACCESS_PRIVATE);
127 }
128
129
130 /**
131 * Manipulate the method access flags.
132 */
133 public void makePrivate ()
134 {
135 _access = BCHelper.setFlag (_access, ACCESS_PUBLIC, false);
136 _access = BCHelper.setFlag (_access, ACCESS_PRIVATE, true);
137 _access = BCHelper.setFlag (_access, ACCESS_PROTECTED, false);
138 }
139
140
141 /**
142 * Manipulate the method access flags.
143 */
144 public boolean isPackage ()
145 {
146 boolean hasAccess = false;
147 hasAccess = hasAccess || BCHelper.hasFlag (_access, ACCESS_PRIVATE);
148 hasAccess = hasAccess || BCHelper.hasFlag (_access, ACCESS_PROTECTED);
149 hasAccess = hasAccess || BCHelper.hasFlag (_access, ACCESS_PUBLIC);
150
151 return !hasAccess;
152 }
153
154
155 /**
156 * Manipulate the method access flags.
157 */
158 public void makePackage ()
159 {
160 _access = BCHelper.setFlag (_access, ACCESS_PUBLIC, false);
161 _access = BCHelper.setFlag (_access, ACCESS_PRIVATE, false);
162 _access = BCHelper.setFlag (_access, ACCESS_PROTECTED, false);
163 }
164
165
166 /**
167 * Manipulate the method access flags.
168 */
169 public boolean isFinal ()
170 {
171 return BCHelper.hasFlag (_access, ACCESS_FINAL);
172 }
173
174
175 /**
176 * Manipulate the method access flags.
177 */
178 public void setFinal (boolean on)
179 {
180 _access = BCHelper.setFlag (_access, ACCESS_FINAL, on);
181 }
182
183
184 /**
185 * Manipulate the method access flags.
186 */
187 public boolean isStatic ()
188 {
189 return BCHelper.hasFlag (_access, ACCESS_STATIC);
190 }
191
192
193 /**
194 * Manipulate the method access flags.
195 */
196 public void setStatic (boolean on)
197 {
198 _access = BCHelper.setFlag (_access, ACCESS_STATIC, on);
199 }
200
201
202 /**
203 * Manipulate the method access flags.
204 */
205 public boolean isSynchronized ()
206 {
207 return BCHelper.hasFlag (_access, ACCESS_SYNCHRONIZED);
208 }
209
210
211 /**
212 * Manipulate the method access flags.
213 */
214 public void setSynchronized (boolean on)
215 {
216 _access = BCHelper.setFlag (_access, ACCESS_SYNCHRONIZED, on);
217 }
218
219
220 /**
221 * Manipulate the method access flags.
222 */
223 public boolean isNative ()
224 {
225 return BCHelper.hasFlag (_access, ACCESS_NATIVE);
226 }
227
228
229 /**
230 * Manipulate the method access flags.
231 */
232 public void setNative (boolean on)
233 {
234 _access = BCHelper.setFlag (_access, ACCESS_NATIVE, on);
235 }
236
237
238 /**
239 * Manipulate the method access flags.
240 */
241 public boolean isAbstract ()
242 {
243 return BCHelper.hasFlag (_access, ACCESS_ABSTRACT);
244 }
245
246
247 /**
248 * Manipulate the method access flags.
249 */
250 public void setAbstract (boolean on)
251 {
252 _access = BCHelper.setFlag (_access, ACCESS_ABSTRACT, on);
253 }
254
255
256 /**
257 * Manipulate the method access flags.
258 */
259 public boolean isStrict ()
260 {
261 return BCHelper.hasFlag (_access, ACCESS_STRICT);
262 }
263
264
265 /**
266 * Manipulate the method access flags.
267 */
268 public void setStrict (boolean on)
269 {
270 _access = BCHelper.setFlag (_access, ACCESS_STRICT, on);
271 }
272
273
274 /**
275 * Get the index in the constant pool of the UTF entry holding the name
276 * of this method.
277 */
278 public int getNameIndex ()
279 {
280 return _nameIndex;
281 }
282
283
284 /**
285 * Set the index in the constant pool of the UTF entry holding the name
286 * of this method.
287 */
288 public void setNameIndex (int index)
289 {
290 _nameIndex = index;
291 }
292
293
294 /**
295 * Get the index in the constant pool of the UTF entry holding the
296 * descriptor of this method.
297 */
298 public int getDescriptorIndex ()
299 {
300 return _descriptorIndex;
301 }
302
303
304 /**
305 * Set the index in the constant pool of the UTF entry holding the
306 * descriptor of this method.
307 */
308 public void setDescriptorIndex (int index)
309 {
310 _descriptorIndex = index;
311 }
312
313
314 /**
315 * Get the name of this method.
316 */
317 public String getName ()
318 {
319 return getPool ().getUTF (_nameIndex);
320 }
321
322
323 /**
324 * Set the name of this method.
325 */
326 public void setName (String name)
327 {
328 String origName = getName ();
329
330 // reset the name
331 _nameIndex = getPool ().setUTF (0, name);
332
333 // find the ComplexEntry matching this field, if any
334 String internalDesc = getPool ().getUTF (_descriptorIndex);
335 String internalOwner = getPool ().getClassName (_owner.getIndex ());
336
337 Class entry = MethodEntry.class;
338 if (_owner.isInterface ())
339 entry = InterfaceMethodEntry.class;
340
341 int index = getPool ().getComplexIndex
342 (origName, internalDesc, internalOwner, entry);
343
344 // change the ComplexEntry to match the new name; this is dones so
345 // that refs to the method in code will still be valid after the name
346 // change, without changing any other constants that happened to match
347 // the old method name
348 if (index != 0)
349 getPool ().setComplex (index, name,
350 internalDesc, internalOwner, entry);
351 }
352
353
354
355 /**
356 * Get the name of the class type returned by this method.
357 */
358 public String getReturnTypeName ()
359 {
360 return BCHelper.getExternalForm (BCHelper.getReturnType
361 (getPool ().getUTF (_descriptorIndex)), true);
362 }
363
364
365 /**
366 * Get the Class of the return type of this method.
367 */
368 public Class getReturnType ()
369 throws ClassNotFoundException
370 {
371 return BCHelper.classForName (BCHelper.getReturnType
372 (getPool ().getUTF (_descriptorIndex)));
373 }
374
375
376 /**
377 * Get the names of all the parameter types for this method.
378 */
379 public String[] getParamTypeNames ()
380 {
381 // get the parameter types from the descriptor
382 String[] params = BCHelper.getParamTypes
383 (getPool ().getUTF (_descriptorIndex));
384
385 // convert them to external form
386 for (int i = 0; i < params.length; i++)
387 params[i] = BCHelper.getExternalForm (params[i], true);
388
389 return params;
390 }
391
392
393 /**
394 * Get the types of parameters this method takes.
395 */
396 public Class[] getParamTypes ()
397 throws ClassNotFoundException
398 {
399 // get the parameter types from the descriptor
400 String[] params = BCHelper.getParamTypes
401 (getPool ().getUTF (_descriptorIndex));
402
403 // convert them
404 Class[] externalParams = new Class[params.length];
405 for (int i = 0; i < params.length; i++)
406 externalParams[i] = BCHelper.classForName (params[i]);
407
408 return externalParams;
409 }
410
411
412 /**
413 * Set the return type of this method.
414 */
415 public void setReturnTypeName (String name)
416 {
417 setDescriptorInternal (BCHelper.getInternalForm (name, true),
418 BCHelper.getParamTypes (getPool ().getUTF (_descriptorIndex)));
419 }
420
421
422 /**
423 * Set the return type of this method.
424 */
425 public void setReturnType (Class type)
426 {
427 setReturnTypeName (type.getName ());
428 }
429
430
431 /**
432 * Set the parameter types of this method.
433 */
434 public void setParamTypeNames (String[] names)
435 {
436 String returnName = BCHelper.getReturnType
437 (getPool ().getUTF (_descriptorIndex));
438 if (returnName.length () == 0)
439 returnName = "V";
440
441 if (names == null)
442 names = new String[0];
443 else
444 for (int i = 0; i < names.length; i++)
445 names[i] = BCHelper.getInternalForm (names[i], true);
446
447 setDescriptorInternal (returnName, names);
448 }
449
450
451 /**
452 * Set the parameter type of this method.
453 */
454 public void setParamTypes (Class[] types)
455 {
456 if (types == null)
457 setParamTypeNames (null);
458
459 String[] names = new String[types.length];
460 for (int i = 0; i < types.length; i++)
461 names[i] = types[i].getName ();
462
463 setParamTypeNames (names);
464 }
465
466
467 /**
468 * Add a parameter type to this method.
469 */
470 public void addParamTypeName (String name)
471 {
472 String[] params = getParamTypeNames ();
473 String[] newParams = new String[params.length + 1];
474
475 for (int i = 0; i < params.length; i++)
476 newParams[i] = params[i];
477 newParams[params.length] = name;
478
479 setParamTypeNames (newParams);
480 }
481
482
483 /**
484 * Remove a parameter from this method.
485 */
486 public boolean removeParamTypeName (String name)
487 {
488 String[] names = getParamTypeNames ();
489 if (names.length == 0)
490 return false;
491
492 String[] newNames = new String[names.length - 1];
493
494 boolean skip = false;
495 for (int i = 0, count = 0; count < newNames.length; i++)
496 {
497 if (names[i].equals (name))
498 skip = true;
499 else
500 newNames[count++] = names[i];
501 }
502
503 if (!skip && newNames.length > 0)
504 return false;
505
506 setParamTypeNames (newNames);
507 return true;
508 }
509
510
511 /**
512 * Remove a parameter from this method.
513 */
514 public boolean removeParamType (Class type)
515 {
516 return removeParamTypeName (type.getName ());
517 }
518
519
520 /**
521 * Add a parameter type to this method.
522 */
523 public void addParamType (Class type)
524 {
525 addParamTypeName (type.getName ());
526 }
527
528
529 /**
530 * Set this method descriptor; using this method is much more
531 * efficient than setting the return type and param types separately.
532 */
533 public void setDescriptor (String returnType, String[] paramTypes)
534 {
535 returnType = BCHelper.getInternalForm (returnType, true);
536 if (paramTypes == null)
537 paramTypes = new String[0];
538 else
539 for (int i = 0; i < paramTypes.length; i++)
540 paramTypes[i] = BCHelper.getInternalForm (paramTypes[i], true);
541
542 setDescriptorInternal (returnType, paramTypes);
543 }
544
545
546 /**
547 * Set this method descriptor; using this method is much more
548 * efficient than setting the return type and param types separately.
549 */
550 public void setDescriptor (Class returnType, Class[] paramTypes)
551 {
552 String returnStr = BCHelper.getInternalForm
553 (returnType.getName (), true);
554
555 String[] paramStr;
556 if (paramTypes == null)
557 paramStr = new String[0];
558 else
559 {
560 paramStr = new String[paramTypes.length];
561 for (int i = 0; i < paramTypes.length; i++)
562 paramStr[i] = BCHelper.getInternalForm
563 (paramTypes[i].getName (), true);
564 }
565
566 setDescriptorInternal (returnStr, paramStr);
567 }
568
569
570 /**
571 * Internal helper method to set the descriptor of this method,
572 * using the internal form of the method return type and params
573 */
574 private void setDescriptorInternal (String returnType, String[] paramTypes)
575 {
576 String origDesc = getPool ().getUTF (_descriptorIndex);
577
578 // reset the descriptor
579 String internalDesc = BCHelper.getDescriptor (returnType, paramTypes);
580 _descriptorIndex = getPool ().setUTF (0, internalDesc);
581
582 // find the ComplexEntry matching this method, if any
583 String internalOwner = getPool ().getClassName (_owner.getIndex ());
584
585 Class entry = MethodEntry.class;
586 if (_owner.isInterface ())
587 entry = InterfaceMethodEntry.class;
588
589 int index = getPool ().getComplexIndex
590 (getName (), origDesc, internalOwner, entry);
591
592 // change the ComplexEntry to match the new type; this is dones so
593 // that refs to the method in code will still be valid after the type
594 // change, without changing any other constants that happened to match
595 // the old method type
596 if (index != 0)
597 getPool ().setComplex (index, getName (),
598 internalDesc, internalOwner, entry);
599 }
600
601
602 /**
603 * Get the exception types thrown by this method.
604 */
605 public String[] getExceptionTypeNames ()
606 {
607 ExceptionsAttribute exceptionTable = (ExceptionsAttribute)
608 getAttribute (ATTR_EXCEPTIONS);
609 if (exceptionTable == null)
610 return new String[0];
611
612 return exceptionTable.getExceptionTypeNames ();
613 }
614
615
616 /**
617 * Get the exception types thrown by this method.
618 */
619 public Class[] getExceptionTypes ()
620 throws ClassNotFoundException
621 {
622 ExceptionsAttribute exceptionTable = (ExceptionsAttribute)
623 getAttribute (ATTR_EXCEPTIONS);
624 if (exceptionTable == null)
625 return new Class[0];
626
627 return exceptionTable.getExceptionTypes ();
628 }
629
630
631 /**
632 * Remove all declared exceptions from this method.
633 */
634 public void clearExceptionTypes ()
635 {
636 removeAttribute (ATTR_EXCEPTIONS);
637 }
638
639
640 /**
641 * Remove the given exception type from those that this method
642 * declares in its throws clause.
643 */
644 public boolean removeExceptionTypeName (String name)
645 {
646 ExceptionsAttribute exceptionTable = (ExceptionsAttribute)
647 getAttribute (ATTR_EXCEPTIONS);
648 if (exceptionTable == null)
649 return false;
650
651 return exceptionTable.removeExceptionTypeName (name);
652 }
653
654
655 /**
656 * Remove the given exception type from those that this method
657 * declares in its throws clause.
658 */
659 public boolean removeExceptionType (Class type)
660 {
661 return removeExceptionTypeName (type.getName ());
662 }
663
664
665 /**
666 * Set the exception types for this method.
667 */
668 public void setExceptionTypeNames (String[] types)
669 {
670 if (types == null || types.length == 0)
671 removeAttribute (ATTR_EXCEPTIONS);
672 else
673 {
674 ExceptionsAttribute exceptionTable = (ExceptionsAttribute)
675 getAttribute (ATTR_EXCEPTIONS);
676 if (exceptionTable == null)
677 exceptionTable = (ExceptionsAttribute) addAttribute
678 (ATTR_EXCEPTIONS);
679
680 exceptionTable.setExceptionTypeNames (types);
681 }
682 }
683
684
685 /**
686 * Set the exception types for this method.
687 */
688 public void setExceptionTypes (Class[] types)
689 {
690 if (types == null)
691 setExceptionTypeNames ((String[]) null);
692 else
693 {
694 String[] names = new String[types.length];
695 for (int i = 0; i < names.length; i++)
696 names[i] = types[i].getName ();
697
698 setExceptionTypeNames (names);
699 }
700 }
701
702
703 /**
704 * Add an exception to those declared by this method.
705 */
706 public void addExceptionTypeName (String name)
707 {
708 ExceptionsAttribute exceptionTable = (ExceptionsAttribute)
709 getAttribute (ATTR_EXCEPTIONS);
710 if (exceptionTable == null)
711 exceptionTable = (ExceptionsAttribute) addAttribute
712 (ATTR_EXCEPTIONS);
713
714 exceptionTable.addExceptionTypeName (name);
715 }
716
717
718 /**
719 * Add an exception to those declared by this method.
720 */
721 public void addExceptionType (Class type)
722 {
723 addExceptionTypeName (type.getName ());
724 }
725
726
727 /**
728 * Get the code for this method; returns null if none.
729 * Note that each time the code is fetched, the position of the code
730 * iterator is reset to before the first opcode.
731 */
732 public Code getCode ()
733 {
734 Code code = (Code) getAttribute (ATTR_CODE);
735 if (code != null)
736 code.beforeFirst ();
737
738 return code;
739 }
740
741
742 /**
743 * Add a code block to this method; replaces the old block if it exists.
744 */
745 public Code addCode ()
746 {
747 removeAttribute (ATTR_CODE);
748 return (Code) addAttribute (ATTR_CODE);
749 }
750
751
752 /**
753 * Remove the code from this method; note that this actually removes the
754 * Code attribute completely; if you want to make an empty code block use
755 * the Code.clear() method.
756 */
757 public boolean removeCode ()
758 {
759 return removeAttribute (ATTR_CODE);
760 }
761
762
763 /**
764 * Import a code block from another method. The given method can be of
765 * this class or a different one. This will cause the code of this method
766 * to become an exact duplicate of the given code block.
767 */
768 public Code importCode (Code code)
769 {
770 removeAttribute (ATTR_CODE);
771 return (Code) importAttribute (code);
772 }
773
774
775 /**
776 * Get the class constant pool; this method delegates to the
777 * owning class.
778 */
779 public ConstantPool getPool ()
780 {
781 return getOwner ().getPool ();
782 }
783
784
785 protected void readData (DataInput in)
786 throws IOException
787 {
788 setAccessFlags (in.readUnsignedShort ());
789 setNameIndex (in.readUnsignedShort ());
790 setDescriptorIndex (in.readUnsignedShort ());
791
792 readAttributes (in);
793 }
794
795
796 protected void writeData (DataOutput out)
797 throws IOException
798 {
799 out.writeShort (getAccessFlags ());
800 out.writeShort (getNameIndex ());
801 out.writeShort (getDescriptorIndex ());
802
803 writeAttributes (out);
804 }
805
806
807 public void acceptVisit (BCVisitor visit)
808 {
809 visit.enterBCMethod (this);
810 visitAttributes (visit);
811 visit.exitBCMethod (this);
812 }
813 }