001package daikon.chicory; 002 003import daikon.dcomp.DCRuntime; 004import java.lang.reflect.Field; 005import java.lang.reflect.Modifier; 006import java.util.EnumSet; 007import java.util.List; 008import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 009import org.checkerframework.checker.nullness.qual.NonNull; 010import org.checkerframework.checker.nullness.qual.Nullable; 011import org.checkerframework.dataflow.qual.Pure; 012 013/** 014 * The OjbectInfo class is a subtype of DaikonVariableInfo used for variable types which are class 015 * fields. 016 */ 017public class FieldInfo extends DaikonVariableInfo { 018 /** The corresponding Field. */ 019 private Field field; 020 021 /** The offset of this field in its containing class. */ 022 private int field_num; 023 024 /** Whether or not this is a static field. */ 025 private boolean is_static; 026 027 /** Whether or not this field is final. */ 028 private boolean is_final; 029 030 /** Whether or not this field is of a primitive type. */ 031 private boolean is_primitive; 032 033 /** Whether or not this field is an outer this variable. */ 034 private boolean is_outer_this; 035 036 /** 037 * Class that gets the tags for fields. Used by DynComp. Accessed only by methods 038 * DCRuntime.get_field_tag and DCRuntime.get_field_tag_refs_only. 039 */ 040 public DCRuntime.@MonotonicNonNull FieldTag field_tag = null; 041 042 public FieldInfo( 043 String theName, Field field, String typeName, String repTypeName, boolean isArr) { 044 super(theName, typeName, repTypeName, isArr); 045 this.field = field; 046 047 is_static = Modifier.isStatic(field.getModifiers()); 048 is_final = Modifier.isFinal(field.getModifiers()); 049 is_primitive = field.getType().isPrimitive(); 050 is_outer_this = field.getName().startsWith("this$"); 051 052 // Calculate the offset of this field in its class 053 Class<?> clazz = field.getDeclaringClass(); 054 if (!field.getType().isPrimitive() || clazz.isInterface()) { 055 field_num = -1; 056 return; 057 } 058 @SuppressWarnings( 059 "nullness") // Object declares no fields, so clazz != object and so superclass != null 060 @NonNull Class<?> superclass = clazz.getSuperclass(); 061 field_num = num_prim_fields(superclass); 062 for (Field f : clazz.getDeclaredFields()) { 063 if (f.equals(field)) { 064 // System.out.printf("field %s has field num %d%n", field, 065 // field_num); 066 return; 067 } 068 if (Modifier.isStatic(f.getModifiers())) { 069 continue; 070 } 071 if (f.getType().isPrimitive()) { 072 field_num++; 073 } 074 } 075 throw new Error("Can't find " + field + " in " + field.getDeclaringClass()); 076 } 077 078 /** Return the number of primitive fields in clazz and all of its superclasses. */ 079 public static int num_prim_fields(Class<?> clazz) { 080 if (clazz == Object.class) { 081 return 0; 082 } else { 083 @SuppressWarnings("nullness") // clazz != object and so superclass != null 084 int field_cnt = num_prim_fields(clazz.getSuperclass()); 085 for (Field f : clazz.getDeclaredFields()) { 086 if (Modifier.isStatic(f.getModifiers())) { 087 continue; 088 } 089 if (f.getType().isPrimitive()) { 090 field_cnt++; 091 } 092 } 093 return field_cnt; 094 } 095 } 096 097 /** Returns true iff the corresponding field is static. */ 098 @Pure 099 @Override 100 public boolean isStatic() { 101 return is_static; 102 } 103 104 /** Returns true iff the corresponding field is final. */ 105 @Pure 106 public boolean isFinal() { 107 return is_final; 108 } 109 110 @Override 111 @SuppressWarnings("unchecked") 112 public Object getMyValFromParentVal(Object val) { 113 if (isArray) { 114 @SuppressWarnings("unchecked") 115 List<Object> valAsList = (List<Object>) val; 116 return DTraceWriter.getFieldValues(field, valAsList); 117 } else { 118 if (Modifier.isStatic(field.getModifiers())) { 119 return DTraceWriter.getStaticValue(field); 120 } else { 121 return DTraceWriter.getValue(field, val); 122 } 123 } 124 } 125 126 public Field getField() { 127 return field; 128 } 129 130 public Class<?> getType() { 131 return field.getType(); 132 } 133 134 public int get_field_num() { 135 return field_num; 136 } 137 138 @Pure 139 public boolean isPrimitive() { 140 return is_primitive; 141 } 142 143 @MonotonicNonNull Field tag_field = null; 144 145 public Field get_tag_field(String tag_field_name, Class<?> parent_class) { 146 if (tag_field == null) { 147 try { 148 tag_field = parent_class.getDeclaredField(tag_field_name); 149 } catch (Exception e) { 150 throw new Error("can't get field " + tag_field_name + " in " + parent_class, e); 151 } 152 } 153 return tag_field; 154 } 155 156 /** 157 * Returns the kind of this variable. Statics are top level variables, instance variables are 158 * fields. 159 */ 160 @Override 161 public VarKind get_var_kind() { 162 if (isStatic() || is_outer_this) { 163 return VarKind.VARIABLE; 164 } else { 165 return VarKind.FIELD; 166 } 167 } 168 169 /** 170 * Returns the name of this field. Since statics are top level, they have no relative name. Fields 171 * return their field name. 172 */ 173 @Override 174 public @Nullable String get_relative_name() { 175 if (isStatic() || is_outer_this) { 176 return null; 177 } else { 178 String theName = field.getName(); 179 // Convert the internal reflection name for an outer class 180 // 'this' field to the Java language format. 181 if (theName.startsWith("this$")) { 182 theName = field.getType().getName() + ".this"; 183 } 184 return theName; 185 } 186 } 187 188 /* Don't include 'this' in instance variable names 189 public String getName() { 190 if (isStatic()) { 191 return super.getName(); 192 } else { 193 return get_relative_name(); 194 } 195 } 196 */ 197 198 /** Static final fields are NOMOD. */ 199 @Override 200 public EnumSet<VarFlags> get_var_flags() { 201 EnumSet<VarFlags> flags = super.get_var_flags(); 202 int modbits = field.getModifiers(); 203 if (Modifier.isFinal(modbits) && Modifier.isStatic(modbits)) { 204 flags.add(VarFlags.NOMOD); 205 } 206 if (is_outer_this) { 207 flags.add(VarFlags.NON_NULL); 208 } 209 return flags; 210 } 211}