/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.util.LinkedList;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Callbacks;
import org.jikesrvm.VM_Configuration;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.opt.OPT_ClassLoadingDependencyManager;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.objectmodel.VM_FieldLayoutContext;
import org.jikesrvm.objectmodel.VM_JavaHeaderConstants;
import org.jikesrvm.objectmodel.VM_ObjectModel;
import org.jikesrvm.objectmodel.VM_TIBLayoutConstants;
import org.jikesrvm.runtime.VM_Magic;
import org.jikesrvm.runtime.VM_Memory;
import org.jikesrvm.runtime.VM_Runtime;
import org.jikesrvm.runtime.VM_StackBrowser;
import org.jikesrvm.runtime.VM_Statics;
import org.jikesrvm.runtime.VM_SubArchEntrypoints;
import org.jikesrvm.runtime.VM_SubArchStatics;
import org.jikesrvm.util.VM_Synchronizer;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* Description of a java "class" type.<br/>
*
* This description is read from a ".class" file as classes/field/methods
* referenced by the running program need to be bound in to the running image.
*
* @see VM_Type
* @see VM_Array
* @see VM_Primitive
*/
public final class VM_Class extends VM_Type implements VM_Constants, VM_ClassLoaderConstants {
/** Flag for for closed world testing */
public static boolean classLoadingDisabled = false;
/** Constant pool entry for a UTF-8 encoded atom */
public static final byte CP_UTF = 0;
/** Constant pool entry for int literal */
public static final byte CP_INT = 1;
/** Constant pool entry for long literal */
public static final byte CP_LONG = 2;
/** Constant pool entry for float literal */
public static final byte CP_FLOAT = 3;
/** Constant pool entry for double literal */
public static final byte CP_DOUBLE = 4;
/** Constant pool entry for string literal */
public static final byte CP_STRING = 5;
/** Constant pool entry for member (field or method) reference */
public static final byte CP_MEMBER = 6;
/** Constant pool entry for type reference or class literal */
public static final byte CP_CLASS = 7;
/**
* The constant pool holds constants used by the class and the Java
* bytecodes in the methods associated with this class. This
* constant pool isn't that from the class file, instead it has been
* processed during class loading (see {@link #readClass}). The loaded
* class' constant pool has 3 bits of type information (such as
* (see {@link #CP_INT})), the rest of the int holds data as follows:
*
* <ul>
* <li>utf: value is a UTF atom identifier</li>
* <li>int, long, float, double, string: value is an offset in the
* JTOC</li>
* <li>member: value is a member reference identifier</li>
* <li>class: value is a type reference identifier. NB this means
* that class literal bytecodes must first convert the identifier
* in to a JTOC offset.</li>
* </ul>
*/
private final int[] constantPool;
/**
* Constant pool for subarch offsets. Same structure as constant pool
* but provides offsets to the statics in the subarch. Only available if
* initForSubArch() is called
*/
private int[] subArchConstantPool;
/** {@link VM_ClassLoaderConstants} */
private final short modifiers;
/** Super class of this class */
private final VM_Class superClass;
/**
* Non-final list of sub-classes. Classes added as sub-classes are
* loaded.
*/
private VM_Class[] subClasses;
/** Interfaces supported by this class */
private final VM_Class[] declaredInterfaces;
/** Fields of this class */
private final VM_Field[] declaredFields;
/** Methods of this class */
private final VM_Method[] declaredMethods;
/** Declared inner classes, may be null */
private final VM_TypeReference[] declaredClasses;
/** The outerclass, or null if this is not a inner/nested class */
private final VM_TypeReference declaringClass;
/** The enclosing class if this is a local class */
private final VM_TypeReference enclosingClass;
/** The enclosing method if this is a local class */
private final VM_MethodReference enclosingMethod;
/** Name of file .class file was compiled from, may be null */
private final VM_Atom sourceName;
/**
* The signature is a string representing the generic type for this
* class declaration, may be null
*/
private final VM_Atom signature;
/**
* Class initializer method, null if no method or if class is
* initialized (ie class initializer method has been run)
*/
private VM_Method classInitializerMethod;
/**
* current class-loading stage (loaded, resolved, instantiated,
* initializing or initialized)
*/
private byte state;
/**
* current sub-arch class-loading stage (loaded, resolved, instantiated,
* initializing or initialized)
*/
private byte subArchState = CLASS_VACANT;
//
// The following are valid only when "state >= CLASS_RESOLVED".
//
// --- Field size and offset information --- //
/** fields shared by all instances of class */
private VM_Field[] staticFields;
/** fields distinct for each instance of class */
private VM_Field[] instanceFields;
/** offsets of reference-containing instance fields */
private int[] referenceOffsets;
/** Total size of per-instance data, in bytes */
private int instanceSize;
/** The desired alignment for instances of this type. */
private int alignment;
/**
* A field layout helper - used to keep context regarding field layouts.
* Managed by the field layout objects in the VM_ObjectModel.
*/
private VM_FieldLayoutContext fieldLayoutContext = null;
// --- Method-dispatching information --- //
/** static methods of class */
private VM_Method[] staticMethods;
/** constructor methods of class */
private VM_Method[] constructorMethods;
/** virtual methods of class */
private VM_Method[] virtualMethods;
/**
* method that overrides java.lang.Object.finalize()
* null => class does not have a finalizer
*/
private VM_Method finalizeMethod;
/** type and virtual method dispatch table for class */
private Object[] typeInformationBlock;
// --- Annotation support --- //
/**
* If this class is an annotation interface, this is the class that
* implements that interface and can be used to make instances of
* the annotation
*/
private VM_Class annotationClass;
// --- Memory manager supprt --- //
/**
* Is this class type in the bootimage? Types in the boot image can
* be initialized prior to execution (therefore removing runtime
* resolution).
*/
private boolean inBootImage;
/**
* At what offset is the thin lock word to be found in instances of
* objects of this type? A value of -1 indicates that the instances of
* this type do not have inline thin locks.
*/
private Offset thinLockOffset;
/** Reference Count GC: is this type acyclic? */
private boolean acyclic;
/** Cached set of inherited and declared annotations. */
private Annotation[] annotations;
// --- General purpose functions --- //
/**
* Name - something like "java.lang.String".
*/
@Override
public String toString() {
return getDescriptor().classNameFromDescriptor();
}
/**
* Package name - something like "java.lang".
* Returns the empty string if the class is a member of the unnamed package.
*/
public String getPackageName() {
String className = toString();
int lastDot = className.lastIndexOf(".");
return (lastDot >= 0) ? className.substring(0, lastDot) : "";
}
/**
* Stack space requirement in words.
*/
@Override
@Uninterruptible
public int getStackWords() {
return 1;
}
/**
* Space required in memory in bytes.
*/
@Override
@Uninterruptible
public int getMemoryBytes() {
return BYTES_IN_ADDRESS;
}
/**
* If class is an annotation (which means its actually an
* interface), get the class that implements it
*/
VM_Class getAnnotationClass() {
if (VM.VerifyAssertions) VM._assert(this.isAnnotation());
return annotationClass;
}
/**
* An "interface" description rather than a "class" description?
*/
@Uninterruptible
public boolean isInterface() {
return (modifiers & ACC_INTERFACE) != 0;
}
/**
* Usable from other packages?
*/
@Uninterruptible
public boolean isPublic() {
return (modifiers & ACC_PUBLIC) != 0;
}
/**
* Non-subclassable?
*/
@Uninterruptible
public boolean isFinal() {
return (modifiers & ACC_FINAL) != 0;
}
/**
* Non-instantiable?
*/
@Uninterruptible
public boolean isAbstract() {
return (modifiers & ACC_ABSTRACT) != 0;
}
/**
* Use new-style "invokespecial" semantics for method calls in this class?
*/
@Uninterruptible
public boolean isSpecial() {
return (modifiers & ACC_SUPER) != 0;
}
/**
* Not present in source code file?
*/
public boolean isSynthetic() {
return (modifiers & ACC_SYNTHETIC) != 0;
}
/**
* Is enumeration?
*/
public boolean isEnum() {
return (modifiers & ACC_ENUM) != 0;
}
/**
* Annotation type
*/
private boolean isAnnotation() {
return (modifiers & ACC_ANNOTATION) != 0;
}
/**
* @return true if this is a representation of an anonymous class
*/
public boolean isAnonymousClass() {
return (enclosingClass != null) && (enclosingMethod == null);
}
/**
* @return true if this is a representation of a local class, ie
* local to a block of code.
*/
public boolean isLocalClass() {
return enclosingMethod != null;
}
/**
* @return true if this is a representation of a member class
*/
public boolean isMemberClass() {
return ((declaringClass != null) && ((modifiers & ACC_STATIC) == 0));
}
/**
* @return true if this an object of this class could be assigned to Throwable
*/
public boolean isThrowable() {
return (getTypeRef() == VM_TypeReference.JavaLangThrowable) ||
VM_Runtime.isAssignableWith(VM_TypeReference.JavaLangThrowable.resolve(false), this);
}
/**
* Get the modifiers associated with this class {@link
* VM_ClassLoaderConstants}.
*/
public int getModifiers() {
return modifiers & APPLICABLE_TO_CLASSES;
}
/**
* Generic type information for class
*/
public VM_Atom getSignature() {
return signature;
}
/**
* Name of source file from which class was compiled -
* something like "c:\java\src\java\lang\Object.java".
* (null --> "unknown - wasn't recorded by compiler").
*/
public VM_Atom getSourceName() {
return sourceName;
}
/**
* Superclass of this class (null means "no superclass",
* ie. class is "java/lang/Object").
*/
@Uninterruptible
public VM_Class getSuperClass() {
return superClass;
}
/**
* Currently loaded classes that "extend" this class.
*/
@Uninterruptible
public VM_Class[] getSubClasses() {
return subClasses;
}
/**
* Interfaces implemented directly by this class
* (ie. not including superclasses).
*/
@Uninterruptible
public VM_Class[] getDeclaredInterfaces() {
return declaredInterfaces;
}
/**
* Fields defined directly by this class (ie. not including superclasses).
*/
@Uninterruptible
public VM_Field[] getDeclaredFields() {
return declaredFields;
}
/**
* Methods defined directly by this class (ie. not including superclasses).
*/
@Uninterruptible
public VM_Method[] getDeclaredMethods() {
return declaredMethods;
}
/**
* Declared inner and static member classes.
*/
public VM_TypeReference[] getDeclaredClasses() {
return declaredClasses;
}
/**
* Class that declared this class, or null if this is not an
* inner/nested class.
*/
public VM_TypeReference getDeclaringClass() {
return declaringClass;
}
/**
* Class that immediately encloses this class, or null if this is not an
* inner/nested class.
*/
public VM_TypeReference getEnclosingClass() {
return enclosingClass;
}
/**
* Set the resolvedMember in all declared members.
*/
void setResolvedMembers() {
for(VM_Field field: declaredFields) {
/* Make all declared fields appear resolved */
field.getMemberRef().asFieldReference().setResolvedMember(field);
}
for(VM_Method method: declaredMethods) {
/* Make all declared methods appear resolved */
method.getMemberRef().asMethodReference().setResolvedMember(method);
}
if (virtualMethods != null) {
/* Possibly created Miranda methods */
for(VM_Method method: virtualMethods) {
if (method.getDeclaringClass() == this) {
method.getMemberRef().asMethodReference().setResolvedMember(method);
}
}
}
}
/**
* Static initializer method for this class (null -> no static initializer
* or initializer already been run).
*/
@Uninterruptible
public VM_Method getClassInitializerMethod() {
return classInitializerMethod;
}
@Override
Annotation[] getAnnotationsInternal() {
final VM_Class parent = getSuperClass();
if (parent == null) {
return super.getAnnotationsInternal();
}
if (annotations == null) {
final Annotation[] declared = getDeclaredAnnotations();
// Not synchronized as it does not matter if occasionally we create two cached copies
final Annotation[] parentAnnotations = parent.getAnnotations();
int rejected = 0;
for (int i = 0; i < parentAnnotations.length; i++) {
final Annotation pa = parentAnnotations[i];
final Class<? extends Annotation> paType = pa.annotationType();
if (!paType.isAnnotationPresent(Inherited.class)) {
parentAnnotations[i] = null;
rejected++;
} else {
for (final Annotation a : declared) {
if (a.annotationType().equals(paType)) {
parentAnnotations[i] = null;
rejected++;
break;
}
}
}
}
final Annotation[] cache = new Annotation[declared.length + parentAnnotations.length - rejected];
System.arraycopy(declared, 0, cache, 0, declared.length);
int index = declared.length;
for (final Annotation pa : parentAnnotations) {
if (pa != null) cache[index++] = pa;
}
annotations = cache;
}
return annotations;
}
/**
* Find description of a field of this class.
* @param fieldName field name - something like "foo"
* @param fieldDescriptor field descriptor - something like "I"
* @return description (null --> not found)
*/
public VM_Field findDeclaredField(VM_Atom fieldName, VM_Atom fieldDescriptor) {
for (int i = 0, n = declaredFields.length; i < n; ++i) {
VM_Field field = declaredFields[i];
if (field.getName() == fieldName && field.getDescriptor() == fieldDescriptor) {
return field;
}
}
return null;
}
/**
* Find description of a method of this class.
* @param methodName method name - something like "foo"
* @param methodDescriptor method descriptor - something like "()I"
* @return description (null --> not found)
*/
public VM_Method findDeclaredMethod(VM_Atom methodName, VM_Atom methodDescriptor) {
for (int i = 0, n = declaredMethods.length; i < n; ++i) {
VM_Method method = declaredMethods[i];
if (method.getName() == methodName && method.getDescriptor() == methodDescriptor) {
return method;
}
}
return null;
}
/**
* Find description of "public static void main(String[])"
* method of this class.
* @return description (null --> not found)
*/
public VM_Method findMainMethod() {
VM_Atom mainName = VM_Atom.findOrCreateAsciiAtom(("main"));
VM_Atom mainDescriptor = VM_Atom.findOrCreateAsciiAtom(("([Ljava/lang/String;)V"));
VM_Method mainMethod = this.findDeclaredMethod(mainName, mainDescriptor);
if (mainMethod == null || !mainMethod.isPublic() || !mainMethod.isStatic()) {
// no such method
return null;
}
return mainMethod;
}
//
// Constant pool accessors.
//
// The constant pool holds literals and external references used by
// the bytecodes of this class's methods.
// Items are fetched by specifying their "constant pool index".
//
@Uninterruptible
private static int packCPEntry(byte type, int value) {
return (type << 29) | (value & 0x1fffffff);
}
@Uninterruptible
private static byte unpackCPType(int cpValue) {
return (byte) (cpValue >>> 29);
}
@Uninterruptible
private static int unpackSignedCPValue(int cpValue) {
return (cpValue << 3) >> 3;
}
@Uninterruptible
private static int unpackUnsignedCPValue(int cpValue) {
return cpValue & 0x1fffffff;
}
@Uninterruptible
private static boolean packedCPTypeIsClassType(int cpValue) {
return (cpValue & (7 << 29)) == (CP_CLASS << 29);
}
@Uninterruptible
private static int packTempCPEntry(int index1, int index2) {
return (index1 << 16) | (index2 & 0xffff);
}
@Uninterruptible
private static int unpackTempCPIndex1(int cpValue) {
return cpValue >>> 16;
}
@Uninterruptible
private static int unpackTempCPIndex2(int cpValue) {
return cpValue & 0xffff;
}
static int getLiteralSize(int[] constantPool, int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
switch (unpackCPType(cpValue)) {
case CP_INT:
case CP_FLOAT:
return BYTES_IN_INT;
case CP_LONG:
case CP_DOUBLE:
return BYTES_IN_LONG;
case CP_CLASS:
case CP_STRING:
return BYTES_IN_ADDRESS;
default:
VM._assert(NOT_REACHED);
return 0;
}
}
/**
* Get offset of a literal constant, in bytes.
* Offset is with respect to virtual machine's "table of contents" (jtoc).
*/
public Offset getSubarchLiteralOffset(int constantPoolIndex) {
if (VM.VerifyAssertions) VM._assert(this.subArchConstantPool != null);
return getLiteralOffset(this.subArchConstantPool, constantPoolIndex, true);
}
/**
* Get offset of a literal constant, in bytes.
* Offset is with respect to virtual machine's "table of contents" (jtoc).
*/
public Offset getLiteralOffset(int constantPoolIndex) {
return getLiteralOffset(this.constantPool, constantPoolIndex, false);
}
/**
* Get offset of a literal constant, in bytes.
* Offset is with respect to virtual machine's "table of contents" (jtoc).
*/
static Offset getLiteralOffset(int[] constantPool, int constantPoolIndex) {
return getLiteralOffset(constantPool, constantPoolIndex, false);
}
/**
* Get offset of a literal constant, in bytes.
* Offset is with respect to virtual machine's "table of contents" (jtoc).
*/
static Offset getLiteralOffset(int[] constantPool, int constantPoolIndex, boolean forSubArch) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) {
int value = unpackSignedCPValue(cpValue);
byte type = unpackCPType(cpValue);
switch (type) {
case CP_INT:
case CP_FLOAT:
case CP_LONG:
case CP_DOUBLE:
return Offset.fromIntSignExtend(value);
case CP_STRING:
if (forSubArch) {
// only create string constant when needed for subarch
Object stringObj = VM_Statics.getSlotContentsAsObject(Offset.fromIntSignExtend(value));
return Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateObjectLiteral(stringObj));
} else {
return Offset.fromIntSignExtend(value);
}
case CP_CLASS: {
int typeId = unpackUnsignedCPValue(cpValue);
if (forSubArch) {
return Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateClassLiteral(typeId));
} else {
return Offset.fromIntSignExtend(VM_Statics.findOrCreateClassLiteral(typeId));
}
}
default:
VM.sysWriteln("bad constant pool - " + cpValue + " type " + type);
VM._assert(NOT_REACHED);
return Offset.fromIntSignExtend(0xebad0ff5);
}
} else {
if (packedCPTypeIsClassType(cpValue)) {
int typeId = unpackUnsignedCPValue(cpValue);
if (forSubArch) {
return Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateClassLiteral(typeId));
} else {
return Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateClassLiteral(typeId));
}
} else {
int value = unpackSignedCPValue(cpValue);
return Offset.fromIntSignExtend(value);
}
}
}
/**
* Get description of a literal constant.
*/
public byte getLiteralDescription(int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
byte type = unpackCPType(cpValue);
return type;
}
/**
* Get contents of a "typeRef" constant pool entry.
* @return type that was referenced
*/
@Uninterruptible
public VM_TypeReference getTypeRef(int constantPoolIndex) {
return getTypeRef(constantPool, constantPoolIndex);
}
/**
* Get contents of a "typeRef" constant pool entry.
* @return type that was referenced
*/
@Uninterruptible
static VM_TypeReference getTypeRef(int[] constantPool, int constantPoolIndex) {
if (constantPoolIndex != 0) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_CLASS);
return VM_TypeReference.getTypeRef(unpackUnsignedCPValue(cpValue));
} else {
return null;
}
}
/**
* Get contents of a "methodRef" constant pool entry.
*/
@Uninterruptible
public VM_MethodReference getMethodRef(int constantPoolIndex) {
return getMethodRef(constantPool, constantPoolIndex);
}
/**
* Get contents of a "methodRef" constant pool entry.
*/
@Uninterruptible
static VM_MethodReference getMethodRef(int[] constantPool, int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_MEMBER);
return (VM_MethodReference) VM_MemberReference.getMemberRef(unpackUnsignedCPValue(cpValue));
}
/**
* Get contents of a "fieldRef" constant pool entry.
*/
@Uninterruptible
public VM_FieldReference getFieldRef(int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_MEMBER);
return (VM_FieldReference) VM_MemberReference.getMemberRef(unpackUnsignedCPValue(cpValue));
}
/**
* Get contents of a "methodRef" constant pool entry.
*/
@Uninterruptible
static VM_FieldReference getFieldRef(int[] constantPool, int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_MEMBER);
return (VM_FieldReference) VM_MemberReference.getMemberRef(unpackUnsignedCPValue(cpValue));
}
/**
* Get contents of a "utf" constant pool entry.
*/
@Uninterruptible
VM_Atom getUtf(int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_UTF);
return VM_Atom.getAtom(unpackUnsignedCPValue(cpValue));
}
/**
* Get contents of a "utf" from a constant pool entry.
*/
@Uninterruptible
static VM_Atom getUtf(int[] constantPool, int constantPoolIndex) {
int cpValue = constantPool[constantPoolIndex];
if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_UTF);
return VM_Atom.getAtom(unpackUnsignedCPValue(cpValue));
}
/**
* Return true if the SynchronizedObject annotation is present.
* @see org.vmmagic.pragma.SynchronizedObject
*/
boolean hasSynchronizedObjectAnnotation() {
return isAnnotationDeclared(VM_TypeReference.SynchronizedObject);
}
/**
* Should the methods of this class be compiled with special
* register save/restore logic?
* @see org.vmmagic.pragma.DynamicBridge
*/
@Uninterruptible
public boolean hasDynamicBridgeAnnotation() {
return isAnnotationDeclared(VM_TypeReference.DynamicBridge);
}
/**
* The methods of this class are only called from native code,
* they are compiled with
* a special prolog to interface with the native stack frame.
*/
@Uninterruptible
public boolean hasBridgeFromNativeAnnotation() {
return isAnnotationDeclared(VM_TypeReference.NativeBridge);
}
/**
* Should the methods of this class save incoming registers ?
* @see org.vmmagic.pragma.SaveVolatile
*/
public boolean hasSaveVolatileAnnotation() {
return isAnnotationDeclared(VM_TypeReference.SaveVolatile);
}
//--------------------------------------------------------------------//
// The following are available after the class has been "resolved". //
//--------------------------------------------------------------------//
/**
* Does this class override java.lang.Object.finalize()?
*/
@Override
@Uninterruptible
public boolean hasFinalizer() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return (finalizeMethod != null);
}
/**
* Get finalize method that overrides java.lang.Object.finalize(),
* if one exists
*/
@Uninterruptible
public VM_Method getFinalizer() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return finalizeMethod;
}
/**
* Static fields of this class.
* Values in these fields are shared by all class instances.
*/
@Override
public VM_Field[] getStaticFields() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return staticFields;
}
/**
* Non-static fields of this class (composed with supertypes, if any).
* Values in these fields are distinct for each class instance.
*/
@Override
public VM_Field[] getInstanceFields() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return instanceFields;
}
/**
* Statically dispatched methods of this class.
*/
@Override
public VM_Method[] getStaticMethods() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return staticMethods;
}
/**
* Constructors (<init>) methods of this class.
*/
public VM_Method[] getConstructorMethods() {
if (VM.VerifyAssertions) VM._assert(isResolved(false), "Error class " + this + " is not resolved but " + state);
return constructorMethods;
}
/**
* Virtually dispatched methods of this class
* (composed with supertypes, if any).
*/
@Override
public VM_Method[] getVirtualMethods() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return virtualMethods;
}
/**
* @return All of the interfaces implemented by this class either
* directly or by inheritance from superclass and superinterfaces
* recursively.
*/
public VM_Class[] getAllImplementedInterfaces() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
int count = 0;
int[] doesImplement = getDoesImplement();
for (int mask : doesImplement) {
while (mask != 0) {
count++;
mask &= (mask - 1); // clear lsb 1 bit
}
}
if (count == 0) return emptyVMClass;
VM_Class[] ans = new VM_Class[count];
for (int i = 0, idx = 0; i < doesImplement.length; i++) {
int mask = doesImplement[i];
if (mask != 0) {
for (int j = 0; j < 32; j++) {
if ((mask & (1 << j)) != 0) {
int id = 32 * i + j;
ans[idx++] = VM_Class.getInterface(id);
}
}
}
}
return ans;
}
/**
* Total size, in bytes, of an instance of this class
* (including object header).
*/
@Uninterruptible
public int getInstanceSize() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return instanceSize;
}
/**
* Total size, in bytes, of an instance of this class (including
* object header). Doesn't perform any verification.
*/
@Uninterruptible
public int getInstanceSizeInternal() {
return instanceSize;
}
/**
* Set the size of the instance. Only meant to be called from
* VM_ObjectModel et al. must be called when lock on class object
* is already held (ie from resolve).
*/
@Uninterruptible
public void setInstanceSizeInternal(int size) {
instanceSize = size;
}
/**
* Offsets of reference-containing instance fields of this class type.
* Offsets are with respect to object pointer -- see VM_Field.getOffset().
*/
@Uninterruptible
public int[] getReferenceOffsets() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return referenceOffsets;
}
/**
* Set object representing available holes in the field layout
*/
public VM_FieldLayoutContext getFieldLayoutContext() {
return fieldLayoutContext;
}
/**
* Set object representing available holes in the field layout
*/
public void setFieldLayoutContext(VM_FieldLayoutContext newLayout) {
fieldLayoutContext = isFinal() ? null : newLayout;
}
/**
* @return alignment for instances of this class type
*/
@Uninterruptible
public int getAlignment() {
if (BYTES_IN_ADDRESS == BYTES_IN_DOUBLE) {
return BYTES_IN_ADDRESS;
} else {
return alignment;
}
}
/**
* Set the alignment for instances of this class type
*/
public void setAlignment(int align) {
if (BYTES_IN_ADDRESS != BYTES_IN_DOUBLE) {
if (VM.VerifyAssertions) VM._assert(align >= alignment);
alignment = align;
}
}
/**
* Find specified static method description.
* @param memberName method name - something like "foo"
* @param memberDescriptor method descriptor - something like "I" or "()I"
* @return method description (null --> not found)
*/
public VM_Method findStaticMethod(VM_Atom memberName, VM_Atom memberDescriptor) {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
VM_Method[] methods = getStaticMethods();
for (int i = 0, n = methods.length; i < n; ++i) {
VM_Method method = methods[i];
if (method.getName() == memberName && method.getDescriptor() == memberDescriptor) {
return method;
}
}
return null;
}
/**
* Find specified initializer method description.
* @param memberDescriptor init method descriptor - something like "(I)V"
* @return method description (null --> not found)
*/
public VM_Method findInitializerMethod(VM_Atom memberDescriptor) {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
VM_Method[] methods = getConstructorMethods();
for (int i = 0, n = methods.length; i < n; ++i) {
VM_Method method = methods[i];
if (method.getDescriptor() == memberDescriptor) {
return method;
}
}
return null;
}
/**
* Runtime type information for this class type.
*/
@Override
@Uninterruptible
public Object[] getTypeInformationBlock() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return typeInformationBlock;
}
/**
* Does this slot in the TIB hold a TIB entry?
* @param slot the TIB slot
* @return false
*/
@Override
public boolean isTIBSlotTIB(int slot) {
if (VM.VerifyAssertions) checkTIBSlotIsAccessible(slot);
return false;
}
/**
* Does this slot in the TIB hold code?
* @param slot the TIB slot
* @return true if slot is one that holds a code array reference
*/
@Override
public boolean isTIBSlotCode(int slot) {
if (VM.VerifyAssertions) checkTIBSlotIsAccessible(slot);
return slot >= TIB_FIRST_VIRTUAL_METHOD_INDEX;
}
//--------------------------------------------------------------------//
// Miscellaneous queries. //
//---------------------------------------------------------------------//
/**
* Support for user-written class loaders:
* It's required to find the classloader of the class
* whose method requires another class to be loaded;
* the initiating loader of the required class is the
* defining loader of the requiring class.
*
*
* @param skip specifies the number of frames back from the
* caller to the method whose class's loader is required
*/
public static ClassLoader getClassLoaderFromStackFrame(int skip) {
skip++; // account for stack frame of this function
VM_StackBrowser browser = new VM_StackBrowser();
VM.disableGC();
browser.init();
while (skip-- > 0) browser.up();
VM.enableGC();
return browser.getClassLoader();
}
/**
* Used for accessibility checks in reflection code.
* Find the class of the method that corresponds to the requested frame.
*
* @param skip Specifies the number of frames back from the
* caller to the method whose class is required
*/
public static VM_Class getClassFromStackFrame(int skip) {
skip++; // account for stack frame of this function
VM_StackBrowser browser = new VM_StackBrowser();
VM.disableGC();
browser.init();
while (skip-- > 0) browser.up();
VM.enableGC();
return browser.getCurrentClass();
}
//--------------------------------------------------------------------//
// Load, Resolve, Instantiate, and Initialize //
//--------------------------------------------------------------------//
/**
* Construct a class from its constituent loaded parts
*
* @param typeRef the type reference that was resolved to this class
* @param constantPool array of ints encoding constant value
* @param modifiers {@link org.jikesrvm.classloader.VM_ClassLoaderConstants}
* @param superClass parent of this class
* @param declaredInterfaces array of interfaces this class implements
* @param declaredFields fields of the class
* @param declaredMethods methods of the class
* @param declaredClasses declared inner classes
* @param declaringClass outer class if an inner class
* @param sourceName source file name
* @param classInitializerMethod handle to class initializer method
* @param signature the generic type name for this class
* @param annotations array of runtime visible annotations
*/
private VM_Class(VM_TypeReference typeRef, int[] constantPool, short modifiers, short annoModifiers, VM_Class superClass,
VM_Class[] declaredInterfaces, VM_Field[] declaredFields, VM_Method[] declaredMethods,
VM_TypeReference[] declaredClasses, VM_TypeReference declaringClass, VM_TypeReference enclosingClass,
VM_MethodReference enclosingMethod, VM_Atom sourceName, VM_Method classInitializerMethod,
VM_Atom signature, VM_Annotation[] annotations) {
super(typeRef, 0, annotations, annoModifiers);
if (VM.VerifyAssertions) VM._assert(!getTypeRef().isUnboxedType());
if (VM.VerifyAssertions && null != superClass) VM._assert(!superClass.getTypeRef().isUnboxedType());
// final fields
this.constantPool = constantPool;
this.modifiers = modifiers;
this.superClass = superClass;
this.declaredInterfaces = declaredInterfaces;
this.declaredFields = declaredFields;
this.declaredMethods = declaredMethods;
this.declaredClasses = declaredClasses;
this.declaringClass = declaringClass;
this.enclosingClass = enclosingClass;
this.enclosingMethod = enclosingMethod;
this.sourceName = sourceName;
this.classInitializerMethod = classInitializerMethod;
this.signature = signature;
// non-final fields
this.subClasses = emptyVMClass;
state = CLASS_LOADED;
this.subArchConstantPool = null;
// we're about to leak a reference to 'this' force memory to be
// consistent
VM_Magic.sync();
if (superClass != null) {
// MUST wait until end of constructor to 'publish' the subclass link.
// If we do this earlier, then people can see an incomplete VM_Class object
// by traversing the subclasses of our superclass!
superClass.addSubClass(this);
}
VM_Callbacks.notifyClassLoaded(this);
if (VM.TraceClassLoading && VM.runningVM) {
VM.sysWriteln("VM_Class: (end) load file " + typeRef.getName());
}
if (VM.verboseClassLoading) VM.sysWrite("[Loaded " + toString() + "]\n");
}
/**
* Create an instance of a VM_Class.
* @param typeRef the cannonical type reference for this type.
* @param input the data stream from which to read the class's description.
*/
static VM_Class readClass(VM_TypeReference typeRef, DataInputStream input) throws ClassFormatError, IOException {
if (classLoadingDisabled) {
throw new RuntimeException("ClassLoading Disabled : " + typeRef);
}
if (VM.TraceClassLoading && VM.runningVM) {
VM.sysWrite("VM_Class: (begin) load file " + typeRef.getName() + "\n");
}
int magic = input.readInt();
if (magic != 0xCAFEBABE) {
throw new ClassFormatError("bad magic number " + Integer.toHexString(magic));
}
// Get the class file version number and check to see if it is a version
// that we support.
int minor = input.readUnsignedShort();
int major = input.readUnsignedShort();
switch (major) {
case 45:
case 46:
case 47:
case 48:
case 49: // we support all variants of these major versions so the minor number doesn't matter.
break;
case 50: // we only support up to 50.0 (ie Java 1.6.0)
if (minor == 0) break;
default:
throw new UnsupportedClassVersionError("unsupported class file version " + major + "." + minor);
}
//
// pass 1: read constant pool
//
int[] constantPool = new int[input.readUnsignedShort()];
byte[] tmpTags = new byte[constantPool.length];
// note: slot 0 is unused
for (int i = 1; i < constantPool.length; i++) {
tmpTags[i] = input.readByte();
switch (tmpTags[i]) {
case TAG_UTF: {
byte[] utf = new byte[input.readUnsignedShort()];
input.readFully(utf);
int atomId = VM_Atom.findOrCreateUtf8Atom(utf).getId();
constantPool[i] = packCPEntry(CP_UTF, atomId);
break;
}
case TAG_UNUSED:
if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
break;
case TAG_INT: {
int literal = input.readInt();
int offset = VM_Statics.findOrCreateIntSizeLiteral(literal);
constantPool[i] = packCPEntry(CP_INT, offset);
break;
}
case TAG_FLOAT: {
int literal = input.readInt();
int offset = VM_Statics.findOrCreateIntSizeLiteral(literal);
constantPool[i] = packCPEntry(CP_FLOAT, offset);
break;
}
case TAG_LONG: {
long literal = input.readLong();
int offset = VM_Statics.findOrCreateLongSizeLiteral(literal);
constantPool[i] = packCPEntry(CP_LONG, offset);
i++;
break;
}
case TAG_DOUBLE: {
long literal = input.readLong();
int offset = VM_Statics.findOrCreateLongSizeLiteral(literal);
constantPool[i] = packCPEntry(CP_DOUBLE, offset);
i++;
break;
}
case TAG_TYPEREF:
constantPool[i] = input.readUnsignedShort();
break;
case TAG_STRING:
constantPool[i] = input.readUnsignedShort();
break;
case TAG_FIELDREF:
case TAG_METHODREF:
case TAG_INTERFACE_METHODREF: {
int classDescriptorIndex = input.readUnsignedShort();
int memberNameAndDescriptorIndex = input.readUnsignedShort();
constantPool[i] = packTempCPEntry(classDescriptorIndex, memberNameAndDescriptorIndex);
break;
}
case TAG_MEMBERNAME_AND_DESCRIPTOR: {
int memberNameIndex = input.readUnsignedShort();
int descriptorIndex = input.readUnsignedShort();
constantPool[i] = packTempCPEntry(memberNameIndex, descriptorIndex);
break;
}
default:
throw new ClassFormatError("bad constant pool");
}
}
//
// pass 2: post-process type and string constant pool entries
// (we must do this in a second pass because of forward references)
//
try {
for (int i = 1; i < constantPool.length; i++) {
switch (tmpTags[i]) {
case TAG_LONG:
case TAG_DOUBLE:
++i;
break;
case TAG_TYPEREF: { // in: utf index
VM_Atom typeName = getUtf(constantPool, constantPool[i]);
int typeRefId =
VM_TypeReference.findOrCreate(typeRef.getClassLoader(), typeName.descriptorFromClassName()).getId();
constantPool[i] = packCPEntry(CP_CLASS, typeRefId);
break;
} // out: type reference id
case TAG_STRING: { // in: utf index
VM_Atom literal = getUtf(constantPool, constantPool[i]);
int offset = VM_Statics.findOrCreateStringLiteral(literal);
constantPool[i] = packCPEntry(CP_STRING, offset);
break;
} // out: jtoc slot number
}
}
} catch (java.io.UTFDataFormatException x) {
throw new ClassFormatError(x.toString());
}
//
// pass 3: post-process type field and method constant pool entries
//
for (int i = 1; i < constantPool.length; i++) {
switch (tmpTags[i]) {
case TAG_LONG:
case TAG_DOUBLE:
++i;
break;
case TAG_FIELDREF:
case TAG_METHODREF:
case TAG_INTERFACE_METHODREF: { // in: classname+membername+memberdescriptor indices
int bits = constantPool[i];
int classNameIndex = unpackTempCPIndex1(bits);
int memberNameAndDescriptorIndex = unpackTempCPIndex2(bits);
int memberNameAndDescriptorBits = constantPool[memberNameAndDescriptorIndex];
int memberNameIndex = unpackTempCPIndex1(memberNameAndDescriptorBits);
int memberDescriptorIndex = unpackTempCPIndex2(memberNameAndDescriptorBits);
VM_TypeReference tref = getTypeRef(constantPool, classNameIndex);
VM_Atom memberName = getUtf(constantPool, memberNameIndex);
VM_Atom memberDescriptor = getUtf(constantPool, memberDescriptorIndex);
VM_MemberReference mr = VM_MemberReference.findOrCreate(tref, memberName, memberDescriptor);
int mrId = mr.getId();
constantPool[i] = packCPEntry(CP_MEMBER, mrId);
break;
} // out: VM_MemberReference id
}
}
short modifiers = input.readShort();
int myTypeIndex = input.readUnsignedShort();
VM_TypeReference myTypeRef = getTypeRef(constantPool, myTypeIndex);
if (myTypeRef != typeRef) {
// eg. file contains a different class than would be
// expected from its .class file name
throw new ClassFormatError("expected class \"" +
typeRef.getName() +
"\" but found \"" +
myTypeRef.getName() +
"\"");
}
VM_TypeReference superType = getTypeRef(constantPool, input.readUnsignedShort()); // possibly null
VM_Class superClass = null;
// hack to prevent VM_CodeArray's from resolving their fake superclass
if (typeRef.name.toString().equals("Lorg/jikesrvm/cellspu/VM_CodeArray;")
|| typeRef.name.toString().equals("Lorg/jikesrvm/ia32/VM_CodeArray;")
|| typeRef.name.toString().equals("Lorg/jikesrvm/ppc/VM_CodeArray;"))
{
superType = VM_TypeReference.JavaLangObject;
}
if (((modifiers & ACC_INTERFACE) == 0) && (superType != null)) {
superClass = superType.resolve(false).asClass();
}
int numInterfaces = input.readUnsignedShort();
VM_Class[] declaredInterfaces;
if (numInterfaces == 0) {
declaredInterfaces = emptyVMClass;
} else {
declaredInterfaces = new VM_Class[numInterfaces];
for (int i = 0; i < numInterfaces; ++i) {
VM_TypeReference inTR = getTypeRef(constantPool, input.readUnsignedShort());
declaredInterfaces[i] = inTR.resolve(false).asClass();
}
}
int numFields = input.readUnsignedShort();
VM_Field[] declaredFields;
if (numFields == 0) {
declaredFields = emptyVMField;
} else {
declaredFields = new VM_Field[numFields];
for (int i = 0; i < numFields; i++) {
short fmodifiers = input.readShort();
VM_Atom fieldName = getUtf(constantPool, input.readUnsignedShort());
VM_Atom fieldDescriptor = getUtf(constantPool, input.readUnsignedShort());
VM_MemberReference memRef = VM_MemberReference.findOrCreate(typeRef, fieldName, fieldDescriptor);
declaredFields[i] = VM_Field.readField(typeRef, constantPool, memRef, fmodifiers, input);
}
}
int numMethods = input.readUnsignedShort();
VM_Method[] declaredMethods;
VM_Method classInitializerMethod = null;
if (numMethods == 0) {
declaredMethods = emptyVMMethod;
} else {
declaredMethods = new VM_Method[numMethods];
for (int i = 0; i < numMethods; i++) {
short mmodifiers = input.readShort();
VM_Atom methodName = getUtf(constantPool, input.readUnsignedShort());
VM_Atom methodDescriptor = getUtf(constantPool, input.readUnsignedShort());
VM_MemberReference memRef = VM_MemberReference.findOrCreate(typeRef, methodName, methodDescriptor);
VM_Method method = VM_Method.readMethod(typeRef, constantPool, memRef, mmodifiers, input);
declaredMethods[i] = method;
if (method.isClassInitializer()) {
classInitializerMethod = method;
}
}
}
VM_TypeReference[] declaredClasses = null;
VM_Atom sourceName = null;
VM_TypeReference declaringClass = null;
VM_Atom signature = null;
VM_Annotation[] annotations = null;
VM_TypeReference enclosingClass = null;
VM_MethodReference enclosingMethod = null;
// Read attributes.
for (int i = 0, n = input.readUnsignedShort(); i < n; ++i) {
VM_Atom attName = getUtf(constantPool, input.readUnsignedShort());
int attLength = input.readInt();
// Class attributes
if (attName == VM_ClassLoader.sourceFileAttributeName && attLength == 2) {
sourceName = getUtf(constantPool, input.readUnsignedShort());
} else if (attName == VM_ClassLoader.innerClassesAttributeName) {
// Parse InnerClasses attribute, and use the information to populate
// the list of declared member classes. We do this so we can
// support the java.lang.Class.getDeclaredClasses()
// and java.lang.Class.getDeclaredClass methods.
int numberOfClasses = input.readUnsignedShort();
declaredClasses = new VM_TypeReference[numberOfClasses];
for (int j = 0; j < numberOfClasses; ++j) {
int innerClassInfoIndex = input.readUnsignedShort();
int outerClassInfoIndex = input.readUnsignedShort();
int innerNameIndex = input.readUnsignedShort();
int innerClassAccessFlags = input.readUnsignedShort();
if (innerClassInfoIndex != 0 && outerClassInfoIndex == myTypeIndex && innerNameIndex != 0) {
// This looks like a declared inner class.
declaredClasses[j] = getTypeRef(constantPool, innerClassInfoIndex);
}
if (innerClassInfoIndex == myTypeIndex) {
if (outerClassInfoIndex != 0) {
declaringClass = getTypeRef(constantPool, outerClassInfoIndex);
if (enclosingClass == null) {
// TODO: is this the null test necessary?
enclosingClass = declaringClass;
}
}
if ((innerClassAccessFlags & (ACC_PRIVATE | ACC_PROTECTED)) != 0) {
modifiers &= ~(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED);
}
modifiers |= innerClassAccessFlags;
}
}
} else if (attName == VM_ClassLoader.syntheticAttributeName) {
modifiers |= ACC_SYNTHETIC;
} else if (attName == VM_ClassLoader.enclosingMethodAttributeName) {
int enclosingClassIndex = input.readUnsignedShort();
enclosingClass = getTypeRef(constantPool, enclosingClassIndex);
int enclosingMethodIndex = input.readUnsignedShort();
if (enclosingMethodIndex != 0) {
int memberNameIndex = constantPool[enclosingMethodIndex] >>> BITS_IN_SHORT;
int memberDescriptorIndex = constantPool[enclosingMethodIndex] & ((1 << BITS_IN_SHORT) - 1);
VM_Atom memberName = getUtf(constantPool, memberNameIndex);
VM_Atom memberDescriptor = getUtf(constantPool, memberDescriptorIndex);
enclosingMethod =
VM_MemberReference.findOrCreate(enclosingClass, memberName, memberDescriptor).asMethodReference();
}
} else if (attName == VM_ClassLoader.signatureAttributeName) {
signature = VM_Class.getUtf(constantPool, input.readUnsignedShort());
} else if (attName == VM_ClassLoader.runtimeVisibleAnnotationsAttributeName) {
annotations = VM_AnnotatedElement.readAnnotations(constantPool, input, 2, typeRef.getClassLoader());
} else {
input.skipBytes(attLength);
}
}
short annoModifiers = createAnnoModifer (annotations);
return new VM_Class(typeRef,
constantPool,
modifiers,
annoModifiers,
superClass,
declaredInterfaces,
declaredFields,
declaredMethods,
declaredClasses,
declaringClass,
enclosingClass,
enclosingMethod,
sourceName,
classInitializerMethod,
signature,
annotations);
}
/**
* Loads a given class into the subarch structures so that it can be used by
* code compiled to run on the subordinate architecture. Does the subarch equivilent of
* what readClass() does for the main architecture - ensures constant values are loaded in
* the subarch JTOC and other static fields are reserved.
*/
public void load(boolean forSubArch) {
if (forSubArch && !isLoaded(true)) {
if (superClass != null) {
superClass.load(forSubArch);
}
for (VM_Class declaredInterface : declaredInterfaces) {
declaredInterface.load(forSubArch);
}
subArchConstantPool = new int [constantPool.length];
for (int i=0; i<constantPool.length; i++) {
int cpValue = constantPool[i];
int value = unpackSignedCPValue(cpValue);
byte type = unpackCPType(cpValue);
switch (type) {
case CP_UTF:
// TODO - do something here for subarch UTF constants
// for now just copy VM_Atom id across
subArchConstantPool[i] = cpValue;
break;
case CP_INT:
case CP_FLOAT: {
int literal = VM_Statics.getSlotContentsAsInt(Offset.fromIntSignExtend(value));
int subArchOffset = VM_SubArchStatics.findOrCreateIntSizeLiteral(literal);
subArchConstantPool[i] = packCPEntry(type, subArchOffset);
break;
}
case CP_LONG:
case CP_DOUBLE: {
long literal = VM_Statics.getSlotContentsAsLong(Offset.fromIntSignExtend(value));
int subArchOffset = VM_SubArchStatics.findOrCreateLongSizeLiteral(literal);
subArchConstantPool[i] = packCPEntry(type, subArchOffset);
i++;
break;
}
case CP_STRING:
subArchConstantPool[i] = packCPEntry(type, value); // create string constants on demand
break;
case CP_CLASS:
// TODO - do something here for subarch typeref's
// for now just copy VM_TypeReference id across
subArchConstantPool[i] = cpValue;
break;
case CP_MEMBER:
// TODO - do something here for subarch memberref's
// for now just copy VM_MemberReference id across
subArchConstantPool[i] = cpValue;
break;
default:
VM.sysWriteln("bad constant pool - " + type + " for " + this);
throw new ClassFormatError("bad constant pool");
}
}
subArchState = CLASS_LOADED;
}
}
/**
* Generate size and offset information for members of this class and
* allocate space in jtoc for static fields, static methods, and virtual
* method table.
* Side effects: superclasses and superinterfaces are resolved.
*/
@Override
public synchronized void resolve(boolean forSubArch) {
if (forSubArch) {
resolveForSubArch();
} else {
resolveForMainArch();
}
}
/**
* Generate size and offset information for members of this class and
* allocate space in jtoc for static fields, static methods, and virtual
* method table.
* Side effects: superclasses and superinterfaces are resolved.
*/
private void resolveForMainArch() {
if (isResolved(false)) return;
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (begin) resolve " + this);
if (VM.VerifyAssertions) VM._assert(state == CLASS_LOADED);
// Resolve superclass and super interfaces
//
if (superClass != null) {
superClass.resolveForMainArch();
}
for (VM_Class declaredInterface : declaredInterfaces) {
declaredInterface.resolveForMainArch();
}
if (isInterface()) {
if (VM.VerifyAssertions) VM._assert(superClass == null);
depth = 1;
thinLockOffset = Offset.max();
} else if (superClass == null) {
if (VM.VerifyAssertions) VM._assert(isJavaLangObjectType());
instanceSize = VM_ObjectModel.computeScalarHeaderSize(this);
alignment = BYTES_IN_ADDRESS;
thinLockOffset = VM_ObjectModel.defaultThinLockOffset();
} else {
depth = superClass.depth + 1;
thinLockOffset = superClass.thinLockOffset;
instanceSize = superClass.instanceSize;
fieldLayoutContext = superClass.fieldLayoutContext;
alignment = superClass.alignment;
}
if (hasSynchronizedObjectAnnotation() || this == VM_Type.JavaLangClassType) {
VM_ObjectModel.allocateThinLock(this);
}
if (VM.verboseClassLoading) VM.sysWrite("[Preparing " + this + "]\n");
// build field and method lists for this class
//
{
VM_FieldVector staticFields = new VM_FieldVector();
VM_FieldVector instanceFields = new VM_FieldVector();
VM_MethodVector staticMethods = new VM_MethodVector();
VM_MethodVector constructorMethods = new VM_MethodVector();
VM_MethodVector virtualMethods = new VM_MethodVector();
// start with fields and methods of superclass
//
if (superClass != null) {
VM_Field[] fields = superClass.getInstanceFields();
for (int i = 0, n = fields.length; i < n; ++i) {
instanceFields.addElement(fields[i]);
}
VM_Method[] methods = superClass.getVirtualMethods();
for (int i = 0, n = methods.length; i < n; ++i) {
virtualMethods.addElement(methods[i]);
}
}
// append fields defined by this class
//
VM_Field[] fields = getDeclaredFields();
for (int i = 0, n = fields.length; i < n; ++i) {
VM_Field field = fields[i];
if (field.isStatic()) {
staticFields.addElement(field);
} else {
instanceFields.addElement(field);
}
}
// append/overlay methods defined by this class
//
VM_Method[] methods = getDeclaredMethods();
for (int i = 0, n = methods.length; i < n; ++i) {
VM_Method method = methods[i];
if (VM.VerifyUnint) {
if (method.isUninterruptible() && method.isSynchronized()) {
if (VM.ParanoidVerifyUnint || !method.hasLogicallyUninterruptibleAnnotation()) {
VM.sysWriteln("WARNING: " + method + " cannot be both uninterruptible and synchronized");
}
}
}
if (method.isObjectInitializer()) {
VM_Callbacks.notifyMethodOverride(method, null);
constructorMethods.addElement(method);
} else if (method.isStatic()) {
if (!method.isClassInitializer()) {
VM_Callbacks.notifyMethodOverride(method, null);
staticMethods.addElement(method);
}
} else { // Virtual method
if (method.isSynchronized()) {
VM_ObjectModel.allocateThinLock(this);
}
// method could override something in superclass - check for it
//
int superclassMethodIndex = -1;
for (int j = 0, m = virtualMethods.size(); j < m; ++j) {
VM_Method alreadyDefinedMethod = virtualMethods.elementAt(j);
if (alreadyDefinedMethod.getName() == method.getName() &&
alreadyDefinedMethod.getDescriptor() == method.getDescriptor()) {
// method already defined in superclass
superclassMethodIndex = j;
break;
}
}
if (superclassMethodIndex == -1) {
VM_Callbacks.notifyMethodOverride(method, null);
virtualMethods.addElement(method); // append
} else {
VM_Method superc = virtualMethods.elementAt(superclassMethodIndex);
if (VM.VerifyUnint) {
if (!superc.isInterruptible() && method.isInterruptible()) {
VM.sysWriteln("WARNING: interruptible " + method + " overrides uninterruptible " + superc);
}
}
VM_Callbacks.notifyMethodOverride(method, superc);
virtualMethods.setElementAt(method, superclassMethodIndex); // override
}
}
}
// Deal with Miranda methods.
// If this is an abstract class, then for each
// interface that this class implements, ensure that a corresponding virtual
// method is declared. If one is not, then create an abstract method to fill the void.
if (!isInterface() && isAbstract()) {
for (VM_Class I : declaredInterfaces) {
VM_Method[] iMeths = I.getVirtualMethods();
outer:
for (VM_Method iMeth : iMeths) {
VM_Atom iName = iMeth.getName();
VM_Atom iDesc = iMeth.getDescriptor();
for (int k = 0; k < virtualMethods.size(); k++) {
VM_Method vMeth = virtualMethods.elementAt(k);
if (vMeth.getName() == iName && vMeth.getDescriptor() == iDesc) continue outer;
}
VM_MemberReference mRef = VM_MemberReference.findOrCreate(typeRef, iName, iDesc);
virtualMethods.addElement(new VM_AbstractMethod(getTypeRef(),
mRef,
(short) (ACC_ABSTRACT | ACC_PUBLIC),
(short) 0,
iMeth.getExceptionTypes(),
null,
null,
null,
null));
}
}
}
// If this is an interface, inherit methods from its superinterfaces
if (isInterface()) {
for (VM_Class declaredInterface : declaredInterfaces) {
VM_Method[] meths = declaredInterface.getVirtualMethods();
for (VM_Method meth : meths) {
virtualMethods.addUniqueElement(meth);
}
}
}
this.staticFields = staticFields.finish();
this.instanceFields = instanceFields.finish();
this.staticMethods = staticMethods.finish();
this.constructorMethods = constructorMethods.finish();
this.virtualMethods = virtualMethods.finish();
}
// begin a new class block in statics
if (VM_Configuration.BuildForSubordinate) {
this.numericStaticsBlock = VM_Statics.beginNextClassNumeric();
this.referenceStaticsBlock = VM_Statics.beginNextClassReference();
this.numericStaticsLength = 0;
this.referenceStaticsLength = 0;
}
// allocate space for class fields
//
for (int i = 0, n = staticFields.length; i < n; ++i) {
VM_Field field = staticFields[i];
VM_TypeReference fieldType = field.getType();
if (!(field.isFinal() && field.getConstantValueIndex() > 0)) {
if (fieldType.isReferenceType()) {
field.setOffset(VM_Statics.allocateReferenceSlot(false));
this.referenceStaticsLength += BYTES_IN_ADDRESS;
} else if (field.getType().getMemoryBytes() <= BYTES_IN_INT) {
field.setOffset(VM_Statics.allocateNumericSlot(BYTES_IN_INT, false));
this.numericStaticsLength += BYTES_IN_INT;
} else {
field.setOffset(VM_Statics.allocateNumericSlot(BYTES_IN_LONG, false));
this.numericStaticsLength += BYTES_IN_LONG;
}
}
}
// begin a new class block in statics
if (VM_Configuration.BuildForSubordinate) {
// numerics statics grow downwards
numericStaticsBlock = Offset.fromIntSignExtend(VM_Memory.alignDown(numericStaticsBlock.minus(numericStaticsLength).toInt(), BYTES_IN_QUAD));
}
// stick final fields outwith subarch reachable block
for (int i = 0, n = staticFields.length; i < n; ++i) {
VM_Field field = staticFields[i];
VM_TypeReference fieldType = field.getType();
if (field.isFinal()) {
if (field.getConstantValueIndex() > 0) {
if (fieldType.isReferenceType()) {
field.setOffset(VM_Statics.allocateReferenceSlot(true));
} else if (field.getType().getMemoryBytes() <= BYTES_IN_INT) {
field.setOffset(VM_Statics.allocateNumericSlot(BYTES_IN_INT, true));
} else {
field.setOffset(VM_Statics.allocateNumericSlot(BYTES_IN_LONG, true));
}
}
// (SJF): Serialization nastily accesses even final private static
// fields via pseudo-reflection! So, we must shove the
// values of final static fields into the JTOC. Now
// seems to be a good time.
setFinalStaticJTOCEntry(field, field.getOffset(), false);
}
}
// lay out instance fields
//
VM_ObjectModel.layoutInstanceFields(this);
// count reference fields
int referenceFieldCount = 0;
for (int i = 0, n = instanceFields.length; i < n; ++i) {
VM_Field field = instanceFields[i];
if (field.getType().isReferenceType()) {
referenceFieldCount += 1;
}
}
// record offsets of those instance fields that contain references
//
referenceOffsets = MM_Interface.newReferenceOffsetArray(referenceFieldCount);
for (int i = 0, j = 0, n = instanceFields.length; i < n; ++i) {
VM_Field field = instanceFields[i];
if (field.getType().isReferenceType()) {
referenceOffsets[j++] = field.getOffset().toInt();
}
}
// Allocate space for <init> method pointers
//
for (int i = 0, n = constructorMethods.length; i < n; ++i) {
VM_Method method = constructorMethods[i];
method.setOffset(VM_Statics.allocateReferenceSlot(true));
}
// Allocate space for static method pointers
//
for (int i = 0, n = staticMethods.length; i < n; ++i) {
VM_Method method = staticMethods[i];
if (method.isClassInitializer()) {
method.setOffset(Offset.fromIntZeroExtend(0xebad0ff5)); // should never be used.
} else {
method.setOffset(VM_Statics.allocateReferenceSlot(true));
}
}
// create "type information block" and initialize its first four words
if (isInterface()) {
// the TIB for an Interface doesn't need space for IMT and VTable; will never be used.
typeInformationBlock = MM_Interface.newTIB(TIB_FIRST_INTERFACE_METHOD_INDEX);
} else {
typeInformationBlock = MM_Interface.newTIB(TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethods.length);
}
VM_Statics.setSlotContents(getTibOffset(), typeInformationBlock);
// Initialize dynamic type checking data structures
typeInformationBlock[TIB_TYPE_INDEX] = this;
typeInformationBlock[TIB_SUPERCLASS_IDS_INDEX] = VM_DynamicTypeCheck.buildSuperclassIds(this);
typeInformationBlock[TIB_DOES_IMPLEMENT_INDEX] = VM_DynamicTypeCheck.buildDoesImplement(this);
if (VM_JavaHeaderConstants.SUBARCH_CLASS_IDX_IN_HEADER) {
subArchTocIdx = Offset.fromIntZeroExtend(VM_TIBLayoutConstants.NOT_RESOLVED_FOR_SUBARCH);
if (VM.runningVM) {
typeInformationBlock[TIB_SUBARCH_CLASS_IDX] = VM_Magic.addressAsObject(Address.fromIntZeroExtend(VM_TIBLayoutConstants.NOT_RESOLVED_FOR_SUBARCH));
} else {
typeInformationBlock[TIB_SUBARCH_CLASS_IDX] = null;
VM_Magic.bootWriterFixup(typeInformationBlock, TIB_SUBARCH_CLASS_IDX << LOG_BYTES_IN_ADDRESS, VM_TIBLayoutConstants.NOT_RESOLVED_FOR_SUBARCH);
}
}
// (element type for arrays not used classes)
if (!isInterface()) {
// lay out virtual method section of type information block
// (to be filled in by instantiate)
for (int i = 0, n = virtualMethods.length; i < n; ++i) {
VM_Method method = virtualMethods[i];
method.setOffset(Offset.fromIntZeroExtend((TIB_FIRST_VIRTUAL_METHOD_INDEX + i) << LOG_BYTES_IN_ADDRESS));
}
}
// RCGC: Determine if class is inherently acyclic
acyclic = false; // must initially be false for recursive types
boolean foundCyclic = false;
for (VM_Field instanceField : instanceFields) {
VM_TypeReference ft = instanceField.getType();
if (!ft.isResolved(false) || !ft.peekType().isAcyclicReference()) {
foundCyclic = true;
break;
}
}
if (!foundCyclic) {
acyclic = true;
}
state = CLASS_RESOLVED; // can't move this beyond "finalize" code block
// TODO: Make this into a more general listener interface
if (VM.BuildForOptCompiler && VM.writingBootImage) {
classLoadListener.classInitialized(this, true);
}
VM_Callbacks.notifyClassResolved(this);
MM_Interface.notifyClassResolved(this);
// check for a "finalize" method that overrides the one in java.lang.Object
//
finalizeMethod = null;
if (!isInterface()) {
final VM_Method method =
findVirtualMethod(VM_ClassLoader.StandardObjectFinalizerMethodName,
VM_ClassLoader.StandardObjectFinalizerMethodDescriptor);
if (!method.getDeclaringClass().isJavaLangObjectType()) {
finalizeMethod = method;
}
}
// Check if this was an annotation, if so create the class that
// will implement the annotation interface
//
if (isAnnotation()) {
annotationClass = createAnnotationClass(this);
}
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (end) resolve " + this);
}
/**
* Resolves class for subordinate architecture. Also resolves subclasses.
*
*/
private void resolveForSubArch() {
if (!isResolved(true)) {
if (!isResolved(false)) resolve(false);
if (!isLoaded(true)) load(true); // load for subarch
if (VM.VerifyAssertions) VM._assert(isLoaded(true) && isResolved(false));
if (superClass != null) {
superClass.resolveForSubArch();
}
for (VM_Class declaredInterface : declaredInterfaces) {
declaredInterface.resolveForSubArch();
}
for (int i = 0, n = staticFields.length; i < n; ++i) {
VM_Field field = staticFields[i];
VM_TypeReference fieldType = field.getType();
int cpValue = field.getConstantValueIndex();
if (field.isFinal() && field.getConstantValueIndex() > 0) {
// compile time constant in fixed subarch JTOC
setFinalSubArchConst(field, cpValue);
} else {
if (fieldType.isReferenceType()) {
field.setSubArchOffset(field.getOffset().minus(this.referenceStaticsBlock));
} else {
field.setSubArchOffset(field.getOffset().minus(this.numericStaticsBlock));
}
}
}
// record subarch field offsets (use same as main arch)
for (VM_Field field : instanceFields) {
field.setSubArchOffset(field.getOffset());
}
// Initialize subarch tib
if (isInterface()) {
subArchTIB = typeInformationBlock;
} else {
subArchTIB = MM_Interface.newTIB(TIB_FIRST_SUBARCH_METHOD_INDEX + 2*constructorMethods.length + 2*staticMethods.length + 2*virtualMethods.length);
subArchTIB[TIB_TYPE_INDEX] = typeInformationBlock[TIB_TYPE_INDEX];
subArchTIB[TIB_SUPERCLASS_IDS_INDEX] = typeInformationBlock[TIB_SUPERCLASS_IDS_INDEX];
subArchTIB[TIB_DOES_IMPLEMENT_INDEX] = typeInformationBlock[TIB_DOES_IMPLEMENT_INDEX];
for (int i = 0, n = virtualMethods.length; i < n; ++i) {
VM_Method method = virtualMethods[i];
method.setSubArchOffset(Offset.fromIntZeroExtend((TIB_FIRST_SUBARCH_METHOD_INDEX + (2 * i)) << LOG_BYTES_IN_ADDRESS));
// method length goes in the next slot
}
for (int i = 0, n = constructorMethods.length; i < n; i++) {
VM_Method method = constructorMethods[i];
method.setSubArchOffset(Offset.fromIntZeroExtend((TIB_FIRST_SUBARCH_METHOD_INDEX + (2 * virtualMethods.length) + (2 * i)) << LOG_BYTES_IN_ADDRESS));
// method length goes in the next slot
}
for (int i = 0, n = staticMethods.length; i < n; i++) {
VM_Method method = staticMethods[i];
if (method.isEntrypoint()) {
// entrypoint methods are placed in the fixed JTOC
VM_SubArchEntrypoints.OffsetTuple offsets = new VM_SubArchEntrypoints.OffsetTuple();
offsets.methodOffset = VM_SubArchStatics.allocateReferenceSlot();
offsets.sizeOffset = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT);
VM_SubArchEntrypoints.entryPointOffsets.put(method, offsets);
}
method.setSubArchOffset(Offset.fromIntZeroExtend((TIB_FIRST_SUBARCH_METHOD_INDEX + (2 * virtualMethods.length) + (2 * constructorMethods.length) + (2 * i)) << LOG_BYTES_IN_ADDRESS));
// method length goes in the next slot
}
}
subArchTocIdx = VM_SubArchStatics.addNewType(VM_Magic.getTocPointer().plus(numericStaticsBlock),
numericStaticsLength,
VM_Magic.getTocPointer().plus(referenceStaticsBlock),
referenceStaticsLength,
VM_Magic.objectAsAddress(subArchTIB),
subArchTIB.length << LOG_BYTES_IN_ADDRESS);
if (VM_JavaHeaderConstants.SUBARCH_CLASS_IDX_IN_HEADER) {
if (VM.runningVM) {
typeInformationBlock[TIB_SUBARCH_CLASS_IDX] = VM_Magic.addressAsObject(Address.fromIntZeroExtend(subArchTocIdx.toInt() >> LOG_BYTES_IN_ADDRESS));
} else {
typeInformationBlock[TIB_SUBARCH_CLASS_IDX] = null;
VM_Magic.bootWriterFixup(typeInformationBlock, TIB_SUBARCH_CLASS_IDX << LOG_BYTES_IN_ADDRESS, subArchTocIdx.toInt() >> LOG_BYTES_IN_ADDRESS);
}
}
subArchState = CLASS_RESOLVED;
}
}
@Override
public void allBootImageTypesResolved() {
for (VM_Method method : declaredMethods) {
if (method instanceof VM_NormalMethod) {
((VM_NormalMethod)method).recomputeSummary(constantPool);
}
}
}
// RCGC: A reference to class is acyclic if the class is acyclic and
// final (otherwise the reference could be to a subsequently loaded
// cyclic subclass).
//
@Override
@Uninterruptible
public boolean isAcyclicReference() {
return acyclic && isFinal();
}
/**
* Insert the value of a final static field into the JTOC
*/
private void setFinalStaticJTOCEntry(VM_Field field, Offset fieldOffset, boolean forSubArch) {
if (!field.isFinal()) return;
// value Index: index into the classes constant pool.
int valueIndex = field.getConstantValueIndex();
// if there's no value in the constant pool, bail out
if (valueIndex <= 0) return;
Offset literalOffset = field.getDeclaringClass().getLiteralOffset(valueIndex);
// if field is object, should use reference form of setSlotContents.
// But getSlotContentsAsObject() uses Magic to recast as Object, and
// Magic is not allowed when BootImageWriter is executing under JDK,
// so we only do the "proper" thing when the vm is running. This is OK
// for now, because the bootImage is not collected (all object get BIG
// reference counts
//
// VM.runningVM &&
if (VM_Statics.isReference(VM_Statics.offsetAsSlot(fieldOffset))) {
Object obj = VM_Statics.getSlotContentsAsObject(literalOffset);
if (forSubArch) {
VM_SubArchStatics.setSlotContents(fieldOffset, obj);
} else {
VM_Statics.setSlotContents(fieldOffset, obj);
}
} else if (field.getType().getMemoryBytes() <= BYTES_IN_INT) {
// copy one word from constant pool to JTOC
int value = VM_Statics.getSlotContentsAsInt(literalOffset);
if (forSubArch) {
VM_SubArchStatics.setSlotContents(fieldOffset, value);
} else {
VM_Statics.setSlotContents(fieldOffset, value);
}
} else {
// copy two words from constant pool to JTOC
long value = VM_Statics.getSlotContentsAsLong(literalOffset);
if (forSubArch) {
VM_SubArchStatics.setSlotContents(fieldOffset, value);
} else {
VM_Statics.setSlotContents(fieldOffset, value);
}
}
}
private void setFinalSubArchConst(VM_Field field, int valueIndex) {
Offset literalOffset = field.getDeclaringClass().getLiteralOffset(valueIndex);
// if field is object, should use reference form of setSlotContents.
// But getSlotContentsAsObject() uses Magic to recast as Object, and
// Magic is not allowed when BootImageWriter is executing under JDK,
// so we only do the "proper" thing when the vm is running. This is OK
// for now, because the bootImage is not collected (all object get BIG
// reference counts
//
// VM.runningVM &&
if (field.getType().isReferenceType()) {
Object obj = VM_Statics.getSlotContentsAsObject(literalOffset);
Offset fieldOffset = Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateObjectLiteral(obj));
field.setSubArchOffset(fieldOffset);
VM_SubArchStatics.setSlotContents(fieldOffset, obj);
} else if (field.getType().getMemoryBytes() <= BYTES_IN_INT) {
int value = VM_Statics.getSlotContentsAsInt(literalOffset);
Offset fieldOffset = Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateIntSizeLiteral(value));
field.setSubArchOffset(fieldOffset);
VM_SubArchStatics.setSlotContents(fieldOffset, value);
} else {
long value = VM_Statics.getSlotContentsAsLong(literalOffset);
Offset fieldOffset = Offset.fromIntSignExtend(VM_SubArchStatics.findOrCreateLongSizeLiteral(value));
field.setSubArchOffset(fieldOffset);
VM_SubArchStatics.setSlotContents(fieldOffset, value);
}
}
/**
* Compile this class's methods, build type information block, populate jtoc.
* Side effects: superclasses are instantiated.
*/
@Override
public synchronized void instantiate(boolean forSubArch) {
if (forSubArch) {
instantiateForSubArch();
} else {
instantiateForMainArch();
}
}
/**
* Compile this class's methods, build type information block, populate jtoc.
* Side effects: superclasses are instantiated.
*/
private void instantiateForMainArch() {
if (isInstantiated(false)) {
return;
}
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (begin) instantiate " + this);
if (VM.VerifyAssertions) VM._assert(state == CLASS_RESOLVED);
// instantiate superclass
//
if (superClass != null) {
superClass.instantiateForMainArch();
}
if (VM.runningVM) {
// can't instantiate if building bootimage, since this can cause
// class initializer to be lost (when interface is not included in bootimage).
// since we don't need to instantiate/initialize for the purposes of
// dynamic type checking and interface invocation, defer it until runtime
// and the class actually refers to a static field of the interface.
for (VM_Class declaredInterface : declaredInterfaces) {
declaredInterface.instantiateForMainArch();
}
}
if (!isInterface() && !((typeRef.equals(VM_TypeReference.CodeArrayDirect)
|| typeRef.equals(VM_TypeReference.SubArchCodeArrayDirect)))) {
// Initialize slots in the TIB for virtual methods
for (int slot = TIB_FIRST_VIRTUAL_METHOD_INDEX + virtualMethods.length - 1,
i = virtualMethods.length - 1; i >= 0; i--, slot--) {
VM_Method method = virtualMethods[i];
if (method.isPrivate() && method.getDeclaringClass() != this) {
typeInformationBlock[slot] = null; // an inherited private method....will never be invoked via this TIB
} else {
typeInformationBlock[slot] = method.getCurrentEntryCodeArray(false);
}
}
// compile <init> methods and put their addresses into jtoc
for (int i = 0, n = constructorMethods.length; i < n; ++i) {
VM_Method method = constructorMethods[i];
VM_Statics.setSlotContents(method.getOffset(), method.getCurrentEntryCodeArray(false));
}
// compile static methods and put their addresses into jtoc
for (int i = 0, n = staticMethods.length; i < n; ++i) {
// don't bother compiling <clinit> here;
// compile it right before we invoke it in initialize.
// This also avoids putting <clinit>s in the bootimage.
VM_Method method = staticMethods[i];
if (!method.isClassInitializer()) {
VM_Statics.setSlotContents(method.getOffset(), method.getCurrentEntryCodeArray(false));
}
}
}
if (!isInstantiated(true)) {
VM_InterfaceInvocation.initializeDispatchStructures(this);
}
VM_SpecializedMethodManager.notifyTypeInstantiated(this);
if (VM.writingBootImage) {
state = CLASS_INITIALIZED;
} else {
state = CLASS_INSTANTIATED;
}
VM_Callbacks.notifyClassInstantiated(this);
if (VM.writingBootImage) {
VM_Callbacks.notifyClassInitialized(this);
}
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (end) instantiate " + this);
}
private void instantiateForSubArch() {
if (!isInstantiated(true)) {
if (VM.VerifyAssertions) VM._assert(isResolved(true));
// instantiate superclass
//
if (superClass != null) {
superClass.instantiateForSubArch();
}
if (VM.runningVM) {
for (VM_Class declaredInterface : declaredInterfaces) {
declaredInterface.instantiateForSubArch();
}
}
if (!isInterface() && !((typeRef.equals(VM_TypeReference.CodeArrayDirect)
|| typeRef.equals(VM_TypeReference.SubArchCodeArrayDirect)))) {
// Initialize slots in the TIB for virtual methods
for (int i = virtualMethods.length - 1; i >= 0; i--) {
VM_Method method = virtualMethods[i];
if (method.isPrivate() && method.getDeclaringClass() != this) {
subArchTIB[TIB_FIRST_SUBARCH_METHOD_INDEX + 2*i] = null; // an inherited private method....will never be invoked via this TIB
} else {
subArchTIB[TIB_FIRST_SUBARCH_METHOD_INDEX + 2*i] = method.getCurrentEntryCodeArray(true);
}
}
// Initialize slots in the tib for <init> methods
for (int i = constructorMethods.length - 1; i >= 0; i--) {
VM_Method method = constructorMethods[i];
subArchTIB[TIB_FIRST_SUBARCH_METHOD_INDEX + (2 * virtualMethods.length) + 2*i] = method.getCurrentEntryCodeArray(true);
}
// compile static methods and put their addresses into jtoc
for (int i = 0, n = staticMethods.length; i < n; ++i) {
// don't bother compiling <clinit> here;
// compile it right before we invoke it in initialize.
// This also avoids putting <clinit>s in the bootimage.
VM_Method method = staticMethods[i];
if (!method.isClassInitializer()) {
subArchTIB[TIB_FIRST_SUBARCH_METHOD_INDEX + (2 * virtualMethods.length) + (2 * constructorMethods.length) + 2*i] = method.getCurrentEntryCodeArray(true);
if (method.isEntrypoint()) {
// update static copies in the fixed JTOC
VM_SubArchEntrypoints.OffsetTuple offsets = VM_SubArchEntrypoints.entryPointOffsets.get(method);
VM_SubArchStatics.setSlotContents(offsets.methodOffset, method.getCurrentEntryCodeArray(true));
VM_SubArchStatics.setSlotContents(offsets.sizeOffset, method.getSubArchLength());
}
}
}
if (!isInstantiated(false)) {
VM_InterfaceInvocation.initializeDispatchStructures(this);
}
}
if (VM.writingBootImage) {
subArchState = CLASS_INITIALIZED;
} else {
subArchState = CLASS_INSTANTIATED;
}
}
}
/**
* Execute this class's static initializer, <clinit>.
* Side effects: superclasses are initialized, static fields receive
* initial values.
*/
@Override
public synchronized void initialize(boolean forSubArch)
// Doesn't really need declaring.
throws ExceptionInInitializerError {
if (forSubArch) {
initializeForSubArch();
} else {
initializeForMainArch();
}
}
/**
* Execute this class's static initializer, <clinit>.
* Side effects: superclasses are initialized, static fields receive
* initial values.
*/
private void initializeForMainArch()
// Doesn't really need declaring.
throws ExceptionInInitializerError {
if (isInitialized(false)) {
return;
}
if (state == CLASS_INITIALIZING) {
return;
}
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (begin) initialize " + this);
if (VM.VerifyAssertions) VM._assert(state == CLASS_INSTANTIATED);
state = CLASS_INITIALIZING;
if (VM.verboseClassLoading) VM.sysWrite("[Initializing " + this + "]\n");
// run super <clinit>
//
if (superClass != null) {
superClass.initializeForMainArch();
}
// run <clinit>
//
if (classInitializerMethod != null) {
VM_CompiledMethod cm = classInitializerMethod.getCurrentCompiledMethod(false);
while (cm == null) {
classInitializerMethod.compile(false);
cm = classInitializerMethod.getCurrentCompiledMethod(false);
}
if (VM.verboseClassLoading) VM.sysWrite("[Running static initializer for " + this + "]\n");
try {
VM_Magic.invokeClassInitializer((ArchitectureSpecific.VM_CodeArray) cm.getEntryCodeArray());
} catch (Error e) {
throw e;
} catch (Throwable t) {
ExceptionInInitializerError eieio = new ExceptionInInitializerError("While initializing " + this);
eieio.initCause(t);
throw eieio;
}
// <clinit> is no longer needed: reclaim space by removing references to it
classInitializerMethod.invalidateCompiledMethod(cm, false);
classInitializerMethod = null;
}
if (VM.BuildForOptCompiler) {
// report that a class is about to be marked initialized to
// the opt compiler so it can invalidate speculative CHA optimizations
// before an instance of this class could actually be created.
classLoadListener.classInitialized(this, false);
}
state = CLASS_INITIALIZED;
VM_Callbacks.notifyClassInitialized(this);
if (VM.verboseClassLoading) VM.sysWrite("[Initialized " + this + "]\n");
if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("VM_Class: (end) initialize " + this);
}
/**
* Execute this class's static initializer, <clinit>.
* Side effects: superclasses are initialized, static fields receive
* initial values.
*/
private void initializeForSubArch()
// Doesn't really need declaring.
throws ExceptionInInitializerError {
if (isInitialized(true)) {
return;
}
if (subArchState == CLASS_INITIALIZING) {
return;
}
if (VM.VerifyAssertions) VM._assert(subArchState == CLASS_INSTANTIATED);
if (VM.VerifyAssertions) VM._assert(state == CLASS_INITIALIZED);
subArchState = CLASS_INITIALIZING;
if (superClass != null) {
superClass.initializeForSubArch();
}
setAllFinalStaticJTOCEntries(true);
subArchState = CLASS_INITIALIZED;
}
/**
* Copy the values of all static final fields into
* the JTOC. Note: This method should only be run AFTER
* the class initializer has run.
*/
public void setAllFinalStaticJTOCEntries(boolean forSubArch) {
if (VM.VerifyAssertions) VM._assert(isInitialized(false));
for (VM_Field f : getStaticFields()) {
if (f.isFinal()) {
setFinalStaticJTOCEntry(f, forSubArch ? f.getSubArchOffset() : f.getOffset(), forSubArch);
}
}
}
void resolveNativeMethods() {
if (VM.VerifyAssertions) VM._assert(isInitialized(false));
resolveNativeMethodsInternal(getStaticMethods());
resolveNativeMethodsInternal(getVirtualMethods());
}
private void resolveNativeMethodsInternal(VM_Method[] methods) {
for (VM_Method m : methods) {
if (m.isNative()) {
m.replaceCompiledMethod(null, false);
}
}
}
/**
* Unregisters all native methods
*/
public void unregisterNativeMethods() {
if (VM.VerifyAssertions) VM._assert(isInitialized(false));
for (VM_Method m : declaredMethods) {
if (m.isNative()) {
VM_NativeMethod nm = (VM_NativeMethod) m;
nm.unregisterNativeSymbol();
m.replaceCompiledMethod(null, false);
}
}
}
/**
* Add to list of classes that derive from this one.
*/
private void addSubClass(VM_Class sub) {
int n = subClasses.length;
VM_Class[] tmp = new VM_Class[n + 1];
for (int i = 0; i < n; ++i) {
tmp[i] = subClasses[i];
}
tmp[n] = sub;
subClasses = tmp;
}
//------------------------------------------------------------//
// Support for speculative optimizations that may need to
// invalidate compiled code when new classes are loaded.
//
// TODO: Make this into a more general listener API
//------------------------------------------------------------//
public static final VM_ClassLoadingListener classLoadListener =
VM.BuildForOptCompiler ? new OPT_ClassLoadingDependencyManager() : null;
/**
* Given a method declared by this class, update all
* dispatching tables to refer to the current compiled
* code for the method.
*/
public void updateMethod(VM_Method m, boolean forSubArch) {
if (VM.VerifyAssertions) VM._assert(isResolved(forSubArch));
if (VM.VerifyAssertions) VM._assert(m.getDeclaringClass() == this);
if (m.isClassInitializer()) return; // we never put this method in the jtoc anyways!
if (m.isStatic() || m.isObjectInitializer()) {
updateJTOCEntry(m, forSubArch);
} else {
updateVirtualMethod(m, forSubArch);
}
}
/**
* Update the JTOC slot for the given static method to point to
* the current compiled code for the given method.
* NOTE: This method is intentionally not synchronized to avoid deadlocks.
* We instead rely on the fact that we are always updating the JTOC with
* the most recent instructions for the method.
*/
public void updateJTOCEntry(VM_Method m, boolean forSubArch) {
if (VM.VerifyAssertions) VM._assert(m.getDeclaringClass() == this);
if (VM.VerifyAssertions) VM._assert(isResolved(forSubArch));
if (VM.VerifyAssertions) VM._assert(m.isStatic() || m.isObjectInitializer());
if (!forSubArch) {
VM_Statics.setSlotContents(m.getOffset(), m.getCurrentEntryCodeArray(forSubArch));
} else {
VM_SubArchStatics.setSlotContents(m.getSubArchOffset(), m.getCurrentEntryCodeArray(forSubArch));
}
}
/**
* Update this class's TIB entry for the given method to point to
* the current compiled code for the given method.
* NOTE: This method is intentionally not synchronized to avoid deadlocks.
* We instead rely on the fact that we are always updating the JTOC with
* the most recent instructions for the method.
*/
public void updateTIBEntry(VM_Method m, boolean forSubArch) {
if (VM.VerifyAssertions) {
VM_Method vm = findVirtualMethod(m.getName(), m.getDescriptor());
VM._assert(vm == m);
}
if (!forSubArch) {
int index = m.getOffset().toInt() >>> LOG_BYTES_IN_ADDRESS;
typeInformationBlock[index] = m.getCurrentEntryCodeArray(forSubArch);
} else {
int index = m.getSubArchOffset().toInt() >>> LOG_BYTES_IN_ADDRESS;
if (isInterface()) {
index += TIB_FIRST_VIRTUAL_METHOD_INDEX;
} else {
index += TIB_FIRST_VIRTUAL_METHOD_INDEX + VM_Memory.alignUp(virtualMethods.length, 4);
}
typeInformationBlock[index] = m.getCurrentEntryCodeArray(forSubArch);
}
VM_InterfaceInvocation.updateTIBEntry(this, m, forSubArch);
}
/**
* Update method size in tib for subarch method.
*/
public void updateSubArchMethodLength(VM_Method m, int size) {
int index = (m.getSubArchOffset().toInt() + BYTES_IN_ADDRESS) >>> LOG_BYTES_IN_ADDRESS;
if (VM.runningVM) {
subArchTIB[index] = VM_Magic.addressAsObject(Address.fromIntZeroExtend(size));
} else {
VM_Magic.bootWriterFixup(subArchTIB, index << LOG_BYTES_IN_ADDRESS, size);
subArchTIB[index] = null; // filled in by boot imager writer
}
}
/**
* Get subarch length of method from tib
*/
public int getSubArchMethodLength(VM_Method m) {
int index = (m.getSubArchOffset().toInt() + BYTES_IN_ADDRESS) >>> LOG_BYTES_IN_ADDRESS;
if (VM.runningVM) {
return VM_Magic.objectAsAddress(subArchTIB[index]).toInt();
} else {
for (Object[] tuple : VM_Magic.bootImageWriterFixupList) {
if ((tuple[0] == subArchTIB) && ((Integer)tuple[1]).intValue() == (index << LOG_BYTES_IN_ADDRESS)) {
return (Integer)tuple[2];
}
}
VM._assert(NOT_REACHED);
return -1;
}
}
/**
* Update the TIB entry's for all classes that inherit the given method
* to point to the current compiled code for the given method.
* NOTE: This method is intentionally not synchronized to avoid deadlocks.
* We instead rely on the fact that we are always updating the JTOC with
* the most recent instructions for the method.
*/
public void updateVirtualMethod(VM_Method m, boolean forSubArch) {
VM_Method dm = findDeclaredMethod(m.getName(), m.getDescriptor());
if (dm != null && dm != m) return; // this method got overridden
updateTIBEntry(m, forSubArch);
if (m.isPrivate()) return; // can't override
for (VM_Class sc : getSubClasses()) {
if (sc.isResolved(forSubArch)) {
sc.updateVirtualMethod(m, forSubArch);
}
}
}
//------------------------------------------------------------//
// Additional fields and methods for Interfaces //
//------------------------------------------------------------//
private static final VM_Synchronizer interfaceCountLock = new VM_Synchronizer();
private static int interfaceCount = 0;
private static VM_Class[] interfaces = new VM_Class[100];
private int interfaceId = -1;
VM_Method[] noIMTConflictMap; // used by VM_InterfaceInvocation to support resetTIB
/**
* VM_Classes used as Interfaces get assigned an interface id.
* If the class is not an interface, attempting to use this
* id will cause an IncompatibleClassChangeError to be thrown
*/
public int getInterfaceId() {
if (interfaceId == -1) {
assignInterfaceId();
}
return interfaceId;
}
public int getDoesImplementIndex() {
return getInterfaceId() >>> 5;
}
public int getDoesImplementBitMask() {
return 1 << (getInterfaceId() & 31);
}
public static VM_Class getInterface(int id) {
return interfaces[id];
}
private synchronized void assignInterfaceId() {
if (interfaceId == -1) {
synchronized (interfaceCountLock) {
interfaceId = interfaceCount++;
if (interfaceId == interfaces.length) {
VM_Class[] tmp = new VM_Class[interfaces.length * 2];
System.arraycopy(interfaces, 0, tmp, 0, interfaces.length);
interfaces = tmp;
}
interfaces[interfaceId] = this;
}
}
}
//------------------------------------------------------------//
// Additional methods for annotation //
//------------------------------------------------------------//
/**
* Method to create a class representing an implementation of an
* annotation interface ({@link VM_Annotation}). The created class
* must have:
* <ul>
* <li>a method for each in the interface</li>
* <li>a field backing store for the values to be returned by the
* methods</li>
* <li>a constructor that assigns default annotation values to each
* of the field backing store values (if they are given)</li>
* <li>an implementation of: annotationType, equals, hashCode and
* toString</li>
* </ul>
*
* @param annotationInterface the annotation interface this class
* will implement
* @return the implementing class
*/
private static VM_Class createAnnotationClass(VM_Class annotationInterface) {
// Compute name of class based on the name of the annotation interface
VM_Atom annotationClassName = annotationInterface.getDescriptor().annotationInterfaceToAnnotationClass();
// Create a handle to the new synthetic type
VM_TypeReference annotationClass =
VM_TypeReference.findOrCreateInternal(annotationInterface.getClassLoader(), annotationClassName);
if (VM.TraceClassLoading && VM.runningVM) {
VM.sysWrite("VM_Class: (begin) create (load) annotation " + annotationClass.getName() + "\n");
}
// Count the number of default values for this class
int numDefaultFields = 0;
for (VM_Method declaredMethod : annotationInterface.declaredMethods) {
if (declaredMethod.annotationDefault != null) {
numDefaultFields++;
}
}
// The constant pool that will be used by bytecodes in our
// synthetic methods. The constant pool is laid out as:
// * 1 - the fields holding the annotation values
// * 2 - the methods implementing those in the interface
// * 3 - the default values to initialise the class fields to
// * 4 - the object initialiser method
int numFields = annotationInterface.declaredMethods.length;
int numMethods = annotationInterface.declaredMethods.length + 1;
int constantPoolSize = numFields + numMethods + numDefaultFields;
int[] constantPool = new int[constantPoolSize];
// Create fields for class
VM_Field[] annotationFields = new VM_Field[numFields];
for (int i = 0; i < numFields; i++) {
VM_Method currentAnnotationValue = annotationInterface.declaredMethods[i];
VM_Atom newFieldName = VM_Atom.findOrCreateAsciiAtom(currentAnnotationValue.getName().toString() + "_field");
VM_Atom newFieldDescriptor = currentAnnotationValue.getReturnType().getName();
VM_MemberReference newFieldRef =
VM_MemberReference.findOrCreate(annotationClass, newFieldName, newFieldDescriptor);
annotationFields[i] = VM_Field.createAnnotationField(annotationClass, newFieldRef);
constantPool[i] = packCPEntry(CP_MEMBER, newFieldRef.getId());
}
// Create copy of methods from the annotation
VM_Method[] annotationMethods = new VM_Method[numMethods];
for (int i = 0; i < annotationInterface.declaredMethods.length; i++) {
VM_Method currentAnnotationValue = annotationInterface.declaredMethods[i];
VM_Atom newMethodName = currentAnnotationValue.getName();
VM_Atom newMethodDescriptor = currentAnnotationValue.getDescriptor();
VM_MemberReference newMethodRef =
VM_MemberReference.findOrCreate(annotationClass, newMethodName, newMethodDescriptor);
annotationMethods[i] =
VM_Method.createAnnotationMethod(annotationClass,
constantPool,
newMethodRef,
annotationInterface.declaredMethods[i],
i);
constantPool[numFields + i] = packCPEntry(CP_MEMBER, annotationMethods[i].getMemberRef().getId());
}
// Create default value constants
int nextFreeConstantPoolSlot = numFields + annotationInterface.declaredMethods.length;
int[] defaultConstants = new int[numDefaultFields];
for (int i = 0, j = 0; i < annotationInterface.declaredMethods.length; i++) {
Object value = annotationInterface.declaredMethods[i].annotationDefault;
if (value != null) {
if (value instanceof Integer) {
constantPool[nextFreeConstantPoolSlot] =
packCPEntry(CP_INT, VM_Statics.findOrCreateIntSizeLiteral((Integer) value));
defaultConstants[j] = nextFreeConstantPoolSlot;
j++;
nextFreeConstantPoolSlot++;
} else if (value instanceof Boolean) {
constantPool[nextFreeConstantPoolSlot] =
packCPEntry(CP_INT, VM_Statics.findOrCreateIntSizeLiteral((Boolean) value ? 1 : 0));
defaultConstants[j] = nextFreeConstantPoolSlot;
j++;
nextFreeConstantPoolSlot++;
} else if (value instanceof String) {
try {
constantPool[nextFreeConstantPoolSlot] =
packCPEntry(CP_STRING,
VM_Statics.findOrCreateStringLiteral(VM_Atom.findOrCreateUnicodeAtom((String) value)));
} catch (UTFDataFormatException e) {
throw new Error(e);
}
defaultConstants[j] = nextFreeConstantPoolSlot;
j++;
nextFreeConstantPoolSlot++;
} else {
throw new Error("Unhandled default assignment: " + value);
}
}
}
// Create initialiser
int objectInitIndex = nextFreeConstantPoolSlot;
VM_MethodReference baInitMemRef = VM_Annotation.getBaseAnnotationInitMemberReference();
constantPool[objectInitIndex] = packCPEntry(CP_MEMBER, baInitMemRef.getId());
VM_MemberReference initMethodRef =
VM_MemberReference.findOrCreate(annotationClass, baInitMemRef.getName(), baInitMemRef.getDescriptor());
annotationMethods[annotationInterface.declaredMethods.length] =
VM_Method.createAnnotationInit(annotationClass,
constantPool,
initMethodRef,
objectInitIndex,
annotationFields,
annotationInterface.declaredMethods,
defaultConstants);
// Create class
VM_Class klass =
new VM_Class(annotationClass, constantPool, (short) (ACC_SYNTHETIC | ACC_PUBLIC | ACC_FINAL), // modifiers
(short) 0, baInitMemRef.resolveMember(false).getDeclaringClass(), // superClass
new VM_Class[]{annotationInterface}, // declaredInterfaces
annotationFields, annotationMethods, null, null, null, null, null, null, null, null);
annotationClass.setType(klass);
return klass;
}
/**
* Number of [ in descriptor for arrays; -1 for primitives; 0 for
* classes
* @return 0
*/
@Override
@Uninterruptible
public int getDimensionality() {
return 0;
}
/**
* Resolution status.
*/
@Override
@Uninterruptible
public boolean isLoaded(boolean forSubArch) {
if (forSubArch) {
return subArchState >= CLASS_LOADED;
} else {
return state >= CLASS_LOADED;
}
}
/**
* Resolution status.
*/
@Override
@Uninterruptible
public boolean isResolved(boolean forSubArch) {
if (forSubArch) {
return subArchState >= CLASS_RESOLVED;
} else {
return state >= CLASS_RESOLVED;
}
}
/**
* Instantiation status.
*/
@Override
@Uninterruptible
public boolean isInstantiated(boolean forSubArch) {
if (forSubArch) {
return subArchState >= CLASS_INSTANTIATED;
} else {
return state >= CLASS_INSTANTIATED;
}
}
/**
* Initialization status.
*/
@Override
@Uninterruptible
public boolean isInitialized(boolean forSubArch) {
if (forSubArch) {
return subArchState >= CLASS_INITIALIZED;
} else {
return state >= CLASS_INITIALIZED;
}
}
/**
* Only intended to be used by the BootImageWriter
*/
@Override
public void markAsBootImageClass() {
inBootImage = true;
}
/**
* Is this class part of the virtual machine's boot image?
*/
@Override
@Uninterruptible
public boolean isInBootImage() {
return inBootImage;
}
/**
* Get the offset in instances of this type assigned to the thin lock word.
* Offset.max() if instances of this type do not have thin lock words.
* Is only known after class has been resolved.
*/
@Override
@Uninterruptible
public Offset getThinLockOffset() {
if (VM.VerifyAssertions) VM._assert(isResolved(false));
return thinLockOffset;
}
/**
* Set the thin lock offset for instances of this type. Can be called at most once.
* and is invoked from VM_ObjectModel.allocateThinLock (in object models which
* do not allocate thin locks for all scalar object types).
*/
public void setThinLockOffset(Offset offset) {
if (VM.VerifyAssertions) VM._assert(thinLockOffset.isMax());
if (VM.VerifyAssertions) VM._assert(!offset.isMax());
thinLockOffset = offset;
}
/**
* get number of superclasses to Object
*/
@Override
@Uninterruptible
public int getTypeDepth() {
return depth;
}
/**
* Whether or not this is an instance of VM_Class?
* @return false
*/
@Override
@Uninterruptible
public boolean isClassType() {
return true;
}
/**
* Whether or not this is an instance of VM_Array?
* @return true
*/
@Override
@Uninterruptible
public boolean isArrayType() {
return false;
}
/**
* Whether or not this is a primitive type
* @return false
*/
@Override
@Uninterruptible
public boolean isPrimitiveType() {
return false;
}
/**
* @return whether or not this is a reference (ie non-primitive) type.
*/
@Override
@Uninterruptible
public boolean isReferenceType() {
return true;
}
}