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}