/**************************************************************************
* Parts copyright (c) 2009, 2015 by Chris Gray, KIFFER Ltd. nn nn *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of KIFFER Ltd nor the names of other contributors *
* may be used to endorse or promote products derived from this *
* software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL KIFFER LTD OR OTHER CONTRIBUTORS BE LIABLE FOR ANY *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING *
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.lang;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.io.InputStream;
import java.net.URL;
import java.security.ProtectionDomain;
import wonka.vm.SystemClassLoader;
/*
** Representation of a Java type.
**
** Note: this class is initialized "by hand" before the VM is fully
** initialized. Consequently it must not have a static initializer.
** (It can have static variables, and even constant initial values
** for those variables, but nothing fancier and certainly no static{}
** clause.)
*/
public final class Class implements java.io.Serializable {
private static final long serialVersionUID = 3206093459760846163L;
/** The ClassLoader which defined this Class. Package-private so SecurityManager can peek.
*/
ClassLoader loader;
/** The protection domain to which this class belongs.
*/
private final ProtectionDomain domain;
/** The signers of this Class.
** Package-accessible so that ClassLoader can set it.
** TODO: how do we stop reflection from screwing with this?
*/
Object[] signers;
/**
** List of prefixes which denote packages that require special handling.
*/
private static String restricted_packages;
;
/**
** Get the list of restricted packages. Doesn't work yet (crashes during
** bootstrapping -- FIXME).
*/
private static synchronized String getRestrictedPackages() {
if (restricted_packages == null){
restricted_packages = java.security.Security.getProperty("package.access");
if (restricted_packages == null) {
restricted_packages = "com.acunia.wonka";
}
}
return restricted_packages;
}
/** Private constructor to disallow ``new Class()''.
** We set the blank finals here to keep the compiler happy
** (in reality they are set up by native code).
*/
private Class(){
domain = null;
signers = null;
}
/** Get the name of this package this class is in.
*/
private synchronized String getPackageName() {
String classname = getName();
int lastdot = classname.lastIndexOf('.');
if (lastdot < 0) {
return "";
}
else {
return classname.substring(0,lastdot);
}
}
/** Find and initialize the class with the given name, using the
** ``current'' class loader (the one that loaded the calling class).
** Can also throw LinkageError or ExceptionInInitializerError.
*/
public static Class forName(String classname)
throws ClassNotFoundException
{
Class theClass = null;
try {
theClass = forName_S(classname);
} catch (NoClassDefFoundError e) {
throw new ClassNotFoundException("class loader threw NoClassDefFoundError for: " + classname, e);
}
return theClass;
}
/** Find and optionally initialize the class with the given name, using
** the given class loader.
** Can also throw LinkageError or ExceptionInInitializerError.
*/
public static Class forName(String classname, boolean initialize, ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null && ClassLoader.getCallingClassLoader() != null) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.theSecurityManager;
if (sm != null) {
sm.checkPermission(new RuntimePermission("getClassLoader"));
}
}
}
Class theClass = null;
try {
theClass = forName_SZCL(classname, initialize, loader);
} catch (NoClassDefFoundError e) {
throw new ClassNotFoundException(loader + " threw NoClassDefFoundError for: " + classname, e);
}
return theClass;
}
/** Native code portion of forName(String).
*/
private static native Class forName_S(String className)
throws ClassNotFoundException;
/** Native code portion of forName(String, Boolean, ClassLoader).
*/
private static native Class forName_SZCL(String className, boolean initialize, ClassLoader loader)
throws ClassNotFoundException;
// IS HACK
// [CG 20010510] IS OBSOLETE HACK
// [CG 20010603] REMOVED OBSOLOETE HACK :)
// => IS NO MORE HACK
/**
** Create a new instance of this class.
** The default (no-args) constructor is used.
** Can also throw ExceptionInIntializerError or SecurityException.
*/
public Object newInstance()
throws InstantiationException, IllegalAccessException
{
if (isPrimitive() || isArray() || Modifier.isAbstract(getModifiers())) {
throw new InstantiationException();
}
access_checks(Member.PUBLIC);
return newInstance0();
}
/** Native portion of newInstance(). Package-visible for the benefit of
* java.jang.System.
*/
native Object newInstance0() throws InstantiationException, IllegalAccessException;
/** Do the same checks as for the `instanceof' operator.
** obj is null: return false.
** this Class is array: return true iff obj is or can be widened to this Class.
** this Class is interface: return true iff class (or any superclass) of obj implements this interface.
** else (normal class): return true iff obj is of this Class or a subclass.
*/
public native boolean isInstance(Object obj);
/** Test whether this Class is the same as, or a superclass or superinterface of, Class cls.
** (Put differently: can Class cls be converted to this Class via an
** identity or a widening conversion. See JLS2 5.1.1, 5.1.4).
*/
public native boolean isAssignableFrom(Class cls);
/** Is this Class an interface?
*/
public native boolean isInterface();
/** Is this Class an array?
*/
public native boolean isArray();
/** Is this Class a primitive?
*/
public native boolean isPrimitive();
/** Get the unadorned, dotted fully qualified name of this Class.
** If an array class, return something like ``[B'' or ``[Ljava.lang.Object;''.
*/
public native String getName();
/** Get the ClassLoader which loaded this Class.
** Returns null if the bootstrap class loader was used.
** Performs a checkPermission ("getClassLoader") if the caller's
** class loader is not this Class's class loader or a delegation ancestor
** thereof (class loader `null' is everybody's ancestor).
*/
public ClassLoader getClassLoader() {
ClassLoader cl = ClassLoader.getCallingClassLoader();
if (cl != null && !cl.isDelegationAncestor(loader)) {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.theSecurityManager;
if (sm != null) {
sm.checkPermission(new RuntimePermission("getClassLoader"));
}
}
}
return loader;
}
/** Get this Class's superclass.
** Returns null if this Class is java.lang.Object, an interface,
** or a primitive type. If this Class is an array then return
** java.lang.Object.
*/
public native Class getSuperclass();
/** Get the Package this Class belongs to.
** If this Class was loaded by the bootstrap classloader,
** get the info from the system class loader.
** Otherwise, ask the classloader which loaded us.
*/
public Package getPackage(){
ClassLoader cl = loader == null ? SystemClassLoader.getInstance() : loader;
return cl.getPackage(getPackageName());
}
/** Get the package this Class belongs to: the Class must have been
** loaded by the bootstrap class loader.
private Package getBootstrapPackage(String name) {
throw new UnsupportedOperationException("getPackage() in Class is not yet implemented for classes loaded by bootstrap loader");
}
*/
/** If this Class is a member of another Class, return that Class.
** Otherwise return null.
** Always returns null for a primitive or array Class.
*/
public native Class getDeclaringClass();
/** Return an array of Class objects, one for every Class which is a public member of this Class.
** The array includes all directly-declared public inner classes, but also
** all public classes inherited via a superclass or interface.
** Always returns a zero-length array for a primitive or array Class.
**
** Should perform access checks for this class and every superclass (TODO).
**
** Currently not implemented, throws Exception !
*/
public Class[] getClasses() {
access_checks(Member.PUBLIC); // TODO - also for superclasses!
return getClasses0();
}
private native Class[] getClasses0();
/** Return an array of Class objects, one for every Class which is a declared in this Class as a member.
** Always returns a zero-length array for a primitive or array Class.
*/
public Class[] getDeclaredClasses()
throws SecurityException {
access_checks(Member.DECLARED);
return getDeclaredClasses0();
}
private native Class[] getDeclaredClasses0();
/** Get this Class's superinterfaces.
** this is an interface: return all interfaces it directly extends
** (in the same order as in the .class file). (*)
** otherwise: return all interfaces it directly implements
** (in the same order as in the .class file). (*)
** (*) The spec says the same order as in the class's declaration, but
** we can't really know that ...
** If this Class does not extend/implement any interfaces, returns an
** array of length 0.
*/
public native Class[] getInterfaces();
/** Get the component type of this Class.
** If this class is not an array class, returns null.
*/
public native Class getComponentType();
/** Get the modifiers of this class.
** Returns a bitmask which can be analysed using methods of
** java.lang.reflect.Modifier. The spec says to return something
** for public, protected, private, final, static, abstract, and
** interface, but currently we never return ``static'' (and for
** inner classes we only look at the flags at the very front of
** the .class file, not any funky attributes further on).
** Probably needs further work ...
*/
public native int getModifiers();
/** Get the signers of this Class, if any.
** For a primitive type, will always return null.
*/
public Object [] getSigners() {
return signers;
}
/** Internal method to get all constructors of type `mtype'.
** `mtype' may be Memeber.PUBLIC or Member.DECLARED.
*/
private native Constructor[] get_constructors (int mtype);
/** Internal method to get the constructor with the given parameter types and type `mtype'.
** `mtype' may be Member.PUBLIC or Member.DECLARED.
*/
private native Constructor get_one_constructor (Class[] parameterTypes, int mtype);
/** Internal method to get all fields of type `mtype'.
** `mtype' may be Member.PUBLIC or Member.DECLARED.
*/
private native Field[] get_fields (int mtype);
/** Internal method to get the field with the given name and type `mtype'.
** `mtype' may be Member.PUBLIC or Member.DECLARED.
*/
private native Field get_one_field (String fieldname, int mtype);
/** Internal method to get all methods of type `mtype'.
** `mtype' may be Member.PUBLIC or Member.DECLARED.
*/
private native Method[] get_methods (int mtype);
/** Internal method to get the method with the given name, parameters and type `mtype'.
** `mtype' may be Member.PUBLIC or Member.DECLARED.
*/
private native Method get_one_method (String methodname, Class[] parameterTypes, int mtype);
/** Perform the typical access check: if there is a security manager,
** then invoke its sm.checkMemberAccess() method, and if this Class
** is in a package, call checkPackageAccess() as well. The parameter
** passed to sm.checkMemberAccess() is determined by check_type, which
** should be either Member.PUBLIC or Member.DECLARED.
*/
private void access_checks(int check_type)
throws SecurityException
{
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
if ((check_type != java.lang.reflect.Member.PUBLIC) && (loader != ClassLoader.getCallingClassLoader())) {
java.security.AccessController.checkPermission(new RuntimePermission("accessDeclaredMembers."+getName()));
}
String restricteds = getRestrictedPackages();
//String restricteds = "java,com.acunia.wonka";
int comma = restricteds.indexOf(',');
String arestricted;
String pname = getPackageName();
while (comma >= 0) {
arestricted = restricteds.substring(0,comma);
if (pname == arestricted || pname.startsWith(arestricted) && pname.charAt(arestricted.length()) == '.') {
java.security.AccessController.checkPermission(new RuntimePermission("accessClassInPackage."+pname));
}
restricteds = restricteds.substring(comma+1);
comma = restricteds.indexOf(',');
}
arestricted = restricteds;
if (pname == arestricted || pname.startsWith(arestricted) && pname.charAt(arestricted.length()) == '.') {
java.security.AccessController.checkPermission(new RuntimePermission("accessClassInPackage."+pname));
}
}
else if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.theSecurityManager;
if (sm != null) {
sm.checkMemberAccess(this,check_type);
String pname = getPackageName();
if (pname != "") {
sm.checkPackageAccess(pname);
}
}
}
}
/** Return an array containing all this Class's public constructors.
** Returns a zero-length array if this class has no public constructors,
** or is primitive or array.
**
** If there is a security manager, call its checkMemberAccess(Member.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Constructor[] getConstructors()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Constructor[0];
}
access_checks(Member.PUBLIC);
return get_constructors(Member.PUBLIC);
}
/** Return an array containing all this Class's declared constructors.
** (This includes the default constructor if any).
** Returns a zero-length array if this class has no declared constructors,
** or is primitive or array or interface.
**
** If there is a security manager, call its checkMemberAccess(Member.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Constructor[] getDeclaredConstructors()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Constructor[0];
}
access_checks(Member.DECLARED);
return get_constructors(Member.DECLARED);
}
/** Get this Class's public constructor with the given parameter types.
** Throws NoSuchMethodException if there ain't no such animal.
**
** If there is a security manager, call its checkMemberAccess(Member.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Constructor getConstructor(Class[] parameterTypes)
throws NoSuchMethodException, SecurityException
{
if (isPrimitive() || isArray()) {
throw new NoSuchMethodException();
}
access_checks(Member.PUBLIC);
Class[] pt = parameterTypes == null ? new Class[0] : parameterTypes;
return get_one_constructor(pt, Member.PUBLIC);
}
/** Get this Class's declared constructor with the given parameter types.
** Throws NoSuchMethodException if there ain't no such animal.
**
** If there is a security manager, call its checkMemberAccess(Member.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Constructor getDeclaredConstructor(Class[] parameterTypes)
throws NoSuchMethodException, SecurityException
{
if (isPrimitive() || isArray()) {
throw new NoSuchMethodException();
}
access_checks(Member.DECLARED);
Class[] pt = parameterTypes == null ? new Class[0] : parameterTypes;
return get_one_constructor(pt, Member.DECLARED);
}
/** Return an array containing all this Class's public fields.
** If this Class is a class, includes fields of superclasses;
** if this Class is an interface, includes fields of superinterfaces.
** Returns a zero-length array if this class has no accessible public fields,
** or is primitive or array.
**
** If there is a security manager, call its checkMemberAccess(Member.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Field[] getFields()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Field[0];
}
access_checks(Member.PUBLIC);
return get_fields(Member.PUBLIC);
}
/** Return an array containing all this Class's declared fields.
** Returns a zero-length array if this class has no accessible declared fields,
** or is primitive or array.
**
** If there is a security manager, call its checkMemberAccess(Memeber.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Field[] getDeclaredFields()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Field[0];
}
access_checks(Member.DECLARED);
return get_fields(Member.DECLARED);
}
/** Get this Class's public field with the given name.
** Includes all fields of any interfaces this Class extends or implements,
** and so on recursively for each of its superclasses.
** Throws NoSuchFieldException if this Class has no public field of that name.
**
** If there is a security manager, call its checkMemberAccess(Member.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Field getField(String fieldname)
throws SecurityException, NoSuchFieldException
{
if (isPrimitive() || isArray()) {
throw new NoSuchFieldException();
}
access_checks(Member.PUBLIC);
return get_one_field(fieldname, Member.PUBLIC);
}
/** Get this Class's declared field with the given name.
** Throws NoSuchFieldException if this Class has no declared field of that name.
**
** If there is a security manager, call its checkMemberAccess(Memeber.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Field getDeclaredField(String fieldname)
throws SecurityException, NoSuchFieldException
{
if (isPrimitive() || isArray()) {
throw new NoSuchFieldException("class "+this+" is a"+
(isPrimitive() ? " primitive" : "n array")
+ " class");
}
access_checks(Member.DECLARED);
return get_one_field(fieldname, Member.DECLARED);
}
/** Return an array containing all this Class's public methods.
** Does not include <clinit>.
** If this Class is a class, includes methods of superclasses;
** if this Class is an interface, includes methods of superinterfaces.
** Returns a zero-length array if this class has no accessible public methods,
** or is primitive or array.
**
** If there is a security manager, call its checkMemberAccess(Memeber.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Method[] getMethods()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Method[0];
}
access_checks(Member.PUBLIC);
return get_methods(Member.PUBLIC);
}
/** Return an array containing all this Class's declared methods.
** Does not include <clinit>.
** Returns a zero-length array if this class has no accessible declared methods,
** If there is a security manager, call its checkMemberAccess(Memeber.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Method[] getDeclaredMethods()
throws SecurityException
{
if (isPrimitive() || isArray()) {
return new Method[0];
}
access_checks(Member.DECLARED);
return get_methods(Member.DECLARED);
}
/** Get this Class's public method with the given name and parameter types.
** Throws NoSuchMethodException if there ain't no such animal,
** or if the method name is <init> or <clinit> (get real).
** The spec says to search first this Class, then its superclasses,
** then its superinterfaces. It also says that for each class or
** interface that we check, if there are multiple methods with the
** same name and parameters but different return types, then we should
** take the one with the most specific return type. I don't grok that:
** surely there cannot be two methods in one class which differ only
** in their return type??? TODO: check JLS 8.2 and 8.4.
**
** If there is a security manager, call its checkMemberAccess(Memeber.PUBLIC),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Method getMethod(String methodname, Class[] parameterTypes)
throws SecurityException, NoSuchMethodException
{
if (isPrimitive() || isArray() || methodname==null || methodname.equals("<init>") || methodname.equals("<clinit>")) {
throw new NoSuchMethodException();
}
access_checks(Member.PUBLIC);
Class[] pt = parameterTypes == null ? new Class[0] : parameterTypes;
return get_one_method(methodname, pt, Member.PUBLIC);
}
/** Get this Class's public method with the given name and parameter types.
** Throws NoSuchMethodException if there ain't no such animal,
** or if the method name is <init> or <clinit> (get real).
**
** If there is a security manager, call its checkMemberAccess(Memeber.DECLARED),
** and if this Class is in a package call checkPackageAccess() too.
*/
public Method getDeclaredMethod(String methodname, Class[] parameterTypes)
throws SecurityException, NoSuchMethodException
{
access_checks(Member.DECLARED);
Class[] pt = parameterTypes == null ? new Class[0] : parameterTypes;
return get_one_method(methodname, pt, Member.DECLARED);
}
/** As getResourceAsStream(String), but returns a URL instead of an InputStream.
*/
public URL getResource(String name) {
ClassLoader cl = getClassLoader();
String slashed;
if (!name.startsWith("/")) {
if (!name.startsWith("{}/")) {
String packagename = getPackageName();
slashed = (packagename == "" ? name :
getPackageName().replace('.','/')+"/"+name);
} else {
slashed = name.substring(3);
}
} else {
slashed = name.substring(1);
}
if (cl==null) {
return ClassLoader.getSystemResource(slashed);
}
else {
return cl.getResource(slashed);
}
}
/** Find the resource with a given name, using the namespace of this Class.
** If the name begins with `/' or `{}/', leave it be: otherwise prefix it with
** the name of this Class's package, with all `.' replaced by `/'.
** Then pass the resulting mess to this Class's class loader.
** (The spec doesn't say what happens if this Class is not part of a
** package, so I took the liberty of prefixing ``./'').
**
** If this Class's class loader is null, punt to getSystemResourceAsStream().
** The result is returned in the form of an InputStream.
*/
public InputStream getResourceAsStream(String name) {
ClassLoader cl = getClassLoader();
String slashed;
if (!name.startsWith("/")) {
if (!name.startsWith("{}/")) {
String packagename = getPackageName();
slashed = (packagename == "" ? name :
getPackageName().replace('.','/')+"/"+name);
} else {
slashed = name.substring(3);
}
} else {
slashed = name.substring(1);
}
if (cl==null) {
return ClassLoader.getSystemResourceAsStream(slashed);
}
else {
return cl.getResourceAsStream(slashed);
}
}
/** Get this Class's ProtectionDomain.
** If there is a SecurityManager, call its checkPermission with
** a RuntimePermission("getProtectionDomain") first.
*/
public ProtectionDomain getProtectionDomain() {
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.theSecurityManager;
if (sm != null) {
sm.checkPermission(new RuntimePermission("getProtectionDomain"));
}
}
return domain;
}
/**
** Get the most specific assertion status from the class loader's data.
** Mainly for use by the VM itself, cannot reliably be used by applications.
*/
public boolean desiredAssertionStatus() {
Boolean status = loader.assertionStatus.getClassStatus(getName());
if (status == null) {
status = loader.assertionStatus.getPackageStatus(getPackageName());
if (status == null) {
status = loader.assertionStatus.getDefaultStatus();
}
}
return status.booleanValue();
}
/** Create the string representation of a Class.
** Class is primitive -> same as get getName()
** Class is interface -> ``interface foo.bar.Baz''.
** else -> ``class foo.bar.Baz''.
*/
public String toString() {
String name = getName();
if (isPrimitive()) {
return name;
}
else if (isInterface()) {
return "interface "+name;
}
else {
return "class "+name;
}
}
}