001package daikon.chicory;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.List;
006import java.util.Map;
007import java.util.regex.Pattern;
008import org.checkerframework.checker.lock.qual.GuardSatisfied;
009import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
010import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
011import org.checkerframework.checker.signature.qual.BinaryName;
012import org.checkerframework.dataflow.qual.SideEffectFree;
013
014/**
015 * Keeps information about a class that is useful for writing out decl and/or dtrace information.
016 * Original information is filled out during the transformation and other information is added after
017 * the class is first loaded.
018 */
019public class ClassInfo {
020
021  /** binary name of the class. */
022  public @BinaryName String class_name;
023
024  // set by initViaReflection()
025  /** reflection object for this class. */
026  public @MonotonicNonNull Class<?> clazz;
027
028  // Does not include class initializers, so each element's .member field
029  // is non-null.
030  /** list of methods in the class. */
031  public List<MethodInfo> method_infos = new ArrayList<>();
032
033  /** This class's classloader. */
034  private ClassLoader loader;
035
036  // traversalClass and traversalObject are set by init_traversal().
037  /** DaikonVariables for the object program point (instance and static variables). */
038  public @MonotonicNonNull RootInfo traversalObject;
039
040  /** DaikonVariables for the class program point (static variables only). */
041  public @MonotonicNonNull RootInfo traversalClass;
042
043  /** Whether or not any methods in this class were instrumented. */
044  public boolean shouldInclude = false;
045
046  /** Mapping from field name to string representation of its value* */
047  // only for static final primitives
048  // which are declared by a CONSTANT VALUE in the code
049  public Map<String, String> staticMap = new HashMap<>();
050
051  /** Create ClassInfo with specified name. */
052  public ClassInfo(@BinaryName String class_name, ClassLoader theLoader) {
053    this.class_name = class_name;
054    loader = theLoader;
055  }
056
057  /** Set the list of methods. */
058  public void set_method_infos(List<MethodInfo> method_infos) {
059    this.method_infos = method_infos;
060  }
061
062  public List<MethodInfo> get_method_infos() {
063    return method_infos;
064  }
065
066  /**
067   * Gets the reflection object Class for this class, and the Method objects for each method that is
068   * already in method_infos.
069   */
070  @EnsuresNonNull("clazz")
071  public void initViaReflection() {
072
073    // get the reflection class
074    try {
075      // clazz = Class.forName (class_name);
076      // change class loading
077
078      // TODO referring class?
079      clazz = Class.forName(class_name, false, loader);
080
081    } catch (Exception e) {
082      throw new Error(e);
083    }
084
085    for (MethodInfo mi : method_infos) {
086      mi.initViaReflection();
087    }
088
089    if (ChicoryPremain.shouldDoPurity()) {
090      for (String pureMeth : ChicoryPremain.getPureMethods()) {
091        if (isInThisClass(pureMeth)) {
092          boolean foundMatch = false;
093          for (MethodInfo mi : method_infos) {
094            assert mi.member != null
095                : "@AssumeAssertion(nullness): member of method_infos have"
096                    + " .member field"; // dependent type
097            // System.out.printf("compare %s to pure %s%n",
098            //                  mi.member.toString() , pureMeth);
099            if (mi.member.toString().trim().equals(pureMeth)) {
100              foundMatch = true;
101              break;
102            }
103          }
104
105          if (!foundMatch) {
106            // pureMeth must not actually be in this class
107            throw new Error(
108                String.format("Could not find pure method \"%s\" in class %s", pureMeth, clazz));
109          }
110        }
111      }
112    }
113  }
114
115  /**
116   * Determines if fully qualified method name is in this class. Example methodName:
117   *
118   * <pre>public static String mypackage.MyClass.doStuff(int, java.lang.Object)</pre>
119   */
120  private boolean isInThisClass(String methodName) {
121    // A heuristical way to determine if the method is in this class.
122    // Match anything of the form: ____class_name.____(____
123    // Where ____ corresponds to any sequence of characters
124    return methodName.matches(".*" + Pattern.quote(class_name) + "\\..*\\(.*");
125  }
126
127  /**
128   * Initializes the daikon variables for the object and class ppts.
129   *
130   * @param depth how deeply to nest variables, as in "a.b.field"
131   */
132  public void init_traversal(int depth) {
133    if (traversalObject == null) {
134      traversalObject = RootInfo.getObjectPpt(this, depth);
135    }
136    if (traversalClass == null) {
137      traversalClass = RootInfo.getClassPpt(this, depth);
138    }
139    assert traversalObject != null : class_name;
140    assert traversalClass != null : class_name;
141  }
142
143  @SideEffectFree
144  @Override
145  public String toString(@GuardSatisfied ClassInfo this) {
146    return String.format("ClassInfo %s [%s] %s", System.identityHashCode(this), class_name, clazz);
147  }
148}