001package daikon.dcomp; 002 003import daikon.DynComp; 004import daikon.chicory.ArrayInfo; 005import daikon.chicory.ClassInfo; 006import daikon.chicory.ComparabilityProvider; 007import daikon.chicory.DaikonClassInfo; 008import daikon.chicory.DaikonVariableInfo; 009import daikon.chicory.DaikonWriter; 010import daikon.chicory.DeclReader; 011import daikon.chicory.DeclWriter; 012import daikon.chicory.FieldInfo; 013import daikon.chicory.MethodInfo; 014import daikon.chicory.ParameterInfo; 015import daikon.chicory.ReturnInfo; 016import daikon.chicory.RootInfo; 017import daikon.chicory.StaticObjInfo; 018import daikon.chicory.StringInfo; 019import daikon.chicory.ThisObjInfo; 020import daikon.plumelib.bcelutil.SimpleLog; 021import daikon.plumelib.util.WeakIdentityHashMap; 022import java.io.PrintWriter; 023import java.lang.reflect.Array; 024import java.lang.reflect.Field; 025import java.lang.reflect.InvocationTargetException; 026import java.lang.reflect.Method; 027import java.lang.reflect.Modifier; 028import java.time.LocalDateTime; 029import java.time.ZoneId; 030import java.util.ArrayDeque; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.Deque; 034import java.util.HashMap; 035import java.util.HashSet; 036import java.util.IdentityHashMap; 037import java.util.LinkedHashMap; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041import java.util.concurrent.ConcurrentHashMap; 042import java.util.regex.Matcher; 043import java.util.regex.Pattern; 044import org.checkerframework.checker.lock.qual.GuardSatisfied; 045import org.checkerframework.checker.mustcall.qual.MustCall; 046import org.checkerframework.checker.nullness.qual.Nullable; 047import org.checkerframework.checker.nullness.qual.PolyNull; 048import org.checkerframework.checker.signature.qual.BinaryName; 049import org.checkerframework.dataflow.qual.Pure; 050 051/** 052 * Runtime support for DynComp, a comparability front end for Chicory. This class is a collection of 053 * methods; it should never be instantiated. 054 */ 055@SuppressWarnings({"nullness", "interning"}) // tricky code, skip for now 056public final class DCRuntime implements ComparabilityProvider { 057 058 /** List of all instrumented methods. */ 059 public static final List<MethodInfo> methods = new ArrayList<>(); 060 061 /** 062 * Keep track of whether or not we are already processing an enter/exit so we can avoid recursion. 063 * Only really necessary during debugging (where we call toString()). 064 */ 065 private static boolean in_enter_exit = false; 066 067 /** Object used to represent nonsensical values. */ 068 private static final Object nonsensical = new Object(); 069 070 /** Object used to represent nonsensical list values. */ 071 private static final Object nonsensical_list = new Object(); 072 073 /** Depth to follow fields in classes. */ 074 public static int depth = 2; 075 076 /** static count of tags in the JDK. Used as an offset for non-JDK code. */ 077 static int max_jdk_static = 100000; 078 079 /** If the application exits with an exception, it should be placed here. */ 080 @SuppressWarnings("StaticAssignmentOfThrowable") // for debugging (I presume) 081 public static @Nullable Throwable exit_exception = null; 082 083 /** Storage for each static tag. */ 084 public static List<@Nullable Object> static_tags = new ArrayList<>(); 085 086 /** Either "java.lang.DCompInstrumented" or "daikon.dcomp.DCompInstrumented". */ 087 static @BinaryName String instrumentation_interface; 088 089 /** 090 * Object used to mark procedure entries in the tag stack. It is pushed on the stack at entry and 091 * checked on exit to make sure it is in on the top of the stack. That allows us to determine 092 * which method caused a tag stack problem. 093 */ 094 public static Object method_marker = new Object(); 095 096 /** Control debug printing. */ 097 public static boolean debug = false; 098 099 /** Log comparability tage stack operations. */ 100 public static boolean debug_tag_frame = false; 101 102 /** Log object compare operations. */ 103 public static boolean debug_objects = false; 104 105 /** Log variable comparability operations. */ 106 public static SimpleLog merge_dv = new SimpleLog(false); 107 108 /** Log array comparability operations. */ 109 public static SimpleLog debug_arr_index = new SimpleLog(false); 110 111 /** Log primitive operations. */ 112 public static SimpleLog debug_primitive = new SimpleLog(false); 113 114 /** Log comparability merges. */ 115 public static SimpleLog debug_merge_comp = new SimpleLog(false); 116 117 /** Log excution time. */ 118 public static SimpleLog debug_timing = new SimpleLog(false); 119 120 /** Log decl output. */ 121 public static SimpleLog debug_decl_print = new SimpleLog(false); 122 123 /** Log excution time. */ 124 public static SimpleLog time_decl = new SimpleLog(false); 125 126 /** Log internal data structure sizes. */ 127 public static SimpleLog map_info = new SimpleLog(false); 128 129 /** If true, merge arrays and their indices. */ 130 private static boolean merge_arrays_and_indices = true; 131 132 /** Decl writer to share output code with Chicory. */ 133 // Set in Premain.premain(). 134 static DeclWriter declWriter; 135 136 /** Used for calling {@link ComparabilityProvider#getComparability}. */ 137 // Set in Premain.premain(). 138 static ComparabilityProvider comparabilityProvider; 139 140 /** Whether the header has been printed. */ 141 private static boolean headerPrinted = false; 142 143 /** Class to hold per-thread comparability data. */ 144 private static class ThreadData { 145 /** Tag stack. */ 146 Deque<Object> tag_stack; 147 148 /** Number of methods currently on tag_stack. */ 149 int tag_stack_call_depth; 150 151 /** class initializer. */ 152 ThreadData() { 153 tag_stack = new ArrayDeque<Object>(); 154 tag_stack_call_depth = 0; 155 } 156 } 157 158 /** Map from Thread to ThreadData. */ 159 private static Map<Thread, ThreadData> thread_to_data = 160 new ConcurrentHashMap<Thread, ThreadData>(); 161 162 /** Map from each object to the tags used for each primitive value in the object. */ 163 public static WeakIdentityHashMap<Object, Object[]> field_map = 164 new WeakIdentityHashMap<Object, Object[]>(); 165 166 /** List of all classes encountered. These are the classes that will have comparability output. */ 167 private static List<ClassInfo> all_classes = new ArrayList<>(); 168 169 /** Set of classes whose static initializer has run. */ 170 private static Set<String> initialized_eclassses = new HashSet<>(); 171 172 /** 173 * Class used as a tag for primitive constants. Only different from Object for debugging purposes. 174 */ 175 private static class Constant {} 176 177 /** 178 * Class used as a tag for uninitialized instance fields. Only different from Object for debugging 179 * purposes. 180 */ 181 @SuppressWarnings("UnusedVariable") // used only for debugging 182 private static class UninitFieldTag { 183 String descr; 184 Throwable stack_trace; 185 186 public UninitFieldTag(String descr, Throwable stack_trace) { 187 this.descr = descr; 188 this.stack_trace = stack_trace; 189 } 190 } 191 192 /** 193 * Class used as a tag for uninitialized array elements. Only different from Object for debugging 194 * purposes. 195 */ 196 private static class UninitArrayElem {} 197 198 /** Either java.lang.DCompMarker or daikon.dcomp.DCompMarker */ 199 private static Class<?> dcomp_marker_class; 200 201 /** java.lang.Object */ 202 private static Class<Object> java_lang_Object_class; 203 204 /** Perform any initialization required before instrumentation begins. */ 205 public static void init() { 206 207 debug_decl_print.enabled = DynComp.debug_decl_print; 208 if (Premain.debug_dcruntime) { 209 debug = true; 210 debug_tag_frame = true; 211 debug_primitive.enabled = true; 212 } 213 if (Premain.debug_dcruntime_all) { 214 debug = true; 215 debug_tag_frame = true; 216 debug_objects = true; 217 merge_dv.enabled = true; 218 debug_arr_index.enabled = true; 219 debug_primitive.enabled = true; 220 debug_merge_comp.enabled = true; 221 debug_decl_print.enabled = true; 222 } 223 224 try { 225 if (!Premain.jdk_instrumented) { 226 dcomp_marker_class = Class.forName("daikon.dcomp.DCompMarker"); 227 } else { 228 dcomp_marker_class = Class.forName("java.lang.DCompMarker"); 229 } 230 @SuppressWarnings("unchecked") 231 Class<Object> tmp = (Class<Object>) Class.forName("java.lang.Object"); 232 java_lang_Object_class = tmp; 233 } catch (Exception e) { 234 throw new RuntimeException("Unexpected error initializing DCRuntime:", e); 235 } 236 237 // Initialize the array of static tags 238 ((ArrayList<@Nullable Object>) static_tags).ensureCapacity(max_jdk_static); 239 while (static_tags.size() <= max_jdk_static) { 240 static_tags.add(null); 241 } 242 } 243 244 public static void debug_print_call_stack() { 245 if (!debug) { 246 return; 247 } 248 249 StackTraceElement[] stack_trace; 250 stack_trace = Thread.currentThread().getStackTrace(); 251 // [0] is getStackTrace 252 // [1] is debug_print_call_stack 253 for (int i = 2; i < stack_trace.length; i++) { 254 System.out.printf("call stack: %s%n", stack_trace[i]); 255 } 256 System.out.println(); 257 } 258 259 /** 260 * Handles calls to instrumented equals() methods. Makes the arguments comparable, and returns 261 * true if they are equals() to one another. 262 * 263 * @param o1 the first argument to equals() 264 * @param o2 the second argument to equals() 265 * @return whether the two values are equal 266 */ 267 public static boolean dcomp_equals(Object o1, Object o2) { 268 // Make obj1 and obj2 comparable 269 if ((o1 != null) && (o2 != null)) { 270 TagEntry.union(o1, o2); 271 } 272 273 if (debug) { 274 System.out.printf("In dcomp_equals%n"); 275 } 276 277 Method m; 278 try { 279 m = 280 o1.getClass() 281 .getMethod("equals_dcomp_instrumented", new Class<?>[] {java_lang_Object_class}); 282 } catch (NoSuchMethodException e) { 283 m = null; 284 } catch (Exception e) { 285 throw new RuntimeException("unexpected error locating equal_dcomp_instrumented", e); 286 } 287 288 if (m != null) { 289 try { 290 // In case the class containing "equals_dcomp_instrumented" is not accessible: 291 m.setAccessible(true); 292 return (Boolean) m.invoke(o1, o2); 293 } catch (Exception e) { 294 throw new RuntimeException("unexpected error invoking equal_dcomp_instrumented", e); 295 } 296 } 297 298 // Push tag for return value, and call the uninstrumented version 299 ThreadData td = thread_to_data.get(Thread.currentThread()); 300 td.tag_stack.push(new Constant()); 301 if (debug_tag_frame) { 302 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 303 } 304 return o1.equals(o2); 305 } 306 307 /** 308 * This map keeps track of active super.equals() calls. 309 * 310 * <p>Each time we make a call on a particular Object, we keep track of which superclass's equals 311 * method we called last. If that Object makes another call to super.equals before the original 312 * call is done, then invoke the equals method of the next-higher class in the class hierarchy 313 * (i.e. the next superclass). 314 * 315 * <p>The map maps an Object to the last Class whose equals method we invoked while invoking that 316 * Object's original super.equals call. Once the equals call terminates, whether by returning a 317 * value or by throwing an exception, the corresponding key is removed from this map. 318 */ 319 static Map<Object, Class<?>> active_equals_calls = new HashMap<>(); 320 321 /** 322 * Handles {@code super.equals(Object)} calls. Makes the arguments comparable, and returns true if 323 * super.equals() returns true for them 324 * 325 * @param o1 the first argument to super.equals() 326 * @param o2 the second argument to super.equals() 327 * @return whether the two values are equal, according to super.equals() 328 * @see #active_equals_calls 329 */ 330 public static boolean dcomp_super_equals(Object o1, Object o2) { 331 // Make obj1 and obj2 comparable 332 if ((o1 != null) && (o2 != null)) { 333 TagEntry.union(o1, o2); 334 } 335 336 if (debug) { 337 System.out.printf("In dcomp_super_equals%n"); 338 } 339 340 Class<?> o1c = o1.getClass(); 341 Class<?> o1super; 342 343 // Check to see if we're already in the middle of a super.equals 344 // call for this Object 345 if (null == active_equals_calls.get(o1)) { 346 // No, we are not 347 o1super = o1c.getSuperclass(); 348 } else { 349 // Yes, we are -- continue up the class hierarchy 350 o1super = active_equals_calls.get(o1).getSuperclass(); 351 } 352 353 // Update the active_equals_calls map 354 active_equals_calls.put(o1, o1super); 355 356 Class<?>[] o1superifaces = o1super.getInterfaces(); 357 358 boolean instrumented = false; 359 for (Class<?> c : o1superifaces) { 360 if (c.getName().equals(instrumentation_interface)) { 361 instrumented = true; 362 break; 363 } 364 } 365 366 boolean return_val; 367 try { 368 // if the superclass whose method we are calling is instrumented... 369 if (instrumented) { 370 // call the instrumented version 371 Method m = 372 o1super.getMethod( 373 "equals", new Class<?>[] {java_lang_Object_class, dcomp_marker_class}); 374 return_val = ((Boolean) m.invoke(o1, o2, null)); 375 } else { 376 // Push tag for return value, and call the uninstrumented version 377 @MustCall() ThreadData td = thread_to_data.get(Thread.currentThread()); 378 td.tag_stack.push(new Constant()); 379 Method m = o1super.getMethod("equals", new Class<?>[] {java_lang_Object_class}); 380 return_val = ((Boolean) m.invoke(o1, o2)); 381 if (debug_tag_frame) { 382 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 383 } 384 } 385 } catch (NoSuchMethodException e) { 386 System.err.printf("dcomp_super_equals(%s, %s)%n", obj_str(o1), obj_str(o2)); 387 System.err.printf("o1super %s%n", o1super); 388 for (Class<?> c : o1superifaces) { 389 System.err.printf(" o1super interface %s%n", c); 390 } 391 for (Method m : o1super.getDeclaredMethods()) { 392 System.err.printf(" o1super method %s%n", m); 393 } 394 throw new RuntimeException(e); 395 } catch (IllegalAccessException e) { 396 throw new RuntimeException(e); 397 } catch (InvocationTargetException e) { 398 e.printStackTrace(); 399 throw new RuntimeException(e); 400 } catch (Exception e) { 401 throw new RuntimeException("unexpected error locating equals method", e); 402 } 403 404 // We are now done with the call, so remove the entry for this 405 // call from the active_equals_calls map 406 active_equals_calls.remove(o1); 407 return return_val; 408 } 409 410 /** 411 * Clone an object and return the cloned object. 412 * 413 * <p>DCInstrument guarantees we will not see a clone of an array. If the object has been 414 * instrumented, call the instrumented version of clone; if not, call the uninstrumented version 415 * and make the objects comparable. Really should make all of their fields comparable instead. 416 * 417 * @param orig_obj object being cloned 418 * @param target_class class to search for clone method 419 * @return the result of the clone 420 * @throws Throwable if unable to clone object 421 */ 422 public static Object dcomp_clone(Object orig_obj, Class<?> target_class) throws Throwable { 423 if (debug) { 424 System.out.printf("In dcomp_clone%n"); 425 System.out.printf("orig: %s, target: %s%n", orig_obj.getClass(), target_class); 426 } 427 428 Object clone_obj = null; 429 while (true) { 430 try { 431 clone_obj = dcomp_clone_worker(orig_obj, target_class); 432 assert (clone_obj != null); 433 break; 434 } catch (Exception e) { 435 if (e.getCause() == null) { 436 // Some exception other than NoSuchMethod. 437 throw e; 438 } 439 if (e.getCause() instanceof java.lang.NoSuchMethodException) { 440 if (debug) { 441 System.out.println("NoSuchMethod " + target_class.getName()); 442 } 443 444 // Should never reach top of class heirarchy without finding a clone() method. 445 assert !target_class.getName().equals("java.lang.Object"); 446 447 // We didn't find a clone method, get next higher super and try again. 448 target_class = target_class.getSuperclass(); 449 } else { 450 // Some exception other than NoSuchMethod. 451 throw e; 452 } 453 } 454 } 455 456 // Make orig_obj and its clone comparable. 457 if ((orig_obj != null) && (clone_obj != null)) { 458 TagEntry.union(orig_obj, clone_obj); 459 } 460 return clone_obj; 461 } 462 463 /** 464 * Tracks active {@code super.clone()} calls. 465 * 466 * @see active_equals_calls 467 */ 468 static Map<Object, Class<?>> active_clone_calls = new HashMap<>(); 469 470 /** 471 * Handles {@code super.clone()} calls. 472 * 473 * <p>Clone an object using its superclass, and returns the cloned object. 474 * 475 * <p>DCInstrument guarantees we will not see a clone of an array. If the object has been 476 * instrumented, call the instrumented version of clone; if not, call the uninstrumented version 477 * and make the objects comparable. TODO: This really should make all of their fields comparable 478 * instead. 479 * 480 * @param orig_obj object being cloned 481 * @param target_class class to search for clone method 482 * @return the result of the clone 483 * @throws Throwable if unable to clone object 484 * @see #active_clone_calls 485 */ 486 public static Object dcomp_super_clone(Object orig_obj, Class<?> target_class) throws Throwable { 487 if (debug) { 488 System.out.printf("In dcomp_super_clone%n"); 489 System.out.printf("orig: %s, target: %s%n", orig_obj.getClass(), target_class); 490 } 491 492 // Check to see if we're already in the middle of a super.clone call for this object. 493 if (null != active_clone_calls.get(orig_obj)) { 494 // Yes, we are -- continue up the class hierarchy 495 target_class = active_clone_calls.get(orig_obj).getSuperclass(); 496 } 497 // Update the active_clone_calls map 498 active_clone_calls.put(orig_obj, target_class); 499 500 Object clone_obj = null; 501 while (true) { 502 try { 503 clone_obj = dcomp_clone_worker(orig_obj, target_class); 504 assert (clone_obj != null); 505 break; 506 } catch (Exception e) { 507 508 if (e.getCause() == null) { 509 // Some exception other than NoSuchMethod. 510 active_clone_calls.remove(orig_obj); 511 throw e; 512 } 513 if (e.getCause() instanceof java.lang.NoSuchMethodException) { 514 if (debug) { 515 System.out.println("NoSuchMethod " + target_class.getName()); 516 } 517 518 // Should never reach top of class heirarchy without finding a clone() method. 519 assert !target_class.getName().equals("java.lang.Object"); 520 521 // We didn't find a clone method, get next higher super and try again. 522 target_class = active_clone_calls.get(orig_obj).getSuperclass(); 523 // Update the active_clone_calls map 524 active_clone_calls.put(orig_obj, target_class); 525 } else { 526 // Some exception other than NoSuchMethod. 527 active_clone_calls.remove(orig_obj); 528 throw e; 529 } 530 } 531 } 532 533 // Make orig_obj and its clone comparable. 534 if ((orig_obj != null) && (clone_obj != null)) { 535 TagEntry.union(orig_obj, clone_obj); 536 } 537 active_clone_calls.remove(orig_obj); 538 return clone_obj; 539 } 540 541 /** 542 * Clone an object and return the cloned object. Throws an exception if unable to clone object. 543 * 544 * <p>DCInstrument guarantees we will not see a clone of an array. If the object has been 545 * instrumented, call the instrumented version of clone; if not, call the uninstrumented version. 546 * 547 * <p>Note: we use getDeclaredMethod as clone is often protected; also, in the case of 548 * dcomp_super_clone we do not want to automatically find clone in a superclass. We will search 549 * the hierarchy ourselves. 550 * 551 * @param orig_obj object being cloned 552 * @param target_class class to search for clone method 553 * @return the result of the clone 554 * @throws Throwable if unable to clone object 555 */ 556 public static Object dcomp_clone_worker(Object orig_obj, Class<?> target_class) throws Throwable { 557 if (debug) { 558 System.out.printf("In dcomp_clone_worker%n"); 559 System.out.printf("orig_obj: %s, target_class: %s%n", obj_str(orig_obj), target_class); 560 } 561 562 Method m = null; 563 try { 564 m = target_class.getDeclaredMethod("clone", new Class<?>[] {dcomp_marker_class}); 565 } catch (NoSuchMethodException e) { 566 m = null; 567 } catch (Exception e) { 568 throw new RuntimeException("unexpected error locating clone(DCompMarker)", e); 569 } 570 571 if (m != null) { 572 try { 573 if (debug) { 574 System.out.printf("found: %s%n", m); 575 } 576 // In case the class containing "clone()" is not accessible 577 // or clone() is protected. 578 m.setAccessible(true); 579 // Since invoke takes a variable number of arguments, we use an array containing null 580 // as the DCompMarker to distinguish from just null, which indicates 0 arguments. 581 return m.invoke(orig_obj, new Object[] {null}); 582 } catch (InvocationTargetException e) { 583 // This might happen - if an exception is thrown from clone(), 584 // propagate it by rethrowing it. 585 throw e.getCause(); 586 } catch (Exception e) { 587 throw new RuntimeException( 588 "unexpected error invoking clone(DCompMarker) on object of class " 589 + orig_obj.getClass(), 590 e); 591 } 592 } 593 594 // No instrumented clone(), try for uninstrumented version. 595 try { 596 m = target_class.getDeclaredMethod("clone", new Class<?>[] {}); 597 } catch (NoSuchMethodException e) { 598 throw new RuntimeException("unable to locate clone()", e); 599 } catch (Exception e) { 600 throw new RuntimeException("unexpected error locating clone()", e); 601 } 602 try { 603 if (debug) { 604 System.out.printf("found: %s%n", m); 605 } 606 // In case the class containing "clone()" is not accessible 607 // or clone() is protected. 608 m.setAccessible(true); 609 return m.invoke(orig_obj); 610 } catch (InvocationTargetException e) { 611 // This might happen - if an exception is thrown from clone(), 612 // propagate it by rethrowing it. 613 throw e.getCause(); 614 } catch (Exception e) { 615 throw new RuntimeException( 616 "unexpected error invoking clone() on object of class " + orig_obj.getClass(), e); 617 } 618 } 619 620 /** 621 * Handle object comparison. Marks the two objects as comparable and returns whether or not they 622 * are equal. Used as part of a replacement for IF_ACMPEQ. 623 */ 624 public static boolean object_eq(Object obj1, Object obj2) { 625 626 if (debug_objects) { 627 System.out.printf("comparing (eq) '%s' and '%s'%n", obj_str(obj1), obj_str(obj2)); 628 } 629 630 // Note that obj1 and obj2 are comparable 631 if ((obj1 != null) && (obj2 != null)) { 632 TagEntry.union(obj1, obj2); 633 } 634 635 return (obj1 == obj2); 636 } 637 638 /** 639 * Handle object comparison. Marks the two objects as comparable and returns whether or not they 640 * are equal. Used as part of a replacement for IF_ACMPNE. 641 */ 642 public static boolean object_ne(Object obj1, Object obj2) { 643 644 if (debug_objects) { 645 System.out.printf("comparing (ne) '%s' and '%s'%n", obj_str(obj1), obj_str(obj2)); 646 } 647 // Note that obj1 and obj2 are comparable 648 if ((obj1 != null) && (obj2 != null)) { 649 TagEntry.union(obj1, obj2); 650 } 651 652 return (obj1 != obj2); 653 } 654 655 static Map<String, Integer> methodCountMap = new HashMap<>(64); 656 657 /** 658 * Create the tag frame for this method. Pop the tags for any primitive parameters off of the tag 659 * stack and store them in the tag frame. 660 * 661 * @param params encodes the position of the primitive parameters into a string. The first 662 * character is size of the tag frame. The remaining characters indicate where each parameter 663 * on the tag stack should be stored into the frame. For example "20" allocates a tag frame 664 * with two elements and stores the top of the tag stack into element 0. A string is used for 665 * simplicity in code generation since strings can easily be placed into the constant portion 666 * of the class file. Note that characters are determined by adding the integer value to '0'. 667 * Values greater than 9 will have unintuitive (but printable) values. 668 * @return the allocated and initialized tag frame 669 */ 670 public static Object[] create_tag_frame(String params) { 671 if (debug) { 672 System.out.printf("%nEnter: %s%n", caller_name()); 673 } 674 675 if (DynComp.verbose) { 676 String method_name = caller_name(); 677 Integer count = methodCountMap.get(method_name); 678 if (count == null) { 679 count = 1; 680 } else { 681 count = count.intValue() + 1; 682 } 683 methodCountMap.put(method_name, count); 684 } 685 686 // create_tag_frame is the first DCRuntime method called for an 687 // instrumented user method. Since it might be on a new thread 688 // we need to check/set the per-thread data map. 689 Thread t = Thread.currentThread(); 690 ThreadData td = thread_to_data.computeIfAbsent(t, __ -> new ThreadData()); 691 692 int frame_size = ((int) params.charAt(0)) - '0'; 693 // Character.digit (params.charAt(0), Character.MAX_RADIX); 694 Object[] tag_frame = new Object[frame_size]; 695 if (debug_tag_frame) { 696 System.out.printf( 697 "Creating tag frame of size %d [%s] for %s%n", frame_size, params, caller_name()); 698 } 699 for (int ii = 1; ii < params.length(); ii++) { 700 int offset = params.charAt(ii) - '0'; 701 // Character.digit (params.charAt(ii), Character.MAX_RADIX); 702 assert td.tag_stack.peek() != method_marker; 703 tag_frame[offset] = td.tag_stack.pop(); 704 if (debug_tag_frame) { 705 System.out.printf("popped %s into tag_frame[%d]%n", tag_frame[offset], offset); 706 } 707 } 708 709 // Push the method marker on the tag stack (now that we have removed 710 // the parameters 711 td.tag_stack.push(method_marker); 712 // save the tag stack call_depth for debugging 713 tag_frame[frame_size - 1] = ++td.tag_stack_call_depth; 714 if (debug_tag_frame) { 715 System.out.printf("push method marker tag: %s%n", method_marker); 716 System.out.printf("tag stack call_depth: %d%n", td.tag_stack_call_depth); 717 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 718 } 719 720 // debug_print_call_stack(); 721 722 return tag_frame; 723 } 724 725 /** 726 * Make sure the tag stack for this method is empty before exit. 727 * 728 * @param tag_frame the tag frame 729 */ 730 public static void normal_exit(Object[] tag_frame) { 731 if (debug) { 732 System.out.printf("Begin normal exit from %s%n", caller_name()); 733 } 734 735 ThreadData td = thread_to_data.get(Thread.currentThread()); 736 if (td.tag_stack.peek() != method_marker) { 737 // Something has gone wrong. It's probably an exception that 738 // was handled by an exception handler other than one added by 739 // DCInstrument. From an <init> method or native code, for example. 740 if (debug_tag_frame) { 741 System.out.printf("tag stack value mismatch! top not method marker%n"); 742 Thread.dumpStack(); 743 } 744 // discard tags until we get to a method marker 745 while (td.tag_stack.peek() != method_marker) td.tag_stack.pop(); 746 } 747 748 int orig_tag_stack_call_depth = ((Integer) tag_frame[tag_frame.length - 1]).intValue(); 749 if (td.tag_stack_call_depth != orig_tag_stack_call_depth) { 750 // Something has gone wrong. It's almost certainly an exception that 751 // was handled by an exception handler other than one added by 752 // DCInstrument. From an <init> method or native code, for example. 753 if (debug_tag_frame) { 754 System.out.printf( 755 "tag stack call_depth mismatch! at %d expected %d%n", 756 td.tag_stack_call_depth, orig_tag_stack_call_depth); 757 Thread.dumpStack(); 758 } 759 } 760 while (td.tag_stack_call_depth > orig_tag_stack_call_depth) { 761 td.tag_stack.pop(); // discard marker 762 td.tag_stack_call_depth--; 763 // discard tags until we get to a method marker 764 while (td.tag_stack.peek() != method_marker) td.tag_stack.pop(); 765 } 766 767 // now do normal exit process 768 td.tag_stack.pop(); // discard marker 769 td.tag_stack_call_depth--; 770 if (debug_tag_frame) { 771 System.out.printf("tag stack call_depth: %d%n", td.tag_stack_call_depth); 772 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 773 } 774 if (debug) { 775 System.out.printf("Normal exit from %s%n%n", caller_name()); 776 } 777 } 778 779 /** 780 * Called for exits from methods with a primitive return type. Pop the return type off of the tag 781 * stack, make sure the tags stack is empty for this method and then put the return value back on 782 * the tag stack. 783 * 784 * @param tag_frame the tag frame 785 */ 786 public static void normal_exit_primitive(Object[] tag_frame) { 787 if (debug) { 788 System.out.printf("Begin normal exit primitive from %s%n", caller_name()); 789 } 790 791 ThreadData td = thread_to_data.get(Thread.currentThread()); 792 Object ret_tag = td.tag_stack.pop(); // save what we hope is the return value tag 793 assert ret_tag != null; 794 795 if (td.tag_stack.peek() != method_marker) { 796 // Something has gone wrong. It's probably an exception that 797 // was handled by an exception handler other than one added by 798 // DCInstrument. From an <init> method or native code, for example. 799 if (debug_tag_frame) { 800 System.out.printf("tag stack value mismatch! top not method marker%n"); 801 Thread.dumpStack(); 802 } 803 // discard tags until we get to a method marker 804 while (td.tag_stack.peek() != method_marker) td.tag_stack.pop(); 805 } 806 807 int orig_tag_stack_call_depth = ((Integer) tag_frame[tag_frame.length - 1]).intValue(); 808 if (td.tag_stack_call_depth != orig_tag_stack_call_depth) { 809 // Something has gone wrong. It's almost certainly an exception that 810 // was handled by an exception handler other than one added by 811 // DCInstrument. From an <init> method or native code, for example. 812 if (debug_tag_frame) { 813 System.out.printf( 814 "tag stack call_depth mismatch! at %d expected %d%n", 815 td.tag_stack_call_depth, orig_tag_stack_call_depth); 816 Thread.dumpStack(); 817 } 818 } 819 while (td.tag_stack_call_depth > orig_tag_stack_call_depth) { 820 td.tag_stack.pop(); // discard marker 821 td.tag_stack_call_depth--; 822 // discard tags until we get to a method marker 823 while (td.tag_stack.peek() != method_marker) td.tag_stack.pop(); 824 } 825 826 // now do normal exit process 827 td.tag_stack.pop(); // discard marker 828 td.tag_stack_call_depth--; 829 if (debug_tag_frame) { 830 System.out.printf("tag stack call_depth: %d%n", td.tag_stack_call_depth); 831 } 832 if (debug) { 833 System.out.printf("Normal exit primitive from %s%n%n", caller_name()); 834 } 835 td.tag_stack.push(ret_tag); 836 if (debug_tag_frame) { 837 System.out.printf("push return value tag: %s%n", ret_tag); 838 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 839 } 840 } 841 842 /** 843 * Clean up the tag stack on an exception exit from a method. Pops items off of the tag stack 844 * until the method marker is found. 845 * 846 * @param throwable cause of the exception 847 */ 848 public static void exception_exit(Object throwable) { 849 if (debug) { 850 System.out.printf("Begin exception exit from %s%n", caller_name()); 851 ((Throwable) throwable).printStackTrace(); 852 } 853 854 ThreadData td = thread_to_data.get(Thread.currentThread()); 855 td.tag_stack_call_depth--; 856 if (debug_tag_frame) { 857 System.out.printf("tag stack call_depth: %d%n", td.tag_stack_call_depth); 858 } 859 if (debug) { 860 System.out.printf("Exception exit from %s%n", caller_name()); 861 } 862 while (!td.tag_stack.isEmpty()) { 863 if (td.tag_stack.pop() == method_marker) { 864 if (debug_tag_frame) { 865 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 866 } 867 return; 868 } 869 } 870 871 System.err.printf("Method marker not found in exception exit%n"); 872 } 873 874 /** Cleans up the tag stack when an exception is thrown. */ 875 public static void throw_op() { 876 if (debug) { 877 System.out.printf("In throw_op%n"); 878 } 879 ThreadData td = thread_to_data.get(Thread.currentThread()); 880 while (td.tag_stack.peek() != method_marker) td.tag_stack.pop(); 881 if (debug_tag_frame) { 882 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 883 } 884 } 885 886 /** Pushes the tag at tag_frame[index] on the tag stack. */ 887 public static void push_local_tag(Object[] tag_frame, int index) { 888 889 ThreadData td = thread_to_data.get(Thread.currentThread()); 890 debug_primitive.log("push_local_tag[%d] %s%n", index, tag_frame[index]); 891 assert tag_frame[index] != null : "index " + index; 892 td.tag_stack.push(tag_frame[index]); 893 if (debug_tag_frame) { 894 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 895 } 896 } 897 898 /** Pops the top of the tag stack into tag_frame[index] */ 899 public static void pop_local_tag(Object[] tag_frame, int index) { 900 901 ThreadData td = thread_to_data.get(Thread.currentThread()); 902 assert td.tag_stack.peek() != method_marker; 903 tag_frame[index] = td.tag_stack.pop(); 904 assert tag_frame[index] != null : "index " + index; 905 debug_primitive.log("pop_local_tag[%d] %s%n", index, tag_frame[index]); 906 if (debug_tag_frame) { 907 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 908 } 909 } 910 911 /** Pushes the tag associated with the static static_num on the tag stack. */ 912 public static void push_static_tag(int static_num) { 913 914 ThreadData td = thread_to_data.get(Thread.currentThread()); 915 Object static_tag = static_tags.get(static_num); 916 if (static_tag == null) { 917 static_tag = new Object(); 918 static_tags.set(static_num, static_tag); 919 } 920 td.tag_stack.push(static_tag); 921 debug_primitive.log("push_static_tag[%d] %s%n", static_num, static_tag); 922 if (debug_tag_frame) { 923 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 924 } 925 } 926 927 /** 928 * Pushes an array reference on the tag stack. 929 * 930 * @param arr_ref array being accessed 931 */ 932 public static void push_array_tag(Object arr_ref) { 933 934 ThreadData td = thread_to_data.get(Thread.currentThread()); 935 td.tag_stack.push(arr_ref); 936 if (debug_arr_index.enabled()) { 937 debug_arr_index.log("push_array_tag %s%n", obj_str(arr_ref)); 938 } 939 if (debug_tag_frame) { 940 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 941 } 942 } 943 944 /** 945 * Pops the top of the tag stack into the tag storage for static_num. 946 * 947 * @param static_num identifies the static variable being accessed 948 */ 949 public static void pop_static_tag(int static_num) { 950 951 ThreadData td = thread_to_data.get(Thread.currentThread()); 952 assert td.tag_stack.peek() != method_marker; 953 static_tags.set(static_num, td.tag_stack.pop()); 954 assert static_tags.get(static_num) != null; 955 debug_primitive.log("pop_static_tag[%d] %s%n", static_num, static_tags.get(static_num)); 956 if (debug_tag_frame) { 957 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 958 } 959 } 960 961 /** 962 * Discard the tag on the top of the tag stack. Called when primitives are pushed but not used in 963 * expressions (such as when allocating arrays). (No longer used?) 964 * 965 * @param cnt number of tags to discard 966 */ 967 public static void discard_tag(int cnt) { 968 if (debug) { 969 System.out.printf("In discard_tag%n"); 970 } 971 972 ThreadData td = thread_to_data.get(Thread.currentThread()); 973 // debug_print_call_stack(); 974 while (--cnt >= 0) { 975 assert td.tag_stack.peek() != method_marker; 976 if (debug) { 977 System.out.printf(" discard a tag%n"); 978 } 979 td.tag_stack.pop(); 980 } 981 if (debug_tag_frame) { 982 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 983 } 984 } 985 986 /** 987 * Manipulate the tags for an array store instruction. The tag at the top of stack is stored into 988 * the tag storage for the array. Mark the array and the index as comparable. 989 * 990 * @param arr_ref array being accessed 991 * @param length size of the array 992 * @param index index of the array element being accessed 993 */ 994 private static void primitive_array_store(Object arr_ref, int length, int index) { 995 if (debug) { 996 System.out.printf("In primitive_array_store%n"); 997 } 998 999 // This is a helper routine always called as the first step 1000 // so we can set the per-thread data here. 1001 ThreadData td = thread_to_data.get(Thread.currentThread()); 1002 1003 // look for the tag storage for this array 1004 Object[] obj_tags = field_map.get(arr_ref); 1005 1006 // If none has been allocated, allocate the space and associate it with 1007 // the array 1008 if (obj_tags == null) { 1009 obj_tags = new Object[length]; 1010 field_map.put(arr_ref, obj_tags); 1011 } 1012 1013 // Pop the tag off of the stack and assign it into the tag storage for 1014 // this index 1015 assert td.tag_stack.peek() != method_marker; 1016 obj_tags[index] = td.tag_stack.pop(); 1017 if (debug_primitive.enabled()) { 1018 debug_primitive.log("array store %s[%d] = %s%n", obj_str(arr_ref), index, obj_tags[index]); 1019 } 1020 1021 // Mark the arry and its index as comparable 1022 assert td.tag_stack.peek() != method_marker; 1023 Object index_tag = td.tag_stack.pop(); 1024 if (debug_arr_index.enabled()) { 1025 debug_arr_index.log("Merging array '%s' and index '%s'", obj_str(arr_ref), index_tag); 1026 } 1027 if (merge_arrays_and_indices) { 1028 TagEntry.union(arr_ref, index_tag); 1029 } 1030 if (debug_tag_frame) { 1031 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 1032 } 1033 } 1034 1035 /** Execute an aastore instruction and mark the array and its index as comparable. */ 1036 public static void aastore(Object[] arr, int index, Object val) { 1037 1038 ThreadData td = thread_to_data.get(Thread.currentThread()); 1039 // Mark the array and its index as comparable 1040 assert td.tag_stack.peek() != method_marker; 1041 Object index_tag = td.tag_stack.pop(); 1042 debug_arr_index.log("Merging array '%s' and index '%s'", arr, index_tag); 1043 if (merge_arrays_and_indices) { 1044 TagEntry.union(arr, index_tag); 1045 } 1046 1047 // Store the value 1048 arr[index] = val; 1049 if (debug_tag_frame) { 1050 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 1051 } 1052 } 1053 1054 /** 1055 * The JVM uses bastore for both boolean and byte data types. With the addition of StackMap 1056 * verification in Java 7 we can no longer use the same runtime routine for both data types. 1057 * Hence, the addition of zastore below for boolean. 1058 * 1059 * <p>Execute an bastore instruction and manipulate the tags accordingly. The tag at the top of 1060 * stack is stored into the tag storage for the array. 1061 */ 1062 public static void bastore(byte[] arr, int index, byte val) { 1063 1064 // Store the tag for val in the tag storage for array and mark 1065 // the array and the index as comparable. 1066 primitive_array_store(arr, arr.length, index); 1067 1068 // Execute the array store 1069 arr[index] = val; 1070 } 1071 1072 public static void zastore(boolean[] arr, int index, boolean val) { 1073 1074 // Store the tag for val in the tag storage for array and mark 1075 // the array and the index as comparable. 1076 primitive_array_store(arr, arr.length, index); 1077 1078 // Execute the array store 1079 arr[index] = val; 1080 } 1081 1082 /** 1083 * Execute an castore instruction and manipulate the tags accordingly. The tag at the top of stack 1084 * is stored into the tag storage for the array. 1085 */ 1086 public static void castore(char[] arr, int index, char val) { 1087 1088 // Store the tag for val in the tag storage for array and mark 1089 // the array and the index as comparable. 1090 primitive_array_store(arr, arr.length, index); 1091 1092 // Execute the array store 1093 arr[index] = val; 1094 } 1095 1096 /** 1097 * Execute an dastore instruction and manipulate the tags accordingly. The tag at the top of stack 1098 * is stored into the tag storage for the array. 1099 */ 1100 public static void dastore(double[] arr, int index, double val) { 1101 1102 // Store the tag for val in the tag storage for array and mark 1103 // the array and the index as comparable. 1104 primitive_array_store(arr, arr.length, index); 1105 1106 // Execute the array store 1107 arr[index] = val; 1108 } 1109 1110 /** 1111 * Execute an fastore instruction and manipulate the tags accordingly. The tag at the top of stack 1112 * is stored into the tag storage for the array. 1113 */ 1114 public static void fastore(float[] arr, int index, float val) { 1115 1116 // Store the tag for val in the tag storage for array and mark 1117 // the array and the index as comparable. 1118 primitive_array_store(arr, arr.length, index); 1119 1120 // Execute the array store 1121 arr[index] = val; 1122 } 1123 1124 /** 1125 * Execute an iastore instruction and manipulate the tags accordingly. The tag at the top of stack 1126 * is stored into the tag storage for the array. 1127 */ 1128 public static void iastore(int[] arr, int index, int val) { 1129 1130 // Store the tag for val in the tag storage for array and mark 1131 // the array and the index as comparable. 1132 primitive_array_store(arr, arr.length, index); 1133 1134 // Execute the array store 1135 arr[index] = val; 1136 } 1137 1138 /** 1139 * Execute an lastore instruction and manipulate the tags accordingly. The tag at the top of stack 1140 * is stored into the tag storage for the array. 1141 */ 1142 public static void lastore(long[] arr, int index, long val) { 1143 1144 // Store the tag for val in the tag storage for array and mark 1145 // the array and the index as comparable. 1146 primitive_array_store(arr, arr.length, index); 1147 1148 // Execute the array store 1149 arr[index] = val; 1150 } 1151 1152 /** 1153 * Execute an sastore instruction and manipulate the tags accordingly. The tag at the top of stack 1154 * is stored into the tag storage for the array. 1155 */ 1156 public static void sastore(short[] arr, int index, short val) { 1157 1158 // Store the tag for val in the tag storage for array and mark 1159 // the array and the index as comparable. 1160 primitive_array_store(arr, arr.length, index); 1161 1162 // Execute the array store 1163 arr[index] = val; 1164 } 1165 1166 /** 1167 * Make the count arguments to multianewarray comparable to the corresponding array indices. 1168 * count1 is made comparable to the index of the given array (arr), and count2 is made comparable 1169 * to the index of each array that is an element of arr. 1170 * 1171 * @param count1 number items in 1st dimension (unused, associated tag value is on tag stack) 1172 * @param count2 number items in 2nd dimension (unused, associated tag value is on tag stack) 1173 * @param arr the new array 1174 */ 1175 public static void multianewarray2(int count1, int count2, Object[] arr) { 1176 if (debug) { 1177 System.out.printf("In multianewarray2%n"); 1178 } 1179 1180 ThreadData td = thread_to_data.get(Thread.currentThread()); 1181 assert td.tag_stack.peek() != method_marker; 1182 Object count2tag = td.tag_stack.pop(); 1183 assert td.tag_stack.peek() != method_marker; 1184 Object count1tag = td.tag_stack.pop(); 1185 1186 TagEntry.union(count1tag, arr); 1187 1188 for (Object subarr : arr) { 1189 TagEntry.union(count2tag, subarr); 1190 } 1191 if (debug_tag_frame) { 1192 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 1193 } 1194 } 1195 1196 /** 1197 * Called when a user method is entered. Any daikon variables whose current values are comparable 1198 * are marked as comparable. 1199 * 1200 * @param tag_frame tag_frame containing the tags for the primitive arguments of this method 1201 * @param obj value of 'this', or null if the method is static 1202 * @param mi_index index into the list of all methods (methods) 1203 * @param args array of the arguments to the method 1204 */ 1205 public static void enter(Object[] tag_frame, @Nullable Object obj, int mi_index, Object[] args) { 1206 1207 // Don't be recursive 1208 if (in_enter_exit) { 1209 return; 1210 } 1211 in_enter_exit = true; 1212 1213 if (debug) { 1214 Throwable stack = new Throwable("enter"); 1215 StackTraceElement[] ste_arr = stack.getStackTrace(); 1216 StackTraceElement ste = ste_arr[1]; 1217 System.out.printf("%n%s.%s():::ENTER%n", ste.getClassName(), ste.getMethodName()); 1218 System.out.printf("this = %s%nmi = %s%n", obj_str(obj), methods.get(mi_index)); 1219 System.out.printf("args: "); 1220 for (Object arg : args) { 1221 System.out.printf("'%s' ", obj_str(arg)); 1222 } 1223 System.out.println(); 1224 } 1225 1226 MethodInfo mi = methods.get(mi_index); 1227 mi.call_cnt++; 1228 ClassInfo ci = mi.class_info; 1229 if (ci.clazz == null) { 1230 ci.initViaReflection(); 1231 if (debug) { 1232 System.out.printf("DCRuntime.enter adding %s to all class list%n", ci); 1233 } 1234 all_classes.add(ci); 1235 merge_dv.log("initializing traversal for %s%n", ci); 1236 ci.init_traversal(depth); 1237 } 1238 if (mi.traversalEnter == null) { 1239 mi.init_traversal(depth); 1240 } 1241 1242 // Merge comparability information for the Daikon variables 1243 merge_dv.log("processing method %s:::ENTER%n", mi); 1244 merge_dv.indent(); 1245 process_all_vars(mi, mi.traversalEnter, tag_frame, obj, args, null); 1246 merge_dv.exdent(); 1247 1248 in_enter_exit = false; 1249 } 1250 1251 /** 1252 * Called when a user method exits. Any daikon variables whose current values are comparable are 1253 * marked as comparable. 1254 * 1255 * @param tag_frame tag_frame containing the tags for the primitive arguments of this method 1256 * @param obj value of 'this', or null if the method is static 1257 * @param mi_index index into the list of all methods (methods) 1258 * @param args array of the arguments to the method 1259 * @param ret_val value returned by the method, or null if the method is a constructor or void 1260 * @param exit_line_number the source line number of this exit point 1261 */ 1262 public static void exit( 1263 Object[] tag_frame, 1264 @Nullable Object obj, 1265 int mi_index, 1266 Object[] args, 1267 Object ret_val, 1268 int exit_line_number) { 1269 1270 // Don't be recursive 1271 if (in_enter_exit) { 1272 return; 1273 } 1274 in_enter_exit = true; 1275 1276 if (debug) { 1277 Throwable stack = new Throwable("exit"); 1278 StackTraceElement[] ste_arr = stack.getStackTrace(); 1279 StackTraceElement ste = ste_arr[1]; 1280 System.out.printf("%n%s.%s():::EXIT%n", ste.getClassName(), ste.getMethodName()); 1281 System.out.printf("this = %s%nmi = %s%n", obj_str(obj), methods.get(mi_index)); 1282 System.out.printf("args: "); 1283 for (Object arg : args) { 1284 System.out.printf("'%s' ", obj_str(arg)); 1285 } 1286 System.out.println(); 1287 System.out.printf( 1288 "ret_val = %s%nexit_line_number= %d%n%n", obj_str(ret_val), exit_line_number); 1289 } 1290 1291 MethodInfo mi = methods.get(mi_index); 1292 1293 // Merge comparability information for the Daikon variables 1294 merge_dv.log("processing method %s:::EXIT%n", mi); 1295 merge_dv.indent(); 1296 process_all_vars(mi, mi.traversalExit, tag_frame, obj, args, ret_val); 1297 merge_dv.exdent(); 1298 1299 in_enter_exit = false; 1300 } 1301 1302 /** 1303 * Process all of the daikon variables in the tree starting at root. If the values referenced by 1304 * those variables are comparable mark the variables as comparable. 1305 * 1306 * @param mi a MethodInfo 1307 * @param root the daikon variables 1308 * @param tag_frame the tags for the primitive arguments of this method 1309 * @param obj the value of {@code this}, or null if the method is static 1310 * @param args the arguments to the method 1311 * @param ret_val value returned by the method, or null if the method is a constructor or void 1312 */ 1313 public static void process_all_vars( 1314 MethodInfo mi, RootInfo root, Object[] tag_frame, Object obj, Object[] args, Object ret_val) { 1315 1316 debug_timing.log("process_all_vars for %s%n", mi); 1317 1318 merge_dv.log("this: %s%n", obj_str(obj)); 1319 1320 // For some reason the following line causes DynComp to behave incorrectly. 1321 // I have not take the time to investigate. 1322 // merge_dv.log("arguments: %s%n", Arrays.toString(args)); 1323 1324 // Map from an Object to the Daikon variable that currently holds 1325 // that object. 1326 IdentityHashMap<Object, DaikonVariableInfo> varmap = 1327 new IdentityHashMap<Object, DaikonVariableInfo>(2048); 1328 1329 for (DaikonVariableInfo dv : root.children) { 1330 if (dv instanceof ThisObjInfo) { 1331 merge_comparability(varmap, null, obj, dv); 1332 } else if (dv instanceof ParameterInfo) { 1333 ParameterInfo pi = (ParameterInfo) dv; 1334 Object p = args[pi.getArgNum()]; 1335 // Class arg_type = mi.arg_types[pi.getArgNum()]; 1336 // if (arg_type.isPrimitive()) 1337 if (pi.isPrimitive()) { 1338 p = tag_frame[pi.get_param_offset() + ((obj == null) ? 0 : 1)]; 1339 } 1340 merge_comparability(varmap, null, p, pi); 1341 } else if (dv instanceof ReturnInfo) { 1342 if (mi.return_type().isPrimitive()) { 1343 ThreadData td = thread_to_data.get(Thread.currentThread()); 1344 ret_val = td.tag_stack.peek(); 1345 } 1346 merge_comparability(varmap, null, ret_val, dv); 1347 } else if (dv instanceof FieldInfo) { 1348 assert ((FieldInfo) dv).isStatic() : "non static field at root " + dv; 1349 merge_comparability(varmap, null, null, dv); 1350 } else if (dv instanceof StaticObjInfo) { 1351 for (DaikonVariableInfo static_dv : dv.children) { 1352 assert ((FieldInfo) static_dv).isStatic() : "non static field at root " + dv; 1353 merge_comparability(varmap, null, null, static_dv); 1354 } 1355 } else { 1356 throw new Error("unexpected node " + dv); 1357 } 1358 } 1359 debug_timing.log("exit process_all_vars for %s%n%n", mi); 1360 map_info.log("varmap size: %d%n", varmap.size()); 1361 } 1362 1363 /** 1364 * Returns the tag for the specified field. If that field is an array, a list of tags will be 1365 * returned. 1366 * 1367 * @param fi a field 1368 * @param parent object that contains the field (if any) 1369 * @param obj value of the field itself (if available and if it's an object) 1370 * @return the tag for the specified field 1371 */ 1372 static Object get_field_tag(FieldInfo fi, Object parent, Object obj) { 1373 1374 // Initialize the code that gets the tag for various field types 1375 if (fi.field_tag == null) { 1376 if (fi.isStatic()) { 1377 if (fi.isPrimitive()) { 1378 // a static final primitive is a constant and there is no tag accessor 1379 if (fi.isFinal()) { 1380 return null; 1381 } 1382 fi.field_tag = new StaticPrimitiveTag(fi); 1383 } else { 1384 fi.field_tag = new StaticReferenceTag(fi); 1385 } 1386 } else { // not static 1387 if (fi.isPrimitive() && fi.isArray()) { 1388 fi.field_tag = new PrimitiveArrayTag(fi); 1389 } else if (fi.isPrimitive()) { 1390 fi.field_tag = new PrimitiveTag(fi); 1391 } else { 1392 fi.field_tag = new ReferenceTag(fi); 1393 } 1394 } 1395 } 1396 1397 // get the tag 1398 return fi.field_tag.get_tag(parent, obj); 1399 } 1400 1401 /** 1402 * Gets the object in field f in object obj. Exceptions are turned into Errors. 1403 * 1404 * @param f which field to return 1405 * @param obj the object that contains the field 1406 * @return the specified object 1407 */ 1408 public static Object get_object_field(Field f, Object obj) { 1409 if (debug) { 1410 System.out.printf("In get_object_field%n"); 1411 } 1412 try { 1413 return f.get(obj); 1414 } catch (Exception e) { 1415 throw new Error("can't get field " + f + " in " + obj_str(obj), e); 1416 } 1417 } 1418 1419 /** 1420 * Merges the comparability of the daikon variable dv and its children whose current values are 1421 * comparable. 1422 * 1423 * @param varmap map from value set leaders to the first daikon variable encountered with that 1424 * leader. Whenever a second daikon variable is encountered whose value has the same leader, 1425 * that daikon variable is merged with the first daikon variable. 1426 * @param parent value of dv's parent 1427 * @param obj value of dv 1428 * @param dv DaikonVariable to process 1429 */ 1430 static void merge_comparability( 1431 IdentityHashMap<Object, DaikonVariableInfo> varmap, 1432 Object parent, 1433 Object obj, 1434 DaikonVariableInfo dv) { 1435 1436 // merge_dv.enabled = dv.getName().contains ("mtfFreq"); 1437 1438 long start_millis = 0; 1439 if (debug_timing.enabled()) { 1440 start_millis = System.currentTimeMillis(); 1441 } 1442 if (merge_dv.enabled()) { 1443 merge_dv.log("merge_comparability: checking var %s = '%s' %n", dv, obj_str(obj)); 1444 } 1445 1446 // Ignore ClassInfo and StringInfo variables. These are not real 1447 // variables in the program 1448 if ((dv instanceof DaikonClassInfo) || (dv instanceof StringInfo)) { 1449 debug_timing.log(" Variable %s : %d msecs%n", dv, System.currentTimeMillis() - start_millis); 1450 return; 1451 } 1452 1453 // Get the tag for this object. For non-primitives this is normally the 1454 // object itself. For static fields, the object is not passed in, but is 1455 // obtained via reflection. 1456 Object tag = obj; 1457 if (dv instanceof FieldInfo) { 1458 tag = get_field_tag((FieldInfo) dv, parent, obj); 1459 } 1460 1461 if (!dv.declShouldPrint()) { 1462 // do nothing 1463 } else if (dv.isArray() && (tag instanceof List<?>)) { 1464 @SuppressWarnings("unchecked") 1465 List<Object> elements = (List<Object>) tag; 1466 if (debug_timing.enabled()) { 1467 debug_timing.log(" ArrayInfo %d elements", elements.size()); 1468 } 1469 for (Object atag : elements) { 1470 // Ignore null and nonsensical tags. There is no reason to process 1471 // their children, because they can't have any with reasonable values 1472 if ((atag == null) || (atag == nonsensical) || (atag == nonsensical_list)) { 1473 continue; 1474 } 1475 1476 // Look up this object. If it already is associated with a 1477 // DaikonVariable merge those variables. Otherwise, add it to 1478 // the map 1479 Object leader = TagEntry.find(atag); 1480 if (merge_dv.enabled()) { 1481 merge_dv.log("Leader for atag '%s' is '%s'%n", obj_str(atag), obj_str(leader)); 1482 } 1483 DaikonVariableInfo current = varmap.get(leader); 1484 merge_dv.log("Daikon variable for leader = %s%n", current); 1485 if (current != null) { 1486 merge_dv.log("**Merging %s and %s%n", current, dv); 1487 TagEntry.union(current, dv); 1488 } else { 1489 varmap.put(leader, dv); 1490 } 1491 } 1492 } else if (dv.isArray()) { 1493 if (tag == null) { 1494 if (debug_timing.enabled()) { 1495 debug_timing.log( 1496 " no array tags for Variable %s : %d msecs%n", 1497 dv, System.currentTimeMillis() - start_millis); 1498 } 1499 return; 1500 } 1501 Object[] elements = (Object[]) tag; 1502 if (debug_timing.enabled()) { 1503 debug_timing.log(" Prim ArrayInfo %d elements", elements.length); 1504 } 1505 Object prev_tag = null; 1506 for (Object atag : elements) { 1507 // Ignore null and nonsensical tags. There is no reason to process 1508 // their children, because they can't have any with reasonable values 1509 if ((atag == null) || (atag == nonsensical) || (atag == nonsensical_list)) { 1510 continue; 1511 } 1512 1513 // No need to handle the same tag twice 1514 if (prev_tag == atag) { 1515 continue; 1516 } 1517 prev_tag = atag; 1518 1519 // Look up this object. If it already is associated with a 1520 // DaikonVariable merge those variables. Otherwise, add it to 1521 // the map 1522 Object leader = TagEntry.find(atag); 1523 if (merge_dv.enabled()) { 1524 merge_dv.log("Leader for atag '%s' is '%s'%n", obj_str(atag), obj_str(leader)); 1525 } 1526 DaikonVariableInfo current = varmap.get(leader); 1527 merge_dv.log("Daikon variable for leader = %s%n", current); 1528 if (current != null) { 1529 merge_dv.log("**Merging %s and %s%n", current, dv); 1530 TagEntry.union(current, dv); 1531 } else { 1532 varmap.put(leader, dv); 1533 } 1534 } 1535 } else { 1536 // Ignore null and nonsensical tags. There is no reason to process 1537 // their children, because they can't have any with reasonable values 1538 if ((tag == null) || (tag == nonsensical) || (tag == nonsensical_list)) { 1539 debug_timing.log( 1540 " Variable %s : %d msecs%n", dv, System.currentTimeMillis() - start_millis); 1541 return; 1542 } 1543 1544 // Look up this object. If it already is associated with a 1545 // DaikonVariable merge those variables. Otherwise, add it to 1546 // the map 1547 Object leader = TagEntry.find(tag); 1548 if (merge_dv.enabled()) { 1549 merge_dv.log("Leader for tag '%s' is '%s'%n", obj_str(tag), obj_str(leader)); 1550 } 1551 DaikonVariableInfo current = varmap.get(leader); 1552 assert leader != null : "null leader for " + obj_str(tag); 1553 merge_dv.log("Daikon variable for leader = %s%n", current); 1554 if (current != null) { 1555 merge_dv.log("**Merging variable '%s' and '%s'%n", current, dv); 1556 TagEntry.union(current, dv); 1557 } else { 1558 varmap.put(leader, dv); 1559 } 1560 } 1561 1562 debug_timing.log(" Variable %s : %d msecs%n", dv, System.currentTimeMillis() - start_millis); 1563 1564 // Process all of the children 1565 for (DaikonVariableInfo child : dv) { 1566 Object child_obj; 1567 if ((child instanceof ArrayInfo) && ((ArrayInfo) child).getType().isPrimitive()) { 1568 // It's a primitive array. 1569 // System.out.printf("child array type %s = %s%n", ai, ai.getType()); 1570 Object[] arr_tags = field_map.get(tag); 1571 // System.out.printf("found arr_tag %s for arr %s%n", arr_tags, tag); 1572 // System.out.printf("tag values = %s%n", Arrays.toString (arr_tags)); 1573 child_obj = arr_tags; 1574 } else { 1575 // It's not a primitive array. 1576 child_obj = child.getMyValFromParentVal(obj); 1577 } 1578 merge_dv.indent(); 1579 merge_comparability(varmap, obj, child_obj, child); 1580 merge_dv.exdent(); 1581 } 1582 } 1583 1584 /** 1585 * Dumps out comparability information for all classes that were processed. 1586 * 1587 * @param pw PrintWriter to write on 1588 */ 1589 public static void printAllComparable(PrintWriter pw) { 1590 1591 for (ClassInfo ci : all_classes) { 1592 merge_class_comparability(ci); 1593 for (MethodInfo mi : ci.method_infos) { 1594 if (mi.is_class_initializer()) { 1595 continue; 1596 } 1597 // skip our added method 1598 if (mi.method_name.equals("equals_dcomp_instrumented")) { 1599 continue; 1600 } 1601 pw.println(); 1602 printComparable(pw, mi); 1603 } 1604 } 1605 } 1606 1607 /** 1608 * Dumps out comparability trace information for all classes that were processed. 1609 * 1610 * @param pw PrintWriter to write on 1611 */ 1612 public static void traceAllComparable(PrintWriter pw) { 1613 1614 for (ClassInfo ci : all_classes) { 1615 merge_class_comparability(ci); 1616 for (MethodInfo mi : ci.method_infos) { 1617 if (mi.is_class_initializer()) { 1618 continue; 1619 } 1620 if (mi.method_name.equals("equals_dcomp_instrumented")) { 1621 continue; 1622 } 1623 pw.println(); 1624 printComparableTraced(pw, mi); 1625 } 1626 } 1627 } 1628 1629 /** 1630 * Prints header information to the decls file. Should be called once before emitting any other 1631 * declarations. 1632 * 1633 * @param pw where to write output 1634 * @param className name of the top-level class (used only for printing comments) 1635 */ 1636 public static void printHeaderInfo(PrintWriter pw, String className) { 1637 1638 // Write the file header 1639 pw.println("// Declarations for " + className); 1640 pw.println( 1641 "// Declarations written " 1642 + LocalDateTime.now(ZoneId.systemDefault()) 1643 + " by daikon.DynComp"); 1644 pw.println(); 1645 pw.println("decl-version 2.0"); 1646 pw.println("var-comparability implicit"); 1647 pw.println(); 1648 } 1649 1650 /** 1651 * Dumps out .decl file information for all classes that were processed. 1652 * 1653 * @param pw where to write output 1654 */ 1655 public static void printDeclFile(PrintWriter pw) { 1656 1657 // Write the information for each class 1658 for (ClassInfo ci : all_classes) { 1659 if (!headerPrinted) { 1660 printHeaderInfo(pw, ci.class_name); 1661 headerPrinted = true; 1662 } 1663 printClassDecl(pw, ci); 1664 } 1665 debug_decl_print.log("finished %d classes%n", all_classes.size()); 1666 } 1667 1668 static int class_cnt = 0; 1669 static int method_cnt = 0; 1670 static int instance_var_cnt = 0; 1671 static int static_final_cnt = 0; 1672 static int hashcode_var_cnt = 0; 1673 static int hashcode_arr_cnt = 0; 1674 static int primitive_var_cnt = 0; 1675 static int tostring_cnt = 0; 1676 static int class_var_cnt = 0; 1677 static int this_instance_cnt = 0; 1678 static int other_instance_cnt = 0; 1679 static int other_cnt = 0; 1680 static int parameter_cnt = 0; 1681 static int static_cnt = 0; 1682 static int synthetic_cnt = 0; 1683 static int enum_cnt = 0; 1684 1685 // Only called if 'verbose' is true. 1686 /** Prints statistics about the number of decls to stdout. */ 1687 public static void decl_stats() { 1688 1689 for (ClassInfo ci : all_classes) { 1690 class_cnt++; 1691 System.out.printf("processing class %s%n", ci); 1692 add_dv_stats(ci.traversalClass); 1693 add_dv_stats(ci.traversalObject); 1694 for (MethodInfo mi : ci.method_infos) { 1695 if (mi.is_class_initializer()) { 1696 continue; 1697 } 1698 method_cnt++; 1699 System.out.printf(" Processing method %s [%d calls]%n", mi, mi.call_cnt); 1700 if (mi.traversalEnter == null) { 1701 System.out.printf(" Skipping method %s%n", mi); 1702 continue; 1703 } 1704 System.out.printf(" Enter%n"); 1705 add_dv_stats(mi.traversalEnter); 1706 for (Integer ii : mi.exit_locations) { 1707 System.out.printf(" Exit%d%n", ii); 1708 add_dv_stats(mi.traversalExit); 1709 } 1710 } 1711 } 1712 1713 // The section above output method count data for non-omitted user code. 1714 // The section below outputs method count data for all methods - including java runtime. 1715 System.out.printf("%ndisplaying all method counts%n"); 1716 for (String key : methodCountMap.keySet()) { 1717 System.out.printf(" method %s [%d calls]%n", key, methodCountMap.get(key)); 1718 } 1719 System.out.println(); 1720 1721 System.out.printf("Classes = %,d%n", class_cnt); 1722 System.out.printf("Methods = %,d%n", method_cnt); 1723 System.out.printf("------------------------------%n"); 1724 System.out.printf("Hashcodes = %,d%n", hashcode_var_cnt); 1725 System.out.printf("Hashcode arrays = %,d%n", hashcode_arr_cnt); 1726 System.out.printf("primitives = %,d%n", primitive_var_cnt); 1727 System.out.printf("------------------------------%n"); 1728 System.out.printf("tostring vars = %,d%n", tostring_cnt); 1729 System.out.printf("class vars = %,d%n", class_var_cnt); 1730 System.out.printf("Enums = %,d%n", enum_cnt); 1731 System.out.printf("Synthetic = %,d%n", synthetic_cnt); 1732 System.out.printf("static final vars = %,d%n", static_final_cnt); 1733 System.out.printf("static vars = %,d%n", static_cnt); 1734 System.out.printf("this instance vars = %,d%n", this_instance_cnt); 1735 System.out.printf("other instance vars = %,d%n", other_instance_cnt); 1736 System.out.printf("Parameters = %,d%n", parameter_cnt); 1737 System.out.printf("Others = %,d%n", other_cnt); 1738 } 1739 1740 private static void add_dv_stats(RootInfo root) { 1741 1742 if (root == null) { 1743 return; 1744 } 1745 List<DaikonVariableInfo> dv_list = root.tree_as_list(); 1746 for (DaikonVariableInfo dv : dv_list) { 1747 if (dv instanceof RootInfo) { 1748 continue; 1749 } 1750 if (!dv.declShouldPrint()) { 1751 continue; 1752 } 1753 // System.out.printf(" processing dv %s [%s]%n", dv, 1754 // dv.getTypeName()); 1755 if (dv.isHashcode()) { 1756 hashcode_var_cnt++; 1757 } else if (dv.isHashcodeArray()) { 1758 hashcode_arr_cnt++; 1759 } else { 1760 primitive_var_cnt++; 1761 } 1762 if (dv.getName().contains(".toString")) { 1763 tostring_cnt++; 1764 } else if (dv.getName().contains(DaikonVariableInfo.class_suffix)) { 1765 class_var_cnt++; 1766 } else if (dv instanceof FieldInfo) { 1767 Field field = ((FieldInfo) dv).getField(); 1768 int modifiers = field.getModifiers(); 1769 if (field.isEnumConstant()) { 1770 enum_cnt++; 1771 } else if (field.isSynthetic()) synthetic_cnt++; 1772 else if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) static_final_cnt++; 1773 else if (Modifier.isStatic(modifiers)) static_cnt++; 1774 else if (dv.getName().startsWith("this")) this_instance_cnt++; 1775 else { 1776 other_instance_cnt++; 1777 } 1778 } else if (dv instanceof ParameterInfo) { 1779 parameter_cnt++; 1780 } else { 1781 other_cnt++; 1782 } 1783 } 1784 } 1785 1786 /** 1787 * Calculates and prints the declarations for the specified class. 1788 * 1789 * @param pw where to produce output 1790 * @param ci the class to write 1791 */ 1792 public static void printClassDecl(PrintWriter pw, ClassInfo ci) { 1793 1794 time_decl.log("Printing decl file for class %s%n", ci.class_name); 1795 time_decl.indent(); 1796 debug_decl_print.log("class %s%n", ci.class_name); 1797 1798 // Make sure that two variables have the same comparability at all 1799 // program points 1800 merge_class_comparability(ci); 1801 1802 // Write the class ppt 1803 String classPptName = ci.class_name + ":::CLASS"; 1804 pw.println("ppt " + classPptName); 1805 pw.println("ppt-type class"); 1806 printDeclVars(get_comparable(ci.traversalClass), ci.traversalClass, classPptName); 1807 pw.println(); 1808 time_decl.log("printed class ppt"); 1809 1810 // Write the object ppt 1811 String objectPptName = ci.class_name + ":::OBJECT"; 1812 pw.println("ppt " + objectPptName); 1813 pw.println("ppt-type object"); 1814 printDeclVars(get_comparable(ci.traversalObject), ci.traversalObject, objectPptName); 1815 pw.println(); 1816 time_decl.log("printed object ppt"); 1817 1818 // Print the information for each enter/exit point 1819 for (MethodInfo mi : ci.method_infos) { 1820 if (mi.is_class_initializer()) { 1821 continue; 1822 } 1823 debug_decl_print.log(" method %s%n", mi.method_name); 1824 printMethod(pw, mi); 1825 } 1826 1827 time_decl.log("finished class %s%n", ci.class_name); 1828 time_decl.exdent(); 1829 } 1830 1831 static long comp_list_ms = 0; 1832 static long ppt_name_ms = 0; 1833 static long decl_vars_ms = 0; 1834 static long total_ms = 0; 1835 1836 // static Stopwatch watch = new Stopwatch(); 1837 1838 /** 1839 * Prints a decl ENTER/EXIT records with comparability. Returns the list of comparabile DVSets for 1840 * the exit. 1841 * 1842 * @param pw where to produce output 1843 * @param mi the class to output 1844 * @return the list of comparabile DVSets for the exit 1845 */ 1846 public static List<DVSet> printMethod(PrintWriter pw, MethodInfo mi) { 1847 1848 // long start = System.currentTimeMillis(); 1849 // watch.reset(); 1850 1851 time_decl.log("Print decls for method '%s'", mi.method_name); 1852 time_decl.indent(); 1853 List<DVSet> l = get_comparable(mi.traversalEnter); 1854 // comp_list_ms += watch.snapshot(); watch.reset(); 1855 if (l == null) { 1856 return null; 1857 } 1858 time_decl.log("got %d comparable sets", l.size()); 1859 1860 // Print the enter point 1861 String enterPptName = clean_decl_name(DaikonWriter.methodEntryName(mi.member)); 1862 pw.println("ppt " + enterPptName); 1863 pw.println("ppt-type enter"); 1864 // ppt_name_ms += watch.snapshot(); watch.reset(); 1865 printDeclVars(l, mi.traversalEnter, enterPptName); 1866 // decl_vars_ms += watch.snapshot(); watch.reset(); 1867 pw.println(); 1868 time_decl.log("after enter"); 1869 1870 // Print the exit points 1871 l = get_comparable(mi.traversalExit); 1872 // comp_list_ms += watch.snapshot(); watch.reset(); 1873 1874 time_decl.log("got exit comparable sets"); 1875 for (Integer ii : mi.exit_locations) { 1876 String exitPptName = clean_decl_name(DaikonWriter.methodExitName(mi.member, ii)); 1877 pw.println("ppt " + exitPptName); 1878 pw.println("ppt-type subexit"); 1879 // ppt_name_ms += watch.snapshot(); watch.reset(); 1880 1881 time_decl.log("after exit clean_decl_name"); 1882 printDeclVars(l, mi.traversalExit, exitPptName); 1883 pw.println(); 1884 // decl_vars_ms += watch.snapshot(); watch.reset(); 1885 1886 } 1887 1888 // total_ms += System.currentTimeMillis() - start; 1889 time_decl.log("Finished processing method '%s'", mi.method_name); 1890 time_decl.exdent(); 1891 return l; 1892 } 1893 1894 /** Map from array name to comparability for its indices (if any). */ 1895 private static Map<String, Integer> arr_index_map; 1896 1897 /** Map from variable to its comparability. */ 1898 private static IdentityHashMap<DaikonVariableInfo, Integer> dv_comp_map; 1899 1900 /** Comparability value for a variable. */ 1901 private static int base_comp; 1902 1903 /** 1904 * Print the variables in sets to pw in DECL file format. Each variable in the same set is given 1905 * the same comparability. Constructed classname variables are made comparable to other classname 1906 * variables only. 1907 * 1908 * @param sets the comparability sets 1909 * @param dv_tree the tree of variables 1910 * @param pptName used only for debugging output 1911 */ 1912 private static void printDeclVars(List<DVSet> sets, RootInfo dv_tree, String pptName) { 1913 1914 time_decl.indent(); 1915 time_decl.log("printDeclVars start"); 1916 1917 debug_decl_print.log("printDeclVars(%s)%n", pptName); 1918 1919 // Map from array name to comparability for its indices (if any) 1920 arr_index_map = new LinkedHashMap<>(); 1921 1922 // Map from daikon variable to its comparability 1923 dv_comp_map = new IdentityHashMap<DaikonVariableInfo, Integer>(256); 1924 1925 // Initial comparability values 1926 int class_comp = 1; 1927 base_comp = 2; 1928 1929 // Loop through each set of comparable variables 1930 for (DVSet set : sets) { 1931 1932 if ((set.size() == 1) && (set.get(0) instanceof StaticObjInfo)) { 1933 continue; 1934 } 1935 1936 // Determine if the set has both hashcode variables and integer 1937 // variables. If it does, it is indicating index comparability 1938 boolean hashcode_vars = false; 1939 boolean non_hashcode_vars = false; 1940 // System.out.printf("Checking dv set %s%n", set); 1941 for (DaikonVariableInfo dv : set) { 1942 if (dv.isHashcode() || dv.isHashcodeArray()) { 1943 hashcode_vars = true; 1944 } else { 1945 non_hashcode_vars = true; 1946 } 1947 // System.out.printf("dv = %s, hashcode_var = %b%n", 1948 // dv, dv.isHashcode() || dv.isHashcodeArray()); 1949 } 1950 debug_decl_print.log( 1951 " %d vars in set, hashcode/non = %b/%b%n", 1952 set.size(), hashcode_vars, non_hashcode_vars); 1953 1954 // Loop through each variable and assign its comparability 1955 // Since hashcodes and their indices are in the same set, assign 1956 // hashcodes one higher comparability number. Note that there is 1957 // not necessarily an array child for a hashcode that is comparable 1958 // to an integer. This can happen when an array and a non-array object 1959 // become comparable to one another. An integer that is comparable 1960 // to the array will also be comparable to the non-array object, but 1961 // that comparability isn't interesting (and it can't be expressed) 1962 for (DaikonVariableInfo dv : set) { 1963 debug_decl_print.log(" dv %s [%s]%n", dv, System.identityHashCode(dv)); 1964 if (dv instanceof DaikonClassInfo) { 1965 dv_comp_map.put(dv, class_comp); 1966 assert set.size() == 1 : "odd set " + set; 1967 base_comp--; // negate increment of base_comp below 1968 } else if (dv.isHashcode() && non_hashcode_vars) { 1969 assert !dv_comp_map.containsKey(dv) : dv + " " + base_comp; 1970 dv_comp_map.put(dv, base_comp + 1); 1971 DaikonVariableInfo array_child = dv.array_child(); 1972 if (array_child != null) { 1973 // System.out.printf("array_index_map put: %s, %d%n", array_child.getName(), 1974 // base_comp); 1975 arr_index_map.put(array_child.getName(), base_comp); 1976 } 1977 } else { 1978 assert !dv_comp_map.containsKey(dv) : dv + " " + base_comp; 1979 dv_comp_map.put(dv, base_comp); 1980 } 1981 } 1982 1983 // Increment the comparability number to the next valid number 1984 base_comp++; 1985 if (hashcode_vars && non_hashcode_vars) { 1986 base_comp++; 1987 } 1988 } 1989 1990 time_decl.log("finished filling maps%n"); 1991 1992 // Loop through each variable and print out its comparability 1993 // Use the dv_tree rather than sets so that we print out in the 1994 // same order each time. Note that arrays get two comparablities, one 1995 // for the elements of the array and one for indices. Normally, these 1996 // comparabilities will be different, but if an index is placed in the 1997 // array the comparabilities can be the same. 1998 List<DaikonVariableInfo> dv_list = dv_tree.tree_as_list(); 1999 time_decl.log("built tree as list with %d elements", dv_list.size()); 2000 for (DaikonVariableInfo dv : dv_list) { 2001 if ((dv instanceof RootInfo) || (dv instanceof StaticObjInfo) || !dv.declShouldPrint()) { 2002 continue; 2003 } 2004 2005 declWriter.printDecl(null, dv, null, comparabilityProvider); 2006 } 2007 2008 time_decl.log("printDeclVars end%n"); 2009 map_info.log("dv_comp_map size: %d%n", dv_comp_map.size()); 2010 time_decl.exdent(); 2011 } 2012 2013 /** 2014 * Calculates a comparability value. 2015 * 2016 * @param dv variable to calculate comparability for 2017 * @param compare_ppt (not used) 2018 * @return string containing comparability value 2019 */ 2020 @Override 2021 public String getComparability(DaikonVariableInfo dv, DeclReader.DeclPpt compare_ppt) { 2022 int comp = dv_comp_map.get(dv); 2023 String comp_str = Integer.toString(comp); 2024 if (dv.isArray()) { 2025 String name = dv.getName(); 2026 // If we an array of CLASSNAME or TO_STRING get the index 2027 // comparability from the base array. 2028 if (name.endsWith(DaikonVariableInfo.class_suffix)) { 2029 name = name.substring(0, name.length() - DaikonVariableInfo.class_suffix.length()); 2030 } else if (name.endsWith(".toString")) { 2031 name = name.substring(0, name.length() - ".toString".length()); 2032 } 2033 Integer index_comp = arr_index_map.get(name); 2034 // System.out.printf("compare: %d [ %s ] ", comp, index_comp); 2035 if (index_comp != null) { 2036 // System.out.println(comp + "[" + index_comp + "]"); 2037 comp_str = comp_str + "[" + index_comp + "]"; 2038 } else { 2039 // There is no index comparability, so just set it to a unique value. 2040 // System.out.println(comp + "[" + base_comp + "]"); 2041 comp_str = comp_str + "[" + base_comp++ + "]"; 2042 } 2043 } 2044 return comp_str; 2045 } 2046 2047 /** 2048 * Prints comparability information for the enter and exit points of the specified method. By 2049 * default, outputs to {@code foo.txt-cset}. 2050 * 2051 * @param pw where to produce output 2052 * @param mi the method whose comparability to output 2053 */ 2054 /* TO DO: Find a way to make this work correctly without using normal 2055 * get_comparable. 2056 */ 2057 public static void printComparable(PrintWriter pw, MethodInfo mi) { 2058 2059 List<DVSet> l = get_comparable(mi.traversalEnter); 2060 pw.printf("Variable sets for %s enter%n", clean_decl_name(mi.toString())); 2061 if (l == null) { 2062 pw.printf(" not called%n"); 2063 } else { 2064 for (DVSet set : l) { 2065 if ((set.size() == 1) && (set.get(0) instanceof StaticObjInfo)) { 2066 continue; 2067 } 2068 List<String> stuff = skinyOutput(set, daikon.DynComp.abridged_vars); 2069 // To see "daikon.chicory.FooInfo:variable", change true to false 2070 pw.printf(" [%d] %s%n", stuff.size(), stuff); 2071 } 2072 } 2073 2074 l = get_comparable(mi.traversalExit); 2075 pw.printf("Variable sets for %s exit%n", clean_decl_name(mi.toString())); 2076 if (l == null) { 2077 pw.printf(" not called%n"); 2078 } else { 2079 for (DVSet set : l) { 2080 if ((set.size() == 1) && (set.get(0) instanceof StaticObjInfo)) { 2081 continue; 2082 } 2083 List<String> stuff = skinyOutput(set, daikon.DynComp.abridged_vars); 2084 // To see "daikon.chicory.FooInfo:variable", change true to false 2085 pw.printf(" [%d] %s%n", stuff.size(), stuff); 2086 } 2087 } 2088 } 2089 2090 /** 2091 * Dumps out comparability trace information for a single method. 2092 * 2093 * @param pw where to write output 2094 * @param mi the method to process 2095 */ 2096 public static void printComparableTraced(PrintWriter pw, MethodInfo mi) { 2097 List<DVSet> l = get_comparable(mi.traversalEnter); 2098 Map<DaikonVariableInfo, DVSet> t = get_comparable_traced(mi.traversalEnter); 2099 pw.printf("DynComp Traced Tree for %s enter%n", clean_decl_name(mi.toString())); 2100 if (t == null) { 2101 pw.printf(" not called%n"); 2102 } else { 2103 for (DVSet set : l) { 2104 if ((set.size() == 1) && (set.get(0) instanceof StaticObjInfo)) { 2105 continue; 2106 } 2107 printTree(pw, t, (DaikonVariableInfo) TagEntry.troot_find(set.get(0)), 0); 2108 pw.println(); 2109 } 2110 } 2111 pw.println(); 2112 2113 l = get_comparable(mi.traversalExit); 2114 t = get_comparable_traced(mi.traversalExit); 2115 pw.printf("DynComp Traced Tree for %s exit%n", clean_decl_name(mi.toString())); 2116 if (t == null) { 2117 pw.printf(" not called%n"); 2118 } else { 2119 for (DVSet set : l) { 2120 if ((set.size() == 1) && (set.get(0) instanceof StaticObjInfo)) { 2121 continue; 2122 } 2123 printTree(pw, t, (DaikonVariableInfo) TagEntry.troot_find(set.get(0)), 0); 2124 pw.println(); 2125 } 2126 } 2127 pw.println(); 2128 } 2129 2130 /** 2131 * Prints to [stream] the segment of the tree that starts at [node], interpreting [node] as 2132 * [depth] steps from the root. Requires a Map [tree] that represents a tree though key-value sets 2133 * of the form {@code <}parent, set of children{@code >}. 2134 * 2135 * @param pw where to write output 2136 * @param tree map parents to children 2137 * @param node starting point of tree to print 2138 * @param depth distance from node to root of tree 2139 */ 2140 static void printTree( 2141 PrintWriter pw, Map<DaikonVariableInfo, DVSet> tree, DaikonVariableInfo node, int depth) { 2142 2143 /* This method, for some reason, triggers a segfault due to the way 2144 * DVSets are handled conceptually. A trace-tree of one element creates 2145 * a key-value pair DVI foo → DVSet {foo}, whereas a trace-tree of 2146 * two elements creates a key-value pair DVI foo → DVSet {bar}. 2147 */ 2148 2149 if (depth == 0) { 2150 pw.printf("%s%n", skinyOutput(node, daikon.DynComp.abridged_vars)); 2151 if (tree.get(node) == null) { 2152 return; 2153 } 2154 for (DaikonVariableInfo child : tree.get(node)) { 2155 if (child != node) { 2156 printTree(pw, tree, child, depth + 1); 2157 } 2158 } 2159 } else { 2160 for (int i = 0; i < depth; i++) { 2161 pw.printf("--"); 2162 } 2163 pw.printf( 2164 "%s (%s)%n", 2165 skinyOutput(node, daikon.DynComp.abridged_vars), TagEntry.get_line_trace(node)); 2166 if (tree.get(node) == null) { 2167 return; 2168 } 2169 for (DaikonVariableInfo child : tree.get(node)) { 2170 if (child != node) { 2171 printTree(pw, tree, child, depth + 1); 2172 } 2173 } 2174 } 2175 } 2176 2177 /** 2178 * If on, returns an ArrayList of Strings that converts the usual DVInfo.toString() output to a 2179 * more readable form 2180 * 2181 * <p>e.g. "daikon.chicory.ParameterInfo:foo" becomes "Parameter foo" 2182 * 2183 * <p>"daikon.chicory.FieldInfo:this.foo" becomes "Field foo" 2184 * 2185 * @param l a DVSet 2186 * @param on value of daikon.Daikon.abridger_vars 2187 * @return a readable version of {@code l} 2188 */ 2189 private static List<String> skinyOutput(DVSet l, boolean on) { 2190 List<String> o = new ArrayList<>(); 2191 for (DaikonVariableInfo dvi : l) { 2192 o.add(skinyOutput(dvi, on)); 2193 } 2194 return o; 2195 } 2196 2197 private static String skinyOutput(DaikonVariableInfo dv, boolean on) { 2198 if (!on) { 2199 return dv.toString(); 2200 } 2201 String dvtxt = dv.toString(); 2202 String type = dvtxt.split(":")[0]; 2203 type = type.substring(type.lastIndexOf(".") + 1); 2204 String name = dvtxt.split(":")[1]; 2205 if (type.equals("ThisObjInfo")) { 2206 dvtxt = "this"; 2207 } else if (type.equals("ReturnInfo")) { 2208 dvtxt = "return"; 2209 } else if (type.endsWith("Info")) { 2210 type = type.substring(0, type.length() - 4); 2211 if (name.endsWith(DaikonVariableInfo.class_suffix)) { 2212 name = name.substring(0, name.length() - DaikonVariableInfo.class_suffix.length()); 2213 type = "Class of"; 2214 } 2215 if (name.startsWith("this.")) { 2216 name = name.substring(5); 2217 if (!type.endsWith("Field")) { 2218 type = (type + " Field").trim(); 2219 } 2220 } 2221 dvtxt = type + " " + name; 2222 } 2223 return dvtxt; 2224 } 2225 2226 /** Set of Daikon variables. Implements comparable on first DaikonVariable in each set. */ 2227 private static class DVSet extends ArrayList<DaikonVariableInfo> implements Comparable<DVSet> { 2228 static final long serialVersionUID = 20050923L; 2229 2230 @Pure 2231 @Override 2232 public int compareTo(@GuardSatisfied DVSet this, DVSet s1) { 2233 if (s1.size() == 0) { 2234 return 1; 2235 } else if (size() == 0) { 2236 return -1; 2237 } else { 2238 return this.get(0).compareTo(s1.get(0)); 2239 } 2240 } 2241 2242 public void sort() { 2243 Collections.sort(this); 2244 } 2245 } 2246 2247 /** 2248 * Gets a list of comparability sets of Daikon variables. If the method has never been executed 2249 * returns null (it would probably be better to return each variable in a separate set, but I 2250 * wanted to differentiate this case for now). 2251 * 2252 * <p>The sets are calculated by processing each daikon variable and adding it to a list 2253 * associated with the leader of that set. 2254 */ 2255 static @Nullable List<DVSet> get_comparable(RootInfo root) { 2256 2257 if (root == null) { 2258 return null; 2259 } 2260 2261 // List of all of the sets of comparable daikon variables 2262 IdentityHashMap<DaikonVariableInfo, DVSet> sets = 2263 new IdentityHashMap<DaikonVariableInfo, DVSet>(); 2264 2265 for (DaikonVariableInfo dv : root) { 2266 add_variable(sets, dv); 2267 } 2268 map_info.log("sets size: %d%n", sets.size()); 2269 2270 // Get each set, sort it, and add it to the list of all sets. Then sort 2271 // the list of all sets. The sorting is not critical except to create 2272 // a reproducible order. 2273 List<DVSet> set_list = new ArrayList<>(sets.size()); 2274 for (DVSet dvs : sets.values()) { 2275 dvs.sort(); 2276 set_list.add(dvs); 2277 } 2278 Collections.sort(set_list); 2279 2280 return set_list; 2281 } 2282 2283 /** 2284 * Returns a map representing the tree of tracers. Represents the tree as entries in a map with 2285 * each parent node as the key to a set contains all its children. The parameter RootInfo node is 2286 * included as a key to all its children. 2287 */ 2288 static @PolyNull Map<DaikonVariableInfo, DVSet> get_comparable_traced(@PolyNull RootInfo root) { 2289 if (root == null) { 2290 return null; 2291 } 2292 2293 // List of all of the parent-child relationships, where parent-child 2294 // represents the equivalence relation of being comparable. 2295 // The keyset of this Map is exactly the RootInfo node and the set of all 2296 // nodes that have children. 2297 // The valueset of this Map is exactly the set of all nodes. 2298 IdentityHashMap<DaikonVariableInfo, DVSet> sets = 2299 new IdentityHashMap<DaikonVariableInfo, DVSet>(256); 2300 2301 for (DaikonVariableInfo child : root) { 2302 if (child.declShouldPrint()) { 2303 add_variable_traced(sets, child); 2304 } 2305 } 2306 for (DVSet dvs : sets.values()) { 2307 dvs.sort(); 2308 } 2309 2310 return sets; 2311 } 2312 2313 static void add_variable_traced(Map<DaikonVariableInfo, DVSet> sets, DaikonVariableInfo dv) { 2314 try { 2315 DaikonVariableInfo parent = (DaikonVariableInfo) TagEntry.tracer_find(dv); 2316 DVSet set = sets.computeIfAbsent(parent, __ -> new DVSet()); 2317 set.add(dv); 2318 } catch (NullPointerException e) { 2319 throw new Error(e); 2320 } 2321 2322 for (DaikonVariableInfo child : dv) { 2323 add_variable_traced(sets, child); 2324 } 2325 } 2326 2327 /** 2328 * Merges comparability so that the same variable has the same comparability at all points in the 2329 * program point hierarchy. The comparability at the class/object points is calculated by merging 2330 * the comparability at each exit point (i.e., if two variables are in the same set at any exit 2331 * point, they are in the same set at the class point). That comparability is then applied back to 2332 * the exit points so that if two class variables are comparable at any exit point they are 2333 * comparable at each exit point. Finally exit point comparability is merged to the enter point so 2334 * that their comparabilities are the same. 2335 * 2336 * <p>This is not the only valid definition of comparability but it is the one that Daikon expects 2337 * because of how equality sets are handled. 2338 */ 2339 static void merge_class_comparability(ClassInfo ci) { 2340 2341 // Get the variables at the object and class point 2342 assert ci.traversalObject != null : ci; 2343 assert ci.traversalClass != null : ci; 2344 // ci.init_traversal (depth); 2345 2346 // If any methods have not been executed, create their information 2347 // now (which will note all of their variables as not comparable) 2348 for (MethodInfo mi : ci.method_infos) { 2349 if (mi.is_class_initializer()) { 2350 continue; 2351 } 2352 if (mi.traversalEnter == null) { 2353 // mi.initViaReflection(); 2354 mi.init_traversal(depth); 2355 // System.out.printf("Warning: Method %s never executed%n", mi); 2356 } 2357 } 2358 2359 // Merge the comparability from each exit point into the object point 2360 for (MethodInfo mi : ci.method_infos) { 2361 if (mi.is_class_initializer()) { 2362 continue; 2363 } 2364 merge_dv_comparability(mi.traversalExit, mi.traversalEnter, "Merging exit to enter: " + mi); 2365 merge_dv_comparability(mi.traversalExit, ci.traversalObject, "Merging exit to object: " + mi); 2366 merge_dv_comparability(mi.traversalEnter, ci.traversalObject, "Merging enter to object" + mi); 2367 } 2368 2369 // Merge the comparability from the object point back to each exit point 2370 for (MethodInfo mi : ci.method_infos) { 2371 if (mi.is_class_initializer()) { 2372 continue; 2373 } 2374 merge_dv_comparability(ci.traversalObject, mi.traversalExit, "Merging object to exit: " + mi); 2375 } 2376 2377 // Merge the comparability for each exit point back to the enter 2378 for (MethodInfo mi : ci.method_infos) { 2379 if (mi.is_class_initializer()) { 2380 continue; 2381 } 2382 merge_dv_comparability(mi.traversalExit, mi.traversalEnter, "Merging exit to enter: " + mi); 2383 } 2384 2385 // Merge the object comparability to the class 2386 merge_dv_comparability(ci.traversalObject, ci.traversalClass, "Merging object to class: " + ci); 2387 } 2388 2389 /** 2390 * Merges any variables in the dest tree that are in the same set in the source tree. The source 2391 * tree's comparability is unchanged. Variables are identified by name. 2392 * 2393 * @param src the comparability to read 2394 * @param dest the comparability to modify 2395 * @param debuginfo information about this method call, for debugging 2396 */ 2397 static void merge_dv_comparability(RootInfo src, RootInfo dest, String debuginfo) { 2398 2399 debug_merge_comp.log("merge_dv_comparability: %s%n", debuginfo); 2400 2401 debug_merge_comp.indent(); 2402 2403 // Create a map relating destination names to their variables 2404 Map<String, DaikonVariableInfo> dest_map = new LinkedHashMap<>(); 2405 for (DaikonVariableInfo dest_var : varlist(dest)) { 2406 dest_map.put(dest_var.getName(), dest_var); 2407 } 2408 2409 // Get the variable sets for the source 2410 List<DVSet> src_sets = get_comparable(src); 2411 2412 // Merge any destination variables that are in the same source set 2413 for (DVSet src_set : src_sets) { 2414 if (src_set.size() == 1) { 2415 continue; 2416 } 2417 DaikonVariableInfo dest_canonical = null; 2418 for (DaikonVariableInfo src_var : src_set) { 2419 if (dest_canonical == null) { 2420 dest_canonical = dest_map.get(src_var.getName()); 2421 continue; 2422 } 2423 DaikonVariableInfo dest_var = dest_map.get(src_var.getName()); 2424 if (dest_var != null) { 2425 TagEntry.union(dest_canonical, dest_var); 2426 debug_merge_comp.log( 2427 "merged '%s' [%s] and '%s' [%s]%n", 2428 dest_canonical, 2429 System.identityHashCode(dest_canonical), 2430 dest_var, 2431 System.identityHashCode(dest_var)); 2432 } 2433 } 2434 } 2435 debug_merge_comp.exdent(); 2436 } 2437 2438 /** 2439 * Adds this daikon variable and all of its children into their appropriate sets (those of their 2440 * leader) in sets. 2441 */ 2442 static void add_variable(Map<DaikonVariableInfo, DVSet> sets, DaikonVariableInfo dv) { 2443 2444 // Add this variable into the set of its leader 2445 if (dv.declShouldPrint()) { 2446 DaikonVariableInfo leader = (DaikonVariableInfo) TagEntry.find(dv); 2447 DVSet set = sets.computeIfAbsent(leader, __ -> new DVSet()); 2448 set.add(dv); 2449 } 2450 2451 // Process the children 2452 for (DaikonVariableInfo child : dv) { 2453 add_variable(sets, child); 2454 } 2455 } 2456 2457 /** 2458 * Pushes the tag associated with field_num in obj on the tag stack. A tag value must have been 2459 * previously stored for this field. 2460 * 2461 * @param obj where to store tag 2462 * @param field_num which field within obj to store into 2463 */ 2464 public static void push_field_tag(Object obj, int field_num) { 2465 if (debug) { 2466 System.out.printf("In push_field_tag%n"); 2467 } 2468 // Since instance variables by default initialize to zero, any field 2469 // can possibly be read before it is set. 2470 push_field_tag_null_ok(obj, field_num); 2471 } 2472 2473 /** 2474 * Pushes the tag associated with field_num in obj on the tag stack. If tag storage for this 2475 * object has not been previously allocated it is allocated now and a tag is allocated for this 2476 * field. This should only be called for objects whose fields can be read without having been 2477 * previously written (in Java). 2478 * 2479 * @param obj where to store tag 2480 * @param field_num which field within obj to store into 2481 */ 2482 public static void push_field_tag_null_ok(Object obj, int field_num) { 2483 if (debug) { 2484 System.out.printf("In push_field_tag_null_ok%n"); 2485 } 2486 2487 ThreadData td = thread_to_data.get(Thread.currentThread()); 2488 Object[] obj_tags = field_map.get(obj); 2489 if (obj_tags != null) { 2490 Object tag = obj_tags[field_num]; 2491 if (tag == null) { 2492 Throwable stack_trace = new Throwable(); 2493 obj_tags[field_num] = 2494 tag = 2495 new UninitFieldTag( 2496 obj.getClass().getName() + ":uninit-field:" + field_num, stack_trace); 2497 } 2498 td.tag_stack.push(tag); 2499 if (debug_primitive.enabled()) { 2500 debug_primitive.log( 2501 "push_field_tag %s %d = %s%n", obj_str(obj), field_num, obj_tags[field_num]); 2502 } 2503 } else { 2504 int fcnt = num_prim_fields(obj.getClass()); 2505 assert field_num < fcnt : obj.getClass() + " " + field_num + " " + fcnt; 2506 obj_tags = new Object[fcnt]; 2507 field_map.put(obj, obj_tags); 2508 debug_primitive.log("push_field_tag: Created tag storage%n"); 2509 Throwable stack_trace = new Throwable(); 2510 Object tag = 2511 new UninitFieldTag(obj.getClass().getName() + ":uninit-field" + field_num, stack_trace); 2512 obj_tags[field_num] = tag; 2513 td.tag_stack.push(tag); 2514 if (debug_primitive.enabled()) { 2515 debug_primitive.log("push_field_tag %s %d = %s%n", obj_str(obj), field_num, tag); 2516 } 2517 } 2518 if (debug_tag_frame) { 2519 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2520 } 2521 } 2522 2523 /** 2524 * Pops the tag from the top of the tag stack and stores it in the tag storage for the specified 2525 * field of the specified object. If tag storage was not previously allocated, it is allocated 2526 * now. 2527 * 2528 * @param obj where to store tag 2529 * @param field_num which field within obj to store into 2530 */ 2531 public static void pop_field_tag(Object obj, int field_num) { 2532 if (debug) { 2533 System.out.printf("In pop_field_tag%n"); 2534 } 2535 2536 ThreadData td = thread_to_data.get(Thread.currentThread()); 2537 // Look for the tag storage for this object 2538 Object[] obj_tags = field_map.get(obj); 2539 2540 // If none has been allocated, determine how many locations are 2541 // required (the number of primitive fields), allocate the space, 2542 // and associate it with the object. 2543 if (obj_tags == null) { 2544 int fcnt = num_prim_fields(obj.getClass()); 2545 assert field_num < fcnt : obj.getClass() + " " + field_num + " " + fcnt; 2546 obj_tags = new Object[fcnt]; 2547 field_map.put(obj, obj_tags); 2548 debug_primitive.log("pop_field_tag: Created tag storage%n"); 2549 } 2550 2551 // Pop the tag off of the stack and assign into the tag storage for 2552 // this field. 2553 assert td.tag_stack.peek() != method_marker; 2554 Object tag = td.tag_stack.pop(); 2555 assert tag != null : "Object " + obj.getClass() + " '" + obj + "' field_num " + field_num; 2556 obj_tags[field_num] = tag; 2557 if (debug_primitive.enabled()) { 2558 debug_primitive.log( 2559 "pop_field_tag (%s %d = %s%n", obj_str(obj), field_num, obj_tags[field_num]); 2560 } 2561 if (debug_tag_frame) { 2562 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2563 } 2564 } 2565 2566 /** Return the number of primitive fields in clazz and all of its superclasses. */ 2567 public static int num_prim_fields(Class<?> clazz) { 2568 if (clazz == Object.class) { 2569 return 0; 2570 } else { 2571 int field_cnt = num_prim_fields(clazz.getSuperclass()); 2572 for (Field f : clazz.getDeclaredFields()) { 2573 if (f.getType().isPrimitive()) { 2574 field_cnt++; 2575 } 2576 } 2577 return field_cnt; 2578 } 2579 } 2580 2581 /** 2582 * Handle a binary operation on the two items at the top of the tag stack. Binary operations pop 2583 * the two items off of the top of the stack perform an operation and push the result back on the 2584 * stack. The tags of the two items on the top of the stack must thus be merged and a 2585 * representative tag pushed back on the stack. 2586 */ 2587 public static void binary_tag_op() { 2588 ThreadData td = thread_to_data.get(Thread.currentThread()); 2589 debug_primitive.log("binary tag op%n"); 2590 assert td.tag_stack.peek() != method_marker; 2591 Object tag1 = td.tag_stack.pop(); 2592 assert td.tag_stack.peek() != method_marker; 2593 TagEntry.union(tag1, td.tag_stack.peek()); 2594 if (debug_tag_frame) { 2595 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2596 } 2597 } 2598 2599 /** 2600 * Handles an i_cmpXX operation. This opcode compares the two integers on the top of the stack and 2601 * jumps accordingly. Thus the two tags on the top of the stack are popped from the tag stack and 2602 * merged. Very similar to binary_tag_op except that nothing is pushed back on the tag stack. 2603 */ 2604 public static void cmp_op() { 2605 ThreadData td = thread_to_data.get(Thread.currentThread()); 2606 debug_primitive.log("cmp_op%n"); 2607 assert td.tag_stack.peek() != method_marker; 2608 Object tag1 = td.tag_stack.pop(); 2609 assert td.tag_stack.peek() != method_marker; 2610 TagEntry.union(tag1, td.tag_stack.pop()); 2611 if (debug_tag_frame) { 2612 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2613 } 2614 // debug_print_call_stack(); 2615 } 2616 2617 /** Handles a dup opcode on a primitive. */ 2618 public static void dup() { 2619 ThreadData td = thread_to_data.get(Thread.currentThread()); 2620 debug_primitive.log("dup%n"); 2621 assert td.tag_stack.peek() != method_marker; 2622 td.tag_stack.push(td.tag_stack.peek()); 2623 if (debug_tag_frame) { 2624 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2625 } 2626 } 2627 2628 /** Handles a dup_x1 opcode on a primitive. */ 2629 public static void dup_x1() { 2630 ThreadData td = thread_to_data.get(Thread.currentThread()); 2631 debug_primitive.log("dup_x1%n"); 2632 assert td.tag_stack.peek() != method_marker; 2633 Object top = td.tag_stack.pop(); 2634 assert td.tag_stack.peek() != method_marker; 2635 Object nxt = td.tag_stack.pop(); 2636 td.tag_stack.push(top); 2637 td.tag_stack.push(nxt); 2638 td.tag_stack.push(top); 2639 if (debug_tag_frame) { 2640 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2641 } 2642 } 2643 2644 /** 2645 * Handles a dup_x2 opcode on a primitive. Currently only support category 1 computational types. 2646 */ 2647 public static void dup_x2() { 2648 ThreadData td = thread_to_data.get(Thread.currentThread()); 2649 debug_primitive.log("dup_x2%n"); 2650 assert td.tag_stack.peek() != method_marker; 2651 Object top = td.tag_stack.pop(); 2652 assert td.tag_stack.peek() != method_marker; 2653 Object tag1 = td.tag_stack.pop(); 2654 assert td.tag_stack.peek() != method_marker; 2655 Object tag2 = td.tag_stack.pop(); 2656 td.tag_stack.push(top); 2657 td.tag_stack.push(tag2); 2658 td.tag_stack.push(tag1); 2659 td.tag_stack.push(top); 2660 if (debug_tag_frame) { 2661 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2662 } 2663 } 2664 2665 /** Handles a dup2 opcode on a primitive. */ 2666 public static void dup2() { 2667 ThreadData td = thread_to_data.get(Thread.currentThread()); 2668 debug_primitive.log("dup2%n"); 2669 assert td.tag_stack.peek() != method_marker; 2670 Object top = td.tag_stack.pop(); 2671 assert td.tag_stack.peek() != method_marker; 2672 Object tag1 = td.tag_stack.pop(); 2673 td.tag_stack.push(tag1); 2674 td.tag_stack.push(top); 2675 td.tag_stack.push(tag1); 2676 td.tag_stack.push(top); 2677 if (debug_tag_frame) { 2678 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2679 } 2680 } 2681 2682 /** Handles a dup2_x1 opcode on a primitive. */ 2683 public static void dup2_x1() { 2684 ThreadData td = thread_to_data.get(Thread.currentThread()); 2685 debug_primitive.log("dup2_x1%n"); 2686 assert td.tag_stack.peek() != method_marker; 2687 Object top = td.tag_stack.pop(); 2688 assert td.tag_stack.peek() != method_marker; 2689 Object tag1 = td.tag_stack.pop(); 2690 assert td.tag_stack.peek() != method_marker; 2691 Object tag2 = td.tag_stack.pop(); 2692 td.tag_stack.push(tag1); 2693 td.tag_stack.push(top); 2694 td.tag_stack.push(tag2); 2695 td.tag_stack.push(tag1); 2696 td.tag_stack.push(top); 2697 if (debug_tag_frame) { 2698 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2699 } 2700 } 2701 2702 /** Handles a dup2_x2 opcode on a primitive. */ 2703 public static void dup2_x2() { 2704 ThreadData td = thread_to_data.get(Thread.currentThread()); 2705 debug_primitive.log("dup2_x2%n"); 2706 assert td.tag_stack.peek() != method_marker; 2707 Object top = td.tag_stack.pop(); 2708 assert td.tag_stack.peek() != method_marker; 2709 Object tag1 = td.tag_stack.pop(); 2710 assert td.tag_stack.peek() != method_marker; 2711 Object tag2 = td.tag_stack.pop(); 2712 assert td.tag_stack.peek() != method_marker; 2713 Object tag3 = td.tag_stack.pop(); 2714 td.tag_stack.push(tag1); 2715 td.tag_stack.push(top); 2716 td.tag_stack.push(tag3); 2717 td.tag_stack.push(tag2); 2718 td.tag_stack.push(tag1); 2719 td.tag_stack.push(top); 2720 if (debug_tag_frame) { 2721 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2722 } 2723 } 2724 2725 /** Swaps the two elements on the top of the tag stack. */ 2726 public static void swap() { 2727 ThreadData td = thread_to_data.get(Thread.currentThread()); 2728 debug_primitive.log("swap%n"); 2729 assert td.tag_stack.peek() != method_marker; 2730 Object top = td.tag_stack.pop(); 2731 assert td.tag_stack.peek() != method_marker; 2732 Object tag1 = td.tag_stack.pop(); 2733 td.tag_stack.push(top); 2734 td.tag_stack.push(tag1); 2735 if (debug_tag_frame) { 2736 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2737 } 2738 } 2739 2740 /** 2741 * Handles the various primitive (int, double, etc) array load instructions. The array and its 2742 * index are made comparable. The tag for the index is removed from the tag stack and the tag for 2743 * the array element is pushed on the stack. 2744 * 2745 * @param arr_ref array reference 2746 * @param index index into array 2747 */ 2748 public static void primitive_array_load(Object arr_ref, int index) { 2749 debug_primitive.log("primitive_array_load%n"); 2750 // Since instance variables by default initialize to zero, any field 2751 // can possibly be read before it is set. 2752 primitive_array_load_null_ok(arr_ref, index); 2753 } 2754 2755 /** 2756 * Handles the various primitive (int, double, etc) array load instructions. The array and its 2757 * index are made comparable. The tag for the index is removed from the tag stack and the tag for 2758 * the array element is pushed on the stack. Unlike primitive_array_load(), this method handles 2759 * array elements whose tags have not previously been set. This can happen when the JVM sets an 2760 * array element directly and there is no corresponding java code that can set the tag. 2761 * 2762 * @param arr_ref array reference 2763 * @param index index into array 2764 */ 2765 public static void primitive_array_load_null_ok(Object arr_ref, int index) { 2766 ThreadData td = thread_to_data.get(Thread.currentThread()); 2767 debug_primitive.log("primitive_array_load_null_ok%n"); 2768 // Get the tag for the index and mark it as comparable with the array 2769 assert td.tag_stack.peek() != method_marker; 2770 Object index_tag = td.tag_stack.pop(); 2771 if (arr_ref == null) { 2772 if (debug_tag_frame) { 2773 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2774 } 2775 return; 2776 } 2777 if (debug_arr_index.enabled()) { 2778 debug_arr_index.log("Merging array '%s' and index '%s'", obj_str(arr_ref), index_tag); 2779 } 2780 if (merge_arrays_and_indices) { 2781 TagEntry.union(arr_ref, index_tag); 2782 } 2783 2784 // Push the tag for the element on the tag stack. 2785 Object[] obj_tags = field_map.get(arr_ref); 2786 if (obj_tags != null) { 2787 Object tag = obj_tags[index]; 2788 if (tag == null) { 2789 obj_tags[index] = tag = new UninitArrayElem(); 2790 } 2791 td.tag_stack.push(tag); 2792 if (debug_primitive.enabled()) { 2793 debug_primitive.log( 2794 "arrayload null-ok %s[%d] = %s%n", obj_str(arr_ref), index, obj_str(obj_tags[index])); 2795 } 2796 } else { 2797 int length = Array.getLength(arr_ref); 2798 obj_tags = new Object[length]; 2799 field_map.put(arr_ref, obj_tags); 2800 Object tag = new UninitArrayElem(); 2801 obj_tags[index] = tag; 2802 td.tag_stack.push(tag); 2803 if (debug_primitive.enabled()) { 2804 debug_primitive.log("arrayload null-ok %s[%d] = null%n", obj_str(arr_ref), index); 2805 } 2806 } 2807 if (debug_tag_frame) { 2808 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2809 } 2810 } 2811 2812 /** 2813 * Handles the aaload instruction. The arry and its index are made comparable. The tag for the 2814 * index is removed from the tag stack. 2815 * 2816 * @param arr_ref array reference 2817 * @param index index into array 2818 */ 2819 public static void ref_array_load(Object arr_ref, int index) { 2820 ThreadData td = thread_to_data.get(Thread.currentThread()); 2821 debug_primitive.log("ref_array_load%n"); 2822 // Get the tag for the index and mark it as comparable with the array 2823 assert td.tag_stack.peek() != method_marker; 2824 Object index_tag = td.tag_stack.pop(); 2825 if (debug_tag_frame) { 2826 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2827 } 2828 if (arr_ref == null) { 2829 return; 2830 } 2831 if (debug_arr_index.enabled()) { 2832 debug_arr_index.log("Merging array '%s' and index '%s'", obj_str(arr_ref), index_tag); 2833 } 2834 if (merge_arrays_and_indices) { 2835 TagEntry.union(arr_ref, index_tag); 2836 } 2837 } 2838 2839 /** 2840 * Allocate a new tag for the constant and push it on the tag stack. Note that this allocates a 2841 * new tag each time the constant is pushed. If the same code is executed multiple time (eg, in a 2842 * loop), and different values interact with the constant each time, those values will not end up 2843 * comparable to each other. 2844 */ 2845 public static void push_const() { 2846 ThreadData td = thread_to_data.get(Thread.currentThread()); 2847 Object tag = new Constant(); 2848 debug_primitive.log("push literal constant tag: %s%n", tag); 2849 td.tag_stack.push(tag); 2850 if (debug_tag_frame) { 2851 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 2852 } 2853 2854 // debug_print_call_stack(); 2855 } 2856 2857 /** 2858 * Marks the specified class as initialized. We don't look at static variables in classes until 2859 * they are initialized. 2860 * 2861 * @param classname class to mark initialized 2862 */ 2863 public static void set_class_initialized(String classname) { 2864 debug_primitive.log("set_class_initialized: %s%n", classname); 2865 initialized_eclassses.add(classname); 2866 } 2867 2868 /** 2869 * Returns whether or not the specified class is initialized. 2870 * 2871 * @param clazz class to check 2872 * @return true if clazz has been initialized 2873 */ 2874 @Pure 2875 public static boolean is_class_initialized(Class<?> clazz) { 2876 debug_primitive.log("is_class_initialized%n"); 2877 return initialized_eclassses.contains(clazz.getName()); 2878 } 2879 2880 /** Returns the name of the method that called the caller of caller_name(). */ 2881 private static String caller_name() { 2882 2883 Throwable stack = new Throwable("caller"); 2884 StackTraceElement[] ste_arr = stack.getStackTrace(); 2885 StackTraceElement ste = ste_arr[2]; 2886 // If JDK 11 runtime transfer method, need to skip another level. 2887 if (ste.getClassName().equals("java.lang.DCRuntime")) { 2888 ste = ste_arr[3]; 2889 } 2890 return ste.getClassName() + "." + ste.getMethodName(); 2891 } 2892 2893 /** 2894 * Returns a string description of the object that includes its class, identity hash code, and the 2895 * result of its toString() function - if it differs from the default implementation. Note that 2896 * the call to toString() may have unintended side effects. Hence, all calls to obj_str are 2897 * protected by debug flag checks or debug logging enabled() checks. 2898 * 2899 * @param obj object to be described 2900 * @return object description 2901 */ 2902 private static String obj_str(Object obj) { 2903 2904 if (obj == null) { 2905 return "<null>"; 2906 } else { 2907 String tostring; 2908 try { 2909 tostring = obj.toString(); 2910 } catch (Exception e) { 2911 // We use System.identityHashCode(obj) as obj.hashCode() can fail. 2912 tostring = 2913 "toString of " 2914 + obj.getClass().getName() 2915 + "@" 2916 + Integer.toHexString(System.identityHashCode(obj)) 2917 + " failed"; 2918 } 2919 String default_tostring = 2920 String.format("%s@%s", obj.getClass().getName(), System.identityHashCode(obj)); 2921 if (tostring.equals(default_tostring)) { 2922 return tostring; 2923 } else { 2924 // Limit display of object contents to 60 characters. 2925 return String.format( 2926 "%s [%s]", 2927 default_tostring, org.apache.commons.lang3.StringUtils.abbreviate(tostring, 60)); 2928 } 2929 } 2930 } 2931 2932 /** 2933 * Returns all of the daikonvariables in the tree rooted at dvi. 2934 * 2935 * @param dvi the tree of variables 2936 * @return all the daikonvarables in the tree 2937 */ 2938 private static List<DaikonVariableInfo> varlist(DaikonVariableInfo dvi) { 2939 2940 List<DaikonVariableInfo> list = new ArrayList<>(); 2941 list.add(dvi); 2942 for (DaikonVariableInfo child : dvi) { 2943 list.addAll(varlist(child)); 2944 } 2945 return list; 2946 } 2947 2948 /** 2949 * Returns the name of the tag field that corresponds to the specified field. 2950 * 2951 * @param field_name field name 2952 * @return tag field name 2953 */ 2954 public static String tag_field_name(String field_name) { 2955 debug_primitive.log("tag_field_name: %s%n", field_name); 2956 return (field_name + "__$tag"); 2957 } 2958 2959 private static Matcher jdk_decl_matcher = 2960 Pattern.compile("(, )?java.lang.DCompMarker( marker)?").matcher(""); 2961 private static Matcher non_jdk_decl_matcher = 2962 Pattern.compile("(, )?daikon.dcomp.DCompMarker( marker)?").matcher(""); 2963 2964 /** Removes DCompMarker from the signature. */ 2965 public static String clean_decl_name(String decl_name) { 2966 2967 if (Premain.jdk_instrumented) { 2968 jdk_decl_matcher.reset(decl_name); 2969 return jdk_decl_matcher.replaceFirst(""); 2970 } else { 2971 non_jdk_decl_matcher.reset(decl_name); 2972 return non_jdk_decl_matcher.replaceFirst(""); 2973 } 2974 } 2975 2976 /** 2977 * Abstract base class for code that gets the tag associated with a particular field. There are 2978 * specific implementors for the various types of fields. FieldTag instances are stored in 2979 * FieldInfo so that tags can be efficiently obtained. 2980 */ 2981 public abstract static class FieldTag { 2982 2983 /** 2984 * Gets the tag for the field. 2985 * 2986 * @param parent object that contains the field (if any) 2987 * @param obj value of the field itself (if available and if it's an object) 2988 * @return the tag for the field 2989 */ 2990 abstract Object get_tag(Object parent, Object obj); 2991 } 2992 2993 /** 2994 * Class that gets the tag for static primitive fields. We retrieve the static tag by using the 2995 * same method as we use during run time to push the tag on the tag stack. 2996 */ 2997 public static class StaticPrimitiveTag extends FieldTag { 2998 2999 Method get_tag; 3000 3001 /** Initialize with information from the field. */ 3002 StaticPrimitiveTag(FieldInfo fi) { 3003 assert fi.isStatic(); 3004 assert fi.isPrimitive(); 3005 Field field = fi.getField(); 3006 Class<?> clazz = field.getDeclaringClass(); 3007 String name = Premain.tag_method_name(Premain.GET_TAG, clazz.getName(), field.getName()); 3008 try { 3009 get_tag = clazz.getMethod(name); 3010 } catch (Exception e) { 3011 throw new Error("can't find tag method " + name, e); 3012 } 3013 } 3014 3015 /** Return the tag associated with this field. */ 3016 @Override 3017 Object get_tag(Object parent, Object obj) { 3018 Object tag; 3019 // jhp - not sure why these are not null... 3020 // assert parent == null && obj == null 3021 // : " parent/obj = " + obj_str(parent) + "/" + obj_str(obj); 3022 try { 3023 ThreadData td = thread_to_data.get(Thread.currentThread()); 3024 Object ret_val = get_tag.invoke(parent); 3025 assert ret_val == null; 3026 assert td.tag_stack.peek() != method_marker; 3027 tag = td.tag_stack.pop(); 3028 assert tag != null; 3029 if (debug_tag_frame) { 3030 System.out.printf("tag stack size: %d%n", td.tag_stack.size()); 3031 } 3032 } catch (Exception e) { 3033 throw new Error("can't execute tag method " + get_tag, e); 3034 } 3035 return tag; 3036 } 3037 } 3038 3039 /** 3040 * Class that gets the tag for a static reference variable. The tag for a reference variable is 3041 * the object itself, so that is obtained via reflection. 3042 */ 3043 public static class StaticReferenceTag extends FieldTag { 3044 3045 /** Corresponding java field. */ 3046 Field field; 3047 3048 /** Set to true when the class containing the field is initialized. */ 3049 boolean is_class_initialized = false; 3050 3051 /** Class that contains the field. */ 3052 Class<?> declaring_class; 3053 3054 /** Initialize for this field. */ 3055 public StaticReferenceTag(FieldInfo fi) { 3056 3057 assert fi.isStatic(); 3058 assert !fi.isPrimitive(); 3059 field = fi.getField(); 3060 declaring_class = field.getDeclaringClass(); 3061 } 3062 3063 /** Gets the tag for this static reference. */ 3064 @Override 3065 @SuppressWarnings("deprecation") // in Java 9+, use canAccess instead of isAccessible 3066 public Object get_tag(Object parent, Object obj) { 3067 3068 // assert parent == null && obj == null; 3069 if (!is_class_initialized) { 3070 if (is_class_initialized(declaring_class)) { 3071 if (!field.isAccessible()) { 3072 field.setAccessible(true); 3073 } 3074 is_class_initialized = true; 3075 } else { 3076 return nonsensical; 3077 } 3078 } 3079 3080 try { 3081 return field.get(null); 3082 } catch (Exception e) { 3083 throw new RuntimeException("Can't get val for static field " + field, e); 3084 } 3085 } 3086 } 3087 3088 /** 3089 * Class that gets the list of tags for primitive arrays. Note that primitive arrays can both be 3090 * actual arrays of primitives and also arrays of classes containing primitives. In the second 3091 * case, there is a separate object that contains each of the 'array' values. 3092 */ 3093 public static class PrimitiveArrayTag extends FieldTag { 3094 3095 /** The field number for this field inside its object. */ 3096 int field_num; 3097 3098 public PrimitiveArrayTag(FieldInfo fi) { 3099 assert !fi.isStatic() && fi.isPrimitive() && fi.isArray(); 3100 field_num = fi.get_field_num(); 3101 } 3102 3103 /** Returns a list of object tags. */ 3104 @Override 3105 public Object get_tag(Object parent, Object obj) { 3106 3107 // Object is an array of objects containing each item 3108 // assert obj == null: "primitive array object = " + obj_str (obj); 3109 @SuppressWarnings("unchecked") 3110 List<Object> parent_list = (List<Object>) parent; 3111 List<Object> tag_list = new ArrayList<>(parent_list.size()); 3112 for (Object parent_element : parent_list) { 3113 Object[] tags = field_map.get(parent_element); 3114 if (tags == null) { 3115 tag_list.add(nonsensical); 3116 } else { 3117 tag_list.add(tags[field_num]); 3118 } 3119 } 3120 return tag_list; 3121 } 3122 } 3123 3124 /** 3125 * Class that gets the tag for a primitive instance field. Each object with primitive fields has a 3126 * corresponding tag array in field map. The tag for a particular field is stored at that fields 3127 * offset in the tag array. 3128 */ 3129 public static class PrimitiveTag extends FieldTag { 3130 3131 int field_num; 3132 3133 public PrimitiveTag(FieldInfo fi) { 3134 assert !fi.isStatic() && fi.isPrimitive() && !fi.isArray(); 3135 field_num = fi.get_field_num(); 3136 } 3137 3138 @Override 3139 public Object get_tag(Object parent, Object obj) { 3140 3141 // obj is the wrapper for the primitive 3142 // assert obj == null: "primitive object = " + obj_str (obj); 3143 Object[] tags = field_map.get(parent); 3144 if (tags == null) { 3145 return nonsensical; // happens if field has never been assigned to 3146 } else { 3147 return tags[field_num]; 3148 } 3149 } 3150 } 3151 3152 /** 3153 * Class that returns the tag for a reference instance field. In this case, the tag is just the 3154 * object itself. 3155 */ 3156 public static class ReferenceTag extends FieldTag { 3157 3158 public ReferenceTag(FieldInfo fi) { 3159 assert !fi.isStatic() && !fi.isPrimitive(); 3160 } 3161 3162 @Override 3163 public Object get_tag(Object parent, Object obj) { 3164 return obj; 3165 } 3166 } 3167}