/**
  * 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);
 }
 /**
  * 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;
   }
 }
  /**
   * 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;
  }