Example #1
0
  @Override
  public void visitClassContext(ClassContext classContext) {
    if (!enabled()) {
      return;
    }

    JavaClass jClass = classContext.getJavaClass();
    XClass xClass = classContext.getXClass();

    try {

      if (!isJunit3TestCase(xClass)) {
        return;
      }
      if ((jClass.getAccessFlags() & ACC_ABSTRACT) == 0) {
        if (!hasTestMethods(jClass)) {
          bugReporter.reportBug(
              new BugInstance(this, "IJU_NO_TESTS", LOW_PRIORITY).addClass(jClass));
        }
      }
      directChildOfTestCase = "junit.framework.TestCase".equals(jClass.getSuperclassName());
      jClass.accept(this);
    } catch (ClassNotFoundException cnfe) {
      bugReporter.reportMissingClass(cnfe);
    }
  }
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();

    // The class extends WebChromeClient
    boolean isWebChromeClient =
        InterfaceUtils.isSubtype(javaClass, "android.webkit.WebChromeClient");

    // Not the target of this detector
    if (!isWebChromeClient) return;

    Method[] methodList = javaClass.getMethods();

    for (Method m : methodList) {
      MethodGen methodGen = classContext.getMethodGen(m);

      if (DEBUG) System.out.println(">>> Method: " + m.getName());

      // The presence of onGeolocationPermissionsShowPrompt is not enforce for the moment
      if (!m.getName().equals("onGeolocationPermissionsShowPrompt")) {
        continue;
      }

      // Since the logic implemented need to be analyze by a human, all implementation will be
      // flagged.

      bugReporter.reportBug(
          new BugInstance(this, ANDROID_GEOLOCATION_TYPE, Priorities.NORMAL_PRIORITY) //
              .addClassAndMethod(javaClass, m));
    }
  }
  @Override
  public void visitClassContext(ClassContext classContext) {
    if (!testingEnabled) {
      return;
    }
    analysisContext = AnalysisContext.currentAnalysisContext();
    Method[] methodList = classContext.getJavaClass().getMethods();
    for (Method method : methodList) {
      if (method.getCode() == null) {
        continue;
      }

      try {

        analyzeMethod(classContext, method);
      } catch (CFGBuilderException e) {
        bugReporter.logError(
            "Error checking for infinite recursive loop in "
                + SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method),
            e);
      } catch (DataflowAnalysisException e) {
        bugReporter.logError(
            "Error checking for infinite recursive loop in "
                + SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method),
            e);
      }
    }
  }
  /**
   * @param classContext
   * @param method
   */
  private void analyzeMethod(ClassContext classContext, Method method)
      throws MethodUnprofitableException, CFGBuilderException, DataflowAnalysisException {
    if (method.isSynthetic()
        || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) return;
    CFG cfg = classContext.getCFG(method);
    TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
    ConstantPoolGen constantPoolGen = classContext.getConstantPoolGen();

    locationLoop:
    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;
      if (ins instanceof INVOKEINTERFACE) continue;

      InvokeInstruction inv = (InvokeInstruction) ins;
      TypeFrame frame = typeDataflow.getFactAtLocation(location);

      String methodName = inv.getMethodName(constantPoolGen);
      if (methodName.toLowerCase().indexOf("unsupported") >= 0) continue;
      String methodSig = inv.getSignature(constantPoolGen);
      if (methodSig.equals("()Ljava/lang/UnsupportedOperationException;")) continue;

      Set<XMethod> targets;
      try {

        targets = Hierarchy2.resolveMethodCallTargets(inv, frame, constantPoolGen);
      } catch (ClassNotFoundException e) {
        AnalysisContext.reportMissingClass(e);
        continue locationLoop;
      }
      if (targets.isEmpty()) continue locationLoop;
      int priority = targets.size() == 1 ? Priorities.HIGH_PRIORITY : Priorities.NORMAL_PRIORITY;
      for (XMethod m : targets) {
        if (!m.isUnsupported()) continue locationLoop;
        XClass xc = AnalysisContext.currentXFactory().getXClass(m.getClassDescriptor());
        if (!(inv instanceof INVOKESTATIC) && !(m.isFinal() || xc.isFinal()))
          priority = Priorities.NORMAL_PRIORITY;
        if (xc == null || xc.isAbstract()) {
          try {
            if (!AnalysisContext.currentAnalysisContext()
                .getSubtypes2()
                .hasSubtypes(m.getClassDescriptor())) continue locationLoop;
          } catch (ClassNotFoundException e) {
            AnalysisContext.reportMissingClass(e);
            continue locationLoop;
          }
        }
      }
      BugInstance bug =
          new BugInstance(this, "DMI_UNSUPPORTED_METHOD", priority)
              .addClassAndMethod(classContext.getJavaClass(), method)
              .addCalledMethod(constantPoolGen, inv)
              .addSourceLine(classContext, method, location);
      bugReporter.reportBug(bug);
    }
  }
