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}