KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > alt > jiapi > reflect > JiapiMethod


1 /*
2  * Copyright (C) 2001 Mika Riekkinen, Joni Suominen
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18
19 package alt.jiapi.reflect;
20
21 import java.lang.reflect.Modifier JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.LinkedList JavaDoc;
25
26 import org.apache.log4j.Category;
27
28 import alt.jiapi.Runtime;
29
30 import alt.jiapi.file.Attribute;
31 import alt.jiapi.file.ClassFile;
32 import alt.jiapi.file.CodeAttribute;
33 import alt.jiapi.file.ConstantPool;
34 import alt.jiapi.file.ExceptionsAttribute;
35 import alt.jiapi.file.LineNumberTableAttribute;
36 import alt.jiapi.file.LocalVariableTableAttribute;
37 import alt.jiapi.file.Method;
38 import alt.jiapi.file.SyntheticAttribute;
39
40 import alt.jiapi.reflect.instruction.Opcodes;
41
42 /*
43  * NOTE:
44  * When instructions, exception table, etc. are modified,
45  * make sure that update() method is called when finished.
46  * This method call syncs changes back to alt.jiapi.file
47  * package.
48  * Currently, JiapiClass.getByteCode() triggers update()
49  * method.
50  */

51
52 /**
53  * This class represents a Method.
54  *
55  * @author Mika Riekkinen
56  * @author Joni Suominen
57  * @version $Revision: 1.34 $ $Date: 2005/06/13 10:28:27 $
58  */