Example #5
0
 @Override
 public void visitClassContext(ClassContext classContext) {
   ConstantPoolGen cpg = classContext.getConstantPoolGen();
   List<InjectionSource> selectedSources = new ArrayList<InjectionSource>();
   for (InjectionSource source : getInjectionSource()) {
     if (source.isCandidate(cpg)) {
       selectedSources.add(source);
     }
   }
   if (selectedSources.isEmpty()) {
     // return; // analysis still must be requested
   }
   for (Method method : classContext.getMethodsInCallOrder()) {
     MethodGen methodGen = classContext.getMethodGen(method);
     if (methodGen == null) {
       continue;
     }
     try {
       analyzeMethod(classContext, method, selectedSources);
     } catch (CheckedAnalysisException e) {
       logException(classContext, method, e);
     } catch (RuntimeException e) {
       logException(classContext, method, e);
     }
   }
 }
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();

    Method[] methodList = javaClass.getMethods();

    for (Method m : methodList) {
      MethodGen methodGen = classContext.getMethodGen(m);

      if (DEBUG) {
        System.out.println(">>> Method: " + m.getName());
      }

      // To suspect that an invalid String representation is being build,
      // we identify the construction of a MessageDigest and
      // the use of a function that trim leading 0.
      boolean invokeMessageDigest = false;
      boolean invokeToHexString = false;

      ConstantPoolGen cpg = classContext.getConstantPoolGen();
      if (methodGen == null || methodGen.getInstructionList() == null) {
        continue; // No instruction .. nothing to do
      }
      for (Iterator itIns = methodGen.getInstructionList().iterator(); itIns.hasNext(); ) {
        Instruction inst = ((InstructionHandle) itIns.next()).getInstruction();
        if (DEBUG) {
          ByteCode.printOpCode(inst, cpg);
        }

        if (inst instanceof INVOKEVIRTUAL) { // MessageDigest.digest is called
          INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) inst;
          if ("java.security.MessageDigest".equals(invoke.getClassName(cpg))
              && "digest".equals(invoke.getMethodName(cpg))) {
            invokeMessageDigest = true;
          }
        } else if (inst instanceof INVOKESTATIC
            && invokeMessageDigest) { // The conversion must occurs after the digest was created
          INVOKESTATIC invoke = (INVOKESTATIC) inst;
          if ("java.lang.Integer".equals(invoke.getClassName(cpg))
              && "toHexString".equals(invoke.getMethodName(cpg))) {
            invokeToHexString = true;
          }
        }
      }

      if (invokeMessageDigest && invokeToHexString) {
        bugReporter.reportBug(
            new BugInstance(this, BAD_HEXA_CONVERSION_TYPE, Priorities.NORMAL_PRIORITY) //
                .addClassAndMethod(javaClass, m));
      }
    }
  }
  /**
   * overrides the visitor to initialize and tear down the opcode stack
   *
   * @param classContext the context object of the currently parsed class
   */
  @Override
  public void visitClassContext(ClassContext classContext) {
    try {
      String clsName = classContext.getJavaClass().getClassName();
      isInnerClass = clsName.contains("$");

      clsSignature = SignatureUtils.classToSignature(clsName);
      stack = new OpcodeStack();
      localSpecialObjects = new HashMap<>();
      fieldSpecialObjects = new HashMap<>();
      super.visitClassContext(classContext);

      if (!isInnerClass && !fieldSpecialObjects.isEmpty()) {

        for (Map.Entry<String, String> entry : fieldSpecialObjects.entrySet()) {
          String fieldName = entry.getKey();
          String signature = entry.getValue();
          bugReporter.reportBug(
              makeFieldBugInstance().addClass(this).addField(clsName, fieldName, signature, false));
        }
      }
    } finally {
      stack = null;
      localSpecialObjects = null;
      fieldSpecialObjects = null;
    }
  }
 /**
  * The detector is only meaningful for Java5 class libraries.
  *
  * @param classContext the context object that holds the JavaClass parsed
  */
 @Override
 public void visitClassContext(ClassContext classContext) {
   int majorVersion = classContext.getJavaClass().getMajor();
   if (majorVersion >= MAJOR_1_5) {
     super.visitClassContext(classContext);
   }
 }
