001// ***** This file is automatically generated from PptSlice.java.jpp
002
003package daikon;
004
005import static daikon.tools.nullness.NullnessUtil.castNonNullDeep;
006
007import daikon.inv.*;
008
009import daikon.inv.binary.*;
010import daikon.inv.binary.twoScalar.*;
011import daikon.inv.binary.twoSequence.*;
012import daikon.inv.binary.twoString.*;
013import daikon.inv.binary.sequenceScalar.*;
014
015import daikon.suppress.*;
016import java.util.*;
017import java.util.logging.Level;
018import java.util.logging.Logger;
019import org.checkerframework.checker.initialization.qual.UnknownInitialization;
020import org.checkerframework.checker.interning.qual.Interned;
021import org.checkerframework.checker.lock.qual.GuardSatisfied;
022import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
023import org.checkerframework.checker.nullness.qual.NonNull;
024import org.plumelib.util.ArraysPlume;
025import org.plumelib.util.Intern;
026import typequals.prototype.qual.Prototype;
027import typequals.prototype.qual.NonPrototype;
028
029/** Contains all of the invariants over a particular set of 2 variables. */
030public final class PptSlice2 extends PptSlice {
031  // We are Serializable, so we specify a version to allow changes to
032  // method signatures without breaking serialization.  If you add or
033  // remove fields, you should change this number to the current date.
034  static final long serialVersionUID = 20040921L;
035
036  /** Debug tracer. */
037  public static final Logger debugSpecific = Logger.getLogger("daikon.PptSlice2");
038
039  public static final Logger debugMerge = Logger.getLogger("daikon.PptSlice.merge");
040
041  /** Create a new PptSlice2. The var_infos must be in varinfo_index order. */
042  public PptSlice2(PptTopLevel parent, VarInfo[] var_infos) {
043
044    super(parent, var_infos);
045    assert var_infos.length == 2;
046
047    if (debug.isLoggable(Level.FINE) || debugSpecific.isLoggable(Level.FINE)) {
048      debug.info("Created PptSlice2 " + this.name());
049    }
050    if (Debug.logOn()) {
051      Debug.log(getClass(), this, "Created");
052    }
053  }
054
055  PptSlice2(PptTopLevel parent, VarInfo var_info1, VarInfo var_info2) {
056    this(parent, new VarInfo[] {var_info1, var_info2});
057  }
058
059  @Override
060  public final int arity(
061      @UnknownInitialization(PptSlice.class) PptSlice2 this) {
062    return 2;
063  }
064  /**
065   * Creates all of the invariants that are appropriate for this slice. No invariants are
066   * created unless the variables in the slice are compatible. If the variables are compatible,
067   * invariants that match the type of the slices variables are created.
068   */
069  @Override
070  public void instantiate_invariants() {
071    instantiate_invariants(Daikon.proto_invs);
072  }
073
074  /**
075   * Creates all of the invariants that are appropriate for this slice based on the list of
076   * invariants passed in. No invariants are created unless the variables in the slice are
077   * compatible. If the variables are compatible, invariants that match the type of the slices
078   * variables are created.
079   */
080  public void instantiate_invariants(List<@Prototype Invariant> proto_invs) {
081
082    if (Debug.logOn()) {
083      log("instantiate invariants");
084    }
085
086    // Do nothing if the variables aren't compatible
087
088      boolean compatible = true;
089      VarInfo v1 = var_infos[0];
090      VarInfo v2 = var_infos[1];
091      if (v1.rep_type.isArray() == v2.rep_type.isArray()) {
092        compatible = v1.compatible(v2);
093      } else if (v1.rep_type.isArray()) {
094        compatible = v1.eltsCompatible(v2);
095      } else {
096        compatible = v2.eltsCompatible(v1);
097      }
098      if (!compatible) {
099        Debug.log(this.getClass(), this, "not created - incompatible");
100        return;
101      }
102
103    // Instantiate each invariant that is valid over those types
104    for (@Prototype Invariant proto : proto_invs) {
105
106      // Skip invariant if the types are not appropriate
107      if (!proto.valid_types(var_infos) || !proto.instantiate_ok(var_infos)) {
108        // Debug.log (proto.getClass(), this, "not created-types not valid");
109        continue;
110      }
111
112      // Skip invariant if it is suppressed.  Note that this will work
113      // even though we haven't instantiated all of the invariants for this
114      // slice yet because it will check constant values.
115      NISuppressionSet ss = proto.get_ni_suppressions();
116      if (NIS.dkconfig_enabled && (ss != null) && !ss.is_instantiate_ok(this)) {
117        if (Debug.logOn()) // avoid stringify if not logging
118          Debug.log(proto.getClass(), this, "not created - suppressed " + ss);
119        continue;
120      }
121
122      // Instantiate the invariant and add it to the list.  Null should
123      // never be returned because we check instantiate_ok() above and
124      // only enabled invariants are in this list.
125      @SuppressWarnings("nullness") // application invariant, see comment above
126      @NonNull Invariant inv = proto.instantiate(this);
127      addInvariant(inv);
128      if (Debug.logOn()) {
129        inv.log("Created invariant %s ss = %s", inv.format(), ss);
130      }
131    }
132  }
133
134  // These accessors are for abstract methods declared in Ppt
135  /** Returns the number of (non-missing) samples observed at this slice. */
136  @Override
137  public int num_samples(@UnknownInitialization @GuardSatisfied PptSlice2 this) {
138    if (parent == null || var_infos == null) { // handle not-yet-initialized slices
139      return 0;
140    }
141    // return an approximation
142
143      int num_slice_samples = parent.num_samples(var_infos[0], var_infos[1]);
144
145    return num_slice_samples;
146  }
147
148  /**
149   * Returns an upper bound on the number of distinct values observed at this slice. This is not the
150   * number of samples observed.
151   */
152  @Override
153  public int num_values() {
154    // return an approximation
155
156      int num_values = parent.num_values(var_infos[0], var_infos[1]);
157
158    return num_values;
159  }
160
161  /**
162   * This procedure accepts a sample (a ValueTuple), extracts the values from it, casts them to the
163   * proper types, and passes them along to the invariants proper. (The invariants accept typed
164   * values rather than a ValueTuple that encapsulates objects of any type whatever.)
165   */
166  @Override
167  public List<Invariant> add(ValueTuple full_vt, int count) {
168
169    assert invs.size() > 0;
170    for (Invariant inv : invs) {
171      assert inv != null;
172    }
173
174      VarInfo vi1 = var_infos[0];
175      VarInfo vi2 = var_infos[1];
176
177    // If any var has encountered out of array bounds values,
178    // stop all invariants in this slice.  The presumption here is that
179    // an index out of bounds implies that the derived variable (eg a[i])
180    // doesn't really make any sense (essentially that i is not a valid
181    // index for a).  Invariants on the derived variable are thus not
182    // relevant
183    for (int i = 0; i < var_infos.length; i++) {
184      if (var_infos[i].missingOutOfBounds()) {
185        List<Invariant> result = new ArrayList<>();
186        for (Invariant inv : invs) {
187          if (PrintInvariants.print_discarded_invariants) {
188            DiscReasonMap.put(
189                  inv,
190                  DiscardCode.bad_sample,
191                  var_infos[i].name() + " array index was out of bounds");
192          }
193          inv.falsify();
194          result.add(inv);
195          if (Invariant.logOn()) {
196            inv.log("destroyed because %s array index out of bounds", var_infos[i].name());
197          }
198        }
199        if (VarInfo.debugMissing.isLoggable(Level.FINE)) {
200          VarInfo.debugMissing.fine(
201              "Removing slice "
202                  + this
203                  + " because var "
204                  + var_infos[i].name()
205                  + " array index out of bounds");
206        }
207        return result;
208      }
209    }
210
211    int mod1 = full_vt.getModified(vi1);
212    if (mod1 == ValueTuple.MISSING_FLOW || mod1 == ValueTuple.MISSING_NONSENSICAL) {
213      return emptyInvList;
214    }
215
216    if (mod1 == ValueTuple.STATIC_CONSTANT) {
217      assert vi1.is_static_constant;
218      mod1 = ((num_samples() == 0) ? ValueTuple.MODIFIED : ValueTuple.UNMODIFIED);
219    }
220
221      int mod2 = full_vt.getModified(vi2);
222      if (mod2 == ValueTuple.MISSING_FLOW || mod2 == ValueTuple.MISSING_NONSENSICAL) {
223        return emptyInvList;
224      }
225      if (mod2 == ValueTuple.STATIC_CONSTANT) {
226        assert vi2.is_static_constant;
227        mod2 = ((num_samples() == 0)
228                ? ValueTuple.MODIFIED : ValueTuple.UNMODIFIED);
229      }
230
231    Object val1 = full_vt.getValue(vi1);
232    assert Intern.isInterned(val1) : "obj " + val1 + " class " + val1.getClass();
233
234      Object val2 = full_vt.getValue(vi2);
235      assert Intern.isInterned(val2);
236
237    // Supply the new values to all the invariant objects.
238    assert (mod1 == vi1.getModified(full_vt))
239                  || ((vi1.getModified(full_vt) == ValueTuple.STATIC_CONSTANT)
240            && ((mod1 == ValueTuple.UNMODIFIED) || (mod1 == ValueTuple.MODIFIED)));
241
242      List<Invariant> weakened_invs = add_val_bu(val1, val2, mod1, mod2, count);
243
244    return weakened_invs;
245  }
246
247  public List<Invariant> add_val_bu(@Interned Object val1, @Interned Object val2,
248                                        int mod1, int mod2, int count) {
249
250    assert (mod1 != ValueTuple.MISSING_FLOW
251                       && mod1 != ValueTuple.MISSING_NONSENSICAL)
252                       && (mod2 != ValueTuple.MISSING_FLOW
253                       && mod2 != ValueTuple.MISSING_NONSENSICAL);
254
255    List<Invariant> result = new ArrayList<>();
256    boolean array1 = var_infos[0].rep_type.isArray();
257    boolean array2 = var_infos[1].rep_type.isArray();
258    if (array2 && ! array1) {
259      for (Invariant invariant : invs) {
260        BinaryInvariant inv = (BinaryInvariant) invariant;
261        if (inv.is_false()) {
262          continue;
263        }
264        InvariantStatus status = inv.add(val2, val1, mod1, count);
265        if (status == InvariantStatus.FALSIFIED) {
266          inv.falsify();
267          result.add(inv);
268        } else if (status == InvariantStatus.WEAKENED) {
269          result.add(inv);
270        }
271        if (PrintInvariants.print_discarded_invariants && inv.is_false()) {
272          DiscReasonMap.put(inv, DiscardCode.bad_sample,
273                            "Falsified from sample: "
274                            + var_infos[0].name()
275                            + " = " + val1 + "," + var_infos[1].name()
276                            + " = " + Debug.toString(val2));
277        }
278      }
279    } else {
280      for (Invariant invariant : invs) {
281        BinaryInvariant inv = (BinaryInvariant) invariant;
282        if (inv.is_false()) {
283          continue;
284        }
285        InvariantStatus status = inv.add(val1, val2, mod1, count);
286        if (status == InvariantStatus.FALSIFIED) {
287          inv.falsify();
288          result.add(inv);
289        } else if (status == InvariantStatus.WEAKENED) {
290          result.add(inv);
291        }
292        if (PrintInvariants.print_discarded_invariants && inv.is_false()) {
293          if (array1
294              && array2
295              && !( (inv instanceof SeqSeqIntEqual)
296                    || (inv instanceof SeqSeqFloatEqual)
297                    || (inv instanceof SeqSeqStringEqual) )
298              && ArraysPlume.length(val1) != ArraysPlume.length(val2)) {
299            // No need to print out two potentially huge arrays if the
300            // reason for discard was because of different array lengths
301            DiscReasonMap.put(inv, DiscardCode.bad_sample,
302                                "Samples seen with different array lengths");
303          } else {
304            DiscReasonMap.put(inv, DiscardCode.bad_sample,
305                    "Falsified from sample: " + var_infos[0].name()
306                    + " = " + Debug.toString(val1)
307                    + "," + var_infos[1].name()
308                    + " = " + Debug.toString(val2));
309          }
310        }
311      }
312    }
313    return result;
314  }
315
316  @Override
317  public void addInvariant(Invariant invariant) {
318    assert invariant != null;
319    // assert invariant.ppt == this;
320
321    if (Debug.dkconfig_internal_check) {
322      // Don't add a check here to ensure that the invairant is not suppressed
323      // in some cases (see NIS.apply_samples), a suppressed invariant is
324      // added and them later removed when the suppression is found
325      // if (invariant.is_ni_suppressed()) {
326        // System.out.printf("suppressed invariant %s is being added to ppt %s "
327        //                    + "with %d samples%n", invariant.format(), this,
328        //                    this.num_samples());
329        // NISuppressionSet ss = invariant.get_ni_suppressions();
330        // ss.suppressed (invariant.ppt);
331        // System.out.printf("suppression = %s%n", ss);
332        // throw new Error();
333     // }
334    }
335
336    invs.add(invariant);
337    Global.instantiated_invariants++;
338    if (Invariant.logOn()) {
339      invariant.log("Instantiated %s", invariant.format());
340    }
341  }
342
343  /**
344   * Copy invariants from this slice to a new slice over the variables argNewVarInfos. The new slice
345   * should not already exist.
346   */
347  @Override
348  protected PptSlice cloneAndPivot(VarInfo[] argNewVarInfos) {
349
350    // Sort the VarInfos by var_index and build a matching permutation
351    // from the current order to the new order
352    VarInfo[] vis_sorted = argNewVarInfos.clone();
353    Arrays.sort(vis_sorted, VarInfo.IndexComparator.getInstance());
354    int[] permutation = PptTopLevel.build_permute(argNewVarInfos, vis_sorted);
355
356    // Assert sorted
357    for (int i = 0; i < 2 - 1; i++) {
358      assert vis_sorted[i].varinfo_index <= vis_sorted[i + 1].varinfo_index;
359    }
360
361    assert ArraysPlume.fnIsPermutation(permutation);
362    // Assert that the permutation represents the rearrangement
363    for (int i = 0; i < 2; i++) {
364      // the variable that used to be at position "i" is now found at
365      // position permutation[i].
366      VarInfo oldvi = argNewVarInfos[i];
367      VarInfo newvi = vis_sorted[permutation[i]];
368      assert oldvi == newvi;
369    }
370
371    // The new slice should not already exist.
372    assert parent.findSlice(vis_sorted) == null;
373
374    // Why not just clone?  Because then index order wouldn't be
375    // preserved
376    PptSlice2 result = new PptSlice2(this.parent, vis_sorted);
377
378    // re-parent the invariants and copy them out.
379    List<Invariant> newInvs = new ArrayList<>();
380    for (Invariant inv : invs) {
381      assert inv.ppt == this;
382      Invariant newInv = inv.transfer(result, permutation);
383      newInvs.add(newInv);
384      assert newInv != inv;
385      assert newInv.ppt == result;
386      assert inv.ppt == this;
387    }
388
389    if (Debug.logOn()) {
390      result.log(
391          "Copied "
392              + newInvs.size()
393              + " invariants from "
394              + this.name()
395              + " with "
396              + invs.size()
397              + " invariants");
398    }
399    result.invs.addAll(newInvs);
400    if (PptSliceEquality.debug.isLoggable(Level.FINE)) {
401      PptSliceEquality.debug.fine("cloneAndPivot: newInvs " + invs);
402    }
403    return result;
404  }
405
406  /**
407   * Creates invariants at this ppt by merging invariants from each of its children. An invariant
408   * must exist at each of the children in order for it to be created here (at the parent).
409   * Additionally, some invariants have state information that must be merged. This is done by the
410   * invariant itself.
411   *
412   * <p>The basic steps are:
413   *
414   * <ol>
415   *   <li>Find all of the child invariants. These are the invariants in the matching slice of each
416   *       child.
417   *   <li>For each invariant class, build a list of all of the invariants of that class. Note that
418   *       some invariant classes (eg, functionBinary) contain distinct invariants, each of which
419   *       must be merged separately. See Invariant.Match for more information concerning what makes
420   *       an invariant the 'same'
421   *   <li>Each invariant that is found at each of the children is then merged to possibly create a
422   *       parent invariant.
423   * </ol>
424   */
425  public void merge_invariants() {
426
427    if (debugMerge.isLoggable(Level.FINE)) {
428      debugMerge.fine("merging invs for " + name());
429    }
430
431    // List of all invariants found over all of the children
432    List<Invariant> all_invs = new ArrayList<>();
433
434    // Keep count of the number of valid children processed.  An invariant
435    // must be found at each valid child in order to exist at the parent.
436    // A valid child is one that has received samples and has a corresponding
437    // variable for each parent variable
438    int valid_child_count = 0;
439
440    // Loop through all of the children of our top level parent
441    child_loop:
442    for (PptRelation rel : parent.children) {
443      PptTopLevel ppt = rel.child;
444
445      // Skip any children that have not seen any samples
446      if (ppt.num_samples() == 0) {
447        if (debugMerge.isLoggable(Level.FINE)) {
448          debugMerge.fine("-- slice ignored (no samples) " + ppt.name());
449        }
450        continue;
451      }
452
453      // Child variable info
454      /*NNC:@MonotonicNonNull*/ VarInfo[] cvis = new VarInfo[var_infos.length];
455      /*NNC:@MonotonicNonNull*/ VarInfo[] cvis_sorted = new VarInfo[var_infos.length];
456
457      // Build the corresponding array of VarInfos for the child.  If any
458      // of the vars don't exist in this child, skip the child (since we
459      // won't have data for each variable).
460      for (int j = 0; j < var_infos.length; j++) {
461        VarInfo pv = var_infos[j];
462        VarInfo cv = rel.childVar(pv);
463        if (cv == null) {
464          continue child_loop;
465        }
466        cvis[j] = cv.canonicalRep();
467        cvis_sorted[j] = cv.canonicalRep();
468      }
469
470      cvis = castNonNullDeep(cvis); // https://tinyurl.com/cfissue/986
471      cvis_sorted = castNonNullDeep(cvis_sorted); // https://tinyurl.com/cfissue/986
472
473      // If any of the child variables have always been missing, this
474      // particular slice in the child received no samples.  If dynamic
475      // constants are enabled, the slice will have never been created.
476      // These slices can be skipped unless they contain a missing out of
477      // bound var.  Out of bounds variables destroy all invariants in
478      // the slice (since the variable is deemed to be nonsensical)
479      if (slice_missing(ppt, cvis)) {
480        if (debugMerge.isLoggable(Level.FINE)) {
481          debugMerge.fine(
482              "-- slice ignored (missing) " + ppt.name() + " vars " + Arrays.toString(cvis_sorted));
483        }
484        continue;
485      }
486
487      // The child variables must be sorted by their index (in the child)
488      Arrays.sort(cvis_sorted, VarInfo.IndexComparator.getInstance());
489
490      // Keep track of the number of valid children
491      valid_child_count++;
492
493      // Find the corresponding slice.  If the slice does not exist or
494      // has no invariants, there can be no invariants to merge (since
495      // invariants must exist at each child to exist at the parent)
496      PptSlice2 cslice = (PptSlice2) ppt.findSlice(cvis_sorted);
497      if ((cslice == null) || (cslice.invs.size() == 0)) {
498        if (Debug.logOn()) {
499          this.log(
500              "slice not found "
501                  + ppt.name()
502                  + " "
503                  + Arrays.toString(cvis_sorted)
504                  + " num_samples= "
505                  + ppt.num_samples()
506                  + " ppt.constants = "
507                  + ppt.constants);
508        }
509        if (debugMerge.isLoggable(Level.FINE)) {
510          debugMerge.fine(
511              "-- slice not found " + ppt.name() + " vars " + Arrays.toString(cvis_sorted));
512        }
513        return;
514      }
515
516      // // Update sample count info
517      // mod_samples += cslice.mod_samples;
518      // unmod_samples += cslice.unmod_samples;
519
520      // Build the permutation array from child to parent slice
521      int[] permute = PptTopLevel.build_permute(cvis_sorted, cvis);
522
523      // Debug print child vars and permute to parent
524      if (debugMerge.isLoggable(Level.FINE)) {
525        debugMerge.fine("-- Processing child " + ppt.name() + " (" + rel.getRelationType() + ")");
526        debugMerge.fine("-- -- child vars = " + Arrays.toString(cvis_sorted));
527        debugMerge.fine("-- -- parent vars = " + Arrays.toString(var_infos));
528        debugMerge.fine("-- -- permute = " + Arrays.toString(permute));
529      }
530
531      // Add each invariant (permuted to match the parent varinfos)
532      // to our list of invariants.
533      for (Invariant orig_inv : cslice.invs) {
534        Invariant inv = orig_inv.clone_and_permute(permute);
535        all_invs.add(inv);
536        if (Invariant.logOn()) {
537          /*NNC:@MonotonicNonNull*/ VarInfo[] child_vars = new VarInfo[var_infos.length];
538          for (int k = 0; k < var_infos.length; k++) {
539            VarInfo pv = var_infos[k];
540            @SuppressWarnings("nullness")
541            @NonNull VarInfo cv = rel.childVar(pv);
542            assert cv != null;
543            child_vars[k] = cv.canonicalRep();
544          }
545          child_vars = castNonNullDeep(child_vars); // https://tinyurl.com/cfissue/986
546          orig_inv.log("org inv");
547          inv.log(
548              "Created %s from %s using permute %s cvis_sorted = %s cvis = %s for ppt %s",
549              inv,
550              orig_inv,
551              Arrays.toString(permute),
552              Arrays.toString(cvis_sorted),
553              Arrays.toString(child_vars),
554              parent.name());
555        }
556      }
557    }
558
559    log("Found " + all_invs.size() + " invariants to merge");
560    if (debugMerge.isLoggable(Level.FINE) && (valid_child_count == 0)) {
561      debugMerge.fine("-- No valid children found");
562    }
563
564    // For each invariant found, find the list of invariants of the
565    // same type (type corresponds basically but not exactly to the
566    // invariants class) and add the invariant to that list.
567    // Invariant.Match.equals() defines if two invariants are of the
568    // same 'type' for the purpose of merging invariants.
569    Map<Invariant.Match, List<Invariant>> inv_map =
570        new LinkedHashMap<>();
571    for (Invariant inv : all_invs) {
572      Invariant.Match imatch = new Invariant.Match(inv);
573      List<Invariant> invs = inv_map.get(imatch);
574      if (Invariant.logOn()) {
575        inv.log("Adding %s to %s invs list %s", inv.format(), name(), invs);
576      }
577      if (invs == null) {
578        invs = new ArrayList<Invariant>();
579        inv_map.put(imatch, invs);
580      }
581      invs.add(inv);
582    }
583
584    // Attempt to create a parent invariant for each invariant that
585    // appeared at each valid child.  Note that some invariants will
586    // not exist at the parent even if they exist at each child (eg,
587    // LinearBinary)
588    for (List<Invariant> child_invs : inv_map.values()) {
589      if (child_invs.size() > valid_child_count) {
590        // this shouldn't happen
591        System.out.println(
592           "Found "
593               + child_invs.size()
594               + " invariants at "
595               + name()
596               + " ("
597               + valid_child_count
598               + " children)");
599        for (Invariant child_inv : child_invs) {
600          System.out.printf(
601              "-- Invariant = '%s' [%s] @%s%n",
602              child_inv.repr(), child_inv.getClass(), child_inv.ppt);
603        }
604        assert child_invs.size() <= valid_child_count;
605      }
606      if (child_invs.size() == valid_child_count) {
607        Invariant first = child_invs.get(0);
608        if (Debug.logOn()) {
609          first.log("Attempting merge of %s invariants into ppt %s", child_invs.size(), name());
610        }
611        Invariant parent_inv = first.merge(child_invs, this);
612        if (parent_inv != null) {
613          invs.add(parent_inv);
614          if (Debug.logOn()) {
615            parent_inv.log("Merge successful of %s into %s", parent_inv.format(), name());
616          }
617        }
618      } else {
619        if (Debug.logOn()) {
620          Invariant inv = child_invs.get(0);
621          inv.log(
622              "Not merging invariant into %s, Found %s child invariants in %s children",
623              name(), child_invs.size(), valid_child_count);
624        }
625      }
626    }
627  }
628
629  /**
630   * Returns whether or not the slice is missing due to having one or more of its variables always
631   * missing. This returns true only for missing flow and/or missing nonsensical. Out of Bounds is
632   * treated differently since it destroys all of its invariants.
633   */
634  private boolean slice_missing(PptTopLevel ppt, VarInfo[] vis) {
635
636    if (ppt.constants != null) {
637
638        if ((ppt.constants.is_missing(vis[0]) || ppt.constants.is_missing(vis[1]))
639            && !vis[0].missingOutOfBounds()
640            && !vis[1].missingOutOfBounds()) {
641          return true;
642        }
643
644    }
645    return false;
646  }
647}