View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.typeresolution.visitors;
5   
6   import java.util.ArrayList;
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Map;
10  import org.objectweb.asm.AnnotationVisitor;
11  import org.objectweb.asm.Attribute;
12  import org.objectweb.asm.ClassVisitor;
13  import org.objectweb.asm.FieldVisitor;
14  import org.objectweb.asm.Label;
15  import org.objectweb.asm.MethodVisitor;
16  import org.objectweb.asm.Opcodes;
17  import org.objectweb.asm.Type;
18  import org.objectweb.asm.signature.SignatureReader;
19  import org.objectweb.asm.signature.SignatureVisitor;
20  
21  public class PMDASMVisitor extends ClassVisitor {
22  
23      public PMDASMVisitor() {
24          super(Opcodes.ASM5);
25      }
26  
27      private Map<String, String> packages = new HashMap<>();
28  
29      private AnnotationVisitor annotationVisitor = new PMDAnnotationVisitor(this);
30  
31      private FieldVisitor fieldVisitor = new PMDFieldVisitor(this);
32  
33      private SignatureVisitor sigVisitor = new PMDSignatureVisitor(this);
34  
35      private MethodVisitor methodVisitor = new PMDMethodVisitor(this);
36  
37      public List<String> innerClasses;
38  
39      public Map<String, String> getPackages() {
40          return packages;
41      }
42  
43      public List<String> getInnerClasses() {
44          return innerClasses;
45      }
46  
47      private String parseClassName(String name) {
48          if (name == null) {
49              return null;
50          }
51  
52          String className = name;
53          int n = name.lastIndexOf('/');
54          if (n > -1) {
55              className = name.substring(n + 1);
56          }
57          name = name.replace('/', '.');
58          packages.put(className, name);
59          n = className.indexOf('$');
60          if (n > -1) {
61              //TODO I don't think the first one, with Class$Inner is needed - come back and check
62              packages.put(className.substring(n + 1), name);
63              packages.put(className.replace('$', '.'), name);
64          }
65  
66          return name;
67      }
68  
69      private void parseClassName(String[] names) {
70          if (names != null) {
71              for (String s : names) {
72                  parseClassName(s);
73              }
74          }
75      }
76  
77      private void extractSignature(String sig) {
78          if (sig != null) {
79              new SignatureReader(sig).accept(sigVisitor);
80          }
81      }
82  
83  	/* Start ClassVisitor implementations */
84  
85      public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) {
86          parseClassName(name);
87          parseClassName(interfaces);
88          if (sig != null) {
89              extractSignature(sig);
90          }
91      }
92  
93      public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
94          addType(Type.getType(desc));
95          return annotationVisitor;
96      }
97  
98      public FieldVisitor visitField(int access, String name, String desc, String sig, Object value) {
99          if (sig != null) {
100             extractSignature(sig);
101         }
102 
103         addType(Type.getType(desc));
104         if (value instanceof Type) {
105             addType((Type) value);
106         }
107         return fieldVisitor;
108     }
109 
110     public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
111         if (sig != null) {
112             extractSignature(sig);
113         }
114         addMethodDesc(desc);
115         parseClassName(exceptions);
116         return methodVisitor;
117     }
118 
119     public void visitSource(String source, String debug) {
120     }
121 
122     public void visitInnerClass(String name, String outerName, String innerName, int access) {
123         if (innerClasses == null) {
124             innerClasses = new ArrayList<>();
125         }
126         if (!innerClasses.contains(name.replace('/', '.'))) {
127             innerClasses.add(name.replace('/', '.'));
128         }
129         packages.put(innerName, name.replace('/', '.'));
130     }
131 
132     public void visitOuterClass(String owner, String name, String desc) {
133     }
134 
135     public void visitEnd() {
136     }
137 
138     private void addMethodDesc(String desc) {
139         addTypes(desc);
140         addType(Type.getReturnType(desc));
141     }
142 
143     private void addTypes(String desc) {
144         Type[] types = Type.getArgumentTypes(desc);
145         for (Type type : types) {
146             addType(type);
147         }
148     }
149 
150     private void addType(Type t) {
151         switch (t.getSort()) {
152             case Type.ARRAY:
153                 addType(t.getElementType());
154                 break;
155             case Type.OBJECT:
156                 parseClassName(t.getClassName().replace('.', '/'));
157                 break;
158             default:
159                 // Do nothing
160                 break;
161         }
162     }
163 
164     public void visitAttribute(Attribute attr) {
165     }
166 
167 	/*
168      * Start visitors
169 	 */
170 
171     private static class PMDFieldVisitor extends FieldVisitor {
172 
173         private PMDASMVisitor parent;
174 
175         public PMDFieldVisitor(PMDASMVisitor visitor) {
176             super(Opcodes.ASM5);
177             parent = visitor;
178         }
179 
180         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
181             parent.addType(Type.getType(desc));
182             return parent.annotationVisitor;
183         }
184 
185         public void visitAttribute(Attribute attr) {
186         }
187 
188         public void visitEnd() {
189         }
190     }
191 
192     private static class PMDAnnotationVisitor extends AnnotationVisitor {
193         private PMDASMVisitor parent;
194 
195         public PMDAnnotationVisitor(PMDASMVisitor visitor) {
196             super(Opcodes.ASM5);
197             parent = visitor;
198         }
199 
200         public AnnotationVisitor visitAnnotation(String name, String desc) {
201             parent.addType(Type.getType(desc));
202             return this;
203         }
204 
205         public void visitEnum(String name, String desc, String value) {
206             parent.addType(Type.getType(desc));
207         }
208 
209         public AnnotationVisitor visitArray(String name) {
210             return this;
211         }
212 
213         public void visitEnd() {
214         }
215 
216         public void visit(String name, Object value) {
217             if (value instanceof Type) {
218                 parent.addType((Type) value);
219             }
220         }
221     }
222 
223     private static class PMDSignatureVisitor extends SignatureVisitor {
224         private PMDASMVisitor parent;
225 
226         public PMDSignatureVisitor(PMDASMVisitor visitor) {
227             super(Opcodes.ASM5);
228             this.parent = visitor;
229         }
230 
231         public void visitFormalTypeParameter(String name) {
232         }
233 
234         public SignatureVisitor visitClassBound() {
235             return this;
236         }
237 
238         public SignatureVisitor visitInterfaceBound() {
239             return this;
240         }
241 
242         public SignatureVisitor visitSuperclass() {
243             return this;
244         }
245 
246         public SignatureVisitor visitInterface() {
247             return this;
248         }
249 
250         public SignatureVisitor visitParameterType() {
251             return this;
252         }
253 
254         public SignatureVisitor visitReturnType() {
255             return this;
256         }
257 
258         public SignatureVisitor visitExceptionType() {
259             return this;
260         }
261 
262         public void visitBaseType(char descriptor) {
263         }
264 
265         public void visitTypeVariable(String name) {
266         }
267 
268         public SignatureVisitor visitArrayType() {
269             return this;
270         }
271 
272         public void visitClassType(String name) {
273             parent.parseClassName(name);
274         }
275 
276         public void visitInnerClassType(String name) {
277             parent.parseClassName(name);
278         }
279 
280         public void visitTypeArgument() {
281         }
282 
283         public SignatureVisitor visitTypeArgument(char wildcard) {
284             return this;
285         }
286 
287         public void visitEnd() {
288         }
289     }
290 
291     private static class PMDMethodVisitor extends MethodVisitor {
292         private PMDASMVisitor parent;
293 
294         public PMDMethodVisitor(PMDASMVisitor visitor) {
295             super(Opcodes.ASM5);
296             parent = visitor;
297         }
298 
299         public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
300             parent.addType(Type.getType(desc));
301             return parent.annotationVisitor;
302         }
303 
304         public AnnotationVisitor visitAnnotation(String name, String desc) {
305             parent.addType(Type.getType(desc));
306             return parent.annotationVisitor;
307         }
308 
309         public void visitTypeInsn(int opcode, String desc) {
310             if (desc.charAt(0) == '[') {
311                 parent.addType(Type.getType(desc));
312             } else {
313                 parent.parseClassName(desc);
314             }
315         }
316 
317         public void visitFieldInsn(int opcode, String owner, String name, String desc) {
318             parent.parseClassName(owner);
319             parent.addType(Type.getType(desc));
320         }
321 
322         public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
323             parent.parseClassName(owner);
324             parent.addMethodDesc(desc);
325         }
326 
327         /**
328          * the constant to be loaded on the stack. This parameter must be a non null
329          * Integer, a Float, a Long, a Double a String (or a Type for .class
330          * constants, for classes whose version is 49.0 or more).
331          *
332          * @see org.objectweb.asm.MethodVisitor#visitLdcInsn(java.lang.Object)
333          */
334         public void visitLdcInsn(Object cst) {
335             if (cst instanceof Type) {
336                 parent.addType((Type) cst);
337             } else if (cst instanceof String) {
338                 parent.parseClassName((String) cst);
339             }
340         }
341 
342         public void visitMultiANewArrayInsn(String desc, int dims) {
343             parent.addType(Type.getType(desc));
344         }
345 
346         public void visitLocalVariable(String name, String desc, String sig, Label start, Label end, int index) {
347             parent.extractSignature(sig);
348         }
349 
350         public void visitCode() {
351         }
352 
353         public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
354         }
355 
356         public void visitInsn(int opcode) {
357         }
358 
359         public void visitIntInsn(int opcode, int operand) {
360         }
361 
362         public void visitVarInsn(int opcode, int var) {
363         }
364 
365         public void visitJumpInsn(int opcode, Label label) {
366         }
367 
368         public void visitLabel(Label label) {
369         }
370 
371         public void visitIincInsn(int var, int increment) {
372         }
373 
374         public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
375         }
376 
377         public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
378         }
379 
380         public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
381             parent.parseClassName(type);
382         }
383 
384         public void visitLineNumber(int line, Label start) {
385         }
386 
387         public void visitMaxs(int maxStack, int maxLocals) {
388         }
389 
390         public AnnotationVisitor visitAnnotationDefault() {
391             return parent.annotationVisitor;
392         }
393 
394         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
395             parent.addType(Type.getType(desc));
396             return parent.annotationVisitor;
397         }
398 
399         public void visitEnd() {
400         }
401 
402         public void visitAttribute(Attribute attr) {
403         }
404 
405     }
406 }