/* (non-Javadoc)
   * @see edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs.classfile.IAnalysisCache, java.lang.Object)
   */
  public Method analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
      throws CheckedAnalysisException {
    JavaClass jclass =
        analysisCache.getClassAnalysis(JavaClass.class, descriptor.getClassDescriptor());
    Method[] methodList = jclass.getMethods();

    Method result = null;

    // As a side-effect, cache all of the Methods for this JavaClass
    for (Method method : methodList) {
      MethodDescriptor methodDescriptor =
          DescriptorFactory.instance()
              .getMethodDescriptor(
                  descriptor.getSlashedClassName(),
                  method.getName(),
                  method.getSignature(),
                  method.isStatic());

      // Put in cache eagerly
      analysisCache.eagerlyPutMethodAnalysis(Method.class, methodDescriptor, method);

      if (methodDescriptor.equals(descriptor)) {
        result = method;
      }
    }

    return result;
  }
  public @CheckForNull XMethod getXMethod(
      String cName, String mName, String sig, boolean isStatic) {
    ClassDescriptor classDesc =
        DescriptorFactory.instance().getClassDescriptorForDottedClassName(cName);
    ClassInfo xclass;

    // Get the XClass (really a ClassInfo object)
    try {
      xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc);
    } catch (MissingClassException e) {
      if (DEBUG) {
        System.out.println("  Class not found!");
      }
      //
      //	AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
      return null;
    } catch (CheckedAnalysisException e) {
      if (DEBUG) {
        System.out.println("  Class not found!");
      }
      //			AnalysisContext.logError("Error adding built-in nullness annotation", e);
      return null;
    }
    XMethod xmethod = xclass.findMethod(mName, sig, isStatic);

    if (xmethod == null) xmethod = XFactory.createXMethod(cName, mName, sig, isStatic);
    return xmethod;
  }
Пример #3
0
  public void setAppClassList(List<ClassDescriptor> appClassCollection) {
    // FIXME: we really should drive the progress callback here
    HashSet<ClassDescriptor> appSet = new HashSet<ClassDescriptor>(appClassCollection);

    Collection<ClassDescriptor> allClassDescriptors =
        new ArrayList<ClassDescriptor>(DescriptorFactory.instance().getAllClassDescriptors());
    for (ClassDescriptor appClass : allClassDescriptors) {
      try {
        XClass xclass = currentXFactory().getXClass(appClass);

        if (xclass == null) {
          continue;
        }

        // Add the application class to the database
        if (appSet.contains(appClass)) {
          getSubtypes2().addApplicationClass(xclass);
        } else if (xclass instanceof ClassInfo) {
          getSubtypes2().addClass(xclass);
        }

      } catch (Exception e) {
        AnalysisContext.logError("Unable to get XClass for " + appClass, e);
      }
    }

    if (true && Subtypes2.DEBUG) {
      System.out.println(
          getSubtypes2().getGraph().getNumVertices() + " vertices in inheritance graph");
    }
  }
Пример #4
0
 /**
  * Construct a MethodDescriptor from JavaClass and method.
  *
  * @param jclass a JavaClass
  * @param method a Method belonging to the JavaClass
  * @return a MethodDescriptor identifying the method
  */
 public static MethodDescriptor getMethodDescriptor(JavaClass jclass, Method method) {
   return DescriptorFactory.instance()
       .getMethodDescriptor(
           jclass.getClassName().replace('.', '/'),
           method.getName(),
           method.getSignature(),
           method.isStatic());
 }
  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation(java.lang.String, java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation)
   */
  public void addDefaultAnnotation(Target target, String c, NullnessAnnotation n) {
    if (DEBUG) {
      System.out.println("addDefaultAnnotation: target=" + target + ", c=" + c + ", n=" + n);
    }

    ClassDescriptor classDesc =
        DescriptorFactory.instance().getClassDescriptorForDottedClassName(c);
    ClassInfo xclass;

    // Get the XClass (really a ClassInfo object)
    try {
      xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc);
    } catch (MissingClassException e) {
      //
      //	AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
      return;
    } catch (CheckedAnalysisException e) {
      //			AnalysisContext.logError("Error adding built-in nullness annotation", e);
      return;
    }
    if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.PARAMETER) {
      xclass.addAnnotation(new AnnotationValue(PARAMETERS_ARE_NONNULL_BY_DEFAULT));
      return;
    } else if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.METHOD) {
      xclass.addAnnotation(new AnnotationValue(RETURN_VALUES_ARE_NONNULL_BY_DEFAULT));
      return;
    }
    // Get the default annotation type
    ClassDescriptor defaultAnnotationType;
    if (target == AnnotationDatabase.Target.ANY) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION;
    } else if (target == AnnotationDatabase.Target.FIELD) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS;
    } else if (target == AnnotationDatabase.Target.METHOD) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS;
    } else if (target == AnnotationDatabase.Target.PARAMETER) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS;
    } else {
      throw new IllegalArgumentException("Unknown target for default annotation: " + target);
    }

    // Get the JSR-305 nullness annotation type
    ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(n);

    // Construct an AnnotationValue containing the default annotation
    AnnotationValue annotationValue = new AnnotationValue(defaultAnnotationType);
    AnnotationVisitor v = annotationValue.getAnnotationVisitor();
    v.visit("value", Type.getObjectType(nullnessAnnotationType.getClassName()));
    v.visitEnd();

    if (DEBUG) {
      System.out.println("Adding AnnotationValue " + annotationValue + " to class " + xclass);
    }

    // Destructively add the annotation to the ClassInfo object
    xclass.addAnnotation(annotationValue);
  }
Пример #6
0
 public void addParameterAnnotation(int parameter, String name, AnnotationValue value) {
   ClassDescriptor annotationClass = DescriptorFactory.createClassDescriptorFromSignature(name);
   Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(parameter);
   if (map == null) {
     map = new HashMap<ClassDescriptor, AnnotationValue>();
     methodParameterAnnotations.put(parameter, map);
   }
   map.put(annotationClass, value);
 }
Пример #7
0
 /**
  * Get FieldDescriptor describing the field accessed by given FieldInstruction.
  *
  * @param fins a FieldInstruction
  * @param cpg ConstantPoolGen for the method containing the FieldInstruction
  * @return FieldDescriptor describing the field accessed by given FieldInstruction
  */
 public static FieldDescriptor getAccessedFieldDescriptor(
     FieldInstruction fins, ConstantPoolGen cpg) {
   String className = fins.getClassName(cpg);
   String fieldName = fins.getName(cpg);
   String fieldSig = fins.getSignature(cpg);
   boolean isStatic =
       (fins.getOpcode() == Constants.GETSTATIC || fins.getOpcode() == Constants.PUTSTATIC);
   return DescriptorFactory.instance()
       .getFieldDescriptor(className, fieldName, fieldSig, isStatic);
 }
Пример #8
0
  /**
   * Get a MethodDescriptor describing the method called by given InvokeInstruction.
   *
   * @param inv the InvokeInstruction
   * @param cpg ConstantPoolGen of class containing instruction
   * @return MethodDescriptor describing the called method
   */
  public static MethodDescriptor getCalledMethodDescriptor(
      InvokeInstruction inv, ConstantPoolGen cpg) {
    String calledClassName = inv.getClassName(cpg).replace('.', '/');
    String calledMethodName = inv.getMethodName(cpg);
    String calledMethodSig = inv.getSignature(cpg);
    boolean isStatic = inv.getOpcode() == Constants.INVOKESTATIC;

    return DescriptorFactory.instance()
        .getMethodDescriptor(calledClassName, calledMethodName, calledMethodSig, isStatic);
  }
 private void addCheckedCall(
     @DottedClassName String className,
     String methodName,
     String sig,
     int argumentParameterIndex,
     int typeParameterIndex) {
   ClassDescriptor c =
       DescriptorFactory.instance().getClassDescriptorForDottedClassName(className);
   String call = methodName + sig;
   Info info = new Info(c, argumentParameterIndex, typeParameterIndex);
   callMap.add(call, info);
 }
