/*
* 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 static org.jikesrvm.classloader.ClassLoaderConstants.ACC_ENUM;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_FINAL;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_PRIVATE;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_STATIC;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_SYNTHETIC;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_TRANSIENT;
import static org.jikesrvm.classloader.ClassLoaderConstants.ACC_VOLATILE;
import static org.jikesrvm.classloader.ClassLoaderConstants.APPLICABLE_TO_FIELDS;
import static org.jikesrvm.mm.mminterface.Barriers.NEEDS_OBJECT_GETFIELD_BARRIER;
import static org.jikesrvm.mm.mminterface.Barriers.NEEDS_OBJECT_GETSTATIC_BARRIER;
import static org.jikesrvm.mm.mminterface.Barriers.NEEDS_OBJECT_PUTFIELD_BARRIER;
import static org.jikesrvm.mm.mminterface.Barriers.NEEDS_OBJECT_PUTSTATIC_BARRIER;
import java.io.DataInputStream;
import java.io.IOException;
import org.jikesrvm.VM;
import org.jikesrvm.mm.mminterface.Barriers;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.Statics;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* A field of a java class.
*/
public final class RVMField extends RVMMember {
/**
* constant pool index of field's value (0 --> not a "static final constant")
*/
private final int constantValueIndex;
/**
* The size of the field in bytes
*/
private final byte size;
/**
* Does the field hold a reference value?
*/
private final boolean reference;
/**
* Has the field been made traced?
*/
private boolean madeTraced;
/**
* Create a field.
*
* @param declaringClass the TypeReference object of the class
* that declared this field
* @param memRef the canonical memberReference for this member.
* @param modifiers modifiers associated with this field.
* @param signature generic type of this field.
* @param constantValueIndex constant pool index of constant value
* @param annotations array of runtime visible annotations
*/
private RVMField(TypeReference declaringClass, MemberReference memRef, short modifiers, Atom signature,
int constantValueIndex, RVMAnnotation[] annotations) {
super(declaringClass, memRef, modifiers, signature, annotations);
this.constantValueIndex = constantValueIndex;
TypeReference typeRef = memRef.asFieldReference().getFieldContentsType();
this.size = (byte)typeRef.getMemoryBytes();
this.reference = typeRef.isReferenceType();
this.madeTraced = false;
if (VM.runningVM && isUntraced()) {
VM.sysFail("Untraced field " + toString() + " created at runtime!" +
" Untraced fields must be resolved at build time to ensure that" +
" the Untraced annotation is visible to the compilers!");
}
}
/**
* Reads and creates a field. NB only {@link RVMClass} is allowed to
* create an instance of a RVMField.
*
* @param declaringClass the TypeReference object of the class
* that declared this field
* @param constantPool the constant pool of the class loading this field
* @param memRef the canonical memberReference for this member.
* @param modifiers modifiers associated with this member.
* @param input the DataInputStream to read the field's attributed from
*
* @return the newly created field
* @throws IOException when a skip is shorter than expected
*/
static RVMField readField(TypeReference declaringClass, int[] constantPool, MemberReference memRef,
short modifiers, DataInputStream input) throws IOException {
// Read the attributes, processing the "non-boring" ones
int cvi = 0;
Atom signature = null;
RVMAnnotation[] annotations = null;
for (int i = 0, n = input.readUnsignedShort(); i < n; ++i) {
Atom attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
int attLength = input.readInt();
if (attName == RVMClassLoader.constantValueAttributeName) {
cvi = input.readUnsignedShort();
} else if (attName == RVMClassLoader.syntheticAttributeName) {
modifiers |= ACC_SYNTHETIC;
} else if (attName == RVMClassLoader.signatureAttributeName) {
signature = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
} else if (attName == RVMClassLoader.runtimeVisibleAnnotationsAttributeName) {
annotations = AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader());
} else {
// all other attributes are boring...
int skippedAmount = input.skipBytes(attLength);
if (skippedAmount != attLength) {
throw new IOException("Unexpected short skip");
}
}
}
return new RVMField(declaringClass,
memRef,
(short) (modifiers & APPLICABLE_TO_FIELDS),
signature,
cvi,
annotations);
}
static RVMField createAnnotationField(TypeReference annotationClass, MemberReference memRef) {
return new RVMField(annotationClass, memRef, (short) (ACC_PRIVATE | ACC_SYNTHETIC), null, 0, null);
}
/**
* @return type of this field's value.
*/
@Uninterruptible
public TypeReference getType() {
return memRef.asFieldReference().getFieldContentsType();
}
/**
* @return the number of stackslots that value of this type takes
*/
public int getNumberOfStackSlots() {
return getType().getStackWords();
}
/**
* @return the number of bytes of memory words that a value of this type takes
*/
public int getSize() {
return size;
}
/**
* Does the field hold a reference?
*
* @return {@code true} if this field needs to be traced by the garbage collector
*/
public boolean isTraced() {
return (reference && !isUntraced()) || madeTraced;
}
/**
* Does the field hold a made-traced reference?
*
* @return {@code true} if this field was made traced
*/
@Uninterruptible
public boolean madeTraced() {
return madeTraced;
}
/**
* @return {@code true} if this field holds a reference
*/
public boolean isReferenceType() {
return reference;
}
/**
* @return {@code true} if this field is shared among all instances of this class
*/
@Uninterruptible
public boolean isStatic() {
return (modifiers & ACC_STATIC) != 0;
}
/**
* @return {@code true} if this field may only be assigned once
*/
@Uninterruptible
public boolean isFinal() {
return (modifiers & ACC_FINAL) != 0;
}
/**
* @return {@code true} if the value of this field may not be cached in a register
*/
@Uninterruptible
public boolean isVolatile() {
return (modifiers & ACC_VOLATILE) != 0;
}
/**
* @return {@code true} if this field's value is not to be written/read by persistent object manager
*/
@Uninterruptible
public boolean isTransient() {
return (modifiers & ACC_TRANSIENT) != 0;
}
/**
* @return {@code true} if this field is not present in source code file
*/
public boolean isSynthetic() {
return (modifiers & ACC_SYNTHETIC) != 0;
}
/**
* @return {@code true} if this field is an enum constant
*/
public boolean isEnumConstant() {
return (modifiers & ACC_ENUM) != 0;
}
/**
* Is the field RuntimeFinal? That is can the annotation value be used in
* place a reading the field.
* @return whether the method is final at runtime
*/
public boolean isRuntimeFinal() {
return hasRuntimeFinalAnnotation();
}
/**
* @return {@code true} if this this field is invisible to the memory
* management system.
*/
public boolean isUntraced() {
return hasUntracedAnnotation();
}
/**
* Make this field a traced field by garbage collection. Affects all
* subclasses of the class in which this field is defined.
*/
public void makeTraced() {
madeTraced = true;
getDeclaringClass().makeFieldTraced(this);
}
/**
* Gets the value from the runtime final field.
* @return the value of the field, i.e. if the method is final at runtime or
* not
*/
public boolean getRuntimeFinalValue() {
org.vmmagic.pragma.RuntimeFinal ann;
if (VM.runningVM) {
ann = getAnnotation(org.vmmagic.pragma.RuntimeFinal.class);
} else {
try {
ann = getDeclaringClass().getClassForType().getField(getName().toString())
.getAnnotation(org.vmmagic.pragma.RuntimeFinal.class);
} catch (NoSuchFieldException e) {
throw new Error(e);
}
}
return ann.value();
}
/**
* Get index of constant pool entry containing this
* "static final constant" field's value.
* @return constant pool index (0 --> field is not a "static final constant")
*/
@Uninterruptible
int getConstantValueIndex() {
return constantValueIndex;
}
//-------------------------------------------------------------------//
// Low level support for various reflective operations //
// Because different clients have different error checking //
// requirements, these operations are completely unsafe and we //
// assume that the client has done the required error checking. //
//-------------------------------------------------------------------//
/**
* Read the contents of the field.
* If the contents of this field is an object, return that object.
* If the contents of this field is a primitive, get the value and wrap it in an object.
*
* @param obj the object whose field is to be read
* @return the value of this field. For primitiv types, a boxed value is used.
*/
public Object getObjectUnchecked(Object obj) {
if (isReferenceType()) {
return getObjectValueUnchecked(obj);
} else {
TypeReference type = getType();
if (type.isCharType()) return getCharValueUnchecked(obj);
if (type.isDoubleType()) return getDoubleValueUnchecked(obj);
if (type.isFloatType()) return getFloatValueUnchecked(obj);
if (type.isLongType()) return getLongValueUnchecked(obj);
if (type.isIntType()) return getIntValueUnchecked(obj);
if (type.isShortType()) return getShortValueUnchecked(obj);
if (type.isByteType()) return getByteValueUnchecked(obj);
if (type.isBooleanType()) return getBooleanValueUnchecked(obj);
return null;
}
}
/**
* Read one object ref from heap using RVM object model, GC safe.
* @param obj the object whose field is to be read,
* or {@code null} if the field is static.
* @return the reference described by this RVMField from the given object.
*/
public Object getObjectValueUnchecked(Object obj) {
if (isStatic()) {
if (NEEDS_OBJECT_GETSTATIC_BARRIER && !isUntraced()) {
return Barriers.objectStaticRead(getOffset(), getId());
} else {
return Statics.getSlotContentsAsObject(getOffset());
}
} else {
if (NEEDS_OBJECT_GETFIELD_BARRIER && !isUntraced()) {
return Barriers.objectFieldRead(obj, getOffset(), getId());
} else {
return Magic.getObjectAtOffset(obj, getOffset());
}
}
}
public Word getWordValueUnchecked(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsAddress(getOffset()).toWord();
} else {
return Magic.getWordAtOffset(obj, getOffset());
}
}
public Address getAddressValueUnchecked(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsAddress(getOffset()).toWord().toAddress();
} else {
return Magic.getAddressAtOffset(obj, getOffset());
}
}
public Offset getOffsetValueUnchecked(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsAddress(getOffset()).toWord().toOffset();
} else {
return Magic.getOffsetAtOffset(obj, getOffset());
}
}
public Extent getExtentValueUnchecked(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsAddress(getOffset()).toWord().toExtent();
} else {
return Magic.getExtentAtOffset(obj, getOffset());
}
}
public boolean getBooleanValueUnchecked(Object obj) {
byte bits;
if (isStatic()) {
bits = (byte) Statics.getSlotContentsAsInt(getOffset());
} else {
bits = Magic.getUnsignedByteAtOffset(obj, getOffset());
}
return (bits != 0);
}
public byte getByteValueUnchecked(Object obj) {
if (isStatic()) {
return (byte) Statics.getSlotContentsAsInt(getOffset());
} else {
return Magic.getByteAtOffset(obj, getOffset());
}
}
public char getCharValueUnchecked(Object obj) {
if (isStatic()) {
return (char) Statics.getSlotContentsAsInt(getOffset());
} else {
return Magic.getCharAtOffset(obj, getOffset());
}
}
public short getShortValueUnchecked(Object obj) {
if (isStatic()) {
return (short) Statics.getSlotContentsAsInt(getOffset());
} else {
return Magic.getShortAtOffset(obj, getOffset());
}
}
public int getIntValueUnchecked(Object obj) {
return get32Bits(obj);
}
public long getLongValueUnchecked(Object obj) {
return get64Bits(obj);
}
public float getFloatValueUnchecked(Object obj) {
return Magic.intBitsAsFloat(get32Bits(obj));
}
public double getDoubleValueUnchecked(Object obj) {
return Magic.longBitsAsDouble(get64Bits(obj));
}
private int get32Bits(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsInt(getOffset());
} else {
return Magic.getIntAtOffset(obj, getOffset());
}
}
private long get64Bits(Object obj) {
if (isStatic()) {
return Statics.getSlotContentsAsLong(getOffset());
} else {
return Magic.getLongAtOffset(obj, getOffset());
}
}
/**
* assign one object ref from heap using RVM object model, GC safe.
* @param obj the object whose field is to be modified, or null if the field is static.
* @param ref the object reference to be assigned.
*/
public void setObjectValueUnchecked(Object obj, Object ref) {
if (isStatic()) {
if (NEEDS_OBJECT_PUTSTATIC_BARRIER && !isUntraced()) {
Barriers.objectStaticWrite(ref, getOffset(), getId());
} else {
Statics.setSlotContents(getOffset(), ref);
}
} else {
if (NEEDS_OBJECT_PUTFIELD_BARRIER && !isUntraced()) {
Barriers.objectFieldWrite(obj, ref, getOffset(), getId());
} else {
Magic.setObjectAtOffset(obj, getOffset(), ref);
}
}
}
/**
* assign one object ref from heap using RVM object model, GC safe.
* @param obj the object whose field is to be modified, or null if the field is static.
* @param ref the object reference to be assigned.
*/
public void setWordValueUnchecked(Object obj, Word ref) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), ref);
} else {
if (Barriers.NEEDS_WORD_PUTFIELD_BARRIER) {
Barriers.wordFieldWrite(obj, ref, getOffset(), getId());
} else {
Magic.setWordAtOffset(obj, getOffset(), ref);
}
}
}
public void setAddressValueUnchecked(Object obj, Address ref) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), ref);
} else {
if (Barriers.NEEDS_ADDRESS_PUTFIELD_BARRIER) {
Barriers.addressFieldWrite(obj, ref, getOffset(), getId());
} else {
Magic.setAddressAtOffset(obj, getOffset(), ref);
}
}
}
public void setExtentValueUnchecked(Object obj, Extent ref) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), ref);
} else {
if (Barriers.NEEDS_EXTENT_PUTFIELD_BARRIER) {
Barriers.extentFieldWrite(obj, ref, getOffset(), getId());
} else {
Magic.setExtentAtOffset(obj, getOffset(), ref);
}
}
}
public void setOffsetValueUnchecked(Object obj, Offset ref) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), ref);
} else {
if (Barriers.NEEDS_OFFSET_PUTFIELD_BARRIER) {
Barriers.offsetFieldWrite(obj, ref, getOffset(), getId());
} else {
Magic.setOffsetAtOffset(obj, getOffset(), ref);
}
}
}
public void setBooleanValueUnchecked(Object obj, boolean b) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), b ? 1 : 0);
} else {
if (Barriers.NEEDS_BOOLEAN_PUTFIELD_BARRIER) {
Barriers.booleanFieldWrite(obj, b, getOffset(), getId());
} else {
Magic.setBooleanAtOffset(obj, getOffset(), b);
}
}
}
public void setByteValueUnchecked(Object obj, byte b) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), b);
} else {
if (Barriers.NEEDS_BYTE_PUTFIELD_BARRIER) {
Barriers.byteFieldWrite(obj, b, getOffset(), getId());
} else {
Magic.setByteAtOffset(obj, getOffset(), b);
}
}
}
public void setCharValueUnchecked(Object obj, char c) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), c);
} else {
if (Barriers.NEEDS_CHAR_PUTFIELD_BARRIER) {
Barriers.charFieldWrite(obj, c, getOffset(), getId());
} else {
Magic.setCharAtOffset(obj, getOffset(), c);
}
}
}
public void setShortValueUnchecked(Object obj, short i) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), i);
} else {
if (Barriers.NEEDS_SHORT_PUTFIELD_BARRIER) {
Barriers.shortFieldWrite(obj, i, getOffset(), getId());
} else {
Magic.setShortAtOffset(obj, getOffset(), i);
}
}
}
public void setIntValueUnchecked(Object obj, int i) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), i);
} else {
if (Barriers.NEEDS_INT_PUTFIELD_BARRIER) {
Barriers.intFieldWrite(obj, i, getOffset(), getId());
} else {
Magic.setIntAtOffset(obj, getOffset(), i);
}
}
}
public void setFloatValueUnchecked(Object obj, float f) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), f);
} else {
if (Barriers.NEEDS_FLOAT_PUTFIELD_BARRIER) {
Barriers.floatFieldWrite(obj, f, getOffset(), getId());
} else {
Magic.setFloatAtOffset(obj, getOffset(), f);
}
}
}
public void setLongValueUnchecked(Object obj, long l) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), l);
} else {
if (Barriers.NEEDS_LONG_PUTFIELD_BARRIER) {
Barriers.longFieldWrite(obj, l, getOffset(), getId());
} else {
Magic.setLongAtOffset(obj, getOffset(), l);
}
}
}
public void setDoubleValueUnchecked(Object obj, double d) {
if (isStatic()) {
Statics.setSlotContents(getOffset(), d);
} else {
if (Barriers.NEEDS_DOUBLE_PUTFIELD_BARRIER) {
Barriers.doubleFieldWrite(obj, d, getOffset(), getId());
} else {
Magic.setDoubleAtOffset(obj, getOffset(), d);
}
}
}
}