/**
* 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) {
}
}
}