Example #9
0
 private static Iterator<Location> getLocationIterator(ClassContext classContext, Method method)
     throws CheckedAnalysisException {
   try {
     return classContext.getCFG(method).locationIterator();
   } catch (CFGBuilderException ex) {
     throw new CheckedAnalysisException("cannot get control flow graph", ex);
   }
 }
 /** Use this to screen out methods that do not contain invocations. */
 public boolean prescreen(ClassContext classContext, Method method) {
   BitSet bytecodeSet = classContext.getBytecodeSet(method);
   return bytecodeSet != null
       && (bytecodeSet.get(Constants.INVOKEINTERFACE)
           || bytecodeSet.get(Constants.INVOKEVIRTUAL)
           || bytecodeSet.get(Constants.INVOKESPECIAL)
           || bytecodeSet.get(Constants.INVOKESTATIC)
           || bytecodeSet.get(Constants.INVOKENONVIRTUAL));
 }
 @Override
 public void visitClassContext(ClassContext context) {
   try {
     stack = new OpcodeStack();
     clsName = context.getJavaClass().getClassName();
     super.visitClassContext(context);
   } finally {
     stack = null;
   }
 }
  public static @CheckForNull LocalVariableAnnotation findMatchingIgnoredParameter(
      ClassContext classContext, Method method, String name, String signature) {
    try {
      Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow =
          classContext.getLiveLocalStoreDataflow(method);
      CFG cfg;

      cfg = classContext.getCFG(method);
      LocalVariableAnnotation match = null;
      int lowestCost = Integer.MAX_VALUE;
      BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
      int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
      int startIndex = 0;
      if (!method.isStatic()) startIndex = 1;
      SignatureParser parser = new SignatureParser(method.getSignature());
      Iterator<String> signatureIterator = parser.parameterSignatureIterator();
      for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) {
        String sig = signatureIterator.next();
        if (!liveStoreSetAtEntry.get(i) && signature.equals(sig)) {
          // parameter isn't live and signatures match
          LocalVariableAnnotation potentialMatch =
              LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0);
          potentialMatch.setDescription(DID_YOU_MEAN_ROLE);
          if (!potentialMatch.isNamed()) return potentialMatch;
          int distance = EditDistance.editDistance(name, potentialMatch.getName());
          if (distance < lowestCost) {
            match = potentialMatch;
            match.setDescription(DID_YOU_MEAN_ROLE);
            lowestCost = distance;
          } else if (distance == lowestCost) {
            // not unique best match
            match = null;
          }
        }
      }
      return match;
    } catch (DataflowAnalysisException e) {
      AnalysisContext.logError("", e);
    } catch (CFGBuilderException e) {
      AnalysisContext.logError("", e);
    }
    return null;
  }
