Merge pull request #102 from dscho/proxy-annotations

This looks good to me, good work.
This commit is contained in:
Mike Jensen 2013-12-02 09:13:01 -08:00
commit a2e1e1eec9
8 changed files with 138 additions and 37 deletions

View File

@ -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();
}
}

View File

@ -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();

View File

@ -142,4 +142,9 @@ public class Method<T> extends AccessibleObject implements Member {
public boolean isVarArgs() {
return (getModifiers() & 0x80) != 0;
}
public Object getDefaultValue() {
ClassLoader loader = getDeclaringClass().getClassLoader();
return Classes.getAnnotationDefaultValue(loader, vmMethod.addendum);
}
}

View File

@ -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<Class, Method[]> methodRefsMap =
new HashMap<Class, Method[]>();
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",
"<init>", "()V") + 1);
write1(out, aload_0);
write1(out, aload_1);
@ -353,47 +367,48 @@ public class Proxy {
(pool, interfaces[i].getName().replace('.', '/'));
}
Map<String,avian.VMMethod> virtualMap = new HashMap();
Set<String> specs = new HashSet<String>();
List<MethodData> methodTable = new ArrayList<MethodData>();
List<Method> refs = new ArrayList<Method>();
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, "<init>"),
ConstantPool.addUtf8
(pool, "(Ljava/lang/reflect/InvocationHandler;)V"),
makeConstructorCode(pool));
}
methodTable.add(new MethodData
(Modifier.PUBLIC,
ConstantPool.addUtf8(pool, "<init>"),
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,

View File

@ -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

View File

@ -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);
}
}

View File

@ -5,5 +5,5 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
public String value();
public String value() default "Hello, world!";
}

View File

@ -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();
}