001package daikon.chicory;
002
003import daikon.Chicory;
004import daikon.Daikon.BugInDaikon;
005import daikon.plumelib.bcelutil.SimpleLog;
006import daikon.plumelib.reflection.ReflectionPlume;
007import daikon.plumelib.reflection.Signatures;
008import java.lang.reflect.Constructor;
009import java.lang.reflect.Field;
010import java.lang.reflect.Member;
011import java.lang.reflect.Method;
012import java.lang.reflect.Modifier;
013import java.util.ArrayList;
014import java.util.Arrays;
015import java.util.Collections;
016import java.util.EnumSet;
017import java.util.HashSet;
018import java.util.Iterator;
019import java.util.List;
020import java.util.Set;
021import java.util.StringJoiner;
022import java.util.regex.Matcher;
023import org.checkerframework.checker.interning.qual.Interned;
024import org.checkerframework.checker.lock.qual.GuardSatisfied;
025import org.checkerframework.checker.nullness.qual.NonNull;
026import org.checkerframework.checker.nullness.qual.Nullable;
027import org.checkerframework.checker.nullness.qual.RequiresNonNull;
028import org.checkerframework.checker.signature.qual.BinaryName;
029import org.checkerframework.dataflow.qual.Pure;
030import org.checkerframework.dataflow.qual.SideEffectFree;
031
032/**
033 * Each DaikonVariableInfo object is a node in the tree structure of the variables in the target
034 * application. In general, the variable a will be the parent of the variables a.b and a.c in the
035 * tree, where b and c are fields in a's class. There is such a tree structure associated with every
036 * program point.
037 *
038 * <p>Each node can have any non-negative number of child nodes. DaikonVariableInfo is an abstract
039 * class. Its subtypes are designed to represent specific types of variables, such as arguments,
040 * arrays, etc.
041 *
042 * <p>The tree structure is built in the DeclWriter and traversed in the DTraceWriter.
043 *
044 * <p>This architecture makes it possible to avoid the issue of "traversal pattern duplication" in
045 * which both the DeclWriter and DTraceWriter must traverse the target application's variables
046 * identically.
047 */
048public abstract class DaikonVariableInfo
049    implements Iterable<DaikonVariableInfo>, Comparable<DaikonVariableInfo> {
050
051  /** Enable experimental techniques on static constants. */
052  public static boolean dkconfig_constant_infer = false;
053
054  /** The variable name. Sensible for all subtypes except RootInfo. */
055  private final @Interned String name;
056
057  /** The child nodes. */
058  public List<DaikonVariableInfo> children;
059
060  /** True iff this variable is an array. */
061  protected final boolean isArray;
062
063  /** Print debug information about the variables. */
064  static SimpleLog debug_vars = new SimpleLog(false);
065
066  private static SimpleLog debug_array = new SimpleLog(true);
067
068  /** Default string for comparability info. */
069  private static final String compareInfoDefaultString = "22";
070
071  // It's not enough to use one of the following 3 strings.
072  // You also need to set the flags that are returned by get_var_flags()!
073
074  /** Indicates that a given variable is non-null. */
075  protected static final String isNonNullString = " # isNonNull=true";
076
077  /** Indicates that a given variable is a parameter to a method. */
078  protected static final String isParamString = " # isParam=true";
079
080  /** Indicates that a given variable is non-null and a parameter. */
081  protected static final String isNonNullParamString = " # isNonNull=true, isParam=true";
082
083  // Certain hardcoded class names
084  protected static final String classClassName = "java.lang.Class";
085  protected static final String stringClassName = "java.lang.String";
086
087  // Suffix for "typeOf" (CLASSNAME) variables that represent a class,
088  // eg, "foo.getClass().getName()".
089  public static final String class_suffix = ".getClass().getName()";
090
091  public static final String class_suffix_relative_name = class_suffix.substring(1);
092
093  /**
094   * The printed type that will appear in the .decls declaration. May include aux information at the
095   * end, such as isParamString.
096   *
097   * @see #getTypeName()
098   * @see #getTypeNameOnly()
099   */
100  protected String typeName;
101
102  /** The printed representation type that will appear in the .decls declaration. */
103  protected String repTypeName;
104
105  /** The printed comparability information that will appear in the .decls declaration. */
106  protected String compareInfoString = compareInfoDefaultString;
107
108  /** Value of static constants. Access via {@link #get_const_val()} method. */
109  @Nullable String const_val = null;
110
111  /** Arguments used to create a function. Access via {@link #get_function_args()} method. */
112  @Nullable String function_args = null;
113
114  // It seems that declShouldPrint and dtraceShouldPrint always have the same value.
115  /** True iff the DeclWriter should print this variable. */
116  protected boolean declShouldPrint = true;
117
118  /** True iff the DTraceWriter should print this variable. */
119  protected boolean dtraceShouldPrint = true;
120
121  /** True iff the DTraceWriter should print the children of this variable. */
122  protected boolean dtraceShouldPrintChildren = true;
123
124  /**
125   * If false, every field in an instrumented class is visible. If true, use standard Java behavior
126   * (if the field is in a class in a different package, it is only visible if public, etc.).
127   */
128  public static boolean std_visibility = false;
129
130  /**
131   * Set of fully qualified static variable names for this ppt. Used to ensure that each static is
132   * only included once (regardless of how many other variables may include its declaring class).
133   */
134  protected static Set<String> ppt_statics = new HashSet<>();
135
136  /**
137   * Constructs a non-array-type DaikonVariableInfo object.
138   *
139   * @param theName the name of the variable
140   * @param typeName the name of the type
141   * @param repTypeName the name of the representation type
142   */
143  protected DaikonVariableInfo(String theName, String typeName, String repTypeName) {
144    this(theName, typeName, repTypeName, false);
145  }
146
147  /**
148   * Constructs a DaikonVariableInfo object.
149   *
150   * @param theName the variable's name
151   * @param arr true iff the variable is an array
152   */
153  protected DaikonVariableInfo(String theName, String typeName, String repTypeName, boolean arr) {
154    // Intern the names because there will be many of the
155    // same variable names at different program points within
156    // the same class.
157    name = theName.intern();
158    this.typeName = typeName.intern();
159    this.repTypeName = repTypeName.intern();
160
161    debug_vars.log(
162        "Construct DaikonVariableInfo: %s : %s : %s", this.getClass().getName(), name, typeName);
163
164    children = new ArrayList<DaikonVariableInfo>();
165    isArray = arr;
166
167    if ((theName != null) && (theName.contains("[..]") || theName.contains("[]")) && !isArray) {
168      debug_array.log("%s is not an array", theName);
169      debug_array.logStackTrace();
170    }
171  }
172
173  /** Returns the name of this variable. */
174  public @Nullable String getName(@GuardSatisfied DaikonVariableInfo this) {
175    if (name == null) {
176      return null;
177    }
178    return name.replaceFirst("\\[\\]", "[..]");
179  }
180
181  /**
182   * Add a child to this node. Should only be called while the tree is being constructed.
183   *
184   * @param info the child object, must be non-null. The child's fields name, typeName, repTypeName,
185   *     and compareInfoString should also be non-null.
186   */
187  protected void addChild(DaikonVariableInfo info) {
188    assert info != null : "info cannot be null in DaikonVariableInfo.addChild()";
189    assert info.name != null : "Child's name should not be null";
190    assert info.typeName != null : "Child's type name should not be null";
191    assert info.repTypeName != null : "Child's representation type name should not be null";
192    assert info.compareInfoString != null : "Child's comparability information should not be null";
193
194    debug_vars.log("Adding %s to %s", info, this);
195    children.add(info);
196  }
197
198  /** Returns a string representation of this node. */
199  @SideEffectFree
200  @Override
201  public String toString(@GuardSatisfied DaikonVariableInfo this) {
202    return getClass().getName() + ":" + getName();
203  }
204
205  /**
206   * Returns a string representation of this node and its descandants.
207   *
208   * @return a string representation of this node and its descandants
209   */
210  public String treeString() {
211    return getStringBuilder(new StringBuilder("--")).toString();
212  }
213
214  /**
215   * Return a StringBuilder that contains the name of this node and all ancestors of this node.
216   * Longer indentations correspond to deeper levels in the tree.
217   *
218   * @param offset the offset to begin each line with
219   * @return StringBuilder that contains all children of this node
220   */
221  private StringBuilder getStringBuilder(StringBuilder offset) {
222    StringBuilder theBuf = new StringBuilder();
223
224    theBuf.append(
225        offset + name + " [" + System.identityHashCode(this) + "]" + DaikonWriter.lineSep);
226
227    StringBuilder childOffset = new StringBuilder(offset);
228    childOffset.append("--");
229    for (DaikonVariableInfo info : children) {
230      theBuf.append(info.getStringBuilder(childOffset));
231    }
232
233    return theBuf;
234  }
235
236  /**
237   * Returns an iterator over all the node's children. Don't modify the list of children through the
238   * iterator, as an unmodifiable list is used to generate the iterator.
239   *
240   * @return an iterator over all the node's children
241   */
242  @Override
243  public Iterator<DaikonVariableInfo> iterator() {
244    return Collections.unmodifiableList(children).iterator();
245  }
246
247  /** Returns the complete tree of variables as a list. */
248  public List<DaikonVariableInfo> tree_as_list() {
249    List<DaikonVariableInfo> list = new ArrayList<>();
250    list.add(this);
251    for (DaikonVariableInfo dv : children) {
252      list.addAll(dv.tree_as_list());
253    }
254    return list;
255  }
256
257  /**
258   * Given a value corresponding to the parent of this, return the value of this.
259   *
260   * <p>For instance, if the variable a has a field b, then calling getMyValFromParentVal(val_of_a)
261   * will return the value of a.b .
262   *
263   * @param parentVal the parent object. Can be null for static fields.
264   * @return the value for this, computed from {@code parentVal}
265   */
266  public abstract @Nullable Object getMyValFromParentVal(Object parentVal);
267
268  ///
269  /// Printing
270  ///
271
272  /**
273   * Returns a String representation of this object suitable for a {@code .dtrace} file.
274   *
275   * @param val the object whose value to print
276   */
277  @SuppressWarnings("unchecked")
278  public String getDTraceValueString(Object val) {
279    if (isArray) {
280      return getValueStringOfListWithMod((List<Object>) val); // unchecked cast
281    } else {
282      return getValueStringOfObjectWithMod(val, true);
283    }
284  }
285
286  /** Gets the value of an object and concatenates the associated "modified" integer. */
287  protected String getValueStringOfObjectWithMod(Object theValue, boolean hashArray) {
288    String retString = getValueStringOfObject(theValue, hashArray) + DaikonWriter.lineSep;
289
290    if (theValue instanceof NonsensicalObject) {
291      retString += "2";
292    } else {
293      retString += "1";
294    }
295
296    return retString;
297  }
298
299  /**
300   * Gets the value, but with no endline. If hashArray is true, it prints the "hash code" of the
301   * array and not its separate values.
302   */
303  private String getValueStringOfObject(Object theValue, boolean hashArray) {
304    if (theValue == null) {
305      return "null";
306    }
307
308    Class<?> type = theValue.getClass();
309
310    assert !type.isPrimitive() : "Objects cannot be primitive";
311
312    if (theValue instanceof Runtime.PrimitiveWrapper) {
313      return getPrimitiveValueString(theValue);
314    } else if (!hashArray && type.isArray()) {
315      // show the full array
316      return getValueStringOfArray(theValue);
317    } else if (theValue instanceof NonsensicalObject) {
318      return "nonsensical";
319    } else {
320      // basically, show the hashcode of theValue
321      return getObjectHashCode(theValue);
322    }
323  }
324
325  /** Get value string for a primitive (wrapped) object. */
326  private String getPrimitiveValueString(Object obj) {
327    assert (obj instanceof Runtime.PrimitiveWrapper)
328        : "Objects passed to showPrimitive must implement PrimitiveWrapper"
329            + DaikonWriter.lineSep
330            + "This object is type: "
331            + obj.getClass().getName();
332
333    // use wrapper classes toString methods to print value
334    return obj.toString();
335  }
336
337  /** Gets a string representation of the values in an array. */
338  private String getValueStringOfArray(Object array) {
339    List<Object> theList = DTraceWriter.getListFromArray(array);
340    return getValueStringOfList(theList);
341  }
342
343  /** Gets the Object's unique ID as a string. In other words, a "hash code". */
344  private String getObjectHashCode(Object theObject) {
345    if (theObject == null) {
346      return "null";
347    } else if (theObject instanceof NonsensicalObject) {
348      return "nonsensical";
349    } else {
350      return Integer.toString(System.identityHashCode(theObject));
351    }
352  }
353
354  /**
355   * Gets the list of values (as a string) from getValueStringOfList and concatenates the "modified"
356   * value.
357   */
358  private String getValueStringOfListWithMod(List<Object> theValues) {
359    String retString = getValueStringOfList(theValues) + DaikonWriter.lineSep;
360
361    if (theValues instanceof NonsensicalList) {
362      retString += "2";
363    } else {
364      retString += "1";
365    }
366
367    return retString;
368  }
369
370  /**
371   * Returns a string representation of the values of a list of values as if it were an array.
372   *
373   * @param theValues the values to print out
374   */
375  protected String getValueStringOfList(List<Object> theValues) {
376    if (theValues == null) {
377      return "null";
378    }
379
380    if (theValues instanceof NonsensicalList) {
381      return "nonsensical";
382    }
383
384    StringJoiner buf = new StringJoiner(" ", "[", "]");
385
386    for (Iterator<Object> iter = theValues.iterator(); iter.hasNext(); ) {
387      Object elementVal = iter.next();
388
389      // hash arrays...
390      // don't want to print arrays within arrays
391      buf.add(getValueStringOfObject(elementVal, true));
392    }
393
394    return buf.toString();
395  }
396
397  ///
398  /// Building the tre
399  ///
400
401  /**
402   * Add the parameters of the given method to this node.
403   *
404   * @param cinfo the method's class
405   * @param method the method
406   * @param argnames the method's arguments
407   * @param depth the remaining depth to print variables to
408   */
409  protected void addParameters(ClassInfo cinfo, Member method, List<String> argnames, int depth) {
410    debug_vars.log("enter addParameters%n");
411
412    Class<?>[] parameterTypes =
413        (method instanceof Constructor<?>)
414            ? ((Constructor<?>) method).getParameterTypes()
415            : ((Method) method).getParameterTypes();
416    assert argnames.size() == parameterTypes.length;
417
418    int param_offset = 0;
419    for (int i = 0; i < parameterTypes.length; i++) {
420      Class<?> type = parameterTypes[i];
421      String name = argnames.get(i);
422      if (type.getName().equals("daikon.dcomp.DCompMarker")
423          || type.getName().equals("java.lang.DCompMarker")) {
424        continue;
425      }
426      debug_vars.log("processing parameter '%s'%n", name);
427      debug_vars.indent();
428      DaikonVariableInfo theChild =
429          addParamDeclVar(cinfo, type, name, /* offset= */ "", depth, i, param_offset);
430      param_offset++;
431      if ((type == Double.TYPE) || (type == Long.TYPE)) {
432        param_offset++;
433      }
434      assert cinfo.clazz != null : "@AssumeAssertion(nullness): need to check justification";
435      theChild.addChildNodes(cinfo, type, name, /* offset= */ "", depth);
436      debug_vars.exdent();
437    }
438    debug_vars.log("exit addParameters%n");
439  }
440
441  /**
442   * Adds class variables (i.e., the fields) for the given type and attach new nodes as children of
443   * this node.
444   *
445   * @param type the class whose fields should all be added to this node
446   * @param offset the prefix for variables -- that is, the expression whose fields are being
447   *     printed
448   * @param depth the remaining depth to print variables to
449   */
450  @RequiresNonNull("#1.clazz")
451  protected void addClassVars(
452      ClassInfo cinfo, boolean dontPrintInstanceVars, Class<?> type, String offset, int depth) {
453
454    debug_vars.log("addClassVars: %s : %s : %s: [%s]%n", this, cinfo, type, offset);
455
456    boolean topLevelCall = offset.equals(""); // true if at first level of recursion
457
458    DaikonVariableInfo thisInfo; // DaikonVariableInfo corresponding to the "this" object
459    if (!dontPrintInstanceVars && topLevelCall) {
460      // "this" variable; must must be at the first level of recursion (not lower) to print it
461      thisInfo = new ThisObjInfo(type);
462      addChild(thisInfo);
463
464      // .class variable
465      if (shouldAddRuntimeClass(type)) {
466        DaikonVariableInfo thisClass =
467            new DaikonClassInfo(
468                "this" + class_suffix, classClassName, stringClassName, "this", false);
469        thisInfo.addChild(thisClass);
470      }
471    } else if (topLevelCall) {
472      // Create a non-printing root for static variables.
473      thisInfo = new StaticObjInfo(type);
474      addChild(thisInfo);
475    } else {
476      thisInfo = this;
477    }
478
479    // Get the fields
480    // System.out.printf("getting fields for %s%n", type);
481
482    // We need to get fields of superclass(es) as well.
483    List<Field> fields = new ArrayList<>();
484    Class<?> c = type;
485    while (c != null && c != Object.class) {
486      fields.addAll(Arrays.asList(c.getDeclaredFields()));
487      c = c.getSuperclass();
488    }
489
490    // if (fields.length > 50)
491    //    System.out.printf("%d fields in %s%n", fields.length, type);
492
493    debug_vars.log(
494        "%s: [%s] %d dontPrintInstanceVars = %b, inArray = %b%n",
495        type, offset, fields.size(), dontPrintInstanceVars, isArray);
496
497    for (Field classField : fields) {
498      boolean is_static = Modifier.isStatic(classField.getModifiers());
499
500      debug_vars.log("considering field %s -> %s%n", offset, classField);
501
502      // Skip some fields
503
504      // In the future, perhaps skip some synthetic fields.  So far, the
505      // only ones we have seen are 'this$0' (a field in an inner class
506      // that contains a pointer to the instance of the outer class),
507      // 'this$1' for an inner inner class, and so on.  These variables
508      // expose the outer class fields of an inner class to Daikon.
509
510      if (!is_static && dontPrintInstanceVars) {
511        debug_vars.log("--field (!static && dontPrintInstanceVars) %s%n", classField);
512        continue;
513      }
514
515      // Skip variables that match the ignore pattern
516      if (Chicory.omit_var != null) {
517        String fullname = offset + "." + classField.getName();
518        if (is_static) {
519          fullname = classField.getDeclaringClass().getName() + "." + classField.getName();
520        }
521        Matcher m = Chicory.omit_var.matcher(fullname);
522        if (m.find()) {
523          // System.out.printf("VAR %s matches omit pattern %s%n", fullname, Chicory.omit_var);
524          continue;
525        }
526      }
527
528      // Don't print arrays of the same static field
529      if (is_static && isArray) {
530        debug_vars.log("--field static and inArray%n");
531        continue;
532      }
533
534      // Skip any statics that have been already included
535      if (is_static) {
536        String full_name = classField.getDeclaringClass().getName() + "." + classField.getName();
537        if (ppt_statics.contains(full_name) && (depth <= 0)) {
538          debug_vars.log("already included static %s (no children)", full_name);
539
540          continue;
541        }
542      }
543
544      if (!isFieldVisible(cinfo.clazz, classField)) {
545        debug_vars.log("--field not visible%n");
546        continue;
547      }
548
549      // ... end of code to skip some fields.
550
551      Class<?> fieldType = classField.getType();
552
553      StringBuilder buf = new StringBuilder();
554      DaikonVariableInfo newChild = thisInfo.addDeclVar(classField, offset, buf);
555
556      debug_vars.log("--Created DaikonVariable %s%n", newChild);
557      debug_vars.indent();
558
559      String newOffset = buf.toString();
560      newChild.addChildNodes(cinfo, fieldType, classField.getName(), newOffset, depth);
561      debug_vars.exdent();
562    }
563
564    // If appropriate, print out decls information for pure methods
565    // and add to the tree.
566    // Check dontPrintInstanceVars is basically checking if the program point method
567    // (not the pure method) is static.  If it is, don't continue because we can't
568    // call instance methods (all pure methods we consider are instance methods)
569    // from static methods.
570    if (ChicoryPremain.shouldDoPurity() && !dontPrintInstanceVars) {
571      ClassInfo typeInfo;
572
573      try {
574        typeInfo = Runtime.getClassInfoFromClass(type);
575      } catch (RuntimeException e) {
576        // Could not find the class... no further purity analysis
577        typeInfo = null;
578      }
579
580      if (typeInfo != null) {
581        // Pure methods with no parameters
582        for (MethodInfo meth : typeInfo.method_infos) {
583          if (meth.isPure() && meth.arg_names.length == 0) {
584            StringBuilder buf = new StringBuilder();
585            DaikonVariableInfo newChild =
586                thisInfo.addPureMethodDecl(
587                    cinfo, meth, new DaikonVariableInfo[] {}, offset, depth, buf);
588            String newOffset = buf.toString();
589            debug_vars.log("Pure method");
590            debug_vars.indent();
591            assert meth.member != null
592                : "@AssumeAssertion(nullness): member of method_infos have"
593                    + " .member field"; // dependent type
594            newChild.addChildNodes(
595                cinfo,
596                ((Method) meth.member).getReturnType(),
597                meth.member.getName(),
598                newOffset,
599                depth);
600            debug_vars.exdent();
601          }
602        }
603
604        // List containing all class variables, excluding pure methods with parameters
605        List<DaikonVariableInfo> siblings = new ArrayList<>(thisInfo.children);
606
607        // Pure methods with one parameter
608        for (MethodInfo meth : typeInfo.method_infos) {
609          if (meth.isPure() && meth.arg_names.length == 1) {
610            for (DaikonVariableInfo sib : siblings) {
611              String sibType = sib.getTypeNameOnly();
612              Class<?> sibClass;
613
614              // Get class type of the class variable
615              try {
616                sibClass =
617                    ReflectionPlume.classForName(Signatures.binaryNameToClassGetName(sibType));
618              } catch (ClassNotFoundException e) {
619                throw new Error(e);
620              }
621
622              // Add node if the class variable can be used as the pure method's parameter
623              if (ReflectionPlume.isSubtype(sibClass, meth.arg_types[0])) {
624                DaikonVariableInfo[] arg = {sib};
625                StringBuilder buf = new StringBuilder();
626                DaikonVariableInfo newChild =
627                    thisInfo.addPureMethodDecl(cinfo, meth, arg, offset, depth, buf);
628                String newOffset = buf.toString();
629                debug_vars.log("Pure method");
630                debug_vars.indent();
631                assert meth.member != null
632                    : "@AssumeAssertion(nullness): member of"
633                        + " method_infos have .member field"; // fix with dependent type
634                newChild.addChildNodes(
635                    cinfo,
636                    ((Method) meth.member).getReturnType(),
637                    meth.member.getName(),
638                    newOffset,
639                    depth);
640                debug_vars.exdent();
641              }
642            }
643          }
644        }
645      }
646    }
647    debug_vars.log("exit addClassVars%n");
648  }
649
650  /**
651   * Adds the decl info for a single parameter as a child of this node. Also adds "derived"
652   * variables such as the runtime .class variable.
653   *
654   * @return the newly created DaikonVariableInfo object, whose parent is this
655   */
656  protected DaikonVariableInfo addParamDeclVar(
657      ClassInfo cinfo,
658      Class<?> type,
659      String name,
660      String offset,
661      int depth,
662      int argNum,
663      int param_offset) {
664    debug_vars.log("enter addParamDeclVar%n");
665    // add this variable to the tree as a child of curNode
666    DaikonVariableInfo newChild = new ParameterInfo(offset + name, argNum, type, param_offset);
667
668    addChild(newChild);
669
670    boolean ignore = newChild.check_for_dup_names();
671    if (!ignore) {
672      newChild.checkForDerivedVariables(type, name, offset);
673    }
674
675    debug_vars.log("exit addParamDeclVar%n");
676    return newChild;
677  }
678
679  /** Adds the decl info for a pure method. */
680  // TODO factor out shared code with printDeclVar
681  @SuppressWarnings("deprecation") // in Java 9+, use canAccess instead of isAccessible
682  protected DaikonVariableInfo addPureMethodDecl(
683      ClassInfo curClass,
684      MethodInfo minfo,
685      DaikonVariableInfo[] args,
686      String offset,
687      int depth,
688      StringBuilder buf) {
689    String arr_str = "";
690    if (isArray) {
691      arr_str = "[]";
692    }
693
694    @SuppressWarnings("nullness") // method precondition
695    @NonNull Method meth = (Method) minfo.member;
696
697    boolean changedAccess = false;
698
699    // we want to access all fields...
700    if (!meth.isAccessible()) {
701      changedAccess = true;
702      meth.setAccessible(true);
703    }
704
705    Class<?> type = meth.getReturnType();
706    assert type != null;
707
708    String theName = meth.getName() + "(";
709    if (args.length > 0) {
710      theName += args[0].getName();
711    }
712    if (args.length > 1) {
713      for (int i = 1; i < args.length - 1; i++) {
714        theName += ", " + args[i].getName();
715      }
716    }
717    theName += ")";
718
719    if (offset.length() > 0) {
720      // offset already starts with "this"
721    } else {
722      offset = "this.";
723    }
724
725    String type_name = stdClassName(type);
726    // TODO: Passing incorrect receiver name????
727    DaikonVariableInfo newPure =
728        new PureMethodInfo(
729            offset + theName,
730            minfo,
731            type_name + arr_str,
732            getRepName(type, isArray) + arr_str,
733            offset.substring(0, offset.length() - 1),
734            isArray,
735            args);
736
737    addChild(newPure);
738
739    newPure.checkForDerivedVariables(type, theName, offset);
740
741    buf.append(offset);
742
743    if (changedAccess) {
744      meth.setAccessible(false);
745    }
746
747    return newPure;
748  }
749
750  /**
751   * Adds the decl info for a single class variable (a field) as a child of this node. Also adds
752   * "derived" variables such as the runtime .class variable.
753   *
754   * @return the newly created DaikonVariableInfo object, whose parent is this
755   */
756  @SuppressWarnings("deprecation") // in Java 9+, use canAccess instead of isAccessible
757  protected DaikonVariableInfo addDeclVar(Field field, String offset, StringBuilder buf) {
758    debug_vars.log("enter addDeclVar(field):%n");
759    debug_vars.log("  field: %s, offset: %s%n", field, offset);
760    String arr_str = "";
761    if (isArray) {
762      arr_str = "[]";
763    }
764
765    // Temporarily make the field accessible.
766    boolean changedAccess = false;
767    if (!field.isAccessible()) {
768      changedAccess = true;
769      field.setAccessible(true);
770    }
771
772    int modifiers = field.getModifiers();
773    if (Modifier.isStatic(modifiers)) {
774      offset = field.getDeclaringClass().getName() + ".";
775    } else if (offset.length() == 0) { // instance fld, 1st recursion step
776      offset = "this.";
777    }
778
779    Class<?> type = field.getType();
780    String type_name = stdClassName(type) + arr_str + appendAuxInfo(field);
781
782    String theName = field.getName();
783
784    // Convert the internal reflection name for an outer class
785    // 'this' field to the Java language format.
786    if (theName.startsWith("this$")) {
787      offset = "";
788      theName = type.getName() + ".this";
789      if (!type_name.contains("#")) {
790        type_name += " # isNonNull=true";
791      } else {
792        type_name += ", isNonNull=true";
793      }
794    }
795
796    DaikonVariableInfo newField =
797        new FieldInfo(
798            offset + theName, field, type_name, getRepName(type, false) + arr_str, isArray);
799    boolean ignore = newField.check_for_dup_names();
800
801    if (DaikonWriter.isStaticConstField(field) && !isArray) {
802      ClassInfo cinfo = Runtime.getClassInfoFromClass(field.getDeclaringClass());
803      String value = null;
804      boolean isPrimitive = true;
805
806      if (cinfo != null) {
807        value = cinfo.staticMap.get(theName);
808
809        if (DaikonVariableInfo.dkconfig_constant_infer) {
810          if (value == null) {
811            isPrimitive = false;
812            String className = field.getDeclaringClass().getName();
813            // If the class has already been statically initialized, get its hash
814            if (Runtime.isInitialized(className)) {
815              try {
816                @SuppressWarnings("nullness") // the field is static, so null is OK as argument
817                Object fieldValue = field.get(null);
818                value = Integer.toString(System.identityHashCode(fieldValue));
819              } catch (Exception e) {
820                throw new BugInDaikon("Problem with field " + field);
821              }
822            }
823          }
824        }
825      }
826
827      // System.out.printf("static final value = %s%n", value);
828
829      // in this case, we don't want to print this variable to
830      // the dtrace file
831      if (value != null) {
832        newField.repTypeName += " = " + value;
833        newField.const_val = value;
834        newField.dtraceShouldPrint = false;
835        if (DaikonVariableInfo.dkconfig_constant_infer && isPrimitive) {
836          newField.dtraceShouldPrintChildren = false;
837        }
838      }
839      // else
840      // {
841      // don't print anything
842      // because this field wasn't declared with an actual "hardcoded" constant
843      // }
844
845    }
846
847    addChild(newField);
848
849    if (!ignore) {
850      newField.checkForDerivedVariables(type, theName, offset);
851    }
852
853    buf.append(offset);
854
855    if (changedAccess) {
856      field.setAccessible(false);
857    }
858
859    debug_vars.log("exit addDeclVar(field)%n");
860    return newField;
861  }
862
863  /**
864   * Returns the class name of the specified class as a binary name (i.e., as the class would have
865   * been declared in Java source code, except with '$' instead of '.' separating outer and inner
866   * classes).
867   */
868  public static @BinaryName String stdClassName(Class<?> type) {
869    return Runtime.classGetNameToBinaryName(type.getName());
870  }
871
872  /**
873   * Given a type, gets the representation type to be used in Daikon. For example, the
874   * representation type of a class object is "hashcode."
875   *
876   * @param type the type of the variable
877   * @param asArray whether the variable is being output as an array (true) or as a pointer (false)
878   * @return the representation type as a string
879   */
880  public static String getRepName(Class<?> type, boolean asArray) {
881    if (type == null) {
882      return "hashcode";
883    } else if (type.isPrimitive()) {
884      if (type.equals(Double.TYPE)) {
885        return "double";
886      } else if (type.equals(Float.TYPE)) {
887        return "double";
888      } else if (type.equals(Boolean.TYPE)) {
889        return "boolean";
890      } else {
891        return "int";
892      }
893    } else if (type.getName().equals("java.lang.String")) {
894      // if we are printing the actual array, the rep type is "java.lang.String"
895      if (true) {
896        return "hashcode";
897      }
898      if (asArray) {
899        return "java.lang.String";
900      }
901      // otherwise, it is just a hashcode
902      else {
903        return "hashcode";
904      }
905    } else {
906      return "hashcode";
907    }
908  }
909
910  /**
911   * Determines if type needs a corresponding .class runtime class variable.
912   *
913   * <p>The .class variable is printed for interfaces, abstract classes, and Object. For these
914   * types, the run-time class is always (or, for Object, usually) different than the declared type.
915   * An alternate, and possibly more useful, heuristic would be to print the .class variable for any
916   * type that has subtypes.
917   *
918   * @param type the variable's type
919   */
920  protected static boolean shouldAddRuntimeClass(Class<?> type) {
921    // For some reason, abstracts seems to be set on arrays
922    // and primitives.  This is a temporary fix to get things
923    // close.
924    if (type.isPrimitive()) {
925      return false;
926    }
927    if (type.isArray()) {
928      Class<?> eltType = type.getComponentType();
929      assert eltType != null; // because type is an array
930      return !eltType.isPrimitive();
931    } else if (type.getName().equals("java.lang.Object")) {
932      // Objects
933      // System.out.println ("type is object " + type);
934      return true;
935    } else if (Modifier.isAbstract(type.getModifiers())) {
936      // System.out.printf("Type [%s] is abstract %Xh %Xh %s%n", type,
937      //                   type.getModifiers(), Modifier.ABSTRACT,
938      //                   Modifier.toString (type.getModifiers()));
939      return true;
940    } else if (type.isInterface()) {
941      // System.out.println ("type is interface " + type);
942      return true;
943    } else {
944      return false;
945    }
946  }
947
948  /**
949   * Returns whether or not the specified field is visible from the Class current. All fields within
950   * instrumented classes are considered visible from everywhere (to match dfej behavior).
951   */
952  public static boolean isFieldVisible(Class<?> current, Field field) {
953    Class<?> fclass = field.getDeclaringClass();
954    int modifiers = field.getModifiers();
955
956    // If the field is within the current class, it is always visible
957    if (current.equals(fclass)) {
958      return true;
959    }
960
961    if (!std_visibility) {
962      // If the field is in any instrumented class it is always visible
963      synchronized (SharedData.all_classes) {
964        for (ClassInfo ci : SharedData.all_classes) {
965          // System.out.printf("comparing %s vs %s%n", ci.class_name,
966          // fclass.getName());
967          if (ci.class_name.equals(fclass.getName())) {
968            return true;
969          }
970        }
971      }
972    }
973
974    // Otherwise we consider the variable not to be visible, even
975    // though it is.  This mimics dfej behavior
976    if (!std_visibility) {
977      return false;
978    }
979
980    // Everything in the same class is visible
981    if (current == fclass) {
982      return true;
983    }
984
985    // If the field is in the same package, it's visible if it is
986    // not private or protected.
987    if (current.getPackage() != null && current.getPackage().equals(fclass.getPackage())) {
988      return !(Modifier.isPrivate(modifiers) || Modifier.isProtected(modifiers));
989    }
990
991    // The field must be in an unrelated class, it must be marked
992    // public to be visible
993    return Modifier.isPublic(modifiers);
994  }
995
996  // Appends as auxiliary information:
997  // the package name of the declaring class
998  private String appendAuxInfo(Field field) {
999    // int modifiers = field.getModifiers();
1000
1001    Package p = field.getDeclaringClass().getPackage();
1002    String pkgName = (p == null ? null : p.getName());
1003
1004    // System.out.printf("Package name for type  %s is %s%n", type, pkgName);
1005
1006    StringBuilder ret = new StringBuilder();
1007
1008    // In Java 9+ package name is empty string for the unnamed package.
1009    if (pkgName != null && !pkgName.isEmpty()) {
1010      ret.append(" # declaringClassPackageName=" + pkgName);
1011    }
1012
1013    return ret.toString();
1014  }
1015
1016  /**
1017   * Checks for "derived" Chicory variables: .class, .tostring, and java.util.List implementors and
1018   * adds appropriate children to this node.
1019   */
1020  protected void checkForDerivedVariables(Class<?> type, String theName, String offset) {
1021    checkForListDecl(type, theName, offset); // implements java.util.List?
1022
1023    // Not fully implemented yet, don't call
1024    // checkForImplicitList(cinfo, type, name, offset, depth);
1025
1026    checkForRuntimeClass(type, theName, offset); // .class var
1027    checkForString(type, theName, offset); // .tostring var
1028  }
1029
1030  /** Determines if type implements list and prints associated decls, if necessary. */
1031  protected void checkForListDecl(Class<?> type, String theName, String offset) {
1032    if (isArray || type.isPrimitive() || type.isArray()) {
1033      return;
1034    }
1035
1036    // System.out.printf("checking %s %sto for list implementation = %b%n",
1037    //                    type, theName, implementsList (type));
1038
1039    if (implementsList(type)) {
1040      @SuppressWarnings("unchecked")
1041      DaikonVariableInfo child =
1042          new ListInfo(offset + theName + "[]", (Class<? extends List<?>>) type);
1043
1044      addChild(child);
1045
1046      boolean ignore = child.check_for_dup_names();
1047
1048      // CLASSNAME var
1049      if (!ignore) {
1050        DaikonVariableInfo childClass =
1051            new DaikonClassInfo(
1052                offset + theName + "[]" + class_suffix,
1053                classClassName + "[]",
1054                stringClassName + "[]",
1055                offset + theName + "[]",
1056                true);
1057
1058        child.addChild(childClass);
1059      }
1060    }
1061  }
1062
1063  /**
1064   * Checks the given type to see if it requires a .class addition to the decls file. If so, it adds
1065   * the correct child to this node.
1066   */
1067  protected void checkForRuntimeClass(Class<?> type, String theName, String offset) {
1068    if (!shouldAddRuntimeClass(type)) {
1069      return;
1070    }
1071
1072    String postString = ""; // either array braces or an empty string
1073
1074    if (theName.contains("[]") || offset.contains("[]")) {
1075      postString = "[]";
1076    }
1077
1078    // add daikoninfo type
1079    DaikonVariableInfo classInfo =
1080        new DaikonClassInfo(
1081            offset + theName + class_suffix,
1082            classClassName + postString,
1083            stringClassName + postString,
1084            offset + theName,
1085            (offset + theName).contains("[]"));
1086
1087    addChild(classInfo);
1088  }
1089
1090  /**
1091   * Checks the given type to see if it is a string. If so, it adds the correct child to this node.
1092   */
1093  private void checkForString(Class<?> type, String theName, String offset) {
1094    if (!type.equals(String.class)) {
1095      return;
1096    }
1097
1098    String postString = ""; // either array braces or an empty string
1099    if (isArray) {
1100      postString = "[]";
1101    }
1102
1103    // add DaikonVariableInfo type
1104    DaikonVariableInfo stringInfo =
1105        new StringInfo(
1106            offset + theName + ".toString",
1107            stringClassName + postString,
1108            stringClassName + postString,
1109            offset + theName,
1110            isArray);
1111
1112    addChild(stringInfo);
1113  }
1114
1115  /**
1116   * Returns true iff type implements the List interface.
1117   *
1118   * @return true iff type implements the List interface
1119   */
1120  public static boolean implementsList(Class<?> type) {
1121    if (type.equals(java.util.List.class)) {
1122      return true;
1123    }
1124
1125    // System.out.println(type);
1126    Class<?>[] interfaces = type.getInterfaces();
1127    for (Class<?> inter : interfaces) {
1128      // System.out.println("  implements: " + inter.getName());
1129      if (inter.equals(java.util.List.class)) {
1130        return true;
1131      }
1132    }
1133    return false;
1134  }
1135
1136  /**
1137   * Explores the tree one level deeper (see {@link DaikonVariableInfo}). This method adds child
1138   * nodes to this node.
1139   *
1140   * <p>For example: "recurse" on a hashcode array object to print the actual array of values or
1141   * recurse on hashcode variable to print its fields. Also accounts for derived variables (.class,
1142   * .tostring) and "recurses" on arrays (that is, adds a variable to print out the arrays's
1143   * elements as opposed to just the hashcode of the array).
1144   *
1145   * @param theName the name of the variable currently being examined, such as "ballCount"
1146   * @param offset the representation of the variables we have previously examined. For examples,
1147   *     offset could be "this." in which case offset + name would be "this.ballCount.".
1148   */
1149  @RequiresNonNull("#1.clazz")
1150  protected void addChildNodes(
1151      ClassInfo cinfo, Class<?> type, String theName, String offset, int depthRemaining) {
1152
1153    debug_vars.log("enter addChildNodes:%n");
1154    debug_vars.log("  name: %s, offset: %s%n", theName, offset);
1155
1156    if (type.isPrimitive()) {
1157      return;
1158    }
1159
1160    // Convert the internal reflection name for an outer class
1161    // 'this' field to the Java language format.
1162    if (theName.startsWith("this$")) {
1163      theName = type.getName() + ".this";
1164      offset = "";
1165    }
1166
1167    if (type.isArray()) {
1168      // don't go into more than one dimension of a multi-dimensional array
1169      if (isArray) {
1170        return;
1171      }
1172
1173      Class<?> eltType = type.getComponentType();
1174      assert eltType != null; // because type is an array
1175      if (eltType.isPrimitive()) {
1176
1177        DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType);
1178
1179        newChild.check_for_dup_names();
1180
1181        addChild(newChild);
1182      }
1183      // multi-dimensional arrays (not currently used)
1184      else if (eltType.isArray()) {
1185        DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType);
1186
1187        newChild.check_for_dup_names();
1188
1189        addChild(newChild);
1190
1191        debug_vars.log("Array variable");
1192        debug_vars.indent();
1193        newChild.addChildNodes(cinfo, eltType, "", offset + theName + "[]", depthRemaining);
1194        debug_vars.exdent();
1195      }
1196      // array is 1-dimensional and element type is a regular class
1197      else {
1198        DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType);
1199
1200        boolean ignore = newChild.check_for_dup_names();
1201
1202        addChild(newChild);
1203
1204        // Print out the class of each element in the array.
1205        // The offset will only be equal to ""
1206        // if we are examining a local variable (parameter).
1207        if (!ignore) {
1208          if (!theName.equals("return") && !offset.equals("")) {
1209            newChild.checkForRuntimeClass(type, theName + "[]", offset);
1210          }
1211
1212          newChild.checkForString(eltType, theName + "[]", offset);
1213        }
1214        newChild.addClassVars(cinfo, false, eltType, offset + theName + "[].", depthRemaining - 1);
1215      }
1216    }
1217    // regular old class type
1218    else {
1219      debug_vars.log("**Depth Remaining = %d%n", depthRemaining);
1220
1221      if (depthRemaining <= 0) {
1222        // don't recurse any more!
1223        return;
1224      }
1225      if (!systemClass(type)) {
1226        addClassVars(cinfo, false, type, offset + theName + ".", depthRemaining - 1);
1227      }
1228    }
1229    debug_vars.log("exit addChildNodes%n");
1230  }
1231
1232  /**
1233   * Returns whether or not the fields of the specified class should be included, based on whether
1234   * the Class type is a system class or not. Right now, any system classes are excluded, but a
1235   * better way of determining this is probably necessary.
1236   */
1237  public static boolean systemClass(Class<?> type) {
1238    String class_name = type.getName();
1239    // System.out.printf("type name is %s%n", class_name);
1240    return class_name.startsWith("java.") || class_name.startsWith("javax.");
1241  }
1242
1243  /**
1244   * Returns the declared type name of this variable. May include auxiliary information (represented
1245   * as a suffix starting with "#").
1246   *
1247   * @see #getTypeNameOnly()
1248   */
1249  public String getTypeName() {
1250    assert typeName != null : "Type name cannot be null";
1251
1252    return typeName;
1253  }
1254
1255  /**
1256   * Return the type name without aux information.
1257   *
1258   * @see #getTypeName()
1259   */
1260  @SuppressWarnings("signature") // substring
1261  public @BinaryName String getTypeNameOnly() {
1262    return typeName.replaceFirst(" # .*", "");
1263  }
1264
1265  /** Returns the representation type name of this variable. */
1266  public String getRepTypeName() {
1267    assert typeName != null : "Representation type name cannot be null";
1268
1269    return repTypeName;
1270  }
1271
1272  /** Return the rep type name without the constant value. */
1273  public String getRepTypeNameOnly() {
1274    return repTypeName.replaceFirst(" = .*", "");
1275  }
1276
1277  /**
1278   * Returns the constant value of the variable. If the variable is not static and final, or if the
1279   * constant value is not available in the class file, returns null.
1280   */
1281  public @Nullable String get_const_val() {
1282    return const_val;
1283  }
1284
1285  /**
1286   * Returns the function args of the variable. If the variable is not a function, or does not have
1287   * any arguments, returns null.
1288   */
1289  public @Nullable String get_function_args() {
1290    return function_args;
1291  }
1292
1293  /** Returns the comparability information for this variable. */
1294  public String getCompareString() {
1295    assert typeName != null : "Coparability info cannot be null";
1296
1297    return compareInfoString;
1298  }
1299
1300  /** Return true iff the DeclWriter should print this node. */
1301  public boolean declShouldPrint() {
1302    return declShouldPrint;
1303  }
1304
1305  /** Return true iff the DTraceWriter should print this node. */
1306  public boolean dTraceShouldPrint() {
1307    return dtraceShouldPrint;
1308  }
1309
1310  public boolean dTraceShouldPrintChildren() {
1311    return dtraceShouldPrintChildren;
1312  }
1313
1314  /** Compares based on the name of the variable. */
1315  @Pure
1316  @Override
1317  public int compareTo(@GuardSatisfied DaikonVariableInfo this, DaikonVariableInfo dv) {
1318    return name.compareTo(dv.name);
1319  }
1320
1321  /** Returns whether or not this variable is an array. */
1322  public boolean isArray() {
1323    return isArray;
1324  }
1325
1326  /** Returns the direct child that is an array, null if one does not exist. */
1327  public @Nullable DaikonVariableInfo array_child() {
1328    for (DaikonVariableInfo dv : children) {
1329      if (dv.isArray()) {
1330        return dv;
1331      }
1332    }
1333    return null;
1334  }
1335
1336  /** Returns whether or not this variable has a rep type of hashcode. */
1337  public boolean isHashcode() {
1338    return getRepTypeName().equals("hashcode");
1339  }
1340
1341  public boolean isHashcodeArray() {
1342    return getRepTypeName().equals("hashcode[]");
1343  }
1344
1345  /** Returns whether or not the declared type of this variable is int. */
1346  public boolean isInt() {
1347    String[] sarr = getTypeName().split("  *");
1348    return sarr[0].equals("int");
1349  }
1350
1351  /** Returns the kind of the variable (array, field, function, etc) */
1352  public abstract VarKind get_var_kind();
1353
1354  /**
1355   * Returns the name of this variable relative to its enclosing variable. For example the relative
1356   * name for 'this.a' is 'a'.
1357   */
1358  public @Nullable String get_relative_name() {
1359    return null;
1360  }
1361
1362  /** Empty set of variable flags. */
1363  private static EnumSet<VarFlags> empty_var_flags = EnumSet.noneOf(VarFlags.class);
1364
1365  /**
1366   * Returns the variable flags for this variable. Subclasses should call super(), then add in any
1367   * flags that they add.
1368   */
1369  public EnumSet<VarFlags> get_var_flags() {
1370    return empty_var_flags.clone();
1371  }
1372
1373  /** Returns true iff the variable is static. Overridden by subclasses that can be static. */
1374  @Pure
1375  public boolean isStatic() {
1376    return false;
1377  }
1378
1379  /**
1380   * If the variable name has been seen before (which can happen with statics and children of
1381   * statics), set the flags so that the variable is not considered for decl or dtrace and return
1382   * true. Otherwise, do nothing and return false.
1383   *
1384   * @return true if this DaikonVariableInfo should be ignored (should not be printed)
1385   */
1386  private boolean check_for_dup_names() {
1387
1388    if (ppt_statics.contains(name)) {
1389      debug_vars.log("ignoring already included variable %s [%s]", name, getClass());
1390      // if (!isStatic()) {
1391      //   System.out.printf("ignoring already included variable %s [%s]", name, getClass());
1392      // }
1393      declShouldPrint = false;
1394      dtraceShouldPrint = false;
1395      return true;
1396    } else { // new variable
1397      ppt_statics.add(name);
1398      return false;
1399    }
1400  }
1401}