/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.*;
import java.io.IOException;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Reflection test.
*/
public class Main {
private static boolean FULL_ACCESS_CHECKS = false; // b/5861201
public Main() {}
public Main(ArrayList<Integer> stuff) {}
void printMethodInfo(Method meth) {
Class[] params, exceptions;
int i;
System.out.println("Method name is " + meth.getName());
System.out.println(" Declaring class is "
+ meth.getDeclaringClass().getName());
params = meth.getParameterTypes();
for (i = 0; i < params.length; i++)
System.out.println(" Arg " + i + ": " + params[i].getName());
exceptions = meth.getExceptionTypes();
for (i = 0; i < exceptions.length; i++)
System.out.println(" Exc " + i + ": " + exceptions[i].getName());
System.out.println(" Return type is " + meth.getReturnType().getName());
System.out.println(" Access flags are 0x"
+ Integer.toHexString(meth.getModifiers()));
//System.out.println(" GenericStr is " + meth.toGenericString());
}
void printFieldInfo(Field field) {
System.out.println("Field name is " + field.getName());
System.out.println(" Declaring class is "
+ field.getDeclaringClass().getName());
System.out.println(" Field type is " + field.getType().getName());
System.out.println(" Access flags are 0x"
+ Integer.toHexString(field.getModifiers()));
}
private void showStrings(Target instance)
throws NoSuchFieldException, IllegalAccessException {
Class target = Target.class;
String one, two, three, four;
Field field = null;
field = target.getField("string1");
one = (String) field.get(instance);
field = target.getField("string2");
two = (String) field.get(instance);
field = target.getField("string3");
three = (String) field.get(instance);
System.out.println(" ::: " + one + ":" + two + ":" + three);
}
public static void checkAccess() {
try {
Class target = otherpackage.Other.class;
Object instance = new otherpackage.Other();
Method meth;
meth = target.getMethod("publicMethod", (Class[]) null);
meth.invoke(instance);
try {
meth = target.getMethod("packageMethod", (Class[]) null);
System.err.println("succeeded on package-scope method");
} catch (NoSuchMethodException nsme) {
// good
}
instance = otherpackage.Other.getInnerClassInstance();
target = instance.getClass();
meth = target.getMethod("innerMethod", (Class[]) null);
try {
if (!FULL_ACCESS_CHECKS) { throw new IllegalAccessException(); }
meth.invoke(instance);
System.err.println("inner-method invoke unexpectedly worked");
} catch (IllegalAccessException iae) {
// good
}
Field field = target.getField("innerField");
try {
int x = field.getInt(instance);
if (!FULL_ACCESS_CHECKS) { throw new IllegalAccessException(); }
System.err.println("field get unexpectedly worked: " + x);
} catch (IllegalAccessException iae) {
// good
}
} catch (Exception ex) {
System.out.println("----- unexpected exception -----");
ex.printStackTrace();
}
}
public void run() {
Class target = Target.class;
Method meth = null;
Field field = null;
boolean excep;
try {
meth = target.getMethod("myMethod", new Class[] { int.class });
if (meth.getDeclaringClass() != target)
throw new RuntimeException();
printMethodInfo(meth);
meth = target.getMethod("myMethod", new Class[] { float.class });
printMethodInfo(meth);
meth = target.getMethod("myNoargMethod", (Class[]) null);
printMethodInfo(meth);
meth = target.getMethod("myMethod",
new Class[] { String[].class, float.class, char.class });
printMethodInfo(meth);
Target instance = new Target();
Object[] argList = new Object[] {
new String[] { "hi there" },
new Float(3.1415926f),
new Character('Q')
};
System.out.println("Before, float is "
+ ((Float)argList[1]).floatValue());
Integer boxval;
boxval = (Integer) meth.invoke(instance, argList);
System.out.println("Result of invoke: " + boxval.intValue());
System.out.println("Calling no-arg void-return method");
meth = target.getMethod("myNoargMethod", (Class[]) null);
meth.invoke(instance, (Object[]) null);
/* try invoking a method that throws an exception */
meth = target.getMethod("throwingMethod", (Class[]) null);
try {
meth.invoke(instance, (Object[]) null);
System.out.println("GLITCH: didn't throw");
} catch (InvocationTargetException ite) {
System.out.println("Invoke got expected exception:");
System.out.println(ite.getClass().getName());
System.out.println(ite.getCause());
}
catch (Exception ex) {
System.out.println("GLITCH: invoke got wrong exception:");
ex.printStackTrace();
}
System.out.println("");
field = target.getField("string1");
if (field.getDeclaringClass() != target)
throw new RuntimeException();
printFieldInfo(field);
String strVal = (String) field.get(instance);
System.out.println(" string1 value is '" + strVal + "'");
showStrings(instance);
field.set(instance, new String("a new string"));
strVal = (String) field.get(instance);
System.out.println(" string1 value is now '" + strVal + "'");
showStrings(instance);
try {
field.set(instance, new Object());
System.out.println("WARNING: able to store Object into String");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected illegal obj store exc");
}
try {
String four;
field = target.getField("string4");
four = (String) field.get(instance);
System.out.println("WARNING: able to access string4: "
+ four);
}
catch (IllegalAccessException iae) {
System.out.println(" got expected access exc");
}
catch (NoSuchFieldException nsfe) {
System.out.println(" got the other expected access exc");
}
try {
String three;
field = target.getField("string3");
three = (String) field.get(this);
System.out.println("WARNING: able to get string3 in wrong obj: "
+ three);
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected arg exc");
}
/*
* Try setting a field to null.
*/
String four;
field = target.getDeclaredField("string3");
field.set(instance, null);
/*
* Do some stuff with long.
*/
long longVal;
field = target.getField("pubLong");
longVal = field.getLong(instance);
System.out.println("pubLong initial value is " +
Long.toHexString(longVal));
field.setLong(instance, 0x9988776655443322L);
longVal = field.getLong(instance);
System.out.println("pubLong new value is " +
Long.toHexString(longVal));
field = target.getField("superInt");
if (field.getDeclaringClass() == target)
throw new RuntimeException();
printFieldInfo(field);
int intVal = field.getInt(instance);
System.out.println(" superInt value is " + intVal);
Integer boxedIntVal = (Integer) field.get(instance);
System.out.println(" superInt boxed is " + boxedIntVal);
field.set(instance, new Integer(20202));
intVal = field.getInt(instance);
System.out.println(" superInt value is now " + intVal);
field.setShort(instance, (short)30303);
intVal = field.getInt(instance);
System.out.println(" superInt value (from short) is now " +intVal);
field.setInt(instance, 40404);
intVal = field.getInt(instance);
System.out.println(" superInt value is now " + intVal);
try {
field.set(instance, new Long(123));
System.out.println("FAIL: expected exception not thrown");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected long->int failure");
}
try {
field.setLong(instance, 123);
System.out.println("FAIL: expected exception not thrown");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected long->int failure");
}
try {
field.set(instance, new String("abc"));
System.out.println("FAIL: expected exception not thrown");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected string->int failure");
}
try {
field.getShort(instance);
System.out.println("FAIL: expected exception not thrown");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected int->short failure");
}
field = target.getField("superClassInt");
printFieldInfo(field);
int superClassIntVal = field.getInt(instance);
System.out.println(" superClassInt value is " + superClassIntVal);
field = target.getField("staticDouble");
printFieldInfo(field);
double staticDoubleVal = field.getDouble(null);
System.out.println(" staticDoubleVal value is " + staticDoubleVal);
try {
field.getLong(instance);
System.out.println("FAIL: expected exception not thrown");
}
catch (IllegalArgumentException iae) {
System.out.println(" got expected double->long failure");
}
excep = false;
try {
field = target.getField("aPrivateInt");
printFieldInfo(field);
}
catch (NoSuchFieldException nsfe) {
System.out.println("as expected: aPrivateInt not found");
excep = true;
}
if (!excep)
System.out.println("BUG: got aPrivateInt");
field = target.getField("constantString");
printFieldInfo(field);
String val = (String) field.get(instance);
System.out.println(" Constant test value is " + val);
field = target.getField("cantTouchThis");
printFieldInfo(field);
intVal = field.getInt(instance);
System.out.println(" cantTouchThis is " + intVal);
try {
field.setInt(instance, 99);
System.out.println("ERROR: set-final did not throw exception");
} catch (IllegalAccessException iae) {
System.out.println(" as expected: set-final throws exception");
}
intVal = field.getInt(instance);
System.out.println(" cantTouchThis is still " + intVal);
System.out.println(" " + field + " accessible=" + field.isAccessible());
field.setAccessible(true);
System.out.println(" " + field + " accessible=" + field.isAccessible());
field.setInt(instance, 87); // exercise int version
intVal = field.getInt(instance);
System.out.println(" cantTouchThis is now " + intVal);
field.set(instance, 88); // exercise Object version
intVal = field.getInt(instance);
System.out.println(" cantTouchThis is now " + intVal);
Constructor<Target> cons;
Target targ;
Object[] args;
cons = target.getConstructor(new Class[] { int.class,float.class });
args = new Object[] { new Integer(7), new Float(3.3333) };
System.out.println("cons modifiers=" + cons.getModifiers());
targ = cons.newInstance(args);
targ.myMethod(17);
try {
Thrower thrower = Thrower.class.newInstance();
System.out.println("ERROR: Class.newInstance did not throw exception");
} catch (UnsupportedOperationException uoe) {
System.out.println("got expected exception for Class.newInstance");
} catch (Exception e) {
System.out.println("ERROR: Class.newInstance got unexpected exception: " +
e.getClass().getName());
}
try {
Constructor<Thrower> constructor = Thrower.class.getDeclaredConstructor();
Thrower thrower = constructor.newInstance();
System.out.println("ERROR: Constructor.newInstance did not throw exception");
} catch (InvocationTargetException ite) {
System.out.println("got expected exception for Constructor.newInstance");
} catch (Exception e) {
System.out.println("ERROR: Constructor.newInstance got unexpected exception: " +
e.getClass().getName());
}
} catch (Exception ex) {
System.out.println("----- unexpected exception -----");
ex.printStackTrace();
}
System.out.println("ReflectTest done!");
}
public static void checkType() {
Method m;
try {
m = Collections.class.getDeclaredMethod("checkType",
Object.class, Class.class);
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
return;
}
System.out.println(m + " accessible=" + m.isAccessible());
m.setAccessible(true);
System.out.println(m + " accessible=" + m.isAccessible());
try {
m.invoke(null, new Object(), Object.class);
} catch (IllegalAccessException iae) {
iae.printStackTrace();
return;
} catch (InvocationTargetException ite) {
ite.printStackTrace();
return;
}
try {
String s = "Should be ignored";
m.invoke(s, new Object(), Object.class);
} catch (IllegalAccessException iae) {
iae.printStackTrace();
return;
} catch (InvocationTargetException ite) {
ite.printStackTrace();
return;
}
try {
System.out.println("checkType invoking null");
m.invoke(null, new Object(), int.class);
System.out.println("ERROR: should throw InvocationTargetException");
} catch (InvocationTargetException ite) {
System.out.println("checkType got expected exception");
} catch (IllegalAccessException iae) {
iae.printStackTrace();
return;
}
}
public static void checkClinitForFields() throws Exception {
// Loading a class constant shouldn't run <clinit>.
System.out.println("calling const-class FieldNoisyInitUser.class");
Class niuClass = FieldNoisyInitUser.class;
System.out.println("called const-class FieldNoisyInitUser.class");
// Getting the declared fields doesn't run <clinit>.
Field[] fields = niuClass.getDeclaredFields();
System.out.println("got fields");
Field field = niuClass.getField("staticField");
System.out.println("got field");
field.get(null);
System.out.println("read field value");
// FieldNoisyInitUser should now be initialized, but FieldNoisyInit shouldn't be initialized yet.
FieldNoisyInitUser niu = new FieldNoisyInitUser();
FieldNoisyInit ni = new FieldNoisyInit();
System.out.println("");
}
public static void checkClinitForMethods() throws Exception {
// Loading a class constant shouldn't run <clinit>.
System.out.println("calling const-class MethodNoisyInitUser.class");
Class niuClass = MethodNoisyInitUser.class;
System.out.println("called const-class MethodNoisyInitUser.class");
// Getting the declared methods doesn't run <clinit>.
Method[] methods = niuClass.getDeclaredMethods();
System.out.println("got methods");
Method method = niuClass.getMethod("staticMethod", (Class[]) null);
System.out.println("got method");
method.invoke(null);
System.out.println("invoked method");
// MethodNoisyInitUser should now be initialized, but MethodNoisyInit shouldn't be initialized yet.
MethodNoisyInitUser niu = new MethodNoisyInitUser();
MethodNoisyInit ni = new MethodNoisyInit();
System.out.println("");
}
/*
* Test some generic type stuff.
*/
public List<String> dummy;
public Map<Integer,String> fancyMethod(ArrayList<String> blah) { return null; }
public static void checkGeneric() {
Field field;
try {
field = Main.class.getField("dummy");
} catch (NoSuchFieldException nsfe) {
throw new RuntimeException(nsfe);
}
Type listType = field.getGenericType();
System.out.println("generic field: " + listType);
Method method;
try {
method = Main.class.getMethod("fancyMethod",
new Class[] { ArrayList.class });
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
Type[] parmTypes = method.getGenericParameterTypes();
Type ret = method.getGenericReturnType();
System.out.println("generic method " + method.getName() + " params='"
+ stringifyTypeArray(parmTypes) + "' ret='" + ret + "'");
Constructor ctor;
try {
ctor = Main.class.getConstructor(new Class[] { ArrayList.class });
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
parmTypes = ctor.getGenericParameterTypes();
System.out.println("generic ctor " + ctor.getName() + " params='"
+ stringifyTypeArray(parmTypes) + "'");
}
/*
* Convert an array of Type into a string. Start with an array count.
*/
private static String stringifyTypeArray(Type[] types) {
StringBuilder stb = new StringBuilder();
boolean first = true;
stb.append("[" + types.length + "]");
for (Type t: types) {
if (first) {
stb.append(" ");
first = false;
} else {
stb.append(", ");
}
stb.append(t.toString());
}
return stb.toString();
}
public static void checkUnique() {
Field field1, field2;
try {
field1 = Main.class.getField("dummy");
field2 = Main.class.getField("dummy");
} catch (NoSuchFieldException nsfe) {
throw new RuntimeException(nsfe);
}
if (field1 == field2) {
System.out.println("ERROR: fields shouldn't have reference equality");
} else {
System.out.println("fields are unique");
}
if (field1.hashCode() == field2.hashCode() && field1.equals(field2)) {
System.out.println("fields are .equals");
} else {
System.out.println("ERROR: fields fail equality");
}
Method method1, method2;
try {
method1 = Main.class.getMethod("fancyMethod", new Class[] { ArrayList.class });
method2 = Main.class.getMethod("fancyMethod", new Class[] { ArrayList.class });
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
if (method1 == method2) {
System.out.println("ERROR: methods shouldn't have reference equality");
} else {
System.out.println("methods are unique");
}
if (method1.hashCode() == method2.hashCode() && method1.equals(method2)) {
System.out.println("methods are .equals");
} else {
System.out.println("ERROR: methods fail equality");
}
}
public static void checkParametrizedTypeEqualsAndHashCode() {
Method method1;
Method method2;
Method method3;
try {
method1 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
method2 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
method3 = ParametrizedTypeTest.class.getDeclaredMethod("aMethodIdentical", Set.class);
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
Type type1 = types1.get(0);
Type type2 = types2.get(0);
Type type3 = types3.get(0);
if (type1 instanceof ParameterizedType) {
System.out.println("type1 is a ParameterizedType");
}
if (type2 instanceof ParameterizedType) {
System.out.println("type2 is a ParameterizedType");
}
if (type3 instanceof ParameterizedType) {
System.out.println("type3 is a ParameterizedType");
}
if (type1.equals(type2)) {
System.out.println("type1("+type1+") equals type2("+type2+")");
} else {
System.out.println("type1("+type1+") does not equal type2("+type2+")");
}
if (type1.equals(type3)) {
System.out.println("type1("+type1+") equals type3("+type3+")");
} else {
System.out.println("type1("+type1+") does not equal type3("+type3+")");
}
if (type1.hashCode() == type2.hashCode()) {
System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
} else {
System.out.println(
"type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
}
if (type1.hashCode() == type3.hashCode()) {
System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
} else {
System.out.println(
"type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
}
}
public static void checkGenericArrayTypeEqualsAndHashCode() {
Method method1;
Method method2;
Method method3;
try {
method1 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
method2 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
method3 = GenericArrayTypeTest.class.getDeclaredMethod("aMethodIdentical", Object[].class);
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
Type type1 = types1.get(0);
Type type2 = types2.get(0);
Type type3 = types3.get(0);
if (type1 instanceof GenericArrayType) {
System.out.println("type1 is a GenericArrayType");
}
if (type2 instanceof GenericArrayType) {
System.out.println("type2 is a GenericArrayType");
}
if (type3 instanceof GenericArrayType) {
System.out.println("type3 is a GenericArrayType");
}
if (type1.equals(type2)) {
System.out.println("type1("+type1+") equals type2("+type2+")");
} else {
System.out.println("type1("+type1+") does not equal type2("+type2+")");
}
if (type1.equals(type3)) {
System.out.println("type1("+type1+") equals type3("+type3+")");
} else {
System.out.println("type1("+type1+") does not equal type3("+type3+")");
}
if (type1.hashCode() == type2.hashCode()) {
System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
} else {
System.out.println(
"type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
}
if (type1.hashCode() == type3.hashCode()) {
System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
} else {
System.out.println(
"type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
}
}
public static void main(String[] args) throws Exception {
Main test = new Main();
test.run();
checkAccess();
checkType();
checkClinitForFields();
checkClinitForMethods();
checkGeneric();
checkUnique();
checkParametrizedTypeEqualsAndHashCode();
checkGenericArrayTypeEqualsAndHashCode();
}
}
class SuperTarget {
public SuperTarget() {
System.out.println("SuperTarget constructor ()V");
superInt = 1010101;
superClassInt = 1010102;
}
public int myMethod(float floatArg) {
System.out.println("myMethod (F)I " + floatArg);
return 6;
}
public int superInt;
public static int superClassInt;
}
class Target extends SuperTarget {
public Target() {
System.out.println("Target constructor ()V");
}
public Target(int ii, float ff) {
System.out.println("Target constructor (IF)V : ii="
+ ii + " ff=" + ff);
anInt = ii;
}
public int myMethod(int intarg) throws NullPointerException, IOException {
System.out.println("myMethod (I)I");
System.out.println(" arg=" + intarg + " anInt=" + anInt);
return 5;
}
public int myMethod(String[] strarg, float f, char c) {
System.out.println("myMethod: " + strarg[0] + " " + f + " " + c + " !");
return 7;
}
public static void myNoargMethod() {
System.out.println("myNoargMethod ()V");
}
public void throwingMethod() {
System.out.println("throwingMethod");
throw new NullPointerException("gratuitous throw!");
}
public void misc() {
System.out.println("misc");
}
public int anInt;
public String string1 = "hey";
public String string2 = "yo";
public String string3 = "there";
private String string4 = "naughty";
public static final String constantString = "a constant string";
private int aPrivateInt;
public final int cantTouchThis = 77;
public long pubLong = 0x1122334455667788L;
public static double staticDouble = 3.3;
}
class FieldNoisyInit {
static {
System.out.println("FieldNoisyInit is initializing");
//Throwable th = new Throwable();
//th.printStackTrace();
}
}
class FieldNoisyInitUser {
static {
System.out.println("FieldNoisyInitUser is initializing");
}
public static int staticField;
public static FieldNoisyInit noisy;
}
class MethodNoisyInit {
static {
System.out.println("MethodNoisyInit is initializing");
//Throwable th = new Throwable();
//th.printStackTrace();
}
}
class MethodNoisyInitUser {
static {
System.out.println("MethodNoisyInitUser is initializing");
}
public static void staticMethod() {}
public void createMethodNoisyInit(MethodNoisyInit ni) {}
}
class Thrower {
public Thrower() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
}
class ParametrizedTypeTest {
public void aMethod(Set<String> names) {}
public void aMethodIdentical(Set<String> names) {}
}
class GenericArrayTypeTest<T> {
public void aMethod(T[] names) {}
public void aMethodIdentical(T[] names) {}
}