package edu.umd.cs.findbugs.detect; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.apache.bcel.Repository; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantCP; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantDouble; import org.apache.bcel.classfile.ConstantFieldref; import org.apache.bcel.classfile.ConstantLong; import org.apache.bcel.classfile.ConstantNameAndType; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.ConstantUtf8; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.BugReporter; import edu.umd.cs.findbugs.Detector; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.ClassContext; import edu.umd.cs.findbugs.ba.XClass; import edu.umd.cs.findbugs.ba.ch.Subtypes2; import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; import edu.umd.cs.findbugs.classfile.Global; import edu.umd.cs.findbugs.classfile.IAnalysisCache; import edu.umd.cs.findbugs.classfile.MissingClassException; import edu.umd.cs.findbugs.util.ClassName; import edu.umd.cs.findbugs.visitclass.PreorderVisitor; public class ResolveAllReferences extends PreorderVisitor implements Detector { private BugReporter bugReporter; public ResolveAllReferences(BugReporter bugReporter) { this.bugReporter = bugReporter; } Set<String> defined; private void compute() { if (defined == null) { // System.out.println("Computing"); defined = new HashSet<String>(); Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2(); Collection<XClass> allClasses = subtypes2.getXClassCollection(); IAnalysisCache analysisCache = Global.getAnalysisCache(); for (XClass c : allClasses) { try { JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class, c.getClassDescriptor()); addAllDefinitions(jclass); } catch (MissingClassException e) { bugReporter.reportMissingClass(e.getClassDescriptor()); } catch (CheckedAnalysisException e) { bugReporter.logError("Could not find class " + c.getClassDescriptor().toDottedClassName(), e); } } // System.out.println("Done Computing: " + defined.contains("edu.umd.cs.findbugs.ba.IsNullValueAnalysis.UNKNOWN_VALUES_ARE_NSP : Z")); } } public void visitClassContext(ClassContext classContext) { classContext.getJavaClass().accept(this); } public void report() { } public void addAllDefinitions(JavaClass obj) { String className2 = obj.getClassName(); defined.add(className2); for (Method m : obj.getMethods()) if (!m.isPrivate()) { String name = getMemberName(obj, className2, m.getNameIndex(), m.getSignatureIndex()); defined.add(name); } for (Field f : obj.getFields()) if (!f.isPrivate()) { String name = getMemberName(obj, className2, f.getNameIndex(), f .getSignatureIndex()); defined.add(name); } } private String getClassName(JavaClass c, int classIndex) { String name = c.getConstantPool().getConstantString(classIndex, CONSTANT_Class); return ClassName.extractClassName(name).replace('/','.'); } private String getMemberName(JavaClass c, String className, int memberNameIndex, int signatureIndex) { return className + "." + ((ConstantUtf8) c.getConstantPool().getConstant( memberNameIndex, CONSTANT_Utf8)).getBytes() + " : " + ((ConstantUtf8) c.getConstantPool().getConstant( signatureIndex, CONSTANT_Utf8)).getBytes(); } private String getMemberName(String className, String memberName, String signature) { return className.replace('/','.') + "." + memberName + " : " + signature; } private boolean find(JavaClass target, String name, String signature) throws ClassNotFoundException { if (target == null) return false; String ref = getMemberName(target.getClassName(), name, signature); if (defined.contains(ref)) return true; if (find(target.getSuperClass(), name, signature)) return true; for(JavaClass i : target.getInterfaces()) if (find(i, name, signature)) return true; return false; } @Override public void visit(JavaClass obj) { compute(); ConstantPool cp = obj.getConstantPool(); Constant[] constants = cp.getConstantPool(); checkConstant: for (int i = 0; i < constants.length; i++) { Constant co = constants[i]; if (co instanceof ConstantDouble || co instanceof ConstantLong) i++; if (co instanceof ConstantClass) { String ref = getClassName(obj, i); if ((ref.startsWith("java") || ref.startsWith("org.w3c.dom")) && !defined.contains(ref)) bugReporter.reportBug(new BugInstance(this, "VR_UNRESOLVABLE_REFERENCE", NORMAL_PRIORITY) .addClass(obj).addString(ref)); } else if (co instanceof ConstantFieldref) { // do nothing until we handle static fields defined in interfaces } else if (co instanceof ConstantCP) { ConstantCP co2 = (ConstantCP) co; String className = getClassName(obj, co2.getClassIndex()); // System.out.println("checking " + ref); if (className.equals(obj.getClassName()) || !defined.contains(className)) { // System.out.println("Skipping check of " + ref); continue checkConstant; } ConstantNameAndType nt = (ConstantNameAndType) cp .getConstant(co2.getNameAndTypeIndex()); String name = ((ConstantUtf8) obj.getConstantPool().getConstant( nt.getNameIndex(), CONSTANT_Utf8)).getBytes(); String signature = ((ConstantUtf8) obj.getConstantPool().getConstant( nt.getSignatureIndex(), CONSTANT_Utf8)).getBytes(); try { JavaClass target = Repository.lookupClass(className); if (! find(target, name, signature)) bugReporter.reportBug(new BugInstance(this, "VR_UNRESOLVABLE_REFERENCE", NORMAL_PRIORITY) .addClass(obj).addString(getMemberName(target.getClassName(), name, signature))); } catch (ClassNotFoundException e) { bugReporter.reportMissingClass(e); } } } } }