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