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}