/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import org.jikesrvm.VM;
import org.vmmagic.pragma.Uninterruptible;
/**
* A class to represent the reference in a class file to a field.
*/
public final class FieldReference extends MemberReference {
/**
* The field's type
*/
private final TypeReference fieldContentsType;
/**
* The RVMField that this field reference resolved to ({@code null} if not yet resolved).
*/
private RVMField resolvedMember;
/**
* @param tr a type reference
* @param mn the field or method name
* @param d the field or method descriptor
* @param id the new ID of the member were a new member required
*/
FieldReference(TypeReference tr, Atom mn, Atom d, int id) {
super(tr, mn, d, id);
fieldContentsType = TypeReference.findOrCreate(tr.getClassLoader(), d);
}
/**
* @return the type of the field's value
*/
@Uninterruptible
public TypeReference getFieldContentsType() {
return fieldContentsType;
}
/**
* @return number of stack slots that a value of this type takes
*/
public int getNumberOfStackSlots() {
return getFieldContentsType().getStackWords();
}
/**
* @return size of the field's value, in bytes.
*/
@Uninterruptible
public int getSize() {
return fieldContentsType.getMemoryBytes();
}
/**
* Do this and that definitely refer to different fields?
*
* @param that the reference to compare with
* @return {@code true} if the fields are definitely different, {@code false}
* if it's not known (e.g. because at least one of the field references is
* unresolved)
*/
public boolean definitelyDifferent(FieldReference that) {
if (this == that) return false;
if (getName() != that.getName() || getDescriptor() != that.getDescriptor()) {
return true;
}
RVMField mine = peekResolvedField();
RVMField theirs = that.peekResolvedField();
if (mine == null || theirs == null) return false;
return mine != theirs;
}
/**
* Do this and that definitely refer to the same field?
*
* @param that the reference to compare with
* @return {@code true} if the fields are definitely the same, {@code false}
* if it's not known (e.g. because at least one of the field references is
* unresolved)
*/
public boolean definitelySame(FieldReference that) {
if (this == that) return true;
if (getName() != that.getName() || getDescriptor() != that.getDescriptor()) {
return false;
}
RVMField mine = peekResolvedField();
RVMField theirs = that.peekResolvedField();
if (mine == null || theirs == null) return false;
return mine == theirs;
}
/**
* @return {@code true} if the field reference already been resolved into a target method
*/
public boolean isResolved() {
return resolvedMember != null;
}
void setResolvedMember(RVMField it) {
if (VM.VerifyAssertions) VM._assert(resolvedMember == null || resolvedMember == it);
resolvedMember = it;
}
/**
* Find the RVMField that this field reference refers to using
* the search order specified in JVM spec 5.4.3.2.
* @return the RVMField that this method ref resolved to or null if it cannot be resolved.
*/
public RVMField peekResolvedField() {
if (resolvedMember != null) return resolvedMember;
// Hasn't been resolved yet. Try to do it now without triggering class loading.
RVMClass declaringClass = (RVMClass) type.peekType();
if (declaringClass == null) return null;
return resolveInternal(declaringClass);
}
/**
* Find the RVMField that this field reference refers to using
* the search order specified in JVM spec 5.4.3.2.
* @return the RVMField that this method ref resolved to.
*/
public synchronized RVMField resolve() {
if (resolvedMember != null) return resolvedMember;
// Hasn't been resolved yet. Do it now triggering class loading if necessary.
return resolveInternal((RVMClass) type.resolve());
}
private RVMField resolveInternal(RVMClass declaringClass) {
if (!declaringClass.isResolved()) {
declaringClass.resolve();
}
for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
// Look in this class
RVMField it = c.findDeclaredField(name, descriptor);
if (it != null) {
resolvedMember = it;
return resolvedMember;
}
// Look at all interfaces directly and indirectly implemented by this class.
for (RVMClass i : c.getDeclaredInterfaces()) {
it = searchInterfaceFields(i);
if (it != null) {
resolvedMember = it;
return resolvedMember;
}
}
}
throw new NoSuchFieldError(this.toString());
}
private RVMField searchInterfaceFields(RVMClass c) {
RVMField it = c.findDeclaredField(name, descriptor);
if (it != null) return it;
for (RVMClass i : c.getDeclaredInterfaces()) {
it = searchInterfaceFields(i);
if (it != null) return it;
}
return null;
}
}