From: <cg...@us...> - 2009-08-22 20:31:52
|
Revision: 6707 http://jython.svn.sourceforge.net/jython/?rev=6707&view=rev Author: cgroves Date: 2009-08-22 20:31:42 +0000 (Sat, 22 Aug 2009) Log Message: ----------- Allow Python classes to specify the ProxyMaker to use to construct their java proxy by setting __proxymaker__ on the class. The value of __proxymaker__ should be a callable that takes a superclass, iterable of interfaces, name of python class, name of python module and the dict of the class. It needs to return an instance of a subclass of ProxyMaker. Rework ProxyMaker to allow it to be subclassed more easily and hide most of its public fields and methods as protected. Still needs more work in exposing the constructors being added. Deprecate Options.proxyDebugDirectory in favor of sys.javaproxy_dir. Modified Paths: -------------- branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java branches/customizable-proxymaker/src/org/python/core/MakeProxies.java branches/customizable-proxymaker/src/org/python/core/Options.java branches/customizable-proxymaker/src/org/python/core/Py.java branches/customizable-proxymaker/src/org/python/core/PyBuiltinType.java branches/customizable-proxymaker/src/org/python/core/PyReflectedFunction.java branches/customizable-proxymaker/src/org/python/core/PyUserType.java Added Paths: ----------- branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java Modified: branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -6,7 +6,6 @@ import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; -import org.python.util.Generic; public class AdapterMaker extends ProxyMaker { @@ -15,25 +14,17 @@ } @Override - public void build() throws Exception { - names = Generic.set(); - int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNCHRONIZED; - classfile = new ClassFile(myClass, "java/lang/Object", access); + protected void build() { classfile.addInterface(mapClass(interfaces[0])); - addMethods(interfaces[0], new HashSet<String>()); - addConstructors(Object.class); - doConstants(); - } - - @Override - public void doConstants() throws Exception { + addMethods(interfaces[0], new HashSet<MethodDescr>()); + addConstructors(); for (String name : names) { classfile.addField(name, $pyObj, Opcodes.ACC_PUBLIC); } } @Override - public void addMethod(Method method, int access) throws Exception { + protected void addMethod(Method method, int access) { Class<?>[] parameters = method.getParameterTypes(); Class<?> ret = method.getReturnType(); String name = method.getName(); Modified: branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -29,11 +29,14 @@ List<FieldVisitor> fieldVisitors; public static String fixName(String n) { - if (n.indexOf('.') == -1) + if (n.indexOf('.') == -1) { return n; + } char[] c = n.toCharArray(); - for(int i=0; i<c.length; i++) { - if (c[i] == '.') c[i] = '/'; + for (int i = 0; i < c.length; i++) { + if (c[i] == '.') { + c[i] = '/'; + } } return new String(c); } @@ -46,13 +49,14 @@ public ClassFile(String name, String superclass, int access) { this(name, superclass, access, org.python.core.imp.NO_MTIME); } + public ClassFile(String name, String superclass, int access, long mtime) { this.name = fixName(name); this.superclass = fixName(superclass); this.interfaces = new String[0]; this.access = access; this.mtime = mtime; - + cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); methodVisitors = Collections.synchronizedList(new ArrayList<MethodVisitor>()); fieldVisitors = Collections.synchronizedList(new ArrayList<FieldVisitor>()); @@ -62,49 +66,50 @@ sfilename = name; } - public void addInterface(String name) throws IOException { + public void addInterface(String name) { String[] new_interfaces = new String[interfaces.length+1]; System.arraycopy(interfaces, 0, new_interfaces, 0, interfaces.length); new_interfaces[interfaces.length] = name; interfaces = new_interfaces; } - public Code addMethod(String name, String type, int access) - throws IOException - { + public Code addMethod(String name, String type, int access) { MethodVisitor mv = cw.visitMethod(access, name, type, null, null); Code pmv = new Code(mv, type, access); methodVisitors.add(pmv); return pmv; } - public void addField(String name, String type, int access) - throws IOException - { + public void addField(String name, String type, int access) { FieldVisitor fv = cw.visitField(access, name, type, null, null); fieldVisitors.add(fv); } - public void endFields() - throws IOException - { + public void endFields() { for (FieldVisitor fv : fieldVisitors) { fv.visitEnd(); } } - - public void endMethods() - throws IOException - { - for (int i=0; i<methodVisitors.size(); i++) { + + public void endMethods() { + for (int i = 0; i < methodVisitors.size(); i++) { MethodVisitor mv = methodVisitors.get(i); - mv.visitMaxs(0,0); + mv.visitMaxs(0, 0); mv.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); + byte[] ba = create(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(ba.length); + baos.write(ba, 0, ba.length); + baos.writeTo(stream); + baos.close(); + } + + public byte[] create() { + 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); // XXX: should imp.java really house this value or should imp.java point into // org.python.compiler? @@ -121,12 +126,6 @@ endFields(); endMethods(); - byte[] ba = cw.toByteArray(); - //fos = io.FileOutputStream("%s.class" % self.name) - ByteArrayOutputStream baos = new ByteArrayOutputStream(ba.length); - baos.write(ba, 0, ba.length); - baos.writeTo(stream); - //debug(baos); - baos.close(); + return cw.toByteArray(); } } Modified: branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -10,18 +10,21 @@ public String pythonClass, pythonModule; - PyObject methods; + protected PyObject dict; + private final boolean proxyNeeded; + public JavaMaker(Class<?> superclass, Class<?>[] interfaces, String pythonClass, String pythonModule, - String myClass, - PyObject methods) { - super(myClass, superclass, interfaces); + String proxyClassName, + PyObject pythonClassDict) { + super(proxyClassName, superclass, interfaces); + proxyNeeded = superclass != null || interfaces.length > 0; this.pythonClass = pythonClass; this.pythonModule = pythonModule; - this.methods = methods; + this.dict = pythonClassDict; } @Override @@ -29,10 +32,19 @@ Class<?>[] parameters, Class<?> ret, String sig, - int access) throws Exception { + int access) { /* Need a fancy constructor for the Java side of things */ Code code = classfile.addMethod("<init>", sig, access); callSuper(code, "<init>", name, parameters, null, sig); + callInitProxy(parameters, code); + } + + /** + * Calls __initProxy__ on this class with the given 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) { code.visitVarInsn(ALOAD, 0); getArgs(code, parameters); code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); @@ -40,9 +52,10 @@ } @Override - public void addProxy() throws Exception { - if (methods != null) + public void addProxy() { + if (dict != null) { super.addProxy(); + } // _initProxy method Code code = classfile.addMethod("__initProxy__", makeSig("V", $objArr), Modifier.PUBLIC); @@ -58,14 +71,18 @@ } @Override - public void addMethod(Method method, int access) throws Exception { + public void addMethod(Method method, int access) { if (Modifier.isAbstract(access)) { - // Maybe throw an exception here??? + // TODO - Allow proxies to determine their handling of missing abstract methods super.addMethod(method, access); - } else if (methods.__finditem__(method.getName().intern()) != null) { + } else if (dict.__finditem__(method.getName().intern()) != null) { super.addMethod(method, access); } else if (Modifier.isProtected(method.getModifiers())) { addSuperMethod(method, access); } } + + public boolean isProxyNeeded() { + return proxyNeeded; + } } Added: branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java (rev 0) +++ branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -0,0 +1,182 @@ +package org.python.compiler; + +import java.util.Map; + +import org.python.util.Generic; + +/** + * Various static methods and constants used in generating bytecode for proxy classes. + */ +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 = Generic.map(); + static { + types.put(Boolean.TYPE, tBoolean); + types.put(Byte.TYPE, tByte); + types.put(Short.TYPE, tShort); + types.put(Integer.TYPE, tInteger); + types.put(Long.TYPE, tLong); + types.put(Float.TYPE, tFloat); + types.put(Double.TYPE, tDouble); + types.put(Character.TYPE, tCharacter); + types.put(Void.TYPE, tVoid); + } + + 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); + } + } + + 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) { + 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) { + 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; + } + } +} Modified: branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -1,11 +1,9 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.compiler; -import java.io.OutputStream; 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; @@ -17,48 +15,7 @@ import org.python.core.PyReflectedFunction; import org.python.util.Generic; -public class ProxyMaker 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); - } - } - +public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes { /** * 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. @@ -86,13 +43,13 @@ return ret; } - Class<?> superclass; - Class<?>[] interfaces; - Set<String> names; - Set<String> supernames = Generic.set(); - public ClassFile classfile; + protected final Class<?> superclass; + protected final Class<?>[] interfaces; + protected final Set<String> names = Generic.set(); + protected final Set<String> supernames = Generic.set(); + protected ClassFile classfile; /** The name of the class to build. */ - public String myClass; + public String proxyClassName; /** * Creates a proxy class maker that produces classes named @@ -100,7 +57,6 @@ * implemented interface or extended class, depending on the its type. * * @deprecated - Use {@link ProxyMaker#ProxyMaker(String, Class, Class[]) - */ @Deprecated public ProxyMaker(String superclassName, Class<?> superclass) { @@ -115,13 +71,15 @@ * extends <code>superclass</code> and implements the interfaces in <code>interfaces</code>. */ public ProxyMaker(String proxyClassName, Class<?> superclass, Class<?>... interfaces) { - this.myClass = proxyClassName; + this.proxyClassName = proxyClassName; if (superclass == null) { superclass = Object.class; } if (superclass.isInterface()) { throw new IllegalArgumentException("Given an interface, " + superclass.getName() + ", for a proxy superclass"); + } else if ((superclass.getModifiers() & Modifier.FINAL) != 0) { + throw new IllegalArgumentException("Can't subclass final class " + superclass.getName()); } this.superclass = superclass; if (interfaces == null) { @@ -137,134 +95,39 @@ 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(); + /** + * Builds this proxy and returns its bytecode. + */ + public synchronized byte[] make() { + classfile = new ClassFile(proxyClassName, mapClass(superclass), Modifier.PUBLIC + | Modifier.SYNCHRONIZED); + build(); + names.clear(); + supernames.clear(); + return classfile.create(); } - public static String mapType(Class<?> type) { - if (type.isArray()) - return "["+mapType(type.getComponentType()); + /** + * Called once per created proxy class. Should fill in the necessary methods, constructors and + * interfaces on <code>classfile</code>. Only one call to build will be active at a time, so + * data can be collected in the class as the call proceed, but it should be cleaned up before + * returning to keep later calls to build functioning properly. + */ + protected void build() { + addProxy(); + addConstructors(); + classfile.addInterface("org/python/core/PyProxy"); - 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)+";"; - } + addMethods(); + addClassDictInit(); } - 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, + protected void callSuper(Code code, String name, String superclass, Class<?>[] parameters, Class<?> ret, - String sig) throws Exception { + String sig) { code.aload(0); int local_index; @@ -302,16 +165,13 @@ doReturn(code, ret); } - public void doJavaCall(Code code, String name, String type, - String jcallName) - throws Exception - { + protected void doJavaCall(Code code, String name, String type, String jcallName) { code.invokevirtual("org/python/core/PyObject", jcallName, makeSig($pyObj, $objArr)); - code.invokestatic("org/python/core/Py", "py2"+name, makeSig(type, $pyObj)); + code.invokestatic("org/python/core/Py", "py2" + name, makeSig(type, $pyObj)); } - public void getArgs(Code code, Class<?>[] parameters) throws Exception { + protected void getArgs(Code code, Class<?>[] parameters) { if (parameters.length == 0) { code.getstatic("org/python/core/Py", "EmptyObjects", $pyObjArr); } else { @@ -367,11 +227,11 @@ } } - public void callMethod(Code code, - String name, - Class<?>[] parameters, - Class<?> ret, - Class<?>[] exceptions) throws Exception { + protected void callMethod(Code code, + String name, + Class<?>[] parameters, + Class<?> ret, + Class<?>[] exceptions) { Label start = null; Label end = null; @@ -469,79 +329,147 @@ } } - - public void addMethod(Method method, int access) throws Exception { + /** + * Adds an override of <code>method</code> to the class being implemented. If + * <code>method</code> is abstract, 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 abstract, if an object is found + * in the Python object, it'll be called. Otherwise the superclass will be called. + * + * @param modifier - the modifier to be used on the generated method. If the given modifier is + * abstract, that will be removed. + */ + protected void addMethod(Method method, int modifier) { boolean isAbstract = false; - - if (Modifier.isAbstract(access)) { - access = access & ~Modifier.ABSTRACT; + if (Modifier.isAbstract(modifier)) { + modifier = modifier & ~Modifier.ABSTRACT; isAbstract = true; } + addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), + method.getExceptionTypes(), modifier, isAbstract ? null : method.getDeclaringClass()); + } - Class<?>[] parameters = method.getParameterTypes(); - Class<?> ret = method.getReturnType(); - String sig = makeSig(ret, parameters); + /** + * Adds a method to the class being implemented with the given signature. The generated method + * will expect to find an object of the method's name in the Python object and call it. + * + * @param - the modifier to be used on the generated method. + */ + protected void addMethod(String name, + Class<?> ret, + Class<?>[] parameters, + Class<?>[] exceptions, + int modifier) { + addMethod(name, ret, parameters, exceptions, modifier, null); + } - String name = method.getName(); + /** + * 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. + */ + protected void addMethod(String name, + Class<?> ret, + Class<?>[] parameters, + Class<?>[] exceptions, + int access, + Class<?> declaringClass) { names.add(name); - + String sig = makeSig(ret, parameters); Code code = classfile.addMethod(name, sig, access); - code.aload(0); code.ldc(name); - - if (!isAbstract) { + if (declaringClass != null) { 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); - Label callPython = new Label(); code.ifnonnull(callPython); - - String superClass = mapClass(method.getDeclaringClass()); - + String superClass = mapClass(declaringClass); callSuper(code, name, superClass, parameters, ret, sig); code.label(callPython); code.aload(tmp); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); - - addSuperMethod("super__"+name, name, superClass, parameters, - ret, sig, access); + callMethod(code, name, parameters, ret, exceptions); + addSuperMethod("super__" + name, name, superClass, parameters, ret, sig, access); } else { code.invokestatic("org/python/compiler/ProxyMaker", "findPython", makeSig($pyObj, $pyProxy, $str)); 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); } } - private String methodString(Method m) { - StringBuffer buf = new StringBuffer(m.getName()); - buf.append(":"); - Class<?>[] params = m.getParameterTypes(); - for (Class<?> param : params) { - buf.append(param.getName()); - buf.append(","); + protected static class MethodDescr { + + public final Class<?> returnType; + + public final String name; + + public final Class<?>[] parameters; + + public final Class<?>[] exceptions; + + public MethodDescr(Method m) { + this(m.getName(), m.getReturnType(), m.getParameterTypes(), m.getExceptionTypes()); } - return buf.toString(); + + public MethodDescr(String name, + Class<?> returnType, + Class<?>[] parameters, + Class<?>[] exceptions) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + } + + @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; + } } - protected void addMethods(Class<?> c, Set<String> t) throws Exception { - Method[] methods = c.getDeclaredMethods(); - for (Method method : methods) { - if (!t.add(methodString(method))) { + protected void addMethods(Class<?> c, Set<MethodDescr> t) { + for (Method method : c.getDeclaredMethods()) { + + int access = method.getModifiers(); + if (Modifier.isStatic(access) || Modifier.isPrivate(access) || + PyReflectedFunction.isPackagedProtected(access)) { + // package protected and private aren't visible to our class, so they don't enter + // into the namespace, and likewise for static methods, regardless of their access. + + // TODO - allow overriding of package protected methods if the generated class is in + // the same package, throw an exception otherwise. continue; } - int access = method.getModifiers(); - if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { + if (!t.add(new MethodDescr(method))) { + // A method with the same signature has already been added, skip this. continue; } @@ -549,6 +477,7 @@ access = access & ~Modifier.NATIVE; } + // TODO - Track accessible final methods in their own Set, protect from overrides if (Modifier.isProtected(access)) { access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; if (Modifier.isFinal(access)) { @@ -557,8 +486,6 @@ } } 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); } @@ -573,18 +500,22 @@ } } - public void addConstructor(String name, - Class<?>[] parameters, - Class<?> ret, - String sig, - int access) throws Exception { + /** Adds a constructor that calls through to superclass. */ + protected void addConstructor(String superclassName, + Class<?>[] parameters, + Class<?> ret, + String sig, + int access) { Code code = classfile.addMethod("<init>", sig, access); - callSuper(code, "<init>", name, parameters, Void.TYPE, sig); + callSuper(code, "<init>", superclassName, parameters, Void.TYPE, sig); } - public void addConstructors(Class<?> c) throws Exception { - Constructor<?>[] constructors = c.getDeclaredConstructors(); - String name = mapClass(c); + /** + * Adds constructors from this proxy's superclass. + */ + protected void addConstructors() { + Constructor<?>[] constructors = superclass.getDeclaredConstructors(); + String superclassName = mapClass(superclass); for (Constructor<?> constructor : constructors) { int access = constructor.getModifiers(); if (Modifier.isPrivate(access)) { @@ -597,7 +528,8 @@ access = access & ~Modifier.PROTECTED | Modifier.PUBLIC; } Class<?>[] parameters = constructor.getParameterTypes(); - addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access); + addConstructor(superclassName, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), + access); } } @@ -615,7 +547,7 @@ // super__ prefix. This gives access to super. version or the // method. // - public void addSuperMethod(Method method, int access) throws Exception { + protected void addSuperMethod(Method method, int access) { Class<?>[] parameters = method.getParameterTypes(); Class<?> ret = method.getReturnType(); String superClass = mapClass(method.getDeclaringClass()); @@ -625,17 +557,17 @@ methodName = "super__" + superName; access &= ~Modifier.FINAL; } - addSuperMethod(methodName, superName, superClass, parameters, - ret, makeSig(ret, parameters), access); + addSuperMethod(methodName, superName, superClass, parameters, ret, + makeSig(ret, parameters), access); } - public void addSuperMethod(String methodName, + protected void addSuperMethod(String methodName, String superName, String declClass, Class<?>[] parameters, Class<?> ret, String sig, - int access) throws Exception { + int access) { if (methodName.startsWith("super__")) { /* rationale: JC java-class, P proxy-class subclassing JC in order to avoid infinite recursion P should define super__foo @@ -657,45 +589,41 @@ callSuper(code, superName, declClass, parameters, ret, sig); } - public void addProxy() throws Exception { - // implement PyProxy interface + /** + * Adds the methods and fields necessary to implement PyProxy. + */ + protected void addProxy() { classfile.addField("__proxy", $pyObj, Modifier.PROTECTED); - // setProxy methods Code code = classfile.addMethod("_setPyInstance", makeSig("V", $pyObj), Modifier.PUBLIC); code.aload(0); code.aload(1); code.putfield(classfile.name, "__proxy", $pyObj); code.return_(); - // getProxy method code = classfile.addMethod("_getPyInstance", makeSig($pyObj), Modifier.PUBLIC); code.aload(0); code.getfield(classfile.name, "__proxy", $pyObj); code.areturn(); String pySys = "Lorg/python/core/PySystemState;"; - // implement PyProxy interface classfile.addField("__systemState", pySys, Modifier.PROTECTED | Modifier.TRANSIENT); - // setProxy method - code = classfile.addMethod("_setPySystemState", - makeSig("V", pySys), - Modifier.PUBLIC); - + code = classfile.addMethod("_setPySystemState", makeSig("V", pySys), Modifier.PUBLIC); code.aload(0); code.aload(1); code.putfield(classfile.name, "__systemState", pySys); code.return_(); - // getProxy method code = classfile.addMethod("_getPySystemState", makeSig(pySys), Modifier.PUBLIC); code.aload(0); code.getfield(classfile.name, "__systemState", pySys); code.areturn(); } - public void addClassDictInit() throws Exception { - // classDictInit method + /** + * Adds the classDictInit static method to fill in __supernames__ on the class' dict + */ + protected void addClassDictInit() { classfile.addInterface(mapClass(org.python.core.ClassDictInit.class)); Code code = classfile.addMethod("classDictInit", makeSig("V", $pyObj), Modifier.PUBLIC | Modifier.STATIC); @@ -710,28 +638,8 @@ code.return_(); } - /** - * Builds this proxy and writes its bytecode to <code>out</code>. - */ - public void build(OutputStream out) throws Exception { - build(); - classfile.write(out); - } - - public void build() throws Exception { - names = Generic.set(); - int access = superclass.getModifiers(); - if ((access & Modifier.FINAL) != 0) { - throw new InstantiationException("can't subclass final class"); - } - access = Modifier.PUBLIC | Modifier.SYNCHRONIZED; - - classfile = new ClassFile(myClass, mapClass(superclass), access); - addProxy(); - addConstructors(superclass); - classfile.addInterface("org/python/core/PyProxy"); - - Set<String> seenmethods = Generic.set(); + protected Set<MethodDescr> addMethods() { + Set<MethodDescr> seenmethods = Generic.set(); addMethods(superclass, seenmethods); for (Class<?> iface : interfaces) { if (iface.isAssignableFrom(superclass)) { @@ -741,7 +649,6 @@ classfile.addInterface(mapClass(iface)); addMethods(iface, seenmethods); } - doConstants(); - addClassDictInit(); + return seenmethods; } } Modified: branches/customizable-proxymaker/src/org/python/core/MakeProxies.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -2,7 +2,6 @@ package org.python.core; -import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; @@ -12,87 +11,120 @@ class MakeProxies { - private static Class<?> makeClass(Class<?> referent, - List<Class<?>> secondary, - String name, - ByteArrayOutputStream bytes) { - List<Class<?>> referents = null; - if (secondary != null) { - if (referent != null) { - secondary.add(0, referent); - } - referents = secondary; - } else if (referent != null) { - referents = new ArrayList<Class<?>>(1); - referents.add(referent); - } - - return BytecodeLoader.makeClass(name, referents, bytes.toByteArray()); - } - public static Class<?> makeAdapter(Class<?> c) { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); AdapterMaker maker = new AdapterMaker(proxyPrefix + c.getName(), c); - try { - maker.build(bytes); - } catch (Exception exc) { - throw Py.JavaError(exc); - } + byte[] bytecode = maker.make(); - Py.saveClassFile(maker.myClass, bytes); + saveDebugBytecode(maker.proxyClassName, bytecode); - return makeClass(c, null, maker.myClass, bytes); + return makeClass(c, null, maker.proxyClassName, bytecode); } - private static final String proxyPrefix = "org.python.proxies."; + public static synchronized Class<?> makeProxy(Class<?> superclass, + List<Class<?>> interfaces, + String className, + PyObject dict) { + JavaMaker javaMaker = null; + String fullProxyName = null; + String moduleName; + PyObject module = dict.__finditem__("__module__"); + if (module == null) { + moduleName = "unknown_module"; + } else { + moduleName = Py.tojava(module, String.class); + } + Class<?>[] interfacesArr = interfaces.toArray(new Class<?>[interfaces.size()]); - private static int proxyNumber = 0; + // 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, interfacesArr, className, moduleName, dict); + javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); + fullProxyName = javaMaker.proxyClassName; + } - public static synchronized Class<?> makeProxy(Class<?> superclass, - List<Class<?>> vinterfaces, String className, String proxyName, - PyObject dict) { - Class<?>[] interfaces = vinterfaces.toArray(new Class<?>[vinterfaces.size()]); - String fullProxyName; + // Grab the proxy name from the class if it exists, and if it does, override the proxy name + // from the maker if there is one PyObject customProxyName = dict.__finditem__("__javaname__"); if (customProxyName != null) { fullProxyName = Py.tojava(customProxyName, String.class); + if (javaMaker != null) { + javaMaker.proxyClassName = fullProxyName; + } + } + + if (fullProxyName != null) { + // If we've gotten a proxy name, check if it's available on the classpath if (!StringUtil.isJavaClassName(fullProxyName)) { throw Py.TypeError(fullProxyName + " isn't a valid Java class name. Classes " + "must be valid Java identifiers: " + "http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#40625"); } Class<?> proxy = Py.findClass(fullProxyName); + + // TODO - check proxy class version if (proxy != null) { return proxy; } - } else { - fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++; } - String pythonModuleName; - PyObject mn = dict.__finditem__("__module__"); - if (mn == null) { - pythonModuleName = "foo"; + if (javaMaker == null) { + if (fullProxyName == null) { + // This is a purely dynamic proxy + fullProxyName = proxyPrefix + moduleName + "$" + className + "$" + proxyNumber++; + } + javaMaker = new JavaMaker(superclass, interfacesArr, className, moduleName, + fullProxyName, dict); + } + if (!javaMaker.isProxyNeeded()) { + return null; + } + + byte[] bytecode = javaMaker.make(); + if (customProxyName != null || customProxyMaker != null) { + saveBytecode(fullProxyName, bytecode, Py.getSystemState().javaproxy_dir); } else { - pythonModuleName = Py.tojava(mn, String.class); + saveDebugBytecode(fullProxyName, bytecode); } - JavaMaker jm = new JavaMaker(superclass, - interfaces, - className, - pythonModuleName, - fullProxyName, - dict); - try { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - jm.build(bytes); - if (customProxyName != null) { - Py.saveClassFile(fullProxyName, bytes, Py.getSystemState().javaproxy_dir); - } else { - Py.saveClassFile(fullProxyName, bytes); + return makeClass(superclass, interfaces, javaMaker.proxyClassName, bytecode); + } + + private static Class<?> makeClass(Class<?> referent, + List<Class<?>> secondary, + String name, + byte[] bytes) { + List<Class<?>> referents = null; + if (secondary != null) { + if (referent != null) { + secondary.add(0, referent); } + referents = secondary; + } else if (referent != null) { + referents = new ArrayList<Class<?>>(1); + referents.add(referent); + } + return BytecodeLoader.makeClass(name, referents, bytes); + } - return makeClass(superclass, vinterfaces, jm.myClass, bytes); - } catch (Exception exc) { - throw Py.JavaError(exc); + /** Writes the bytecode to the given directory if it's non-null. */ + private static void saveBytecode(String className, byte[] bytecode, String directory) { + if (directory == null) { + return; } + Py.saveClassFile(className, bytecode, directory); } + + /** Writes the bytecode to {@link Options#proxyDebugDirectory} if it's non-null. */ + private static void saveDebugBytecode(String className, byte[] bytecode) { + @SuppressWarnings("deprecation") + String proxyDebugDir = Options.proxyDebugDirectory; + Py.saveClassFile(className, bytecode, proxyDebugDir); + } + + private static final String proxyPrefix = "org.python.proxies."; + + private static int proxyNumber = 0; } Modified: branches/customizable-proxymaker/src/org/python/core/Options.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/Options.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/Options.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -54,8 +54,11 @@ public static int verbose = Py.MESSAGE; /** - * A directory where the dynamically generated classes are written. Nothing is - * ever read from here, it is only for debugging purposes. + * A directory where the dynamically generated classes are written. Nothing is ever read from + * here, it is only for debugging purposes. + * + * @deprecated - Give the proxy a __javaname__ and use {@link PySystemState#javaproxy_dir} + * instead. */ public static String proxyDebugDirectory; Modified: branches/customizable-proxymaker/src/org/python/core/Py.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/Py.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/Py.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -1780,10 +1780,24 @@ maybeWrite(type, msg, DEBUG); } + /** + * Writes a class with the given name contained in the given output stream to + * {@link Options#proxyDebugDirectory} if it's set. + * + * @deprecated {@link Options#proxyDebugDirectory} is deprecated. Use + * {@link #saveClassFile(String, ByteArrayOutputStream, String)} directly instead. + */ public static void saveClassFile(String name, ByteArrayOutputStream bytestream) { saveClassFile(name, bytestream, Options.proxyDebugDirectory); } + /** + * Writes a class with the given name contained in the given output stream to the given + * directory, if it's non-null. + * + * @deprecated Use {@link #saveClassFile(String, ByteArrayOutputStream, String)} directly + * instead. + */ public static void saveClassFile(String name, ByteArrayOutputStream baos, String dirname) { if (dirname == null) { return; @@ -1791,6 +1805,9 @@ saveClassFile(name, baos.toByteArray(), dirname); } + /** + * Writes a class with the given name consisting of the given bytes to the given directory. + */ public static void saveClassFile(String name, byte[] bytes, String dirname) { File dir = new File(dirname); File file = makeFilename(name, dir); Modified: branches/customizable-proxymaker/src/org/python/core/PyBuiltinType.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/PyBuiltinType.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/PyBuiltinType.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -17,7 +17,7 @@ /** Mapping of Java classes to their TypeBuilders. */ private static Map<Class<?>, TypeBuilder> classToBuilder; - PyBuiltinType(PyType pyType) { + protected PyBuiltinType(PyType pyType) { super(pyType); } Modified: branches/customizable-proxymaker/src/org/python/core/PyReflectedFunction.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/PyReflectedFunction.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/PyReflectedFunction.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -113,7 +113,10 @@ } public static boolean isPackagedProtected(Class<?> c) { - int mods = c.getModifiers(); + return isPackagedProtected(c.getModifiers()); + } + + public static boolean isPackagedProtected(int mods) { return !(Modifier.isPublic(mods) || Modifier.isPrivate(mods) || Modifier.isProtected(mods)); } Modified: branches/customizable-proxymaker/src/org/python/core/PyUserType.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/PyUserType.java 2009-08-22 20:23:25 UTC (rev 6706) +++ branches/customizable-proxymaker/src/org/python/core/PyUserType.java 2009-08-22 20:31:42 UTC (rev 6707) @@ -9,7 +9,7 @@ public class PyUserType extends PyType implements ExposeAsSuperclass { - protected PyUserType(PyType metatype, String name, PyTuple bases, PyObject dict) { + public PyUserType(PyType metatype, String name, PyTuple bases, PyObject dict) { super(metatype); this.name = name; this.bases = bases.isEmpty() ? new PyObject[] {PyObject.TYPE} : bases.getArray(); @@ -46,31 +46,11 @@ protected void setupProxy() { List<Class<?>> interfaces = Generic.list(); Class<?> baseProxyClass = getJavaLayout(this.bases, interfaces); - setupProxy(baseProxyClass, interfaces); - } - - /** - * Setup the javaProxy for this type. If baseProxyClass is null and interfaces is empty, no - * proxy class will be created or assigned. - * - * @param baseProxyClass - * this type's base proxyClass or null if there is no base class - * @param interfaces - * a list of Java interfaces this type should implement - */ - protected void setupProxy(Class<?> baseProxyClass, List<Class<?>> interfaces) { - if (baseProxyClass == null && interfaces.isEmpty()) { - // javaProxy not applicable + javaProxy = MakeProxies.makeProxy(baseProxyClass, interfaces, name, dict); + if (javaProxy == null) { return; } - String proxyName = name; - PyObject module = dict.__finditem__("__module__"); - if (module != null) { - proxyName = module.toString() + "$" + proxyName; - } - javaProxy = MakeProxies.makeProxy(baseProxyClass, interfaces, name, proxyName, dict); - PyType proxyType = PyType.fromClass((Class<?>)javaProxy); List<PyObject> cleanedBases = Generic.list(); boolean addedProxyType = false; @@ -97,6 +77,9 @@ } } } + if (!addedProxyType) { + cleanedBases.add(proxyType); + } bases = cleanedBases.toArray(new PyObject[cleanedBases.size()]); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |