001package daikon;
002
003import gnu.getopt.Getopt;
004import gnu.getopt.LongOpt;
005import java.io.File;
006import org.plumelib.util.FilesPlume;
007import org.plumelib.util.StringsPlume;
008
009/**
010 * UnionInvariants is a command-line tool that will read in one (or more) {@code .inv} files
011 * (possibly gzipped) and write their union into a new {@code .inv} file (possibly gzipped). Run
012 * with {@code -h} flag to view the command line syntax.
013 *
014 * <p>Currently, UnionInvariants works at program point granularity, so two inv files cannot have
015 * printable invariants at the same program point.
016 *
017 * <p>You can optionally use Simplify after combination in case you believe invariant context from
018 * other types will suppress some invariants. (This tool is also a nice way to run Simplify on a
019 * single inv file.)
020 */
021public final class UnionInvariants {
022  private UnionInvariants() {
023    throw new Error("do not instantiate");
024  }
025
026  /** The usage message for this program. */
027  // Non-empty program points in the input files must be distinct.
028  private static String usage =
029      StringsPlume.joinLines(
030          "Usage: java daikon.UnionInvariants [OPTION]... FILE.inv[.gz] [FILE.inv[.gz] ...]",
031          "  -h, --" + Daikon.help_SWITCH,
032          "      Display this usage message",
033          "  --" + Daikon.suppress_redundant_SWITCH,
034          "      Suppress display of logically redundant invariants.");
035
036  public static void main(final String[] args) throws Exception {
037    try {
038      mainHelper(args);
039    } catch (Daikon.DaikonTerminationException e) {
040      Daikon.handleDaikonTerminationException(e);
041    }
042  }
043
044  /**
045   * This does the work of {@link #main(String[])}, but it never calls System.exit, so it is
046   * appropriate to be called progrmmatically.
047   */
048  public static void mainHelper(String[] args) throws Exception {
049    File inv_file = null;
050
051    LongOpt[] longopts =
052        new LongOpt[] {
053          new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
054        };
055    Getopt g = new Getopt("daikon.UnionInvariants", args, "ho:", longopts);
056    int c;
057    while ((c = g.getopt()) != -1) {
058      switch (c) {
059        case 0:
060          // got a long option
061          String option_name = longopts[g.getLongind()].getName();
062          if (Daikon.help_SWITCH.equals(option_name)) {
063            System.out.println(usage);
064            throw new Daikon.NormalTermination();
065          } else if (Daikon.suppress_redundant_SWITCH.equals(option_name)) {
066            Daikon.suppress_redundant_invariants_with_simplify = true;
067          } else {
068            throw new Daikon.UserError("Unknown option received: " + option_name);
069          }
070          break;
071        case 'h':
072          System.out.println(usage);
073          throw new Daikon.NormalTermination();
074        case 'o':
075          String inv_filename = Daikon.getOptarg(g);
076
077          if (inv_file != null) {
078            throw new Daikon.UserError(
079                "multiple serialization output files supplied on command line: "
080                    + inv_file
081                    + " "
082                    + inv_filename);
083          }
084
085          System.out.println("Inv filename = " + inv_filename);
086          inv_file = new File(inv_filename);
087
088          if (!FilesPlume.canCreateAndWrite(inv_file)) {
089            throw new Daikon.UserError("Cannot write to serialization output file " + inv_file);
090          }
091          break;
092          //
093        case '?':
094          break; // getopt() already printed an error
095        default:
096          System.out.println("getopt() returned " + c);
097          break;
098      }
099    }
100
101    // The index of the first non-option argument
102    int fileIndex = g.getOptind();
103    if ((inv_file == null) || (args.length - fileIndex == 0)) {
104      System.out.println(usage);
105      throw new Daikon.UserError("Wrong number of args");
106    }
107
108    PptMap result = new PptMap();
109    for (int i = fileIndex; i < args.length; i++) {
110      String filename = args[i];
111      System.out.println("Reading " + filename + "...");
112      PptMap ppt_map =
113          FileIO.read_serialized_pptmap(
114              new File(filename), true // use saved config
115              );
116      union(result, ppt_map);
117    }
118
119    // TODO: We should check consistency things, such as entry_ppt not
120    // pointing outside of the PptMap.  (What else?)
121
122    // Mark redundant invariants (may have more given additional
123    // surrounding program points)
124
125    if (Daikon.suppress_redundant_invariants_with_simplify) {
126      System.out.print("Invoking Simplify to identify redundant invariants...");
127      System.out.flush();
128      long start = System.currentTimeMillis();
129      for (PptTopLevel ppt : result.pptIterable()) {
130        ppt.mark_implied_via_simplify(result);
131      }
132      long end = System.currentTimeMillis();
133      double elapsed = (end - start) / 1000.0;
134      System.out.println(new java.text.DecimalFormat("#.#").format(elapsed) + "s");
135    }
136
137    // Write serialized output
138    System.out.println("Writing " + inv_file + "...");
139    FileIO.write_serialized_pptmap(result, inv_file);
140
141    System.out.println("Exiting");
142  }
143
144  /**
145   * Union multiple PptMaps into one.
146   *
147   * @param collector is mutated
148   * @param source is unmodified (but is aliased into)
149   */
150  public static void union(PptMap collector, PptMap source) {
151    for (PptTopLevel ppt : source.pptIterable()) {
152
153      if ((ppt.numViews() == 0) && (ppt.joiner_view.invs.size() == 0)) {
154        continue;
155      }
156
157      if (collector.get(ppt.ppt_name) != null) {
158        throw new RuntimeException("Cannot merge two non-empty ppts named " + ppt.name());
159      }
160
161      System.out.println("Adding ppt " + ppt.name());
162      collector.add(ppt);
163    }
164  }
165}