Пример #10
0
 /**
  * Return whether or not the given class is an application class.
  *
  * @param className name of a class
  * @return true if the class is an application class, false if not an application class or if the
  *     class cannot be located
  */
 public boolean isApplicationClass(@DottedClassName String className) {
   // try {
   // JavaClass javaClass = lookupClass(className);
   // return isApplicationClass(javaClass);
   // } catch (ClassNotFoundException e) {
   // AnalysisContext.reportMissingClass(e);
   // return false;
   // }
   ClassDescriptor classDesc =
       DescriptorFactory.createClassDescriptorFromDottedClassName(className);
   return getSubtypes2().isApplicationClass(classDesc);
 }
Пример #11
0
  /**
   * @param className
   * @param methodName
   * @param methodSignature
   * @param methodSourceSignature
   * @param isUnsupported
   * @param usesConcurrency TODO
   * @param isStub TODO
   * @param methodCallCount TODO
   * @param accessMethodFor TODO
   * @param isStatic
   */
  MethodInfo(
      @SlashedClassName String className,
      String methodName,
      String methodSignature,
      String methodSourceSignature,
      int accessFlags,
      boolean isUnconditionalThrower,
      boolean isUnsupported,
      boolean usesConcurrency,
      boolean hasBackBranch,
      boolean isStub,
      boolean isIdentity,
      int methodCallCount,
      @CheckForNull String[] exceptions,
      @CheckForNull MethodDescriptor accessMethodFor,
      Map<ClassDescriptor, AnnotationValue> methodAnnotations,
      Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations) {
    super(className, methodName, methodSignature, (accessFlags & Constants.ACC_STATIC) != 0);
    this.accessFlags = accessFlags;
    this.exceptions = exceptions;
    if (exceptions != null)
      for (int i = 0; i < exceptions.length; i++)
        exceptions[i] = DescriptorFactory.canonicalizeString(exceptions[i]);
    this.methodSourceSignature = DescriptorFactory.canonicalizeString(methodSourceSignature);
    this.methodAnnotations = Util.immutableMap(methodAnnotations);
    this.methodParameterAnnotations = Util.immutableMap(methodParameterAnnotations);
    if (isUnconditionalThrower) unconditionalThrowers.put(this, null);
    if (isUnsupported) unsupportedMethods.put(this, null);
    if (accessMethodFor != null) MethodInfo.accessMethodFor.put(this, accessMethodFor);
    if (isIdentity) {
      MethodInfo.identifyMethods.put(this, null);
    }

    this.usesConcurrency = usesConcurrency;
    this.hasBackBranch = hasBackBranch;
    this.isStub = isStub;
    this.methodCallCount = methodCallCount;
  }
public class CbeckMustOverrideSuperAnnotation extends OpcodeStackDetector {

  BugReporter bugReporter;

  ClassDescriptor mustOverrideAnnotation =
      DescriptorFactory.createClassDescriptor(OverridingMethodsMustInvokeSuper.class);

  public CbeckMustOverrideSuperAnnotation(BugReporter bugReporter) {
    this.bugReporter = bugReporter;
  }

  private boolean sawCallToSuper;

  @Override
  public void visit(Code code) {
    if (getMethod().isStatic() || getMethod().isPrivate()) return;
    XMethod overrides =
        Lookup.findSuperImplementorAsXMethod(
            getThisClass(), getMethodName(), getMethodSig(), bugReporter);

    if (overrides == null) return;
    AnnotationValue annotation = overrides.getAnnotation(mustOverrideAnnotation);
    if (annotation == null) return;
    sawCallToSuper = false;
    super.visit(code);
    if (!sawCallToSuper)
      bugReporter.reportBug(
          new BugInstance(this, "TESTING", NORMAL_PRIORITY).addClassAndMethod(this));
  }

  /*
   * (non-Javadoc)
   *
   * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
   */
  @Override
  public void sawOpcode(int seen) {
    if (seen != INVOKESPECIAL) return;

    String calledClassName = getClassConstantOperand();
    String calledMethodName = getNameConstantOperand();
    String calledMethodSig = getSigConstantOperand();
    if (calledClassName.equals(getSuperclassName())
        && calledMethodName.equals(getMethodName())
        && calledMethodSig.equals(getMethodSig())) {
      sawCallToSuper = true;
    }
  }
}
  private boolean isGenericCollection(ClassDescriptor operandClass) {
    String dottedClassName = operandClass.getDottedClassName();

    if (baseGenericTypes.contains(dottedClassName)) return true;

    String found = null;
    for (String c : baseGenericTypes) {
      if (Subtypes2.instanceOf(operandClass, c)) {
        found = c;
        break;
      }
    }
    if (found == null) return false;
    if (dottedClassName.startsWith("java.util.")
        || dottedClassName.startsWith("com.google.common.collect.")) return true;
    try {
      XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, operandClass);

      String sig = xclass.getSourceSignature();
      if (sig == null) return false;

      String typeParameter = null;
      List<String> split = GenericUtilities.split(sig, true);
      if (sig.charAt(0) == '<') {
        int end = sig.indexOf(':');
        if (end > 0) typeParameter = sig.substring(1, end);
      }
      if (DEBUG) System.out.println(dottedClassName + " " + typeParameter + " " + split);
      for (String s : split) {
        int i = s.indexOf('<');
        if (i < 0) continue;
        if (s.charAt(0) != 'L') throw new IllegalStateException("unexpected non signature: " + s);
        ClassDescriptor c = DescriptorFactory.createClassDescriptor(s.substring(1, i));
        String superTypeParameter = s.substring(i + 1);
        if (isGenericCollection(c)
            && (typeParameter == null || superTypeParameter.startsWith("T" + typeParameter))) {
          if (DEBUG) System.out.println(operandClass + " is a subtype of " + s);
          return true;
        }
      }
      if (DEBUG) System.out.println("Not a subtype");

    } catch (CheckedAnalysisException e1) {
      AnalysisContext.logError(
          "Error checking for weird generic parameterization of " + operandClass, e1);
    }
    return false;
  }
Пример #14
0
 /**
  * Lookup a class's source file
  *
  * @param dottedClassName the name of the class
  * @return the source file for the class, or {@link SourceLineAnnotation#UNKNOWN_SOURCE_FILE} if
  *     unable to determine
  */
 public final String lookupSourceFile(@Nonnull @DottedClassName String dottedClassName) {
   requireNonNull(dottedClassName, "className is null");
   try {
     XClass xClass =
         Global.getAnalysisCache()
             .getClassAnalysis(
                 XClass.class,
                 DescriptorFactory.createClassDescriptorFromDottedClassName(dottedClassName));
     String name = xClass.getSource();
     if (name == null) {
       return SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
     }
     return name;
   } catch (CheckedAnalysisException e) {
     return SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
   }
 }
Пример #15
0
  /**
   * Get the ClassContext for a class.
   *
   * @param javaClass the class
   * @return the ClassContext for that class
   */
  public ClassContext getClassContext(JavaClass javaClass) {
    // This is a bit silly since we're doing an unnecessary
    // ClassDescriptor->JavaClass lookup.
    // However, we can be assured that it will succeed.

    ClassDescriptor classDescriptor =
        DescriptorFactory.instance()
            .getClassDescriptor(ClassName.toSlashedClassName(javaClass.getClassName()));

    try {
      return Global.getAnalysisCache().getClassAnalysis(ClassContext.class, classDescriptor);
    } catch (CheckedAnalysisException e) {
      IllegalStateException ise =
          new IllegalStateException("Could not get ClassContext for JavaClass");
      ise.initCause(e);
      throw ise;
    }
  }
