diff --git a/classpath/avian/AnnotationInvocationHandler.java b/classpath/avian/AnnotationInvocationHandler.java index 47d7428863..c49f56f13c 100644 --- a/classpath/avian/AnnotationInvocationHandler.java +++ b/classpath/avian/AnnotationInvocationHandler.java @@ -21,11 +21,12 @@ public class AnnotationInvocationHandler implements InvocationHandler { } public Object invoke(Object proxy, Method method, Object[] arguments) { + String name = method.getName(); for (int i = 2; i < data.length; i += 2) { - if (method.getName().equals(data[i])) { + if (name.equals(data[i])) { return data[i + 1]; } } - throw new IllegalArgumentException(); + return method.getDefaultValue(); } } diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 86fba20710..8b14fd233f 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -117,7 +117,7 @@ public class Classes { } case '@': - return parseAnnotation(loader, pool, in); + return getAnnotation(loader, parseAnnotation(loader, pool, in)); case '[': { Object[] array = new Object[read2(in)]; @@ -450,6 +450,25 @@ public class Classes { return (Annotation) a[0]; } + public static Object getAnnotationDefaultValue(ClassLoader loader, + MethodAddendum addendum) { + if (addendum == null) { + return null; + } + byte[] annotationDefault = (byte[]) addendum.annotationDefault; + if (annotationDefault == null) { + return null; + } + try { + return parseAnnotationValue(loader, addendum.pool, + new ByteArrayInputStream(annotationDefault)); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } + public static native Method makeMethod(Class c, int slot); private static native void acquireClassLock(); diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 51a64c905c..55c7744c13 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -142,4 +142,9 @@ public class Method extends AccessibleObject implements Member { public boolean isVarArgs() { return (getModifiers() & 0x80) != 0; } + + public Object getDefaultValue() { + ClassLoader loader = getDeclaringClass().getClassLoader(); + return Classes.getAnnotationDefaultValue(loader, vmMethod.addendum); + } } diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java index c4ed436dfb..4160c6bdf3 100644 --- a/classpath/java/lang/reflect/Proxy.java +++ b/classpath/java/lang/reflect/Proxy.java @@ -29,6 +29,8 @@ import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; import java.io.OutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -37,6 +39,13 @@ public class Proxy { private static int nextNumber; protected InvocationHandler h; + protected final static Map methodRefsMap = + new HashMap(); + protected final Method[] methodRefs; + + public Proxy() { + methodRefs = methodRefsMap.get(getClass()); + } public static Class getProxyClass(ClassLoader loader, Class ... interfaces) @@ -90,15 +99,14 @@ public class Proxy { write1(out, aload_0); - write1(out, ldc_w); - write2(out, ConstantPool.addClass(pool, className) + 1); + write1(out, aload_0); + write1(out, getfield); + write2(out, ConstantPool.addFieldRef + (pool, className, + "methodRefs", "[Ljava/lang/reflect/Method;") + 1); write1(out, ldc_w); write2(out, ConstantPool.addInteger(pool, index) + 1); - write1(out, invokestatic); - write2(out, ConstantPool.addMethodRef - (pool, "avian/Classes", - "makeMethod", "(Ljava/lang/Class;I)Ljava/lang/reflect/Method;") - + 1); + write1(out, aaload); write1(out, ldc_w); write2(out, ConstantPool.addInteger(pool, parameterCount) + 1); @@ -324,7 +332,13 @@ public class Proxy { ByteArrayOutputStream out = new ByteArrayOutputStream(); write2(out, 2); // max stack write2(out, 2); // max locals - write4(out, 6); // length + write4(out, 10); // length + + write1(out, aload_0); + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/reflect/Proxy", + "", "()V") + 1); write1(out, aload_0); write1(out, aload_1); @@ -353,47 +367,48 @@ public class Proxy { (pool, interfaces[i].getName().replace('.', '/')); } - Map virtualMap = new HashMap(); + Set specs = new HashSet(); + List methodTable = new ArrayList(); + List refs = new ArrayList(); for (Class c: interfaces) { avian.VMMethod[] ivtable = SystemClassLoader.vmClass(c).virtualTable; if (ivtable != null) { for (avian.VMMethod m: ivtable) { - virtualMap.put - (Classes.toString(m.name) + Classes.toString(m.spec), m); + String spec = Classes.toString(m.name) + Classes.toString(m.spec); + if (specs.contains(spec)) { + continue; + } + methodTable.add(new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, Classes.toString(m.name)), + ConstantPool.addUtf8(pool, Classes.toString(m.spec)), + makeInvokeCode(pool, name, m.spec, m.parameterCount, + m.parameterFootprint, methodTable.size()))); + refs.add(new Method(m)); } } } - MethodData[] methodTable = new MethodData[virtualMap.size() + 1]; - { int i = 0; - for (avian.VMMethod m: virtualMap.values()) { - methodTable[i] = new MethodData - (0, - ConstantPool.addUtf8(pool, Classes.toString(m.name)), - ConstantPool.addUtf8(pool, Classes.toString(m.spec)), - makeInvokeCode(pool, name, m.spec, m.parameterCount, - m.parameterFootprint, i)); - ++ i; - } - - methodTable[i++] = new MethodData - (Modifier.PUBLIC, - ConstantPool.addUtf8(pool, ""), - ConstantPool.addUtf8 - (pool, "(Ljava/lang/reflect/InvocationHandler;)V"), - makeConstructorCode(pool)); - } + methodTable.add(new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, ""), + ConstantPool.addUtf8 + (pool, "(Ljava/lang/reflect/InvocationHandler;)V"), + makeConstructorCode(pool))); int nameIndex = ConstantPool.addClass(pool, name); int superIndex = ConstantPool.addClass(pool, "java/lang/reflect/Proxy"); ByteArrayOutputStream out = new ByteArrayOutputStream(); Assembler.writeClass - (out, pool, nameIndex, superIndex, interfaceIndexes, methodTable); + (out, pool, nameIndex, superIndex, interfaceIndexes, + methodTable.toArray(new MethodData[methodTable.size()])); byte[] classData = out.toByteArray(); - return avian.SystemClassLoader.getClass + Class result = avian.SystemClassLoader.getClass (avian.Classes.defineVMClass(loader, classData, 0, classData.length)); + methodRefsMap.put(result, refs.toArray(new Method[refs.size()])); + return result; } public static Object newProxyInstance(ClassLoader loader, diff --git a/makefile b/makefile index b3058f4dd2..d5594388f9 100755 --- a/makefile +++ b/makefile @@ -1567,7 +1567,7 @@ $(test-dep): $(test-sources) $(test-library) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \ if test -n "$${files}"; then \ - $(javac) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ + $(javac) -classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ -bootclasspath $(boot-classpath) test/Subroutine.java diff --git a/test/Annotations.java b/test/Annotations.java index c0234d21c0..f09b0251eb 100644 --- a/test/Annotations.java +++ b/test/Annotations.java @@ -1,7 +1,10 @@ +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import avian.testing.annotations.Color; import avian.testing.annotations.Test; +import avian.testing.annotations.TestComplex; import avian.testing.annotations.TestEnum; import avian.testing.annotations.TestInteger; @@ -27,6 +30,8 @@ public class Annotations { Method noAnno = Annotations.class.getMethod("noAnnotation"); expect(noAnno.getAnnotation(Test.class) == null); expect(noAnno.getAnnotations().length == 0); + testProxyDefaultValue(); + testComplexAnnotation(); } @Test("couscous") @@ -39,4 +44,46 @@ public class Annotations { public static void noAnnotation() { } + + private static void testProxyDefaultValue() { + ClassLoader loader = Annotations.class.getClassLoader(); + InvocationHandler handler = new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object... args) { + return method.getDefaultValue(); + } + }; + Test test = (Test) + Proxy.newProxyInstance(loader, new Class[] { Test.class }, handler); + expect("Hello, world!".equals(test.value())); + } + + private interface World { + @TestComplex(arrayValue = { @Test, @Test(value = "7/9") }, + stringValue = "adjunct element", charValue = '7', doubleValue = 0.7778, + classValue = TestInteger.class) + int hello(); + } + + private static void testComplexAnnotation(TestComplex annotation) + throws Exception + { + expect(2 == annotation.arrayValue().length); + expect("Hello, world!".equals(annotation.arrayValue()[0].value())); + expect("7/9".equals(annotation.arrayValue()[1].value())); + expect("adjunct element".equals(annotation.stringValue())); + expect('7' == annotation.charValue()); + expect(0.7778 == annotation.doubleValue()); + expect(TestInteger.class == annotation.classValue()); + } + + public static void testComplexAnnotation() throws Exception { + ClassLoader loader = Annotations.class.getClassLoader(); + TestComplex annotation = (TestComplex) + World.class.getMethod("hello").getAnnotation(TestComplex.class); + testComplexAnnotation(annotation); + Class clazz = Proxy.getProxyClass(loader, new Class[] { World.class }); + annotation = (TestComplex) + clazz.getMethod("hello").getAnnotation(TestComplex.class); + expect(annotation == null); + } } diff --git a/test/avian/testing/annotations/Test.java b/test/avian/testing/annotations/Test.java index f9ba919647..24b8229480 100644 --- a/test/avian/testing/annotations/Test.java +++ b/test/avian/testing/annotations/Test.java @@ -5,5 +5,5 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Test { - public String value(); + public String value() default "Hello, world!"; } diff --git a/test/avian/testing/annotations/TestComplex.java b/test/avian/testing/annotations/TestComplex.java new file mode 100644 index 0000000000..4e271cc9d9 --- /dev/null +++ b/test/avian/testing/annotations/TestComplex.java @@ -0,0 +1,14 @@ +package avian.testing.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TestComplex { + public Test[] arrayValue(); + public Class classValue(); + public String stringValue(); + public char charValue(); + public double doubleValue(); +} +