Exemplo n.º 1
0
  private boolean bad(Item left, Item right) {
    XMethod m = left.getReturnValueOf();

    if (m == null) return false;
    Object value = right.getConstant();
    if (!(value instanceof Integer) && ((Integer) value).intValue() == 0) return false;
    if (m.isStatic() || !m.isPublic()) return false;

    if (m.getName().equals("compareTo") && m.getSignature().equals("(Ljava/lang/Object;)I"))
      return true;
    if (m.getName().equals("compare")
        && m.getSignature().equals("(Ljava/lang/Object;Ljava/lang/Object;)I")) return true;

    return false;
  }
  /* (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;
  }
  public static Set<ValueNumber> checkNonNullParams(
      Location location,
      ValueNumberFrame vnaFrame,
      ConstantPoolGen constantPool,
      @CheckForNull Method method,
      @CheckForNull IsNullValueFrame invFrame)
      throws DataflowAnalysisException {

    if (invFrame != null && !invFrame.isValid()) {
      return Collections.emptySet();
    }
    INullnessAnnotationDatabase database =
        AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase();

    InvokeInstruction inv = (InvokeInstruction) location.getHandle().getInstruction();
    XMethod called = XFactory.createXMethod(inv, constantPool);
    SignatureParser sigParser = new SignatureParser(called.getSignature());
    int numParams = sigParser.getNumParameters();

    Set<ValueNumber> result = new HashSet<ValueNumber>();
    Iterator<String> parameterIterator = sigParser.parameterSignatureIterator();
    for (int i = 0; i < numParams; i++) {
      String parameterSignature = parameterIterator.next();
      char firstChar = parameterSignature.charAt(0);
      if (firstChar != 'L' && firstChar != '[') {
        continue;
      }
      int offset = sigParser.getSlotsFromTopOfStackForParameter(i);
      if (invFrame != null) {
        int slot = invFrame.getStackLocation(offset);
        if (!reportDereference(invFrame, slot)) {
          continue;
        }
      }
      if (database.parameterMustBeNonNull(called, i)) {
        int catchSizeNPE =
            Util.getSizeOfSurroundingTryBlock(
                method, "java/lang/NullPointerException", location.getHandle().getPosition());
        int catchSizeNFE =
            Util.getSizeOfSurroundingTryBlock(
                method, "java/lang/NumberFormatException", location.getHandle().getPosition());
        if (catchSizeNPE == Integer.MAX_VALUE
            && (!"java.lang.Integer".equals(called.getClassName())
                || catchSizeNFE == Integer.MAX_VALUE)) {
          // Get the corresponding value number
          ValueNumber vn = vnaFrame.getArgument(inv, constantPool, i, sigParser);
          result.add(vn);
        }
      }
    }
    return result;
  }
  @Override
  public void sawOpcode(int seen) {
    // only acts on constructor invoke
    if (seen != INVOKESPECIAL) {
      return;
    }

    if (!"<init>".equals(getNameConstantOperand())) {
      return;
    }
    String cls = getClassConstantOperand();
    XMethod shouldCall = boxClasses.get(cls);
    if (shouldCall == null) {
      return;
    }

    if (!shouldCall
        .getSignature()
        .substring(0, 3)
        .equals(getSigConstantOperand().substring(0, 3))) {
      return;
    }

    int prio;
    String type;
    if (cls.equals("java/lang/Float") || cls.equals("java/lang/Double")) {
      prio = LOW_PRIORITY;
      type = "DM_FP_NUMBER_CTOR";
    } else {
      prio = NORMAL_PRIORITY;
      Object constantValue = stack.getStackItem(0).getConstant();
      if (constantValue instanceof Number) {
        long value = ((Number) constantValue).longValue();
        if (value < -128 || value > 127) prio = LOW_PRIORITY;
      }

      type = "DM_NUMBER_CTOR";
    }

    BugInstance bug =
        new BugInstance(this, type, prio)
            .addClass(this)
            .addMethod(this)
            .addCalledMethod(this)
            .addMethod(shouldCall)
            .describe("SHOULD_CALL");
    bugAccumulator.accumulateBug(bug, this);
  }
  @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 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();
  }
  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;
  }