Пример #16
0
 /**
  * Lookup a class. <em>Use this method instead of Repository.lookupClass().</em>
  *
  * @param className the name of the class
  * @return the JavaClass representing the class
  * @throws ClassNotFoundException (but not really)
  */
 public JavaClass lookupClass(@Nonnull @DottedClassName String className)
     throws ClassNotFoundException {
   try {
     if (className.length() == 0) {
       throw new IllegalArgumentException("Class name is empty");
     }
     if (!ClassName.isValidClassName(className)) {
       throw new ClassNotFoundException("Invalid class name: " + className);
     }
     return Global.getAnalysisCache()
         .getClassAnalysis(
             JavaClass.class,
             DescriptorFactory.instance()
                 .getClassDescriptor(ClassName.toSlashedClassName(className)));
   } catch (CheckedAnalysisException e) {
     throw new ClassNotFoundException("Class not found: " + className, e);
   }
 }
  @Override
  public void visit(Field obj) {
    int flags = obj.getAccessFlags();
    if (isEjbImplClass) {
      ClassDescriptor fieldType =
          DescriptorFactory.createClassDescriptorFromFieldSignature(obj.getSignature());
      if (fieldType != null) {
        if (Subtypes2.instanceOf(fieldType, "javax.ejb.SessionContext")
            || Subtypes2.instanceOf(fieldType, "javax.transaction.UserTransaction")
            || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBHome")
            || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBObject")
            || Subtypes2.instanceOf(fieldType, "javax.naming.Context")) {
          if (obj.isTransient())
            bugReporter.reportBug(
                new BugInstance(this, "UNKNOWN", NORMAL_PRIORITY)
                    .addClass(this)
                    .addVisitedField(this));
          return;
        }
      }
    }

    if (obj.isTransient()) {
      if (isSerializable && !isExternalizable) {
        seenTransientField = true;
        transientFieldsUpdates.put(getXField(), 0);
      } else if (reportTransientFieldOfNonSerializableClass) {
        bugReporter.reportBug(
            new BugInstance(this, "SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS", NORMAL_PRIORITY)
                .addClass(this)
                .addVisitedField(this));
      }
    } else if (getClassName().indexOf("ObjectStreamClass") == -1
        && isSerializable
        && !isExternalizable
        && getFieldSig().indexOf("L") >= 0
        && !obj.isTransient()
        && !obj.isStatic()) {
      if (DEBUG) {
        System.out.println(
            "Examining non-transient field with name: "
                + getFieldName()
                + ", sig: "
                + getFieldSig());
      }
      try {

        double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(getFieldSig());
        if (DEBUG) {
          System.out.println("  isSerializable: " + isSerializable);
        }
        if (isSerializable < 1.0)
          fieldsThatMightBeAProblem.put(obj.getName(), XFactory.createXField(this));
        if (isSerializable < 0.9) {

          // Priority is LOW for GUI classes (unless explicitly marked Serializable),
          // HIGH if the class directly implements Serializable,
          // NORMAL otherwise.
          int priority = computePriority(isSerializable, 0);
          if (!strongEvidenceForIntendedSerialization()) {
            if (obj.getName().startsWith("this$")) priority = Math.max(priority, NORMAL_PRIORITY);
            else if (innerClassHasOuterInstance) {
              if (isAnonymousInnerClass) priority += 2;
              else priority += 1;
            }
            if (isGUIClass || isEjbImplClass) priority++;
          } else if (isGUIClass || isEjbImplClass) priority = Math.max(priority, NORMAL_PRIORITY);
          if (DEBUG)
            System.out.println(
                "SE_BAD_FIELD: "
                    + getThisClass().getClassName()
                    + " "
                    + obj.getName()
                    + " "
                    + isSerializable
                    + " "
                    + implementsSerializableDirectly
                    + " "
                    + sawSerialVersionUID
                    + " "
                    + isGUIClass
                    + " "
                    + isEjbImplClass);
          // Report is queued until after the entire class has been seen.

          if (obj.getName().equals("this$0"))
            fieldWarningList.add(
                new BugInstance(this, "SE_BAD_FIELD_INNER_CLASS", priority)
                    .addClass(getThisClass().getClassName()));
          else if (isSerializable < 0.9)
            fieldWarningList.add(
                new BugInstance(this, "SE_BAD_FIELD", priority)
                    .addClass(getThisClass().getClassName())
                    .addField(getDottedClassName(), obj.getName(), getFieldSig(), false));
        } else if (!isGUIClass && !isEjbImplClass && obj.getName().equals("this$0"))
          fieldWarningList.add(
              new BugInstance(
                      this,
                      "SE_INNER_CLASS",
                      implementsSerializableDirectly ? NORMAL_PRIORITY : LOW_PRIORITY)
                  .addClass(getThisClass().getClassName()));
      } catch (ClassNotFoundException e) {
        if (DEBUG) {
          System.out.println("Caught ClassNotFoundException");
        }
        bugReporter.reportMissingClass(e);
      }
    }

    if (!getFieldName().startsWith("this") && isSynthetic(obj)) foundSynthetic = true;
    if (!getFieldName().equals("serialVersionUID")) return;
    int mask = ACC_STATIC | ACC_FINAL;
    if (!getFieldSig().equals("I") && !getFieldSig().equals("J")) return;
    if ((flags & mask) == mask && getFieldSig().equals("I")) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONLONG_SERIALVERSIONID", LOW_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      sawSerialVersionUID = true;
      return;
    } else if ((flags & ACC_STATIC) == 0) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONSTATIC_SERIALVERSIONID", NORMAL_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      return;
    } else if ((flags & ACC_FINAL) == 0) {
      bugReporter.reportBug(
          new BugInstance(this, "SE_NONFINAL_SERIALVERSIONID", NORMAL_PRIORITY)
              .addClass(this)
              .addVisitedField(this));
      return;
    }
    sawSerialVersionUID = true;
  }
 /**
  * Get the MethodDescriptor that (hopefully) uniqely names this method.
  *
  * @return the MethodDescriptor uniquely naming this method
  */
 public MethodDescriptor toMethodDescriptor() {
   return DescriptorFactory.instance()
       .getMethodDescriptor(
           getSlashedClassName(), method.getName(), method.getSignature(), method.isStatic());
 }
Пример #19
0
 /**
  * Construct a ClassDescriptor from a JavaClass.
  *
  * @param jclass a JavaClass
  * @return a ClassDescriptor identifying that JavaClass
  */
 public static ClassDescriptor getClassDescriptor(JavaClass jclass) {
   return DescriptorFactory.instance()
       .getClassDescriptor(ClassName.toSlashedClassName(jclass.getClassName()));
 }
Пример #20
0
 /**
  * Return whether or not the given class is an application class.
  *
  * @param cls the class to lookup
  * @return true if the class is an application class, false if not an application class or if the
  *     class cannot be located
  */
 public boolean isApplicationClass(JavaClass cls) {
   // return getSubtypes().isApplicationClass(cls);
   return getSubtypes2().isApplicationClass(DescriptorFactory.createClassDescriptor(cls));
 }
 public TypeQualifierNullnessAnnotationDatabase() {
   ClassDescriptor nonnullClassDesc =
       DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class);
   this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
 }
/**
 * Implementation of INullnessAnnotationDatabase that is based on JSR-305 type qualifiers.
 *
 * @author David Hovemeyer
 */