59 public class JiapiMethod {
60     private static Category log = Runtime.getLogCategory(JiapiMethod.class);
61
62     private Method method;
63     private Signature signature;
64     private JiapiClass declaringClass;
65
66     private InstructionList instructions;
67     private TryBlock[] tryBlocks = null;
68
69     private List JavaDoc lineNumberTable;
70     private List JavaDoc exceptionTable;
71
72
73     /**
74      * Constructor for JiapiMethod.
75      */

76     public JiapiMethod(Method m) {
77         this.method = m;
78         this.signature = new Signature(m.getDescriptor());
79     }
80
81
82
83     /**
84      * Get the name of this Method.
85      *
86      * @return name of the method
87      */

88     public String JavaDoc getName() {
89         return method.getName();
90     }
91
92     /**
93      * Gets modifiers of this Method.
94      *
95      * @return Modifiers of this method
96      * @see java.lang.reflect.Modifier
97      */

98     public int getModifiers() {
99         return method.getAccessFlags();
100     }
101
102 // public abstract setModifiers(int modifiers);
103

104     /**
105      * Gets the return type of a method.
106      *
107      * @return the return type of a method
108      */

109     public String JavaDoc getReturnType() {
110         return signature.getReturnType();
111     }
112
113     /**
114      * Gets the signature of this method.
115      *
116      * @return method signature
117      */

118     public Signature getSignature() {
119         return signature;
120     }
121
122
123     /**
124      * Get the InstructionFactory.
125      */

126     public InstructionFactory getInstructionFactory() {
127         return new InstructionFactory(method.getConstantPool());
128     }
129
130
131     /**
132      * Get an InstructionList, that represents a byte-code of this method.
133      *
134      * @return InstructionList
135      */

136     public InstructionList getInstructionList() {
137         CodeAttribute ca = null;
138         if (instructions == null) {
139             ca = (CodeAttribute)getAttribute("Code");
140
141             if (ca == null) {
142                 return null;
143             }
144
145             byte[] byteCode = ca.getByteCode();
146             
147             instructions = new InstructionList(byteCode,
148                                                method.getConstantPool());
149             instructions.setDeclaringMethod(this);
150
151             if (true/* configurable by jiapi.properties */) {
152                 // create private representation of
153
// line number table, so that we can update
154
// its offsets, when class is instrumented
155
lineNumberTable = new LinkedList JavaDoc();
156                 
157                 LineNumberTableAttribute lnta = (LineNumberTableAttribute)ca.getAttribute(LineNumberTableAttribute.ATTRIBUTE_NAME);
158                 
159                 if (lnta != null) {
160                     List JavaDoc entries = lnta.getEntries();
161                     Iterator JavaDoc i = entries.iterator();
162                     while(i.hasNext()) {
163                         LineNumberTableAttribute.Entry entry =
164                             (LineNumberTableAttribute.Entry)i.next();
165                         
166                         Instruction ins =
167                             instructions.instructionAtOffset(entry.getStartPc());
168                         
169                         lineNumberTable.add(new LNTableEntry(entry, ins));
170                     }
171                 }
172                 else {
173                     //System.out.println("ERROR: could not get line number table");
174
}
175             }
176
177
178             this.exceptionTable = new LinkedList JavaDoc();
179             List JavaDoc eTable = ca.getExceptionTable();
180             
181             if (eTable != null) {
182                 Iterator JavaDoc i = eTable.iterator();
183                 while(i.hasNext()) {
184                     CodeAttribute.ExceptionTableEntry entry =
185                         (CodeAttribute.ExceptionTableEntry)i.next();
186                     
187                     Instruction start =
188                         instructions.instructionAtOffset(entry.getStartPc());
189                     Instruction end =
190                         instructions.instructionAtOffset(entry.getEndPc());
191                     Instruction handler =
192                         instructions.instructionAtOffset(entry.getHandlerPc());
193                     
194                     this.exceptionTable.add(new ETEntry(entry, start, end,
195                                                    handler));
196                 }
197             }
198             else {
199                 //System.out.println("ERROR: could not get exception table");
200
}
201         }
202         
203         return instructions;
204     }
205
206
207     /**
208      * Get an array of try blocks within this method.
209      * If a physical try blocks are constructed so, that one block
210      * is inside another one, they are both returned separately in
211      * TryBlock array. It is then the responsibility of the developer
212      * to check whether or not try blocks have this kind of a relationship.
213      *
214      * @return an array of TryBlocks. If this method has no try blocks,
215      * an Array of length 0 is returned.
216      */

217     TryBlock[] getTryBlocks() {
218         return null;
219     }
220
221
222     // Package protected for now!!!
223
void addTryBlock(InstructionList tryBlock, String JavaDoc exceptionName,
224                      Instruction handlerStart) {
225     }
226     // Alternative:
227
void addTryBlock(TryBlock b) {
228     }
229
230     /**
231      * Gets the class that declared this JiapiMethod.
232      *
233      * @return a JiapiClass which declares this method
234      */

235     public JiapiClass getDeclaringClass() {
236         return declaringClass;
237     }
238
239     /**
240      * Sets the declaring class.
241      *
242      * @param declaringClass A JiapiClass, that declares this JiapiField
243      */

244     void setDeclaringClass(JiapiClass declaringClass) {
245         this.declaringClass = declaringClass;
246     }
247     
248
249     /**
250      * Gets the parameter types in this method's signature. Each
251      * of the parameters are loaded with the same Loader as
252      * the declaring class was loaded with.
253      *
254      * @return an array of parameter types
255      * @exception ClassNotFoundException is thrown, if one of the types
256      * could not be loaded
257      * @see #getParameterTypeNames()
258      */

259     public JiapiClass[] getParameterTypes() throws ClassNotFoundException JavaDoc {
260         String JavaDoc[] paramNames = getParameterTypeNames();
261         JiapiClass[] types = new JiapiClass[paramNames.length];
262         Loader loader = getDeclaringClass().getLoader();
263
264         int i = 0;
265         try {
266             for (i = 0; i < paramNames.length; i++) {
267                 types[i] = loader.loadClass(paramNames[i]);
268             }
269         }
270         catch(java.io.IOException JavaDoc ioe) {
271             throw new ClassNotFoundException JavaDoc(paramNames[i]);
272         }
273
274         return types;
275     }
276
277     /**
278      * Gets the names of parameter types in this method's signature.
279      * (e.g. "java.lang.String", "com.Foo", "int")
280      *
281      * @return an array of parameter type names
282      */

283     public String JavaDoc[] getParameterTypeNames() {
284         return signature.getParameters();
285     }
286
287     /**
288      * Gets the parameter types of exceptions this method can throw.
289      * Types are loaded with the same Loader as declaring class was loaded
290      * with.
291      *
292      * @return an array of parameter types
293      * @exception ClassNotFoundException is thrown, if one of the types
294      * could not be loaded
295      * @see #getExceptionNames()
296      */

297     public JiapiClass[] getExceptionTypes() throws ClassNotFoundException JavaDoc {
298         String JavaDoc[] exceptionNames = getExceptionNames();
299         JiapiClass[] types = new JiapiClass[exceptionNames.length];
300         Loader loader = getDeclaringClass().getLoader();
301
302         int i = 0;
303         try {
304             for (i = 0; i < exceptionNames.length; i++) {
305                 types[i] = loader.loadClass(exceptionNames[i]);
306             }
307         }
308         catch(java.io.IOException JavaDoc ioe) {
309             throw new ClassNotFoundException JavaDoc(exceptionNames[i]);
310         }
311
312         return types;
313     }
314
315     /**
316      * Gets the names of exceptions this method can throw.
317      * (e.g. "java.lang.ClassNotFoundException").
318      *
319      * @return an array of parameter type names
320      */

321     public String JavaDoc[] getExceptionNames() {
322         ExceptionsAttribute a =
323             (ExceptionsAttribute)getAttribute("Exceptions");
324
325         if (a == null) {
326             return new String JavaDoc[0];
327         }
328
329         return a.getExceptionNames();
330     }
331
332     /**
333      * Adds a local variable for this method.
334      *
335      * @param type a type of the local variable
336      * @param name a name of the local variable
337      * @return a local variable
338      */

339     public LocalVariable addLocalVariable(String JavaDoc type, String JavaDoc name) {
340         return null;
341     }
342
343
344     /*
345      * CONSIDER adding this method
346      *
347      * Copies source JiapiMethod to this JiapiMethod.
348      * ExceptionTables and all the other metadata is copied.
349      * As well as InstructionList. InstructionList gets appended
350      * to InstructionList of this JiapiMethod.
351      */

352     /*public*/ void copy(JiapiMethod source) {
353         // TBDL
354
}
355
356     /**
357      * Convert this JiapiMethod to String.
358      */

359     public String JavaDoc toString() {
360         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
361         String JavaDoc ms = Modifier.toString(getModifiers());
362         sb.append(ms);
363         if (ms.length() > 0) {
364             sb.append(' ');
365         }
366
367         sb.append(getReturnType());
368         sb.append(' ');
369         sb.append(getName());
370         sb.append('(');
371         String JavaDoc[] params = getParameterTypeNames();
372         for (int i = 0; i < params.length; i++) {
373             sb.append(params[i]);
374             if (i < params.length - 1) {
375                 sb.append(',');
376             }
377         }
378         sb.append(')');
379
380         String JavaDoc[] eNames = getExceptionNames();
381         if (eNames.length > 0) {
382             sb.append(" throws ");
383
384             for(int i = 0; i < eNames.length; i++) {
385                 sb.append(eNames[i]);
386                 if (i < eNames.length - 1) {
387                     sb.append(", ");
388                 }
389             }
390         }
391         
392         //getInstructionList();
393
// if (this.exceptionTable != null) {
394
// Iterator i = exceptionTable.iterator();
395
// sb.append("Exception table:\nstart_pc end_pc handler_pc catch_type\n");
396
// while(i.hasNext()) {
397
// ETEntry entry = (ETEntry)i.next();
398
// sb.append("start: " + entry.getStart() + " " +
399
// "end: " + entry.getEnd() + " " +
400
// "handler: " +entry.getHandler() + "\n");
401
// }
402
// }
403
// else {
404
// // System.out.println("Exception table was null");
405
// }
406

407 // sb.append("\nmax-locals: ");
408
// sb.append(getMaxLocals());
409
// sb.append(", max-stack: ");
410
// sb.append(getMaxStack());
411

412         return sb.toString();
413     }
414
415
416
417     /**
418      * Gets all the method attributes defined in ClassFile.
419      */

420     private List JavaDoc getAttributes() {
421         return method.getAttributes();
422     }
423
424     Method getMethod() {
425         return method;
426     }
427
428
429     /**
430      * Gets all the method attributes defined in ClassFile.
431      */

432     Attribute getAttribute(String JavaDoc name) {
433         List JavaDoc l = method.getAttributes();
434         Iterator JavaDoc i = l.iterator();
435
436         while(i.hasNext()) {
437             Attribute a = (Attribute)i.next();
438             if (a != null) { // NOTE: Should never be null
439
if (a.getName().equals(name)) {
440                     return a;
441                 }
442             }
443         }
444
445         return null;
446     }
447
448
449     // This method is called, when bytecode has changed, so that
450
// we can update Method and CodeAttribute
451
void update() {
452         if (instructions != null) {
453             instructions.updateOffsets();
454             // Update branch offsets
455
updateBranchOffsets();
456             
457             CodeAttribute ca = (CodeAttribute)getAttribute("Code");
458             ca.setByteCode(instructions.getBytes());
459             
460             // Update max locals & max stack
461
updateMaxLocals(ca, instructions);
462             updateMaxStack(ca, instructions);
463         }
464
465         updateLineNumberTableOffsets();
466         updateExceptionTableOffsets();
467     }
468
469
470     //
471
// Update routines follow.
472
// Update changes from reflect layer to file layer
473
//
474
private short updateMaxLocals(CodeAttribute ca, InstructionList il) {
475         // maxLocals = locals + (this); // locals + 1;
476
short maxLocals = (short)(getParameterTypeNames().length);
477
478 // if (!Modifier.isStatic(getModifiers())) {
479
// maxLocals++; // 'this'
480
// }
481

482         LocalVariableTableAttribute lvta = (LocalVariableTableAttribute)getAttribute(LocalVariableTableAttribute.ATTRIBUTE_NAME);
483
484         // Add local variables other than ones in method signature
485
if (lvta != null) {
486             maxLocals += lvta.getLocalVariables().size();
487         }
488
489
490         short maxLocalsInInstructions = 0;
491         for(int i = 0; i < il.size(); i++) {
492             Instruction ins = il.get(i);
493             switch(ins.getOpcode()) {
494             case Opcodes.FLOAD_0:
495             case Opcodes.FSTORE_0:
496             case Opcodes.ILOAD_0:
497             case Opcodes.ISTORE_0:
498             case Opcodes.ALOAD_0:
499             case Opcodes.ASTORE_0:
500                 if (maxLocals < 1) {
501                     maxLocals = 1;
502                 }
503                 break;
504                 
505             case Opcodes.FLOAD_1:
506             case Opcodes.FSTORE_1:
507             case Opcodes.LSTORE_0: // long occupies two slots
508
case Opcodes.LLOAD_0:
509             case Opcodes.DSTORE_0: // double occupies two slots
510
case Opcodes.DLOAD_0:
511             case Opcodes.ILOAD_1:
512             case Opcodes.ISTORE_1:
513             case Opcodes.ALOAD_1:
514             case Opcodes.ASTORE_1:
515                 if (maxLocals < 2) {
516                     maxLocals = 2;
517                 }
518                 break;
519                 
520             case Opcodes.FLOAD_2:
521             case Opcodes.FSTORE_2:
522             case Opcodes.LSTORE_1: // long occupies two slots
523
case Opcodes.LLOAD_1:
524             case Opcodes.DSTORE_1: // double occupies two slots
525
case Opcodes.DLOAD_1:
526             case Opcodes.ILOAD_2:
527             case Opcodes.ISTORE_2:
528             case Opcodes.ALOAD_2:
529             case Opcodes.ASTORE_2:
530                 if (maxLocals < 3) {
531                     maxLocals = 3;
532                 }
533                 break;
534                 
535             case Opcodes.FLOAD_3:
536             case Opcodes.FSTORE_3:
537             case Opcodes.LSTORE_2: // long occupies two slots
538
case Opcodes.LLOAD_2:
539             case Opcodes.DSTORE_2: // double occupies two slots
540
case Opcodes.DLOAD_2:
541             case Opcodes.ILOAD_3:
542             case Opcodes.ISTORE_3:
543             case Opcodes.ALOAD_3:
544             case Opcodes.ASTORE_3:
545                 if (maxLocals < 4) {
546                     maxLocals = 4;
547                 }
548                 break;
549                 
550             case Opcodes.DSTORE_3: // double occupies two slots
551
case Opcodes.DLOAD_3:
552             case Opcodes.LSTORE_3: // long occupies two slots
553
case Opcodes.LLOAD_3:
554                 if (maxLocals < 5) {
555                     maxLocals = 5;
556                 }
557                 break;
558
559             case Opcodes.DSTORE: // double occupies two slots
560
case Opcodes.DLOAD:
561             case Opcodes.LSTORE: // long occupies two slots
562
case Opcodes.LLOAD:
563                 byte[] bytes_l = ins.getBytes();
564                 int mloc_l = bytes_l[1];
565                 if (maxLocals < mloc_l + 1) {
566                     maxLocals = (short)(mloc_l + 1);
567                 }
568                 break;
569
570             case Opcodes.FLOAD:
571             case Opcodes.FSTORE:
572             case Opcodes.ILOAD:
573             case Opcodes.ISTORE:
574             case Opcodes.ALOAD:
575             case Opcodes.ASTORE:
576                 byte[] bytes = ins.getBytes();
577                 short mloc = bytes[1];
578                 if (maxLocals < mloc) {
579                     maxLocals = mloc;
580                 }
581                 break;
582             }
583         }
584         
585         maxLocals++;
586         ca.setMaxLocals(maxLocals);
587
588         return maxLocals;
589     }
590
591
592     /*
593      * Calculate max stack. If instructions' stack usage is positive,
594      * increase maxStackUsage.
595      */

596     private short updateMaxStack(CodeAttribute ca, InstructionList il) {
597         short maxStack = 0;
598         short su = 0;
599
600         if (il != null) { // Abstract methods do not have instructionlist
601
for(int i = 0; i < il.size(); i++) {
602                 Instruction ins = il.get(i);
603                 su += ins.stackUsage();
604                 
605                 if (su > maxStack) {
606                     maxStack = su;
607                 }
608                 
609                 // stack usage above says only stack consumption.
610
// Some instructions both consume stack and produce
611
// stack. Fix it here.
612
if (ins.getOpcode() == Opcodes.GETSTATIC ||
613                     ins.getOpcode() == Opcodes.GETFIELD) {
614                     maxStack++;
615                 }
616
617
618
619             }
620
621             ca.setMaxStack(maxStack);
622         }
623
624         return maxStack;
625     }
626
627     /*
628      * Update branch intructions' offsets.
629      */

630     private void updateBranchOffsets() {
631         for(int i = 0; i < instructions.size(); i++) {
632             Instruction ins = instructions.get(i);
633
634             if (ins instanceof BranchInstruction) {
635                 BranchInstruction bIns = (BranchInstruction)ins;
636
637         // Check, that target actually is found in this list
638
// If not, Leave offsets as is, and hope for the
639
// best
640
if (instructions.indexOf(bIns.getTarget()) != -1) {
641             int branchOffset = bIns.getOffset();
642             int targetOffset = bIns.getTarget().getOffset();
643
644             // Set offset of the branch to (target - branch)
645
bIns.setTargetOffset(targetOffset - branchOffset);
646         }
647         else {
648             log.warn("Target instruction for branch was not found. This is most likely because branch-instruction was copied from some other instruction-list, and target was not copied. Branch offset is left as is.");
649         }
650             }
651         }
652     }
653
654     private void updateLineNumberTableOffsets() {
655         if (lineNumberTable != null) {
656             Iterator JavaDoc i = lineNumberTable.iterator();
657             while(i.hasNext()) {
658                 LNTableEntry entry = (LNTableEntry)i.next();
659                 entry.update();
660             }
661         }
662     }
663
664
665     private void updateExceptionTableOffsets() {
666         if (exceptionTable != null) {
667             Iterator JavaDoc i = exceptionTable.iterator();
668             while(i.hasNext()) {
669                 ETEntry entry = (ETEntry)i.next();
670                 entry.update();
671             }
672         }
673     }
674
675     public /*private*/ int getMaxLocals() {
676         CodeAttribute ca = (CodeAttribute)getAttribute("Code");
677         if (ca == null) {
678             throw new NullPointerException JavaDoc("No Code attribute found");
679         }
680         ca.setByteCode(getInstructionList().getBytes());
681
682         return updateMaxLocals(ca, getInstructionList());
683     }
684
685     public /*private*/ int getMaxStack() {
686         CodeAttribute ca = (CodeAttribute)getAttribute("Code");
687         //ca.setByteCode(instructions.getBytes());
688

689         return updateMaxStack(ca, getInstructionList());
690     }
691
692
693     /**
694      * ExceptionTableEntry.
695      */

696     private class ETEntry {
697         private Instruction start;
698         private Instruction end;
699         private Instruction handler;
700         private CodeAttribute.ExceptionTableEntry entry;
701         
702         ETEntry(CodeAttribute.ExceptionTableEntry entry, Instruction start,
703                 Instruction end, Instruction handler) {
704 // System.out.println("Created ETEntry: " + start + ", " + end+
705
// ", " + handler);
706
this.entry = entry;
707             this.start = start;
708             this.end = end;
709             this.handler = handler;
710         }
711         
712         /**
713          * Called, when bytecode instrumentation is done.
714          * This method updates offset of the instruction to reflect
715          * offset changes.
716          */

717         void update() {
718 // System.out.println("Updating ETEntry: start " + entry.getStartPc()+
719
// "-->" + start.getOffset() +
720
// entry.getEndPc() + "-->" + end.getOffset() +
721
// entry.getHandlerPc() + "-->"+handler.getOffset()
722
// );
723
entry.setStartPc(start.getOffset());
724             entry.setEndPc(end.getOffset());
725             entry.setHandlerPc(handler.getOffset());
726         }
727
728
729         Instruction getStart() {
730             return start;
731         }
732         Instruction getEnd() {
733             return end;
734         }
735         Instruction getHandler() {
736             return handler;
737         }
738     }
739
740
741     /**
742      * Checks, whether or not this JiapiMethod is synthetic.
743      *
744      * @return true, if this JiapiMethod is synthetic
745      */

746     public boolean isSynthetic() {
747         return (method.getAttribute(SyntheticAttribute.ATTRIBUTE_NAME) !=null);
748     }
749 }
750
Popular Tags