/*
* 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 java.lang;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.JikesRVMSupport;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.net.URL;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.jikesrvm.classloader.Atom;
import org.jikesrvm.classloader.BootstrapClassLoader;
import org.jikesrvm.classloader.MethodReference;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMClassLoader;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.runtime.Callbacks;
import org.jikesrvm.runtime.Reflection;
import org.jikesrvm.runtime.RuntimeEntrypoints;
import org.jikesrvm.runtime.StackBrowser;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.Pure;
/**
* Implementation of java.lang.Class for JikesRVM.
*
* By convention, order methods in the same order
* as they appear in the method summary list of Sun's 1.4 Javadoc API.
*/
public final class Class<T> implements Serializable, Type, AnnotatedElement, GenericDeclaration {
private static final class StaticData {
static final ProtectionDomain unknownProtectionDomain;
static {
Permissions permissions = new Permissions();
permissions.add(new AllPermission());
unknownProtectionDomain = new ProtectionDomain(null, permissions);
}
}
static final long serialVersionUID = 3206093459760846163L;
/**
* This field holds the RVMType object for this class.
*/
final RVMType type;
/**
* This field holds the protection domain of this class.
*/
ProtectionDomain pd;
/**
* The signers of this class
*/
Object[] signers;
/**
* Cached default constructor value
*/
RVMMethod defaultConstructor;
/**
* Prevents this class from being instantiated, except by the
* create method in this class.
*/
private Class(RVMType type) {
this.type = type;
}
/**
* Create a java.lang.Class corresponding to a given RVMType
*/
static <T> Class<T> create(RVMType type) {
Class<T> c = new Class<T>(type);
return c;
}
void setSigners(Object[] signers) {
this.signers = signers;
}
public boolean desiredAssertionStatus() {
return type.getDesiredAssertionStatus();
}
@Pure
public int getModifiers() {
if (type.isClassType()) {
return type.asClass().getOriginalModifiers();
} else if (type.isArrayType()) {
RVMType innermostElementType = type.asArray().getInnermostElementType();
int result = Modifier.FINAL;
if (innermostElementType.isClassType()) {
int component = innermostElementType.asClass().getOriginalModifiers();
result |= (component & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE));
} else {
result |= Modifier.PUBLIC; // primitive
}
return result;
} else {
return Modifier.PUBLIC | Modifier.FINAL;
}
}
@Pure
public String getName() {
return type.toString();
}
public Package getPackage() {
ClassLoader cl = type.getClassLoader();
return cl.getPackage(getPackageName());
}
public ProtectionDomain getProtectionDomain() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(new RuntimePermission("getProtectionDomain"));
}
return pd == null ? StaticData.unknownProtectionDomain : pd;
}
void setProtectionDomain(ProtectionDomain protectionDomain) {
pd = protectionDomain;
}
public URL getResource(String resName) {
throwNPEWhenNameIsNull(resName);
ClassLoader loader = type.getClassLoader();
if (loader == BootstrapClassLoader.getBootstrapClassLoader())
return ClassLoader.getSystemResource(toResourceName(resName));
else
return loader.getResource(toResourceName(resName));
}
public InputStream getResourceAsStream(String resName) {
ClassLoader loader = type.getClassLoader();
if (loader == BootstrapClassLoader.getBootstrapClassLoader())
return ClassLoader.getSystemResourceAsStream(toResourceName(resName));
else
return loader.getResourceAsStream(toResourceName(resName));
}
public Object[] getSigners() {
if (signers == null) {
return null;
} else {
return signers.clone();
}
}
@SuppressWarnings("unchecked")
@Pure
public Class<? super T> getSuperclass() {
if (type.isArrayType()) {
return Object.class;
} else if (type.isClassType()) {
RVMClass myClass = type.asClass();
if (myClass.isInterface()) return null;
RVMType supe = myClass.getSuperClass();
return supe == null ? null : (Class<? super T>) supe.getClassForType();
} else {
return null;
}
}
@Pure
public boolean isAnnotation() {
return type.isClassType() && type.asClass().isAnnotation();
}
@Pure
public boolean isArray() {
return type.isArrayType();
}
@Pure
public boolean isAssignableFrom(Class<?> cls) {
return type.isAssignableFrom(cls.type);
}
@Pure
public boolean isInstance(Object object) {
if (object == null) return false;
if (isPrimitive()) return false;
return isAssignableFrom(object.getClass());
}
@Pure
public boolean isInterface() {
return type.isClassType() && type.asClass().isInterface();
}
@Pure
public boolean isPrimitive() {
return type.isPrimitiveType();
}
@Pure
public boolean isSynthetic() {
return type.isClassType() && type.asClass().isSynthetic();
}
@Pure
public boolean isAnonymousClass() {
if (type.isClassType()) {
return type.asClass().isAnonymousClass();
} else {
return false;
}
}
@Pure
public boolean isLocalClass() {
if (type.isClassType()) {
return type.asClass().isLocalClass();
} else {
return false;
}
}
@Pure
public boolean isMemberClass() {
if (type.isClassType()) {
return type.asClass().isMemberClass();
} else {
return false;
}
}
/**
* Utility method for use by classes in this package.
*/
static void setAccessible(final AccessibleObject obj) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
obj.setAccessible(true);
return null;
}
});
}
public Type[] getGenericInterfaces() {
if (type.isPrimitiveType() || type.isUnboxedType()) {
return new Type[0];
} else if (type.isArrayType()) {
// arrays implement JavaLangSerializable & JavaLangCloneable
return new Class[] { RVMType.JavaLangCloneableType.getClassForType(),
RVMType.JavaIoSerializableType.getClassForType()};
} else {
RVMClass klass = type.asClass();
Atom sig = klass.getSignature();
if (sig == null) {
return getInterfaces();
} else {
return JikesRVMHelpers.getInterfaceTypesFromSignature(this, sig);
}
}
}
public Type getGenericSuperclass() {
if (type.isArrayType()) {
return Object.class;
} else if (type.isPrimitiveType() || type.isUnboxedType() ||
(type.isClassType() && type.asClass().isInterface()) ||
this == Object.class) {
return null;
} else {
RVMClass klass = type.asClass();
Atom sig = klass.getSignature();
if (sig == null) {
return getSuperclass();
} else {
return JikesRVMHelpers.getSuperclassType(this, sig);
}
}
}
@Override
@SuppressWarnings("unchecked")
public TypeVariable<Class<T>>[] getTypeParameters() {
if (!type.isClassType()) {
return new TypeVariable[0];
} else {
RVMClass klass = type.asClass();
Atom sig = klass.getSignature();
if (sig == null) {
return new TypeVariable[0];
} else {
return JikesRVMHelpers.getTypeParameters(this, sig);
}
}
}
@Pure
public String getSimpleName() {
if (type.isArrayType()) {
return getComponentType().getSimpleName() + "[]";
} else {
String fullName = getName();
return fullName.substring(fullName.lastIndexOf('.') + 1);
}
}
@Pure
public String getCanonicalName() {
if (type.isArrayType()) {
String componentName = getComponentType().getCanonicalName();
if (componentName != null)
return componentName + "[]";
}
if (isMemberClass()) {
String memberName = getDeclaringClass().getCanonicalName();
if (memberName != null)
return memberName + "." + getSimpleName();
}
if (isLocalClass() || isAnonymousClass())
return null;
return getName();
}
@Override
@Pure
public String toString() {
String name = getName();
if (isPrimitive()) {
return name;
} else if (isInterface()) {
return "interface " + name;
} else {
return "class " + name;
}
}
@SuppressWarnings("unchecked")
public T cast(Object obj) {
if (obj != null && ! isInstance(obj))
throw new ClassCastException();
return (T)obj;
}
@SuppressWarnings("unchecked")
public <U> Class<? extends U> asSubclass(Class<U> klass) {
if (! klass.isAssignableFrom(this))
throw new ClassCastException();
return (Class<? extends U>) this;
}
public Class<?> getDeclaringClass() {
if (!type.isClassType()) return null;
TypeReference dc = type.asClass().getDeclaringClass();
if (dc == null) return null;
return dc.resolve().getClassForType();
}
public Class<?>[] getClasses() throws SecurityException {
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType()) return new Class[0];
ArrayList<Class<?>> publicClasses = new ArrayList<Class<?>>();
for (Class<?> c = this; c != null; c = c.getSuperclass()) {
c.checkMemberAccess(Member.PUBLIC);
TypeReference[] declaredClasses = c.type.asClass().getDeclaredClasses();
if (declaredClasses != null) {
for (TypeReference declaredClass : declaredClasses) {
if (declaredClass != null) {
RVMClass dc = declaredClass.resolve().asClass();
if (dc.isPublic()) {
publicClasses.add(dc.getClassForType());
}
}
}
}
}
Class<?>[] result = new Class[publicClasses.size()];
result = publicClasses.toArray(result);
return result;
}
public ClassLoader getClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
ClassLoader parentCL = RVMClass.getClassLoaderFromStackFrame(1);
if (parentCL != null) {
security.checkPermission(new RuntimePermission("getClassLoader"));
}
}
ClassLoader cl = type.getClassLoader();
return cl == BootstrapClassLoader.getBootstrapClassLoader() ? null : cl;
}
public Class<?> getComponentType() {
return type.isArrayType() ? type.asArray().getElementType().getClassForType() : null;
}
public Class<?>[] getDeclaredClasses() throws SecurityException {
checkMemberAccess(Member.DECLARED);
if (!type.isClassType()) return new Class[0];
// Get array of declared classes from RVMClass object
RVMClass cls = type.asClass();
TypeReference[] declaredClasses = cls.getDeclaredClasses();
// The array can be null if the class has no declared inner class members
if (declaredClasses == null)
return new Class[0];
// Count the number of actual declared inner and static classes.
// (The array may contain null elements, which we want to skip.)
int count = 0;
int length = declaredClasses.length;
for (int i = 0; i < length; ++i) {
if (declaredClasses[i] != null) {
++count;
}
}
// Now build actual result array.
Class<?>[] result = new Class[count];
count = 0;
for (int i = 0; i < length; ++i) {
if (declaredClasses[i] != null) {
result[count++] = declaredClasses[i].resolve().getClassForType();
}
}
return result;
}
public Class<?>[] getInterfaces() {
if (type.isArrayType()) {
// arrays implement JavaLangSerializable & JavaLangCloneable
return new Class[] { RVMType.JavaLangCloneableType.getClassForType(),
RVMType.JavaIoSerializableType.getClassForType() };
} else if (type.isClassType()) {
RVMClass[] interfaces = type.asClass().getDeclaredInterfaces();
Class<?>[] jinterfaces = new Class[interfaces.length];
for (int i = 0; i != interfaces.length; i++)
jinterfaces[i] = interfaces[i].getClassForType();
return jinterfaces;
} else {
return new Class[0];
}
}
// --- Utilities ---
/**
* Utility for security checks
*/
private void checkMemberAccess(int type) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkMemberAccess(this, type);
String packageName = getPackageName();
if (!packageName.isEmpty()) {
security.checkPackageAccess(packageName);
}
}
}
/**
* Compare parameter lists for agreement.
*/
private boolean parametersMatch(TypeReference[] lhs, Class<?>[] rhs) {
if (rhs == null) return lhs.length == 0;
if (lhs.length != rhs.length) return false;
for (int i = 0, n = lhs.length; i < n; ++i) {
if (rhs[i] == null) return false;
if (lhs[i].resolve() != rhs[i].type) {
return false;
}
}
return true;
}
@Pure
private String getPackageName() {
String name = getName();
int index = name.lastIndexOf('.');
if (index >= 0) return name.substring(0, index);
return "";
}
@Pure
private String toResourceName(String resName) {
// Turn package name into a directory path
if (resName.charAt(0) == '/') return resName.substring(1);
String qualifiedClassName = getName();
int classIndex = qualifiedClassName.lastIndexOf('.');
if (classIndex == -1) return resName; // from a default package
return qualifiedClassName.substring(0, classIndex + 1).replace('.', '/') + resName;
}
@Pure
private static boolean validArrayDescriptor(String name) {
int i;
int length = name.length();
for (i = 0; i < length; i++)
if (name.charAt(i) != '[') break;
if (i == length) return false; // string of only ['s
if (i == length - 1) {
switch (name.charAt(i)) {
case 'B': return true; // byte
case 'C': return true; // char
case 'D': return true; // double
case 'F': return true; // float
case 'I': return true; // integer
case 'J': return true; // long
case 'S': return true; // short
case 'Z': return true; // boolean
default: return false;
}
} else if (name.charAt(i) != 'L') {
return false; // not a class descriptor
} else if (name.charAt(length - 1) != ';') {
return false; // ditto
}
return true; // a valid class descriptor
}
@NoInline
private void throwNoSuchMethodException(String name, Class<?>... parameterTypes) throws NoSuchMethodException {
StringBuilder typeString;
if (parameterTypes == null || parameterTypes.length == 0) {
typeString = new StringBuilder("()");
} else {
typeString = new StringBuilder("(");
for (int i = 0; i < parameterTypes.length - 1; i++) {
Class<?> c = parameterTypes[i];
typeString.append(c.toString());
typeString.append(", ");
}
typeString.append(parameterTypes[parameterTypes.length - 1]);
typeString.append(')');
}
throw new NoSuchMethodException(name + typeString);
}
@NoInline
private void throwNoSuchFieldException(String name) throws NoSuchFieldException {
throw new NoSuchFieldException(name);
}
// --- Harmony --
// TODO: Harmony
ClassLoader getClassLoaderImpl() {
return null;
}
// TODO: Harmony
static Class<?>[] getStackClasses(int maxDepth, boolean stopAtPrivileged) {
StackBrowser browser = new StackBrowser();
if (maxDepth == -1) {
browser.init();
maxDepth = 0;
while (browser.hasMoreFrames()) {
maxDepth++;
browser.up();
}
}
if (maxDepth == 0) return new Class[0];
else if (maxDepth < 0) {
throw new Error("Unexpected negative call stack size" + maxDepth);
}
Class<?>[] result = new Class[maxDepth];
browser.init();
for (int i = 0; i < maxDepth; i++) {
result[i] = browser.getCurrentClass().getClassForType();
browser.up();
}
return result;
}
// --- AnnotatedElement interface ---
@Override
public Annotation[] getDeclaredAnnotations() {
return type.getDeclaredAnnotations();
}
@Override
public Annotation[] getAnnotations() {
return type.getAnnotations();
}
@Override
public <U extends Annotation> U getAnnotation(Class<U> annotationClass) {
return type.getAnnotation(annotationClass);
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return type.isAnnotationPresent(annotationClass);
}
// --- Enclosing support ---
public Class<?> getEnclosingClass() {
if (type.isClassType()) {
TypeReference enclosingClass = type.asClass().getEnclosingClass();
if (enclosingClass != null) {
return enclosingClass.resolve().getClassForType();
} else {
return null;
}
} else {
return null;
}
}
public Constructor<?> getEnclosingConstructor() {
if (!(isAnonymousClass() || isLocalClass())) {
return null;
}
MethodReference enclosingMethodRef = type.asClass().getEnclosingMethod();
if (enclosingMethodRef == null) {
return null;
}
RVMMethod method = enclosingMethodRef.resolve();
if (!method.isObjectInitializer()) {
return null;
}
return JikesRVMSupport.createConstructor(method);
}
public Method getEnclosingMethod() {
if (!(isAnonymousClass() || isLocalClass())) {
return null;
}
MethodReference enclosingMethodRef = type.asClass().getEnclosingMethod();
if (enclosingMethodRef == null) {
return null;
} else {
RVMMethod method = enclosingMethodRef.resolve();
if (method.isObjectInitializer() || method.isClassInitializer()) {
return null;
}
return JikesRVMSupport.createMethod(method);
}
}
// --- Enumeration support ---
@SuppressWarnings("unchecked")
public T[] getEnumConstants() {
if (isEnum()) {
try {
return (T[])getMethod("values", new Class[0]).invoke(null, new Object[0]);
} catch (NoSuchMethodException exception) {
throw new Error("Enum lacks values() method");
} catch (IllegalAccessException exception) {
throw new Error("Unable to access Enum class");
} catch (InvocationTargetException exception) {
throw new RuntimeException("The values method threw an exception",
exception);
}
} else {
return null;
}
}
@Pure
public boolean isEnum() {
if (type.isClassType()) {
return type.asClass().isEnum();
} else {
return false;
}
}
// --- Constructors ---
@Pure
private RVMMethod getDefaultConstructor() {
if (this.defaultConstructor == null) {
RVMMethod defaultConstructor = null;
RVMMethod[] methods = type.asClass().getConstructorMethods();
for (RVMMethod method : methods) {
if (method.getParameterTypes().length == 0) {
defaultConstructor = method;
break;
}
}
this.defaultConstructor = defaultConstructor;
}
return this.defaultConstructor;
}
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType())
throwNoSuchMethodException("<init>", parameterTypes);
RVMMethod answer = null;
if (parameterTypes == null || parameterTypes.length == 0) {
answer = getDefaultConstructor();
} else {
RVMMethod[] methods = type.asClass().getConstructorMethods();
for (RVMMethod method : methods) {
if (method.isPublic() &&
parametersMatch(method.getParameterTypes(), parameterTypes)) {
answer = method;
break;
}
}
}
if (answer == null) {
throwNoSuchMethodException("<init>", parameterTypes);
}
return JikesRVMSupport.createConstructor(answer);
}
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED);
if (!type.isClassType())
throwNoSuchMethodException("<init>", parameterTypes);
RVMMethod answer = null;
if (parameterTypes == null || parameterTypes.length == 0) {
answer = getDefaultConstructor();
} else {
RVMMethod[] methods = type.asClass().getConstructorMethods();
for (RVMMethod method : methods) {
if (parametersMatch(method.getParameterTypes(), parameterTypes)) {
answer = method;
break;
}
}
}
if (answer == null) {
throwNoSuchMethodException("<init>", parameterTypes);
}
return JikesRVMSupport.createConstructor(answer);
}
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType()) return new Constructor[0];
RVMMethod[] methods = type.asClass().getConstructorMethods();
ArrayList<Constructor<T>> coll = new ArrayList<Constructor<T>>(methods.length);
for (RVMMethod method : methods) {
if (method.isPublic()) {
@SuppressWarnings("unchecked")
Constructor<T> x = (Constructor<T>)JikesRVMSupport.createConstructor(method);
coll.add(x);
}
}
return coll.toArray(new Constructor[coll.size()]);
}
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED);
if (!type.isClassType()) return new Constructor[0];
RVMMethod[] methods = type.asClass().getConstructorMethods();
Constructor<?>[] ans = new Constructor[methods.length];
for (int i = 0; i < methods.length; i++) {
ans[i] = JikesRVMSupport.createConstructor(methods[i]);
}
return ans;
}
// --- ForName ---
@Inline
public static Class<?> forName(String typeName) throws ClassNotFoundException {
throwNPEWhenNameIsNull(typeName);
ClassLoader parentCL = RVMClass.getClassLoaderFromStackFrame(1);
return forNameInternal(typeName, true, parentCL);
}
public static Class<?> forName(String className, boolean initialize, ClassLoader classLoader)
throws ClassNotFoundException,
LinkageError,
ExceptionInInitializerError {
throwNPEWhenNameIsNull(className);
if (classLoader == null) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
ClassLoader parentCL = RVMClass.getClassLoaderFromStackFrame(1);
if (parentCL != null) {
try {
security.checkPermission(new RuntimePermission("getClassLoader"));
} catch (SecurityException e) {
throw new ClassNotFoundException("Security exception when" +
" trying to get a classloader so we can load the" +
" class named \"" + className + "\"", e);
}
}
}
classLoader = BootstrapClassLoader.getBootstrapClassLoader();
}
return forNameInternal(className, initialize, classLoader);
}
private static Class<?> forNameInternal(String className, boolean initialize, ClassLoader classLoader)
throws ClassNotFoundException, LinkageError, ExceptionInInitializerError {
if (className == null) {
throw new ClassNotFoundException("Cannot load a class with a name of null");
}
try {
if (className.startsWith("[")) {
if (!validArrayDescriptor(className)) {
throw new ClassNotFoundException(className);
}
}
Atom descriptor = Atom.findOrCreateAsciiAtom(className.replace('.','/')).descriptorFromClassName();
TypeReference tRef = TypeReference.findOrCreate(classLoader, descriptor);
RVMType ans = tRef.resolve();
Callbacks.notifyForName(ans);
if (initialize && !ans.isInitialized()) {
ans.prepareForFirstUse();
}
return ans.getClassForType();
} catch (NoClassDefFoundError ncdfe) {
Throwable cause2 = ncdfe.getCause();
ClassNotFoundException cnf;
// If we get a NCDFE that was caused by a CNFE, throw the original CNFE.
if (cause2 instanceof ClassNotFoundException)
cnf = (ClassNotFoundException) cause2;
else
cnf = new ClassNotFoundException(className, ncdfe);
throw cnf;
}
}
// --- newInstance ---
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {0})
public T newInstance() throws IllegalAccessException, InstantiationException,
ExceptionInInitializerError, SecurityException {
// Basic checks
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType())
throw new InstantiationException();
RVMClass cls = type.asClass();
if (cls.isAbstract() || cls.isInterface())
throw new InstantiationException();
// Ensure that the class is initialized
if (!cls.isInitialized()) {
RuntimeEntrypoints.initializeClassForDynamicLink(cls);
}
// Find the defaultConstructor
RVMMethod defaultConstructor = getDefaultConstructor();
if (defaultConstructor == null)
throw new InstantiationException();
// Check that caller is allowed to access it
if (!defaultConstructor.isPublic()) {
RVMClass accessingClass = RVMClass.getClassFromStackFrame(1);
VMCommonLibrarySupport.checkAccess(defaultConstructor, accessingClass);
}
// Allocate an uninitialized instance;
@SuppressWarnings("unchecked") // yes, we're giving an anonymous object a type.
T obj = (T)RuntimeEntrypoints.resolvedNewScalar(cls);
// Run the default constructor on the it.
Reflection.invoke(defaultConstructor, null, obj, null, true);
return obj;
}
// --- Methods ---
@Pure
private RVMMethod getMethodInternal1(Atom aName, Class<?>... parameterTypes) {
RVMMethod answer = null;
for (RVMClass current = type.asClass(); current != null && answer == null; current = current.getSuperClass()) {
RVMMethod[] methods = current.getDeclaredMethods();
for (RVMMethod meth : methods) {
if (meth.getName() == aName && meth.isPublic() &&
parametersMatch(meth.getParameterTypes(), parameterTypes)) {
if (answer == null) {
answer = meth;
} else {
RVMMethod m2 = meth;
if (answer.getReturnType().resolve().isAssignableFrom(m2.getReturnType().resolve())) {
answer = m2;
}
}
}
}
}
return answer;
}
@Pure
private RVMMethod getMethodInternal2(Atom aName, Class<?>... parameterTypes) {
RVMMethod answer = null;
RVMMethod[] methods = type.asClass().getVirtualMethods();
for (RVMMethod meth : methods) {
if (meth.getName() == aName && meth.isPublic() &&
parametersMatch(meth.getParameterTypes(), parameterTypes)) {
if (answer == null) {
answer = meth;
} else {
RVMMethod m2 = meth;
if (answer.getReturnType().resolve().isAssignableFrom(m2.getReturnType().resolve())) {
answer = m2;
}
}
}
}
return answer;
}
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
throwNPEWhenNameIsNull(name);
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType()) throwNoSuchMethodException(name, parameterTypes);
Atom aName = Atom.findOrCreateUnicodeAtom(name);
if (aName == RVMClassLoader.StandardClassInitializerMethodName ||
aName == RVMClassLoader.StandardObjectInitializerMethodName) {
// <init> and <clinit> are not methods.
throwNoSuchMethodException(name, parameterTypes);
}
// (1) Scan the declared public methods of this class and each of its superclasses
RVMMethod answer = getMethodInternal1(aName, parameterTypes);
if (answer == null) {
// (2) Now we need to consider methods inherited from interfaces.
// Because we inject the requisite Miranda methods, we can do this simply
// by looking at this class's virtual methods instead of searching interface hierarchies.
answer = getMethodInternal2(aName, parameterTypes);
}
if (answer == null) {
throwNoSuchMethodException(name, parameterTypes);
}
return JikesRVMSupport.createMethod(answer);
}
private static void throwNPEWhenNameIsNull(String name) {
if (name == null) {
throw new NullPointerException("Name parameter must not be null (but was)!");
}
}
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
throwNPEWhenNameIsNull(name);
checkMemberAccess(Member.DECLARED);
if (!type.isClassType()) throwNoSuchMethodException(name, parameterTypes);
if (name == null) {
throwNoSuchMethodException(name, parameterTypes);
}
Atom aName = Atom.findOrCreateUnicodeAtom(name);
if (aName == RVMClassLoader.StandardClassInitializerMethodName ||
aName == RVMClassLoader.StandardObjectInitializerMethodName) {
// <init> and <clinit> are not methods.
throwNoSuchMethodException(name, parameterTypes);
}
RVMMethod[] methods = type.asClass().getDeclaredMethods();
RVMMethod answer = null;
for (RVMMethod meth : methods) {
if (meth.getName() == aName &&
parametersMatch(meth.getParameterTypes(), parameterTypes)) {
if (answer == null) {
answer = meth;
} else {
RVMMethod m2 = meth;
if (answer.getReturnType().resolve().isAssignableFrom(m2.getReturnType().resolve())) {
answer = m2;
}
}
}
}
if (answer == null) {
throwNoSuchMethodException(name, parameterTypes);
}
return JikesRVMSupport.createMethod(answer);
}
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED);
if (!type.isClassType()) return new Method[0];
RVMMethod[] methods = type.asClass().getDeclaredMethods();
ArrayList<Method> coll = new ArrayList<Method>(methods.length);
for (RVMMethod meth : methods) {
if (!meth.isClassInitializer() && !meth.isObjectInitializer()) {
coll.add(JikesRVMSupport.createMethod(meth));
}
}
return coll.toArray(new Method[coll.size()]);
}
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC);
RVMMethod[] static_methods = type.getStaticMethods();
RVMMethod[] virtual_methods = type.getVirtualMethods();
HashSet<Method> coll = new HashSet<Method>(static_methods.length +
virtual_methods.length);
for (RVMMethod meth : static_methods) {
if (meth.isPublic()) {
coll.add(JikesRVMSupport.createMethod(meth));
}
}
for (RVMMethod meth : virtual_methods) {
if (meth.isPublic()) {
coll.add(JikesRVMSupport.createMethod(meth));
}
}
// The Java API says that duplicate versions are returned if multiple
// versions of a method are defined by a class. This only applies to
// abstract classes and interfaces because normal classes always have
// exactly one definition for a given signature-name pair.
RVMClass thisClass = type.asClass();
boolean isAbstract = thisClass.isAbstract();
if (isInterface() || isAbstract) {
// For each virtual method , search all superinterfaces
// to find all declarations that aren't shadowed by superinterfaces and
// add those to the set of methods.
HashSet<Method> methods = new HashSet<Method>();
for (RVMMethod m : virtual_methods) {
Atom name = m.getName();
Atom desc = m.getDescriptor();
if (isAbstract && !m.getDeclaringClass().isInterface()) {
// If the method is declared by a class (and not an interface),
// only that declaration is relevant. Declarations that may come
// from interfaces are overridden by the class' definition. That
// definition is already in the virtual methods. Therefore, it's
// unnecessary to search for additional declarations for that
// method.
continue;
}
collectDeclarations(thisClass, name, desc, methods);
}
coll.addAll(methods);
}
return coll.toArray(new Method[coll.size()]);
}
private static void collectDeclarations(RVMClass i, Atom name, Atom desc, Set<Method> methods) {
for (RVMMethod declared : i.getDeclaredMethods()) {
if (declared.getName() == name && declared.getDescriptor() == desc) {
methods.add(JikesRVMSupport.createMethod(declared));
return;
}
}
for (RVMClass declardInterface : i.getDeclaredInterfaces()) {
collectDeclarations(declardInterface, name, desc, methods);
}
}
// --- Fields ---
@Pure
private RVMField getFieldInternal(Atom name) {
RVMClass ctype = type.asClass();
// (1) Check my public declared fields
RVMField[] fields = ctype.getDeclaredFields();
for (RVMField field : fields) {
if (field.isPublic() && field.getName() == name) {
return field;
}
}
// (2) Check superinterfaces
RVMClass[] interfaces = ctype.getDeclaredInterfaces();
for (RVMClass anInterface : interfaces) {
RVMField ans = anInterface.getClassForType().getFieldInternal(name);
if (ans != null) return ans;
}
// (3) Check superclass (if I have one).
if (ctype.getSuperClass() != null) {
return ctype.getSuperClass().getClassForType().getFieldInternal(name);
}
return null;
}
public Field getField(String name) throws NoSuchFieldException, SecurityException {
throwNPEWhenNameIsNull(name);
checkMemberAccess(Member.PUBLIC);
if (!type.isClassType()) throw new NoSuchFieldException();
Atom aName = Atom.findUnicodeAtom(name);
if (aName == null) throwNoSuchFieldException(name);
RVMField answer = getFieldInternal(aName);
if (answer == null) {
throwNoSuchFieldException(name);
}
return JikesRVMSupport.createField(answer);
}
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
throwNPEWhenNameIsNull(name);
checkMemberAccess(Member.DECLARED);
if (!type.isClassType() || name == null) throwNoSuchFieldException(name);
Atom aName = Atom.findOrCreateUnicodeAtom(name);
RVMField answer = type.asClass().findDeclaredField(aName);
if (answer == null) {
throwNoSuchFieldException(name);
}
return JikesRVMSupport.createField(answer);
}
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC);
RVMField[] static_fields = type.getStaticFields();
RVMField[] instance_fields = type.getInstanceFields();
ArrayList<Field> coll = new ArrayList<Field>(static_fields.length + instance_fields.length);
for (RVMField field : static_fields) {
if (field.isPublic()) {
coll.add(JikesRVMSupport.createField(field));
}
}
for (RVMField field : instance_fields) {
if (field.isPublic()) {
coll.add(JikesRVMSupport.createField(field));
}
}
return coll.toArray(new Field[coll.size()]);
}
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED);
if (!type.isClassType()) return new Field[0];
RVMField[] fields = type.asClass().getDeclaredFields();
Field[] ans = new Field[fields.length];
for (int i = 0; i < fields.length; i++) {
ans[i] = JikesRVMSupport.createField(fields[i]);
}
return ans;
}
}