/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.typeresolution.visitors; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureVisitor; public class PMDASMVisitor extends ClassVisitor { private String outerName; private Map<String, String> packages = new HashMap<>(); private AnnotationVisitor annotationVisitor = new PMDAnnotationVisitor(this); private FieldVisitor fieldVisitor = new PMDFieldVisitor(this); private SignatureVisitor sigVisitor = new PMDSignatureVisitor(this); private MethodVisitor methodVisitor = new PMDMethodVisitor(this); public List<String> innerClasses; public PMDASMVisitor(String outerName) { super(Opcodes.ASM5); this.outerName = outerName; } public Map<String, String> getPackages() { return packages; } public List<String> getInnerClasses() { return innerClasses; } private String parseClassName(String name) { if (name == null) { return null; } String className = name; int n = name.lastIndexOf('/'); if (n > -1) { className = name.substring(n + 1); } name = name.replace('/', '.'); packages.put(className, name); n = className.indexOf('$'); if (n > -1) { // TODO I don't think the first one, with Class$Inner is needed - // come back and check packages.put(className.substring(n + 1), name); packages.put(className.replace('$', '.'), name); } return name; } private void parseClassName(String[] names) { if (names != null) { for (String s : names) { parseClassName(s); } } } private void extractSignature(String sig) { if (sig != null) { new SignatureReader(sig).accept(sigVisitor); } } /* Start ClassVisitor implementations */ @Override public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) { parseClassName(name); parseClassName(interfaces); if (sig != null) { extractSignature(sig); } } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { addType(Type.getType(desc)); return annotationVisitor; } @Override public FieldVisitor visitField(int access, String name, String desc, String sig, Object value) { if (sig != null) { extractSignature(sig); } addType(Type.getType(desc)); if (value instanceof Type) { addType((Type) value); } return fieldVisitor; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) { if (sig != null) { extractSignature(sig); } addMethodDesc(desc); parseClassName(exceptions); return methodVisitor; } @Override public void visitSource(String source, String debug) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if (!this.outerName.replace('.', '/').equals(outerName)) { // do not consider the inner class if it is not a member of our // outer class return; } if (innerClasses == null) { innerClasses = new ArrayList<>(); } if (!innerClasses.contains(name.replace('/', '.'))) { innerClasses.add(name.replace('/', '.')); } packages.put(innerName, name.replace('/', '.')); } @Override public void visitOuterClass(String owner, String name, String desc) { } @Override public void visitEnd() { } private void addMethodDesc(String desc) { addTypes(desc); addType(Type.getReturnType(desc)); } private void addTypes(String desc) { Type[] types = Type.getArgumentTypes(desc); for (Type type : types) { addType(type); } } private void addType(Type t) { switch (t.getSort()) { case Type.ARRAY: addType(t.getElementType()); break; case Type.OBJECT: parseClassName(t.getClassName().replace('.', '/')); break; default: // Do nothing break; } } @Override public void visitAttribute(Attribute attr) { } /* * Start visitors */ private static class PMDFieldVisitor extends FieldVisitor { private PMDASMVisitor parent; PMDFieldVisitor(PMDASMVisitor visitor) { super(Opcodes.ASM5); parent = visitor; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { parent.addType(Type.getType(desc)); return parent.annotationVisitor; } @Override public void visitAttribute(Attribute attr) { } @Override public void visitEnd() { } } private static class PMDAnnotationVisitor extends AnnotationVisitor { private PMDASMVisitor parent; PMDAnnotationVisitor(PMDASMVisitor visitor) { super(Opcodes.ASM5); parent = visitor; } @Override public AnnotationVisitor visitAnnotation(String name, String desc) { parent.addType(Type.getType(desc)); return this; } @Override public void visitEnum(String name, String desc, String value) { parent.addType(Type.getType(desc)); } @Override public AnnotationVisitor visitArray(String name) { return this; } @Override public void visitEnd() { } @Override public void visit(String name, Object value) { if (value instanceof Type) { parent.addType((Type) value); } } } private static class PMDSignatureVisitor extends SignatureVisitor { private PMDASMVisitor parent; PMDSignatureVisitor(PMDASMVisitor visitor) { super(Opcodes.ASM5); this.parent = visitor; } @Override public void visitFormalTypeParameter(String name) { } @Override public SignatureVisitor visitClassBound() { return this; } @Override public SignatureVisitor visitInterfaceBound() { return this; } @Override public SignatureVisitor visitSuperclass() { return this; } @Override public SignatureVisitor visitInterface() { return this; } @Override public SignatureVisitor visitParameterType() { return this; } @Override public SignatureVisitor visitReturnType() { return this; } @Override public SignatureVisitor visitExceptionType() { return this; } @Override public void visitBaseType(char descriptor) { } @Override public void visitTypeVariable(String name) { } @Override public SignatureVisitor visitArrayType() { return this; } @Override public void visitClassType(String name) { parent.parseClassName(name); } @Override public void visitInnerClassType(String name) { parent.parseClassName(name); } @Override public void visitTypeArgument() { } @Override public SignatureVisitor visitTypeArgument(char wildcard) { return this; } @Override public void visitEnd() { } } private static class PMDMethodVisitor extends MethodVisitor { private PMDASMVisitor parent; PMDMethodVisitor(PMDASMVisitor visitor) { super(Opcodes.ASM5); parent = visitor; } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { parent.addType(Type.getType(desc)); return parent.annotationVisitor; } public AnnotationVisitor visitAnnotation(String name, String desc) { parent.addType(Type.getType(desc)); return parent.annotationVisitor; } @Override public void visitTypeInsn(int opcode, String desc) { if (desc.charAt(0) == '[') { parent.addType(Type.getType(desc)); } else { parent.parseClassName(desc); } } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { parent.parseClassName(owner); parent.addType(Type.getType(desc)); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { parent.parseClassName(owner); parent.addMethodDesc(desc); } /** * the constant to be loaded on the stack. This parameter must be a non * null Integer, a Float, a Long, a Double a String (or a Type for * .class constants, for classes whose version is 49.0 or more). * * @see org.objectweb.asm.MethodVisitor#visitLdcInsn(java.lang.Object) */ @Override public void visitLdcInsn(Object cst) { if (cst instanceof Type) { parent.addType((Type) cst); } else if (cst instanceof String) { parent.parseClassName((String) cst); } } @Override public void visitMultiANewArrayInsn(String desc, int dims) { parent.addType(Type.getType(desc)); } @Override public void visitLocalVariable(String name, String desc, String sig, Label start, Label end, int index) { parent.extractSignature(sig); } @Override public void visitCode() { } @Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { } @Override public void visitInsn(int opcode) { } @Override public void visitIntInsn(int opcode, int operand) { } @Override public void visitVarInsn(int opcode, int var) { } @Override public void visitJumpInsn(int opcode, Label label) { } @Override public void visitLabel(Label label) { } @Override public void visitIincInsn(int var, int increment) { } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { parent.parseClassName(type); } @Override public void visitLineNumber(int line, Label start) { } @Override public void visitMaxs(int maxStack, int maxLocals) { } @Override public AnnotationVisitor visitAnnotationDefault() { return parent.annotationVisitor; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { parent.addType(Type.getType(desc)); return parent.annotationVisitor; } @Override public void visitEnd() { } @Override public void visitAttribute(Attribute attr) { } } }