mirror of
https://github.com/corda/corda.git
synced 2025-02-09 12:21:22 +00:00
Merge pull request #102 from dscho/proxy-annotations
This looks good to me, good work.
This commit is contained in:
commit
a2e1e1eec9
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
2
makefile
2
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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Test {
|
||||
public String value();
|
||||
public String value() default "Hello, world!";
|
||||
}
|
||||
|
14
test/avian/testing/annotations/TestComplex.java
Normal file
14
test/avian/testing/annotations/TestComplex.java
Normal 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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user