001package daikon.chicory;
002
003import java.lang.reflect.Constructor;
004import java.lang.reflect.Member;
005import java.lang.reflect.Method;
006import java.lang.reflect.Modifier;
007import java.util.HashMap;
008import java.util.List;
009import org.checkerframework.checker.lock.qual.GuardSatisfied;
010import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
011import org.checkerframework.checker.nullness.qual.RequiresNonNull;
012import org.checkerframework.checker.signature.qual.ClassGetName;
013import org.checkerframework.dataflow.qual.Pure;
014import org.checkerframework.dataflow.qual.SideEffectFree;
015
016/**
017 * Keeps information about a method that is useful for writing out decl and/or dtrace information.
018 * Original information is filled out during the transformation and other information is added the
019 * first time a method is called.
020 */
021@SuppressWarnings("nullness") // to do.  member field is tricky.
022public class MethodInfo {
023
024  /** Class that contains this method. */
025  public ClassInfo class_info;
026
027  /**
028   * Reflection information on this method. Null if a class initializer, {@code <clinit>} (see
029   * {@link #is_class_initializer()}.
030   */
031  // The code often assumes that member != null.
032  public @MonotonicNonNull Member member = null;
033
034  /**
035   * Method name. For example: "public static void sort(int[] arr)" would have method_name "sort".
036   */
037  public String method_name;
038
039  /** Array of argument names for this method. */
040  public String[] arg_names;
041
042  /**
043   * Array of argument types for this method (fully qualified). For example: "public static void
044   * examineObject(Object x)" would have arg_types {"java.lang.Object"}.
045   */
046  public @ClassGetName String[] arg_type_strings;
047
048  /** Array of argument types as classes for this method. */
049  public Class<?>[] arg_types;
050
051  /** Exit locations for this method. */
052  public List<Integer> exit_locations;
053
054  /** Tells whether each exit point in method is instrumented, based on filters. */
055  public List<Boolean> is_included;
056
057  /**
058   * The root of the variable tree for the method entry program point.
059   *
060   * <p>Set by Runtime and read by DeclWriter and DTraceWriter.
061   */
062  public @MonotonicNonNull RootInfo traversalEnter = null;
063
064  /**
065   * The root of the variable tree for the method exit program point(s).
066   *
067   * <p>Set by Runtime and read by DeclWriter and DTraceWriter.
068   */
069  public @MonotonicNonNull RootInfo traversalExit = null;
070
071  /** The number of times this method has been called. */
072  public int call_cnt = 0;
073
074  /** The number of times we have captured the output for this method. */
075  public int capture_cnt = 0;
076
077  /**
078   * Whether or not the method is pure (has no side-effects). Will only be set to true if the {@code
079   * --purity-analysis} command-line option is given to Chicory, and the method returns some value.
080   * Only set during initViaReflection() method.
081   */
082  private boolean isPure;
083
084  /** Creates a MethodInfo with the specified class, arg_names, and exit locations. */
085  public MethodInfo(
086      ClassInfo class_info,
087      String method_name,
088      String[] arg_names,
089      @ClassGetName String[] arg_type_strings,
090      List<Integer> exit_locations,
091      List<Boolean> is_included) {
092
093    this.class_info = class_info;
094    this.method_name = method_name;
095    this.arg_names = arg_names;
096    this.arg_type_strings = arg_type_strings;
097    this.exit_locations = exit_locations;
098    this.is_included = is_included;
099  }
100
101  // Use reserved keyword for basic type rather than signature to
102  // avoid conflicts with user defined types.
103  private static HashMap<String, Class<?>> primitive_classes = new HashMap<>(8);
104
105  static {
106    primitive_classes.put("boolean", Boolean.TYPE);
107    primitive_classes.put("byte", Byte.TYPE);
108    primitive_classes.put("char", Character.TYPE);
109    primitive_classes.put("double", Double.TYPE);
110    primitive_classes.put("float", Float.TYPE);
111    primitive_classes.put("int", Integer.TYPE);
112    primitive_classes.put("long", Long.TYPE);
113    primitive_classes.put("short", Short.TYPE);
114  }
115
116  /** Populates this class with data from reflection. */
117  public void initViaReflection() {
118
119    // Get the Class for each argument type
120    arg_types = new Class<?>[arg_names.length];
121    for (int ii = 0; ii < arg_type_strings.length; ii++) {
122      try {
123        String aname = arg_type_strings[ii];
124        Class<?> c = primitive_classes.get(aname);
125
126        if (c == null) {
127          // c = Class.forName (aname);
128          // change class loading
129          // TODO referring class?
130          c = Class.forName(aname, false, this.class_info.clazz.getClassLoader());
131        }
132
133        arg_types[ii] = c;
134      } catch (Exception e) {
135        throw new Error(
136            "can't find class for "
137                + arg_type_strings[ii]
138                + " in  method "
139                + class_info.class_name
140                + "."
141                + method_name
142                + ": "
143                + e);
144      }
145    }
146
147    // Look up the method
148    try {
149      if (is_class_initializer()) {
150        member = null;
151        // This case DOES occur at run time.  -MDE 1/22/2010
152      } else if (is_constructor()) {
153        member = class_info.clazz.getDeclaredConstructor(arg_types);
154      } else {
155        member = class_info.clazz.getDeclaredMethod(method_name, arg_types);
156      }
157    } catch (Exception e) {
158      throw new Error("can't find method " + method_name, e);
159    }
160
161    if (ChicoryPremain.shouldDoPurity() && (member != null)) {
162      int mod = member.getModifiers();
163
164      // Only consider purity on non-abstract, non-static, and non-constructor
165      // methods which return a value!
166      if (!Modifier.isAbstract(mod)
167          && !Modifier.isStatic(mod)
168          && !(member instanceof Constructor<?>)
169          && !((Method) member).getReturnType().equals(Void.TYPE)) {
170        if (ChicoryPremain.isMethodPure(member)) {
171          isPure = true;
172        }
173      }
174    }
175  }
176
177  /**
178   * Returns true iff this method is a constructor.
179   *
180   * @return true iff this method is a constructor
181   */
182  @Pure
183  public boolean is_constructor() {
184    return method_name.equals("<init>") || method_name.equals("");
185  }
186
187  /**
188   * Returns true iff this method is a class initializer.
189   *
190   * @return true iff this method is a class initializer
191   */
192  @Pure
193  public boolean is_class_initializer() {
194    return method_name.equals("<clinit>");
195  }
196
197  /**
198   * Returns true iff this method is static.
199   *
200   * @return true iff this method is static
201   */
202  @RequiresNonNull("member")
203  @Pure
204  public boolean is_static() {
205    return Modifier.isStatic(member.getModifiers());
206  }
207
208  /**
209   * Initialize the enter and exit daikon variable trees (traversalEnter and traversalExit). The
210   * reflection information must have already been initialized.
211   */
212  /*TO DO: @PostNonNull({"traversalEnter", "traversalExit"})*/
213  public void init_traversal(int depth) {
214
215    traversalEnter = RootInfo.enter_process(this, depth);
216    // System.out.printf("Method %s.%s: %n ", class_info.clazz.getName(),
217    //                    this);
218    // System.out.printf("Enter daikon variable tree%n%s%n",
219    //                    traversalEnter.treeString());
220
221    traversalExit = RootInfo.exit_process(this, depth);
222    // System.out.printf("Exit daikon variable tree%n%s%n",
223    //                    traversalExit.treeString());
224  }
225
226  @SideEffectFree
227  @Override
228  public String toString(@GuardSatisfied MethodInfo this) {
229    String out = "";
230    if (class_info != null) {
231      out = class_info.class_name + ".";
232    }
233    out += method_name + "(";
234    for (int ii = 0; ii < arg_names.length; ii++) {
235      if (ii > 0) {
236        out += ", ";
237      }
238      out += arg_type_strings[ii] + " " + arg_names[ii];
239    }
240    return (out + ")");
241  }
242
243  public boolean isPure() {
244    return isPure;
245  }
246
247  /** Returns the turn type of the method, or Void.TYPE for a constructor. */
248  public Class<?> return_type() {
249    if (member instanceof Method) {
250      Method m = (Method) member;
251      return m.getReturnType();
252    } else {
253      return Void.TYPE;
254    }
255  }
256}