Example #1
0
  /**
   * Output this DataFlowInput as a text file.
   *
   * @param outFile The file where output is to be written. If outFile ends in ".gz" it will be
   *     output as a compressed file.
   * @param outputCodeRep If true, also output the code representation of each sequence, as a
   *     comment following each record.
   */
  public void toParseableFile(String outFile, boolean outputCodeRep) {
    if (outFile == null || outFile.length() == 0)
      throw new IllegalArgumentException("Illegal output file name: " + outFile);

    try {
      BufferedWriter out = UtilMDE.bufferedFileWriter(outFile);
      for (Map.Entry<Branch, Set<Sequence>> e : frontierMap.entrySet()) {
        for (Sequence seq : e.getValue()) {
          // Print one record.
          out.append("START RECORD" + Globals.lineSep + Globals.lineSep);
          out.append("BRANCH" + Globals.lineSep);
          out.append(e.getKey().toString());
          out.append(Globals.lineSep + Globals.lineSep);
          out.append("SEQUENCE" + Globals.lineSep);
          out.append(seq.toParseableString());
          out.append(Globals.lineSep + Globals.lineSep);
          out.append("END RECORD" + Globals.lineSep + Globals.lineSep);

          if (outputCodeRep) {
            out.append("# Code representation:" + Globals.lineSep);
            for (String line : seq.toCodeString().split(Globals.lineSep)) {
              out.append("# " + line);
              out.append(Globals.lineSep);
            }
            out.append(Globals.lineSep + Globals.lineSep);
          }
        }
      }
      out.close();
    } catch (IOException e) {
      throw new Error(e);
    }
  }
Example #2
0
 public String toString() {
   return String.format("%s (%s)", name, UtilMDE.join(arg_types, ", "));
 }
Example #3
0
  /**
   * Transforms invoke instructions that match the specified list for this class to call the
   * specified static call instead.
   */
  private InstructionList xform_inst(MethodGen mg, Instruction inst) {

    switch (inst.getOpcode()) {
      case Constants.INVOKESTATIC:
        {
          InstructionList il = new InstructionList();
          INVOKESTATIC is = (INVOKESTATIC) inst;
          String cname = is.getClassName(pgen);
          String mname = is.getMethodName(pgen);
          Type[] args = is.getArgumentTypes(pgen);
          MethodDef orig = new MethodDef(cname + "." + mname, args);
          MethodInfo call = method_map.get(orig);
          if (call != null) {
            call.cnt++;
            String classname = call.method_class;
            String methodname = mname;
            debug_map.log(
                "%s.%s: Replacing method %s.%s (%s) with %s.%s%n",
                mg.getClassName(),
                mg.getName(),
                cname,
                mname,
                UtilMDE.join(args, ", "),
                classname,
                methodname);
            il.append(
                ifact.createInvoke(
                    classname, methodname, is.getReturnType(pgen), args, Constants.INVOKESTATIC));
          }
          return (il);
        }

      case Constants.INVOKEVIRTUAL:
        {
          InstructionList il = new InstructionList();
          INVOKEVIRTUAL iv = (INVOKEVIRTUAL) inst;
          String cname = iv.getClassName(pgen);
          String mname = iv.getMethodName(pgen);
          Type[] args = iv.getArgumentTypes(pgen);
          Type instance_type = iv.getReferenceType(pgen);
          Type[] new_args = BCELUtil.insert_type(instance_type, args);
          MethodDef orig = new MethodDef(cname + "." + mname, args);
          if (debug_class) System.out.printf("looking for %s in map %s%n", orig, method_map);
          MethodInfo call = method_map.get(orig);
          if (call != null) {
            call.cnt++;
            String classname = call.method_class;
            String methodname = mname;
            debug_map.log(
                "Replacing method %s.%s (%s) with %s.%s%n",
                cname, mname, ArraysMDE.toString(args), classname, methodname);
            il.append(
                ifact.createInvoke(
                    classname,
                    methodname,
                    iv.getReturnType(pgen),
                    new_args,
                    Constants.INVOKESTATIC));
          }
          return (il);
        }

      default:
        return (null);
    }
  }
/**
 * Extract the consequents of all Implication invariants that are predicated by membership in a
 * cluster, from a .inv file. An example of such an implication would be "(cluster == <em>NUM</em>)
 * ==&gt; consequent". The consequent is only true in certain clusters, but is not generally true
 * for all executions of the program point to which the Implication belongs. These resulting
 * implications are written to standard output in the format of a splitter info file.
 */