public class TypeQualifierNullnessAnnotationDatabase implements INullnessAnnotationDatabase {
  private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.npe.tq.debug");

  public final TypeQualifierValue nonnullTypeQualifierValue;

  public TypeQualifierNullnessAnnotationDatabase() {
    ClassDescriptor nonnullClassDesc =
        DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class);
    this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
  }

  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#getResolvedAnnotation(java.lang.Object, boolean)
   */
  public NullnessAnnotation getResolvedAnnotation(Object o, boolean getMinimal) {
    Profiler profiler = Global.getAnalysisCache().getProfiler();
    profiler.start(this.getClass());
    try {

      if (DEBUG) {
        System.out.println("getResolvedAnnotation: o=" + o + "...");
      }

      TypeQualifierAnnotation tqa = null;

      if (o instanceof XMethodParameter) {
        XMethodParameter param = (XMethodParameter) o;

        tqa =
            TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(
                param.getMethod(), param.getParameterNumber(), nonnullTypeQualifierValue);
      } else if (o instanceof XMethod || o instanceof XField) {
        tqa =
            TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(
                (AnnotatedObject) o, nonnullTypeQualifierValue);
      }

      NullnessAnnotation result = toNullnessAnnotation(tqa);
      if (DEBUG) {
        System.out.println("   ==> " + (result != null ? result.toString() : "not found"));
      }
      return result;
    } finally {
      profiler.end(this.getClass());
    }
  }

  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#parameterMustBeNonNull(edu.umd.cs.findbugs.ba.XMethod, int)
   */
  public boolean parameterMustBeNonNull(XMethod m, int param) {
    if (DEBUG) {
      System.out.print("Checking " + m + " param " + param + " for @Nonnull...");
    }
    TypeQualifierAnnotation tqa =
        TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(
            m, param, nonnullTypeQualifierValue);

    if (tqa == null && param == 0) {
      String name = m.getName();
      String signature = m.getSignature();
      if (name.equals("main")
          && signature.equals("([Ljava/lang/String;)V")
          && m.isStatic()
          && m.isPublic()) return true;
      else if (NullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m)) return true;
      else if (name.equals("compareTo")
          && signature.substring(signature.indexOf(";") + 1).equals(")Z")
          && !m.isStatic()) return true;
    }
    boolean answer = (tqa != null) && tqa.when == When.ALWAYS;

    if (DEBUG) {
      System.out.println(answer ? "yes" : "no");
    }

    return answer;
  }

  // NOTE:
  // The way we handle adding default annotations is to actually add AnnotationValues
  // to the corresponding XFoo objects, giving the illusion that the annotations
  // were actually read from the underlying class files.

  /**
   * Convert a NullnessAnnotation into the ClassDescriptor of the equivalent JSR-305 nullness type
   * qualifier.
   *
   * @param n a NullnessAnnotation
   * @return ClassDescriptor of the equivalent JSR-305 nullness type qualifier
   */
  private ClassDescriptor getNullnessAnnotationClassDescriptor(NullnessAnnotation n) {
    if (n == NullnessAnnotation.CHECK_FOR_NULL) {
      return JSR305NullnessAnnotations.CHECK_FOR_NULL;
    } else if (n == NullnessAnnotation.NONNULL) {
      return JSR305NullnessAnnotations.NONNULL;
    } else if (n == NullnessAnnotation.NULLABLE) {
      return JSR305NullnessAnnotations.NULLABLE;
    } else if (n == NullnessAnnotation.UNKNOWN_NULLNESS) {
      return JSR305NullnessAnnotations.NULLABLE;
    } else {
      throw new IllegalArgumentException("Unknown NullnessAnnotation: " + n);
    }
  }

  private static final ClassDescriptor PARAMETERS_ARE_NONNULL_BY_DEFAULT =
      DescriptorFactory.createClassDescriptor(javax.annotation.ParametersAreNonnullByDefault.class);
  private static final ClassDescriptor RETURN_VALUES_ARE_NONNULL_BY_DEFAULT =
      DescriptorFactory.createClassDescriptor(
          edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault.class);
  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation(java.lang.String, java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation)
   */
  public void addDefaultAnnotation(Target target, String c, NullnessAnnotation n) {
    if (DEBUG) {
      System.out.println("addDefaultAnnotation: target=" + target + ", c=" + c + ", n=" + n);
    }

    ClassDescriptor classDesc =
        DescriptorFactory.instance().getClassDescriptorForDottedClassName(c);
    ClassInfo xclass;

    // Get the XClass (really a ClassInfo object)
    try {
      xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc);
    } catch (MissingClassException e) {
      //
      //	AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
      return;
    } catch (CheckedAnalysisException e) {
      //			AnalysisContext.logError("Error adding built-in nullness annotation", e);
      return;
    }
    if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.PARAMETER) {
      xclass.addAnnotation(new AnnotationValue(PARAMETERS_ARE_NONNULL_BY_DEFAULT));
      return;
    } else if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.METHOD) {
      xclass.addAnnotation(new AnnotationValue(RETURN_VALUES_ARE_NONNULL_BY_DEFAULT));
      return;
    }
    // Get the default annotation type
    ClassDescriptor defaultAnnotationType;
    if (target == AnnotationDatabase.Target.ANY) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION;
    } else if (target == AnnotationDatabase.Target.FIELD) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS;
    } else if (target == AnnotationDatabase.Target.METHOD) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS;
    } else if (target == AnnotationDatabase.Target.PARAMETER) {
      defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS;
    } else {
      throw new IllegalArgumentException("Unknown target for default annotation: " + target);
    }

    // Get the JSR-305 nullness annotation type
    ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(n);

    // Construct an AnnotationValue containing the default annotation
    AnnotationValue annotationValue = new AnnotationValue(defaultAnnotationType);
    AnnotationVisitor v = annotationValue.getAnnotationVisitor();
    v.visit("value", Type.getObjectType(nullnessAnnotationType.getClassName()));
    v.visitEnd();

    if (DEBUG) {
      System.out.println("Adding AnnotationValue " + annotationValue + " to class " + xclass);
    }

    // Destructively add the annotation to the ClassInfo object
    xclass.addAnnotation(annotationValue);
  }

  //	/* (non-Javadoc)
  //	 * @see
  // edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultMethodAnnotation(java.lang.String,
  // edu.umd.cs.findbugs.ba.NullnessAnnotation)
  //	 */
  //	public void addDefaultMethodAnnotation(String name, NullnessAnnotation annotation) {
  //	}

  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addFieldAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation)
   */
  public void addFieldAnnotation(
      String cName, String mName, String mSig, boolean isStatic, NullnessAnnotation annotation) {
    if (DEBUG) {
      System.out.println(
          "addFieldAnnotation: annotate " + cName + "." + mName + " with " + annotation);
    }

    XField xfield = XFactory.createXField(cName, mName, mSig, isStatic);
    if (!(xfield instanceof FieldInfo)) {
      if (DEBUG) {
        System.out.println(
            "  Field not found! "
                + cName
                + "."
                + mName
                + ":"
                + mSig
                + " "
                + isStatic
                + " "
                + annotation);
      }
      return;
    }

    // Get JSR-305 nullness annotation type
    ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);

    // Create an AnnotationValue
    AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType);

    // Destructively add the annotation to the FieldInfo object
    ((FieldInfo) xfield).addAnnotation(annotationValue);
  }

  public @CheckForNull XMethod getXMethod(
      String cName, String mName, String sig, boolean isStatic) {
    ClassDescriptor classDesc =
        DescriptorFactory.instance().getClassDescriptorForDottedClassName(cName);
    ClassInfo xclass;

    // Get the XClass (really a ClassInfo object)
    try {
      xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc);
    } catch (MissingClassException e) {
      if (DEBUG) {
        System.out.println("  Class not found!");
      }
      //
      //	AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
      return null;
    } catch (CheckedAnalysisException e) {
      if (DEBUG) {
        System.out.println("  Class not found!");
      }
      //			AnalysisContext.logError("Error adding built-in nullness annotation", e);
      return null;
    }
    XMethod xmethod = xclass.findMethod(mName, sig, isStatic);

    if (xmethod == null) xmethod = XFactory.createXMethod(cName, mName, sig, isStatic);
    return xmethod;
  }
  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, edu.umd.cs.findbugs.ba.NullnessAnnotation)
   */
  public void addMethodAnnotation(
      String cName, String mName, String sig, boolean isStatic, NullnessAnnotation annotation) {
    if (DEBUG) {
      System.out.println(
          "addMethodAnnotation: annotate " + cName + "." + mName + " with " + annotation);
    }
    XMethod xmethod = getXMethod(cName, mName, sig, isStatic);
    if (xmethod == null) return;
    // Get JSR-305 nullness annotation type
    ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);

    // Create an AnnotationValue
    AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType);

    // Destructively add the annotation to the MethodInfo object
    xmethod.addAnnotation(annotationValue);
  }

  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodParameterAnnotation(java.lang.String, java.lang.String, java.lang.String, boolean, int, edu.umd.cs.findbugs.ba.NullnessAnnotation)
   */
  public void addMethodParameterAnnotation(
      @DottedClassName String cName,
      String mName,
      String sig,
      boolean isStatic,
      int param,
      NullnessAnnotation annotation) {
    if (DEBUG) {
      System.out.println(
          "addMethodParameterAnnotation: annotate "
              + cName
              + "."
              + mName
              + " param "
              + param
              + " with "
              + annotation);
    }
    XMethod xmethod = getXMethod(cName, mName, sig, isStatic);
    if (xmethod == null) return;
    // Get JSR-305 nullness annotation type
    ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);

    // Create an AnnotationValue
    AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType);

    if (!xmethod.getClassName().equals(cName)) {
      if (false)
        AnalysisContext.logError(
            "Could not fully resolve method "
                + cName
                + "."
                + mName
                + sig
                + " to apply annotation "
                + annotation);
      return;
    }

    // Destructively add the annotation to the MethodInfo object
    xmethod.addParameterAnnotation(param, annotationValue);
  }

  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#loadAuxiliaryAnnotations()
   */
  public void loadAuxiliaryAnnotations() {
    DefaultNullnessAnnotations.addDefaultNullnessAnnotations(this);
  }

  /**
   * Convert a Nonnull-based TypeQualifierAnnotation into a NullnessAnnotation.
   *
   * @param tqa Nonnull-based TypeQualifierAnnotation
   * @return corresponding NullnessAnnotation
   */
  private NullnessAnnotation toNullnessAnnotation(@CheckForNull TypeQualifierAnnotation tqa) {
    if (tqa == null) {
      return null;
    }

    switch (tqa.when) {
      case ALWAYS:
        return NullnessAnnotation.NONNULL;
      case MAYBE:
        return NullnessAnnotation.CHECK_FOR_NULL;
      case NEVER:
        return NullnessAnnotation.CHECK_FOR_NULL;
      case UNKNOWN:
        return NullnessAnnotation.UNKNOWN_NULLNESS;
    }

    throw new IllegalStateException();
  }
}
Пример #23
0
 /**
  * Get a ClassDescriptor for the class described by given ObjectType object.
  *
  * @param type an ObjectType
  * @return a ClassDescriptor for the class described by the ObjectType
  * @deprecated Use {@link DescriptorFactory#getClassDescriptor(ObjectType)} instead
  */
 @Deprecated
 public static ClassDescriptor getClassDescriptor(ObjectType type) {
   return DescriptorFactory.getClassDescriptor(type);
 }
  private void analyzeMethod(ClassContext classContext, Method method)
      throws CFGBuilderException, DataflowAnalysisException {
    if (isSynthetic(method) || !prescreen(classContext, method)) return;
    XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
    if (xmethod.isSynthetic()) return;

    BugAccumulator accumulator = new BugAccumulator(bugReporter);

    CFG cfg = classContext.getCFG(method);
    TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
    ValueNumberDataflow vnDataflow = classContext.getValueNumberDataflow(method);

    ConstantPoolGen cpg = classContext.getConstantPoolGen();
    MethodGen methodGen = classContext.getMethodGen(method);
    if (methodGen == null) return;
    String fullMethodName = methodGen.getClassName() + "." + methodGen.getName();

    String sourceFile = classContext.getJavaClass().getSourceFileName();
    if (DEBUG) {
      System.out.println("\n" + fullMethodName);
    }

    // Process each instruction
    for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext(); ) {
      Location location = iter.next();
      InstructionHandle handle = location.getHandle();
      Instruction ins = handle.getInstruction();

      // Only consider invoke instructions
      if (!(ins instanceof InvokeInstruction)) continue;

      InvokeInstruction inv = (InvokeInstruction) ins;

      XMethod invokedMethod = XFactory.createXMethod(inv, cpg);

      String invokedMethodName = invokedMethod.getName();
      String argSignature = invokedMethod.getSignature();
      argSignature = argSignature.substring(0, argSignature.indexOf(')') + 1);
      String call = invokedMethodName + argSignature;
      SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));

      Collection<Info> collection = callMap.get(call);
      if (!callMap.containsKey(call)) continue;
      for (Info info : collection) {
        Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
        if (DEBUG)
          System.out.println(
              "at "
                  + handle.getPosition()
                  + " Checking call to "
                  + info.interfaceForCall
                  + " : "
                  + invokedMethod);
        try {
          if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall))
            continue;
        } catch (ClassNotFoundException e) {
          if (info.interfaceForCall.getClassName().equals("java/util/Collection")
              && invokedMethod.getClassName().equals("com.google.common.collect.Multiset")) {
            assert true;
            // we know this is OK without needing to find definition of Multiset
          } else {
            AnalysisContext.reportMissingClass(e);
            continue;
          }
        }

        boolean allMethod;

        int typeArgument;
        if (info.typeIndex >= 0) {
          allMethod = false;
          typeArgument = info.typeIndex;
        } else {
          allMethod = true;
          typeArgument = -(1 + info.typeIndex);
        }
        int pos = info.argumentIndex;

        int lhsPos;
        if (inv instanceof INVOKESTATIC) lhsPos = sigParser.getSlotsFromTopOfStackForParameter(0);
        else lhsPos = sigParser.getTotalArgumentSize();

        int stackPos = sigParser.getSlotsFromTopOfStackForParameter(pos);

        TypeFrame frame = typeDataflow.getFactAtLocation(location);
        if (!frame.isValid()) {
          // This basic block is probably dead
          continue;
        }

        Type operandType = frame.getStackValue(stackPos);
        if (operandType.equals(TopType.instance())) {
          // unreachable
          continue;
        }

        if (operandType.equals(NullType.instance())) {
          // ignore
          continue;
        }

        ValueNumberFrame vnFrame = vnDataflow.getFactAtLocation(location);

        if (!vnFrame.isValid()) {
          AnalysisContext.logError("Invalid value number frame in " + xmethod);
          continue;
        }

        ValueNumber objectVN = vnFrame.getStackValue(lhsPos);
        ValueNumber argVN = vnFrame.getStackValue(stackPos);

        if (objectVN.equals(argVN)) {
          String bugPattern = "DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES";
          int priority = HIGH_PRIORITY;
          if (invokedMethodName.equals("removeAll")) {
            bugPattern = "DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION";
            priority = NORMAL_PRIORITY;
          } else if (invokedMethodName.endsWith("All")) {
            bugPattern = "DMI_VACUOUS_SELF_COLLECTION_CALL";
            priority = NORMAL_PRIORITY;
          }
          if (invokedMethodName.startsWith("contains")) {
            InstructionHandle next = handle.getNext();
            if (next != null) {
              Instruction nextIns = next.getInstruction();

              if (nextIns instanceof InvokeInstruction) {
                XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
                if (nextMethod.getName().equals("assertFalse")) continue;
              }
            }
          }
          accumulator.accumulateBug(
              new BugInstance(this, bugPattern, priority)
                  .addClassAndMethod(methodGen, sourceFile)
                  .addCalledMethod(methodGen, (InvokeInstruction) ins)
                  .addOptionalAnnotation(
                      ValueNumberSourceInfo.findAnnotationFromValueNumber(
                          method, location, objectVN, vnFrame, "INVOKED_ON")),
              SourceLineAnnotation.fromVisitedInstruction(
                  classContext, methodGen, sourceFile, handle));
        }

        // Only consider generic...
        Type objectType = frame.getStackValue(lhsPos);
        if (!(objectType instanceof GenericObjectType)) continue;

        GenericObjectType operand = (GenericObjectType) objectType;

        int expectedTypeParameters = 1;
        String simpleName = info.interfaceForCall.getSimpleName();
        if (simpleName.toLowerCase().endsWith("map") || simpleName.equals("Hashtable"))
          expectedTypeParameters = 2;
        else if (simpleName.equals("Table")) expectedTypeParameters = 3;

        // ... containers
        if (!operand.hasParameters()) continue;
        if (operand.getNumParameters() != expectedTypeParameters) continue;
        ClassDescriptor operandClass = DescriptorFactory.getClassDescriptor(operand);
        if (!isGenericCollection(operandClass)) continue;

        if (expectedTypeParameters == 2
            && Subtypes2.instanceOf(operandClass, Map.class)
            && !TypeFrameModelingVisitor.isStraightGenericMap(operandClass)) continue;
        Type expectedType;
        if (allMethod) expectedType = operand;
        else expectedType = operand.getParameterAt(typeArgument);
        Type actualType = frame.getStackValue(stackPos);
        Type equalsType = actualType;
        if (allMethod) {
          if (!(actualType instanceof GenericObjectType)) {
            continue;
          }
          equalsType = ((GenericObjectType) actualType).getParameterAt(typeArgument);
        }

        IncompatibleTypes matchResult = compareTypes(expectedType, actualType, allMethod);

        boolean parmIsObject = expectedType.getSignature().equals("Ljava/lang/Object;");
        boolean selfOperation = !allMethod && operand.equals(actualType) && !parmIsObject;
        if (!allMethod && !parmIsObject && actualType instanceof GenericObjectType) {

          GenericObjectType p2 = (GenericObjectType) actualType;
          List<? extends ReferenceType> parameters = p2.getParameters();
          if (parameters != null && parameters.equals(operand.getParameters()))
            selfOperation = true;
        }

        if (!selfOperation
            && (matchResult == IncompatibleTypes.SEEMS_OK
                || matchResult.getPriority() == Priorities.IGNORE_PRIORITY)) continue;

        if (invokedMethodName.startsWith("contains") || invokedMethodName.equals("remove")) {
          InstructionHandle next = handle.getNext();
          if (next != null) {
            Instruction nextIns = next.getInstruction();

            if (nextIns instanceof InvokeInstruction) {
              XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
              if (nextMethod.getName().equals("assertFalse")) continue;
            }
          }
        } else if (invokedMethodName.equals("get") || invokedMethodName.equals("remove")) {
          InstructionHandle next = handle.getNext();
          if (next != null) {
            Instruction nextIns = next.getInstruction();

            if (nextIns instanceof InvokeInstruction) {
              XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
              if (nextMethod.getName().equals("assertNull")) continue;
            }
          }
        }
        boolean noisy = false;
        if (invokedMethodName.equals("get")) {
          UnconditionalValueDerefDataflow unconditionalValueDerefDataflow =
              classContext.getUnconditionalValueDerefDataflow(method);

          UnconditionalValueDerefSet unconditionalDeref =
              unconditionalValueDerefDataflow.getFactAtLocation(location);
          ValueNumberFrame vnAfter = vnDataflow.getFactAfterLocation(location);
          ValueNumber top = vnAfter.getTopValue();
          noisy =
              unconditionalDeref.getValueNumbersThatAreUnconditionallyDereferenced().contains(top);
        }
        // Prepare bug report
        SourceLineAnnotation sourceLineAnnotation =
            SourceLineAnnotation.fromVisitedInstruction(
                classContext, methodGen, sourceFile, handle);

        // Report a bug that mentions each of the failed arguments in
        // matches

        if (expectedType instanceof GenericObjectType)
          expectedType = ((GenericObjectType) expectedType).getUpperBound();

        int priority = matchResult.getPriority();
        if (!operandClass.getClassName().startsWith("java/util")
            && priority == Priorities.HIGH_PRIORITY)
          priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
        if (TestCaseDetector.likelyTestCase(xmethod))
          priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
        else if (selfOperation) priority = Priorities.HIGH_PRIORITY;
        ClassDescriptor expectedClassDescriptor =
            DescriptorFactory.createClassOrObjectDescriptorFromSignature(
                expectedType.getSignature());
        ClassDescriptor actualClassDescriptor =
            DescriptorFactory.createClassOrObjectDescriptorFromSignature(equalsType.getSignature());
        ClassSummary classSummary = AnalysisContext.currentAnalysisContext().getClassSummary();
        Set<XMethod> targets = null;
        try {
          targets =
              Hierarchy2.resolveVirtualMethodCallTargets(
                  actualClassDescriptor, "equals", "(Ljava/lang/Object;)Z", false, false);
          boolean allOk = targets.size() > 0;
          for (XMethod m2 : targets)
            if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), expectedClassDescriptor))
              allOk = false;
          if (allOk) priority += 2;
        } catch (ClassNotFoundException e) {
          AnalysisContext.reportMissingClass(e);
        }
        String bugPattern = "GC_UNRELATED_TYPES";

        BugInstance bug =
            new BugInstance(this, bugPattern, priority)
                .addClassAndMethod(methodGen, sourceFile)
                .addFoundAndExpectedType(actualType, expectedType)
                .addCalledMethod(methodGen, (InvokeInstruction) ins)
                .addOptionalAnnotation(
                    ValueNumberSourceInfo.findAnnotationFromValueNumber(
                        method, location, objectVN, vnFrame, "INVOKED_ON"))
                .addOptionalAnnotation(
                    ValueNumberSourceInfo.findAnnotationFromValueNumber(
                        method, location, argVN, vnFrame, "ARGUMENT"))
                .addEqualsMethodUsed(targets);
        if (noisy) {
          WarningPropertySet<WarningProperty> propertySet =
              new WarningPropertySet<WarningProperty>();

          propertySet.addProperty(GeneralWarningProperty.NOISY_BUG);
          propertySet.decorateBugInstance(bug);
        }
        accumulator.accumulateBug(bug, sourceLineAnnotation);
      }
    }
    accumulator.reportAccumulatedBugs();
  }
 public InconsistentAnnotations(BugReporter reporter) {
   ClassDescriptor nonnullClassDesc =
       DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class);
   this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
   this.reporter = reporter;
 }
