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