View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.dcd.graph;
5   
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.util.ArrayList;
9   import java.util.Arrays;
10  import java.util.List;
11  import org.objectweb.asm.AnnotationVisitor;
12  import org.objectweb.asm.Attribute;
13  import org.objectweb.asm.ClassReader;
14  import org.objectweb.asm.ClassVisitor;
15  import org.objectweb.asm.FieldVisitor;
16  import org.objectweb.asm.Label;
17  import org.objectweb.asm.MethodVisitor;
18  import org.objectweb.asm.Opcodes;
19  import org.objectweb.asm.signature.SignatureReader;
20  import net.sourceforge.pmd.dcd.asm.PrintVisitor;
21  import net.sourceforge.pmd.dcd.asm.TypeSignatureVisitor;
22  import net.sourceforge.pmd.util.filter.Filter;
23  
24  /**
25   * Utility class used to build a UsageGraph.
26   */
27  public class UsageGraphBuilder {
28  
29      /**
30       * Should trace level logging be enabled.  This is a development debugging
31       * option.
32       */
33      private static final boolean TRACE = false;
34      private static final boolean INDEX = true;
35  
36      protected final UsageGraph usageGraph;
37      protected final Filter<String> classFilter;
38  
39      public UsageGraphBuilder(Filter<String> classFilter) {
40          this.classFilter = classFilter;
41          this.usageGraph = new UsageGraph(classFilter);
42      }
43  
44      public void index(String name) {
45          try {
46              String className = getClassName(name);
47              String classResourceName = getResourceName(name);
48              if (classFilter.filter(className)) {
49                  if (!usageGraph.isClass(className)) {
50                      usageGraph.defineClass(className);
51                      InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(
52                              classResourceName + ".class");
53                      ClassReader classReader = new ClassReader(inputStream);
54                      classReader.accept(getNewClassVisitor(), 0);
55                  }
56              }
57          } catch (IOException e) {
58              throw new RuntimeException("For " + name + ": " + e.getMessage(), e);
59          }
60      }
61  
62      public UsageGraph getUsageGraph() {
63          return usageGraph;
64      }
65  
66      private ClassVisitor getNewClassVisitor() {
67          return new MyClassVisitor();
68      }
69  
70      // ASM visitor to on Class files to build a UsageGraph
71      class MyClassVisitor extends ClassVisitor {
72          private final PrintVisitor p;
73  
74          protected void println(String s) {
75              p.println(s);
76          }
77  
78          protected void printlnIndent(String s) {
79              p.printlnIndent(s);
80          }
81  
82          public MyClassVisitor() {
83              super(Opcodes.ASM5);
84              p = new PrintVisitor();
85          }
86  
87          private String className;
88  
89          public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
90              if (TRACE) {
91                  println("visit:");
92                  printlnIndent("version: " + version);
93                  printlnIndent("access: " + access);
94                  printlnIndent("name: " + name);
95                  printlnIndent("signature: " + signature);
96                  printlnIndent("superName: " + superName);
97                  printlnIndent("interfaces: " + asList(interfaces));
98              }
99              this.className = getClassName(name);
100         }
101 
102         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
103             if (TRACE) {
104                 println("visitAnnotation:");
105                 printlnIndent("desc: " + desc);
106                 printlnIndent("visible: " + visible);
107             }
108             return null;
109         }
110 
111         public void visitAttribute(Attribute attr) {
112             if (TRACE) {
113                 println("visitAttribute:");
114                 printlnIndent("attr: " + attr);
115             }
116         }
117 
118         public void visitEnd() {
119             if (TRACE) {
120                 println("visitEnd:");
121             }
122         }
123 
124         public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
125             if (TRACE) {
126                 println("visitField:");
127                 printlnIndent("access: " + access);
128                 printlnIndent("name: " + name);
129                 printlnIndent("desc: " + desc);
130                 printlnIndent("signature: " + signature);
131                 printlnIndent("value: " + value);
132             }
133             if (INDEX) {
134                 SignatureReader signatureReader = new SignatureReader(desc);
135                 TypeSignatureVisitor visitor = new TypeSignatureVisitor(p);
136                 signatureReader.acceptType(visitor);
137                 if (TRACE) {
138                     printlnIndent("fieldType: " + visitor.getFieldType());
139                 }
140 
141                 usageGraph.defineField(className, name, desc);
142             }
143             return null;
144         }
145 
146         public void visitInnerClass(String name, String outerName, String innerName, int access) {
147             if (TRACE) {
148                 println("visitInnerClass:");
149                 printlnIndent("name: " + name);
150                 printlnIndent("outerName: " + outerName);
151                 printlnIndent("innerName: " + innerName);
152                 printlnIndent("access: " + access);
153             }
154             index(name);
155         }
156 
157         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
158             MemberNode memberNode = null;
159             if (TRACE) {
160                 println("visitMethod:");
161                 printlnIndent("access: " + access);
162                 printlnIndent("name: " + name);
163                 printlnIndent("desc: " + desc);
164                 printlnIndent("signature: " + signature);
165                 printlnIndent("exceptions: " + asList(exceptions));
166             }
167             if (INDEX) {
168                 memberNode = usageGraph.defineMethod(className, name, desc);
169             }
170             return getNewMethodVisitor(p, memberNode);
171         }
172 
173         public void visitOuterClass(String owner, String name, String desc) {
174             if (TRACE) {
175                 println("visitOuterClass:");
176                 printlnIndent("owner: " + owner);
177                 printlnIndent("name: " + name);
178                 printlnIndent("desc: " + desc);
179             }
180         }
181 
182         public void visitSource(String source, String debug) {
183             if (TRACE) {
184                 println("visitSource:");
185                 printlnIndent("source: " + source);
186                 printlnIndent("debug: " + debug);
187             }
188         }
189     }
190 
191     protected MethodVisitor getNewMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
192         return new MyMethodVisitor(parent, usingMemberNode);
193     }
194 
195     protected class MyMethodVisitor extends MethodVisitor {
196         private final PrintVisitor p;
197 
198         protected void println(String s) {
199             p.println(s);
200         }
201 
202         protected void printlnIndent(String s) {
203             p.printlnIndent(s);
204         }
205 
206         private final MemberNode usingMemberNode;
207 
208         public MyMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
209             super(Opcodes.ASM5);
210             p = parent;
211             this.usingMemberNode = usingMemberNode;
212         }
213 
214         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
215             if (TRACE) {
216                 println("visitAnnotation:");
217                 printlnIndent("desc: " + desc);
218                 printlnIndent("visible: " + visible);
219             }
220             return null;
221         }
222 
223         public AnnotationVisitor visitAnnotationDefault() {
224             if (TRACE) {
225                 println("visitAnnotationDefault:");
226             }
227             return null;
228         }
229 
230         public void visitAttribute(Attribute attr) {
231             if (TRACE) {
232                 println("visitAttribute:");
233                 printlnIndent("attr: " + attr);
234             }
235         }
236 
237         public void visitCode() {
238             if (TRACE) {
239                 println("visitCode:");
240             }
241         }
242 
243         public void visitEnd() {
244             if (TRACE) {
245                 println("visitEnd:");
246             }
247         }
248 
249         public void visitFieldInsn(int opcode, String owner, String name, String desc) {
250             if (TRACE) {
251                 println("visitFieldInsn:");
252                 printlnIndent("opcode: " + opcode);
253                 printlnIndent("owner: " + owner);
254                 printlnIndent("name: " + name);
255                 printlnIndent("desc: " + desc);
256             }
257             if (INDEX) {
258                 String className = getClassName(owner);
259                 usageGraph.usageField(className, name, desc, usingMemberNode);
260             }
261         }
262 
263         public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
264             if (TRACE) {
265                 println("visitFrame:");
266                 printlnIndent("type: " + type);
267                 printlnIndent("local: " + local);
268                 printlnIndent("local2: " + asList(local2));
269                 printlnIndent("stack: " + stack);
270                 printlnIndent("stack2: " + asList(stack2));
271             }
272         }
273 
274         public void visitIincInsn(int var, int increment) {
275             if (TRACE) {
276                 println("visitIincInsn:");
277                 printlnIndent("var: " + var);
278                 printlnIndent("increment: " + increment);
279             }
280         }
281 
282         public void visitInsn(int opcode) {
283             if (TRACE) {
284                 println("visitInsn:");
285                 printlnIndent("opcode: " + opcode);
286             }
287         }
288 
289         public void visitIntInsn(int opcode, int operand) {
290             if (TRACE) {
291                 println("visitIntInsn:");
292                 printlnIndent("opcode: " + opcode);
293                 printlnIndent("operand: " + operand);
294             }
295         }
296 
297         public void visitJumpInsn(int opcode, Label label) {
298             if (TRACE) {
299                 println("visitJumpInsn:");
300                 printlnIndent("opcode: " + opcode);
301                 printlnIndent("label: " + label);
302             }
303         }
304 
305         public void visitLabel(Label label) {
306             if (TRACE) {
307                 println("visitLabel:");
308                 printlnIndent("label: " + label);
309             }
310         }
311 
312         public void visitLdcInsn(Object cst) {
313             if (TRACE) {
314                 println("visitLdcInsn:");
315                 printlnIndent("cst: " + cst);
316             }
317         }
318 
319         public void visitLineNumber(int line, Label start) {
320             if (TRACE) {
321                 println("visitLineNumber:");
322                 printlnIndent("line: " + line);
323                 printlnIndent("start: " + start);
324             }
325         }
326 
327         public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
328             if (TRACE) {
329                 println("visitLocalVariable:");
330                 printlnIndent("name: " + name);
331                 printlnIndent("desc: " + desc);
332                 printlnIndent("signature: " + signature);
333                 printlnIndent("start: " + start);
334                 printlnIndent("end: " + end);
335                 printlnIndent("index: " + index);
336             }
337         }
338 
339         public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
340             if (TRACE) {
341                 println("visitLookupSwitchInsn:");
342                 printlnIndent("dflt: " + dflt);
343                 printlnIndent("keys: " + asList(keys));
344                 printlnIndent("labels: " + asList(labels));
345             }
346         }
347 
348         public void visitMaxs(int maxStack, int maxLocals) {
349             if (TRACE) {
350                 println("visitMaxs:");
351                 printlnIndent("maxStack: " + maxStack);
352                 printlnIndent("maxLocals: " + maxLocals);
353             }
354         }
355 
356         public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
357             if (TRACE) {
358                 println("visitMethodInsn:");
359                 printlnIndent("opcode: " + opcode);
360                 printlnIndent("owner: " + owner);
361                 printlnIndent("name: " + name);
362                 printlnIndent("desc: " + desc);
363                 printlnIndent("itf: " + itf);
364             }
365             if (INDEX) {
366                 String className = getClassName(owner);
367                 usageGraph.usageMethod(className, name, desc, usingMemberNode);
368             }
369         }
370 
371         public void visitMultiANewArrayInsn(String desc, int dims) {
372             if (TRACE) {
373                 println("visitMultiANewArrayInsn:");
374                 printlnIndent("desc: " + desc);
375                 printlnIndent("dims: " + dims);
376             }
377         }
378 
379         public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
380             if (TRACE) {
381                 println("visitParameterAnnotation:");
382                 printlnIndent("parameter: " + parameter);
383                 printlnIndent("desc: " + desc);
384                 printlnIndent("visible: " + visible);
385             }
386             return null;
387         }
388 
389         public void visitTableSwitchInsn(int min, int max, Label dflt,
390                 Label... labels) {
391             if (TRACE) {
392                 println("visitTableSwitchInsn:");
393                 printlnIndent("min: " + min);
394                 printlnIndent("max: " + max);
395                 printlnIndent("dflt: " + dflt);
396                 printlnIndent("labels: " + asList(labels));
397             }
398         }
399 
400         public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
401             if (TRACE) {
402                 println("visitTryCatchBlock:");
403                 printlnIndent("start: " + start);
404                 printlnIndent("end: " + end);
405                 printlnIndent("handler: " + handler);
406                 printlnIndent("type: " + type);
407             }
408         }
409 
410         public void visitTypeInsn(int opcode, String desc) {
411             if (TRACE) {
412                 println("visitTypeInsn:");
413                 printlnIndent("opcode: " + opcode);
414                 printlnIndent("desc: " + desc);
415             }
416         }
417 
418         public void visitVarInsn(int opcode, int var) {
419             if (TRACE) {
420                 println("visitVarInsn:");
421                 printlnIndent("opcode: " + opcode);
422                 printlnIndent("var: " + var);
423             }
424         }
425     }
426 
427     private static String getResourceName(String name) {
428         return name.replace('.', '/');
429     }
430 
431     static String getClassName(String name) {
432         return name.replace('/', '.');
433     }
434 
435     private static List<Integer> asList(int[] array) {
436         List<Integer> list = null;
437         if (array != null) {
438             list = new ArrayList<>(array.length);
439             for (int i : array) {
440                 list.add(i);
441             }
442         }
443         return list;
444     }
445 
446     private static List<Object> asList(Object[] array) {
447         return array != null ? Arrays.asList(array) : null;
448     }
449 }