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}