/**
   * Resolve the concrete target of a special invoke using our modified semantics for special invoke
   * expression.
   */
  private SootMethod resolveSpecialInvokeTarget(SpecialInvokeExpr si) {
    SootMethod target = null;

    try {
      target = SootUtils.resolve(si.getMethodRef());
    } catch (CannotFindMethodException e) {
      logger.error("Cannot find concrete method target for special invoke: {}", si);
      return null;
    }

    String targetSubSig = target.getSubSignature();

    SootClass current = target.getDeclaringClass();

    while (true) {
      if (current.declaresMethod(targetSubSig)) {
        return current.getMethod(targetSubSig);
      }

      // not a match in current, try superclass on next loop
      if (current.hasSuperclass()) current = current.getSuperclass();
      else {
        logger.error("Cannot find concrete method target for special invoke: {}", si);
        droidsafe.main.Main.exit(1);
        return null;
      }
    }
  }
  /** 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);
    }
  }