/**
   * Check to see if the instruction has a null check associated with it, and if so, add a
   * dereference.
   *
   * @param location the Location of the instruction
   * @param vnaFrame ValueNumberFrame at the Location of the instruction
   * @param fact the dataflow value to modify
   * @throws DataflowAnalysisException
   */
  private void checkInstance(
      Location location, ValueNumberFrame vnaFrame, UnconditionalValueDerefSet fact)
      throws DataflowAnalysisException {
    // See if this instruction has a null check.
    // If it does, the fall through predecessor will be
    // identify itself as the null check.
    if (!location.isFirstInstructionInBasicBlock()) {
      return;
    }
    if (invDataflow == null) {
      return;
    }
    BasicBlock fallThroughPredecessor =
        cfg.getPredecessorWithEdgeType(location.getBasicBlock(), EdgeTypes.FALL_THROUGH_EDGE);
    if (fallThroughPredecessor == null || !fallThroughPredecessor.isNullCheck()) {
      return;
    }

    // Get the null-checked value
    ValueNumber vn =
        vnaFrame.getInstance(location.getHandle().getInstruction(), methodGen.getConstantPool());

    // Ignore dereferences of this
    if (!methodGen.isStatic()) {
      ValueNumber v = vnaFrame.getValue(0);
      if (v.equals(vn)) {
        return;
      }
    }
    if (vn.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
      return;
    }

    IsNullValueFrame startFact = null;

    startFact = invDataflow.getStartFact(fallThroughPredecessor);

    if (!startFact.isValid()) {
      return;
    }

    int slot =
        startFact.getInstanceSlot(
            location.getHandle().getInstruction(), methodGen.getConstantPool());
    if (!reportDereference(startFact, slot)) {
      return;
    }
    if (DEBUG) {
      System.out.println("FOUND GUARANTEED DEREFERENCE");
      System.out.println("Load: " + vnaFrame.getLoad(vn));
      System.out.println("Pred: " + fallThroughPredecessor);
      System.out.println("startFact: " + startFact);
      System.out.println("Location: " + location);
      System.out.println("Value number frame: " + vnaFrame);
      System.out.println("Dereferenced valueNumber: " + vn);
      System.out.println("invDataflow: " + startFact);
      System.out.println("IGNORE_DEREF_OF_NCP: " + IGNORE_DEREF_OF_NCP);
    }
    // Mark the value number as being dereferenced at this location
    fact.addDeref(vn, location);
  }
  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));
      }
    }
  }