From 7aa906d97bf5476edff745ed2bc8ac096cbb54c5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 18 Sep 2009 18:01:54 -0600 Subject: [PATCH] support runtime-visible annotations and java.lang.reflect.Proxy --- .../avian/AnnotationInvocationHandler.java | 31 ++ classpath/avian/Pair.java | 16 + classpath/java/lang/Class.java | 256 +++++++++- classpath/java/lang/ClassLoader.java | 4 +- classpath/java/lang/annotation/Retention.java | 16 + .../java/lang/annotation/RetentionPolicy.java | 15 + .../java/lang/reflect/AccessibleObject.java | 10 +- .../java/lang/reflect/AnnotatedElement.java | 23 + classpath/java/lang/reflect/Constructor.java | 14 + classpath/java/lang/reflect/Field.java | 50 +- .../java/lang/reflect/InvocationHandler.java | 15 + classpath/java/lang/reflect/Method.java | 50 +- classpath/java/lang/reflect/Proxy.java | 54 +++ src/builtin.cpp | 449 +++++++++++++++++- src/compile.cpp | 9 +- src/interpret.cpp | 6 +- src/machine.cpp | 274 ++++++----- src/machine.h | 32 +- src/process.h | 16 - src/processor.h | 2 + src/types.def | 14 +- test/Annotations.java | 49 ++ test/Proxies.java | 42 ++ 23 files changed, 1278 insertions(+), 169 deletions(-) create mode 100644 classpath/avian/AnnotationInvocationHandler.java create mode 100644 classpath/avian/Pair.java create mode 100644 classpath/java/lang/annotation/Retention.java create mode 100644 classpath/java/lang/annotation/RetentionPolicy.java create mode 100644 classpath/java/lang/reflect/AnnotatedElement.java create mode 100644 classpath/java/lang/reflect/InvocationHandler.java create mode 100644 classpath/java/lang/reflect/Proxy.java create mode 100644 test/Annotations.java create mode 100644 test/Proxies.java diff --git a/classpath/avian/AnnotationInvocationHandler.java b/classpath/avian/AnnotationInvocationHandler.java new file mode 100644 index 0000000000..1a0a229fa0 --- /dev/null +++ b/classpath/avian/AnnotationInvocationHandler.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2009, 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 avian; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationHandler; + +public class AnnotationInvocationHandler implements InvocationHandler { + private Object[] data; + + public AnnotationInvocationHandler(Object[] data) { + this.data = data; + } + + public Object invoke(Object proxy, Method method, Object[] arguments) { + for (int i = 2; i < data.length; i += 2) { + if (method.getName().equals(data[i])) { + return data[i + 1]; + } + } + throw new IllegalArgumentException(); + } +} diff --git a/classpath/avian/Pair.java b/classpath/avian/Pair.java new file mode 100644 index 0000000000..028aa17f9a --- /dev/null +++ b/classpath/avian/Pair.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2009, 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 avian; + +public class Pair { + public A first; + public B second; +} diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index fc0aea9b65..138f272b78 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -10,14 +10,20 @@ package java.lang; +import avian.AnnotationInvocationHandler; +import avian.Pair; + import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.AnnotatedElement; import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.IOException; @@ -28,8 +34,11 @@ import java.security.ProtectionDomain; import java.security.Permissions; import java.security.AllPermission; -public final class Class implements Type, GenericDeclaration { +public final class Class + implements Type, GenericDeclaration, AnnotatedElement +{ private static final int PrimitiveFlag = 1 << 5; + private static final int LinkFlag = 1 << 8; private short flags; private short vmFlags; @@ -44,6 +53,7 @@ public final class Class implements Type, GenericDeclaration { private Method[] virtualTable; private Field[] fieldTable; private Method[] methodTable; + private Addendum addendum; private Object staticTable; private ClassLoader loader; @@ -139,6 +149,157 @@ public final class Class implements Type, GenericDeclaration { } } + private static Class loadClass(ClassLoader loader, + byte[] nameBytes, int offset, int length) + { + String name = new String(nameBytes, offset, length, false); + try { + return loader.loadClass(name); + } catch (ClassNotFoundException e) { + NoClassDefFoundError error = new NoClassDefFoundError(name); + error.initCause(e); + throw error; + } + } + + private static Object resolveAnnotationValue(ClassLoader loader, + Object raw) + { + if (raw instanceof Pair) { + Pair p = (Pair) raw; + return Enum.valueOf + (loadClass(loader, p.first, 1, p.first.length - 3), + new String(p.second, 0, p.second.length - 1, false)); + } else if (raw instanceof byte[]) { + byte[] typeName = (byte[]) raw; + return loadClass(loader, typeName, 0, typeName.length - 1); + } else if (raw.getClass().isArray()) { + Object[] array = (Object[]) raw; + if (array.length > 0 && array[0] == null) { + resolveAnnotation(loader, array); + } else { + for (int i = 0; i < array.length; ++i) { + array[i] = resolveAnnotationValue(loader, array[i]); + } + } + } + + return raw; + } + + private static void resolveAnnotation(ClassLoader loader, + Object[] annotation) + { + byte[] typeName = (byte[]) annotation[1]; + annotation[1] = loadClass(loader, typeName, 1, typeName.length - 3); + + for (int i = 2; i < annotation.length; i += 2) { + byte[] name = (byte[]) annotation[i]; + annotation[i] = new String(name, 0, name.length - 1, false).intern(); + annotation[i + 1] = resolveAnnotationValue(loader, annotation[i + 1]); + } + } + + private static void resolveAnnotationTable(ClassLoader loader, + Object[] table) + { + for (int i = 0; i < table.length; ++i) { + resolveAnnotation(loader, (Object[]) table[i]); + } + } + + private static int resolveSpec(ClassLoader loader, byte[] spec, int start) { + int result; + int end; + switch (spec[start]) { + case 'L': + ++ start; + end = start; + while (spec[end] != ';') ++ end; + result = end + 1; + break; + + case '[': + end = start + 1; + while (spec[end] == '[') ++ end; + switch (spec[end]) { + case 'L': + ++ end; + while (spec[end] != ';') ++ end; + ++ end; + break; + + default: + ++ end; + } + result = end; + break; + + default: + return start + 1; + } + + loadClass(loader, spec, start, end - start); + + return result; + } + + private static native void acquireClassLock(); + + private static native void releaseClassLock(); + + void link(ClassLoader loader) { + acquireClassLock(); + try { + if ((vmFlags & LinkFlag) == 0) { + if (super_ != null) { + super_.link(loader); + } + + if (addendum != null && addendum.annotationTable != null) { + resolveAnnotationTable(loader, addendum.annotationTable); + } + + if (interfaceTable != null) { + int stride = (isInterface() ? 1 : 2); + for (int i = 0; i < interfaceTable.length; i += stride) { + ((Class) interfaceTable[i]).link(loader); + } + } + + if (methodTable != null) { + for (int i = 0; i < methodTable.length; ++i) { + Method m = methodTable[i]; + + for (int j = 1; j < m.spec.length;) { + j = resolveSpec(loader, m.spec, j); + } + + if (m.addendum != null && m.addendum.annotationTable != null) { + resolveAnnotationTable(loader, m.addendum.annotationTable); + } + } + } + + if (fieldTable != null) { + for (int i = 0; i < fieldTable.length; ++i) { + Field f = fieldTable[i]; + + resolveSpec(loader, f.spec, 0); + + if (f.addendum != null && f.addendum.annotationTable != null) { + resolveAnnotationTable(loader, f.addendum.annotationTable); + } + } + } + + vmFlags |= LinkFlag; + } + } finally { + releaseClassLock(); + } + } + public static Class forName(String name) throws ClassNotFoundException { return forName (name, true, Method.getCaller().getDeclaringClass().getClassLoader()); @@ -161,8 +322,6 @@ public final class Class implements Type, GenericDeclaration { private static native Class primitiveClass(char name); - private native void link(ClassLoader loader); - private native void initialize(); public static Class forCanonicalName(String name) { @@ -479,9 +638,10 @@ public final class Class implements Type, GenericDeclaration { if (interfaceTable != null) { link(loader); - Class[] array = new Class[interfaceTable.length / 2]; + int stride = (isInterface() ? 1 : 2); + Class[] array = new Class[interfaceTable.length / stride]; for (int i = 0; i < array.length; ++i) { - array[i] = (Class) interfaceTable[i * 2]; + array[i] = (Class) interfaceTable[i * stride]; } return array; } else { @@ -566,7 +726,7 @@ public final class Class implements Type, GenericDeclaration { } public Object[] getSigners() { - return Static.signers.get(this); + return addendum == null ? null : addendum.signers; } public Package getPackage() { @@ -584,8 +744,76 @@ public final class Class implements Type, GenericDeclaration { } } + public boolean isAnnotationPresent + (Class class_) + { + return getAnnotation(class_) != null; + } + + private Annotation getAnnotation(Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (loader, new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public T getAnnotation(Class class_) { + for (Class c = this; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + link(c.loader); + + Object[] table = c.addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) c.getAnnotation(a); + } + } + } + } + return null; + } + public Annotation[] getDeclaredAnnotations() { - throw new UnsupportedOperationException(); + if (addendum != null && addendum.annotationTable != null) { + link(loader); + + Object[] table = addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = getAnnotation((Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + private int countAnnotations() { + int count = 0; + for (Class c = this; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + count += c.addendum.annotationTable.length; + } + } + return count; + } + + public Annotation[] getAnnotations() { + Annotation[] array = new Annotation[countMethods(true)]; + int i = 0; + for (Class c = this; c != null; c = c.super_) { + if (c.addendum != null && c.addendum.annotationTable != null) { + Object[] table = c.addendum.annotationTable; + for (int j = 0; j < table.length; ++j) { + array[i++] = getAnnotation((Object[]) table[j]); + } + } + } + + return array; } public boolean isEnum() { @@ -612,10 +840,6 @@ public final class Class implements Type, GenericDeclaration { throw new UnsupportedOperationException(); } - public A getAnnotation(Class c) { - throw new UnsupportedOperationException(); - } - public ProtectionDomain getProtectionDomain() { Permissions p = new Permissions(); p.add(new AllPermission()); @@ -625,11 +849,15 @@ public final class Class implements Type, GenericDeclaration { // for GNU Classpath compatibility: void setSigners(Object[] signers) { if (signers != null && signers.length > 0) { - Static.signers.put(this, signers); + if (addendum == null) { + addendum = new Addendum(); + } + addendum.signers = signers; } } - private static class Static { - public static final Map signers = new HashMap(); + private static class Addendum { + public Object[] annotationTable; + public Object[] signers; } } diff --git a/classpath/java/lang/ClassLoader.java b/classpath/java/lang/ClassLoader.java index 9fe9228dea..46f9f67954 100644 --- a/classpath/java/lang/ClassLoader.java +++ b/classpath/java/lang/ClassLoader.java @@ -83,7 +83,9 @@ public abstract class ClassLoader { return c; } - protected native void resolveClass(Class c); + protected void resolveClass(Class c) { + c.link(this); + } private ClassLoader getParent() { return parent; diff --git a/classpath/java/lang/annotation/Retention.java b/classpath/java/lang/annotation/Retention.java new file mode 100644 index 0000000000..6b47da4f2d --- /dev/null +++ b/classpath/java/lang/annotation/Retention.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2009, 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.annotation; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Retention { + public RetentionPolicy value(); +} diff --git a/classpath/java/lang/annotation/RetentionPolicy.java b/classpath/java/lang/annotation/RetentionPolicy.java new file mode 100644 index 0000000000..badc611fb3 --- /dev/null +++ b/classpath/java/lang/annotation/RetentionPolicy.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, 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.annotation; + +public enum RetentionPolicy { + CLASS, RUNTIME, SOURCE +} diff --git a/classpath/java/lang/reflect/AccessibleObject.java b/classpath/java/lang/reflect/AccessibleObject.java index af659ae6d3..2836e7f63b 100644 --- a/classpath/java/lang/reflect/AccessibleObject.java +++ b/classpath/java/lang/reflect/AccessibleObject.java @@ -10,9 +10,17 @@ package java.lang.reflect; -public abstract class AccessibleObject { +import java.lang.annotation.Annotation; + +public abstract class AccessibleObject implements AnnotatedElement { protected static final int Accessible = 1 << 0; + public boolean isAnnotationPresent + (Class class_) + { + return getAnnotation(class_) != null; + } + public abstract boolean isAccessible(); public abstract void setAccessible(boolean v); diff --git a/classpath/java/lang/reflect/AnnotatedElement.java b/classpath/java/lang/reflect/AnnotatedElement.java new file mode 100644 index 0000000000..7bdf9e331b --- /dev/null +++ b/classpath/java/lang/reflect/AnnotatedElement.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2009, 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.lang.annotation.Annotation; + +public interface AnnotatedElement { + public boolean isAnnotationPresent(Class class_); + + public T getAnnotation(Class class_); + + public Annotation[] getAnnotations(); + + public Annotation[] getDeclaredAnnotations(); +} diff --git a/classpath/java/lang/reflect/Constructor.java b/classpath/java/lang/reflect/Constructor.java index 11df2f1fae..792cfdae2f 100644 --- a/classpath/java/lang/reflect/Constructor.java +++ b/classpath/java/lang/reflect/Constructor.java @@ -10,6 +10,8 @@ package java.lang.reflect; +import java.lang.annotation.Annotation; + public class Constructor extends AccessibleObject implements Member, GenericDeclaration { @@ -52,6 +54,18 @@ public class Constructor extends AccessibleObject return method.isSynthetic(); } + public T getAnnotation(Class class_) { + return method.getAnnotation(class_); + } + + public Annotation[] getAnnotations() { + return method.getAnnotations(); + } + + public Annotation[] getDeclaredAnnotations() { + return method.getDeclaredAnnotations(); + } + public TypeVariable>[] getTypeParameters() { throw new UnsupportedOperationException(); } diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 36cbe8482a..a8e70a28e1 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -10,6 +10,10 @@ package java.lang.reflect; +import avian.AnnotationInvocationHandler; + +import java.lang.annotation.Annotation; + public class Field extends AccessibleObject { private static final int VoidField = 0; private static final int ByteField = 1; @@ -27,7 +31,8 @@ public class Field extends AccessibleObject { private short flags; private short offset; private byte[] name; - private byte[] spec; + public byte[] spec; + public Addendum addendum; private Class class_; private Field() { } @@ -195,6 +200,45 @@ public class Field extends AccessibleObject { } } + private Annotation getAnnotation(Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (class_.getClassLoader(), new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public T getAnnotation(Class class_) { + if (addendum != null && addendum.annotationTable != null) { + Object[] table = addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) getAnnotation(a); + } + } + } + return null; + } + + public Annotation[] getAnnotations() { + if (addendum != null && addendum.annotationTable != null) { + Object[] table = addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = getAnnotation((Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + public boolean isEnumConstant() { throw new UnsupportedOperationException(); } @@ -210,4 +254,8 @@ public class Field extends AccessibleObject { private static native void setObject (Object instance, int offset, Object value); + + public static class Addendum { + public Object[] annotationTable; + } } diff --git a/classpath/java/lang/reflect/InvocationHandler.java b/classpath/java/lang/reflect/InvocationHandler.java new file mode 100644 index 0000000000..21932bae6e --- /dev/null +++ b/classpath/java/lang/reflect/InvocationHandler.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, 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 InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] arguments); +} diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index ec648042ac..d9413c4700 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -10,6 +10,10 @@ package java.lang.reflect; +import avian.AnnotationInvocationHandler; + +import java.lang.annotation.Annotation; + public class Method extends AccessibleObject implements Member, GenericDeclaration { @@ -21,7 +25,8 @@ public class Method extends AccessibleObject private short offset; private int nativeID; private byte[] name; - private byte[] spec; + public byte[] spec; + public Addendum addendum; private Class class_; private Object code; private long compiled; @@ -142,6 +147,45 @@ public class Method extends AccessibleObject throw new RuntimeException(); } + private Annotation getAnnotation(Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (class_.getClassLoader(), new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public T getAnnotation(Class class_) { + if (addendum != null && addendum.annotationTable != null) { + Object[] table = addendum.annotationTable; + for (int i = 0; i < table.length; ++i) { + Object[] a = (Object[]) table[i]; + if (a[1] == class_) { + return (T) getAnnotation(a); + } + } + } + return null; + } + + public Annotation[] getAnnotations() { + if (addendum != null && addendum.annotationTable != null) { + Object[] table = addendum.annotationTable; + Annotation[] array = new Annotation[table.length]; + for (int i = 0; i < table.length; ++i) { + array[i] = getAnnotation((Object[]) table[i]); + } + return array; + } else { + return new Annotation[0]; + } + } + + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + public boolean isSynthetic() { throw new UnsupportedOperationException(); } @@ -165,4 +209,8 @@ public class Method extends AccessibleObject public TypeVariable>[] getTypeParameters() { throw new UnsupportedOperationException(); } + + public static class Addendum { + public Object[] annotationTable; + } } diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java new file mode 100644 index 0000000000..0313eafe4b --- /dev/null +++ b/classpath/java/lang/reflect/Proxy.java @@ -0,0 +1,54 @@ +/* Copyright (c) 2009, 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 class Proxy { + private static int nextNumber; + + protected InvocationHandler h; + + public static Class getProxyClass(ClassLoader loader, + Class ... interfaces) + { + for (Class c: interfaces) { + if (! c.isInterface()) { + throw new IllegalArgumentException(); + } + } + + int number; + synchronized (Proxy.class) { + number = nextNumber++; + } + + return makeClass + (loader, interfaces, ("Proxy-" + number + "\0").getBytes()); + } + + private static native Class makeClass(ClassLoader loader, + Class[] interfaces, + byte[] name); + + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler handler) + { + try { + return Proxy.getProxyClass(loader, interfaces) + .getConstructor(new Class[] { InvocationHandler.class }) + .newInstance(new Object[] { handler }); + } catch (Exception e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } +} diff --git a/src/builtin.cpp b/src/builtin.cpp index 5a44a6a260..3d1cd8c283 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -79,6 +79,242 @@ runOnLoadIfFound(Thread* t, System::Library* library) } } +void JNICALL +proxyConstruct(Thread* t, object, uintptr_t* arguments) +{ + set(t, reinterpret_cast(arguments[0]), ProxyH, + reinterpret_cast(arguments[1])); +} + +int64_t JNICALL +proxyInvoke(Thread* t, object method, uintptr_t* arguments) +{ + PROTECT(t, method); + + unsigned size = methodParameterFootprint(t, method); + RUNTIME_ARRAY(bool, objectMask, size); + + assert(t, (methodFlags(t, method) & ACC_STATIC) == 0); + + unsigned i = 0; + RUNTIME_ARRAY_BODY(objectMask)[i++] = true; + + unsigned argumentCount = 0; + for (MethodSpecIterator it + (t, reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0))); + it.hasNext();) + { + ++ argumentCount; + + switch (*it.next()) { + case 'L': + case '[': + RUNTIME_ARRAY_BODY(objectMask)[i++] = true; + break; + + case 'J': + case 'D': + RUNTIME_ARRAY_BODY(objectMask)[i++] = false; + RUNTIME_ARRAY_BODY(objectMask)[i++] = false; + break; + + default: + RUNTIME_ARRAY_BODY(objectMask)[i++] = false; + break; + } + } + + class MyProtector: public Thread::Protector { + public: + MyProtector(Thread* t, uintptr_t* array, bool* mask, unsigned count): + Protector(t), array(array), mask(mask), count(count) { } + + virtual void visit(Heap::Visitor* v) { + for (unsigned i = 0; i < count; ++i) { + if (mask[i]) { + v->visit(reinterpret_cast(array + i)); + } + } + } + + uintptr_t* array; + bool* mask; + unsigned count; + } protector(t, arguments, RUNTIME_ARRAY_BODY(objectMask), i); + + object array = makeObjectArray + (t, t->m->loader, arrayBody(t, t->m->types, Machine::JobjectType), + argumentCount); + PROTECT(t, array); + + i = 0; + unsigned ai = 1; + for (MethodSpecIterator it + (t, reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0))); + it.hasNext();) + { + object a; + unsigned size; + switch (*it.next()) { + case 'L': + case '[': + a = reinterpret_cast(arguments[ai]); + size = 1; + break; + + case 'Z': + a = makeBoolean(t, static_cast(arguments[ai])); + size = 1; + break; + + case 'B': + a = makeByte(t, static_cast(arguments[ai])); + size = 1; + break; + + case 'S': + a = makeShort(t, static_cast(arguments[ai])); + size = 1; + break; + + case 'C': + a = makeChar(t, static_cast(arguments[ai])); + size = 1; + break; + + case 'I': + a = makeInt(t, static_cast(arguments[ai])); + size = 1; + break; + + case 'F': + a = makeFloat(t, bitsToFloat(static_cast(arguments[ai]))); + size = 1; + break; + + case 'J': { + int64_t v; memcpy(&v, arguments + ai, 8); + a = makeLong(t, v); + size = 2; + } break; + + case 'D': { + double v; memcpy(&v, arguments + ai, 8); + a = makeDouble(t, v); + size = 2; + } break; + + default: + abort(t); + } + + set(t, array, ArrayBody + (i * BytesPerWord), a); + + ++ i; + ai += size; + } + + if (t->m->invokeMethod == 0) { + object m = resolveMethod + (t, t->m->loader, "java/lang/reflect/InvocationHandler", "invoke", + "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)" + "Ljava/lang/Object;"); + + if (m) { + t->m->invokeMethod = m; + } + + if (UNLIKELY(t->exception)) return 0; + } + + object invoke = findInterfaceMethod + (t, t->m->invokeMethod, objectClass + (t, proxyH(t, reinterpret_cast(arguments[0])))); + PROTECT(t, invoke); + + object result = t->m->processor->invoke + (t, invoke, proxyH(t, reinterpret_cast(arguments[0])), + reinterpret_cast(arguments[0]), method, array); + if (UNLIKELY(t->exception)) return 0; + + switch (methodReturnCode(t, method)) { + case BooleanField: + return booleanValue(t, result); + + case ByteField: + return byteValue(t, result); + + case CharField: + return charValue(t, result); + + case ShortField: + return shortValue(t, result); + + case FloatField: + return floatToBits(floatValue(t, result)); + + case IntField: + return intValue(t, result); + + case LongField: + return longValue(t, result); + + case DoubleField: + return doubleToBits(doubleValue(t, result)); + + case ObjectField: + return reinterpret_cast(result); + + case VoidField: + return 0; + + default: + abort(t); + } +} + +void +addInterface(Thread* t, object map, object interface) +{ + hashMapInsertMaybe + (t, map, className(t, interface), interface, byteArrayHash, + byteArrayEqual); +} + +object +allInterfaces(Thread* t, object array) +{ + PROTECT(t, array); + + object map = makeHashMap(t, 0, 0); + PROTECT(t, map); + + for (unsigned i = 0; i < objectArrayLength(t, array); ++i) { + addInterface(t, map, objectArrayBody(t, array, i)); + + object itable = classInterfaceTable(t, objectArrayBody(t, array, i)); + if (itable) { + PROTECT(t, itable); + + for (unsigned j = 0; j < arrayLength(t, itable); ++j) { + addInterface(t, map, arrayBody(t, itable, j)); + } + } + } + + object result = makeArray(t, hashMapSize(t, map)); + + unsigned i = 0; + for (HashMapIterator it(t, map); it.hasMore();) { + set(t, result, ArrayBody + (i * BytesPerWord), tripleSecond(t, it.next())); + ++ i; + } + + return result; +} + } // namespace extern "C" JNIEXPORT int64_t JNICALL @@ -199,16 +435,6 @@ Avian_java_lang_ClassLoader_defineClass return reinterpret_cast(c); } -extern "C" JNIEXPORT void JNICALL -Avian_java_lang_ClassLoader_resolveClass -(Thread* t, object, uintptr_t* arguments) -{ - object loader = reinterpret_cast(arguments[0]); - object class_ = reinterpret_cast(arguments[1]); - - linkClass(t, loader, class_); -} - extern "C" JNIEXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findLoadedClass (Thread* t, object, uintptr_t* arguments) @@ -302,13 +528,17 @@ Avian_java_lang_Class_initialize } extern "C" JNIEXPORT void JNICALL -Avian_java_lang_Class_link -(Thread* t, object, uintptr_t* arguments) +Avian_java_lang_Class_acquireClassLock +(Thread* t, object) { - object this_ = reinterpret_cast(arguments[0]); - object loader = reinterpret_cast(arguments[1]); + acquire(t, t->m->classLock); +} - linkClass(t, loader, this_); +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Class_releaseClassLock +(Thread* t, object) +{ + release(t, t->m->classLock); } extern "C" JNIEXPORT int64_t JNICALL @@ -499,6 +729,195 @@ Avian_java_lang_reflect_Array_makeObjectArray (makeObjectArray(t, classLoader(t, elementType), elementType, length)); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Proxy_makeClass +(Thread* t, object, uintptr_t* arguments) +{ + object loader = reinterpret_cast(arguments[0]); + PROTECT(t, loader); + + object interfaces = allInterfaces(t, reinterpret_cast(arguments[1])); + PROTECT(t, interfaces); + + object name = reinterpret_cast(arguments[2]); + PROTECT(t, name); + + object virtualMap = makeHashMap(t, 0, 0); + PROTECT(t, virtualMap); + + object superVtable = classVirtualTable + (t, arrayBody(t, t->m->types, Machine::ProxyType)); + PROTECT(t, superVtable); + + for (unsigned i = 0; i < arrayLength(t, superVtable); ++i) { + object method = arrayBody(t, superVtable, i); + hashMapInsert(t, virtualMap, method, method, methodHash); + } + + unsigned virtualCount = arrayLength(t, superVtable); + + object newVirtuals = makeList(t, 0, 0, 0); + PROTECT(t, newVirtuals); + + object ivtable = 0; + PROTECT(t, ivtable); + + object method = 0; + PROTECT(t, method); + + for (unsigned i = 0; i < arrayLength(t, interfaces); ++i) { + ivtable = classVirtualTable(t, arrayBody(t, interfaces, i)); + + if (ivtable) { + for (unsigned j = 0; j < arrayLength(t, ivtable); ++j) { + method = arrayBody(t, ivtable, j); + + method = makeMethod + (t, + methodVmFlags(t, method) | FastNative, + methodReturnCode(t, method), + methodParameterCount(t, method), + methodParameterFootprint(t, method), + methodFlags(t, method) | ACC_NATIVE, + 0, + 0, + methodName(t, method), + methodSpec(t, method), + 0, + 0, + 0, + reinterpret_cast(proxyInvoke)); + + object p = hashMapFindNode + (t, virtualMap, method, methodHash, methodEqual); + + if (p) { + methodOffset(t, method) = methodOffset(t, tripleFirst(t, p)); + + set(t, p, TripleSecond, method); + } else { + methodOffset(t, method) = virtualCount++; + + listAppend(t, newVirtuals, method); + + hashMapInsert(t, virtualMap, method, method, methodHash); + } + } + } + } + + object vtable = makeArray(t, virtualCount); + PROTECT(t, vtable); + + unsigned i = 0; + for (; i < arrayLength(t, superVtable); ++i) { + method = hashMapFind + (t, virtualMap, arrayBody(t, superVtable, i), methodHash, methodEqual); + + set(t, vtable, ArrayBody + (i * BytesPerWord), method); + } + + object methodTable = makeArray(t, listSize(t, newVirtuals) + 1); + PROTECT(t, methodTable); + + unsigned mti = 0; + for (object p = listFront(t, newVirtuals); p; p = pairSecond(t, p)) { + set(t, vtable, ArrayBody + (i * BytesPerWord), pairFirst(t, p)); + ++ i; + + set(t, methodTable, ArrayBody + (mti * BytesPerWord), pairFirst(t, p)); + ++ mti; + } + +#define NAME "" + object constructorName = makeByteArray(t, sizeof(NAME)); + PROTECT(t, constructorName); + memcpy(&byteArrayBody(t, constructorName, 0), NAME, sizeof(NAME)); + +#define SPEC "(Ljava/lang/reflect/InvocationHandler;)V" + object constructorSpec = makeByteArray(t, sizeof(SPEC)); + PROTECT(t, constructorSpec); + memcpy(&byteArrayBody(t, constructorSpec, 0), SPEC, sizeof(SPEC)); + + object constructor = makeMethod + (t, + methodVmFlags(t, method) | FastNative, + VoidField, + 1, + 2, + methodFlags(t, method) | ACC_NATIVE, + 0, + 0, + constructorName, + constructorSpec, + 0, + 0, + 0, + reinterpret_cast(proxyConstruct)); + + set(t, methodTable, ArrayBody + (mti * BytesPerWord), constructor); + ++ mti; + + assert(t, arrayLength(t, vtable) == i); + assert(t, arrayLength(t, methodTable) == mti); + + object itable = makeArray(t, arrayLength(t, interfaces) * 2); + PROTECT(t, itable); + + object newIVTable = 0; + PROTECT(t, newIVTable); + + for (unsigned i = 0; i < arrayLength(t, interfaces); ++i) { + object interface = arrayBody(t, interfaces, i); + + set(t, itable, ArrayBody + ((i * 2) * BytesPerWord), interface); + + ivtable = classVirtualTable(t, interface); + if (ivtable) { + newIVTable = makeArray(t, arrayLength(t, ivtable)); + + set(t, itable, ArrayBody + (((i * 2) + 1) * BytesPerWord), newIVTable); + + for (unsigned j = 0; j < arrayLength(t, ivtable); ++j) { + method = hashMapFind + (t, virtualMap, arrayBody(t, ivtable, j), methodHash, methodEqual); + assert(t, method); + + set(t, newIVTable, ArrayBody + (j * BytesPerWord), method); + } + } + } + + object c = t->m->processor->makeClass + (t, + 0, + 0, + classFixedSize(t, arrayBody(t, t->m->types, Machine::ProxyType)), + 0, + 0, + classObjectMask(t, arrayBody(t, t->m->types, Machine::ProxyType)), + name, + 0, + arrayBody(t, t->m->types, Machine::ProxyType), + itable, + vtable, + 0, + methodTable, + 0, + 0, + loader, + arrayLength(t, vtable)); + PROTECT(t, c); + + t->m->processor->initVtable(t, c); + + for (unsigned i = 0; i < arrayLength(t, methodTable); ++i) { + set(t, arrayBody(t, methodTable, i), MethodClass, c); + } + + return reinterpret_cast(c); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_java_lang_Float_floatToRawIntBits (Thread*, object, uintptr_t* arguments) diff --git a/src/compile.cpp b/src/compile.cpp index ac87bfa8d8..165ff3b7a1 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -6331,7 +6331,7 @@ invoke(Thread* thread, object method, ArgumentList* arguments) default: abort(t); - }; + } return r; } @@ -6450,12 +6450,13 @@ class MyProcessor: public Processor { uint16_t offset, object name, object spec, + object addendum, object class_, object code) { return vm::makeMethod (t, vmFlags, returnCode, parameterCount, parameterFootprint, flags, - offset, 0, name, spec, class_, code, + offset, 0, name, spec, addendum, class_, code, local::defaultThunk(static_cast(t))); } @@ -6475,13 +6476,14 @@ class MyProcessor: public Processor { object fieldTable, object methodTable, object staticTable, + object addendum, object loader, unsigned vtableLength) { return vm::makeClass (t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions, objectMask, name, sourceFile, super, interfaceTable, virtualTable, - fieldTable, methodTable, staticTable, loader, vtableLength); + fieldTable, methodTable, staticTable, addendum, loader, vtableLength); } virtual void @@ -7656,6 +7658,7 @@ compile(MyThread* t, Allocator* allocator, BootContext* bootContext, methodNativeID(t, method), methodName(t, method), methodSpec(t, method), + methodAddendum(t, method), methodClass(t, method), methodCode(t, method), reinterpret_cast(compiled)); diff --git a/src/interpret.cpp b/src/interpret.cpp index 2491b27847..6715299504 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -3093,11 +3093,12 @@ class MyProcessor: public Processor { object name, object spec, object class_, + object addendum, object code) { return vm::makeMethod (t, vmFlags, returnCode, parameterCount, parameterFootprint, flags, - offset, 0, name, spec, class_, code, 0); + offset, 0, name, spec, addendum, class_, code, 0); } virtual object @@ -3116,13 +3117,14 @@ class MyProcessor: public Processor { object fieldTable, object methodTable, object staticTable, + object addendum, object loader, unsigned vtableLength UNUSED) { return vm::makeClass (t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions, objectMask, name, sourceFile, super, interfaceTable, virtualTable, - fieldTable, methodTable, staticTable, loader, 0); + fieldTable, methodTable, addendum, staticTable, loader, 0); } virtual void diff --git a/src/machine.cpp b/src/machine.cpp index a0ce673f92..48bfc6f8de 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -68,9 +68,8 @@ dispose(Thread* t, Thread* o, bool remove) expect(t, find(t->m->rootThread, o)); unsigned c = count(t->m->rootThread, o); - Thread** threads = static_cast - (allocate(t->m->system, c * sizeof(Thread*))); - fill(t->m->rootThread, o, threads); + RUNTIME_ARRAY(Thread*, threads, c); + fill(t->m->rootThread, o, RUNTIME_ARRAY_BODY(threads)); #endif if (o->parent) { @@ -115,7 +114,7 @@ dispose(Thread* t, Thread* o, bool remove) expect(t, not find(t->m->rootThread, o)); for (unsigned i = 0; i < c; ++i) { - expect(t, find(t->m->rootThread, threads[i])); + expect(t, find(t->m->rootThread, RUNTIME_ARRAY_BODY(threads)[i])); } #endif } @@ -604,51 +603,6 @@ makeByteArray(Thread* t, const char* format, va_list a) return s; } -unsigned -resolveSpec(Thread* t, object loader, object spec, unsigned offset) -{ - int8_t* s = &byteArrayBody(t, spec, offset); - unsigned result; - switch (*s) { - case 'L': - ++ offset; - while (*s and *s != ';') ++ s; - result = s + 1 - &byteArrayBody(t, spec, 0); - break; - - case '[': - while (*s == '[') ++ s; - switch (*s) { - case 'L': - while (*s and *s != ';') ++ s; - ++ s; - break; - - default: - ++ s; - break; - } - result = s - &byteArrayBody(t, spec, 0); - break; - - default: - return offset + 1; - } - - PROTECT(t, spec); - PROTECT(t, loader); - - unsigned length = s - &byteArrayBody(t, spec, offset); - - object name = makeByteArray(t, length + 1); - memcpy(&byteArrayBody(t, name, 0), - &byteArrayBody(t, spec, offset), - length); - resolveClass(t, loader, name); - - return result; -} - unsigned readByte(Stream& s, unsigned* value) { @@ -983,7 +937,7 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) object interfaceTable = 0; if (hashMapSize(t, map)) { - unsigned length = hashMapSize(t, map) ; + unsigned length = hashMapSize(t, map); if ((classFlags(t, class_) & ACC_INTERFACE) == 0) { length *= 2; } @@ -1016,6 +970,122 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) set(t, class_, ClassInterfaceTable, interfaceTable); } +object +parseAnnotation(Thread* t, Stream& s, object pool); + +object +parseAnnotationValue(Thread* t, Stream& s, object pool) +{ + unsigned tag = s.read1(); + switch (tag) { + case 'Z': + return makeBoolean(t, singletonValue(t, pool, s.read2() - 1)); + + case 'B': + return makeByte(t, singletonValue(t, pool, s.read2() - 1)); + + case 'C': + return makeChar(t, singletonValue(t, pool, s.read2() - 1)); + + case 'S': + return makeShort(t, singletonValue(t, pool, s.read2() - 1)); + + case 'I': + return makeInt(t, singletonValue(t, pool, s.read2() - 1)); + + case 'F': + return makeFloat(t, bitsToFloat(singletonValue(t, pool, s.read2() - 1))); + + case 'J': { + int64_t v; memcpy(&v, &singletonValue(t, pool, s.read2() - 1), 8); + return makeLong(t, v); + } + + case 'D': { + double v; memcpy(&v, &singletonValue(t, pool, s.read2() - 1), 8); + return makeDouble(t, v); + } + + case 's': { + object value = singletonObject(t, pool, s.read2() - 1); + return intern + (t, makeString(t, value, 0, byteArrayLength(t, value) - 1, 0)); + } + + case 'e': { + unsigned typeNameIndex = s.read2() - 1; + unsigned nameIndex = s.read2() - 1; + return makePair(t, singletonObject(t, pool, typeNameIndex), + singletonObject(t, pool, nameIndex)); + } + + case 'c': + return singletonObject(t, pool, s.read2() - 1); + + case '@': + return parseAnnotation(t, s, pool); + + case '[': { + unsigned count = s.read2(); + object array = makeObjectArray(t, count); + PROTECT(t, array); + for (unsigned i = 0; i < count; ++i) { + object value = parseAnnotationValue(t, s, pool); + if (UNLIKELY(t->exception)) return 0; + + set(t, array, ArrayBody + (i * BytesPerWord), value); + } + return array; + } + + default: abort(t); + } +} + +object +parseAnnotation(Thread* t, Stream& s, object pool) +{ + PROTECT(t, pool); + + unsigned typeIndex = s.read2() - 1; + unsigned elementCount = s.read2(); + object array = makeObjectArray(t, (elementCount * 2) + 2); + PROTECT(t, array); + + set(t, array, ArrayBody + BytesPerWord, singletonObject(t, pool, typeIndex)); + + for (unsigned i = 0; i < elementCount; ++i) { + set(t, array, ArrayBody + (((i * 2) + 2) * BytesPerWord), + singletonObject(t, pool, s.read2() - 1)); + + object value = parseAnnotationValue(t, s, pool); + if (UNLIKELY(t->exception)) return 0; + + set(t, array, ArrayBody + (((i * 2) + 3) * BytesPerWord), value); + } + + return array; +} + +object +parseAnnotationTable(Thread* t, Stream& s, object pool) +{ + PROTECT(t, pool); + + unsigned annotationCount = s.read2(); + object annotations = makeArray(t, annotationCount); + PROTECT(t, annotations); + + for (unsigned i = 0; i < annotationCount; ++i) { + object annotation = parseAnnotation(t, s, pool); + if (UNLIKELY(t->exception)) return 0; + + set(t, annotations, ArrayBody + (i * BytesPerWord), annotation); + } + + return annotations; +} + void parseFieldTable(Thread* t, Stream& s, object class_, object pool) { @@ -1038,6 +1108,9 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) object staticValueTable = makeIntArray(t, count); PROTECT(t, staticValueTable); + object addendum = 0; + PROTECT(t, addendum); + RUNTIME_ARRAY(uint8_t, staticTypes, count); for (unsigned i = 0; i < count; ++i) { @@ -1059,6 +1132,11 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { value = s.read2(); + } else if (vm::strcmp(reinterpret_cast + ("RuntimeVisibleAnnotations"), + &byteArrayBody(t, name, 0)) == 0) + { + addendum = makeFieldAddendum(t, parseAnnotationTable(t, s, pool)); } else { s.skip(length); } @@ -1072,6 +1150,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) 0, // offset singletonObject(t, pool, name - 1), singletonObject(t, pool, spec - 1), + addendum, class_); if (flags & ACC_STATIC) { @@ -1318,6 +1397,7 @@ addInterfaceMethods(Thread* t, object class_, object virtualMap, 0, methodName(t, method), methodSpec(t, method), + 0, class_, 0, 0); @@ -1380,12 +1460,19 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) object methodTable = makeArray(t, count); PROTECT(t, methodTable); + object addendum = 0; + PROTECT(t, addendum); + + object code = 0; + PROTECT(t, code); + for (unsigned i = 0; i < count; ++i) { unsigned flags = s.read2(); unsigned name = s.read2(); unsigned spec = s.read2(); - object code = 0; + code = 0; + unsigned attributeCount = s.read2(); for (unsigned j = 0; j < attributeCount; ++j) { object name = singletonObject(t, pool, s.read2() - 1); @@ -1395,6 +1482,11 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { code = parseCode(t, s, pool); + } else if (vm::strcmp(reinterpret_cast + ("RuntimeVisibleAnnotations"), + &byteArrayBody(t, name, 0)) == 0) + { + addendum = makeMethodAddendum(t, parseAnnotationTable(t, s, pool)); } else { s.skip(length); } @@ -1417,6 +1509,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) 0, // offset singletonObject(t, pool, name - 1), singletonObject(t, pool, spec - 1), + addendum, class_, code); @@ -1493,7 +1586,8 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) if (classInterfaceTable(t, classSuper(t, class_)) and arrayLength(t, classInterfaceTable(t, class_)) - == arrayLength(t, classInterfaceTable(t, classSuper(t, class_)))) + == arrayLength + (t, classInterfaceTable(t, classSuper(t, class_)))) { // inherit interface table from superclass set(t, class_, ClassInterfaceTable, @@ -1541,6 +1635,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) } if (abstractVirtuals) { + PROTECT(t, vtable); PROTECT(t, abstractVirtuals); unsigned oldLength = arrayLength(t, classMethodTable(t, class_)); @@ -1592,7 +1687,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) (t, virtualMap, method, methodHash, methodEqual); assert(t, method); - set(t, vtable, ArrayBody + (j * BytesPerWord), method); + set(t, vtable, ArrayBody + (j * BytesPerWord), method); } } } @@ -1612,6 +1707,14 @@ parseAttributeTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { set(t, class_, ClassSourceFile, singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast + ("RuntimeVisibleAnnotations"), + &byteArrayBody(t, name, 0)) == 0) + { + object addendum = makeClassAddendum + (t, parseAnnotationTable(t, s, pool), 0); + + set(t, class_, ClassAddendum, addendum); } else { s.skip(length); } @@ -1702,6 +1805,7 @@ makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec, vtable, 0, 0, + 0, elementClass, loader, arrayLength(t, vtable)); @@ -1832,7 +1936,7 @@ bootClass(Thread* t, Machine::Type type, int superType, uint32_t objectMask, object class_ = t->m->processor->makeClass (t, 0, BootstrapFlag, fixedSize, arrayElementSize, 0, mask, 0, 0, super, 0, - 0, 0, 0, 0, t->m->loader, vtableLength); + 0, 0, 0, 0, 0, t->m->loader, vtableLength); set(t, t->m->types, ArrayBody + (type * BytesPerWord), class_); } @@ -1945,7 +2049,7 @@ boot(Thread* t) { object bootCode = makeCode(t, 0, 0, 0, 0, 0, 1); codeBody(t, bootCode, 0) = impdep1; object bootMethod = makeMethod - (t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode, 0); + (t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode, 0); PROTECT(t, bootMethod); #include "type-java-initializations.cpp" @@ -2084,6 +2188,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, tenuredWeakReferences(0), shutdownHooks(0), objectsToFinalize(0), + invokeMethod(0), unsafe(false), triedBuiltinOnLoad(false), heapPoolIndex(0) @@ -2824,12 +2929,13 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size) 0, // object mask referenceName (t, singletonObject(t, pool, name - 1)), - 0, + 0, // source file 0, // super 0, // interfaces 0, // vtable 0, // fields 0, // methods + 0, // addendum 0, // static table loader, 0);// vtable length @@ -2878,6 +2984,7 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size) classVirtualTable(t, class_), classFieldTable(t, class_), classMethodTable(t, class_), + classAddendum(t, class_), classStaticTable(t, class_), classLoader(t, class_), vtableLength); @@ -3032,60 +3139,6 @@ resolveClass(Thread* t, object loader, object spec) } } -void -linkClass(Thread* t, object loader, object class_) -{ - PROTECT(t, loader); - PROTECT(t, class_); - - ACQUIRE(t, t->m->classLock); - - if ((classVmFlags(t, class_) & LinkFlag) == 0) { - if (classSuper(t, class_)) { - linkClass(t, loader, classSuper(t, class_)); - } - - if (classInterfaceTable(t, class_)) { - unsigned increment = 2; - if (classFlags(t, class_) & ACC_INTERFACE) { - increment = 1; - } - - for (unsigned i = 0; i < arrayLength(t, classInterfaceTable(t, class_)); - i += increment) - { - linkClass(t, loader, arrayBody(t, classInterfaceTable(t, class_), i)); - } - } - - if (classMethodTable(t, class_)) { - for (unsigned i = 0; - i < arrayLength(t, classMethodTable(t, class_)); ++i) - { - object spec = methodSpec - (t, arrayBody(t, classMethodTable(t, class_), i)); - PROTECT(t, spec); - - for (unsigned j = 1; j < byteArrayLength(t, spec);) { - j = resolveSpec(t, loader, spec, j); - if (UNLIKELY(t->exception)) return; - } - } - } - - if (classFieldTable(t, class_)) { - for (unsigned i = 0; i < arrayLength(t, classFieldTable(t, class_)); ++i) - { - resolveSpec(t, loader, fieldSpec - (t, arrayBody(t, classFieldTable(t, class_), i)), 0); - if (UNLIKELY(t->exception)) return; - } - } - - classVmFlags(t, class_) |= LinkFlag; - } -} - object resolveMethod(Thread* t, object class_, const char* methodName, const char* methodSpec) @@ -3573,6 +3626,7 @@ visitRoots(Machine* m, Heap::Visitor* v) v->visit(&(m->jniMethodTable)); v->visit(&(m->shutdownHooks)); v->visit(&(m->objectsToFinalize)); + v->visit(&(m->invokeMethod)); for (Thread* t = m->rootThread; t; t = t->peer) { ::visitRoots(t, v); diff --git a/src/machine.h b/src/machine.h index 60fecc3864..ebdee216a7 100644 --- a/src/machine.h +++ b/src/machine.h @@ -1204,6 +1204,7 @@ class Machine { object tenuredWeakReferences; object shutdownHooks; object objectsToFinalize; + object invokeMethod; bool unsafe; bool triedBuiltinOnLoad; JavaVMVTable javaVMVTable; @@ -1846,6 +1847,12 @@ makeExceptionInInitializerError(Thread* t, object cause) return makeExceptionInInitializerError(t, 0, trace, cause, cause); } +inline object +makeIncompatibleClassChangeError(Thread* t) +{ + return makeIncompatibleClassChangeError(t, 0, makeTrace(t), 0); +} + inline object makeNew(Thread* t, object class_) { @@ -2194,6 +2201,13 @@ initClass(Thread* t, object c); object makeObjectArray(Thread* t, object loader, object elementClass, unsigned count); +inline object +makeObjectArray(Thread* t, unsigned count) +{ + return makeObjectArray + (t, t->m->loader, arrayBody(t, t->m->types, Machine::JobjectType), count); +} + object findInTable(Thread* t, object table, object name, object spec, object& (*getName)(Thread*, object), @@ -2232,6 +2246,22 @@ findVirtualMethod(Thread* t, object method, object class_) methodOffset(t, method)); } +inline object +findInterfaceMethod(Thread* t, object method, object class_) +{ + assert(t, (classVmFlags(t, class_) & BootstrapFlag) == 0); + + object interface = methodClass(t, method); + object itable = classInterfaceTable(t, class_); + for (unsigned i = 0; i < arrayLength(t, itable); i += 2) { + if (arrayBody(t, itable, i) == interface) { + return arrayBody(t, arrayBody(t, itable, i + 1), + methodOffset(t, method)); + } + } + abort(t); +} + inline unsigned objectArrayLength(Thread* t UNUSED, object array) { @@ -2248,7 +2278,7 @@ objectArrayBody(Thread* t UNUSED, object array, unsigned index) assert(t, classObjectMask(t, objectClass(t, array)) == classObjectMask(t, arrayBody (t, t->m->types, Machine::ArrayType))); - return cast(array, (2 + index) * BytesPerWord); + return cast(array, ArrayBody + (index * BytesPerWord)); } unsigned diff --git a/src/process.h b/src/process.h index 97e6e88450..95da97cde8 100644 --- a/src/process.h +++ b/src/process.h @@ -59,22 +59,6 @@ isSpecialMethod(Thread* t, object method, object class_) void* resolveNativeMethod(Thread* t, object method); -inline object -findInterfaceMethod(Thread* t, object method, object class_) -{ - assert(t, (classVmFlags(t, class_) & BootstrapFlag) == 0); - - object interface = methodClass(t, method); - object itable = classInterfaceTable(t, class_); - for (unsigned i = 0; i < arrayLength(t, itable); i += 2) { - if (arrayBody(t, itable, i) == interface) { - return arrayBody(t, arrayBody(t, itable, i + 1), - methodOffset(t, method)); - } - } - abort(t); -} - inline void populateMultiArray(Thread* t, object array, int32_t* counts, unsigned index, unsigned dimensions) diff --git a/src/processor.h b/src/processor.h index 187994f3b2..2d68b63d2d 100644 --- a/src/processor.h +++ b/src/processor.h @@ -54,6 +54,7 @@ class Processor { uint16_t offset, object name, object spec, + object addendum, object class_, object code) = 0; @@ -72,6 +73,7 @@ class Processor { object virtualTable, object fieldTable, object methodTable, + object addendum, object staticTable, object loader, unsigned vtableLength) = 0; diff --git a/src/types.def b/src/types.def index 1bf786e8f3..1050486aa8 100644 --- a/src/types.def +++ b/src/types.def @@ -13,10 +13,20 @@ (type accessibleObject java/lang/reflect/AccessibleObject) +(type classAddendum java/lang/Class$Addendum) + (type field java/lang/reflect/Field) +(type fieldAddendum java/lang/reflect/Field$Addendum) + (type method java/lang/reflect/Method) +(type methodAddendum java/lang/reflect/Method$Addendum) + +(type proxy java/lang/reflect/Proxy) + +(type pair avian/Pair) + (type nativeMethodData (void* function) (uint16_t argumentTableSize) @@ -54,10 +64,6 @@ (object name) (object spec)) -(type pair - (object first) - (object second)) - (type triple (object first) (object second) diff --git a/test/Annotations.java b/test/Annotations.java new file mode 100644 index 0000000000..17929065d1 --- /dev/null +++ b/test/Annotations.java @@ -0,0 +1,49 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; + +public class Annotations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + Method m = Annotations.class.getMethod("foo"); + + expect(m.isAnnotationPresent(Test.class)); + + expect(((Test) m.getAnnotation(Test.class)).value().equals("couscous")); + + expect(((TestEnum) m.getAnnotation(TestEnum.class)).value() + .equals(Color.Red)); + + expect(((TestInteger) m.getAnnotation(TestInteger.class)).value() == 42); + } + + @Test("couscous") + @TestEnum(Color.Red) + @TestInteger(42) + public static void foo() { + + } + + @Retention(RetentionPolicy.RUNTIME) + private @interface Test { + public String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + private @interface TestEnum { + public Color value(); + } + + @Retention(RetentionPolicy.RUNTIME) + private @interface TestInteger { + public int value(); + } + + private static enum Color { + Red, Yellow, Blue + } + +} diff --git a/test/Proxies.java b/test/Proxies.java new file mode 100644 index 0000000000..e52a0e7d68 --- /dev/null +++ b/test/Proxies.java @@ -0,0 +1,42 @@ +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class Proxies { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) { + Foo foo = (Foo) Proxy.newProxyInstance + (Proxies.class.getClassLoader(), new Class[] { Foo.class }, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] arguments) + { + if (method.getName().equals("bar")) { + return "bam"; + } else if (method.getName().equals("baz")) { + return ((Integer) arguments[0]) + 1; + } else if (method.getName().equals("bim")) { + return ((Long) arguments[0]) - 1L; + } else if (method.getName().equals("boom")) { + return ((String) arguments[0]).substring(1); + } else { + throw new IllegalArgumentException(); + } + } + }); + + expect(foo.bar().equals("bam")); + expect(foo.baz(42) == 43); + expect(foo.bim(42L) == 41L); + expect(foo.boom("hello").equals("ello")); + } + + private interface Foo { + public String bar(); + public int baz(int v); + public long bim(long v); + public String boom(String s); + } +}