Пример #26
0
 public void addAnnotation(String name, AnnotationValue value) {
   ClassDescriptor annotationClass = DescriptorFactory.createClassDescriptorFromSignature(name);
   methodAnnotations.put(annotationClass, value);
 }
  @Override
  public void visit(JavaClass obj) {
    String superClassname = obj.getSuperclassName();
    // System.out.println("superclass of " + getClassName() + " is " + superClassname);
    isEnum = superClassname.equals("java.lang.Enum");
    if (isEnum) return;
    int flags = obj.getAccessFlags();
    isAbstract = (flags & ACC_ABSTRACT) != 0 || (flags & ACC_INTERFACE) != 0;
    isAnonymousInnerClass = anonymousInnerClassNamePattern.matcher(getClassName()).matches();
    innerClassHasOuterInstance = false;
    for (Field f : obj.getFields()) {
      if (f.getName().equals("this$0")) {
        innerClassHasOuterInstance = true;
        break;
      }
    }

    sawSerialVersionUID = false;
    isSerializable = implementsSerializableDirectly = false;
    isExternalizable = false;
    directlyImplementsExternalizable = false;
    isGUIClass = false;
    isEjbImplClass = false;
    seenTransientField = false;
    // boolean isEnum = obj.getSuperclassName().equals("java.lang.Enum");
    fieldsThatMightBeAProblem.clear();
    transientFieldsUpdates.clear();
    transientFieldsSetInConstructor.clear();
    transientFieldsSetToDefaultValueInConstructor.clear();
    // isRemote = false;

    // Does this class directly implement Serializable?
    String[] interface_names = obj.getInterfaceNames();
    for (String interface_name : interface_names) {
      if (interface_name.equals("java.io.Externalizable")) {
        directlyImplementsExternalizable = true;
        isExternalizable = true;
        if (DEBUG) {
          System.out.println("Directly implements Externalizable: " + getClassName());
        }
      } else if (interface_name.equals("java.io.Serializable")) {
        implementsSerializableDirectly = true;
        isSerializable = true;
        if (DEBUG) {
          System.out.println("Directly implements Serializable: " + getClassName());
        }
        break;
      }
    }

    // Does this class indirectly implement Serializable?
    if (!isSerializable) {
      if (Subtypes2.instanceOf(obj, "java.io.Externalizable")) {
        isExternalizable = true;
        if (DEBUG) {
          System.out.println("Indirectly implements Externalizable: " + getClassName());
        }
      }
      if (Subtypes2.instanceOf(obj, "java.io.Serializable")) {
        isSerializable = true;
        if (DEBUG) {
          System.out.println("Indirectly implements Serializable: " + getClassName());
        }
      }
    }

    hasPublicVoidConstructor = false;
    superClassHasVoidConstructor = true;
    superClassHasReadObject = false;
    superClassImplementsSerializable = isSerializable && !implementsSerializableDirectly;
    ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor();
    if (superclassDescriptor != null)
      try {
        XClass superXClass =
            Global.getAnalysisCache().getClassAnalysis(XClass.class, superclassDescriptor);
        if (superXClass != null) {
          superClassImplementsSerializable =
              AnalysisContext.currentAnalysisContext()
                  .getSubtypes2()
                  .isSubtype(
                      superXClass.getClassDescriptor(),
                      DescriptorFactory.createClassDescriptor(java.io.Serializable.class));
          superClassHasVoidConstructor = false;
          for (XMethod m : superXClass.getXMethods()) {
            if (m.getName().equals("<init>") && m.getSignature().equals("()V") && !m.isPrivate()) {
              superClassHasVoidConstructor = true;
            }
            if (m.getName().equals("readObject")
                && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")
                && m.isPrivate()) superClassHasReadObject = true;
          }
        }
      } catch (ClassNotFoundException e) {
        bugReporter.reportMissingClass(e);
      } catch (CheckedAnalysisException e) {
        bugReporter.logError("huh", e);
      }

    // Is this a GUI  or other class that is rarely serialized?

    isGUIClass = false;
    isEjbImplClass = false;
    if (true || !directlyImplementsExternalizable && !implementsSerializableDirectly) {
      isEjbImplClass = Subtypes2.instanceOf(obj, "javax.ejb.SessionBean");
      isGUIClass =
          (Subtypes2.instanceOf(obj, "java.lang.Throwable")
              || Subtypes2.instanceOf(obj, "java.awt.Component")
              || Subtypes2.instanceOf(obj, "java.awt.Component$AccessibleAWTComponent")
              || Subtypes2.instanceOf(obj, "java.awt.event.ActionListener")
              || Subtypes2.instanceOf(obj, "java.util.EventListener"));
      if (!isGUIClass) {
        JavaClass o = obj;
        while (o != null) {
          if (o.getClassName().startsWith("java.awt")
              || o.getClassName().startsWith("javax.swing")) {
            isGUIClass = true;
            break;
          }
          try {
            o = o.getSuperClass();
          } catch (ClassNotFoundException e) {
            break;
          }
        }
      }
    }

    foundSynthetic = false;
    foundSynchronizedMethods = false;
    writeObjectIsSynchronized = false;

    sawReadExternal = sawWriteExternal = sawReadObject = sawReadResolve = sawWriteObject = false;
    if (isSerializable) {
      for (Method m : obj.getMethods()) {

        if (m.getName().equals("readObject")
            && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")) sawReadObject = true;
        else if (m.getName().equals("readResolve") && m.getSignature().startsWith("()"))
          sawReadResolve = true;
        else if (m.getName().equals("readObjectNoData") && m.getSignature().equals("()V"))
          sawReadObject = true;
        else if (m.getName().equals("writeObject")
            && m.getSignature().equals("(Ljava/io/ObjectOutputStream;)V")) sawWriteObject = true;
      }
      for (Field f : obj.getFields()) {
        if (f.isTransient()) seenTransientField = true;
      }
    }
  }
  private TypeQualifierValue(ClassDescriptor typeQualifier, @CheckForNull Object value) {
    this.typeQualifier = typeQualifier;
    this.value = value;
    boolean isStrict = false; // will be set to true if this is a strict
    // type qualifier value
    boolean isExclusive = false; // will be set to true if this is an
    // exclusive type qualifier value
    boolean isExhaustive = false; // will be set to true if this is an
    // exhaustive type qualifier value

    TypeQualifierValidator<A> validator = null;
    Class<A> qualifierClass = null;
    A proxy = null;
    try {
      XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, typeQualifier);

      // Annotation elements appear as abstract methods in the annotation
      // class (interface).
      // So, if the type qualifier annotation has specified a default When
      // value,
      // it will appear as an abstract method called "when".
      XMethod whenMethod = xclass.findMethod("when", "()Ljavax/annotation/meta/When;", false);
      if (whenMethod == null) {
        isStrict = true;
      }
      for (XMethod xmethod : xclass.getXMethods()) {
        if (xmethod.getName().equals("value") && xmethod.getSignature().startsWith("()")) {
          isExhaustive = xmethod.getAnnotation(EXHAUSTIVE_ANNOTATION) != null;
          if (isExhaustive) {
            // exhaustive qualifiers are automatically exclusive
            isExclusive = true;
          } else {
            // see if there is an explicit @Exclusive annotation
            isExclusive = xmethod.getAnnotation(EXCLUSIVE_ANNOTATION) != null;
          }

          break;
        }
      }
    } catch (MissingClassException e) {
      AnalysisContext.currentAnalysisContext()
          .getLookupFailureCallback()
          .reportMissingClass(e.getClassNotFoundException());
    } catch (CheckedAnalysisException e) {
      AnalysisContext.logError(
          "Error looking up annotation class " + typeQualifier.toDottedClassName(), e);
    }
    this.isStrict = isStrict;
    this.isExclusive = isExclusive;
    this.isExhaustive = isExhaustive;
    ClassDescriptor checkerName =
        DescriptorFactory.createClassDescriptor(typeQualifier.getClassName() + "$Checker");
    try {
      Global.getAnalysisCache().getClassAnalysis(ClassData.class, checkerName);
      // found it.
      //            System.out.println(checkerName);
      SecurityManager m = System.getSecurityManager();
      if (m == null) System.setSecurityManager(new ValidationSecurityManager());
      Class<?> c = validatorLoader.loadClass(checkerName.getDottedClassName());
      if (TypeQualifierValidator.class.isAssignableFrom(c)) {
        Class<? extends TypeQualifierValidator> checkerClass =
            c.asSubclass(TypeQualifierValidator.class);
        validator = getValidator(checkerClass);
        qualifierClass = getQualifierClass(typeQualifier);

        InvocationHandler handler =
            new InvocationHandler() {

              public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
                if (arg1.getName() == "value") return TypeQualifierValue.this.value;
                throw new UnsupportedOperationException("Can't handle " + arg1);
              }
            };

        proxy =
            qualifierClass.cast(
                Proxy.newProxyInstance(validatorLoader, new Class[] {qualifierClass}, handler));
      }
    } catch (ClassNotFoundException e) {
      assert true; // ignore
    } catch (CheckedAnalysisException e) {
      assert true; // ignore
    } catch (Exception e) {
      AnalysisContext.logError("Unable to construct type qualifier checker " + checkerName, e);
    } catch (Throwable e) {
      AnalysisContext.logError(
          "Unable to construct type qualifier checker "
              + checkerName
              + " due to "
              + e.getClass().getSimpleName()
              + ":"
              + e.getMessage());
    }
    this.validator = validator;
    this.typeQualifierClass = qualifierClass;
    this.proxy = proxy;
  }
  /**
   * Resolve possible instance method call targets.
   *
   * @param receiverType type of the receiver object
   * @param invokeInstruction the InvokeInstruction
   * @param cpg the ConstantPoolGen
   * @param receiverTypeIsExact if true, the receiver type is known exactly, which should allow a
   *     precise result
   * @return Set of methods which might be called
   * @throws ClassNotFoundException
   */
  public static Set<JavaClassAndMethod> resolveMethodCallTargets(
      ReferenceType receiverType,
      InvokeInstruction invokeInstruction,
      ConstantPoolGen cpg,
      boolean receiverTypeIsExact)
      throws ClassNotFoundException {
    HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>();

    if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC)
      throw new IllegalArgumentException();

    String methodName = invokeInstruction.getName(cpg);
    String methodSig = invokeInstruction.getSignature(cpg);

    // Array method calls aren't virtual.
    // They should just resolve to Object methods.
    if (receiverType instanceof ArrayType) {
      JavaClass javaLangObject =
          AnalysisContext.currentAnalysisContext().lookupClass("java.lang.Object");
      JavaClassAndMethod classAndMethod =
          findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD);
      if (classAndMethod != null) result.add(classAndMethod);
      return result;
    }

    if (receiverType instanceof NullType) {
      return Collections.<JavaClassAndMethod>emptySet();
    }
    AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();

    // Get the receiver class.
    String receiverClassName = ((ObjectType) receiverType).getClassName();
    JavaClass receiverClass = analysisContext.lookupClass(receiverClassName);
    ClassDescriptor receiverDesc =
        DescriptorFactory.createClassDescriptorFromDottedClassName(receiverClassName);

    // Figure out the upper bound for the method.
    // This is what will be called if this is not a virtual call site.
    JavaClassAndMethod upperBound =
        findMethod(receiverClass, methodName, methodSig, CONCRETE_METHOD);
    if (upperBound == null) {
      upperBound =
          findInvocationLeastUpperBound(
              receiverClass, methodName, methodSig, CONCRETE_METHOD, false);
    }
    if (upperBound != null) {
      if (DEBUG_METHOD_LOOKUP) {
        System.out.println(
            "Adding upper bound: "
                + SignatureConverter.convertMethodSignature(
                    upperBound.getJavaClass(), upperBound.getMethod()));
      }
      result.add(upperBound);
    }

    // Is this a virtual call site?
    boolean virtualCall =
        (invokeInstruction.getOpcode() == Constants.INVOKEVIRTUAL
                || invokeInstruction.getOpcode() == Constants.INVOKEINTERFACE)
            && (upperBound == null
                || !upperBound.getJavaClass().isFinal() && !upperBound.getMethod().isFinal())
            && !receiverTypeIsExact;

    if (virtualCall) {
      if (!receiverClassName.equals("java.lang.Object")) {

        // This is a true virtual call: assume that any concrete
        // subtype method may be called.
        Set<ClassDescriptor> subTypeSet = analysisContext.getSubtypes2().getSubtypes(receiverDesc);
        for (ClassDescriptor subtype : subTypeSet) {
          XMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, false);
          if (concreteSubtypeMethod != null
              && (concreteSubtypeMethod.getAccessFlags() & Constants.ACC_ABSTRACT) == 0) {
            result.add(new JavaClassAndMethod(concreteSubtypeMethod));
          }
        }
        if (false && subTypeSet.size() > 500)
          new RuntimeException(
                  receiverClassName
                      + " has "
                      + subTypeSet.size()
                      + " subclasses, "
                      + result.size()
                      + " of which implement "
                      + methodName
                      + methodSig
                      + " "
                      + invokeInstruction)
              .printStackTrace(System.out);
      }
    }
    return result;
  }
