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