[Jython-checkins] jython (merge default -> default): Merge io work with trunk

jeff.allen jython-checkins at python.org
Tue Dec 25 02:05:26 CET 2012


http://hg.python.org/jython/rev/481f7d06d470
changeset:   6917:481f7d06d470
parent:      6916:e5c55803473f
parent:      6911:f8224fee2886
user:        Jeff Allen <ja...py at farowl.co.uk>
date:        Tue Dec 25 00:47:34 2012 +0000
summary:
  Merge io work with trunk

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