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}