diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 70bd4dc81e..2f9c9a72ee 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -415,7 +415,7 @@ public final class Class implements Type, AnnotatedElement { byte[] inner = table[i].inner; if (inner != null && inner.length > 1) { String name = new String(inner, 0, inner.length - 1); - if (name.startsWith(prefix)) { + if (name.startsWith(prefix) && name.indexOf('$', prefix.length()) < 0) { Class innerClass = getClassLoader().loadClass(name); result[counter++] = innerClass; } diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 7b8517901c..debabdb0f3 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -66,6 +66,14 @@ public class Field extends AccessibleObject { new String(vmField.spec, 0, vmField.spec.length - 1, false)); } + public Type getGenericType() { + if (vmField.addendum == null || vmField.addendum.signature == null) { + return getType(); + } + String signature = Classes.toString((byte[]) vmField.addendum.signature); + return SignatureParser.parse(vmField.class_.loader, signature); + } + public Object get(Object instance) throws IllegalAccessException { Object target; if ((vmField.flags & Modifier.STATIC) != 0) { diff --git a/classpath/java/lang/reflect/ParameterizedType.java b/classpath/java/lang/reflect/ParameterizedType.java new file mode 100644 index 0000000000..3fc6401ce1 --- /dev/null +++ b/classpath/java/lang/reflect/ParameterizedType.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface ParameterizedType extends Type { + Type[] getActualTypeArguments(); + Type getOwnerType(); + Type getRawType(); +} diff --git a/classpath/java/lang/reflect/SignatureParser.java b/classpath/java/lang/reflect/SignatureParser.java new file mode 100644 index 0000000000..8db3b68b98 --- /dev/null +++ b/classpath/java/lang/reflect/SignatureParser.java @@ -0,0 +1,135 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +import java.util.ArrayList; +import java.util.List; + +class SignatureParser { + private final ClassLoader loader; + private final char[] array; + private int offset; + private final Type type; + + static Type parse(ClassLoader loader, String signature) { + return new SignatureParser(loader, signature).type; + } + + private SignatureParser(ClassLoader loader, String signature) { + this.loader = loader; + array = signature.toCharArray(); + type = parseType(); + if (offset != array.length) { + throw new IllegalArgumentException("Extra characters after " + offset + + ": " + signature); + } + } + + private Type parseType() { + char c = array[offset++]; + if (c == 'B') { + return Byte.TYPE; + } else if (c == 'C') { + return Character.TYPE; + } else if (c == 'D') { + return Double.TYPE; + } else if (c == 'F') { + return Float.TYPE; + } else if (c == 'I') { + return Integer.TYPE; + } else if (c == 'J') { + return Long.TYPE; + } else if (c == 'S') { + return Short.TYPE; + } else if (c == 'Z') { + return Boolean.TYPE; + } else if (c != 'L') { + throw new IllegalArgumentException("Unexpected character: " + c); + } + StringBuilder builder = new StringBuilder(); + Type ownerType = null; + for (;;) { + for (;;) { + c = array[offset++]; + if (c == ';' || c == '<') { + break; + } + builder.append(c == '/' ? '.' : c); + } + String rawTypeName = builder.toString(); + Class rawType; + try { + rawType = loader.loadClass(rawTypeName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Could not find class " + rawTypeName); + } + if (c == ';') { + return rawType; + } + List args = new ArrayList(); + while (array[offset] != '>') { + args.add(parseType()); + } + ++offset; + c = array[offset++]; + ParameterizedType type = makeType(args.toArray(new Type[args.size()]), ownerType, rawType); + if (c == ';') { + return type; + } + if (c != '.') { + throw new RuntimeException("TODO"); + } + ownerType = type; + builder.append("$"); + } + } + + private static String typeName(Type type) { + if (type instanceof Class) { + Class clazz = (Class) type; + return clazz.getName(); + } + return type.toString(); + } + + private static ParameterizedType makeType(final Type[] args, final Type owner, final Type raw) { + return new ParameterizedType() { + @Override + public Type getRawType() { + return raw; + } + + @Override + public Type getOwnerType() { + return owner; + } + + @Override + public Type[] getActualTypeArguments() { + return args; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(typeName(raw)); + builder.append('<'); + String sep = ""; + for (Type t : args) { + builder.append(sep).append(typeName(t)); + sep = ", "; + } + builder.append('>'); + return builder.toString(); + } + }; + } +} diff --git a/test/Reflection.java b/test/Reflection.java index 8d889837b4..764293f0af 100644 --- a/test/Reflection.java +++ b/test/Reflection.java @@ -1,5 +1,7 @@ -import java.lang.reflect.Method; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; public class Reflection { public static boolean booleanMethod() { @@ -38,7 +40,9 @@ public class Reflection { if (! v) throw new RuntimeException(); } - private static class Hello { } + private static class Hello { + private class World { } + } private static void innerClasses() throws Exception { Class c = Reflection.class; @@ -54,9 +58,44 @@ public class Reflection { expect(egads.getAnnotation(Deprecated.class) == null); } + public static Hello>.World> 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); + } + public static void main(String[] args) throws Exception { innerClasses(); annotations(); + genericType(); Class system = Class.forName("java.lang.System"); Field out = system.getDeclaredField("out");