001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.classloader;
014
015import org.jikesrvm.VM;
016import org.vmmagic.pragma.Uninterruptible;
017
018/**
019 * A class to represent the reference in a class file to a field.
020 */
021public final class FieldReference extends MemberReference {
022
023  /**
024   * The field's type
025   */
026  private final TypeReference fieldContentsType;
027
028  /**
029   * The RVMField that this field reference resolved to ({@code null} if not yet resolved).
030   */
031  private RVMField resolvedMember;
032
033  /**
034   * @param tr a type reference
035   * @param mn the field or method name
036   * @param d the field or method descriptor
037   * @param id the new ID of the member were a new member required
038   */
039  FieldReference(TypeReference tr, Atom mn, Atom d, int id) {
040    super(tr, mn, d, id);
041    fieldContentsType = TypeReference.findOrCreate(tr.getClassLoader(), d);
042  }
043
044  /**
045   * @return the type of the field's value
046   */
047  @Uninterruptible
048  public TypeReference getFieldContentsType() {
049    return fieldContentsType;
050  }
051
052  /**
053   * @return number of stack slots that a value of this type takes
054   */
055  public int getNumberOfStackSlots() {
056    return getFieldContentsType().getStackWords();
057  }
058
059  /**
060   * @return size of the field's value, in bytes.
061   */
062  @Uninterruptible
063  public int getSize() {
064    return fieldContentsType.getMemoryBytes();
065  }
066
067  /**
068   * Do this and that definitely refer to different fields?
069   *
070   * @param that the reference to compare with
071   * @return {@code true} if the fields are definitely different, {@code false}
072   *  if it's not known (e.g. because at least one of the field references is
073   *  unresolved)
074   */
075  public boolean definitelyDifferent(FieldReference that) {
076    if (this == that) return false;
077    if (getName() != that.getName() || getDescriptor() != that.getDescriptor()) {
078      return true;
079    }
080    RVMField mine = peekResolvedField();
081    RVMField theirs = that.peekResolvedField();
082    if (mine == null || theirs == null) return false;
083    return mine != theirs;
084  }
085
086  /**
087   * Do this and that definitely refer to the same field?
088   *
089   * @param that the reference to compare with
090   * @return {@code true} if the fields are definitely the same, {@code false}
091   *  if it's not known (e.g. because at least one of the field references is
092   *  unresolved)
093   */
094  public boolean definitelySame(FieldReference that) {
095    if (this == that) return true;
096    if (getName() != that.getName() || getDescriptor() != that.getDescriptor()) {
097      return false;
098    }
099    RVMField mine = peekResolvedField();
100    RVMField theirs = that.peekResolvedField();
101    if (mine == null || theirs == null) return false;
102    return mine == theirs;
103  }
104
105  /**
106   * @return {@code true} if the field reference already been resolved into a target method
107   */
108  public boolean isResolved() {
109    return resolvedMember != null;
110  }
111
112  void setResolvedMember(RVMField it) {
113    if (VM.VerifyAssertions) VM._assert(resolvedMember == null || resolvedMember == it);
114    resolvedMember = it;
115  }
116
117  /**
118   * Find the RVMField that this field reference refers to using
119   * the search order specified in JVM spec 5.4.3.2.
120   * @return the RVMField that this method ref resolved to or null if it cannot be resolved.
121   */
122  public RVMField peekResolvedField() {
123    if (resolvedMember != null) return resolvedMember;
124
125    // Hasn't been resolved yet. Try to do it now without triggering class loading.
126    RVMClass declaringClass = (RVMClass) type.peekType();
127    if (declaringClass == null) return null;
128    return resolveInternal(declaringClass);
129  }
130
131  /**
132   * Find the RVMField that this field reference refers to using
133   * the search order specified in JVM spec 5.4.3.2.
134   * @return the RVMField that this method ref resolved to.
135   */
136  public synchronized RVMField resolve() {
137    if (resolvedMember != null) return resolvedMember;
138
139    // Hasn't been resolved yet. Do it now triggering class loading if necessary.
140    return resolveInternal((RVMClass) type.resolve());
141  }
142
143  private RVMField resolveInternal(RVMClass declaringClass) {
144    if (!declaringClass.isResolved()) {
145      declaringClass.resolve();
146    }
147    for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
148      // Look in this class
149      RVMField it = c.findDeclaredField(name, descriptor);
150      if (it != null) {
151        resolvedMember = it;
152        return resolvedMember;
153      }
154      // Look at all interfaces directly and indirectly implemented by this class.
155      for (RVMClass i : c.getDeclaredInterfaces()) {
156        it = searchInterfaceFields(i);
157        if (it != null) {
158          resolvedMember = it;
159          return resolvedMember;
160        }
161      }
162    }
163    throw new NoSuchFieldError(this.toString());
164  }
165
166  private RVMField searchInterfaceFields(RVMClass c) {
167    RVMField it = c.findDeclaredField(name, descriptor);
168    if (it != null) return it;
169    for (RVMClass i : c.getDeclaredInterfaces()) {
170      it = searchInterfaceFields(i);
171      if (it != null) return it;
172    }
173    return null;
174  }
175}