Example #13
0
  private void analyzeMethod(
      ClassContext classContext, Method method, Collection<InjectionSource> selectedSources)
      throws DataflowAnalysisException, CheckedAnalysisException {
    TaintDataflow dataflow = getTaintDataFlow(classContext, method);
    ConstantPoolGen cpg = classContext.getConstantPoolGen();
    String currentMethod = getFullMethodName(classContext.getMethodGen(method));
    for (Iterator<Location> i = getLocationIterator(classContext, method); i.hasNext(); ) {
      Location location = i.next();
      InstructionHandle handle = location.getHandle();
      Instruction instruction = handle.getInstruction();
      if (!(instruction instanceof InvokeInstruction)) {
        continue;
      }
      InvokeInstruction invoke = (InvokeInstruction) instruction;
      TaintFrame fact = dataflow.getFactAtLocation(location);
      assert fact != null;
      if (!fact.isValid()) {
        continue;
      }
      SourceLineAnnotation sourceLine =
          SourceLineAnnotation.fromVisitedInstruction(classContext, method, handle);
      checkTaintSink(getFullMethodName(cpg, invoke), fact, sourceLine, currentMethod);
      InjectionPoint injectionPoint = getInjectionPoint(invoke, cpg, handle, selectedSources);
      for (int offset : injectionPoint.getInjectableArguments()) {

        Taint parameterTaint = fact.getStackValue(offset);

        int priority = getPriority(parameterTaint);
        if (priority == Priorities.IGNORE_PRIORITY) {
          continue;
        }
        BugInstance bugInstance = new BugInstance(this, injectionPoint.getBugType(), priority);

        bugInstance.addClassAndMethod(classContext.getJavaClass(), method);
        bugInstance.addSourceLine(sourceLine);
        if (injectionPoint.getInjectableMethod() != null) {
          bugInstance.addString(injectionPoint.getInjectableMethod());
        }
        reportBug(bugInstance, parameterTaint, currentMethod);
      }
    }
  }
 /**
  * implements the visitor to create and clear the stack and syncRegs
  *
  * @param classContext the context object of the currently parsed class
  */
 @Override
 public void visitClassContext(ClassContext classContext) {
   try {
     stack = new OpcodeStack();
     syncRegs = new HashMap<Integer, CollectionRegInfo>();
     classVersion = classContext.getJavaClass().getMajor();
     super.visitClassContext(classContext);
   } finally {
     stack = null;
     syncRegs = null;
   }
 }
