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