KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > firstpartners > nounit > reader > bytecode > ByteCodeCallsSnippetFactory


1 package net.firstpartners.nounit.reader.bytecode;
2
3 /*
4     This Class Modified from JClassLib , Free software under the terms of the
5     Gnu Public Library (below). You can redistribute it and/or
6     modify it under the terms of the GNU General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the license, or (at your option) any later version.
9  */

10
11 import java.io.IOException JavaDoc;
12 import java.util.HashMap JavaDoc;
13 import java.util.HashSet JavaDoc;
14 import java.util.Iterator JavaDoc;
15
16 import net.firstpartners.nounit.reader.ISnippetFactory;
17 import net.firstpartners.nounit.snippet.SnippetCalls;
18 import net.firstpartners.nounit.snippet.Snippets;
19 import net.firstpartners.nounit.utility.NoUnitException;
20
21 import org.apache.log4j.Logger;
22
23 import org.gjt.jclasslib.bytecode.AbstractInstruction;
24 import org.gjt.jclasslib.bytecode.BranchInstruction;
25 import org.gjt.jclasslib.bytecode.ImmediateByteInstruction;
26 import org.gjt.jclasslib.bytecode.ImmediateIntInstruction;
27 import org.gjt.jclasslib.bytecode.ImmediateShortInstruction;
28 import org.gjt.jclasslib.bytecode.IncrementInstruction;
29 import org.gjt.jclasslib.bytecode.InvokeInterfaceInstruction;
30 import org.gjt.jclasslib.bytecode.LookupSwitchInstruction;
31 import org.gjt.jclasslib.bytecode.MultianewarrayInstruction;
32 import org.gjt.jclasslib.bytecode.Opcodes;
33 import org.gjt.jclasslib.bytecode.OpcodesUtil;
34 import org.gjt.jclasslib.bytecode.TableSwitchInstruction;
35 import org.gjt.jclasslib.bytecode.MatchOffsetPair;
36 import org.gjt.jclasslib.io.ByteCodeReader;
37 import org.gjt.jclasslib.structures.AttributeInfo;
38 import org.gjt.jclasslib.structures.ClassFile;
39 import org.gjt.jclasslib.structures.InvalidByteCodeException;
40 import org.gjt.jclasslib.structures.MethodInfo;
41 import org.gjt.jclasslib.structures.attributes.CodeAttribute;
42
43
44
45 /**
46  * Get the Other Methods Called by this one
47  * @author modified by Paul Browne
48  * @version 20/11/01
49  */