Example #15
0
 @Override
 public void visitClassContext(ClassContext classContext) {
   try {
     JavaClass cls = classContext.getJavaClass();
     if (cls.getMajor() >= MAJOR_1_4) {
       stack = new OpcodeStack();
       regValueType = new HashMap<Integer, State>();
       super.visitClassContext(classContext);
     }
   } finally {
     stack = null;
     regValueType = null;
   }
 }
  public void visitClassContext(ClassContext classContext) {

    JavaClass jclass = classContext.getJavaClass();

    for (Method method : jclass.getMethods()) {
      XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
      ParameterProperty nonnullParameters =
          AnalysisContext.currentAnalysisContext()
              .getUnconditionalDerefParamDatabase()
              .getProperty(xmethod.getMethodDescriptor());
      if (nonnullParameters != null) {
        for (int p : nonnullParameters.iterable()) {
          TypeQualifierAnnotation directTypeQualifierAnnotation =
              TypeQualifierApplications.getDirectTypeQualifierAnnotation(
                  xmethod, p, nonnullTypeQualifierValue);
          if (directTypeQualifierAnnotation != null
              && directTypeQualifierAnnotation.when == When.UNKNOWN) {
            //
            // The LocalVariableAnnotation is constructed using the local variable
            // number of the parameter, not the parameter number.
            //
            int paramLocal = xmethod.isStatic() ? p : p + 1;

            reporter.reportBug(
                new BugInstance(
                        this,
                        "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE",
                        NORMAL_PRIORITY)
                    .addClassAndMethod(jclass, method)
                    .add(
                        LocalVariableAnnotation.getParameterLocalVariableAnnotation(
                            method, paramLocal)));
          }
        }
      }
    }
  }
 /**
  * implements the detector to collect the super classes
  *
  * @param classContext the context object for the currently parsed class
  */
 @Override
 public void visitClassContext(ClassContext classContext) {
   try {
     clsContext = classContext;
     JavaClass cls = classContext.getJavaClass();
     if (cls.isInterface()) return;
     superClasses = cls.getSuperClasses();
     cls.accept(this);
   } catch (ClassNotFoundException cnfe) {
     bugReporter.reportMissingClass(cnfe);
   } finally {
     clsContext = null;
     superClasses = null;
   }
 }
  @Override
  public void visitClassContext(ClassContext classContext) {
    if (appletClass == null) {
      return;
    }

    JavaClass cls = classContext.getJavaClass();
    try {
      if (cls.instanceOf(appletClass)) {
        cls.accept(this);
      }
    } catch (ClassNotFoundException cnfe) {
      bugReporter.reportMissingClass(cnfe);
    }
  }
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();
    Method[] methodList = javaClass.getMethods();

    for (Method method : methodList) {
      if (method.getCode() == null) continue;

      try {
        analyzeMethod(classContext, method);
      } catch (MethodUnprofitableException e) {
        assert true; // move along; nothing to see
      } catch (CFGBuilderException e) {
        String msg =
            "Detector "
                + this.getClass().getName()
                + " caught exception while analyzing "
                + javaClass.getClassName()
                + "."
                + method.getName()
                + " : "
                + method.getSignature();
        bugReporter.logError(msg, e);
      } catch (DataflowAnalysisException e) {
        String msg =
            "Detector "
                + this.getClass().getName()
                + " caught exception while analyzing "
                + javaClass.getClassName()
                + "."
                + method.getName()
                + " : "
                + method.getSignature();
        bugReporter.logError(msg, e);
      }
    }
  }
  private void analyzeMethod(ClassContext classContext, Method method)
      throws CFGBuilderException, DataflowAnalysisException {
    if (BCELUtil.isSynthetic(method)
        || (method.getAccessFlags() & Const.ACC_BRIDGE) == Const.ACC_BRIDGE) {
      return;
    }
    CFG cfg = classContext.getCFG(method);

    ConstantPoolGen cpg = classContext.getConstantPoolGen();
    TypeDataflow typeDataflow = classContext.getTypeDataflow(method);

    for (Iterator<BasicBlock> i = cfg.blockIterator(); i.hasNext(); ) {
      BasicBlock basicBlock = i.next();

      // Check if it's a method invocation.
      if (!basicBlock.isExceptionThrower()) {
        continue;
      }
      InstructionHandle thrower = basicBlock.getExceptionThrower();
      Instruction ins = thrower.getInstruction();
      if (!(ins instanceof InvokeInstruction)) {
        continue;
      }

      InvokeInstruction inv = (InvokeInstruction) ins;
      boolean foundThrower = false;
      boolean foundNonThrower = false;

      if (inv instanceof INVOKEINTERFACE) {
        continue;
      }

      String className = inv.getClassName(cpg);

      Location loc = new Location(thrower, basicBlock);
      TypeFrame typeFrame = typeDataflow.getFactAtLocation(loc);
      XMethod primaryXMethod = XFactory.createXMethod(inv, cpg);
      // if (primaryXMethod.isAbstract()) continue;
      Set<XMethod> targetSet = null;
      try {

        if (className.startsWith("[")) {
          continue;
        }
        String methodSig = inv.getSignature(cpg);
        if (!methodSig.endsWith("V")) {
          continue;
        }

        targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, cpg);

        for (XMethod xMethod : targetSet) {
          if (DEBUG) {
            System.out.println("\tFound " + xMethod);
          }

          boolean isUnconditionalThrower =
              xMethod.isUnconditionalThrower()
                  && !xMethod.isUnsupported()
                  && !xMethod.isSynthetic();
          if (isUnconditionalThrower) {
            foundThrower = true;
            if (DEBUG) {
              System.out.println("Found thrower");
            }
          } else {
            foundNonThrower = true;
            if (DEBUG) {
              System.out.println("Found non thrower");
            }
          }
        }
      } catch (ClassNotFoundException e) {
        analysisContext.getLookupFailureCallback().reportMissingClass(e);
      }
      boolean newResult = foundThrower && !foundNonThrower;
      if (newResult) {
        bugReporter.reportBug(
            new BugInstance(this, "TESTING", Priorities.NORMAL_PRIORITY)
                .addClassAndMethod(classContext.getJavaClass(), method)
                .addString("Call to method that always throws Exception")
                .addMethod(primaryXMethod)
                .describe(MethodAnnotation.METHOD_CALLED)
                .addSourceLine(classContext, method, loc));
      }
    }
  }
