001// "Ppt" stands for "Program point" (but is easier to type).
002
003package daikon;
004
005import daikon.inv.Invariant; // for emptyInvList
006import java.io.Serializable;
007import java.util.ArrayList;
008import java.util.Comparator;
009import java.util.List;
010import org.checkerframework.checker.initialization.qual.UnknownInitialization;
011import org.checkerframework.checker.interning.qual.UsesObjectEquals;
012import org.checkerframework.checker.lock.qual.GuardSatisfied;
013import org.checkerframework.checker.nullness.qual.Nullable;
014import org.checkerframework.dataflow.qual.Pure;
015import org.checkerframework.dataflow.qual.SideEffectFree;
016
017// Types of Ppt (program point) objects:
018//  Ppt:  abstract base class
019//  PptTopLevel:  pointed to by top-level PptMap object.  Contains all variables
020//    and all data for those variables.
021//  PptConditional:  contains only value tuples satisfying some condition.
022//    Probably doesn't make sense for parent to be a PptSlice.
023//  PptSlice:  contains a subset of variables.  Probably doesn't contain its
024//    own data structure with all the values, but depends on its parent
025//    (which may be any type of Ppt except a PptSlice, which wouldn't
026//    make good sense).
027// Originally, both PptConditional and PptSlice were called "Views"; but
028// presently (6/2002), only Slices are called Views.
029
030// Ppt is an abstract base class rather than an interface in part because
031// interfaces cannot declare member variables.  I suspect that using
032// members directly will be more efficient than calling accessor
033// functions such as num_vars() and var_info_iterator().
034
035// The common interface for all Ppt objects.
036@UsesObjectEquals
037public abstract class Ppt implements Serializable {
038  // We are Serializable, so we specify a version to allow changes to
039  // method signatures without breaking serialization.  If you add or
040  // remove fields, you should change this number to the current date.
041  static final long serialVersionUID = 20040914L;
042
043  // Not final:  modified by PptTopLevel.addVarInfos (which is called by
044  // Daikon.create_orig_vars and PptTopLevel.create_derived_variables)
045  // and also by PptSlice0.makeFakePrestate.
046  public VarInfo[] var_infos;
047
048  protected Ppt(VarInfo[] var_infos) {
049    this.var_infos = var_infos;
050  }
051
052  // The "name" and "ppt_name" fields were moved to PptTopLevel:  they take
053  // up too much space in PptSlice objects.
054  // This is safe if the receiver is @UnknownInitialization(PptTopLevel.class) OR
055  // @UnknownInitialization(PptSlice.class), but annotations cannot express that.
056  public abstract String name(@GuardSatisfied @UnknownInitialization(PptTopLevel.class) Ppt this);
057
058  /** Trim the collections used in this Ppt. */
059  public void trimToSize() {
060    for (VarInfo vi : var_infos) {
061      vi.trimToSize();
062    }
063  }
064
065  protected static final List<Invariant> emptyInvList = new ArrayList<>();
066
067  /** Returns a string rep of the specified variable names. */
068  @SuppressWarnings("all:purity") // Impure side effects do not escape (string creation)
069  @SideEffectFree
070  public static String varNames(VarInfo[] infos) {
071    StringBuilder sb = new StringBuilder();
072    sb.append("(");
073    if (infos.length == 0) {
074      sb.append("<implication slice>");
075    } else {
076      sb.append(infos[0].name());
077      for (int i = 1; i < infos.length; i++) {
078        sb.append(", ");
079        sb.append(infos[i].name());
080      }
081    }
082    sb.append(")");
083    return sb.toString();
084  }
085
086  /** Return a string representation of the variable names. */
087  @SideEffectFree
088  public String varNames(@GuardSatisfied @UnknownInitialization(Ppt.class) Ppt this) {
089    return varNames(var_infos);
090  }
091
092  /**
093   * Returns the varinfo_index of the variable whose name is varname. Returns -1 if there is no such
094   * variable.
095   */
096  @Pure
097  public int indexOf(@UnknownInitialization(Ppt.class) Ppt this, String varname) {
098    for (int i = 0; i < var_infos.length; i++) {
099      if (var_infos[i].name().equals(varname)) {
100        return i;
101      }
102    }
103    return -1;
104  }
105
106  /** Returns the VarInfo with the specified name. Null if the name is not found. */
107  @Pure
108  public @Nullable VarInfo find_var_by_name(
109      @UnknownInitialization(Ppt.class) Ppt this, String varname) {
110    // System.out.printf("Ppt.find_var_by_name(%s): %s%n", varname, this);
111    int i = indexOf(varname);
112    if (i == -1) {
113      if (varname.contains("[]")) {
114        return find_var_by_name(varname.replace("[]", "[..]"));
115      }
116      // System.out.printf("Ppt.find_var_by_name: Didn't find %s or %s in %s%n", varname,
117      // varname.replace ("[]", "[..]"), this);
118      return null;
119    } else {
120      return var_infos[i];
121    }
122  }
123
124  public boolean containsVar(VarInfo vi) {
125    // There's gotta be a faster way of doing this.  I don't want to
126    // use a HashSet for var_infos because various things clobber
127    // this.var_infos.
128    for (VarInfo elt : var_infos) {
129      if (elt == vi) {
130        return true;
131      }
132    }
133    return false;
134  }
135
136  // It might make more sense to put the sorting into
137  // PptMap.sortedIterator(), for example, but it's in here for now
138
139  // Check if o1 and o2 are both main exits (combined or only exits)
140  // If so, compare their name without the EXIT[line]
141  // If the name is the same, return 0, otherwise
142  // Orders ppts by the name, except . and : are swapped
143  //   so that Foo:::OBJECT and Foo:::CLASS are processed before Foo.method.
144  public static final class NameComparator implements Comparator<PptTopLevel> {
145    @Pure
146    @Override
147    public int compare(PptTopLevel p1, PptTopLevel p2) {
148      if (p1 == p2) {
149        return 0;
150      }
151
152      String name1 = p1.name();
153      String name2 = p2.name();
154
155      String swapped1 = swap(name1, '.', ':');
156      String swapped2 = swap(name2, '.', ':');
157
158      return swapped1.compareTo(swapped2);
159    }
160
161    static String swap(String s, char a, char b) {
162      final char magic = '\255';
163      return s.replace(a, magic).replace(b, a).replace(magic, b);
164    }
165  }
166}