001package daikon.inv;
002
003import daikon.Debug;
004import daikon.Global;
005import daikon.PptSlice;
006import daikon.PptTopLevel;
007import daikon.Quantify.QuantFlags;
008import daikon.ValueTuple;
009import daikon.VarComparability;
010import daikon.VarInfo;
011import java.util.ArrayList;
012import java.util.Collection;
013import java.util.Collections;
014import java.util.Iterator;
015import java.util.List;
016import java.util.Set;
017import java.util.TreeSet;
018import java.util.logging.Level;
019import java.util.logging.Logger;
020import org.checkerframework.checker.initialization.qual.UnknownInitialization;
021import org.checkerframework.checker.lock.qual.GuardSatisfied;
022import org.checkerframework.checker.nullness.qual.Nullable;
023import org.checkerframework.dataflow.qual.Pure;
024import org.checkerframework.dataflow.qual.SideEffectFree;
025import typequals.prototype.qual.NonPrototype;
026import typequals.prototype.qual.Prototype;
027
028// Note that this Invariant is used in a *very* different way from
029// the same-named one in V2.  In V2, this is just for printing.  In V3,
030// this does all the canonicalizing, etc.
031
032// This is a Java (not Javadoc) comment because the Daikon user manual
033// reproduces the Javadoc but doesn't need all these implementation
034// details.
035//
036// During checking, Equality keeps track of variables that are
037// comparable and equal, so we only need to instantiate (other)
038// invariants for one member of each Equal set, the leader.
039//
040// During postProcessing, each instance of Equality instantiates into
041// displaying several EqualityComparison invariants ("x == y", "x ==
042// z").  Equality invariants have leaders, which are the canonical
043// forms of their variables.  In the previous example, x is the
044// leader.  Equality invariants sort their variables by index ordering
045// during checking.  During printing, however, equality invariants may
046// "pivot" -- that is, switch leaders if the current leader wouldn't
047// be printed because it was not an interesting variable.  Notice that
048// when pivoting, all the other invariants based on this.leader also
049// need to be pivoted.
050
051/**
052 * Keeps track of sets of variables that are equal. Other invariants are instantiated for only one
053 * member of the Equality set, the leader. If variables {@code x}, {@code y}, and {@code z} are
054 * members of the Equality set and {@code x} is chosen as the leader, then the Equality will
055 * internally convert into binary comparison invariants that print as {@code x == y} and {@code x ==
056 * z}.
057 */
058public final /*(at)Interned*/ class Equality extends Invariant {
059  static final long serialVersionUID = 20021231L;
060
061  public static final Logger debug = Logger.getLogger("daikon.inv.Equality");
062
063  public static final Logger debugPostProcess = Logger.getLogger("daikon.inv.Equality.postProcess");
064
065  /** How many samples this has seen. */
066  private int numSamples;
067
068  public void setSamples(int sample_cnt) {
069    numSamples = sample_cnt;
070  }
071
072  public int numSamples(@GuardSatisfied Equality this) {
073    return numSamples;
074  }
075
076  /**
077   * The Set of VarInfos that this represents equality for. Can change over time as this invariant
078   * weakens. Sorted by index until pivoting.
079   */
080  private TreeSet<VarInfo> vars;
081
082  /** Returns the number of variables in the set. */
083  @Pure
084  public int size(@GuardSatisfied Equality this) {
085    return vars.size();
086  }
087
088  /** Returns the variables in their index order. Unmodifiable. */
089  public Set<VarInfo> getVars() {
090    return Collections.unmodifiableSet(vars);
091  }
092
093  /**
094   * Creates a new Equality invariant.
095   *
096   * @param variables variables that are equivalent, with the canonical one first
097   * @param ppt the program point
098   */
099  @SuppressWarnings("initialization.field.write.initialized") // weakness of FBC type system
100  public Equality(Collection<VarInfo> variables, PptSlice ppt) {
101    super(ppt);
102    if (debug.isLoggable(Level.FINE)) {
103      debug.fine("Creating at " + ppt.parent.name() + " vars: ");
104    }
105
106    numSamples = 0;
107    vars = new TreeSet<VarInfo>(VarInfo.IndexComparator.theInstance);
108    vars.addAll(variables);
109    VarInfo leader = leader();
110
111    // ensure well-formedness and set equality slots
112    assert variables.size() > 0;
113    assert vars.size() == variables.size();
114
115    for (VarInfo vi : variables) {
116      if (debug.isLoggable(Level.FINE)) {
117        debug.fine("  " + vi.name() + " [" + vi.comparability + "]");
118      }
119      assert vi.ppt == leader.ppt;
120      assert vi.comparableNWay(leader);
121      assert VarComparability.comparable(leader, vi)
122          : "not comparable " + leader.name() + " " + vi.name() + " at ppt " + ppt.parent.name();
123      assert vi.rep_type.isArray() == leader.rep_type.isArray();
124      vi.equalitySet = this;
125    }
126  }
127
128  // ////////////////////////
129  // Accessors
130
131  private @Nullable VarInfo leaderCache = null;
132
133  /**
134   * Return the canonical VarInfo of this. Note that the leader never changes.
135   *
136   * @return the canonical VarInfo of this
137   */
138  @SuppressWarnings("all:purity") // set cache field
139  @Pure
140  public VarInfo leader(@GuardSatisfied @UnknownInitialization(Equality.class) Equality this) {
141    if (leaderCache == null) {
142      leaderCache = vars.iterator().next();
143    }
144    return leaderCache;
145  }
146
147  public boolean hasNonCanonicalVariable() {
148    throw new Error("Illegal operation on Equality invariant");
149  }
150
151  /**
152   * Always return JUSTIFIED because we aggregate EqualityComparison invariants that are all
153   * justified to the confidence_limit threshold.
154   */
155  @Override
156  public double computeConfidence() {
157    return Invariant.CONFIDENCE_JUSTIFIED;
158  }
159
160  // ////////////////////////
161  // Printing
162
163  // The format methods aren't called, because for output, we
164  // convert to normal two-way IntEqual type invariants.  However,
165  // they can be called if desired.
166
167  @Override
168  public String repr(@GuardSatisfied Equality this) {
169    return "Equality: size="
170        + size()
171        + " leader: "
172        + leader().name()
173        + " with "
174        + format_daikon()
175        + " samples: "
176        + numSamples();
177  }
178
179  @SideEffectFree
180  @Override
181  public String format_using(@GuardSatisfied Equality this, OutputFormat format) {
182
183    if (format.isJavaFamily()) {
184      return format_java_family(format);
185    }
186
187    if (format == OutputFormat.DAIKON) {
188      return format_daikon();
189    }
190    if (format == OutputFormat.ESCJAVA) {
191      return format_esc();
192    }
193    // Commented out by MDE 7/27/2003.  I can't figure out whether
194    // to just change JAVA_IDENTIFIER to IDENTIFIER, or whether other
195    // changes are also necessary.
196    // if (format == OutputFormat.JAVA_IDENTIFIER) {
197    //   return format_java();
198    // }
199    if (format == OutputFormat.SIMPLIFY) {
200      return format_simplify();
201    }
202    return format_unimplemented(format);
203  }
204
205  public String format_daikon(@GuardSatisfied Equality this) {
206    StringBuilder result = new StringBuilder();
207    boolean start = true;
208    for (VarInfo var : vars) {
209      if (!start) {
210        result.append(" == ");
211      } else {
212        start = false;
213      }
214      result.append(var.name());
215      result.append("[" + var.varinfo_index + "]");
216      // result.append("[" + var.comparability + "]");
217      if (var == leader()) {
218        result.append("L");
219      }
220    }
221    return result.toString();
222  }
223
224  public String format_java() {
225    VarInfo leader = leader();
226    String leaderName = leader.name();
227    List<String> clauses = new ArrayList<>();
228    for (VarInfo var : vars) {
229      if (leader == var) {
230        continue;
231      }
232      clauses.add(String.format("(%s == %s)", leaderName, var.name()));
233    }
234    return String.join(" && ", clauses);
235  }
236
237  public String format_esc(@GuardSatisfied Equality this) {
238    String result = "";
239
240    List<VarInfo> valid_equiv = new ArrayList<>();
241    List<VarInfo> invalid_equiv = new ArrayList<>();
242
243    List<VarInfo> equal_vars = new ArrayList<>();
244
245    for (VarInfo other : vars) {
246      if (other.isDerivedSequenceMinMaxSum()) {
247        break;
248      }
249      if (other.isValidEscExpression()) {
250        valid_equiv.add(other);
251      } else {
252        invalid_equiv.add(other);
253      }
254    }
255    // Choose a leader, preferring the valid variables.
256    VarInfo leader;
257    if (valid_equiv.size() > 0) {
258      leader = valid_equiv.get(0);
259    } else {
260      assert invalid_equiv.size() > 0;
261      leader = invalid_equiv.get(0);
262    }
263    // Print the equality statements, stating expressible ones first.
264    equal_vars.clear();
265    equal_vars.addAll(valid_equiv);
266    equal_vars.addAll(invalid_equiv);
267    int numprinted = 0;
268    for (int j = 0; j < equal_vars.size(); j++) {
269      VarInfo other = equal_vars.get(j);
270      if (other == leader) {
271        continue;
272      }
273      if (leader.prestate_name().equals(other.name())) {
274        continue;
275      }
276      if (other.prestate_name().equals(leader.name())) {
277        continue;
278      }
279      if (numprinted > 0) {
280        result += Global.lineSep;
281      }
282      numprinted++;
283      if (j >= valid_equiv.size()) {
284        result =
285            result + "warning: method Equality.format_esc() needs to be implemented: " + format();
286      }
287      if (leader.rep_type.isArray()) {
288        String[] form = VarInfo.esc_quantify(leader, other);
289        result = result + form[0] + "( " + form[1] + " == " + form[2] + " )" + form[3];
290      } else {
291        result = result + leader.esc_name() + " == " + other.esc_name();
292      }
293    }
294    return result;
295  }
296
297  // When A and B are pointers, don't say (EQ A B); instead say (EQ
298  // (hash A) (hash B)).  If we said the former, Simplify would
299  // presume that A and B were always interchangeable, which is not
300  // the case when your programming language involves mutation.
301  private String format_elt(@GuardSatisfied Equality this, String simname) {
302    String result = simname;
303    if (leader().is_reference()) {
304      result = "(hash " + result + ")";
305    }
306    return result;
307  }
308
309  public String format_simplify(@GuardSatisfied Equality this) {
310    StringBuilder result = new StringBuilder("(AND");
311    VarInfo leader = leader();
312    String leaderName = leader.simplify_name();
313    if (leader.rep_type.isArray()) {
314      for (VarInfo var : vars) {
315        if (var == leader) {
316          continue;
317        }
318        String[] form = VarInfo.simplify_quantify(QuantFlags.element_wise(), leader, var);
319        String a = format_elt(form[1]);
320        String b = format_elt(form[2]);
321        result.append(" " + form[0] + "(EQ " + a + " " + b + ")" + form[3]);
322      }
323    } else {
324      for (VarInfo var : vars) {
325        if (var == leader) {
326          continue;
327        }
328        String a = format_elt(leaderName);
329        String b = format_elt(var.simplify_name());
330        result.append(" (EQ ");
331        result.append(a);
332        result.append(" ");
333        result.append(b);
334        result.append(")");
335      }
336    }
337    result.append(")");
338    return result.toString();
339  }
340
341  public String shortString() {
342    return format_daikon();
343  }
344
345  public String format_java_family(@GuardSatisfied Equality this, OutputFormat format) {
346    VarInfo leader = leader();
347    String leaderName = leader.name_using(format);
348    List<String> clauses = new ArrayList<>();
349    for (VarInfo var : vars) {
350      if (leader == var) {
351        continue;
352      }
353      if (leader.rep_type.isArray()) {
354        clauses.add(
355            String.format(
356                "(daikon.Quant.pairwiseEqual(%s, %s)", leaderName, var.name_using(format)));
357      } else {
358        if (leader.type.isFloat()) {
359          clauses.add("(" + Invariant.formatFuzzy("eq", leader, var, format) + ")");
360        } else {
361          if ((leaderName.indexOf("daikon.Quant.collectObject") != -1)
362              || (var.name_using(format).indexOf("daikon.Quant.collectObject") != -1)) {
363            clauses.add(
364                "(warning: it is meaningless to compare hashcodes for values "
365                    + "obtained through daikon.Quant.collect... methods.");
366          } else {
367            clauses.add(String.format("(%s == %s)", leaderName, var.name_using(format)));
368          }
369        }
370      }
371    }
372    return String.join(" && ", clauses);
373  }
374
375  @SideEffectFree
376  @Override
377  public String toString(@GuardSatisfied Equality this) {
378    return repr();
379  }
380
381  // //////////////////////////////////////////////////////////////////////
382  // Processing of data
383
384  /**
385   * Return a List of VarInfos that do not fit into this set anymore.
386   *
387   * <p>Originally (8/14/2003), this did not check for the modified bits. It seems however, quite
388   * wrong to leave variables in the same equality set when one is missing and the other is not.
389   * It's possible we should go farther and break out of the equality set any variable that is
390   * missingOutOfBounds (JHP).
391   *
392   * @param vt the newly-observed sample
393   * @param count the number of times the sample is seen
394   * @return a List of VarInfos that do not fit into this set anymore
395   */
396  public List<VarInfo> add(ValueTuple vt, int count) {
397    // Need to handle specially if leader is missing.
398    VarInfo leader = leader();
399    Object leaderValue = leader.getValueOrNull(vt);
400    int leaderMod = leader.getModified(vt);
401    boolean leaderOutOfBounds = leader.missingOutOfBounds();
402    if (leader.isMissing(vt)) {
403    } else {
404      numSamples += count;
405    }
406
407    List<VarInfo> result = new ArrayList<>();
408    if (debug.isLoggable(Level.FINE)) {
409      debug.fine("Doing add at " + this.ppt.parent.name() + " for " + this);
410    }
411    for (Iterator<VarInfo> i = vars.iterator(); i.hasNext(); ) {
412      VarInfo vi = i.next();
413      if (vi == leader) {
414        continue;
415      }
416      assert vi.comparableNWay(leader);
417      Object viValue = vi.getValueOrNull(vt);
418      int viMod = vi.getModified(vt);
419      // The following is possible because values are interned.  The
420      // test also takes into account missing values, since they are
421      // null.
422      if ((leaderValue == viValue)
423          && (leaderMod == viMod)
424          && !leaderOutOfBounds
425          && !vi.missingOutOfBounds()
426          // If the values are NaN, treat them as different.
427          && !((leaderValue instanceof Double) && ((Double) leaderValue).isNaN())) {
428        // The values are the same.
429        continue;
430      }
431      // The values differ.  Remove this from the equality set.
432
433      //       if (debug.isLoggable(Level.FINE)) {
434      //         debug.fine ("  vi name: " + vi.name.name());
435      //         debug.fine ("  vi value: " + viValue);
436      //         debug.fine ("  le value: " + leaderValue);
437      //       }
438      if (Debug.logOn()) {
439        Debug.log(
440            getClass(),
441            ppt.parent,
442            Debug.vis(vi),
443            "Var "
444                + vi.name()
445                + " ["
446                + viValue
447                + ","
448                + viMod
449                + "] split from leader "
450                + leader.name()
451                + " ["
452                + Debug.toString(leaderValue)
453                + ","
454                + leaderMod
455                + "]");
456      }
457
458      result.add(vi);
459      i.remove();
460    }
461
462    return result;
463  }
464
465  //  This method isn't going to be called, but it's declared abstract in Invariant.
466  @Override
467  protected Invariant resurrect_done(int[] permutation) {
468    throw new UnsupportedOperationException();
469  }
470
471  //  This method isn't going to be called, but it's declared abstract in Invariant.
472  @Pure
473  @Override
474  public boolean isSameFormula(Invariant other) {
475    throw new UnsupportedOperationException(
476        "Equality.isSameFormula(): this method should not be called");
477  }
478
479  /**
480   * Convert Equality invariants into normal IntEqual type for filtering, printing, etc. Add these
481   * to parent.
482   *
483   * <p>If the leader was changed to not be the first member of the group adds leader == leader
484   * invariant as well since that invariant is used in suppressions and obvious tests.
485   */
486  public void postProcess() {
487    if (this.numSamples() == 0) {
488      // All were missing or not present
489      return;
490    }
491    PptTopLevel parent = this.ppt.parent;
492    VarInfo[] varArray = this.vars.toArray(new VarInfo[0]);
493    if (debugPostProcess.isLoggable(Level.FINE)) {
494      debugPostProcess.fine("Doing postProcess: " + this.format_daikon());
495      debugPostProcess.fine("  at: " + this.ppt.parent.name());
496    }
497    VarInfo leader = leader();
498
499    if (debugPostProcess.isLoggable(Level.FINE)) {
500      debugPostProcess.fine("  var1: " + leader.name());
501    }
502    for (int i = 0; i < varArray.length; i++) {
503      if (varArray[i] == leader) {
504        continue;
505      }
506      if (debugPostProcess.isLoggable(Level.FINE)) {
507        debugPostProcess.fine("  var2: " + varArray[i].name());
508      }
509
510      // Guard to prevent creating unnecessary Equality invariants related to
511      // purity method black boxing
512      if (leader.function_args != null
513          && varArray[i].function_args != null
514          && leader.function_args.size() > 1
515          && leader.function_args.size() == varArray[i].function_args.size()) {
516        boolean allEqual = true;
517        for (int j = 0; j < leader.function_args.size(); j++) {
518          if (!leader.function_args.get(j).isEqualTo(varArray[i].function_args.get(j))) {
519            allEqual = false;
520            break;
521          }
522        }
523        if (allEqual) {
524          continue;
525        }
526      }
527
528      parent.create_equality_inv(leader, varArray[i], numSamples());
529    }
530  }
531
532  /**
533   * Switch the leader of this invariant, if possible, to a more canonical VarInfo: a VarInfo that
534   * is not isDerived() is better than one that is; one that is not isDerivedParamAndUninteresting()
535   * is better than one that is; and other things being equal, choose the least complex name. Call
536   * this only after postProcess has been called. We do a pivot so that anything that's interesting
537   * to be printed gets printed and not filtered out. For example, if a == b and a is the leader,
538   * but not interesting, we still want to print f(b) as an invariant. Thus we pivot b to be the
539   * leader. Later on, each relevant PptSlice gets pivoted. But not here.
540   */
541  public void pivot() {
542    VarInfo newLeader = null;
543    for (VarInfo var : vars) {
544      // System.out.printf("  processing %s%n", var);
545      if (newLeader == null) {
546        newLeader = var;
547      } else if (newLeader.isDerivedParamAndUninteresting()
548          && !var.isDerivedParamAndUninteresting()) {
549        // System.out.printf("%s derived and uninteresting, %s is leader%n",
550        //                   newLeader, var);
551        newLeader = var;
552      } else if (var.isDerivedParamAndUninteresting()
553          && !newLeader.isDerivedParamAndUninteresting()) {
554        // do nothing
555      } else if (var.derivedDepth() < newLeader.derivedDepth()) {
556        // System.out.printf("%s greater depth, %s is leader%n",
557        //                    newLeader, var);
558        newLeader = var;
559      } else if (var.derivedDepth() > newLeader.derivedDepth()) {
560        // do nothing
561      }
562      // if we got here, this is the "all other things being equal" case
563      else if (var.complexity() < newLeader.complexity()) {
564        // System.out.printf("%s greater comlexity, %s is leader%n",
565        //                   newLeader, var);
566        newLeader = var;
567      }
568    }
569    // System.out.printf("%s complexity = %d, %s complexity = %d%n", leaderCache,
570    //                    leaderCache.complexity(), newLeader,
571    //                    newLeader.complexity());
572    leaderCache = newLeader;
573  }
574
575  @Override
576  public void repCheck() {
577    super.repCheck();
578    VarInfo leader = leader();
579    for (VarInfo var : vars) {
580      assert VarComparability.comparable(leader, var)
581          : "not comparable: " + leader.name() + " " + var.name() + " at ppt " + ppt.parent.name();
582    }
583  }
584
585  @Override
586  public boolean enabled(@Prototype Equality this) {
587    throw new Error("do not invoke " + getClass() + ".enabled()");
588  }
589
590  @Override
591  public boolean valid_types(@Prototype Equality this, VarInfo[] vis) {
592    throw new Error("do not invoke " + getClass() + ".valid_types()");
593  }
594
595  @Override
596  protected @NonPrototype Equality instantiate_dyn(@Prototype Equality this, PptSlice slice) {
597    throw new Error("do not invoke " + getClass() + ".instantiate_dyn()");
598  }
599
600  @Override
601  public @Nullable @NonPrototype Equality merge(
602      @Prototype Equality this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) {
603    throw new Error("Don't merge Equality invariants");
604  }
605}