/** merge multiple call chains from different contexts * */
 public void merge(SourceCallChainInfo other) {
   if (method != other.method)
     throw new RuntimeException(
         "methods don't match: "
             + method
             + ", "
             + other.method
             + " "
             + method.equals(other.method)
             + " "
             + method.hashCode()
             + " "
             + other.method.hashCode());
   if (contents.length == 0) {
     contents = other.contents;
     return;
   } else if (other.contents.length == 0) {
     return;
   }
   List<SourceCallChainInfo> ccis = new ArrayList<SourceCallChainInfo>(Arrays.asList(contents));
   Map<SootMethod, SourceCallChainInfo> minfo = new HashMap<SootMethod, SourceCallChainInfo>();
   for (SourceCallChainInfo cci : contents) minfo.put(cci.method, cci);
   for (SourceCallChainInfo other_cci : other.contents) {
     SourceCallChainInfo cci = minfo.get(other_cci.method);
     if (cci == null) ccis.add(other_cci);
     else cci.merge(other_cci);
   }
   if (contents.length == ccis.size())
     logger.info("merge: {} old/new size = {}", method, ccis.size());
   else logger.info("merge: {} orig {} elems, new {} elems", method, contents.length, ccis.size());
   contents = ccis.toArray(new SourceCallChainInfo[0]);
 }
 /** merge any duplicate method calls * */
 public void merge_contents() {
   if (contents.length == 0) return;
   Arrays.sort(contents);
   List<SourceCallChainInfo> unique_calls = new ArrayList<SourceCallChainInfo>();
   unique_calls.add(contents[0]);
   for (int ii = 1; ii < contents.length; ii++) {
     SourceCallChainInfo top = unique_calls.get(unique_calls.size() - 1);
     if (contents[ii].method == top.method) top.merge(contents[ii]);
     else unique_calls.add(contents[ii]);
   }
   logger.info(
       "merge_contents {}: old {} elems, new {} elems",
       method,
       contents.length,
       unique_calls.size());
   contents = unique_calls.toArray(new SourceCallChainInfo[0]);
 }
  /** Dumps out the call chain in json format * */
  public void dump_json(PrintStream fp, String indent) {
    fp.printf("%s{ %s,\n", indent, json_field("type", type));
    fp.printf("%s  %s,\n", indent, json_field("link", link));
    String sig = method.getSignature();
    fp.printf("%s  %s,\n", indent, json_field("signature", sig));
    if (stmt != null) {
      SootMethodRef invoke = stmt.getInvokeExpr().getMethodRef();
      String invokeSig;
      try {
        SootMethod concrete = SootUtils.resolve(stmt.getInvokeExpr().getMethodRef());
        invokeSig = concrete.getSignature();
      } catch (CannotFindMethodException e1) {
        logger.debug("Cannot find concrete method for {} in SourceCallChainInfo.dump_json()", stmt);
        invokeSig = invoke.getSignature();
      }
      if (!invokeSig.equals(sig)) {
        fp.printf("%s  %s,\n", indent, json_field("source-signature", invokeSig));
      }
    }
    SourceLocationTag slt =
        (stmt == null) ? SootUtils.getMethodLocation(method) : getSourceLocation(stmt);
    if (slt != null) {
      fp.printf("%s  %s", indent, json_field("src-loc"));
      fp.printf(
          "{ %s, %s},\n", json_field("class", slt.getClz()), json_field("line", slt.getLine()));
    }
    fp.printf("%s  %s,\n", indent, json_field("syscalls", syscalls));
    fp.printf("%s  %s,\n", indent, json_field("calls", calls));

    if ((contents != null) && (contents.length > 0)) {
      fp.printf("%s  %s,\n", indent, json_field("score", score));
      fp.printf("%s  %s [\n", indent, json_field("contents"));
      String delim = "";
      for (SourceCallChainInfo cci : contents) {
        fp.print(delim);
        delim = ",\n";
        cci.dump_json(fp, indent + "  ");
      }
      fp.printf("\n%s]}", indent);
    } else {
      fp.printf("%s  %s\n", indent, json_field("score", score));
      fp.printf("%s}", indent);
    }
  }
 /** Calculate the score for each entry in the call chain * */
 public void calculate_scores() {
   score = 0;
   if (contents.length == 0) {
     API api = API.v();
     Set<InfoKind> source = api.getSourceInfoKinds(method);
     Set<InfoKind> sink = api.getSinkInfoKinds(method);
     if (is_system(method)) {
       if (api.isSafeMethod(method)) score = 0;
       else if (api.isSpecMethod(method)) score = 5;
       else if (api.isBannedMethod(method)) score = 6;
       if (!source.isEmpty()) score += 1;
       else if (!sink.isEmpty()) score += 2;
     }
     return;
   }
   for (SourceCallChainInfo cci : contents) {
     cci.calculate_scores();
     calls += cci.calls;
     syscalls += cci.syscalls;
     if (cci.score > score) score = cci.score;
   }
 }