50 public class ByteCodeCallsSnippetFactory
51 extends AbstractByteCodeSnippetFactory
52     implements ISnippetFactory, Opcodes {
53     
54     /*
55      * Attribute that we are interested in within the file
56      */

57     private CodeAttribute attribute;
58     
59     /*
60      *Source Class file
61      */

62     private ClassFile classFile;
63     
64     private HashMap JavaDoc offsetToPosition = new HashMap JavaDoc();
65     private int opcodeCounterWidth;
66     private int offsetWidth;
67     
68     //handle to logger
69
static Logger log = Logger.getLogger(ByteCodeCallsSnippetFactory.class);
70     
71     
72     /**
73      * Holds Process ByteCode -> Instructions
74      */

75     private HashMap JavaDoc internalInstructions = new HashMap JavaDoc();
76     
77     /*
78      * Position in internalInstruction
79      */

80     private int currentInstructionPosition=0;
81     
82     
83     /**
84      * Makes note of important positons in the Internal Instructions Table
85      */

86     private HashSet JavaDoc methodCalls= new HashSet JavaDoc();
87     
88     
89     /**
90      * Constant - marks normal instruction
91      */

92     private static final int I_NORMAL =0;
93     
94     /**
95      * Constant - marks method call instruction
96      */

97     private static final int I_CALL =1;
98     
99     
100
101     /**
102      * Construct a bytecode document with an associated <tt>Code</tt>
103      * attribute and a parent class file.
104      * Only use the first Attribute from the method , which for methods should be OK!!
105      * @param styles the style cache
106      * @param attribute the <tt>Code</tt> attribute
107      * @param classFile the class file
108      */

109     public ByteCodeCallsSnippetFactory(MethodInfo thisMethod, ClassFile classFile)
110     throws IOException JavaDoc, InvalidByteCodeException, NoUnitException {
111         
112         //Local Variables
113
CodeAttribute attribute;
114         int counter=0;
115         boolean foundFlag=false;
116         
117         //Convert to handle to code attribute
118
//Following presumes only one (CodeAttribute) - throws NoUnitException if not
119
AttributeInfo[] genAttributes =thisMethod.getAttributes();
120         
121         
122         while((counter < genAttributes.length)&&(!foundFlag)){
123         
124              //Check and convert if possible
125
if (genAttributes[counter] instanceof CodeAttribute){
126           
127                 //Set flag to skip loop
128
foundFlag=true;
129                  
130                 //Do conversion and doc preparation
131

132                 
133                 log.debug(genAttributes[counter].getClass().toString());
134                  
135                 attribute = (CodeAttribute)genAttributes[counter];
136
137                 // Once Converted , Same as other constructor
138
this.attribute = attribute;
139                 this.classFile = classFile;
140
141                 setupDocument();
142                 
143             } else {
144               counter++;
145             }
146         }
147
148     }
149     
150     /**
151      * Construct a bytecode document with an associated <tt>Code</tt>
152      * attribute and a parent class file.
153      * @param styles the style cache
154      * @param attribute the <tt>Code</tt> attribute
155      * @param classFile the class file
156      */

157     public ByteCodeCallsSnippetFactory(CodeAttribute attribute, ClassFile classFile) throws IOException JavaDoc, InvalidByteCodeException {
158         
159         this.attribute = attribute;
160         this.classFile = classFile;
161         
162         setupDocument();
163     }
164     
165     /**
166      * Get the document containing the opcode counters.
167      * @return internalByteCodes - String describing the internal calls
168      */

169     public String JavaDoc toString() {
170         
171         StringBuffer JavaDoc internalByteCodes = new StringBuffer JavaDoc();
172         
173         //Loop through , adding internal Instructions
174
for (int a=0; a<this.currentInstructionPosition; a++) {
175             
176             internalByteCodes.append(this.internalInstructions.get(String.valueOf(a)));
177             
178         }
179         
180         return internalByteCodes.toString();
181     }
182     
183     /**
184      * See what methods are called by the implementation of this code
185      * <BR> Called Methods are the one <I>after</I> the stored instruction
186      * <BR> How they are called , is the instruction <I>before</I> the stored instruction
187      * <BR> Constant pool reference is the <I>actual</I> stored instruction
188      * @return Snippets Collection of Called Methods (String , Names)
189      * Get the Collection of Snippets (as read from the source)
190      * @return Snippets
191      */

192     public Snippets getSnippets() throws NoUnitException {
193    
194         
195         //Internal Variables
196
int thisInstruction;
197         String JavaDoc tmpString;
198         String JavaDoc tmpClass;
199         String JavaDoc tmpMethod;
200         Snippets calls = new Snippets();
201         SnippetCalls tmpSnippet ;
202         Iterator JavaDoc myLoop = methodCalls.iterator();
203         
204         
205         //Loop and add relevant calls
206
while (myLoop.hasNext()) {
207             
208             //Get the Param Info via the Class Pool
209

210             //Get the Class & Method Info
211
thisInstruction = ((Integer JavaDoc)myLoop.next()).intValue();
212             thisInstruction++; // we want the one after - see note above
213
tmpString = (String JavaDoc)this.internalInstructions.get(String.valueOf(thisInstruction));
214             
215                 // Put this into Snippet Form and save
216
tmpClass = getClassFromCalledString(tmpString);
217                 tmpMethod = getMethodFromCalledString(tmpString);
218                 tmpSnippet= new SnippetCalls(tmpClass,tmpMethod,null);
219                 calls.add(tmpSnippet);
220                 
221         }
222         
223         return calls;
224     }
225     
226     
227     
228     /**
229      * Store the String as Part of the Internal Instruction Set (nomral type)
230      * @param string to Add
231      */

232     protected void appendString(String JavaDoc string) {
233         
234         appendString(I_NORMAL,string);
235         
236     }
237     
238     /**
239      * Store the String as Part of the Internal Instruction Set
240      * @param type of Instruction Being added (as per constants)
241      * @param string to Add
242      */

243     protected void appendString(int type,String JavaDoc string) {
244         
245
246         //Add only if the incoming String is not null
247
if (string!=null) {
248             
249             //Check to see if it should be noted in the method calls table
250
if ((type==I_CALL)&&(this.doubleCheckCalledMethod(currentInstructionPosition))) {
251                 this.methodCalls.add(new Integer JavaDoc(currentInstructionPosition));
252
253             }
254             
255             //Add everything to the internal instruction table
256
internalInstructions.put(
257                 String.valueOf(this.currentInstructionPosition),string);
258             currentInstructionPosition++;
259             
260
261         }
262     }
263     
264     
265     /**
266      * Setup the Internal Document
267      */

268     private void setupDocument()
269     throws IOException JavaDoc , InvalidByteCodeException {
270         
271         byte[] code = attribute.getCode();
272         
273         java.util.List JavaDoc instructions = ByteCodeReader.readByteCode(code);
274         
275        // calculateOffsetWidth(instructions);
276

277         int[] linesPerOpcode = new int[instructions.size()];
278         
279         Iterator JavaDoc it = instructions.iterator();
280         AbstractInstruction currentInstruction;
281         int instructionCount = 0;
282         while (it.hasNext()) {
283             currentInstruction = (AbstractInstruction)it.next();
284             linesPerOpcode[instructionCount++] = addInstructionToDocument(currentInstruction);
285         }
286         
287         createOpcodeCounterDocument(linesPerOpcode);
288         
289         
290     }
291     
292     /**
293      * Add More Information to internal store
294      */

295     private int addInstructionToDocument(AbstractInstruction instruction)
296     throws InvalidByteCodeException {
297         
298         int offset = instruction.getOffset();
299         
300         
301         appendString(getPaddedValue(offset, offsetWidth));
302         
303         appendString(" " + instruction.getOpcodeVerbose());
304         
305         int additionalLines = addOpcodeSpecificInfo(instruction);
306         
307         appendString("\n");
308         
309         return additionalLines + 1;
310     }
311     
312     /**
313      * Add More Information to internal store
314      */

315     private int addOpcodeSpecificInfo(AbstractInstruction instruction)
316     throws InvalidByteCodeException {
317         
318         int additionalLines = 0;
319         
320         if (instruction instanceof ImmediateByteInstruction) {
321             additionalLines +=
322             addImmediateByteSpecificInfo((ImmediateByteInstruction)instruction);
323         } else if (instruction instanceof ImmediateShortInstruction) {
324             additionalLines +=
325             addImmediateShortSpecificInfo((ImmediateShortInstruction)instruction);
326         } else if (instruction instanceof ImmediateIntInstruction) {
327             additionalLines +=
328             addImmediateIntSpecificInfo((ImmediateIntInstruction)instruction);
329         } else if (instruction instanceof BranchInstruction) {
330             additionalLines +=
331             addBranchSpecificInfo((BranchInstruction)instruction);
332         } else if (instruction instanceof TableSwitchInstruction) {
333             additionalLines +=
334             addTableSwitchSpecificInfo((TableSwitchInstruction)instruction);
335         } else if (instruction instanceof LookupSwitchInstruction) {
336             additionalLines +=
337             addLookupSwitchSpecificInfo((LookupSwitchInstruction)instruction);
338         }
339         
340         return additionalLines;
341     }
342     
343     /**
344      * Add More Information to internal store
345      */

346     private int addImmediateByteSpecificInfo(ImmediateByteInstruction instruction)
347     throws InvalidByteCodeException {
348         
349         int opcode = instruction.getOpcode();
350         int sourceOffset = instruction.getOffset();
351         int immediateByte = instruction.getImmediateByte();
352         
353         if (opcode == OPCODE_LDC) {
354             addConstantPoolLink(immediateByte, sourceOffset);
355         } else if (opcode == OPCODE_NEWARRAY) {
356             String JavaDoc verbose = OpcodesUtil.getArrayTypeVerbose(immediateByte);
357             appendString(" " + immediateByte + " (" + verbose + ")");
358             
359         } else {
360             appendString(" " + immediateByte);
361             
362             if (instruction instanceof IncrementInstruction) {
363                 appendString(" by");
364                 appendString(" " + ((IncrementInstruction)instruction).getIncrementConst());
365             }
366         }
367         return 0;
368     }
369     
370     /**
371      * Add More Information to internal store
372      */

373     private int addImmediateShortSpecificInfo(ImmediateShortInstruction instruction)
374     throws InvalidByteCodeException {
375         
376         int opcode = instruction.getOpcode();
377         int sourceOffset = instruction.getOffset();
378         int immediateShort = instruction.getImmediateShort();
379         
380         if (opcode == OPCODE_SIPUSH) {
381             appendString(" " + immediateShort);
382         } else {
383             addConstantPoolLink(immediateShort, sourceOffset);
384             
385             if (instruction instanceof InvokeInterfaceInstruction) {
386                 appendString(" count " + ((InvokeInterfaceInstruction)instruction).getCount());
387                 
388             } else if (instruction instanceof MultianewarrayInstruction) {
389                 appendString(" dim " + ((MultianewarrayInstruction)instruction).getDimensions());
390                 
391             }
392         }
393         
394         return 0;
395     }
396     
397     /**
398      * Add More Information to internal store
399      */

400     private int addImmediateIntSpecificInfo(ImmediateIntInstruction instruction)
401     throws InvalidByteCodeException {
402         
403         int immediateInt = instruction.getImmediateInt();
404         int sourceOffset = instruction.getOffset();
405         
406         addConstantPoolLink(immediateInt, sourceOffset);
407         
408         return 0;
409     }
410     
411     /**
412      * Add More Information to internal store
413      */

414     private int addBranchSpecificInfo(BranchInstruction instruction) {
415         
416         int branchOffset = instruction.getBranchOffset();
417         int instructionOffset = instruction.getOffset();
418         
419         addOffsetLink(branchOffset, instructionOffset);
420         
421         return 0;
422     }
423     
424     /**
425      * Add More Information to internal store
426      */

427     private int addTableSwitchSpecificInfo(TableSwitchInstruction instruction) {
428         
429         int instructionOffset = instruction.getOffset();
430         int lowByte = instruction.getLowByte();
431         int highByte = instruction.getHighByte();
432         int[] jumpOffsets = instruction.getJumpOffsets();
433         
434         appendString(" " + lowByte + " to " + highByte + "\n");
435         
436         for (int i = 0; i <= highByte - lowByte; i++) {
437             appendString("\u0009" + (i + lowByte) + ": " );
438             addOffsetLink(jumpOffsets[i], instructionOffset);
439             appendString("\n" );
440             
441         }
442         appendString("\u0009default: " );
443         addOffsetLink(instruction.getDefaultOffset(), instructionOffset);
444         
445         return highByte - lowByte + 2;
446     }
447     
448     /**
449      * Add More Information to internal store
450      */

451     private int addLookupSwitchSpecificInfo(LookupSwitchInstruction instruction) {
452         
453         int instructionOffset = instruction.getOffset();
454         java.util.List JavaDoc matchOffsetPairs = instruction.getMatchOffsetPairs();
455         int matchOffsetPairsCount = matchOffsetPairs.size();
456         
457         appendString(" " + matchOffsetPairsCount + "\n");
458         
459         MatchOffsetPair matchOffsetPairEntry;
460         
461         for (int i = 0; i < matchOffsetPairsCount; i++) {
462             matchOffsetPairEntry = (MatchOffsetPair)matchOffsetPairs.get(i);
463             appendString("\u0009" + matchOffsetPairEntry.getMatch() + ": ");
464             addOffsetLink(matchOffsetPairEntry.getOffset(), instructionOffset);
465             appendString("\n" );
466             
467         }
468         appendString("\u0009default: ");
469         addOffsetLink(instruction.getDefaultOffset(), instructionOffset);
470         
471         return matchOffsetPairsCount + 1;
472     }
473     
474     /**
475      * Add More Information to internal store
476      */

477     private void addConstantPoolLink(int constantPoolIndex, int sourceOffset)
478     throws InvalidByteCodeException {
479                         
480         appendString(I_CALL,"#" + constantPoolIndex);
481         
482         String JavaDoc name = classFile.getConstantPoolEntryName(constantPoolIndex);
483         if (name.length() > 0) {
484             appendString(" <" + name + ">");
485         }
486         
487     }
488     
489     /**
490      * Add More Information to internal store
491      */

492     private void addOffsetLink(int branchOffset, int instructionOffset) {
493         
494         int totalOffset = branchOffset + instructionOffset;
495         
496         appendString(String.valueOf(totalOffset));
497         
498         appendString(" (" + (branchOffset > 0 ? "+" : "")
499         + String.valueOf(branchOffset) + ")");
500     }
501     
502
503     
504     /**
505      *Called during inital setup
506      */

507     private void createOpcodeCounterDocument(int[] linesPerOpcode) {
508         
509         
510         int numberOfOpcodes = linesPerOpcode.length;
511         
512         opcodeCounterWidth = String.valueOf(numberOfOpcodes - 1).length();
513         
514         
515         for (int i = 0; i < numberOfOpcodes; i++) {
516             appendString(getPaddedValue(i, opcodeCounterWidth));
517             
518             for (int j = 0; j < linesPerOpcode[i]; j++) {
519                 appendString("\n");
520             }
521         }
522         
523         
524     }
525     
526     /**
527      * Pads out a number to a certain width
528      * @param number to pad out
529      * @param width required
530      * @return String of required length , containing spaces and number
531      */

532     private static String JavaDoc getPaddedValue(int number, int width) {
533         
534         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
535         String JavaDoc value = String.valueOf(number);
536         int valueLength = value.length();
537         for (int i = valueLength; i < width; i++) {
538             buffer.append(' ');
539         }
540         buffer.append(value);
541         return buffer.toString();
542     }
543     
544     /**
545      * Double Checks to see if this is really an instruction to call another method
546      * @see getCalledMethods() javadoc for info on how the internal instruction store
547      * is currently implemented
548      * @param instructionReference to check
549      * @return true if is call to another method , false if not
550      */

551     private boolean doubleCheckCalledMethod(int instructionReference) {
552         
553         
554         //Internal Variables
555
String JavaDoc previousInstruction = (String JavaDoc)internalInstructions.get(String.valueOf(instructionReference-1));
556         
557         //Check for nothing found
558
if (previousInstruction == null) {
559             return false;
560         }
561         
562         //Check to see what this string says
563
if(previousInstruction.indexOf("invoke")>-1) {
564             return true;
565         } else {
566             return false;
567         }
568         
569     }
570     
571     /**
572      * Extracts the class portion from the called string
573      * @param inString to split
574      * @return String , the class portion only
575      */

576     private String JavaDoc getClassFromCalledString(String JavaDoc inString){
577        
578         String JavaDoc returnString="";
579         
580         //Find Place to break
581
int breakplace = inString.lastIndexOf(".");
582         
583         //Break, clean and retrun
584
returnString = inString.substring(0,breakplace);
585         
586         returnString = tidyNames(returnString);
587         
588         
589         return returnString;
590     }
591     
592     /**
593      * Extracts the class portion from the called string
594      * @param inString to split
595      * @return String , the class portion only
596      */

597     private String JavaDoc getMethodFromCalledString(String JavaDoc inString){
598        
599         String JavaDoc returnString="";
600         
601         //Find Place to break
602
int breakplace = inString.lastIndexOf(".");
603         
604         //Break , clean and retrun
605
returnString = inString.substring(breakplace,inString.length());
606         
607         returnString =tidyNames(returnString);
608         
609         return returnString;
610     }
611     
612 }
613
614
615
616
617
Popular Tags