001package daikon; 002 003import daikon.inv.Invariant; 004import daikon.inv.InvariantStatus; 005import daikon.inv.binary.twoScalar.LinearBinary; 006import daikon.inv.binary.twoScalar.LinearBinaryFloat; 007import daikon.inv.ternary.threeScalar.LinearTernary; 008import daikon.inv.ternary.threeScalar.LinearTernaryFloat; 009import daikon.inv.unary.scalar.OneOfFloat; 010import daikon.inv.unary.scalar.OneOfScalar; 011import daikon.inv.unary.sequence.OneOfFloatSequence; 012import daikon.inv.unary.sequence.OneOfSequence; 013import daikon.inv.unary.string.OneOfString; 014import daikon.inv.unary.stringsequence.OneOfStringSequence; 015import daikon.suppress.NIS; 016import java.io.PrintWriter; 017import java.io.Serializable; 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Comparator; 021import java.util.Iterator; 022import java.util.LinkedHashSet; 023import java.util.List; 024import java.util.Set; 025import java.util.logging.Level; 026import java.util.logging.Logger; 027import org.checkerframework.checker.interning.qual.Interned; 028import org.checkerframework.checker.lock.qual.GuardSatisfied; 029import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 030import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 031import org.checkerframework.checker.nullness.qual.NonNull; 032import org.checkerframework.checker.nullness.qual.Nullable; 033import org.checkerframework.checker.nullness.qual.RequiresNonNull; 034import org.checkerframework.dataflow.qual.Pure; 035import org.checkerframework.dataflow.qual.SideEffectFree; 036 037/** 038 * Class that implements dynamic constants optimization. This optimization doesn't instantiate 039 * invariants over constant variables (i.e., that that have only seen one value). When the variable 040 * receives a second value, invariants are instantiated and are given the sample representing the 041 * previous constant value. Each DynamicConstants object is associated with a single program point, 042 * ppt. 043 */ 044public class DynamicConstants implements Serializable { 045 046 static final long serialVersionUID = 20040401L; 047 048 // If true don't create any invariants (including OneOfs) over dynamic 049 // constants during post processing. Normally, the configuration 050 // variable OneOf_only is more appropriate. 051 static final boolean no_post_process = false; 052 053 // Variables starting with dkconfig_ should only be set via the 054 // daikon.config.Configuration interface. 055 056 /** 057 * Whether to use the dynamic constants optimization. This optimization doesn't instantiate 058 * invariants over constant variables (i.e., that that have only seen one value). When the 059 * variable receives a second value, invariants are instantiated and are given the sample 060 * representing the previous constant value. 061 */ 062 public static boolean dkconfig_use_dynamic_constant_optimization = true; 063 064 /** 065 * Boolean. Controls which invariants are created for variables that are constant for the entire 066 * run. If true, create only OneOf invariants. If false, create all possible invariants. 067 * 068 * <p>Note that setting this to true only fails to create invariants between constants. Invariants 069 * between constants and non-constants are created regardless. 070 * 071 * <p>A problem occurs with merging when this is turned on. If a var_info is constant at one child 072 * slice, but not constant at the other child slice, interesting invariants may not be merged 073 * because they won't exist on the slice with the constant. This is thus currently defaulted to 074 * false. 075 */ 076 public static boolean dkconfig_OneOf_only = false; 077 078 /** Debug tracer. */ 079 public static final Logger debug = Logger.getLogger("daikon.DynamicConstants"); 080 081 /** 082 * List of dynamic constants. 083 * 084 * <p>Each element, c, has c.constant = true, c.count > 0, elt.val != null. 085 */ 086 @SuppressWarnings("serial") 087 List<Constant> con_list = new ArrayList<>(); 088 089 /** 090 * List of variables that have always been missing. 091 * 092 * <p>For each element c, c.always_missing = true or con.vi.missingOutOfBounds(). 093 */ 094 @SuppressWarnings("serial") 095 List<Constant> missing_list = new ArrayList<>(); 096 097 // Same contents in both. Why two data structures? 098 /** Array of all variables. Some may be non-constant. */ 099 Constant[] all_vars; 100 101 // Same contents in both. Why two data structures? 102 /** List of all variables. Some may be non-constant. */ 103 @SuppressWarnings("serial") 104 List<Constant> all_list = new ArrayList<>(); 105 106 /** Program point of these constants. */ 107 PptTopLevel ppt; 108 109 /** Number of samples received. */ 110 int sample_cnt = 0; 111 112 /** 113 * Class used to indicate, for each variable, whether it is constant (see boolean field 114 * "constant"). If it is, then the class also stores its constant value and its sample count. 115 * 116 * <p>Note that two objects of this class are equal if they refer to the same variable. This 117 * allows these to be stored in sets. 118 */ 119 public static @Interned class Constant implements Serializable { 120 121 // We are Serializable, so we specify a version to allow changes to 122 // method signatures without breaking serialization. If you add or 123 // remove fields, you should change this number to the current date. 124 static final long serialVersionUID = 20030913L; 125 126 // XXX Question: what if the constant value is itself null, as for a 127 // String or pointer? Does the code distinguish that case from val not 128 // being set? 129 /** 130 * The value of the constant, or the previous constant value if constant==false and 131 * previously_constant==true. Null iff count=0. 132 */ 133 @SuppressWarnings("serial") 134 public @MonotonicNonNull @Interned Object val = null; 135 136 /** The sample count of the constant. */ 137 public int count = 0; 138 139 /** The variable that has this value. */ 140 public VarInfo vi; 141 142 /** Whether or not this has been missing for every sample to date. */ 143 boolean always_missing = true; 144 145 /** Whether or not this is constant. */ 146 boolean constant = false; 147 148 /** 149 * Whether or not this was constant at the beginning of this sample. At the beginning of the 150 * add() method, all newly non-constant variables are marked (constant=false). It is sometimes 151 * useful within the remainder of processing that sample to know that a variable was constant at 152 * the beginning. The field previously_constant is set to true when constant is set to false, 153 * and then is itself set to false at the end of the add() method. 154 */ 155 boolean previously_constant = false; 156 157 /** 158 * Whether or not this was always missing at the beginning of this sample. At the beginning of 159 * the add() method, all newly non missing variables are marked (always_missing=false). It is 160 * sometimes useful within the remainder of processing that sample to know that a variable was 161 * missing at the beginning. The field previous_missing set to true when missing is set to 162 * false, and then is itself set to false at the end of the add() method. 163 */ 164 boolean previous_missing = false; 165 166 /** Check representation invariant. */ 167 public void checkRep() { 168 // This assertion is not valid. If first sample is missing, then 169 // always_missing=true and previous_missing=false. 170 // assert (always_missing ? previous_missing : true) : toString(); 171 172 assert !(constant && previously_constant) : toString(); 173 174 // Whereas values can be null, null is never the value for a dynamic 175 // constant. 176 assert ((constant || previously_constant) 177 ? (val != null && count > 0) 178 : (val == null && count == 0)) 179 : toString(); 180 } 181 182 /** 183 * Creates a new Constant, indicating whether the given variable is a constant. 184 * 185 * @param vi the variable that might be a constant 186 */ 187 @SuppressWarnings("super.invocation") 188 public Constant(VarInfo vi) { 189 this.vi = vi; 190 } 191 192 /** 193 * Returns whether the specified variable is currently a constant OR was a constant at the 194 * beginning of constants processing. 195 */ 196 @Pure 197 public boolean is_prev_constant() { 198 return constant || previously_constant; 199 } 200 201 @EnsuresNonNullIf(result = true, expression = "#1") 202 @Pure 203 @Override 204 public boolean equals(@GuardSatisfied Constant this, @GuardSatisfied @Nullable Object obj) { 205 if (!(obj instanceof Constant)) { 206 return false; 207 } 208 Constant c = (Constant) obj; 209 return c.vi == vi; 210 } 211 212 @Pure 213 @Override 214 public int hashCode(@GuardSatisfied Constant this) { 215 return vi.hashCode(); 216 } 217 218 @Override 219 @SuppressWarnings("all:purity") // side effects to local state (string creation) 220 @SideEffectFree 221 public String toString(@GuardSatisfied Constant this) { 222 223 StringBuilder out = new StringBuilder(); 224 out.append(vi.name()); 225 if (val == null) { 226 out.append(" (val missing)"); 227 } else { 228 out.append(" (val=" + val + ")"); 229 } 230 if (vi.isCanonical()) { 231 out.append(" (leader) "); 232 } 233 out.append( 234 " [always_missing=" 235 + always_missing 236 + ", constant=" 237 + constant 238 + ", previously_constant=" 239 + previously_constant 240 + ", previous_missing=" 241 + previous_missing 242 + "]"); 243 return out.toString(); 244 } 245 } 246 247 /** Compares two constants based on the vi_index of their variable. */ 248 public static final class ConIndexComparator implements Comparator<Constant>, Serializable { 249 // We are Serializable, so we specify a version to allow changes to 250 // method signatures without breaking serialization. If you add or 251 // remove fields, you should change this number to the current date. 252 static final long serialVersionUID = 20050923L; 253 254 private ConIndexComparator() {} 255 256 @Pure 257 @Override 258 public int compare(Constant con1, Constant con2) { 259 return con1.vi.varinfo_index - con2.vi.varinfo_index; 260 } 261 262 public static ConIndexComparator getInstance() { 263 return theInstance; 264 } 265 266 static final ConIndexComparator theInstance = new ConIndexComparator(); 267 } 268 269 /** Create an initial list of constants and missing variables for the specified ppt. */ 270 public DynamicConstants(PptTopLevel ppt) { 271 272 this.ppt = ppt; 273 274 // Start everything off as missing (since we haven't seen any values yet) 275 for (VarInfo vi : ppt.var_infos) { 276 @SuppressWarnings("interning") // one Constant per VarInfo 277 @Interned Constant c = new Constant(vi); 278 all_list.add(c); 279 missing_list.add(c); 280 } 281 all_vars = all_list.toArray(new Constant[0]); 282 } 283 284 /** 285 * Checks each current constant to see if it is still a constant. Constants must have the same 286 * value and cannot be missing. In the long run a better job of dealing with missing might be 287 * helpful. Also checks each variable that has always been missing to date to insure that it is 288 * still missing. 289 * 290 * <p>Creates all new views required for the newly non constants (noncons) and the newly 291 * non-missing (non_missing). 292 */ 293 public void add(ValueTuple vt, int count) { 294 295 // System.out.println("DynamicConstants.add : " + vt.toString(ppt.var_infos)); 296 297 List<Constant> non_missing = new ArrayList<>(); 298 List<Constant> non_con = new ArrayList<>(); 299 300 // Check each constant, destroy any that are missing or different 301 for (Iterator<Constant> i = con_list.iterator(); i.hasNext(); ) { 302 Constant con = i.next(); 303 assert con.constant; 304 con.checkRep(); 305 306 if (Debug.logDetail()) { 307 Debug.log( 308 getClass(), 309 ppt, 310 Debug.vis(con.vi), 311 "Adding " 312 + Debug.toString(con.vi.getValueOrNull(vt)) 313 + " to constant " 314 + con.val 315 + " : missing = " 316 + missing(con.vi, vt) 317 + ": samples = " 318 + con.count 319 + "/" 320 + count); 321 } 322 if (missing(con.vi, vt) || (con.val != con.vi.getValue(vt))) { 323 i.remove(); 324 con.constant = false; 325 con.previously_constant = true; 326 assert all_vars[con.vi.varinfo_index].constant == false; 327 non_con.add(con); 328 } else { 329 con.count += count; 330 } 331 con.checkRep(); 332 } 333 334 // Move any non-missing variables to the constant list and init their val. 335 // If a variable is missing out of bounds, leave it on this list 336 // forever (guaranteeing that invariants will never be instantiated over 337 // it). 338 for (Iterator<Constant> i = missing_list.iterator(); i.hasNext(); ) { 339 Constant con = i.next(); 340 con.checkRep(); 341 if (con.vi.missingOutOfBounds()) { 342 continue; 343 } 344 345 if (missing(con.vi, vt)) { 346 // value is still missing, nothing to do (we incremented its count above) 347 continue; 348 } 349 350 @Interned Object val = con.vi.getValue(vt); 351 // the variable is not missing, so it is non-null 352 assert val != null; 353 354 i.remove(); 355 con.always_missing = false; 356 if (Debug.logDetail()) { 357 Debug.log( 358 getClass(), 359 ppt, 360 Debug.vis(con.vi), 361 "Adding " 362 + Debug.toString(val) 363 + " to missing : missing = " 364 + missing(con.vi, vt) 365 + ": samples = " 366 + con.count 367 + "/" 368 + count 369 + "/" 370 + sample_cnt); 371 } 372 // Is the Constant missing because it was initialized that way, or 373 // has the program point seen values in the past? 374 if (sample_cnt == 0) { 375 // First sample for this program point (& this variable) 376 con.val = val; 377 con.count = count; 378 con.constant = true; 379 con_list.add(con); 380 } else { 381 // This variable truly is missing; has seen a missing value in the past. 382 non_missing.add(con); 383 con.previous_missing = true; 384 } 385 } 386 387 sample_cnt += count; 388 389 // Create slices over newly non-constant and non-missing variables 390 instantiate_new_views(non_con, non_missing); 391 392 // Turn off previously_constant on all newly non-constants 393 for (Constant con : non_con) { 394 con.previously_constant = false; 395 @SuppressWarnings("nullness") // reinitialization 396 @NonNull Object nullValue = null; 397 con.val = nullValue; 398 con.count = 0; 399 con.checkRep(); 400 } 401 402 // Turn off previous_missing on all newly non-missing 403 for (Constant con : non_missing) { 404 con.previous_missing = false; 405 con.checkRep(); 406 } 407 } 408 409 /** Returns whether the specified variable is missing in this ValueTuple. */ 410 private boolean missing(VarInfo vi, ValueTuple vt) { 411 412 int mod = vt.getModified(vi); 413 return (mod == ValueTuple.MISSING_FLOW) || (mod == ValueTuple.MISSING_NONSENSICAL); 414 } 415 416 /** Returns the Constant for the specified variable. */ 417 @Pure 418 public Constant getConstant(VarInfo vi) { 419 420 Constant result = all_vars[vi.varinfo_index]; 421 result.checkRep(); 422 return result; 423 } 424 425 /** Returns whether the specified variable is currently a constant. */ 426 @Pure 427 public boolean is_constant(VarInfo vi) { 428 429 return getConstant(vi).constant; 430 } 431 432 /** 433 * Returns whether the specified variable is currently a constant OR was a constant at the 434 * beginning of constants processing. 435 */ 436 @Pure 437 public boolean is_prev_constant(VarInfo vi) { 438 439 return getConstant(vi).is_prev_constant(); 440 } 441 442 /** 443 * Returns the constant value of the specified variable, or null if the variable is not constant 444 * or prev_constant. But, it is apparently only called on constants with a value. 445 */ 446 public @Interned Object constant_value(VarInfo vi) { 447 448 @SuppressWarnings("nullness") // non-missing value, so non-null val field 449 @NonNull Object result = getConstant(vi).val; 450 return result; 451 } 452 453 /** Returns whether the specified variable missing for all values so far. */ 454 @Pure 455 public boolean is_missing(VarInfo vi) { 456 457 return getConstant(vi).always_missing; 458 } 459 460 /** 461 * Returns whether the specified variable is currently missing OR was missing at the beginning of 462 * constants processing. 463 */ 464 @Pure 465 public boolean is_prev_missing(VarInfo vi) { 466 467 Constant c = all_vars[vi.varinfo_index]; 468 return c.always_missing || c.previous_missing; 469 } 470 471 /** Returns the number of constants that are leaders. */ 472 public int constant_leader_cnt() { 473 474 int con_cnt = 0; 475 for (Constant con : con_list) { 476 if (con.vi.isCanonical()) { 477 con_cnt++; 478 } 479 } 480 481 return con_cnt; 482 } 483 484 /** 485 * Creates all new views required for the newly non constants (noncons) and the newly non-missing 486 * (non_missing). 487 */ 488 public void instantiate_new_views(List<Constant> noncons, List<Constant> non_missing) { 489 490 if (Debug.logOn()) { 491 for (Constant con : noncons) { 492 Debug.log( 493 getClass(), 494 ppt, 495 Debug.vis(con.vi), 496 "is non constant" 497 + " with val = " 498 + Debug.toString(con.val) 499 + " with count = " 500 + con.count); 501 } 502 for (Constant con : non_missing) { 503 Debug.log(getClass(), ppt, Debug.vis(con.vi), "is non missing"); 504 } 505 } 506 507 for (Constant con : noncons) { 508 con.checkRep(); 509 } 510 for (Constant con : non_missing) { 511 con.checkRep(); 512 } 513 514 // Create all of the views over noncons and noncons+con_list. 515 // Since everything starts out as a constant, it is only necessary 516 // to combine the newly non-constants with a combination of 517 // the remaining constants and the newly-non constants. Any slices 518 // between the non-constants and other variables will have already 519 // been created when those other variables became non-constants. 520 if (noncons.size() > 0) { 521 List<Constant> cons = new ArrayList<>(); 522 cons.addAll(con_list); 523 cons.addAll(noncons); 524 debug.fine("Instantiating non constants in ppt: " + ppt.name()); 525 instantiate_views(noncons, cons); 526 } 527 528 // Create all views over the newly non-missing. Since missing 529 // vars were not included in any previous views, we must match them 530 // against all variables. 531 if (non_missing.size() > 0) { 532 debug.fine("Instantiating non missing in ppt: " + ppt.name()); 533 instantiate_views(non_missing, all_list); 534 } 535 536 // Create any ternary invariants that are suppressed when one 537 // of the variables is a constant. Currently, only LinearTernary 538 // falls into this list (It is suppressed by (x = C) && (Ay + Bz = D)) 539 if (NIS.dkconfig_enabled) { 540 instantiate_constant_suppressions(noncons, all_list); 541 } 542 } 543 544 /** 545 * Instantiate views and invariants across each combination of vars from list1 and list2. If each 546 * item in a new slice was a constant, the constant values are applied. 547 * 548 * <p>The following slices will be created: 549 * 550 * <pre> 551 * unary: list1-vars 552 * binary: list1-vars X list2-vars 553 * ternary: list1-vars X list2-vars X list2-vars 554 * </pre> 555 */ 556 private void instantiate_views(List<Constant> list1, List<Constant> list2) { 557 558 // Get list1 leaders 559 Set<Constant> leaders1 = new LinkedHashSet<>(); 560 for (Constant con : list1) { 561 if (con.vi.isCanonical()) { 562 leaders1.add(con); 563 } 564 } 565 566 // Get list2 leaders 567 Set<Constant> leaders2 = new LinkedHashSet<>(); 568 for (Constant con : list2) { 569 if (con.vi.isCanonical()) { 570 leaders2.add(con); 571 } 572 } 573 574 if (debug.isLoggable(Level.FINE)) { 575 debug.fine("instantiating over " + leaders1.size() + " leaders1: " + leaders1); 576 debug.fine("instantiating over " + leaders2.size() + " leaders2: " + leaders2); 577 } 578 579 // any new views created 580 List<PptSlice> new_views = new ArrayList<>(); 581 582 int mod = ValueTuple.MODIFIED; 583 584 // Unary slices/invariants 585 for (Constant con : leaders1) { 586 if (Debug.logOn()) { 587 Debug.log(getClass(), ppt, Debug.vis(con.vi), "Considering slice"); 588 } 589 if (!ppt.is_slice_ok(con.vi)) { 590 continue; 591 } 592 PptSlice1 slice1 = new PptSlice1(ppt, con.vi); 593 slice1.instantiate_invariants(); 594 if (Debug.logOn()) { 595 Debug.log(getClass(), ppt, Debug.vis(con.vi), "Instantiated invs"); 596 } 597 if (con.count > 0) { 598 assert con.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 599 slice1.add_val_bu(con.val, mod, con.count); 600 } 601 new_views.add(slice1); 602 } 603 604 // Binary slices/invariants. 605 for (Constant con1 : leaders1) { 606 for (Constant con2 : leaders2) { 607 Constant c1 = con1; 608 Constant c2 = con2; 609 Debug.log(getClass(), ppt, Debug.vis(c1.vi, c2.vi), "Considering slice"); 610 if (con2.vi.varinfo_index < con1.vi.varinfo_index) { 611 if (leaders1.contains(con2)) { 612 // The variable is in both leader lists. 613 // Don't add it on this iteration; add it when the variables 614 // are given in order (to prevent creating the slice twice). 615 continue; 616 } 617 c1 = con2; 618 c2 = con1; 619 } 620 if (!ppt.is_slice_ok(c1.vi, c2.vi)) { 621 if (Debug.logOn()) { 622 Debug.log( 623 debug, 624 getClass(), 625 ppt, 626 Debug.vis(c1.vi, c2.vi), 627 "Not instantiating slice " + c1.vi.equalitySet.size()); 628 } 629 continue; 630 } 631 PptSlice2 slice2 = new PptSlice2(ppt, c1.vi, c2.vi); 632 Debug.log( 633 getClass(), 634 ppt, 635 Debug.vis(c1.vi, c2.vi), 636 String.format("instantiating slice %s [%s %s]%n", slice2, c1, c2)); 637 slice2.instantiate_invariants(); 638 if (c1.count > 0 && c2.count > 0) { 639 assert c1.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 640 assert c2.val != null : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 641 slice2.add_val_bu(c1.val, c2.val, mod, mod, con1.count); 642 } 643 new_views.add(slice2); 644 } 645 } 646 647 // Ternary slices/invariants. Note that if a variable is in both 648 // leader lists, it is only added when it is in order (to prevent 649 // creating the slice twice). 650 for (Constant con1 : leaders1) { 651 for (Constant con2 : leaders2) { 652 if ((con2.vi.varinfo_index < con1.vi.varinfo_index) && leaders1.contains(con2)) { 653 continue; 654 } 655 for (Constant con3 : leaders2) { 656 if ((con3.vi.varinfo_index < con2.vi.varinfo_index) 657 || ((con3.vi.varinfo_index < con1.vi.varinfo_index) && leaders1.contains(con3))) 658 continue; 659 Constant[] con_arr = {con1, con2, con3}; 660 Arrays.sort(con_arr, ConIndexComparator.getInstance()); 661 assert (con_arr[0].vi.varinfo_index <= con_arr[1].vi.varinfo_index) 662 && (con_arr[1].vi.varinfo_index <= con_arr[2].vi.varinfo_index); 663 if (!ppt.is_slice_ok(con_arr[0].vi, con_arr[1].vi, con_arr[2].vi)) { 664 continue; 665 } 666 667 PptSlice3 slice3 = new PptSlice3(ppt, con_arr[0].vi, con_arr[1].vi, con_arr[2].vi); 668 slice3.instantiate_invariants(); 669 if ((con_arr[0].count > 0) && (con_arr[1].count > 0) && (con_arr[2].count > 0)) { 670 assert con_arr[0].val != null 671 : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 672 assert con_arr[1].val != null 673 : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 674 assert con_arr[2].val != null 675 : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 676 slice3.add_val_bu( 677 con_arr[0].val, con_arr[1].val, con_arr[2].val, mod, mod, mod, con_arr[0].count); 678 } 679 new_views.add(slice3); 680 } 681 } 682 } 683 684 // Debug print the created slices 685 if (Debug.logOn() || debug.isLoggable(Level.FINE)) { 686 int[] slice_cnt = {0, 0, 0, 0}; 687 int[] inv_cnt = {0, 0, 0, 0}; 688 int[] true_inv_cnt = {0, 0, 0, 0}; 689 for (PptSlice slice : new_views) { 690 for (Invariant inv : slice.invs) { 691 inv.log("created, falsified = %b", inv.is_false()); 692 if (!inv.is_false()) { 693 true_inv_cnt[slice.arity()]++; 694 } else { 695 String vals = ""; 696 for (VarInfo vi : slice.var_infos) { 697 vals += vi.name() + "=" + Debug.toString(constant_value(vi)) + " "; 698 } 699 inv.log("Invariant %s destroyed by constant values %s", inv.format(), vals); 700 } 701 } 702 if (slice.invs.size() > 0) { 703 slice_cnt[slice.arity()]++; 704 } 705 inv_cnt[slice.arity()] += slice.invs.size(); 706 if (Debug.logDetail()) { 707 StringBuilder sb = new StringBuilder(); 708 for (int j = 0; j < slice.arity(); j++) { 709 VarInfo v = slice.var_infos[j]; 710 sb.append(v.name() + " [" + v.file_rep_type + "] [" + v.comparability + "] "); 711 } 712 Debug.log( 713 debug, 714 getClass(), 715 ppt, 716 slice.var_infos, 717 "Adding slice over " + sb + ": with " + slice.invs.size() + " invariants"); 718 } 719 } 720 for (int i = 1; i <= 3; i++) { 721 debug.fine( 722 "Added " 723 + slice_cnt[i] 724 + " slice" 725 + i 726 + "s with " 727 + true_inv_cnt[i] 728 + " invariants (" 729 + inv_cnt[i] 730 + " total)"); 731 } 732 733 String leader1_str = ""; 734 int leader1_cnt = 0; 735 for (Constant con1 : leaders1) { 736 if (con1.vi.file_rep_type == ProglangType.INT) { 737 leader1_str += con1.vi.name() + " "; 738 leader1_cnt++; 739 } 740 } 741 742 String leader2_str = ""; 743 int leader2_cnt = 0; 744 for (Constant con1 : leaders2) { 745 if (con1.vi.file_rep_type == ProglangType.INT) { 746 leader2_str += con1.vi.name() + " "; 747 leader2_cnt++; 748 } 749 } 750 debug.fine( 751 leader1_cnt 752 + " leader1 ints (" 753 + leader1_str 754 + "): " 755 + leader2_cnt 756 + " leader2 ints (" 757 + leader2_str); 758 } 759 760 // Remove any falsified invariants from the new views. Don't 761 // call remove_falsified() since that has side-effects (such as 762 // NIS processing on the falsified invariants) that we don't want. 763 for (PptSlice slice : new_views) { 764 List<Invariant> to_remove = new ArrayList<>(); 765 for (Invariant inv : slice.invs) { 766 if (inv.is_false()) { 767 to_remove.add(inv); 768 } 769 } 770 slice.removeInvariants(to_remove); 771 } 772 773 // Add the new slices to the top level ppt. This will discard any 774 // slices that ended up with zero invariants 775 ppt.addViews(new_views); 776 } 777 778 public void instantiate_constant_suppressions(List<Constant> new_noncons, List<Constant> all) { 779 780 // Find all of the variable (non-constant) non-missing 781 // integral/float leaders 782 List<Constant> vars = new ArrayList<>(); 783 for (Constant con : all) { 784 if (con.always_missing || con.previous_missing) { 785 continue; 786 } 787 if (con.constant || con.previously_constant) { 788 continue; 789 } 790 if (!con.vi.isCanonical()) { 791 continue; 792 } 793 if (!con.vi.file_rep_type.isIntegral() && !con.vi.file_rep_type.isFloat()) { 794 continue; 795 } 796 if (con.vi.rep_type.isArray()) { 797 continue; 798 } 799 vars.add(con); 800 } 801 802 // Find all of the new non-constant integer/float leaders 803 List<Constant> new_leaders = new ArrayList<>(); 804 for (Constant con : new_noncons) { 805 if (!con.vi.isCanonical()) { 806 continue; 807 } 808 if (!con.vi.file_rep_type.isIntegral() && !con.vi.file_rep_type.isFloat()) { 809 continue; 810 } 811 if (con.vi.rep_type.isArray()) { 812 continue; 813 } 814 new_leaders.add(con); 815 } 816 817 if (debug.isLoggable(Level.FINE)) { 818 debug.fine("new non-con leaders = " + new_leaders); 819 debug.fine("variable leaders = " + vars); 820 } 821 822 // Consider all of the ternary slices with one new non-constant 823 for (int i = 0; i < new_leaders.size(); i++) { 824 Constant con1 = new_leaders.get(i); 825 assert con1.val != null : "@AssumeAssertion(nullness)"; 826 for (int j = 0; j < vars.size(); j++) { 827 Constant con2 = vars.get(j); 828 assert con1 != con2; 829 for (int k = j; k < vars.size(); k++) { 830 Constant con3 = vars.get(k); 831 assert con1 != con3; 832 if (!ppt.is_slice_ok(con1.vi, con2.vi, con3.vi)) { 833 continue; 834 } 835 836 if (debug.isLoggable(Level.FINE)) { 837 debug.fine(String.format("considering slice %s %s %s", con1, con2, con3)); 838 } 839 840 // Look for a LinearBinary over two variables. If it doesn't 841 // exist we don't create a LinearTernary 842 Invariant lb = find_linear_binary(ppt.findSlice(con2.vi, con3.vi)); 843 if (lb == null) { 844 continue; 845 } 846 847 // Find the ternary slice and create it if it is not there 848 PptSlice slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi); 849 850 // Create the LinearTernary invariant from the LinearBinary 851 // invariant and the constant value 852 Invariant lt = null; 853 if (con1.vi.file_rep_type.isIntegral()) { 854 lt = LinearTernary.get_proto().instantiate(slice); 855 if (lt != null) { 856 ((LinearTernary) lt).setup((LinearBinary) lb, con1.vi, ((Long) con1.val).longValue()); 857 } 858 } else /* must be float */ { 859 lt = LinearTernaryFloat.get_proto().instantiate(slice); 860 if (lt != null) { 861 ((LinearTernaryFloat) lt) 862 .setup((LinearBinaryFloat) lb, con1.vi, ((Double) con1.val).doubleValue()); 863 } 864 } 865 if (lt != null) { 866 if (Debug.dkconfig_internal_check) { 867 assert slice.find_inv_by_class(lt.getClass()) == null 868 : "inv = " + lt.format() + " slice = " + slice; 869 } 870 slice.addInvariant(lt); 871 if (debug.isLoggable(Level.FINE)) { 872 debug.fine("Adding invariant " + lt.format() + " to slice " + slice); 873 } 874 } 875 } 876 } 877 } 878 879 // Consider all of the ternary slices with two new non-constants 880 for (int i = 0; i < new_leaders.size(); i++) { 881 Constant con1 = new_leaders.get(i); 882 assert con1.val != null : "@AssumeAssertion(nullness)"; 883 for (int j = i; j < new_leaders.size(); j++) { 884 Constant con2 = new_leaders.get(j); 885 for (int k = 0; k < vars.size(); k++) { 886 Constant con3 = vars.get(k); 887 assert con2 != con3; 888 assert con1 != con3; 889 if (!ppt.is_slice_ok(con1.vi, con2.vi, con3.vi)) { 890 continue; 891 } 892 893 if (debug.isLoggable(Level.FINE)) { 894 debug.fine(String.format("considering slice %s %s %s", con1, con2, con3)); 895 } 896 897 // Create the ternary slice 898 899 // Create the LinearTernary invariant from the OneOf invariant 900 // (if any) and the constant values. If no OneOf exists, 901 // there can be no interesting plane of the points 902 Invariant lt = null; 903 PptSlice slice = null; 904 InvariantStatus sts = InvariantStatus.NO_CHANGE; 905 if (con1.vi.file_rep_type.isIntegral()) { 906 OneOfScalar oo = 907 (OneOfScalar) ppt.find_inv_by_class(new VarInfo[] {con3.vi}, OneOfScalar.class); 908 if (oo == null) { 909 continue; 910 } 911 slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi); 912 913 lt = LinearTernary.get_proto().instantiate(slice); 914 if (lt != null) { 915 assert con2.val != null : "@AssumeAssertion(nullness)"; 916 sts = 917 ((LinearTernary) lt) 918 .setup( 919 oo, 920 con1.vi, 921 ((Long) con1.val).longValue(), 922 con2.vi, 923 ((Long) con2.val).longValue()); 924 } 925 } else /* must be float */ { 926 OneOfFloat oo = 927 (OneOfFloat) ppt.find_inv_by_class(new VarInfo[] {con3.vi}, OneOfFloat.class); 928 if (oo == null) { 929 continue; 930 } 931 slice = ppt.get_or_instantiate_slice(con1.vi, con2.vi, con3.vi); 932 lt = LinearTernaryFloat.get_proto().instantiate(slice); 933 if (lt != null) { 934 assert con2.val != null : "@AssumeAssertion(nullness)"; 935 sts = 936 ((LinearTernaryFloat) lt) 937 .setup( 938 oo, 939 con1.vi, 940 ((Double) con1.val).doubleValue(), 941 con2.vi, 942 ((Double) con2.val).doubleValue()); 943 } 944 } 945 if ((lt != null) && (sts == InvariantStatus.NO_CHANGE)) { 946 if (Debug.dkconfig_internal_check) { 947 assert slice.find_inv_by_class(lt.getClass()) == null 948 : "inv = " + lt.format() + " slice = " + slice; 949 } 950 slice.addInvariant(lt); 951 debug.fine("Adding invariant " + lt.format() + " to slice " + slice); 952 } 953 } 954 } 955 } 956 } 957 958 /** 959 * Looks for a LinearBinary invariant in the specified slice. Will match either float or integer 960 * versions. 961 */ 962 private @Nullable Invariant find_linear_binary(@Nullable PptSlice slice) { 963 964 // if (debug.isLoggable (Level.FINE)) 965 // debug.fine ("considering slice " + slice); 966 967 if (slice == null) { 968 return null; 969 } 970 971 for (Invariant inv : slice.invs) { 972 // debug.fine ("inv = " + inv.getClass()); 973 if ((inv.getClass() == LinearBinary.class) || (inv.getClass() == LinearBinaryFloat.class)) { 974 return inv; 975 } 976 } 977 978 return null; 979 } 980 981 /** 982 * Create invariants for any remaining constants. Right now, this looks for invariants between all 983 * of the constants. It's not clear that invariants between constants are interesting, but to 984 * match previous behavior, this is what we will do for now. 985 */ 986 public void post_process() { 987 988 // if requested, don't create any post-processed invariants 989 if (no_post_process) { 990 int con_count = 0; 991 for (Constant con : con_list) { 992 if (!con.vi.isCanonical()) { 993 continue; 994 } 995 System.out.println( 996 " Not creating invariants over leader " + con.vi.name() + " = " + con.val); 997 con_count++; 998 } 999 System.out.println(con_count + " constants at ppt " + ppt); 1000 return; 1001 } 1002 1003 // If specified, create only OneOf invariants. Also create a reflexive 1004 // equality invariant, since that is assumed to exist in many places. 1005 if (dkconfig_OneOf_only) { 1006 for (Constant con : con_list) { 1007 if (!con.vi.isCanonical()) { 1008 continue; 1009 } 1010 instantiate_oneof(con); 1011 ppt.create_equality_inv(con.vi, con.vi, con.count); 1012 } 1013 return; 1014 } 1015 1016 // Get a list of all remaining constants and clear the existing list 1017 // (if the existing list is not cleared, constant slices will not 1018 // be created). 1019 List<Constant> noncons = con_list; 1020 for (Constant con : con_list) { 1021 con.constant = false; 1022 con.previously_constant = true; 1023 } 1024 con_list = new ArrayList<Constant>(); 1025 1026 // Don't do anything with variables that have always been missing. They 1027 // should have no invariants over them. 1028 List<Constant> non_missing = new ArrayList<>(); 1029 1030 instantiate_new_views(noncons, non_missing); 1031 1032 /* Code to just create just unary slices for constants 1033 for (Constant con : con_list) { 1034 if (!con.vi.isCanonical()) { 1035 continue; 1036 } 1037 PptSlice1 slice1 = new PptSlice1 (ppt, con.vi); 1038 slice1.instantiate_invariants(); 1039 if (con.val != null) { 1040 slice1.add_val (con.val, ValueTuple.MODIFIED, con.count); 1041 } 1042 new_views.add (slice1); 1043 } 1044 ppt.addViews (new_views); 1045 */ 1046 } 1047 1048 /** 1049 * Create unary and binary constant invariants. The slices and invariants are created and 1050 * returned, but not added to the ppt. Note that when NIS.dkconfig_suppressor_list is turned on 1051 * (default is on), only unary and binary invariants that can be suppressors in NIS suppressions 1052 * are created. 1053 */ 1054 @RequiresNonNull("daikon.suppress.NIS.suppressor_proto_invs") 1055 public List<PptSlice> create_constant_invs() { 1056 1057 // Turn off track logging so that we don't get voluminous messages 1058 // each time this is called 1059 boolean debug_on = Logger.getLogger("daikon.Debug").isLoggable(Level.FINE); 1060 if (debug_on) { 1061 LogHelper.setLevel("daikon.Debug", Level.OFF); 1062 } 1063 1064 // Get constant leaders 1065 List<Constant> leaders = new ArrayList<>(100); 1066 for (Constant con : con_list) { 1067 if (!con.vi.isCanonical()) { 1068 continue; 1069 } 1070 1071 // hashcode types are not involved in suppressions 1072 if (NIS.dkconfig_skip_hashcode_type) { 1073 if (con.vi.file_rep_type.isHashcode()) { 1074 continue; 1075 } 1076 } 1077 1078 leaders.add(con); 1079 } 1080 1081 List<PptSlice> new_views = new ArrayList<>(100); 1082 int mod = ValueTuple.MODIFIED; 1083 1084 // Unary slices/invariants 1085 for (Constant con : leaders) { 1086 1087 PptSlice1 slice1 = new PptSlice1(ppt, con.vi); 1088 1089 if (NIS.dkconfig_suppressor_list) { 1090 slice1.instantiate_invariants(NIS.suppressor_proto_invs); 1091 } else { 1092 slice1.instantiate_invariants(); 1093 } 1094 1095 if (con.count > 0) { 1096 assert con.val != null : "@AssumeAssertion(nullness): dependent: val when count>0"; 1097 slice1.add_val_bu(con.val, mod, con.count); 1098 } 1099 if (slice1.invs.size() > 0) { 1100 new_views.add(slice1); 1101 } 1102 } 1103 1104 // Binary slices/invariants 1105 for (int i = 0; i < leaders.size(); i++) { 1106 Constant con1 = leaders.get(i); 1107 for (int j = i; j < leaders.size(); j++) { 1108 Constant con2 = leaders.get(j); 1109 if (!con1.vi.compatible(con2.vi)) { 1110 continue; 1111 } 1112 1113 PptSlice2 slice2 = new PptSlice2(ppt, con1.vi, con2.vi); 1114 if (NIS.dkconfig_suppressor_list) { 1115 slice2.instantiate_invariants(NIS.suppressor_proto_invs); 1116 } else { 1117 slice2.instantiate_invariants(); 1118 } 1119 1120 if (con1.count > 0 && con2.count > 0) { 1121 assert con1.val != null 1122 : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 1123 assert con2.val != null 1124 : "@AssumeAssertion(nullness): dependent: val != null when count>0"; 1125 slice2.add_val_bu(con1.val, con2.val, mod, mod, con1.count); 1126 } 1127 if (slice2.invs.size() > 0) { 1128 new_views.add(slice2); 1129 } 1130 } 1131 } 1132 1133 // Remove any falsified invariants from the new views. 1134 for (PptSlice slice : new_views) { 1135 for (Iterator<Invariant> j = slice.invs.iterator(); j.hasNext(); ) { 1136 Invariant inv = j.next(); 1137 if (inv.is_false()) { 1138 j.remove(); 1139 } 1140 } 1141 } 1142 1143 if (debug_on) { 1144 LogHelper.setLevel("daikon.Debug", Level.FINE); 1145 } 1146 1147 return new_views; 1148 } 1149 1150 public void print_missing(PrintWriter out) { 1151 1152 for (Constant con : missing_list) { 1153 out.println(con.vi.name() + " is always missing"); 1154 } 1155 } 1156 1157 /** 1158 * Merge dynamic constants from the children of this ppt. Only missing is merged since constants 1159 * are not used after we are done processing samples. 1160 */ 1161 public void merge() { 1162 1163 // clear the constant and missing lists 1164 missing_list.clear(); 1165 con_list.clear(); 1166 1167 // Process each variable at this ppt. If the variable is missing at 1168 // each of the children, it is also missing here. Ignore children that 1169 // have no mapping for this variable 1170 for (VarInfo pvar : ppt.var_infos) { 1171 boolean missing = true; 1172 for (PptRelation rel : ppt.children) { 1173 VarInfo cvar = rel.childVar(pvar); 1174 if ((cvar != null) 1175 && (rel.child.constants != null) 1176 && !rel.child.constants.is_missing(cvar)) { 1177 missing = false; 1178 break; 1179 } 1180 } 1181 Constant c = all_vars[pvar.varinfo_index]; 1182 c.checkRep(); 1183 c.always_missing = missing; 1184 c.checkRep(); 1185 if (missing) { 1186 missing_list.add(c); 1187 } 1188 } 1189 } 1190 1191 /** Creates OneOf invariants for each constant. */ 1192 public void instantiate_oneof(Constant con) { 1193 assert con.val != null : "@AssumeAssertion(nullness)"; 1194 1195 // @NonNull, but not marked that way to ease warning suppression. 1196 Invariant inv; 1197 PptSlice1 slice1 = (PptSlice1) ppt.get_or_instantiate_slice(con.vi); 1198 1199 // Create the correct OneOf invariant 1200 ProglangType rep_type = con.vi.rep_type; 1201 boolean is_scalar = rep_type.isScalar(); 1202 if (is_scalar) { 1203 if (!OneOfScalar.dkconfig_enabled) { 1204 return; 1205 } 1206 inv = OneOfScalar.get_proto().instantiate(slice1); 1207 } else if (rep_type == ProglangType.INT_ARRAY) { 1208 if (!OneOfSequence.dkconfig_enabled) { 1209 return; 1210 } 1211 inv = OneOfSequence.get_proto().instantiate(slice1); 1212 } else if (rep_type == ProglangType.DOUBLE) { 1213 if (!OneOfFloat.dkconfig_enabled) { 1214 return; 1215 } 1216 inv = OneOfFloat.get_proto().instantiate(slice1); 1217 } else if (rep_type == ProglangType.DOUBLE_ARRAY) { 1218 if (!OneOfFloatSequence.dkconfig_enabled) { 1219 return; 1220 } 1221 inv = OneOfFloatSequence.get_proto().instantiate(slice1); 1222 } else if (rep_type == ProglangType.STRING) { 1223 if (!OneOfString.dkconfig_enabled) { 1224 return; 1225 } 1226 inv = OneOfString.get_proto().instantiate(slice1); 1227 } else if (rep_type == ProglangType.STRING_ARRAY) { 1228 if (!OneOfStringSequence.dkconfig_enabled) { 1229 return; 1230 } 1231 inv = OneOfStringSequence.get_proto().instantiate(slice1); 1232 } else { 1233 throw new Error("Unrecognized rep_type in instantiate_oneof"); 1234 } 1235 assert inv != null 1236 : "@AssumeAssertion(nullness): instantiation of the given invariants always succeeds"; 1237 slice1.addInvariant(inv); 1238 1239 // Add the value to it 1240 slice1.add_val_bu(con.val, ValueTuple.MODIFIED, con.count); 1241 } 1242}