[Jython-checkins] jython (merge default -> default): Initial implementation of customizable proxymaker with annotation support and
frank.wierzbicki
jython-checkins at python.org
Sat Dec 22 20:52:11 CET 2012
http://hg.python.org/jython/rev/f8224fee2886
changeset: 6911:f8224fee2886
parent: 6904:9968367fa217
parent: 6910:a310960970b3
user: Darjus Loktevic <darjus at gmail.com>
date: Sat Dec 22 11:51:49 2012 -0800
summary:
Initial implementation of customizable proxymaker with annotation support and
reference implementation in tests.
files:
src/org/python/compiler/ClassFile.java | 89 ++
src/org/python/compiler/Code.java | 2 +-
src/org/python/compiler/JavaMaker.java | 7 +-
src/org/python/compiler/ProxyCodeHelpers.java | 338 +++++++
src/org/python/compiler/ProxyMaker.java | 426 +++++-----
src/org/python/core/MakeProxies.java | 45 +-
src/org/python/util/ProxyCompiler.java | 32 +
tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java | 36 +
tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java | 33 +
tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java | 30 +
tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java | 18 +
tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java | 77 +
tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java | 182 ++++
tests/python/custom_proxymaker/annotated_class.py | 18 +
tests/python/custom_proxymaker/clamp.py | 5 +
tests/python/custom_proxymaker/constructor_signatures.py | 32 +
tests/python/custom_proxymaker/junit_test.py | 28 +
tests/python/custom_proxymaker/method_signatures.py | 41 +
18 files changed, 1207 insertions(+), 232 deletions(-)
diff --git a/src/org/python/compiler/ClassFile.java b/src/org/python/compiler/ClassFile.java
--- a/src/org/python/compiler/ClassFile.java
+++ b/src/org/python/compiler/ClassFile.java
@@ -7,14 +7,18 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
import org.python.core.imp;
+import org.python.compiler.ProxyCodeHelpers.AnnotationDescr;
public class ClassFile
{
@@ -27,6 +31,7 @@
String[] interfaces;
List<MethodVisitor> methodVisitors;
List<FieldVisitor> fieldVisitors;
+ List<AnnotationVisitor> annotationVisitors;
public static String fixName(String n) {
if (n.indexOf('.') == -1)
@@ -37,6 +42,34 @@
}
return new String(c);
}
+
+
+ public static void visitAnnotations(AnnotationVisitor av, Map<String, Object> fields) {
+ for (Entry<String, Object>field: fields.entrySet()) {
+ visitAnnotation(av, field.getKey(), field.getValue());
+ }
+ }
+
+ // See org.objectweb.asm.AnnotationVisitor for details
+ // TODO Support annotation annotations and annotation array annotations
+ public static void visitAnnotation(AnnotationVisitor av, String fieldName, Object fieldValue) {
+ Class<?> fieldValueClass = fieldValue.getClass();
+
+ if (fieldValue instanceof Class) {
+ av.visit(fieldName, Type.getType((Class<?>)fieldValue));
+ } else if (fieldValueClass.isEnum()) {
+ av.visitEnum(fieldName, ProxyCodeHelpers.mapType(fieldValueClass), fieldValue.toString());
+ } else if (fieldValue instanceof List) {
+ AnnotationVisitor arrayVisitor = av.visitArray(fieldName);
+ List<Object> fieldList = (List<Object>)fieldValue;
+ for (Object arrayField: fieldList) {
+ visitAnnotation(arrayVisitor, null, arrayField);
+ }
+ arrayVisitor.visitEnd();
+ } else {
+ av.visit(fieldName, fieldValue);
+ }
+ }
public ClassFile(String name) {
this(name, "java/lang/Object", Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_PUBLIC,
@@ -56,6 +89,7 @@
cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
methodVisitors = Collections.synchronizedList(new ArrayList<MethodVisitor>());
fieldVisitors = Collections.synchronizedList(new ArrayList<FieldVisitor>());
+ annotationVisitors = Collections.synchronizedList(new ArrayList<AnnotationVisitor>());
}
public void setSource(String name) {
@@ -77,7 +111,54 @@
methodVisitors.add(pmv);
return pmv;
}
+ public Code addMethod(String name, String type, int access, String[] exceptions)
+ throws IOException
+ {
+ MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions);
+ Code pmv = new Code(mv, type, access);
+ methodVisitors.add(pmv);
+ return pmv;
+ }
+
+ public Code addMethod(String name, String type, int access, String[] exceptions,
+ AnnotationDescr[]methodAnnotationDescrs, AnnotationDescr[][] parameterAnnotationDescrs)
+ throws IOException
+ {
+ MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions);
+ // method annotations
+ for (AnnotationDescr ad: methodAnnotationDescrs) {
+ AnnotationVisitor av = mv.visitAnnotation(ad.getName(), true);
+ if (ad.hasFields()) {
+ visitAnnotations(av, ad.getFields());
+ }
+ av.visitEnd();
+ }
+
+ // parameter annotations
+ for (int i = 0; i < parameterAnnotationDescrs.length; i++) {
+ for (AnnotationDescr ad: parameterAnnotationDescrs[i]) {
+ AnnotationVisitor av = mv.visitParameterAnnotation(i, ad.getName(), true);
+ if (ad.hasFields()) {
+ visitAnnotations(av, ad.getFields());
+ }
+ av.visitEnd();
+ }
+ }
+
+ Code pmv = new Code(mv, type, access);
+ methodVisitors.add(pmv);
+ return pmv;
+ }
+
+ public void addClassAnnotation(AnnotationDescr annotationDescr) {
+ AnnotationVisitor av = cw.visitAnnotation(annotationDescr.getName(), true);
+ if (annotationDescr.hasFields()) {
+ visitAnnotations(av, annotationDescr.getFields());
+ }
+ annotationVisitors.add(av);
+ }
+
public void addField(String name, String type, int access)
throws IOException
{
@@ -103,6 +184,12 @@
}
}
+ public void endClassAnnotations() {
+ for (AnnotationVisitor av: annotationVisitors) {
+ av.visitEnd();
+ }
+ }
+
public void write(OutputStream stream) throws IOException {
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, this.name, null, this.superclass, interfaces);
AnnotationVisitor av = cw.visitAnnotation("Lorg/python/compiler/APIVersion;", true);
@@ -118,6 +205,7 @@
if (sfilename != null) {
cw.visitSource(sfilename, null);
}
+ endClassAnnotations();
endFields();
endMethods();
@@ -129,4 +217,5 @@
//debug(baos);
baos.close();
}
+
}
diff --git a/src/org/python/compiler/Code.java b/src/org/python/compiler/Code.java
--- a/src/org/python/compiler/Code.java
+++ b/src/org/python/compiler/Code.java
@@ -10,7 +10,7 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-class Code extends MethodVisitor implements Opcodes {
+public class Code extends MethodVisitor implements Opcodes {
MethodVisitor mv;
String sig;
String locals[];
diff --git a/src/org/python/compiler/JavaMaker.java b/src/org/python/compiler/JavaMaker.java
--- a/src/org/python/compiler/JavaMaker.java
+++ b/src/org/python/compiler/JavaMaker.java
@@ -32,11 +32,8 @@
int access) throws Exception {
/* Need a fancy constructor for the Java side of things */
Code code = classfile.addMethod("<init>", sig, access);
- callSuper(code, "<init>", name, parameters, null, sig);
- code.visitVarInsn(ALOAD, 0);
- getArgs(code, parameters);
- code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr));
- code.visitInsn(RETURN);
+ callSuper(code, "<init>", name, parameters, Void.TYPE, false);
+ callInitProxy(parameters, code);
}
@Override
diff --git a/src/org/python/compiler/ProxyCodeHelpers.java b/src/org/python/compiler/ProxyCodeHelpers.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/compiler/ProxyCodeHelpers.java
@@ -0,0 +1,338 @@
+package org.python.compiler;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.objectweb.asm.Type;
+import org.python.core.Py;
+import org.python.core.PyMethod;
+import org.python.core.PyObject;
+import org.python.core.PyProxy;
+import org.python.core.PyReflectedFunction;
+import org.python.util.Generic;
+
+/*
+ * Various constants and methods for generating Proxy code
+ */
+public class ProxyCodeHelpers {
+ public static final int tBoolean=0;
+ public static final int tByte=1;
+ public static final int tShort=2;
+ public static final int tInteger=3;
+ public static final int tLong=4;
+ public static final int tFloat=5;
+ public static final int tDouble=6;
+ public static final int tCharacter=7;
+ public static final int tVoid=8;
+ public static final int tOther=9;
+ public static final int tNone=10;
+
+ public static Map<Class<?>, Integer> types = fillTypes();
+
+ public static Map<Class<?>, Integer> fillTypes() {
+ Map<Class<?>, Integer> typeMap = Generic.map();
+ typeMap.put(Boolean.TYPE, tBoolean);
+ typeMap.put(Byte.TYPE, tByte);
+ typeMap.put(Short.TYPE, tShort);
+ typeMap.put(Integer.TYPE, tInteger);
+ typeMap.put(Long.TYPE, tLong);
+ typeMap.put(Float.TYPE, tFloat);
+ typeMap.put(Double.TYPE, tDouble);
+ typeMap.put(Character.TYPE, tCharacter);
+ typeMap.put(Void.TYPE, tVoid);
+ return typeMap;
+ }
+
+ public static int getType(Class<?> c) {
+ if (c == null) {
+ return tNone;
+ }
+ Object i = types.get(c);
+ if (i == null) {
+ return tOther;
+ } else {
+ return ((Integer)i);
+ }
+ }
+
+ /**
+ * Retrieves <code>name</code> from the PyObject in <code>proxy</code> if it's defined in
+ * Python. This is a specialized helper function for internal PyProxy use.
+ */
+ public static PyObject findPython(PyProxy proxy, String name) {
+ PyObject o = proxy._getPyInstance();
+ if (o == null) {
+ proxy.__initProxy__(new Object[0]);
+ o = proxy._getPyInstance();
+ }
+ PyObject ret = o.__findattr__(name);
+ if (ret instanceof PyMethod) {
+ PyMethod meth = ((PyMethod)ret);
+ if (meth.__func__ instanceof PyReflectedFunction) {
+ PyReflectedFunction func = (PyReflectedFunction)meth.__func__;
+ if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) {
+ // This function is the default return for the proxy type if the Python instance
+ // hasn't returned something of its own from __findattr__, so do the standard
+ // Java call on this
+ return null;
+ }
+ }
+ }
+ Py.setSystemState(proxy._getPySystemState());
+ return ret;
+ }
+
+ public static String mapClass(Class<?> c) {
+ String name = c.getName();
+ int index = name.indexOf(".");
+ if (index == -1) {
+ return name;
+ }
+ StringBuffer buf = new StringBuffer(name.length());
+ int last_index = 0;
+ while (index != -1) {
+ buf.append(name.substring(last_index, index));
+ buf.append("/");
+ last_index = index+1;
+ index = name.indexOf(".", last_index);
+ }
+ buf.append(name.substring(last_index, name.length()));
+ return buf.toString();
+ }
+
+ public static String mapType(Class<?> type) {
+ if (type.isArray())
+ return "["+mapType(type.getComponentType());
+
+ switch (getType(type)) {
+ case tByte: return "B";
+ case tCharacter: return "C";
+ case tDouble: return "D";
+ case tFloat: return "F";
+ case tInteger: return "I";
+ case tLong: return "J";
+ case tShort: return "S";
+ case tBoolean: return "Z";
+ case tVoid: return "V";
+ default:
+ return "L"+mapClass(type)+";";
+ }
+ }
+
+ public static String makeSig(Class<?> ret, Class<?>... sig) {
+ String[] mapped = new String[sig.length];
+ for (int i = 0; i < mapped.length; i++) {
+ mapped[i] = mapType(sig[i]);
+ }
+ return makeSig(mapType(ret), mapped);
+ }
+
+ public static String makeSig(String returnType, String... parameterTypes) {
+ StringBuilder buf = new StringBuilder("(");
+ for (String param : parameterTypes) {
+ buf.append(param);
+ }
+ return buf.append(')').append(returnType).toString();
+ }
+
+ public static void doReturn(Code code, Class<?> type) throws Exception {
+ switch (getType(type)) {
+ case tNone:
+ break;
+ case tCharacter:
+ case tBoolean:
+ case tByte:
+ case tShort:
+ case tInteger:
+ code.ireturn();
+ break;
+ case tLong:
+ code.lreturn();
+ break;
+ case tFloat:
+ code.freturn();
+ break;
+ case tDouble:
+ code.dreturn();
+ break;
+ case tVoid:
+ code.return_();
+ break;
+ default:
+ code.areturn();
+ break;
+ }
+ }
+
+ public static void doNullReturn(Code code, Class<?> type) throws Exception {
+ switch (getType(type)) {
+ case tNone:
+ break;
+ case tCharacter:
+ case tBoolean:
+ case tByte:
+ case tShort:
+ case tInteger:
+ code.iconst_0();
+ code.ireturn();
+ break;
+ case tLong:
+ code.lconst_0();
+ code.lreturn();
+ break;
+ case tFloat:
+ code.fconst_0();
+ code.freturn();
+ break;
+ case tDouble:
+ code.dconst_0();
+ code.dreturn();
+ break;
+ case tVoid:
+ code.return_();
+ break;
+ default:
+ code.aconst_null();
+ code.areturn();
+ break;
+ }
+ }
+
+ public static String[] mapClasses(Class<?>[] classes) {
+ String[] mapped = new String[classes.length];
+ for (int i = 0; i < mapped.length; i++) {
+ mapped[i] = mapClass(classes[i]);
+ }
+ return mapped;
+ }
+
+ public static String[] mapExceptions(Class<?>[] classes) {
+ String[] exceptionTypes = new String[classes.length];
+ for (int i = 0; i < classes.length; i++) {
+ // Exceptions are represented by their internal names
+ exceptionTypes[i] = Type.getType(classes[i]).getInternalName();
+ }
+ return exceptionTypes;
+ }
+
+ public static class MethodDescr {
+
+ public final Class<?> returnType;
+
+ public final String name;
+
+ public final Class<?>[] parameters;
+ public final Class<?>[] exceptions;
+ public final Map<String, Object> methodAnnotations;
+ public final Map<String, Object>[] parameterAnnotations;
+
+ public MethodDescr(Method m) {
+ this(m.getName(), m.getReturnType(), m.getParameterTypes(), m.getExceptionTypes(), null, null);
+ }
+
+ public MethodDescr(String name,
+ Class<?> returnType,
+ Class<?>[] parameters,
+ Class<?>[] exceptions) {
+ this.name = name;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ this.exceptions = exceptions;
+ this.methodAnnotations = null;
+ this.parameterAnnotations = null;
+ }
+
+ public MethodDescr(String name,
+ Class<?> returnType,
+ Class<?>[] parameters,
+ Class<?>[] exceptions,
+ Map<String, Object> methodAnnotations,
+ Map<String, Object>[] parameterAnnotations) {
+ this.name = name;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ this.exceptions = exceptions;
+ this.methodAnnotations = methodAnnotations;
+ this.parameterAnnotations = parameterAnnotations;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() + parameters.length;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MethodDescr)) {
+ return false;
+ }
+ MethodDescr oDescr = (MethodDescr)obj;
+ if (!name.equals(oDescr.name) || parameters.length != oDescr.parameters.length) {
+ return false;
+ }
+ for (int i = 0; i < parameters.length; i++) {
+ if (!parameters[i].equals(oDescr.parameters[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static class ConstructorDescr extends MethodDescr {
+
+ public ConstructorDescr(Constructor<?> cons) {
+ this(cons.getParameterTypes(), cons.getExceptionTypes());
+ }
+
+ public ConstructorDescr(Class<?>[] parameters, Class<?>[] exceptions) {
+ super("<init>", Void.TYPE, parameters, exceptions);
+ }
+ }
+
+ public static class AnnotationDescr {
+ public final Class<?> annotation;
+ public final Map<String, Object> fields;
+
+ public AnnotationDescr(Class<?>annotation) {
+ this.annotation = annotation;
+ this.fields = null;
+ }
+
+ public AnnotationDescr(Class<?>annotation, Map<String, Object> fields) {
+ this.annotation = annotation;
+ this.fields = fields;
+ }
+
+ public boolean hasFields() {
+ if (fields == null) {
+ return false;
+ }
+ return true;
+ }
+
+ public String getName() {
+ return mapType(annotation);
+ }
+
+ public Map<String, Object> getFields() {
+ return fields;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hasFields()) {
+ int hash = annotation.hashCode();
+ for (Entry<String, Object> field: fields.entrySet()) {
+ hash += field.getKey().hashCode() + field.getValue().hashCode();
+ }
+ return hash;
+ } else {
+ return annotation.hashCode();
+ }
+ }
+ }
+}
diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java
--- a/src/org/python/compiler/ProxyMaker.java
+++ b/src/org/python/compiler/ProxyMaker.java
@@ -5,91 +5,21 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.util.Map;
import java.util.Set;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.python.core.Py;
-import org.python.core.PyMethod;
-import org.python.core.PyObject;
-import org.python.core.PyProxy;
-import org.python.core.PyReflectedFunction;
import org.python.util.Generic;
-public class ProxyMaker implements ClassConstants, Opcodes
+
+public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes
{
- public static final int tBoolean=0;
- public static final int tByte=1;
- public static final int tShort=2;
- public static final int tInteger=3;
- public static final int tLong=4;
- public static final int tFloat=5;
- public static final int tDouble=6;
- public static final int tCharacter=7;
- public static final int tVoid=8;
- public static final int tOther=9;
- public static final int tNone=10;
-
- public static Map<Class<?>, Integer> types = fillTypes();
-
- public static Map<Class<?>, Integer> fillTypes() {
- Map<Class<?>, Integer> typeMap = Generic.map();
- typeMap.put(Boolean.TYPE, tBoolean);
- typeMap.put(Byte.TYPE, tByte);
- typeMap.put(Short.TYPE, tShort);
- typeMap.put(Integer.TYPE, tInteger);
- typeMap.put(Long.TYPE, tLong);
- typeMap.put(Float.TYPE, tFloat);
- typeMap.put(Double.TYPE, tDouble);
- typeMap.put(Character.TYPE, tCharacter);
- typeMap.put(Void.TYPE, tVoid);
- return typeMap;
- }
-
- public static int getType(Class<?> c) {
- if (c == null) {
- return tNone;
- }
- Object i = types.get(c);
- if (i == null) {
- return tOther;
- } else {
- return ((Integer)i);
- }
- }
-
- /**
- * Retrieves <code>name</code> from the PyObject in <code>proxy</code> if it's defined in
- * Python. This is a specialized helper function for internal PyProxy use.
- */
- public static PyObject findPython(PyProxy proxy, String name) {
- PyObject o = proxy._getPyInstance();
- if (o == null) {
- proxy.__initProxy__(new Object[0]);
- o = proxy._getPyInstance();
- }
- PyObject ret = o.__findattr__(name);
- if (ret instanceof PyMethod) {
- PyMethod meth = ((PyMethod)ret);
- if (meth.__func__ instanceof PyReflectedFunction) {
- PyReflectedFunction func = (PyReflectedFunction)meth.__func__;
- if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) {
- // This function is the default return for the proxy type if the Python instance
- // hasn't returned something of its own from __findattr__, so do the standard
- // Java call on this
- return null;
- }
- }
- }
- Py.setSystemState(proxy._getPySystemState());
- return ret;
- }
-
- Class<?> superclass;
- Class<?>[] interfaces;
+ protected final Class<?> superclass;
+ protected final Class<?>[] interfaces;
Set<String> names;
Set<String> supernames = Generic.set();
+ Set<String> namesAndSigs; // name+signature pairs
public ClassFile classfile;
/** The name of the class to build. */
public String myClass;
@@ -137,134 +67,17 @@
this.interfaces = interfaces;
}
- public static String mapClass(Class<?> c) {
- String name = c.getName();
- int index = name.indexOf(".");
- if (index == -1) {
- return name;
- }
- StringBuffer buf = new StringBuffer(name.length());
- int last_index = 0;
- while (index != -1) {
- buf.append(name.substring(last_index, index));
- buf.append("/");
- last_index = index+1;
- index = name.indexOf(".", last_index);
- }
- buf.append(name.substring(last_index, name.length()));
- return buf.toString();
- }
-
- public static String mapType(Class<?> type) {
- if (type.isArray())
- return "["+mapType(type.getComponentType());
-
- switch (getType(type)) {
- case tByte: return "B";
- case tCharacter: return "C";
- case tDouble: return "D";
- case tFloat: return "F";
- case tInteger: return "I";
- case tLong: return "J";
- case tShort: return "S";
- case tBoolean: return "Z";
- case tVoid: return "V";
- default:
- return "L"+mapClass(type)+";";
- }
- }
-
- public static String makeSig(Class<?> ret, Class<?>... sig) {
- String[] mapped = new String[sig.length];
- for (int i = 0; i < mapped.length; i++) {
- mapped[i] = mapType(sig[i]);
- }
- return makeSig(mapType(ret), mapped);
- }
-
- public static String makeSig(String returnType, String... parameterTypes) {
- StringBuilder buf = new StringBuilder("(");
- for (String param : parameterTypes) {
- buf.append(param);
- }
- return buf.append(')').append(returnType).toString();
- }
-
-
public void doConstants() throws Exception {
Code code = classfile.addMethod("<clinit>", makeSig("V"), Modifier.STATIC);
code.return_();
}
- public static void doReturn(Code code, Class<?> type) throws Exception {
- switch (getType(type)) {
- case tNone:
- break;
- case tCharacter:
- case tBoolean:
- case tByte:
- case tShort:
- case tInteger:
- code.ireturn();
- break;
- case tLong:
- code.lreturn();
- break;
- case tFloat:
- code.freturn();
- break;
- case tDouble:
- code.dreturn();
- break;
- case tVoid:
- code.return_();
- break;
- default:
- code.areturn();
- break;
- }
- }
-
- public static void doNullReturn(Code code, Class<?> type) throws Exception {
- switch (getType(type)) {
- case tNone:
- break;
- case tCharacter:
- case tBoolean:
- case tByte:
- case tShort:
- case tInteger:
- code.iconst_0();
- code.ireturn();
- break;
- case tLong:
- code.lconst_0();
- code.lreturn();
- break;
- case tFloat:
- code.fconst_0();
- code.freturn();
- break;
- case tDouble:
- code.dconst_0();
- code.dreturn();
- break;
- case tVoid:
- code.return_();
- break;
- default:
- code.aconst_null();
- code.areturn();
- break;
- }
- }
-
public void callSuper(Code code,
String name,
String superclass,
Class<?>[] parameters,
Class<?> ret,
- String sig) throws Exception {
+ boolean doReturn) throws Exception {
code.aload(0);
int local_index;
@@ -297,9 +110,11 @@
break;
}
}
- code.invokespecial(superclass, name, sig);
+ code.invokespecial(superclass, name, makeSig(ret, parameters));
- doReturn(code, ret);
+ if (doReturn) {
+ doReturn(code, ret);
+ }
}
public void doJavaCall(Code code, String name, String type,
@@ -471,24 +286,73 @@
public void addMethod(Method method, int access) throws Exception {
+ addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(),
+ method.getExceptionTypes(), access, method.getDeclaringClass());
+ }
+
+ /**
+ * Adds a method of the given name to the class being implemented. If
+ * <code>declaringClass</code> is null, the generated method will expect to find an object of
+ * the method's name in the Python object and call it. If it isn't null, if an object is found
+ * in the Python object, it'll be called. Otherwise the superclass will be called. No checking
+ * is done to guarantee that the superclass has a method with the same signature.
+ */
+ public void addMethod(String name,
+ Class<?> ret,
+ Class<?>[] parameters,
+ Class<?>[] exceptions,
+ int access,
+ Class<?> declaringClass) throws Exception {
+ addMethod(name, name, ret, parameters, exceptions, access, declaringClass, null, null);
+ }
+
+
+ /**
+ * Generates and adds a proxy method to the proxy class
+ *
+ * @param name: name of the java method
+ * @param pyName: name of the python method to which the java method
+ * proxies (useful for clamped objects)
+ *
+ * @param ret: return type
+ * @param parameters: parameter types
+ * @param exceptions: throwable exception types
+ * @param access
+ * @param declaringClass
+ * @param methodAnnotations: method annotations
+ * @param parameterAnnotations: parameter annotations
+ * @throws Exception
+ */
+ public void addMethod(String name,
+ String pyName,
+ Class<?> ret,
+ Class<?>[] parameters,
+ Class<?>[] exceptions,
+ int access,
+ Class<?> declaringClass,
+ AnnotationDescr[] methodAnnotations,
+ AnnotationDescr[][]parameterAnnotations) throws Exception {
boolean isAbstract = false;
-
+
if (Modifier.isAbstract(access)) {
access = access & ~Modifier.ABSTRACT;
isAbstract = true;
}
- Class<?>[] parameters = method.getParameterTypes();
- Class<?> ret = method.getReturnType();
String sig = makeSig(ret, parameters);
+ String[] exceptionTypes = mapExceptions(exceptions);
- String name = method.getName();
names.add(name);
- Code code = classfile.addMethod(name, sig, access);
+ Code code = null;
+ if (methodAnnotations != null && parameterAnnotations != null) {
+ code = classfile.addMethod(name, sig, access, exceptionTypes, methodAnnotations, parameterAnnotations);
+ } else {
+ code = classfile.addMethod(name, sig, access, exceptionTypes);
+ }
code.aload(0);
- code.ldc(name);
+ code.ldc(pyName);
if (!isAbstract) {
int tmp = code.getLocal("org/python/core/PyObject");
@@ -500,12 +364,12 @@
Label callPython = new Label();
code.ifnonnull(callPython);
- String superClass = mapClass(method.getDeclaringClass());
+ String superClass = mapClass(declaringClass);
- callSuper(code, name, superClass, parameters, ret, sig);
+ callSuper(code, name, superClass, parameters, ret, true);
code.label(callPython);
code.aload(tmp);
- callMethod(code, name, parameters, ret, method.getExceptionTypes());
+ callMethod(code, name, parameters, ret, exceptions);
addSuperMethod("super__"+name, name, superClass, parameters,
ret, sig, access);
@@ -515,13 +379,34 @@
code.dup();
Label returnNull = new Label();
code.ifnull(returnNull);
- callMethod(code, name, parameters, ret, method.getExceptionTypes());
+ callMethod(code, name, parameters, ret, exceptions);
code.label(returnNull);
code.pop();
doNullReturn(code, ret);
}
}
+
+ /**
+ * A constructor that is also a method (!)
+ */
+ public void addConstructorMethodCode(String pyName,
+ Class<?>[] parameters,
+ Class<?>[] exceptions,
+ int access,
+ Class<?> declaringClass,
+ Code code) throws Exception {
+ code.aload(0);
+ code.ldc(pyName);
+
+ int tmp = code.getLocal("org/python/core/PyObject");
+ code.invokestatic("org/python/compiler/ProxyMaker", "findPython",
+ makeSig($pyObj, $pyProxy, $str));
+ code.astore(tmp);
+ code.aload(tmp);
+ callMethod(code, "<init>", parameters, Void.TYPE, exceptions);
+ }
+
private String methodString(Method m) {
StringBuffer buf = new StringBuffer(m.getName());
buf.append(":");
@@ -579,7 +464,7 @@
String sig,
int access) throws Exception {
Code code = classfile.addMethod("<init>", sig, access);
- callSuper(code, "<init>", name, parameters, Void.TYPE, sig);
+ callSuper(code, "<init>", name, parameters, Void.TYPE, true);
}
public void addConstructors(Class<?> c) throws Exception {
@@ -600,6 +485,10 @@
addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access);
}
}
+
+ protected void addClassAnnotation(AnnotationDescr annotation) {
+ classfile.addClassAnnotation(annotation);
+ }
// Super methods are added for the following three reasons:
//
@@ -654,7 +543,7 @@
}
supernames.add(methodName);
Code code = classfile.addMethod(methodName, sig, access);
- callSuper(code, superName, declClass, parameters, ret, sig);
+ callSuper(code, superName, declClass, parameters, ret, true);
}
public void addProxy() throws Exception {
@@ -720,6 +609,7 @@
public void build() throws Exception {
names = Generic.set();
+ namesAndSigs = Generic.set();
int access = superclass.getModifiers();
if ((access & Modifier.FINAL) != 0) {
throw new InstantiationException("can't subclass final class");
@@ -728,20 +618,130 @@
classfile = new ClassFile(myClass, mapClass(superclass), access);
addProxy();
- addConstructors(superclass);
+ visitConstructors();
classfile.addInterface("org/python/core/PyProxy");
+
+ visitClassAnnotations();
+ visitMethods();
+ doConstants();
+ addClassDictInit();
+ }
+
+ /**
+ * Visits all methods declared on the given class and classes in its inheritance hierarchy.
+ * Methods visible to subclasses are added to <code>seen</code>.
+ */
+ protected void visitMethods(Class<?> klass) throws Exception {
+ for (Method method : klass.getDeclaredMethods()) {
+
+
+ // make sure we have only one name + signature pair available per method
+ if (!namesAndSigs.add(methodString(method))) {
+ continue;
+ }
- Set<String> seenmethods = Generic.set();
- addMethods(superclass, seenmethods);
+ int access = method.getModifiers();
+ if (Modifier.isStatic(access) || Modifier.isPrivate(access)) {
+ continue;
+ }
+
+ if (Modifier.isNative(access)) {
+ access = access & ~Modifier.NATIVE;
+ }
+
+ if (Modifier.isProtected(access)) {
+ access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC;
+ if (Modifier.isFinal(access)) {
+ addSuperMethod(method, access);
+ continue;
+ }
+ } else if (Modifier.isFinal(access)) {
+ continue;
+ } else if (!Modifier.isPublic(access)) {
+ continue; // package protected by process of elimination; we can't override
+ }
+ addMethod(method, access);
+ }
+
+ Class<?> superClass = klass.getSuperclass();
+ if (superClass != null) {
+ visitMethods(superClass);
+ }
+
+ for (Class<?> iface : klass.getInterfaces()) {
+ visitMethods(iface);
+ }
+ }
+
+ /**
+ * Called for every method on the proxy's superclass and interfaces that can be overriden by the
+ * proxy class. If the proxy wants to perform Python lookup and calling for the method,
+ * {@link #addMethod(Method)} should be called. For abstract methods, addMethod must be called.
+ */
+ protected void visitMethod(Method method) throws Exception {
+ addMethod(method, method.getModifiers());
+ }
+
+ protected void visitMethods() throws Exception {
+ visitMethods(superclass);
for (Class<?> iface : interfaces) {
if (iface.isAssignableFrom(superclass)) {
Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName());
continue;
}
classfile.addInterface(mapClass(iface));
- addMethods(iface, seenmethods);
+ visitMethods(iface);
}
- doConstants();
- addClassDictInit();
}
+
+ /** Adds a constructor that calls through to superclass. */
+ protected void addConstructor(Class<?>[] parameters, int access) throws Exception {
+ String sig = makeSig(Void.TYPE, parameters);
+ Code code = classfile.addMethod("<init>", sig, access);
+ callSuper(code, "<init>", mapClass(superclass), parameters, Void.TYPE, true);
+ }
+
+ /**
+ * Called for every constructor on the proxy's superclass that can be overridden by
+ * the proxy class.
+ */
+ protected void visitConstructor(Constructor<?> constructor) throws Exception {
+ /* Need a fancy constructor for the Java side of things */
+ callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor));
+ }
+
+ /**
+ * Adds a constructor that calls through to the superclass constructor with the same signature
+ * and leaves the returned Code open for more operations. The caller of this method must add a
+ * return to the Code.
+ */
+ protected Code addOpenConstructor(Constructor<?> constructor) throws Exception {
+ String sig = makeSig(Void.TYPE, constructor.getParameterTypes());
+ Code code = classfile.addMethod("<init>", sig, constructor.getModifiers());
+ callSuper(code, "<init>", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, true);
+ return code;
+ }
+
+ /**
+ * Calls __initProxy__ on this class with the given types of parameters, which must be
+ * available as arguments to the currently called method in the order of the parameters.
+ */
+ protected void callInitProxy(Class<?>[] parameters, Code code) throws Exception {
+ code.visitVarInsn(ALOAD, 0);
+ getArgs(code, parameters);
+ code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr));
+ code.visitInsn(RETURN);
+ }
+
+ /**
+ * Visits constructors from this proxy's superclass.
+ */
+ protected void visitConstructors() throws Exception {
+ addConstructors(superclass);
+ }
+
+ protected void visitClassAnnotations() throws Exception {
+ // ProxyMaker itself does nothing with class annotations for now
+ }
+
}
diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java
--- a/src/org/python/core/MakeProxies.java
+++ b/src/org/python/core/MakeProxies.java
@@ -6,6 +6,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.python.compiler.APIVersion;
import org.python.compiler.AdapterMaker;
import org.python.compiler.JavaMaker;
@@ -50,27 +51,45 @@
public static synchronized Class<?> makeProxy(Class<?> superclass,
List<Class<?>> vinterfaces, String className, String proxyName,
PyObject dict) {
+ JavaMaker javaMaker = null;
+
Class<?>[] interfaces = vinterfaces.toArray(new Class<?>[vinterfaces.size()]);
String fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++;
String pythonModuleName;
- PyObject mn = dict.__finditem__("__module__");
- if (mn == null) {
- pythonModuleName = "foo";
+ PyObject module = dict.__finditem__("__module__");
+ if (module == null) {
+ pythonModuleName = "foo"; // FIXME Really, module name foo?
} else {
- pythonModuleName = (String) mn.__tojava__(String.class);
+ pythonModuleName = (String) module.__tojava__(String.class);
}
- JavaMaker jm = new JavaMaker(superclass,
- interfaces,
- className,
- pythonModuleName,
- fullProxyName,
- dict);
+
+ // Grab the proxy maker from the class if it exists, and if it does, use the proxy class
+ // name from the maker
+ PyObject customProxyMaker = dict.__finditem__("__proxymaker__");
+ if (customProxyMaker != null) {
+ if (module == null) {
+ throw Py.TypeError("Classes using __proxymaker__ must define __module__");
+ }
+ PyObject[] args = Py.javas2pys(superclass, interfaces, className, pythonModuleName, fullProxyName, dict);
+ javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class);
+ // TODO Full proxy name
+ }
+
+ if (javaMaker == null) {
+ javaMaker = new JavaMaker(superclass,
+ interfaces,
+ className,
+ pythonModuleName,
+ fullProxyName,
+ dict);
+ }
+
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- jm.build(bytes);
- Py.saveClassFile(fullProxyName, bytes);
+ javaMaker.build(bytes);
+ Py.saveClassFile(javaMaker.myClass, bytes);
- return makeClass(superclass, vinterfaces, jm.myClass, bytes);
+ return makeClass(superclass, vinterfaces, javaMaker.myClass, bytes);
} catch (Exception exc) {
throw Py.JavaError(exc);
}
diff --git a/src/org/python/util/ProxyCompiler.java b/src/org/python/util/ProxyCompiler.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/util/ProxyCompiler.java
@@ -0,0 +1,32 @@
+package org.python.util;
+
+import java.util.Properties;
+
+import org.python.core.PySystemState;
+
+public class ProxyCompiler {
+ /**
+ * Compiles the python file by loading it
+ *
+ * FIXME: this is quite hackish right now. It basically starts a whole interpreter
+ * and set's proxyDebugDirectory as the destination for the compiled proxy class
+ *
+ * @param filename: python filename to exec
+ * @param destDir: destination directory for the proxy classes
+ */
+ public static void compile(String filename, String destDir) {
+ Properties props = new Properties(System.getProperties());
+ props.setProperty(PySystemState.PYTHON_CACHEDIR_SKIP, "true");
+ PySystemState.initialize(props, null);
+ PythonInterpreter interp = new PythonInterpreter();
+
+ String origProxyDir = org.python.core.Options.proxyDebugDirectory;
+ try {
+ org.python.core.Options.proxyDebugDirectory = destDir;
+ interp.execfile(filename);
+ } finally {
+ org.python.core.Options.proxyDebugDirectory = origProxyDir;
+ }
+ }
+
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java
@@ -0,0 +1,36 @@
+package org.python.compiler.custom_proxymaker;
+
+/*
+ * Test support for Python class annotations
+ */
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+import org.python.util.ProxyCompiler;
+
+public class ClassAnnotationTest {
+
+ Class<?> proxy;
+
+ @Before
+ public void setUp() throws Exception {
+ ProxyCompiler.compile("tests/python/custom_proxymaker/annotated_class.py", "build/classes");
+ proxy = Class.forName("custom_proxymaker.tests.AnnotatedInputStream");
+ }
+
+ @Test
+ public void hasClassAnnotation() {
+ // Just by "finding" it we satisfy the test.
+ @SuppressWarnings("unused")
+ Deprecated deprecatedAnnotation = proxy.getAnnotation(Deprecated.class);
+ }
+
+ @Test
+ public void hasCustomAnnotationWithFields() throws Exception {
+ CustomAnnotation customAnnotation = proxy.getAnnotation(CustomAnnotation.class);
+ assertEquals("Darusik", customAnnotation.createdBy());
+ assertEquals(CustomAnnotation.Priority.LOW, customAnnotation.priority());
+ assertArrayEquals(new String[] {"Darjus", "Darjunia"}, customAnnotation.changedBy());
+ }
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java
@@ -0,0 +1,33 @@
+package org.python.compiler.custom_proxymaker;
+
+/*
+ * Tests constructor signatures
+ */
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.*;
+
+import org.junit.*;
+import org.python.util.ProxyCompiler;
+
+import java.awt.Container;
+import javax.swing.BoxLayout;
+
+public class ConstructorSignatureTest {
+ Class<?> proxy;
+
+ @Before
+ public void setUp() throws Exception {
+ ProxyCompiler.compile("tests/python/custom_proxymaker/constructor_signatures.py", "build/classes");
+ proxy = Class.forName("custom_proxymaker.tests.ConstructorSignatures");
+ }
+
+ @Ignore // Constructor signatures are not working yet
+ @Test
+ @SuppressWarnings("unused")
+ public void returnsVoid() throws Exception {
+ Constructor<?> constructor = proxy.getConstructor(new Class<?>[] {Container.class, Integer.TYPE});
+ constructor.newInstance(new Container(), BoxLayout.X_AXIS);
+ }
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java
@@ -0,0 +1,30 @@
+package org.python.compiler.custom_proxymaker;
+
+/**
+ * This Annotation contains most of the possible annotation fields,
+ * used to test the annotation part of custom proxymaker
+ */
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+ at Documented
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.TYPE,ElementType.METHOD,
+ ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE,
+ ElementType.PACKAGE,ElementType.FIELD,ElementType.LOCAL_VARIABLE})
+ at Inherited
+public @interface CustomAnnotation {
+ public enum Priority { LOW, MEDIUM, HIGH }
+ String value();
+ String[] changedBy() default "";
+ Priority[] priorities();
+ Priority priority() default Priority.MEDIUM;
+ String createdBy() default "Darjus Loktevic";
+ String lastChanged() default "08/06/2012";
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java
@@ -0,0 +1,18 @@
+package org.python.compiler.custom_proxymaker;
+
+/*
+ * This test tests that we can create JUnit 4 tests in Python and that JUnit's own
+ * reflection system picks our annotations and runs the underlying code
+ */
+
+import org.junit.Test;
+import org.junit.runner.JUnitCore;
+import org.python.util.ProxyCompiler;
+
+public class JUnitTest {
+ @Test
+ public void testMethodSignatures() throws Exception {
+ ProxyCompiler.compile("tests/python/custom_proxymaker/junit_test.py", "build/classes");
+ JUnitCore.runClasses(Class.forName("custom_proxymaker.tests.JUnitTest"));
+ }
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java
@@ -0,0 +1,77 @@
+package org.python.compiler.custom_proxymaker;
+
+/*
+ * Tests support for various combinations of method signatures
+ */
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.python.util.ProxyCompiler;
+
+public class MethodSignatureTest {
+ Class<?> proxy;
+
+ @Before
+ public void setUp() throws Exception {
+ ProxyCompiler.compile("tests/python/custom_proxymaker/method_signatures.py", "build/classes");
+ proxy = Class.forName("custom_proxymaker.tests.MethodSignatures");
+ }
+
+ @Test
+ public void methodThrows() throws Exception {
+ Method method = proxy.getMethod("throwsException");
+ assertArrayEquals(new Class<?>[] {RuntimeException.class}, method.getExceptionTypes());
+ }
+
+ @Test
+ public void returnsVoid() throws Exception {
+ Method method = proxy.getMethod("throwsException");
+ assertEquals(Void.TYPE, method.getReturnType());
+ }
+
+ @Test
+ public void returnsLong() throws Exception {
+ Method method = proxy.getMethod("returnsLong");
+ assertEquals(Long.TYPE, method.getReturnType());
+ }
+
+ @Test
+ public void returnsObject() throws Exception {
+ Method method = proxy.getMethod("returnsObject");
+ assertEquals(Object.class, method.getReturnType());
+ }
+
+ @Test
+ public void returnsArray() throws Exception {
+ Method method = proxy.getMethod("returnsArray");
+ Object compareType = Array.newInstance(Long.TYPE, 0);
+ assertEquals(compareType.getClass(), method.getReturnType());
+ }
+
+ @Test
+ public void returnsArrayObj() throws Exception {
+ Method method = proxy.getMethod("returnsArrayObj");
+ Object compareType = Array.newInstance(Object.class, 0);
+ assertEquals(compareType.getClass(), method.getReturnType());
+ }
+
+ @Test
+ @SuppressWarnings("unused")
+ public void acceptsString() throws Exception {
+ Class<?>[] partypes = new Class[] {String.class};
+ Method method = proxy.getMethod("acceptsString", partypes);
+ }
+
+ @Test
+ @SuppressWarnings("unused")
+ public void acceptsArray() throws Exception {
+ Object compareType = Array.newInstance(Long.TYPE, 0);
+ Class<?>[] partypes = new Class[] {compareType.getClass()};
+ Method method = proxy.getMethod("acceptsArray", partypes);
+ }
+
+}
diff --git a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java
@@ -0,0 +1,182 @@
+package org.python.compiler.custom_proxymaker;
+
+/*
+ * This is a bare bones implementation of ClampMaker. It's goal is to be a "reference"
+ * implementation for the features that are provided by customizable ProxyMaker
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.python.compiler.Code;
+import org.python.compiler.JavaMaker;
+import org.python.core.Py;
+import org.python.core.PyDictionary;
+import org.python.core.PyObject;
+import org.python.core.__builtin__;
+import org.python.util.Generic;
+
+public class MiniClampMaker extends JavaMaker {
+
+ private final Map<String, PyObject> methodsToAdd = Generic.map();
+ private final Map<String, PyObject> constructorsToAdd = Generic.map();
+ private AnnotationDescr[] classAnnotations = new AnnotationDescr[]{};
+
+ private static AnnotationDescr[] extractAnnotation(PyDictionary dict) {
+ List<AnnotationDescr> annotationDescrs = Generic.list();
+ for (PyObject annotationIter: dict.iteritems().asIterable()) {
+ PyObject annotationClass = annotationIter.__getitem__(0);
+ PyObject annotationFields = annotationIter.__getitem__(1);
+ AnnotationDescr annotationDescr = null;
+ if (annotationFields == Py.None) {
+ annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class));
+ } else {
+ Map<String, Object> fields = Generic.map();
+ for (PyObject item: ((PyDictionary)annotationFields).iteritems().asIterable()) {
+ fields.put(Py.tojava(item.__getitem__(0), String.class), Py.tojava(item.__getitem__(1), Object.class));
+ }
+ annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class), fields);
+ }
+ annotationDescrs.add(annotationDescr);
+ }
+ return (AnnotationDescr[]) annotationDescrs.toArray(new AnnotationDescr[annotationDescrs.size()]);
+ }
+
+ public MiniClampMaker(Class<?> superclass,
+ Class<?>[] interfaces,
+ String pythonClass,
+ String pythonModule,
+ String myClass,
+ PyObject methods) {
+ super(superclass, interfaces, pythonClass, pythonModule, myClass, methods);
+
+ // if we find __java_package__, override the default proxy naming with
+ // __java_package__ + .pythonClass
+ PyObject javaPackage = methods.__finditem__("__java_package__");
+ if (javaPackage != null) {
+ String newMyClass = new String((String)javaPackage.__tojava__(String.class));
+ newMyClass += "." + pythonClass;
+ this.myClass = newMyClass;
+ }
+
+
+ PyObject clampAttr = Py.newString("_clamp");
+ for (PyObject pykey : methods.asIterable()) {
+ String key = Py.tojava(pykey, String.class);
+ PyObject value = methods.__finditem__(key);
+ PyObject clampObj = __builtin__.getattr(value, clampAttr, Py.None);
+ if (clampObj == Py.None) {
+ continue;
+ }
+ String name = (String)clampObj.__getattr__("name").__tojava__(String.class);
+ if (name.equals("__init__")) {
+ constructorsToAdd.put(key, clampObj);
+ } else {
+ methodsToAdd.put(key, clampObj);
+ }
+ }
+ PyObject pyAnnotations = methods.__finditem__("_clamp_class_annotations");
+ if (pyAnnotations != null) {
+ classAnnotations = extractAnnotation((PyDictionary)pyAnnotations);
+ }
+
+ }
+
+ @Override
+ protected void visitClassAnnotations() throws Exception {
+ for (AnnotationDescr annotation: classAnnotations) {
+ addClassAnnotation(annotation);
+ }
+ }
+
+ @Override
+ protected void visitConstructors() throws Exception {
+
+ Set<Constructor<?>> superConstructors = Generic.set();
+ for (Constructor<?> constructor: superclass.getDeclaredConstructors()) {
+ superConstructors.add(constructor);
+ }
+
+ for (Entry<String, PyObject> meth : constructorsToAdd.entrySet()) {
+ Constructor<?> superToCall = null;
+ String pyName = meth.getKey();
+ PyObject clampObj = meth.getValue();
+
+ Class<?>[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class);
+ Class<?>[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class);
+
+ if (clampObj.__findattr__("super_constructor") != null) {
+ superToCall = (Constructor<?>)clampObj.__getattr__("super_constructor").__tojava__(Constructor.class);
+ } else { // try to find a matching super constructor
+ try {
+ superToCall = superclass.getDeclaredConstructor(parameterClasses);
+ } catch (NoSuchMethodException err) {
+ // FIXME need a fancy constructor finder
+ superToCall = superConstructors.iterator().next();
+ }
+ }
+
+ for (Constructor<?> constructor: superConstructors) {
+ if (Arrays.equals(constructor.getParameterTypes(), superToCall.getParameterTypes())) {
+ superConstructors.remove(constructor);
+ }
+ }
+
+ AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations"));
+ PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class);
+
+ AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][];
+ for (int i = 0; i<parameterAnnotationObjs.length; i++) {
+ if (parameterAnnotationObjs[i].isMappingType()) {
+ parameterAnnotations[i] = extractAnnotation((PyDictionary)parameterAnnotationObjs[i]);
+ }
+ }
+
+ String fullsig = makeSig(Void.TYPE, parameterClasses);
+ String[] mappedExceptions = mapExceptions(thrownClasses);
+
+ // if our superclass already has
+ Code code = classfile.addMethod("<init>", fullsig, Modifier.PUBLIC, mappedExceptions, methodAnnotations, parameterAnnotations);
+ callSuper(code, "<init>", mapClass(superclass), superToCall.getParameterTypes(), Void.TYPE, false);
+ // instead of calling the proxy, we construct the full method code
+
+ addConstructorMethodCode(pyName, superToCall.getParameterTypes(), thrownClasses, Modifier.PUBLIC, superclass, code);
+
+ }
+
+ // the non-overwritten constructors
+ for (Constructor<?> constructor: superConstructors) {
+ addConstructor(constructor.getParameterTypes(), Modifier.PUBLIC);
+ }
+ }
+
+ @Override
+ protected void visitMethods () throws Exception {
+ for (Entry<String, PyObject> meth : methodsToAdd.entrySet()) {
+ PyObject clampObj = meth.getValue();
+ String methodName = (String)clampObj.__getattr__("name").__tojava__(String.class);
+ Class<?> returnClass = (Class<?>)clampObj.__getattr__("returntype").__tojava__(Class.class);
+ Class<?>[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class);
+ Class<?>[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class);
+ AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations"));
+ PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class);
+
+ AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][];
+ for (int i = 0; i<parameterAnnotationObjs.length; i++) {
+ if (parameterAnnotationObjs[i].isMappingType()) {
+ parameterAnnotations[i] = extractAnnotation((PyDictionary)parameterAnnotationObjs[i]);
+ }
+ }
+
+ addMethod(methodName, meth.getKey(), (Class<?>)returnClass, parameterClasses, thrownClasses,
+ Modifier.PUBLIC, superclass, methodAnnotations, parameterAnnotations);
+
+ }
+ }
+}
+
diff --git a/tests/python/custom_proxymaker/annotated_class.py b/tests/python/custom_proxymaker/annotated_class.py
new file mode 100644
--- /dev/null
+++ b/tests/python/custom_proxymaker/annotated_class.py
@@ -0,0 +1,18 @@
+import java.io.BufferedInputStream
+
+from java.lang import Deprecated
+
+from org.python.compiler.custom_proxymaker import MiniClampMaker
+from org.python.compiler.custom_proxymaker import CustomAnnotation
+
+class AnnotatedInputStream(java.io.BufferedInputStream):
+ __proxymaker__ = MiniClampMaker
+ __java_package__ = 'custom_proxymaker.tests'
+
+ _clamp_class_annotations = {CustomAnnotation:
+ {'createdBy': 'Darusik',
+ 'priority': CustomAnnotation.Priority.LOW,
+ 'changedBy': ['Darjus', 'Darjunia']},
+ Deprecated:None}
+
+
diff --git a/tests/python/custom_proxymaker/clamp.py b/tests/python/custom_proxymaker/clamp.py
new file mode 100644
--- /dev/null
+++ b/tests/python/custom_proxymaker/clamp.py
@@ -0,0 +1,5 @@
+from collections import namedtuple
+
+# just a helper
+ClampMethod = namedtuple('ClampMethod', 'name returntype argtypes throws '
+ 'method_annotations parameter_annotations')
diff --git a/tests/python/custom_proxymaker/constructor_signatures.py b/tests/python/custom_proxymaker/constructor_signatures.py
new file mode 100644
--- /dev/null
+++ b/tests/python/custom_proxymaker/constructor_signatures.py
@@ -0,0 +1,32 @@
+from java.lang import (Void, String, Integer, Long)
+from javax.swing import BoxLayout
+from java.awt import Container
+
+from org.python.compiler.custom_proxymaker import MiniClampMaker
+
+import sys
+sys.path.append('tests/python/custom_proxymaker/')
+from clamp import ClampMethod
+
+class ConstructorSignatures(BoxLayout):
+ __proxymaker__ = MiniClampMaker
+ __java_package__ = 'custom_proxymaker.tests'
+
+# def __init__(self, val):
+# super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS)
+# print val
+
+ def __jinit__(self, one, two):
+ # super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS)
+ print one, two
+
+ __jinit__._clamp = ClampMethod('__init__', Void.TYPE, [Container, Integer.TYPE], [], {}, [{}])
+
+ def test(self):
+ return 1
+ test._clamp = ClampMethod('test', Long.TYPE, [], [], {}, [{}])
+
+ def toString(self):
+ return self.__class__.__name__
+ toString._clamp = ClampMethod('toString', String, [], [], {}, [{}])
+
diff --git a/tests/python/custom_proxymaker/junit_test.py b/tests/python/custom_proxymaker/junit_test.py
new file mode 100644
--- /dev/null
+++ b/tests/python/custom_proxymaker/junit_test.py
@@ -0,0 +1,28 @@
+from org.junit.Assert import assertEquals
+from org.junit import Test
+from java.lang import (Object, Void, Long)
+from java.lang import Exception as JavaException
+import sys
+import time
+
+from org.python.compiler.custom_proxymaker import MiniClampMaker
+
+sys.path.append('tests/python/custom_proxymaker/')
+from clamp import ClampMethod
+
+class JUnitTest(Object):
+ __proxymaker__ = MiniClampMaker
+ __java_package__ = 'custom_proxymaker.tests'
+
+
+ def testAddition(self):
+ assertEquals(4, 1 + 3)
+ testAddition._clamp = ClampMethod('testAddition',Void.TYPE,[],[],{Test:None},[{}])
+
+ def testJavaException(self):
+ raise JavaException()
+ testJavaException._clamp = ClampMethod('testJavaException', Void.TYPE, [], [JavaException], {Test:{'expected':JavaException}}, [{}])
+
+ def testTimeout(self):
+ time.sleep(0.1)
+ testTimeout._clamp = ClampMethod('testTimeout', Void.TYPE, [], [], {Test:{'timeout':Long(1000)}}, [{}])
diff --git a/tests/python/custom_proxymaker/method_signatures.py b/tests/python/custom_proxymaker/method_signatures.py
new file mode 100644
--- /dev/null
+++ b/tests/python/custom_proxymaker/method_signatures.py
@@ -0,0 +1,41 @@
+from java.lang import (Class, Object, Void, String, Long)
+from java.lang import RuntimeException
+
+from org.python.compiler.custom_proxymaker import MiniClampMaker
+
+import sys
+sys.path.append('tests/python/custom_proxymaker/')
+from clamp import ClampMethod
+
+class MethodSignatures(Object):
+ __proxymaker__ = MiniClampMaker
+ __java_package__ = 'custom_proxymaker.tests'
+
+ def throwsException(self):
+ pass
+ throwsException._clamp = ClampMethod('throwsException', Void.TYPE, [], [RuntimeException], {}, [{}])
+
+ def returnsLong(self):
+ return 2
+ returnsLong._clamp = ClampMethod('returnsLong', Long.TYPE, [], [], {}, [{}])
+
+ def returnsObject(self):
+ return Object()
+ returnsObject._clamp = ClampMethod('returnsObject', Object, [], [], {}, [{}])
+
+ def returnsArray(self):
+ return [1,2,3]
+ returnsArray._clamp = ClampMethod('returnsArray', Class.forName('[J'), [], [], {}, [{}])
+
+ def returnsArrayObj(self):
+ return [1,2,3]
+ returnsArrayObj._clamp = ClampMethod('returnsArrayObj', Class.forName('[Ljava.lang.Object;'), [], [], {}, [{}])
+
+ def acceptsString(self, arg):
+ pass
+ acceptsString._clamp = ClampMethod('acceptsString', Void.TYPE, [String], [], {}, [{}])
+
+ def acceptsArray(self, arg):
+ pass
+ acceptsArray._clamp = ClampMethod('acceptsArray', Void.TYPE, [Class.forName('[J')], [], {}, [{}])
+
\ No newline at end of file
--
Repository URL: http://hg.python.org/jython
More information about the Jython-checkins
mailing list