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