Пример #30
0
  public static double isDeepSerializable(JavaClass x) throws ClassNotFoundException {
    if (storedException != null) throw storedException;

    if (x.getClassName().equals("java.lang.Object")) return 0.4;

    if (DEBUG) {
      System.out.println("checking " + x.getClassName());
    }

    double result = Analyze.deepInstanceOf(x, serializable);
    if (result >= 0.9) {
      if (DEBUG) {
        System.out.println("Direct high serializable result: " + result);
      }
      return result;
    }

    if (x.isFinal()) return result;

    double collectionResult = Analyze.deepInstanceOf(x, collection);
    double mapResult = Analyze.deepInstanceOf(x, map);

    if (x.isInterface() || x.isAbstract()) {
      result = Math.max(result, Math.max(mapResult, collectionResult) * 0.95);
      if (result >= 0.9) {
        return result;
      }
    }
    ClassDescriptor classDescriptor = DescriptorFactory.createClassDescriptor(x);

    Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();

    Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(classDescriptor);
    directSubtypes.remove(classDescriptor);

    double confidence = 0.6;
    if (x.isAbstract() || x.isInterface()) {
      confidence = 0.8;
      result = Math.max(result, 0.4);
    } else if (directSubtypes.isEmpty()) confidence = 0.2;

    double confidence2 = (1 + confidence) / 2;
    result = Math.max(result, confidence2 * collectionResult);
    if (result >= 0.9) {
      if (DEBUG) {
        System.out.println("High collection result: " + result);
      }
      return result;
    }
    result = Math.max(result, confidence2 * mapResult);
    if (result >= 0.9) {
      if (DEBUG) {
        System.out.println("High map result: " + result);
      }
      return result;
    }
    result = Math.max(result, confidence2 * 0.5 * Analyze.deepInstanceOf(x, comparator));
    if (result >= 0.9) {
      if (DEBUG) {
        System.out.println("High comparator result: " + result);
      }
      return result;
    }

    for (ClassDescriptor subtype : directSubtypes) {
      JavaClass subJavaClass = Repository.lookupClass(subtype.getDottedClassName());
      result = Math.max(result, confidence * Analyze.deepInstanceOf(subJavaClass, serializable));

      // result = Math.max(result, confidence * isDeepSerializable(subJavaClass));
      if (result >= 0.9) {
        return result;
      }
    }

    if (DEBUG) {
      System.out.println("No high results; max: " + result);
    }
    return result;
  }