001package daikon;
002
003import daikon.inv.Invariant;
004import daikon.inv.InvariantStatus;
005import daikon.inv.binary.twoScalar.LinearBinary;
006import daikon.inv.binary.twoScalar.LinearBinaryFloat;
007import daikon.inv.ternary.threeScalar.LinearTernary;
008import daikon.inv.ternary.threeScalar.LinearTernaryFloat;
009import daikon.inv.unary.scalar.OneOfFloat;
010import daikon.inv.unary.scalar.OneOfScalar;
011import daikon.inv.unary.sequence.OneOfFloatSequence;
012import daikon.inv.unary.sequence.OneOfSequence;
013import daikon.inv.unary.string.OneOfString;
014import daikon.inv.unary.stringsequence.OneOfStringSequence;
015import daikon.suppress.NIS;
016import java.io.PrintWriter;
017import java.io.Serializable;
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Comparator;
021import java.util.Iterator;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Set;
025import java.util.logging.Level;
026import java.util.logging.Logger;
027import org.checkerframework.checker.interning.qual.Interned;
028import org.checkerframework.checker.lock.qual.GuardSatisfied;
029import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
030import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
031import org.checkerframework.checker.nullness.qual.NonNull;
032import org.checkerframework.checker.nullness.qual.Nullable;
033import org.checkerframework.checker.nullness.qual.RequiresNonNull;
034import org.checkerframework.dataflow.qual.Pure;
035import org.checkerframework.dataflow.qual.SideEffectFree;
036
037/**
038 * Class that implements dynamic constants optimization. This optimization doesn't instantiate
039 * invariants over constant variables (i.e., that that have only seen one value). When the variable
040 * receives a second value, invariants are instantiated and are given the sample representing the
041 * previous constant value. Each DynamicConstants object is associated with a single program point,
042 * ppt.
043 */
044public class DynamicConstants implements Serializable {
045
046  static final long serialVersionUID = 20040401L;
047
048  // If true don't create any invariants (including OneOfs) over dynamic
049  // constants during post processing.  Normally, the configuration
050  // variable OneOf_only is more appropriate.
051  static final boolean no_post_process = false;
052
053  // Variables starting with dkconfig_ should only be set via the
054  // daikon.config.Configuration interface.
055
056  /**
057   * Whether to use the dynamic constants optimization. This optimization doesn't instantiate
058   * invariants over constant variables (i.e., that that have only seen one value). When the
059   * variable receives a second value, invariants are instantiated and are given the sample
060   * representing the previous constant value.
061   */
062  public static boolean dkconfig_use_dynamic_constant_optimization = true;
063
064  /**
065   * Boolean. Controls which invariants are created for variables that are constant for the entire
066   * run. If true, create only OneOf invariants. If false, create all possible invariants.
067   *
068   * <p>Note that setting this to true only fails to create invariants between constants. Invariants
069   * between constants and non-constants are created regardless.
070   *
071   * <p>A problem occurs with merging when this is turned on. If a var_info is constant at one child
072   * slice, but not constant at the other child slice, interesting invariants may not be merged
073   * because they won't exist on the slice with the constant. This is thus currently defaulted to
074   * false.
075   */
076  public static boolean dkconfig_OneOf_only = false;
077
078  /** Debug tracer. */
079  public static final Logger debug = Logger.getLogger("daikon.DynamicConstants");
080
081  /**
082   * List of dynamic constants.
083   *
084   * <p>Each element, c, has c.constant = true, c.count &gt; 0, elt.val != null.
085   */
086  @SuppressWarnings("serial")
087  List<Constant> con_list = new ArrayList<>();
088
089  /**
090   * List of variables that have always been missing.
091   *
092   * <p>For each element c, c.always_missing = true or con.vi.missingOutOfBounds().
093   */
094  @SuppressWarnings("serial")
095  List<Constant> missing_list = new ArrayList<>();
096
097  // Same contents in both.  Why two data structures?
098  /** Array of all variables. Some may be non-constant. */
099  Constant[] all_vars;
100
101  // Same contents in both.  Why two data structures?
102  /** List of all variables. Some may be non-constant. */
103  @SuppressWarnings("serial")
104  List<Constant> all_list = new ArrayList<>();
105
106  /** Program point of these constants. */
107  PptTopLevel ppt;
108
109  /** Number of samples received. */
110  int sample_cnt = 0;
111
112  /**
113   * Class used to indicate, for each variable, whether it is constant (see boolean field
114   * "constant"). If it is, then the class also stores its constant value and its sample count.
115   *
116   * <p>Note that two objects of this class are equal if they refer to the same variable. This
117   * allows these to be stored in sets.
118   */
119  public static @Interned class Constant implements Serializable {
120
121    // We are Serializable, so we specify a version to allow changes to
122    // method signatures without breaking serialization.  If you add or
123    // remove fields, you should change this number to the current date.
124    static final long serialVersionUID = 20030913L;
125
126    // XXX Question: what if the constant value is itself null, as for a
127    // String or pointer?  Does the code distinguish that case from val not
128    // being set?
129    /**
130     * The value of the constant, or the previous constant value if constant==false and
131     * previously_constant==true. Null iff count=0.
132     */
133    @SuppressWarnings("serial")
134    public @MonotonicNonNull @Interned Object val = null;
135
136    /** The sample count of the constant. */
137    public int count = 0;
138
139    /** The variable that has this value. */
140    public VarInfo vi;
141
142    /** Whether or not this has been missing for every sample to date. */
143    boolean always_missing = true;
144
145    /** Whether or not this is constant. */
146    boolean constant = false;
147
148    /**
149     * Whether or not this was constant at the beginning of this sample. At the beginning of the
150     * add() method, all newly non-constant variables are marked (constant=false). It is sometimes
151     * useful within the remainder of processing that sample to know that a variable was constant at
152     * the beginning. The field previously_constant is set to true when constant is set to false,
153     * and then is itself set to false at the end of the add() method.
154     */
155    boolean previously_constant = false;
156
157    /**
158     * Whether or not this was always missing at the beginning of this sample. At the beginning of
159     * the add() method, all newly non missing variables are marked (always_missing=false). It is
160     * sometimes useful within the remainder of processing that sample to know that a variable was
161     * missing at the beginning. The field previous_missing set to true when missing is set to
162     * false, and then is itself set to false at the end of the add() method.
163     */
164    boolean previous_missing = false;
165
166    /** Check representation invariant. */
167    public void checkRep() {
168      // This assertion is not valid.  If first sample is missing, then
169      // always_missing=true and previous_missing=false.
170      // assert (always_missing ? previous_missing : true) : toString();
171
172      assert !(constant && previously_constant) : toString();
173
174      // Whereas values can be null, null is never the value for a dynamic
175      // constant.
176      assert ((constant || previously_constant)
177              ? (val != null && count > 0)
178              : (val == null && count == 0))
179          : toString();
180    }
181
182    /**
183     * Creates a new Constant, indicating whether the given variable is a constant.
184     *
185     * @param vi the variable that might be a constant
186     */
187    @SuppressWarnings("super.invocation")
188    public Constant(VarInfo vi) {
189      this.vi = vi;
190    }
191
192    /**
193     * Returns whether the specified variable is currently a constant OR was a constant at the
194     * beginning of constants processing.
195     */
196    @Pure
197    public boolean is_prev_constant() {
198      return constant || previously_constant;
199    }
200
201    @EnsuresNonNullIf(result = true, expression = "#1")
202    @Pure
203    @Override
204    public boolean equals(@GuardSatisfied Constant this, @GuardSatisfied @Nullable Object obj) {
205      if (!(obj instanceof Constant)) {
206        return false;
207      }
208      Constant c = (Constant) obj;
209      return c.vi == vi;
210    }
211
212    @Pure
213    @Override
214    public int hashCode(@GuardSatisfied Constant this) {
215      return vi.hashCode();
216    }
217
218    @Override
219    @SuppressWarnings("all:purity") // side effects to local state (string creation)
220    @SideEffectFree
221    public String toString(@GuardSatisfied Constant this) {
222
223      StringBuilder out = new StringBuilder();
224      out.append(vi.name());
225      if (val == null) {
226        out.append(" (val missing)");
227      } else {
228        out.append(" (val=" + val + ")");
229      }
230      if (vi.isCanonical()) {
231        out.append(" (leader) ");
232      }
233      out.append(
234          " [always_missing="
235              + always_missing
236              + ", constant="
237              + constant
238              + ", previously_constant="
239              + previously_constant
240              + ", previous_missing="
241              + previous_missing
242              + "]");
243      return out.toString();
244    }
245  }
246
247  /** Compares two constants based on the vi_index of their variable. */
248  public static final class ConIndexComparator implements Comparator<Constant>, Serializable {
249    // We are Serializable, so we specify a version to allow changes to
250    // method signatures without breaking serialization.  If you add or
251    // remove fields, you should change this number to the current date.
252    static final long serialVersionUID = 20050923L;
253
254    private ConIndexComparator() {}
255
256    @Pure
257    @Override
258    public int compare(Constant con1, Constant con2) {
259      return con1.vi.varinfo_index - con2.vi.varinfo_index;
260    }
261
262    public static ConIndexComparator getInstance() {
263      return theInstance;
264    }
265
266    static final ConIndexComparator theInstance = new ConIndexComparator();
267  }
268
269  /** Create an initial list of constants and missing variables for the specified ppt. */
270  public DynamicConstants(PptTopLevel ppt) {
271
272    this.ppt = ppt;
273
274    // Start everything off as missing (since we haven't seen any values yet)
275    for (VarInfo vi : ppt.var_infos) {
276      @SuppressWarnings("interning") // one Constant per VarInfo
277      @Interned Constant c = new Constant(vi);
278      all_list.add(c);
279      missing_list.add(c);
280    }
281    all_vars = all_list.toArray(new Constant[0]);
282  }
283
284  /**
285   * Checks each current constant to see if it is still a constant. Constants must have the same
286   * value and cannot be missing. In the long run a better job of dealing with missing might be
287   * helpful. Also checks each variable that has always been missing to date to insure that it is
288   * still missing.
289   *
290   * <p>Creates all new views required for the newly non constants (noncons) and the newly
291   * non-missing (non_missing).
292   */
293  public void add(ValueTuple vt, int count) {
294
295    // System.out.println("DynamicConstants.add : " + vt.toString(ppt.var_infos));
296
297    List<Constant> non_missing = new ArrayList<>();
298    List<Constant> non_con = new ArrayList<>();
299
300    // Check each constant, destroy any that are missing or different
301    for (Iterator<Constant> i = con_list.iterator(); i.hasNext(); ) {
302      Constant con = i.next();
303      assert con.constant;
304      con.checkRep();
305
306      if (Debug.logDetail()) {
307        Debug.log(
308            getClass(),
309            ppt,
310            Debug.vis(con.vi),
311            "Adding "
312                + Debug.toString(con.vi.getValueOrNull(vt))
313                + " to constant "
314                + con.val
315                + " : missing = "
316                + missing(con.vi, vt)
317                + ": samples = "
318                + con.count
319                + "/"
320                + count);
321      }
322      if (missing(con.vi, vt) || (con.val != con.vi.getValue(vt))) {
323        i.remove();
324        con.constant = false;
325        con.previously_constant = true;
326        assert all_vars[con.vi.varinfo_index].constant == false;
327        non_con.add(con);
328      } else {
329        con.count += count;
330      }
331      con.checkRep();
332    }
333
334    // Move any non-missing variables to the constant list and init their val.
335    // If a variable is missing out of bounds, leave it on this list
336    // forever (guaranteeing that invariants will never be instantiated over
337    // it).
338    for (Iterator<Constant> i = missing_list.iterator(); i.hasNext(); ) {
339      Constant con = i.next();
340      con.checkRep();
341      if (con.vi.missingOutOfBounds()) {
342        continue;
343      }
344
345      if (missing(con.vi, vt)) {
346        // value is still missing, nothing to do (we incremented its count above)
347        continue;
348      }
349
350      @Interned Object val = con.vi.getValue(vt);
351      // the variable is not missing, so it is non-null
352      assert val != null;
353
354      i.remove();
355      con.always_missing = false;
356      if (Debug.logDetail()) {
357        Debug.log(
358            getClass(),
359            ppt,
360            Debug.vis(con.vi),
361            "Adding "
362                + Debug.toString(val)
363                + " to missing : missing = "
364                + missing(con.vi, vt)
365                + ": samples = "
366                + con.count
367                + "/"
368                + count
369                + "/"
370                + sample_cnt);
371      }
372      // Is the Constant missing because it was initialized that way, or
373      // has the program point seen values in the past?
374      if (sample_cnt == 0) {
375        // First sample for this program point (& this variable)
376        con.val = val;
377        con.count = count;
378        con.constant = true;
379        con_list.add(con);
380      } else {
381        // This variable truly is missing; has seen a missing value in the past.
382        non_missing.add(con);
383        con.previous_missing = true;
384      }
385    }
386
387    sample_cnt += count;
388
389    // Create slices over newly non-constant and non-missing variables
390    instantiate_new_views(non_con, non_missing);
391
392    // Turn off previously_constant on all newly non-constants
393    for (Constant con : non_con) {
394      con.previously_constant = false;
395      @SuppressWarnings("nullness") // reinitialization
396      @NonNull Object nullValue = null;
397      con.val = nullValue;
398      con.count = 0;
399      con.checkRep();
400    }
401
402    // Turn off previous_missing on all newly non-missing
403    for (Constant con : non_missing) {
404      con.previous_missing = false;
405      con.checkRep();
406    }
407  }
408
409  /** Returns whether the specified variable is missing in this ValueTuple. */
410  private boolean missing(VarInfo vi, ValueTuple vt) {
411
412    int mod = vt.getModified(vi);
413    return (mod == ValueTuple.MISSING_FLOW) || (mod == ValueTuple.MISSING_NONSENSICAL);
414  }
415
416  /** Returns the Constant for the specified variable. */
417  @Pure
418  public Constant getConstant(VarInfo vi) {
419
420    Constant result = all_vars[vi.varinfo_index];
421    result.checkRep();
422    return result;
423  }
424
425  /** Returns whether the specified variable is currently a constant. */
426  @Pure
427  public boolean is_constant(VarInfo vi) {
428
429    return getConstant(vi).constant;
430  }
431
432  /**
433   * Returns whether the specified variable is currently a constant OR was a constant at the
434   * beginning of constants processing.
435   */
436  @Pure
437  public boolean is_prev_constant(VarInfo vi) {
438
439    return getConstant(vi).is_prev_constant();
440  }
441
442  /**
443   * Returns the constant value of the specified variable, or null if the variable is not constant
444   * or prev_constant. But, it is apparently only called on constants with a value.
445   */
446  public @Interned Object constant_value(VarInfo vi) {
447
448    @SuppressWarnings("nullness") // non-missing value, so non-null val field
449    @NonNull Object result = getConstant(vi).val;
450    return result;
451  }
452
453  /** Returns whether the specified variable missing for all values so far. */
454  @Pure
455  public boolean is_missing(VarInfo vi) {
456
457    return getConstant(vi).always_missing;
458  }
459
460  /**
461   * Returns whether the specified variable is currently missing OR was missing at the beginning of
462   * constants processing.
463   */
464  @Pure
465  public boolean is_prev_missing(VarInfo vi) {
466
467    Constant c = all_vars[vi.varinfo_index];
468    return c.always_missing || c.previous_missing;
469  }
470
471  /** Returns the number of constants that are leaders. */
472  public int constant_leader_cnt() {
473
474    int con_cnt = 0;
475    for (Constant con : con_list) {
476      if (con.vi.isCanonical()) {
477        con_cnt++;
478      }
479    }
480
481    return con_cnt;
482  }
483
484  /**
485   * Creates all new views required for the newly non constants (noncons) and the newly non-missing
486   * (non_missing).
487   */
488  public void instantiate_new_views(List<Constant> noncons, List<Constant> non_missing) {
489
490    if (Debug.logOn()) {
491      for (Constant con : noncons) {
492        Debug.log(
493            getClass(),
494            ppt,
495            Debug.vis(con.vi),
496            "is non constant"
497                + " with val = "
498                + Debug.toString(con.val)
499                + " with count = "
500                + con.count);
501      }
502      for (Constant con : non_missing) {
503        Debug.log(getClass(), ppt, Debug.vis(con.vi), "is non missing");
504      }
505    }
506
507    for (Constant con : noncons) {
508      con.checkRep();
509    }
510    for (Constant con : non_missing) {
511      con.checkRep();
512    }
513
514    // Create all of the views over noncons and noncons+con_list.
515    // Since everything starts out as a constant, it is only necessary
516    // to combine the newly non-constants with a combination of
517    // the remaining constants and the newly-non constants.  Any slices
518    // between the non-constants and other variables will have already
519    // been created when those other variables became non-constants.
520    if (noncons.size() > 0) {
521      List<Constant> cons = new ArrayList<>();
522      cons.addAll(con_list);
523      cons.addAll(noncons);
524      debug.fine("Instantiating non constants in ppt: " + ppt.name());
525      instantiate_views(noncons, cons);
526    }
527
528    // Create all views over the newly non-missing.  Since missing
529    // vars were not included in any previous views, we must match them
530    // against all variables.
531    if (non_missing.size() > 0) {
532      debug.fine("Instantiating non missing in ppt: " + ppt.name());
533      instantiate_views(non_missing, all_list);
534    }
535
536    // Create any ternary invariants that are suppressed when one
537    // of the variables is a constant.  Currently, only LinearTernary
538    // falls into this list (It is suppressed by (x = C) && (Ay + Bz = D))
539    if (NIS.dkconfig_enabled) {
540      instantiate_constant_suppressions(noncons, all_list);
541    }
542  }
543
544  /**
545   * Instantiate views and invariants across each combination of vars from list1 and list2. If each
546   * item in a new slice was a constant, the constant values are applied.
547   *
548   * <p>The following slices will be created:
549   *
550   * <pre>
551   *    unary:   list1-vars
552   *    binary:  list1-vars X list2-vars
553   *    ternary: list1-vars X list2-vars X list2-vars
554   * </pre>
555   */
556  private void instantiate_views(List<Constant> list1, List<Constant> list2) {
557
558    // Get list1 leaders
559    Set<Constant> leaders1 = new LinkedHashSet<>();
560    for (Constant con : list1) {
561      if (con.vi.isCanonical()) {
562        leaders1.add(con);
563      }
564    }
565
566    // Get list2 leaders
567    Set<Constant> leaders2 = new LinkedHashSet<>();
568    for (Constant con : list2) {
569      if (con.vi.isCanonical()) {
570        leaders2.add(con);
571      }
572    }
573
574    if (debug.isLoggable(Level.FINE)) {
575      debug.fine("instantiating over " + leaders1.size() + " leaders1: " + leaders1);
576      debug.fine("instantiating over " + leaders2.size() + " leaders2: " + leaders2);
577    }
578
579    // any new views created
580    List<PptSlice> new_views = new ArrayList<>();
581
582    int mod = ValueTuple.MODIFIED;
583
584    // Unary slices/invariants
585    for (Constant con : leaders1) {
586      if (Debug.logOn()) {
587        Debug.log(getClass(), ppt, Debug.vis(con.vi), "Considering slice");
588      }
589      if (!ppt.is_slice_ok(con.vi)) {
590        continue;
591      }
592      PptSlice1 slice1 = new PptSlice1(ppt, con.vi);
593      slice1.instantiate_invariants();
594      if (Debug.logOn()) {
595        Debug.log(getClass(), ppt, Debug.vis(con.vi), "Instantiated invs");
596      }
597      if (con.count > 0) {
598        assert con.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0";
599        slice1.add_val_bu(con.val, mod, con.count);
600      }
601      new_views.add(slice1);
602    }
603
604    // Binary slices/invariants.
605    for (Constant con1 : leaders1) {
606      for (Constant con2 : leaders2) {
607        Constant c1 = con1;
608        Constant c2 = con2;
609        Debug.log(getClass(), ppt, Debug.vis(c1.vi, c2.vi), "Considering slice");
610        if (con2.vi.varinfo_index < con1.vi.varinfo_index) {
611          if (leaders1.contains(con2)) {
612            // The variable is in both leader lists.
613            // Don't add it on this iteration; add it when the variables
614            // are given in order (to prevent creating the slice twice).
615            continue;
616          }
617          c1 = con2;
618          c2 = con1;
619        }
620        if (!ppt.is_slice_ok(c1.vi, c2.vi)) {
621          if (Debug.logOn()) {
622            Debug.log(
623                debug,
624                getClass(),
625                ppt,
626                Debug.vis(c1.vi, c2.vi),
627                "Not instantiating slice " + c1.vi.equalitySet.size());
628          }
629          continue;
630        }
631        PptSlice2 slice2 = new PptSlice2(ppt, c1.vi, c2.vi);
632        Debug.log(
633            getClass(),
634            ppt,
635            Debug.vis(c1.vi, c2.vi),
636            String.format("instantiating slice %s [%s %s]%n", slice2, c1, c2));
637        slice2.instantiate_invariants();
638        if (c1.count > 0 && c2.count > 0) {
639          assert c1.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0";
640          assert c2.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0";
641          slice2.add_val_bu(c1.val, c2.val, mod, mod, con1.count);
642        }
643        new_views.add(slice2);
644      }
645    }
646
647    // Ternary slices/invariants.  Note that if a variable is in both
648    // leader lists, it is only added when it is in order (to prevent
649    // creating the slice twice).
650    for (Constant con1 : leaders1) {
651      for (Constant con2 : leaders2) {
652        if ((con2.vi.varinfo_index < con1.vi.varinfo_index) && leaders1.contains(con2)) {
653          continue;
654        }
655        for (Constant con3 : leaders2) {
656          if ((con3.vi.varinfo_index < con2.vi.varinfo_index)
657              || ((con3.vi.varinfo_index < con1.vi.varinfo_index) && leaders1.contains(con3)))
658            continue;
659          Constant[] con_arr = {con1, con2, con3};
660          Arrays.sort(con_arr, ConIndexComparator.getInstance());
661          assert (con_arr[0].vi.varinfo_index <= con_arr[1].vi.varinfo_index)
662              && (con_arr[1].vi.varinfo_index <= con_arr[2].vi.varinfo_index);
663          if (!ppt.is_slice_ok(con_arr[0].vi, con_arr[1].vi, con_arr[2].vi)) {
664            continue;
665          }
666
667          PptSlice3 slice3 = new PptSlice3(ppt, con_arr[0].vi, con_arr[1].vi, con_arr[2].vi);
668          slice3.instantiate_invariants();
669          if ((con_arr[0].count > 0) && (con_arr[1].count > 0) && (con_arr[2].count > 0)) {
670            assert con_arr[0].val != null
671                : "@AssumeAssertion(nullness): dependent: val != null when count>0";
672            assert con_arr[1].val != null
673                : "@AssumeAssertion(nullness): dependent: val != null when count>0";
674            assert con_arr[2].val != null
675                : "@AssumeAssertion(nullness): dependent: val != null when count>0";
676            slice3.add_val_bu(
677                con_arr[0].val, con_arr[1].val, con_arr[2].val, mod, mod, mod, con_arr[0].count);
678          }
679          new_views.add(slice3);
680        }
681      }
682    }
683
684    // Debug print the created slices
685    if (Debug.logOn() || debug.isLoggable(Level.FINE)) {
686      int[] slice_cnt = {0, 0, 0, 0};
687      int[] inv_cnt = {0, 0, 0, 0};
688      int[] true_inv_cnt = {0, 0, 0, 0};
689      for (PptSlice slice : new_views) {
690        for (Invariant inv : slice.invs) {
691          inv.log("created, falsified = %b", inv.is_false());
692          if (!inv.is_false()) {
693            true_inv_cnt[slice.arity()]++;
694          } else {
695            String vals = "";
696            for (VarInfo vi : slice.var_infos) {
697              vals += vi.name() + "=" + Debug.toString(constant_value(vi)) + " ";
698            }
699            inv.log("Invariant %s destroyed by constant values %s", inv.format(), vals);
700          }
701        }
702        if (slice.invs.size() > 0) {
703          slice_cnt[slice.arity()]++;
704        }
705        inv_cnt[slice.arity()] += slice.invs.size();
706        if (Debug.logDetail()) {
707          StringBuilder sb = new StringBuilder();
708          for (int j = 0; j < slice.arity(); j++) {
709            VarInfo v = slice.var_infos[j];
710            sb.append(v.name() + " [" + v.file_rep_type + "] [" + v.comparability + "] ");
711          }
712          Debug.log(
713              debug,
714              getClass(),
715              ppt,
716              slice.var_infos,
717              "Adding slice over " + sb + ": with " + slice.invs.size() + " invariants");
718        }
719      }
720      for (int i = 1; i <= 3; i++) {
721        debug.fine(
722            "Added "
723                + slice_cnt[i]
724                + " slice"
725                + i
726                + "s with "
727                + true_inv_cnt[i]
728                + " invariants ("
729                + inv_cnt[i]
730                + " total)");
731      }
732
733      String leader1_str = "";
734      int leader1_cnt = 0;
735      for (Constant con1 : leaders1) {
736        if (con1.vi.file_rep_type == ProglangType.INT) {
737          leader1_str += con1.vi.name() + " ";
738          leader1_cnt++;
739        }
740      }
741
742      String leader2_str = "";
743      int leader2_cnt = 0;
744      for (Constant con1 : leaders2) {
745        if (con1.vi.file_rep_type == ProglangType.INT) {
746          leader2_str += con1.vi.name() + " ";
747          leader2_cnt++;
748        }
749      }
750      debug.fine(
751          leader1_cnt
752              + " leader1 ints ("
753              + leader1_str
754              + "): "
755              + leader2_cnt
756              + " leader2 ints ("
757              + leader2_str);
758    }
759
760    // Remove any falsified invariants from the new views.  Don't
761    // call remove_falsified() since that has side-effects (such as
762    // NIS processing on the falsified invariants) that we don't want.
763    for (PptSlice slice : new_views) {
764      List<Invariant> to_remove = new ArrayList<>();
765      for (Invariant inv : slice.invs) {
766        if (inv.is_false()) {
767          to_remove.add(inv);
768        }
769      }
770      slice.removeInvariants(to_remove);
771    }
772
773    // Add the new slices to the top level ppt.  This will discard any
774    // slices that ended up with zero invariants
775    ppt.addViews(new_views);
776  }
777
778  public void instantiate_constant_suppressions(List<Constant> new_noncons, List<Constant> all) {
779
780    // Find all of the variable (non-constant) non-missing
781    // integral/float leaders
782    List<Constant> vars = new ArrayList<>();
783    for (Constant con : all) {
784      if (con.always_missing || con.previous_missing) {
785        continue;
786      }
787      if (con.constant || con.previously_constant) {
788        continue;
789      }
790      if (!con.vi.isCanonical()) {
791        continue;
792      }
793      if (!con.vi.file_rep_type.isIntegral() && !con.vi.file_rep_type.isFloat()) {
794        continue;
795      }
796      if (con.vi.rep_type.isArray()) {
797        continue;
798      }
799      vars.add(con);
800    }
801
802    // Find all of the new non-constant integer/float leaders
803    List<Constant> new_leaders = new ArrayList<>();
804    for (Constant con : new_noncons) {
805      if (!con.vi.isCanonical()) {
806        continue;
807      }
808      if (!con.vi.file_rep_type.isIntegral() && !con.vi.file_rep_type.isFloat()) {
809        continue;
810      }
811      if (con.vi.rep_type.isArray()) {
812        continue;
813      }
814      new_leaders.add(con);
815    }
816
817    if (debug.isLoggable(Level.FINE)) {
818      debug.fine("new non-con leaders = " + new_leaders);
819      debug.fine("variable leaders = " + vars);
820    }
821
822    // Consider all of the ternary slices with one new non-constant
823    for (int i = 0; i < new_leaders.size(); i++) {
824      Constant con1 = new_leaders.get(i);
825      assert con1.val != null : "@AssumeAssertion(nullness)";
826      for (int j = 0; j < vars.size(); j++) {
827        Constant con2 = vars.get(j);
828        assert con1 != con2;
829        for (int k = j; k < vars.size(); k++) {
830          Constant con3 = vars.get(k);
831          assert con1 != con3;
832          if (!ppt.is_slice_ok(con1.vi, con2.vi, con3.vi)) {
833            continue;
834          }
835
836          if (debug.isLoggable(Level.FINE)) {
837            debug.fine(String.format("considering slice %s %s %s", con1, con2, con3));
838          }
839
840          // Look for a LinearBinary over two variables.  If it doesn't
841          // exist we don't create a LinearTernary
842          Invariant lb = find_linear_binary(ppt.findSlice(con2.vi, con3.vi));
843          if (lb == null) {
844            continue;
845          }
846
847          // Find the ternary slice and create it if it is not there
848          PptSlice slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi);
849
850          // Create the LinearTernary invariant from the LinearBinary
851          // invariant and the constant value
852          Invariant lt = null;
853          if (con1.vi.file_rep_type.isIntegral()) {
854            lt = LinearTernary.get_proto().instantiate(slice);
855            if (lt != null) {
856              ((LinearTernary) lt).setup((LinearBinary) lb, con1.vi, ((Long) con1.val).longValue());
857            }
858          } else /* must be float */ {
859            lt = LinearTernaryFloat.get_proto().instantiate(slice);
860            if (lt != null) {
861              ((LinearTernaryFloat) lt)
862                  .setup((LinearBinaryFloat) lb, con1.vi, ((Double) con1.val).doubleValue());
863            }
864          }
865          if (lt != null) {
866            if (Debug.dkconfig_internal_check) {
867              assert slice.find_inv_by_class(lt.getClass()) == null
868                  : "inv = " + lt.format() + " slice = " + slice;
869            }
870            slice.addInvariant(lt);
871            if (debug.isLoggable(Level.FINE)) {
872              debug.fine("Adding invariant " + lt.format() + " to slice " + slice);
873            }
874          }
875        }
876      }
877    }
878
879    // Consider all of the ternary slices with two new non-constants
880    for (int i = 0; i < new_leaders.size(); i++) {
881      Constant con1 = new_leaders.get(i);
882      assert con1.val != null : "@AssumeAssertion(nullness)";
883      for (int j = i; j < new_leaders.size(); j++) {
884        Constant con2 = new_leaders.get(j);
885        for (int k = 0; k < vars.size(); k++) {
886          Constant con3 = vars.get(k);
887          assert con2 != con3;
888          assert con1 != con3;
889          if (!ppt.is_slice_ok(con1.vi, con2.vi, con3.vi)) {
890            continue;
891          }
892
893          if (debug.isLoggable(Level.FINE)) {
894            debug.fine(String.format("considering slice %s %s %s", con1, con2, con3));
895          }
896
897          // Create the ternary slice
898
899          // Create the LinearTernary invariant from the OneOf invariant
900          // (if any) and the constant values.  If no OneOf exists,
901          // there can be no interesting plane of the points
902          Invariant lt = null;
903          PptSlice slice = null;
904          InvariantStatus sts = InvariantStatus.NO_CHANGE;
905          if (con1.vi.file_rep_type.isIntegral()) {
906            OneOfScalar oo =
907                (OneOfScalar) ppt.find_inv_by_class(new VarInfo[] {con3.vi}, OneOfScalar.class);
908            if (oo == null) {
909              continue;
910            }
911            slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi);
912
913            lt = LinearTernary.get_proto().instantiate(slice);
914            if (lt != null) {
915              assert con2.val != null : "@AssumeAssertion(nullness)";
916              sts =
917                  ((LinearTernary) lt)
918                      .setup(
919                          oo,
920                          con1.vi,
921                          ((Long) con1.val).longValue(),
922                          con2.vi,
923                          ((Long) con2.val).longValue());
924            }
925          } else /* must be float */ {
926            OneOfFloat oo =
927                (OneOfFloat) ppt.find_inv_by_class(new VarInfo[] {con3.vi}, OneOfFloat.class);
928            if (oo == null) {
929              continue;
930            }
931            slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi);
932            lt = LinearTernaryFloat.get_proto().instantiate(slice);
933            if (lt != null) {
934              assert con2.val != null : "@AssumeAssertion(nullness)";
935              sts =
936                  ((LinearTernaryFloat) lt)
937                      .setup(
938                          oo,
939                          con1.vi,
940                          ((Double) con1.val).doubleValue(),
941                          con2.vi,
942                          ((Double) con2.val).doubleValue());
943            }
944          }
945          if ((lt != null) && (sts == InvariantStatus.NO_CHANGE)) {
946            if (Debug.dkconfig_internal_check) {
947              assert slice.find_inv_by_class(lt.getClass()) == null
948                  : "inv = " + lt.format() + " slice = " + slice;
949            }
950            slice.addInvariant(lt);
951            debug.fine("Adding invariant " + lt.format() + " to slice " + slice);
952          }
953        }
954      }
955    }
956  }
957
958  /**
959   * Looks for a LinearBinary invariant in the specified slice. Will match either float or integer
960   * versions.
961   */
962  private @Nullable Invariant find_linear_binary(@Nullable PptSlice slice) {
963
964    // if (debug.isLoggable (Level.FINE))
965    //  debug.fine ("considering slice " + slice);
966
967    if (slice == null) {
968      return null;
969    }
970
971    for (Invariant inv : slice.invs) {
972      // debug.fine ("inv = " + inv.getClass());
973      if ((inv.getClass() == LinearBinary.class) || (inv.getClass() == LinearBinaryFloat.class)) {
974        return inv;
975      }
976    }
977
978    return null;
979  }
980
981  /**
982   * Create invariants for any remaining constants. Right now, this looks for invariants between all
983   * of the constants. It's not clear that invariants between constants are interesting, but to
984   * match previous behavior, this is what we will do for now.
985   */
986  public void post_process() {
987
988    // if requested, don't create any post-processed invariants
989    if (no_post_process) {
990      int con_count = 0;
991      for (Constant con : con_list) {
992        if (!con.vi.isCanonical()) {
993          continue;
994        }
995        System.out.println(
996            "  Not creating invariants over leader " + con.vi.name() + " = " + con.val);
997        con_count++;
998      }
999      System.out.println(con_count + " constants at ppt " + ppt);
1000      return;
1001    }
1002
1003    // If specified, create only OneOf invariants.  Also create a reflexive
1004    // equality invariant, since that is assumed to exist in many places.
1005    if (dkconfig_OneOf_only) {
1006      for (Constant con : con_list) {
1007        if (!con.vi.isCanonical()) {
1008          continue;
1009        }
1010        instantiate_oneof(con);
1011        ppt.create_equality_inv(con.vi, con.vi, con.count);
1012      }
1013      return;
1014    }
1015
1016    // Get a list of all remaining constants and clear the existing list
1017    // (if the existing list is not cleared, constant slices will not
1018    // be created).
1019    List<Constant> noncons = con_list;
1020    for (Constant con : con_list) {
1021      con.constant = false;
1022      con.previously_constant = true;
1023    }
1024    con_list = new ArrayList<Constant>();
1025
1026    // Don't do anything with variables that have always been missing.  They
1027    // should have no invariants over them.
1028    List<Constant> non_missing = new ArrayList<>();
1029
1030    instantiate_new_views(noncons, non_missing);
1031
1032    /* Code to just create just unary slices for constants
1033      for (Constant con : con_list) {
1034        if (!con.vi.isCanonical()) {
1035          continue;
1036          }
1037        PptSlice1 slice1 = new PptSlice1 (ppt, con.vi);
1038        slice1.instantiate_invariants();
1039        if (con.val != null) {
1040          slice1.add_val (con.val, ValueTuple.MODIFIED, con.count);
1041          }
1042        new_views.add (slice1);
1043      }
1044      ppt.addViews (new_views);
1045    */
1046  }
1047
1048  /**
1049   * Create unary and binary constant invariants. The slices and invariants are created and
1050   * returned, but not added to the ppt. Note that when NIS.dkconfig_suppressor_list is turned on
1051   * (default is on), only unary and binary invariants that can be suppressors in NIS suppressions
1052   * are created.
1053   */
1054  @RequiresNonNull("daikon.suppress.NIS.suppressor_proto_invs")
1055  public List<PptSlice> create_constant_invs() {
1056
1057    // Turn off track logging so that we don't get voluminous messages
1058    // each time this is called
1059    boolean debug_on = Logger.getLogger("daikon.Debug").isLoggable(Level.FINE);
1060    if (debug_on) {
1061      LogHelper.setLevel("daikon.Debug", Level.OFF);
1062    }
1063
1064    // Get constant leaders
1065    List<Constant> leaders = new ArrayList<>(100);
1066    for (Constant con : con_list) {
1067      if (!con.vi.isCanonical()) {
1068        continue;
1069      }
1070
1071      // hashcode types are not involved in suppressions
1072      if (NIS.dkconfig_skip_hashcode_type) {
1073        if (con.vi.file_rep_type.isHashcode()) {
1074          continue;
1075        }
1076      }
1077
1078      leaders.add(con);
1079    }
1080
1081    List<PptSlice> new_views = new ArrayList<>(100);
1082    int mod = ValueTuple.MODIFIED;
1083
1084    // Unary slices/invariants
1085    for (Constant con : leaders) {
1086
1087      PptSlice1 slice1 = new PptSlice1(ppt, con.vi);
1088
1089      if (NIS.dkconfig_suppressor_list) {
1090        slice1.instantiate_invariants(NIS.suppressor_proto_invs);
1091      } else {
1092        slice1.instantiate_invariants();
1093      }
1094
1095      if (con.count > 0) {
1096        assert con.val != null : "@AssumeAssertion(nullness): dependent: val when count>0";
1097        slice1.add_val_bu(con.val, mod, con.count);
1098      }
1099      if (slice1.invs.size() > 0) {
1100        new_views.add(slice1);
1101      }
1102    }
1103
1104    // Binary slices/invariants
1105    for (int i = 0; i < leaders.size(); i++) {
1106      Constant con1 = leaders.get(i);
1107      for (int j = i; j < leaders.size(); j++) {
1108        Constant con2 = leaders.get(j);
1109        if (!con1.vi.compatible(con2.vi)) {
1110          continue;
1111        }
1112
1113        PptSlice2 slice2 = new PptSlice2(ppt, con1.vi, con2.vi);
1114        if (NIS.dkconfig_suppressor_list) {
1115          slice2.instantiate_invariants(NIS.suppressor_proto_invs);
1116        } else {
1117          slice2.instantiate_invariants();
1118        }
1119
1120        if (con1.count > 0 && con2.count > 0) {
1121          assert con1.val != null
1122              : "@AssumeAssertion(nullness): dependent: val != null when count>0";
1123          assert con2.val != null
1124              : "@AssumeAssertion(nullness): dependent: val != null when count>0";
1125          slice2.add_val_bu(con1.val, con2.val, mod, mod, con1.count);
1126        }
1127        if (slice2.invs.size() > 0) {
1128          new_views.add(slice2);
1129        }
1130      }
1131    }
1132
1133    // Remove any falsified invariants from the new views.
1134    for (PptSlice slice : new_views) {
1135      for (Iterator<Invariant> j = slice.invs.iterator(); j.hasNext(); ) {
1136        Invariant inv = j.next();
1137        if (inv.is_false()) {
1138          j.remove();
1139        }
1140      }
1141    }
1142
1143    if (debug_on) {
1144      LogHelper.setLevel("daikon.Debug", Level.FINE);
1145    }
1146
1147    return new_views;
1148  }
1149
1150  public void print_missing(PrintWriter out) {
1151
1152    for (Constant con : missing_list) {
1153      out.println(con.vi.name() + " is always missing");
1154    }
1155  }
1156
1157  /**
1158   * Merge dynamic constants from the children of this ppt. Only missing is merged since constants
1159   * are not used after we are done processing samples.
1160   */
1161  public void merge() {
1162
1163    // clear the constant and missing lists
1164    missing_list.clear();
1165    con_list.clear();
1166
1167    // Process each variable at this ppt.  If the variable is missing at
1168    // each of the children, it is also missing here.  Ignore children that
1169    // have no mapping for this variable
1170    for (VarInfo pvar : ppt.var_infos) {
1171      boolean missing = true;
1172      for (PptRelation rel : ppt.children) {
1173        VarInfo cvar = rel.childVar(pvar);
1174        if ((cvar != null)
1175            && (rel.child.constants != null)
1176            && !rel.child.constants.is_missing(cvar)) {
1177          missing = false;
1178          break;
1179        }
1180      }
1181      Constant c = all_vars[pvar.varinfo_index];
1182      c.checkRep();
1183      c.always_missing = missing;
1184      c.checkRep();
1185      if (missing) {
1186        missing_list.add(c);
1187      }
1188    }
1189  }
1190
1191  /** Creates OneOf invariants for each constant. */
1192  public void instantiate_oneof(Constant con) {
1193    assert con.val != null : "@AssumeAssertion(nullness)";
1194
1195    // @NonNull, but not marked that way to ease warning suppression.
1196    Invariant inv;
1197    PptSlice1 slice1 = (PptSlice1) ppt.get_or_instantiate_slice(con.vi);
1198
1199    // Create the correct OneOf invariant
1200    ProglangType rep_type = con.vi.rep_type;
1201    boolean is_scalar = rep_type.isScalar();
1202    if (is_scalar) {
1203      if (!OneOfScalar.dkconfig_enabled) {
1204        return;
1205      }
1206      inv = OneOfScalar.get_proto().instantiate(slice1);
1207    } else if (rep_type == ProglangType.INT_ARRAY) {
1208      if (!OneOfSequence.dkconfig_enabled) {
1209        return;
1210      }
1211      inv = OneOfSequence.get_proto().instantiate(slice1);
1212    } else if (rep_type == ProglangType.DOUBLE) {
1213      if (!OneOfFloat.dkconfig_enabled) {
1214        return;
1215      }
1216      inv = OneOfFloat.get_proto().instantiate(slice1);
1217    } else if (rep_type == ProglangType.DOUBLE_ARRAY) {
1218      if (!OneOfFloatSequence.dkconfig_enabled) {
1219        return;
1220      }
1221      inv = OneOfFloatSequence.get_proto().instantiate(slice1);
1222    } else if (rep_type == ProglangType.STRING) {
1223      if (!OneOfString.dkconfig_enabled) {
1224        return;
1225      }
1226      inv = OneOfString.get_proto().instantiate(slice1);
1227    } else if (rep_type == ProglangType.STRING_ARRAY) {
1228      if (!OneOfStringSequence.dkconfig_enabled) {
1229        return;
1230      }
1231      inv = OneOfStringSequence.get_proto().instantiate(slice1);
1232    } else {
1233      throw new Error("Unrecognized rep_type in instantiate_oneof");
1234    }
1235    assert inv != null
1236        : "@AssumeAssertion(nullness): instantiation of the given invariants always succeeds";
1237    slice1.addInvariant(inv);
1238
1239    // Add the value to it
1240    slice1.add_val_bu(con.val, ValueTuple.MODIFIED, con.count);
1241  }
1242}