public class ExtractConsequent {

  public static final Logger debug = Logger.getLogger("daikon.ExtractConsequent");
  private static final String lineSep = Global.lineSep;

  private static class HashedConsequent {
    Invariant inv;

    // We prefer "x < y", "x > y", and "x == y" to the conditions
    // "x >= y", "x <= y", and "x != y" that (respectively) give the
    // same split.  When we see a dispreferred form, we index it by
    // the preferred form, and if there's already an entry (from the
    // real preferred one) we throw the new one out. Otherwise, we
    // insert both the dispreferred form and an entry for the
    // preferred form, with a pointer pack to the dispreferred
    // form. If we later see the preferred form, we replace the
    // placeholder and remove the dispreferred form.
    /*@Nullable*/ String fakeFor;

    HashedConsequent(Invariant inv, /*@Nullable*/ String fakeFor) {
      this.inv = inv;
      this.fakeFor = fakeFor;
    }
  }

  /* A HashMap whose keys are PPT names (Strings) and whose values are
  HashMaps whose keys are predicate names (Strings) and whose values are
   HashMaps whose keys are Strings (normalized java-format invariants)
     and whose values are HashedConsequent objects. */
  private static Map<String, Map<String, Map<String, HashedConsequent>>> pptname_to_conditions =
      new HashMap<String, Map<String, Map<String, HashedConsequent>>>();

  private static String usage =
      UtilMDE.joinLines(
          "Usage: java daikon.ExtractConsequent [OPTION]... FILE",
          "  -h, --" + Daikon.help_SWITCH,
          "      Display this usage message",
          "  --" + Daikon.suppress_redundant_SWITCH,
          "      Suppress display of logically redundant invariants.",
          "  --" + Daikon.debugAll_SWITCH,
          "      Turn on all debug switches",
          "  --" + Daikon.debug_SWITCH + " <logger>",
          "      Turn on the specified debug logger");

  public static void main(String[] args)
      throws FileNotFoundException, IOException, ClassNotFoundException {
    try {
      mainHelper(args);
    } catch (Daikon.TerminationMessage e) {
      System.err.println(e.getMessage());
      System.exit(1);
    }
    // Any exception other than Daikon.TerminationMessage gets propagated.
    // This simplifies debugging by showing the stack trace.
  }

