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}