Example #21
0
 private void logException(ClassContext classContext, Method method, Exception ex) {
   bugReporter.logError(
       "Exception while analyzing " + classContext.getFullyQualifiedMethodName(method), ex);
 }
Example #22
0
 private static TaintDataflow getTaintDataFlow(ClassContext classContext, Method method)
     throws CheckedAnalysisException {
   MethodDescriptor descriptor = BCELUtil.getMethodDescriptor(classContext.getJavaClass(), method);
   return Global.getAnalysisCache().getMethodAnalysis(TaintDataflow.class, descriptor);
 }
 public void visitClassContext(ClassContext classContext) {
   classContext.getJavaClass().accept(this);
 }
  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();
  }
 @Override
 public void visitClassContext(ClassContext classContext) {
   this.classContext = classContext;
   classContext.getJavaClass().accept(this);
 }
  public void meetInto(
      UnconditionalValueDerefSet fact,
      Edge edge,
      UnconditionalValueDerefSet result,
      boolean onlyEdge) {
    if (isExceptionEdge(edge) && !onlyEdge) {
      if (DEBUG) {
        System.out.println("Skipping exception edge");
      }
      return;
    }

    ValueNumber knownNonnullOnBranch = null;
    // Edge transfer function
    if (isFactValid(fact)) {
      fact = propagateDerefSetsToMergeInputValues(fact, edge);
      if (invDataflow != null) {
        knownNonnullOnBranch = findValueKnownNonnullOnBranch(fact, edge);
        if (knownNonnullOnBranch != null) {
          fact = duplicateFact(fact);
          fact.clearDerefSet(knownNonnullOnBranch);
        }
      }
    }
    boolean isBackEdge = edge.isBackwardInBytecode();
    Set<Integer> loopExitBranches = ClassContext.getLoopExitBranches(method, methodGen);
    assert loopExitBranches != null;
    boolean sourceIsTopOfLoop = edge.sourceIsTopOfLoop(loopExitBranches);
    if (sourceIsTopOfLoop && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE) {
      isBackEdge = true;
    }
    /*
    if (false && (edge.getType() == EdgeTypes.IFCMP_EDGE || sourceIsTopOfLoop)) {
        System.out.println("Meet into " + edge);
        System.out.println("  foo2: " + sourceIsTopOfLoop);
        System.out.println("  getType: " + edge.getType());
        System.out.println("  Backedge according to bytecode: " + isBackEdge);
        System.out.println("  Fact hashCode: " + System.identityHashCode(result));
        System.out.println("  Initial fact: " + result);
        System.out.println("  Edge fact: " + fact);
    }
     */
    if (result.isTop() || fact.isBottom()) {
      // Make result identical to other fact
      copy(fact, result);
      if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
        result.resultsFromBackEdge = true;
      }
    } else if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
      result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
      result.resultsFromBackEdge = true;
      if (DEBUG) {
        System.out.println(
            "\n Forcing union of " + System.identityHashCode(result) + " due to backedge info");
        System.out.println("  result: " + result);
      }

    } else if (result.isBottom() || fact.isTop()) {
      // No change in result fact
    } else {
      // Dataflow merge
      // (intersection of unconditional deref values)
      if (ASSUME_NONZERO_TRIP_LOOPS && result.resultsFromBackEdge) {
        result.backEdgeUpdateCount++;
        if (result.backEdgeUpdateCount < 10) {
          if (DEBUG) {
            System.out.println(
                "\n Union update of " + System.identityHashCode(result) + " due to backedge info");
          }
          result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
          return;
        }
      }
      result.mergeWith(fact, knownNonnullOnBranch, vnaDataflow.getAnalysis().getFactory());
      if (DEBUG) {
        System.out.println("  updated: " + System.identityHashCode(result));
        System.out.println("  result: " + result);
      }
    }
    if (DEBUG && isBackEdge && edge.getType() == EdgeTypes.IFCMP_EDGE) {
      System.out.println("  result: " + result);
    }
  }
 @Override
 public void visitClassContext(ClassContext classContext) {
   classContext.getJavaClass().accept(this);
   flush();
 }
  /**
   * overrides the visitor to report on classes without toStrings that have fields
   *
   * @param classContext the context object of the currently parsed class
   */
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass cls = classContext.getJavaClass();

    if (cls.getPackageName().isEmpty()) {
      bugReporter.reportBug(
          new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_PACKAGE.name(), LOW_PRIORITY)
              .addClass(cls));
    }

    if ((!cls.isAbstract())
        && (!cls.isEnum())
        && !cls.getClassName().contains("$")
        && !isTestClass(cls)) {

      try {
        boolean clsHasRuntimeAnnotation = classHasRuntimeVisibleAnnotation(cls);
        HEStatus heStatus = HEStatus.UNKNOWN;

        checkIDEGeneratedParmNames(cls);

        for (Field f : cls.getFields()) {
          if (!f.isStatic() && !f.isSynthetic()) {

            boolean fieldHasRuntimeAnnotation = fieldHasRuntimeVisibleAnnotation(f);
            if (!fieldHasRuntimeAnnotation) {
              /* only report one of these, so as not to flood the report */
              if (!hasMethodInHierarchy(cls, "toString", "()Ljava/lang/String;")) {
                bugReporter.reportBug(
                    new BugInstance(
                            this, BugType.IMC_IMMATURE_CLASS_NO_TOSTRING.name(), LOW_PRIORITY)
                        .addClass(cls));
                return;
              }
              if (heStatus != HEStatus.NOT_NEEDED) {
                String fieldSig = f.getSignature();
                if (fieldSig.startsWith("L")) {
                  if (!fieldSig.startsWith("Ljava")) {
                    JavaClass fieldClass =
                        Repository.lookupClass(fieldSig.substring(1, fieldSig.length() - 1));
                    if (!hasMethodInHierarchy(fieldClass, "equals", "(Ljava/lang/Object)Z")) {
                      heStatus = HEStatus.NOT_NEEDED;
                    }
                  } else if (!fieldSig.startsWith("Ljava/lang/")
                      && !fieldSig.startsWith("Ljava/util/")) {
                    heStatus = HEStatus.NOT_NEEDED;
                  }
                } else if (!fieldSig.startsWith("[")) {
                  heStatus = HEStatus.NEEDED;
                }
              }
            } else {
              heStatus = HEStatus.NOT_NEEDED;
            }
          }
        }

        if (!clsHasRuntimeAnnotation && (heStatus == HEStatus.NEEDED)) {
          if (!hasMethodInHierarchy(cls, "equals", "(Ljava/lang/Object;)Z")) {
            bugReporter.reportBug(
                new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_EQUALS.name(), LOW_PRIORITY)
                    .addClass(cls));
          } else if (!hasMethodInHierarchy(cls, "hashCode", "()I")) {
            bugReporter.reportBug(
                new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_HASHCODE.name(), LOW_PRIORITY)
                    .addClass(cls));
          }
        }

      } catch (ClassNotFoundException cnfe) {
        bugReporter.reportMissingClass(cnfe);
      }
    }
  }