  /**
   * This does the work of main, but it never calls System.exit, so it is appropriate to be called
   * progrmmatically. Termination of the program with a message to the user is indicated by throwing
   * Daikon.TerminationMessage.
   *
   * @see #main(String[])
   * @see daikon.Daikon.TerminationMessage
   */
  public static void mainHelper(final String[] args)
      throws FileNotFoundException, IOException, ClassNotFoundException {
    daikon.LogHelper.setupLogs(daikon.LogHelper.INFO);
    LongOpt[] longopts =
        new LongOpt[] {
          new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
          new LongOpt(Daikon.config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
          new LongOpt(Daikon.debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
          new LongOpt(Daikon.debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
        };
    Getopt g = new Getopt("daikon.ExtractConsequent", args, "h", longopts);
    int c;
    while ((c = g.getopt()) != -1) {
      switch (c) {
        case 0:
          // got a long option
          String option_name = longopts[g.getLongind()].getName();
          if (Daikon.help_SWITCH.equals(option_name)) {
            System.out.println(usage);
            throw new Daikon.TerminationMessage();
          } else if (Daikon.suppress_redundant_SWITCH.equals(option_name)) {
            Daikon.suppress_redundant_invariants_with_simplify = true;
          } else if (Daikon.config_option_SWITCH.equals(option_name)) {
            String item = Daikon.getOptarg(g);
            daikon.config.Configuration.getInstance().apply(item);
            break;
          } else if (Daikon.debugAll_SWITCH.equals(option_name)) {
            Global.debugAll = true;
          } else if (Daikon.debug_SWITCH.equals(option_name)) {
            LogHelper.setLevel(Daikon.getOptarg(g), LogHelper.FINE);
          } else {
            throw new RuntimeException("Unknown long option received: " + option_name);
          }
          break;
        case 'h':
          System.out.println(usage);
          throw new Daikon.TerminationMessage();
        case '?':
          break; // getopt() already printed an error
        default:
          System.out.println("getopt() returned " + c);
          break;
      }
    }
    // The index of the first non-option argument -- the name of the file
    int fileIndex = g.getOptind();
    if (args.length - fileIndex != 1) {
      throw new Daikon.TerminationMessage("Wrong number of arguments." + Daikon.lineSep + usage);
    }
    String filename = args[fileIndex];
    PptMap ppts =
        FileIO.read_serialized_pptmap(
            new File(filename), true // use saved config
            );
    extract_consequent(ppts);
  }

  public static void extract_consequent(PptMap ppts) {
    // Retrieve Ppt objects in sorted order.
    // Use a custom comparator for a specific ordering
    Comparator<PptTopLevel> comparator = new Ppt.NameComparator();
    TreeSet<PptTopLevel> ppts_sorted = new TreeSet<PptTopLevel>(comparator);
    ppts_sorted.addAll(ppts.asCollection());

    for (PptTopLevel ppt : ppts_sorted) {
      extract_consequent_maybe(ppt, ppts);
    }

    PrintWriter pw = new PrintWriter(System.out, true);

    // All conditions at a program point.  A TreeSet to enable
    // deterministic output.
    TreeSet<String> allConds = new TreeSet<String>();
    for (String pptname : pptname_to_conditions.keySet()) {
      Map<String, Map<String, HashedConsequent>> cluster_to_conditions =
          pptname_to_conditions.get(pptname);
      for (Map.Entry</*@KeyFor("cluster_to_conditions")*/ String, Map<String, HashedConsequent>>
          entry : cluster_to_conditions.entrySet()) {
        String predicate = entry.getKey();
        Map<String, HashedConsequent> conditions = entry.getValue();
        StringBuffer conjunctionJava = new StringBuffer();
        StringBuffer conjunctionDaikon = new StringBuffer();
        StringBuffer conjunctionESC = new StringBuffer();
        StringBuffer conjunctionSimplify = new StringBuffer("(AND ");
        int count = 0;
        for (Map.Entry</*@KeyFor("conditions")*/ String, HashedConsequent> entry2 :
            conditions.entrySet()) {
          count++;
          String condIndex = entry2.getKey();
          HashedConsequent cond = entry2.getValue();
          if (cond.fakeFor != null) {
            count--;
            continue;
          }
          String javaStr = cond.inv.format_using(OutputFormat.JAVA);
          String daikonStr = cond.inv.format_using(OutputFormat.DAIKON);
          String escStr = cond.inv.format_using(OutputFormat.ESCJAVA);
          String simplifyStr = cond.inv.format_using(OutputFormat.SIMPLIFY);
          allConds.add(combineDummy(condIndex, "<dummy> " + daikonStr, escStr, simplifyStr));
          //           allConds.add(condIndex);
          if (count > 0) {
            conjunctionJava.append(" && ");
            conjunctionDaikon.append(" and ");
            conjunctionESC.append(" && ");
            conjunctionSimplify.append(" ");
          }
          conjunctionJava.append(javaStr);
          conjunctionDaikon.append(daikonStr);
          conjunctionESC.append(escStr);
          conjunctionSimplify.append(simplifyStr);
        }
        conjunctionSimplify.append(")");
        String conj = conjunctionJava.toString();
        // Avoid inserting self-contradictory conditions such as "x == 1 &&
        // x == 2", or conjunctions of only a single condition.
        if (count < 2
            || contradict_inv_pattern.matcher(conj).find()
            || useless_inv_pattern_1.matcher(conj).find()
            || useless_inv_pattern_2.matcher(conj).find()) {
          // System.out.println("Suppressing: " + conj);
        } else {
          allConds.add(
              combineDummy(
                  conjunctionJava.toString(),
                  conjunctionDaikon.toString(),
                  conjunctionESC.toString(),
                  conjunctionSimplify.toString()));
        }
      }

      if (allConds.size() > 0) {
        pw.println();
        pw.println("PPT_NAME " + pptname);
        for (String s : allConds) {
          pw.println(s);
        }
      }
      allConds.clear();
    }

    pw.flush();
  }

  static String combineDummy(String inv, String daikonStr, String esc, String simplify) {
    StringBuffer combined = new StringBuffer(inv);
    combined.append(lineSep + "\tDAIKON_FORMAT ");
    combined.append(daikonStr);
    combined.append(lineSep + "\tESC_FORMAT ");
    combined.append(esc);
    combined.append(lineSep + "\tSIMPLIFY_FORMAT ");
    combined.append(simplify);
    return combined.toString();
  }

  /**
   * Extract consequents from a implications at a single program point. It only searches for top
   * level Program points because Implications are produced only at those points.
   */
  public static void extract_consequent_maybe(PptTopLevel ppt, PptMap all_ppts) {
    ppt.simplify_variable_names();

    List<Invariant> invs = new ArrayList<Invariant>();
    if (invs.size() > 0) {
      String pptname = cleanup_pptname(ppt.name());
      for (Invariant maybe_as_inv : invs) {
        Implication maybe = (Implication) maybe_as_inv;

        // don't print redundant invariants.
        if (Daikon.suppress_redundant_invariants_with_simplify
            && maybe.ppt.parent.redundant_invs.contains(maybe)) {
          continue;
        }

        // don't print out invariants with min(), max(), or sum() variables
        boolean mms = false;
        VarInfo[] varbls = maybe.ppt.var_infos;
        for (int v = 0; !mms && v < varbls.length; v++) {
          mms |= varbls[v].isDerivedSequenceMinMaxSum();
        }
        if (mms) {
          continue;
        }

        if (maybe.ppt.parent.ppt_name.isExitPoint()) {
          for (int i = 0; i < maybe.ppt.var_infos.length; i++) {
            VarInfo vi = maybe.ppt.var_infos[i];
            if (vi.isDerivedParam()) {
              continue;
            }
          }
        }

        Invariant consequent = maybe.consequent();
        Invariant predicate = maybe.predicate();
        Invariant inv, cluster_inv;
        boolean cons_uses_cluster = false, pred_uses_cluster = false;
        // extract the consequent (predicate) if the predicate
        // (consequent) uses the variable "cluster".  Ignore if they
        // both depend on "cluster"
        if (consequent.usesVarDerived("cluster")) cons_uses_cluster = true;
        if (predicate.usesVarDerived("cluster")) pred_uses_cluster = true;

        if (!(pred_uses_cluster ^ cons_uses_cluster)) {
          continue;
        } else if (pred_uses_cluster) {
          inv = consequent;
          cluster_inv = predicate;
        } else {
          inv = predicate;
          cluster_inv = consequent;
        }

        if (!inv.isInteresting()) {
          continue;
        }

        if (!inv.isWorthPrinting()) {
          continue;
        }

        if (contains_constant_non_012(inv)) {
          continue;
        }

        // filter out unwanted invariants

        // 1) Invariants involving sequences
        if (inv instanceof daikon.inv.binary.twoSequence.TwoSequence
            || inv instanceof daikon.inv.binary.sequenceScalar.SequenceScalar
            || inv instanceof daikon.inv.binary.sequenceString.SequenceString
            || inv instanceof daikon.inv.unary.sequence.SingleSequence
            || inv instanceof daikon.inv.unary.stringsequence.SingleStringSequence) {
          continue;
        }

        if (inv instanceof daikon.inv.ternary.threeScalar.LinearTernary
            || inv instanceof daikon.inv.binary.twoScalar.LinearBinary) {
          continue;
        }

        String inv_string = inv.format_using(OutputFormat.JAVA);
        if (orig_pattern.matcher(inv_string).find()
            || dot_class_pattern.matcher(inv_string).find()) {
          continue;
        }
        String fake_inv_string = simplify_inequalities(inv_string);
        HashedConsequent real = new HashedConsequent(inv, null);
        if (!fake_inv_string.equals(inv_string)) {
          // For instance, inv_string is "x != y", fake_inv_string is "x == y"
          HashedConsequent fake = new HashedConsequent(inv, inv_string);
          boolean added =
              store_invariant(
                  cluster_inv.format_using(OutputFormat.JAVA), fake_inv_string, fake, pptname);
          if (!added) {
            // We couldn't add "x == y", (when we're "x != y") because
            // it already exists; so don't add "x == y" either.
            continue;
          }
        }
        store_invariant(cluster_inv.format_using(OutputFormat.JAVA), inv_string, real, pptname);
      }
    }
  }

  // Store the invariant for later printing. Ignore duplicate
  // invariants at the same program point.
  private static boolean store_invariant(
      String predicate, String index, HashedConsequent consequent, String pptname) {
    if (!pptname_to_conditions.containsKey(pptname)) {
      pptname_to_conditions.put(pptname, new HashMap<String, Map<String, HashedConsequent>>());
    }

    Map<String, Map<String, HashedConsequent>> cluster_to_conditions =
        pptname_to_conditions.get(pptname);
    if (!cluster_to_conditions.containsKey(predicate)) {
      cluster_to_conditions.put(predicate, new HashMap<String, HashedConsequent>());
    }

    Map<String, HashedConsequent> conditions = cluster_to_conditions.get(predicate);
    if (conditions.containsKey(index)) {
      HashedConsequent old = conditions.get(index);
      if (old.fakeFor != null && consequent.fakeFor == null) {
        // We already saw (say) "x != y", but we're "x == y", so replace it.
        conditions.remove(index);
        conditions.remove(old.fakeFor);
        conditions.put(index, consequent);
        return true;
      }
      return false;
    } else {
      conditions.put(index, consequent);
      return true;
    }
  }

  private static boolean contains_constant_non_012(Invariant inv) {
    if (inv instanceof daikon.inv.unary.scalar.OneOfScalar) {
      daikon.inv.unary.scalar.OneOfScalar oneof = (daikon.inv.unary.scalar.OneOfScalar) inv;
      // OneOf invariants that indicate a small set ( > 1 element) of
      // possible values are not interesting, and have already been
      // eliminated by the isInteresting check
      long num = ((Long) oneof.elt()).longValue();
      if (num > 2 || num < -1) return true;
    }

    return false;
  }

  // remove non-word characters and everything after ":::" from the
  // program point name, leaving PackageName.ClassName.MethodName
  private static String cleanup_pptname(String pptname) {
    int index;
    if ((index = pptname.indexOf("(")) > 0) {
      pptname = pptname.substring(0, index);
    }

    if (pptname.endsWith(".")) pptname = pptname.substring(0, pptname.length() - 2);

    Matcher m = non_word_pattern.matcher(pptname);
    return m.replaceAll(".");
  }

  /**
   * Prevents the occurence of "equivalent" inequalities, or inequalities which produce the same
   * pair of splits at a program point, for example "x <= y" and "x &gt; y". Replaces "&ge;" with
   * "<", "&le;" with ">", and "!=" with "==" so that the occurence of equivalent inequalities can
   * be detected. However it tries not to be smart ... If there is more than one inequality in the
   * expression, it doesn't perform a substitution.
   */
  private static String simplify_inequalities(String condition) {
    if (contains_exactly_one(condition, inequality_pattern)) {
      if (gteq_pattern.matcher(condition).find()) {
        condition = gteq_pattern.matcher(condition).replaceFirst("<");
      } else if (lteq_pattern.matcher(condition).find()) {
        condition = lteq_pattern.matcher(condition).replaceFirst(">");
      } else if (neq_pattern.matcher(condition).find()) {
        condition = neq_pattern.matcher(condition).replaceFirst("==");
      } else throw new Error("this can't happen");
    }
    return condition;
  }

  private static boolean contains_exactly_one(String string, Pattern pattern) {
    Matcher m = pattern.matcher(string);
    // return true if first call returns true and second returns false
    return (m.find() && !m.find());
  }

  static Pattern orig_pattern, dot_class_pattern, non_word_pattern;
  static Pattern gteq_pattern, lteq_pattern, neq_pattern, inequality_pattern;
  static Pattern contradict_inv_pattern, useless_inv_pattern_1, useless_inv_pattern_2;

  static {
    try {
      non_word_pattern = Pattern.compile("\\W+");
      orig_pattern = Pattern.compile("orig\\s*\\(");
      dot_class_pattern = Pattern.compile("\\.class");
      inequality_pattern = Pattern.compile("[\\!<>]=");
      gteq_pattern = Pattern.compile(">=");
      lteq_pattern = Pattern.compile("<=");
      neq_pattern = Pattern.compile("\\!=");
      contradict_inv_pattern =
          Pattern.compile("(^| && )(.*) == -?[0-9]+ &.*& \\2 == -?[0-9]+($| && )");
      useless_inv_pattern_1 =
          Pattern.compile("(^| && )(.*) > -?[0-9]+ &.*& \\2 > -?[0-9]+($| && )");
      useless_inv_pattern_2 =
          Pattern.compile("(^| && )(.*) < -?[0-9]+ &.*& \\2 < -?[0-9]+($| && )");
    } catch (PatternSyntaxException me) {
      throw new Error("ExtractConsequent: Error while compiling pattern " + me.getMessage());
    }
  }
}