2007-07-24 01:44:20 +00:00
|
|
|
import java.lang.reflect.Field;
|
2013-12-03 20:08:21 +00:00
|
|
|
import java.lang.reflect.Method;
|
2014-01-03 21:40:47 +00:00
|
|
|
import java.lang.reflect.Modifier;
|
2013-12-03 20:08:21 +00:00
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
|
import java.lang.reflect.Type;
|
2013-12-06 05:28:13 +00:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
2007-07-24 01:44:20 +00:00
|
|
|
|
|
|
|
public class Reflection {
|
2011-02-23 00:54:56 +00:00
|
|
|
public static boolean booleanMethod() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte byteMethod() {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static char charMethod() {
|
|
|
|
return '2';
|
|
|
|
}
|
|
|
|
|
|
|
|
public static short shortMethod() {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int intMethod() {
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static float floatMethod() {
|
|
|
|
return 5.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static long longMethod() {
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static double doubleMethod() {
|
|
|
|
return 7.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void expect(boolean v) {
|
|
|
|
if (! v) throw new RuntimeException();
|
|
|
|
}
|
|
|
|
|
2013-12-03 20:08:21 +00:00
|
|
|
private static class Hello<T> {
|
|
|
|
private class World<S> { }
|
2013-12-03 22:30:25 +00:00
|
|
|
}
|
2013-10-31 22:01:33 +00:00
|
|
|
|
|
|
|
private static void innerClasses() throws Exception {
|
|
|
|
Class c = Reflection.class;
|
|
|
|
Class[] inner = c.getDeclaredClasses();
|
2017-07-10 16:01:56 +00:00
|
|
|
expect(3 == inner.length);
|
2014-03-06 22:01:44 +00:00
|
|
|
expect(Hello.class == inner[0]
|
2017-07-10 16:01:56 +00:00
|
|
|
|| Hello.class == inner[1]
|
|
|
|
|| Hello.class == inner[2]);
|
2013-10-31 22:01:33 +00:00
|
|
|
}
|
|
|
|
|
2013-10-25 20:35:52 +00:00
|
|
|
private int egads;
|
|
|
|
|
|
|
|
private static void annotations() throws Exception {
|
|
|
|
Field egads = Reflection.class.getDeclaredField("egads");
|
|
|
|
expect(egads.getAnnotation(Deprecated.class) == null);
|
|
|
|
}
|
|
|
|
|
2013-12-06 22:45:46 +00:00
|
|
|
private Integer[] array;
|
|
|
|
|
|
|
|
private Integer integer;
|
|
|
|
|
2013-12-03 20:08:21 +00:00
|
|
|
public static Hello<Hello<Reflection>>.World<Hello<String>> pinky;
|
|
|
|
|
|
|
|
private static void genericType() throws Exception {
|
|
|
|
Field field = Reflection.class.getDeclaredField("egads");
|
|
|
|
expect(field.getGenericType() == Integer.TYPE);
|
|
|
|
|
|
|
|
field = Reflection.class.getField("pinky");
|
|
|
|
expect("Reflection$Hello$World".equals(field.getType().getName()));
|
|
|
|
expect(field.getGenericType() instanceof ParameterizedType);
|
|
|
|
ParameterizedType type = (ParameterizedType) field.getGenericType();
|
|
|
|
|
|
|
|
expect(type.getRawType() instanceof Class);
|
|
|
|
Class<?> clazz = (Class<?>) type.getRawType();
|
|
|
|
expect("Reflection$Hello$World".equals(clazz.getName()));
|
|
|
|
|
|
|
|
expect(type.getOwnerType() instanceof ParameterizedType);
|
|
|
|
ParameterizedType owner = (ParameterizedType) type.getOwnerType();
|
|
|
|
clazz = (Class<?>) owner.getRawType();
|
|
|
|
expect(clazz == Hello.class);
|
|
|
|
|
|
|
|
Type[] args = type.getActualTypeArguments();
|
|
|
|
expect(1 == args.length);
|
|
|
|
expect(args[0] instanceof ParameterizedType);
|
|
|
|
|
|
|
|
ParameterizedType arg = (ParameterizedType) args[0];
|
|
|
|
expect(arg.getRawType() instanceof Class);
|
|
|
|
clazz = (Class<?>) arg.getRawType();
|
|
|
|
expect("Reflection$Hello".equals(clazz.getName()));
|
|
|
|
|
|
|
|
args = arg.getActualTypeArguments();
|
|
|
|
expect(1 == args.length);
|
|
|
|
expect(args[0] == String.class);
|
|
|
|
}
|
2017-07-10 16:01:56 +00:00
|
|
|
|
2013-12-06 05:28:13 +00:00
|
|
|
public static void throwOOME() {
|
|
|
|
throw new OutOfMemoryError();
|
|
|
|
}
|
|
|
|
|
2014-10-11 19:50:53 +00:00
|
|
|
public static void classType() throws Exception {
|
|
|
|
// Class types
|
|
|
|
expect(!Reflection.class.isAnonymousClass());
|
|
|
|
expect(!Reflection.class.isLocalClass());
|
|
|
|
expect(!Reflection.class.isMemberClass());
|
|
|
|
|
|
|
|
expect(Reflection.Hello.class.isMemberClass());
|
2017-07-10 16:01:56 +00:00
|
|
|
|
2014-10-11 19:50:53 +00:00
|
|
|
Cloneable anonymousLocal = new Cloneable() {};
|
|
|
|
expect(anonymousLocal.getClass().isAnonymousClass());
|
2017-07-10 16:01:56 +00:00
|
|
|
|
2014-10-11 19:50:53 +00:00
|
|
|
class NamedLocal {}
|
|
|
|
expect(NamedLocal.class.isLocalClass());
|
|
|
|
}
|
|
|
|
|
2017-07-10 16:01:56 +00:00
|
|
|
private static class MyClassLoader extends ClassLoader {
|
|
|
|
public Package definePackage1(String name) {
|
|
|
|
return definePackage(name, null, null, null, null, null, null, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-24 01:44:20 +00:00
|
|
|
public static void main(String[] args) throws Exception {
|
2017-07-10 16:01:56 +00:00
|
|
|
expect(new MyClassLoader().definePackage1("foo").getName().equals("foo"));
|
|
|
|
|
2013-10-31 22:01:33 +00:00
|
|
|
innerClasses();
|
2013-10-25 20:35:52 +00:00
|
|
|
annotations();
|
2013-12-03 20:08:21 +00:00
|
|
|
genericType();
|
2014-10-11 19:50:53 +00:00
|
|
|
classType();
|
2013-10-31 22:01:33 +00:00
|
|
|
|
2007-07-24 01:44:20 +00:00
|
|
|
Class system = Class.forName("java.lang.System");
|
|
|
|
Field out = system.getDeclaredField("out");
|
2007-07-25 00:34:45 +00:00
|
|
|
Class output = Class.forName("java.io.PrintStream");
|
2007-07-24 01:44:20 +00:00
|
|
|
Method println = output.getDeclaredMethod("println", String.class);
|
|
|
|
|
|
|
|
println.invoke(out.get(null), "Hello, World!");
|
2011-02-23 00:54:56 +00:00
|
|
|
|
|
|
|
expect((Boolean) Reflection.class.getMethod("booleanMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(1 == (Byte) Reflection.class.getMethod("byteMethod").invoke(null));
|
|
|
|
|
|
|
|
expect('2' == (Character) Reflection.class.getMethod
|
|
|
|
("charMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(3 == (Short) Reflection.class.getMethod
|
|
|
|
("shortMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(4 == (Integer) Reflection.class.getMethod
|
|
|
|
("intMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(5.0 == (Float) Reflection.class.getMethod
|
|
|
|
("floatMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(6 == (Long) Reflection.class.getMethod
|
|
|
|
("longMethod").invoke(null));
|
|
|
|
|
|
|
|
expect(7.0 == (Double) Reflection.class.getMethod
|
|
|
|
("doubleMethod").invoke(null));
|
2013-10-25 18:23:41 +00:00
|
|
|
|
2013-12-06 22:45:46 +00:00
|
|
|
{ Class[][] array = new Class[][] { { Class.class } };
|
|
|
|
expect("[Ljava.lang.Class;".equals(array[0].getClass().getName()));
|
|
|
|
expect(Class[].class == array[0].getClass());
|
|
|
|
expect(array.getClass().getComponentType() == array[0].getClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
{ Reflection r = new Reflection();
|
|
|
|
expect(r.egads == 0);
|
|
|
|
|
2013-12-11 15:51:00 +00:00
|
|
|
Reflection.class.getDeclaredField("egads").set(r, (Integer)42);
|
2013-12-11 03:23:16 +00:00
|
|
|
expect(((Integer)Reflection.class.getDeclaredField("egads").get(r)) == 42);
|
2013-12-06 22:45:46 +00:00
|
|
|
|
|
|
|
Reflection.class.getDeclaredField("egads").setInt(r, 43);
|
|
|
|
expect(Reflection.class.getDeclaredField("egads").getInt(r) == 43);
|
|
|
|
|
|
|
|
Integer[] array = new Integer[0];
|
|
|
|
Reflection.class.getDeclaredField("array").set(r, array);
|
|
|
|
expect(Reflection.class.getDeclaredField("array").get(r) == array);
|
|
|
|
|
|
|
|
try {
|
|
|
|
Reflection.class.getDeclaredField("array").set(r, new Object());
|
|
|
|
expect(false);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
|
|
|
Integer integer = 45;
|
|
|
|
Reflection.class.getDeclaredField("integer").set(r, integer);
|
|
|
|
expect(Reflection.class.getDeclaredField("integer").get(r) == integer);
|
|
|
|
|
|
|
|
try {
|
|
|
|
Reflection.class.getDeclaredField("integer").set(r, new Object());
|
|
|
|
expect(false);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Reflection.class.getDeclaredField("integer").set
|
|
|
|
(new Object(), integer);
|
|
|
|
expect(false);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Reflection.class.getDeclaredField("integer").get(new Object());
|
|
|
|
expect(false);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
}
|
2013-12-06 05:28:13 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
Foo.class.getMethod("foo").invoke(null);
|
|
|
|
expect(false);
|
|
|
|
} catch (ExceptionInInitializerError e) {
|
|
|
|
expect(e.getCause() instanceof MyException);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Foo.class.getConstructor().newInstance();
|
|
|
|
expect(false);
|
|
|
|
} catch (NoClassDefFoundError e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Foo.class.getField("foo").get(null);
|
|
|
|
expect(false);
|
|
|
|
} catch (NoClassDefFoundError e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
2013-12-06 22:45:46 +00:00
|
|
|
try {
|
2013-12-11 15:51:00 +00:00
|
|
|
Foo.class.getField("foo").set(null, (Integer)42);
|
2013-12-06 22:45:46 +00:00
|
|
|
expect(false);
|
|
|
|
} catch (NoClassDefFoundError e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Foo.class.getField("foo").set(null, new Object());
|
|
|
|
expect(false);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// cool
|
|
|
|
} catch (NoClassDefFoundError e) {
|
|
|
|
// cool
|
|
|
|
}
|
|
|
|
|
2013-12-06 05:28:13 +00:00
|
|
|
{ Method m = Reflection.class.getMethod("throwOOME");
|
|
|
|
try {
|
|
|
|
m.invoke(null);
|
|
|
|
} catch(Throwable t) {
|
|
|
|
expect(t.getClass() == InvocationTargetException.class);
|
|
|
|
}
|
|
|
|
}
|
2014-01-03 21:40:47 +00:00
|
|
|
|
|
|
|
expect((Foo.class.getMethod("toString").getModifiers()
|
|
|
|
& Modifier.PUBLIC) != 0);
|
2014-03-06 22:01:44 +00:00
|
|
|
|
|
|
|
expect(avian.TestReflection.get(Baz.class.getField("foo"), new Baz())
|
|
|
|
.equals(42));
|
|
|
|
expect((Baz.class.getModifiers() & Modifier.PUBLIC) == 0);
|
fix Class.getDeclaredMethods
getDeclaredMethods was returning methods which were inherited from
interfaces but not (re)declared in the class itself, due to the VM's
internal use of VMClass.methodTable differing from its role in
reflection. For reflection, we must only include the declared
methods, not the inherited but un-redeclared ones.
Previously, we saved the original method table in
ClassAddendum.methodTable before creating a new one which contains
both declared and inherited methods. That wasted space, so this patch
replaces ClassAddendum.methodTable with
ClassAddendum.declaredMethodCount, which specifies how many of the
methods in VMClass.methodTable were declared in that class.
Alternatively, we could ensure that undeclared methods always have
their VMMethod.class_ field set to the declaring class instead of the
inheriting class. I tried this, but it led to subtle crashes in
interface method lookup. The rest of the VM relies not only on
VMClass.methodTable containing all inherited interface methods but
also that those methods point to the inheriting class, not the
declaring class. Changing those assumptions would be a much bigger
(and more dangerous in terms of regression potential) effort than I
care to take on right now. The solution I chose is a bit ugly, but
it's safe.
2014-03-03 16:56:26 +00:00
|
|
|
|
|
|
|
expect(B.class.getDeclaredMethods().length == 0);
|
2014-03-14 17:08:08 +00:00
|
|
|
|
|
|
|
new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
expect(getClass().getDeclaringClass() == null);
|
|
|
|
}
|
|
|
|
}.run();
|
2014-03-19 17:21:26 +00:00
|
|
|
|
|
|
|
expect(avian.testing.annotations.Test.class.getPackage().getName().equals
|
|
|
|
("avian.testing.annotations"));
|
2014-04-17 19:16:21 +00:00
|
|
|
|
|
|
|
expect(Baz.class.getField("foo").getAnnotation(Ann.class) == null);
|
|
|
|
expect(Baz.class.getField("foo").getAnnotations().length == 0);
|
2014-07-12 22:03:11 +00:00
|
|
|
|
2014-10-08 23:04:32 +00:00
|
|
|
expect(new Runnable() { public void run() { } }.getClass()
|
|
|
|
.getEnclosingClass().equals(Reflection.class));
|
|
|
|
|
2014-07-12 22:03:11 +00:00
|
|
|
expect(new Runnable() { public void run() { } }.getClass()
|
|
|
|
.getEnclosingMethod().equals
|
|
|
|
(Reflection.class.getMethod
|
|
|
|
("main", new Class[] { String[].class })));
|
2014-08-28 22:59:06 +00:00
|
|
|
|
|
|
|
Slithy.class.getMethod("tove", Gybe.class);
|
|
|
|
|
|
|
|
try {
|
|
|
|
Slithy.class.getMethod("tove", Bandersnatch.class);
|
|
|
|
expect(false);
|
|
|
|
} catch (NoSuchMethodException e) {
|
|
|
|
// cool
|
|
|
|
}
|
2014-10-05 22:28:36 +00:00
|
|
|
|
|
|
|
expect(C.class.getInterfaces().length == 1);
|
|
|
|
expect(C.class.getInterfaces()[0].equals(B.class));
|
2014-03-06 22:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected static class Baz {
|
|
|
|
public int foo = 42;
|
2007-07-24 01:44:20 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-06 05:28:13 +00:00
|
|
|
|
2014-08-28 22:59:06 +00:00
|
|
|
class Bandersnatch { }
|
|
|
|
|
|
|
|
class Gybe extends Bandersnatch { }
|
|
|
|
|
|
|
|
class Slithy {
|
|
|
|
public static void tove(Gybe gybe) {
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-06 05:28:13 +00:00
|
|
|
class Foo {
|
|
|
|
static {
|
|
|
|
if (true) throw new MyException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Foo() { }
|
|
|
|
|
|
|
|
public static int foo;
|
|
|
|
|
|
|
|
public static void foo() {
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class MyException extends RuntimeException { }
|
fix Class.getDeclaredMethods
getDeclaredMethods was returning methods which were inherited from
interfaces but not (re)declared in the class itself, due to the VM's
internal use of VMClass.methodTable differing from its role in
reflection. For reflection, we must only include the declared
methods, not the inherited but un-redeclared ones.
Previously, we saved the original method table in
ClassAddendum.methodTable before creating a new one which contains
both declared and inherited methods. That wasted space, so this patch
replaces ClassAddendum.methodTable with
ClassAddendum.declaredMethodCount, which specifies how many of the
methods in VMClass.methodTable were declared in that class.
Alternatively, we could ensure that undeclared methods always have
their VMMethod.class_ field set to the declaring class instead of the
inheriting class. I tried this, but it led to subtle crashes in
interface method lookup. The rest of the VM relies not only on
VMClass.methodTable containing all inherited interface methods but
also that those methods point to the inheriting class, not the
declaring class. Changing those assumptions would be a much bigger
(and more dangerous in terms of regression potential) effort than I
care to take on right now. The solution I chose is a bit ugly, but
it's safe.
2014-03-03 16:56:26 +00:00
|
|
|
|
|
|
|
interface A {
|
|
|
|
void foo();
|
|
|
|
}
|
|
|
|
|
|
|
|
interface B extends A { }
|
2014-04-17 19:16:21 +00:00
|
|
|
|
2014-10-05 22:28:36 +00:00
|
|
|
class C implements B {
|
|
|
|
public void foo() { }
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:16:21 +00:00
|
|
|
@interface Ann { }
|