From: <cg...@us...> - 2009-08-23 19:05:39
|
Revision: 6712 http://jython.svn.sourceforge.net/jython/?rev=6712&view=rev Author: cgroves Date: 2009-08-23 19:05:32 +0000 (Sun, 23 Aug 2009) Log Message: ----------- Kill __javaname__ as a method of setting the classname on generated proxy classes. Instead, add a couple simple proxy makers to a new module, javaproxymaker, that allow proxy classes to be created with a custom proxy name or with the fully qualfied python module + class name. Also, simplify the callable for __proxymaker__ to take Java superclass, Java interfaces, fully qualified Python class name and python class dict. Modified Paths: -------------- branches/customizable-proxymaker/Lib/test/static_proxy.py branches/customizable-proxymaker/Lib/test/test_java_subclasses.py branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java branches/customizable-proxymaker/src/org/python/core/MakeProxies.java Added Paths: ----------- branches/customizable-proxymaker/Lib/javaproxymaker.py branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py Added: branches/customizable-proxymaker/Lib/javaproxymaker.py =================================================================== --- branches/customizable-proxymaker/Lib/javaproxymaker.py (rev 0) +++ branches/customizable-proxymaker/Lib/javaproxymaker.py 2009-08-23 19:05:32 UTC (rev 6712) @@ -0,0 +1,8 @@ +from org.python.compiler import JavaMaker as NamedProxyMaker + +def make_proxynamer(classname): + def make_proxymaker(superclass, interfaces, pythonClassName, pythonClassDict): + proxymaker = NamedProxyMaker(superclass, interfaces, pythonClassName, pythonClassDict) + proxymaker.proxyClassName = classname + return proxymaker + return make_proxymaker Modified: branches/customizable-proxymaker/Lib/test/static_proxy.py =================================================================== --- branches/customizable-proxymaker/Lib/test/static_proxy.py 2009-08-23 18:45:25 UTC (rev 6711) +++ branches/customizable-proxymaker/Lib/test/static_proxy.py 2009-08-23 19:05:32 UTC (rev 6712) @@ -1,9 +1,10 @@ # Part of test_java_subclasses.StaticProxyCompilationTest. This needs to be its own module # so the statically compiled proxy can import it. from java.lang import Runnable +from javaproxymaker import NamedProxyMaker class RunnableImpl(Runnable): - __javaname__ = "test.static_proxy.RunnableImpl" + __proxymaker__ = NamedProxyMaker def run(self): pass Modified: branches/customizable-proxymaker/Lib/test/test_java_subclasses.py =================================================================== --- branches/customizable-proxymaker/Lib/test/test_java_subclasses.py 2009-08-23 18:45:25 UTC (rev 6711) +++ branches/customizable-proxymaker/Lib/test/test_java_subclasses.py 2009-08-23 19:05:32 UTC (rev 6712) @@ -323,56 +323,10 @@ self.assertEquals(len(called), 1) -class SettingJavaClassNameTest(unittest.TestCase): - def test_setting_name(self): - class Fixedname(Runnable): - __javaname__ = 'name.set.in.Python' - def run(self): - pass - self.assertEquals('name.set.in.Python', Fixedname().getClass().name) - try: - class NumberPackageName(Runnable): - __javaname__ = 'ok.7.ok' - def run(self): - pass - self.fail("Shouldn't be able to set a package name that starts with a digit") - except TypeError: - pass - try: - class LiteralPackageName(Runnable): - __javaname__ = 'ok.true.ok' - def run(self): - pass - self.fail("Shouldn't be able to use a Java literal as a package name") - except TypeError: - pass - -class StaticProxyCompilationTest(unittest.TestCase): - def setUp(self): - self.orig_proxy_dir = sys.javaproxy_dir - sys.javaproxy_dir = tempfile.mkdtemp() - - def tearDown(self): - sys.javaproxy_dir = self.orig_proxy_dir - - def test_proxies_without_classloader(self): - # importing with proxy_dir set compiles RunnableImpl there - import static_proxy - - # Use the existing environment with the proxy dir added on the classpath - env = dict(os.environ) - env["CLASSPATH"] = sys.javaproxy_dir - script = test_support.findfile("import_as_java_class.py") - self.assertEquals(subprocess.call([sys.executable, "-J-Dpython.cachedir.skip=true", - script], env=env), - 0) - def test_main(): test_support.run_unittest(InterfaceTest, TableModelTest, AutoSuperTest, PythonSubclassesTest, AbstractOnSyspathTest, - ContextClassloaderTest, - SettingJavaClassNameTest, - StaticProxyCompilationTest) + ContextClassloaderTest) Added: branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py =================================================================== --- branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py (rev 0) +++ branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py 2009-08-23 19:05:32 UTC (rev 6712) @@ -0,0 +1,59 @@ +import os +import subprocess +import sys +import tempfile +import unittest + +from test import test_support + +from java.lang import Runnable + +from javaproxymaker import make_proxynamer, NamedProxyMaker + +class SettingJavaClassNameTest(unittest.TestCase): + def test_setting_name(self): + class Fixedname(Runnable): + __proxymaker__ = make_proxynamer('name.set.in.Python') + def run(self): + pass + self.assertEquals('name.set.in.Python', Fixedname().getClass().name) + try: + class NumberPackageName(Runnable): + __proxymaker__ = make_proxynamer('ok.7.ok') + def run(self): + pass + self.fail("Shouldn't be able to set a package name that starts with a digit") + except TypeError: + pass + try: + class LiteralPackageName(Runnable): + __proxymaker__ = make_proxynamer('ok.true.ok') + def run(self): + pass + self.fail("Shouldn't be able to use a Java literal as a package name") + except TypeError: + pass + +class StaticProxyCompilationTest(unittest.TestCase): + def setUp(self): + self.orig_proxy_dir = sys.javaproxy_dir + sys.javaproxy_dir = tempfile.mkdtemp() + + def tearDown(self): + sys.javaproxy_dir = self.orig_proxy_dir + + def test_proxies_without_classloader(self): + # importing with proxy_dir set compiles RunnableImpl there + import static_proxy + + # Use the existing environment with the proxy dir added on the classpath + env = dict(os.environ) + env["CLASSPATH"] = sys.javaproxy_dir + script = test_support.findfile("import_as_java_class.py") + self.assertEquals(subprocess.call([sys.executable, "-J-Dpython.cachedir.skip=true", + script], env=env), + 0) + +def test_main(): + test_support.run_unittest(SettingJavaClassNameTest, + StaticProxyCompilationTest) Modified: branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-23 18:45:25 UTC (rev 6711) +++ branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-23 19:05:32 UTC (rev 6712) @@ -8,22 +8,26 @@ public class JavaMaker extends ProxyMaker { - public String pythonClass, pythonModule; + public final String pythonClass, pythonModule; - protected PyObject dict; + public PyObject dict; private final boolean proxyNeeded; public JavaMaker(Class<?> superclass, Class<?>[] interfaces, - String pythonClass, - String pythonModule, - String proxyClassName, + String qualifiedPythonClassName, PyObject pythonClassDict) { - super(proxyClassName, superclass, interfaces); + super(qualifiedPythonClassName, superclass, interfaces); proxyNeeded = superclass != null || interfaces.length > 0; - this.pythonClass = pythonClass; - this.pythonModule = pythonModule; + int lastDot = qualifiedPythonClassName.lastIndexOf('.'); + if (lastDot != -1) { + pythonClass = qualifiedPythonClassName.substring(lastDot + 1); + pythonModule = qualifiedPythonClassName.substring(0, lastDot); + } else { + pythonClass = qualifiedPythonClassName; + pythonModule = ""; + } this.dict = pythonClassDict; } @@ -39,7 +43,6 @@ /** * 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); @@ -50,9 +53,7 @@ @Override public void addProxy() { - if (dict != null) { - super.addProxy(); - } + super.addProxy(); // _initProxy method Code code = classfile.addMethod("__initProxy__", makeSig("V", $objArr), Modifier.PUBLIC); Modified: branches/customizable-proxymaker/src/org/python/core/MakeProxies.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-08-23 18:45:25 UTC (rev 6711) +++ branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-08-23 19:05:32 UTC (rev 6712) @@ -24,17 +24,17 @@ List<Class<?>> interfaces, String className, PyObject dict) { - JavaMaker javaMaker = null; - String fullProxyName = null; - String moduleName; + String qualifiedClassName; PyObject module = dict.__finditem__("__module__"); + if (module == null) { - moduleName = "unknown_module"; + qualifiedClassName = className; } else { - moduleName = Py.tojava(module, String.class); + qualifiedClassName = module + "." + className; } Class<?>[] interfacesArr = interfaces.toArray(new Class<?>[interfaces.size()]); + JavaMaker javaMaker; // 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__"); @@ -42,52 +42,35 @@ if (module == null) { throw Py.TypeError("Classes using __proxymaker__ must define __module__"); } - PyObject[] args = Py.javas2pys(superclass, interfacesArr, className, moduleName, dict); + PyObject[] args = Py.javas2pys(superclass, interfacesArr, qualifiedClassName, dict); javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); - fullProxyName = javaMaker.proxyClassName; - } - - // 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 (!StringUtil.isJavaClassName(javaMaker.proxyClassName)) { + throw Py.TypeError(javaMaker.proxyClassName + " 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"); } - } - 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); + Class<?> proxy = Py.findClass(javaMaker.proxyClassName); // TODO - check proxy class version if (proxy != null) { return proxy; } + } else { + javaMaker = new JavaMaker(superclass, interfacesArr, qualifiedClassName, dict); + // This is a purely dynamic proxy + javaMaker.proxyClassName = proxyPrefix + qualifiedClassName + "$" + proxyNumber++; } - 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); + if (customProxyMaker != null) { + saveBytecode(javaMaker.proxyClassName, bytecode, Py.getSystemState().javaproxy_dir); } else { - saveDebugBytecode(fullProxyName, bytecode); + saveDebugBytecode(javaMaker.proxyClassName, bytecode); } return makeClass(superclass, interfaces, javaMaker.proxyClassName, bytecode); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cg...@us...> - 2009-08-24 00:11:03
|
Revision: 6716 http://jython.svn.sourceforge.net/jython/?rev=6716&view=rev Author: cgroves Date: 2009-08-24 00:10:42 +0000 (Mon, 24 Aug 2009) Log Message: ----------- Split out visiting and adding a method in a proxy, add a proxy maker that requires all abstract and interface methods be implemented in the Python class, and another overrides all possible superclass methods to allow the Python to method assignment and lookup at runtime. Modified Paths: -------------- branches/customizable-proxymaker/Lib/javaproxymaker.py branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java Modified: branches/customizable-proxymaker/Lib/javaproxymaker.py =================================================================== --- branches/customizable-proxymaker/Lib/javaproxymaker.py 2009-08-23 22:47:12 UTC (rev 6715) +++ branches/customizable-proxymaker/Lib/javaproxymaker.py 2009-08-24 00:10:42 UTC (rev 6716) @@ -1,8 +1,33 @@ +from java.lang.reflect import Modifier from org.python.compiler import JavaMaker as NamedProxyMaker def make_proxynamer(classname): + '''Creates a callable for use in __proxymaker__ that names the generated classes the given name + + If the given name is an invalid Java class name, an exception will be raised at class definition + time.''' def make_proxymaker(superclass, interfaces, pythonClassName, pythonClassDict): proxymaker = NamedProxyMaker(superclass, interfaces, pythonClassName, pythonClassDict) proxymaker.proxyClassName = classname return proxymaker return make_proxymaker + +class StrictProxyMaker(NamedProxyMaker): + '''Requires the Python class implement every abstract or interface Java method. + + If the Python class doesn't have a definition for an abstract or interface method on the class + itself, a TypeError will be raised at class definition time.''' + def visitMethod(self, meth): + if Modifier.isAbstract(meth.modifiers) and not meth.name in self.dict: + raise TypeError("%s must implement %s for %s" % + (self.proxyClassName, meth.name, meth.declaringClass)) + self.super__visitMethod(meth) + +class AllMethodProxyMaker(NamedProxyMaker): + '''Implements every overridable method in the proxy class + + This means that if a Python class doesn't implement a method in its definition, it can still + handle it from __getattribute__ unlike from regular proxy classes. Also, if the overriden + method is abstract and isn't handled by the Python class, a noop return will be performed.''' + def visitMethod(self, meth): + self.addMethod(meth) Modified: branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py =================================================================== --- branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py 2009-08-23 22:47:12 UTC (rev 6715) +++ branches/customizable-proxymaker/Lib/test/test_javaproxymaker.py 2009-08-24 00:10:42 UTC (rev 6716) @@ -8,9 +8,11 @@ from java.lang import Runnable -from javaproxymaker import make_proxynamer, NamedProxyMaker +from org.python.tests import Callbacker -class SettingJavaClassNameTest(unittest.TestCase): +from javaproxymaker import make_proxynamer, AllMethodProxyMaker, NamedProxyMaker, StrictProxyMaker + +class CustomProxyMakerTest(unittest.TestCase): def test_setting_name(self): class Fixedname(Runnable): __proxymaker__ = make_proxynamer('name.set.in.Python') @@ -34,6 +36,35 @@ except TypeError: pass + def test_strict_subclass(self): + try: + class NoRunRunnable(Runnable): + __proxymaker__ = StrictProxyMaker + self.fail("Not implementing Run should throw a TypeError") + except TypeError: + pass + class RunRunnable(Runnable): + __proxymaker__ = StrictProxyMaker + def run(self): + pass + + def test_all_method_subclass(self): + calls = [] + class RuntimeCallbacker(Callbacker.Callback): + __proxymaker__ = AllMethodProxyMaker + + def __getattribute__(self, name): + if name == 'call': + def call(arg=None): + calls.append(arg) + return call + else: + return getattr(self, name) + Callbacker.callNoArg(RuntimeCallbacker()) + self.assertEquals(None, calls[0]) + Callbacker.callOneArg(RuntimeCallbacker(), 12) + self.assertEquals(12, calls[1]) + class StaticProxyCompilationTest(unittest.TestCase): def setUp(self): self.orig_proxy_dir = sys.javaproxy_dir @@ -55,5 +86,4 @@ 0) def test_main(): - test_support.run_unittest(SettingJavaClassNameTest, - StaticProxyCompilationTest) + test_support.run_unittest(CustomProxyMakerTest, StaticProxyCompilationTest) Modified: branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-08-23 22:47:12 UTC (rev 6715) +++ branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-08-24 00:10:42 UTC (rev 6716) @@ -16,15 +16,15 @@ @Override protected void build() { classfile.addInterface(mapClass(interfaces[0])); - addMethods(interfaces[0], new HashSet<MethodDescr>()); - addConstructors(); + visitMethods(interfaces[0], new HashSet<MethodDescr>()); + visitConstructors(); for (String name : names) { classfile.addField(name, $pyObj, Opcodes.ACC_PUBLIC); } } @Override - protected void addMethod(Method method, int access) { + protected void addMethod(Method method) { Class<?>[] parameters = method.getParameterTypes(); Class<?> ret = method.getReturnType(); String name = method.getName(); Modified: branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-23 22:47:12 UTC (rev 6715) +++ branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-08-24 00:10:42 UTC (rev 6716) @@ -69,14 +69,13 @@ } @Override - public void addMethod(Method method, int access) { - if (Modifier.isAbstract(access)) { - // TODO - Allow proxies to determine their handling of missing abstract methods - super.addMethod(method, access); + public void visitMethod(Method method) { + if (Modifier.isAbstract(method.getModifiers())) { + super.visitMethod(method); } else if (dict.__finditem__(method.getName().intern()) != null) { - super.addMethod(method, access); + super.visitMethod(method); } else if (Modifier.isProtected(method.getModifiers())) { - addSuperMethod(method, access); + addSuperMethod(method); } } Modified: branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-08-23 22:47:12 UTC (rev 6715) +++ branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-08-24 00:10:42 UTC (rev 6716) @@ -115,10 +115,10 @@ */ protected void build() { addProxy(); - addConstructors(); + visitConstructors(); classfile.addInterface("org/python/core/PyProxy"); - addMethods(); + visitMethods(); addClassDictInit(); } @@ -335,18 +335,13 @@ * <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(modifier)) { - modifier = modifier & ~Modifier.ABSTRACT; - isAbstract = true; - } + protected void addMethod(Method method) { + boolean isAbstract = Modifier.isAbstract(method.getModifiers()); + addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), - method.getExceptionTypes(), modifier, isAbstract ? null : method.getDeclaringClass()); + method.getExceptionTypes(), Modifier.PUBLIC, + isAbstract ? null : method.getDeclaringClass()); } /** @@ -394,7 +389,7 @@ code.label(callPython); code.aload(tmp); callMethod(code, name, parameters, ret, exceptions); - addSuperMethod("super__" + name, name, superClass, parameters, ret, access); + addSuperMethod("super__" + name, name, superClass, parameters, ret); } else { code.invokestatic("org/python/compiler/ProxyMaker", "findPython", makeSig($pyObj, $pyProxy, $str)); @@ -466,8 +461,12 @@ } } - protected void addMethods(Class<?> c, Set<MethodDescr> t) { - for (Method method : c.getDeclaredMethods()) { + /** + * 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, Set<MethodDescr> seen) { + for (Method method : klass.getDeclaredMethods()) { int access = method.getModifiers(); if (Modifier.isStatic(access) || Modifier.isPrivate(access) || @@ -480,38 +479,40 @@ continue; } - if (!t.add(new MethodDescr(method))) { + if (!seen.add(new MethodDescr(method))) { // A method with the same signature has already been added, skip this. continue; } - if (Modifier.isNative(access)) { - 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)) { - addSuperMethod(method, access); - continue; + if (Modifier.isFinal(access)) { + if (Modifier.isProtected(access)) { + addSuperMethod(method); } - } else if (Modifier.isFinal(access)) { continue; } - addMethod(method, access); + visitMethod(method); } - Class<?> sc = c.getSuperclass(); - if (sc != null) { - addMethods(sc, t); + Class<?> superClass = klass.getSuperclass(); + if (superClass != null) { + visitMethods(superClass, seen); } - for (Class<?> iface : c.getInterfaces()) { - addMethods(iface, t); + for (Class<?> iface : klass.getInterfaces()) { + visitMethods(iface, seen); } } + /** + * 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) { + addMethod(method); + } + /** Adds a constructor that calls through to superclass. */ protected void addConstructor(Class<?>[] parameters, int access) { String sig = makeSig(Void.TYPE, parameters); @@ -520,12 +521,11 @@ } /** - * Adds constructors from this proxy's superclass. + * Visits constructors from this proxy's superclass. */ - protected Set<ConstructorDescr> addConstructors() { + protected Set<ConstructorDescr> visitConstructors() { Set<ConstructorDescr> added = Generic.set(); - Constructor<?>[] constructors = superclass.getDeclaredConstructors(); - for (Constructor<?> constructor : constructors) { + for (Constructor<?> constructor : superclass.getDeclaredConstructors()) { int access = constructor.getModifiers(); if (Modifier.isPrivate(access)) { continue; @@ -556,25 +556,23 @@ // super__ prefix. This gives access to super. version or the // method. // - protected void addSuperMethod(Method method, int access) { + protected void addSuperMethod(Method method) { Class<?>[] parameters = method.getParameterTypes(); Class<?> ret = method.getReturnType(); String superClass = mapClass(method.getDeclaringClass()); String superName = method.getName(); String methodName = superName; - if (Modifier.isFinal(access)) { + if (Modifier.isFinal(method.getModifiers())) { methodName = "super__" + superName; - access &= ~Modifier.FINAL; } - addSuperMethod(methodName, superName, superClass, parameters, ret, access); + addSuperMethod(methodName, superName, superClass, parameters, ret); } protected void addSuperMethod(String methodName, String superName, String declClass, Class<?>[] parameters, - Class<?> ret, - int access) { + Class<?> ret) { if (methodName.startsWith("super__")) { /* rationale: JC java-class, P proxy-class subclassing JC in order to avoid infinite recursion P should define super__foo @@ -592,7 +590,7 @@ } } supernames.add(methodName); - Code code = classfile.addMethod(methodName, makeSig(ret, parameters), access); + Code code = classfile.addMethod(methodName, makeSig(ret, parameters), Modifier.PUBLIC); callSuper(code, superName, declClass, parameters, ret, true); } @@ -645,16 +643,16 @@ code.return_(); } - protected Set<MethodDescr> addMethods() { + protected Set<MethodDescr> visitMethods() { Set<MethodDescr> seenmethods = Generic.set(); - addMethods(superclass, seenmethods); + visitMethods(superclass, seenmethods); 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, seenmethods); } return seenmethods; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cg...@us...> - 2009-09-06 09:42:15
|
Revision: 6760 http://jython.svn.sourceforge.net/jython/?rev=6760&view=rev Author: cgroves Date: 2009-09-06 09:42:02 +0000 (Sun, 06 Sep 2009) Log Message: ----------- Kill the JavaMaker/ProxyMaker distinction to collect the set of proxy code generation methods together, and unify the actual code generation around visit methods for each type of thing a proxy class might be interested in. Hopefully this is leading to more straightforward subclasses. Modified Paths: -------------- branches/customizable-proxymaker/Lib/javaproxymaker.py branches/customizable-proxymaker/Lib/test/test_class_jy.py 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/Module.java branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java branches/customizable-proxymaker/src/org/python/core/MakeProxies.java Added Paths: ----------- branches/customizable-proxymaker/src/org/python/compiler/VerifyingClassFile.java Removed Paths: ------------- branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java Modified: branches/customizable-proxymaker/Lib/javaproxymaker.py =================================================================== --- branches/customizable-proxymaker/Lib/javaproxymaker.py 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/Lib/javaproxymaker.py 2009-09-06 09:42:02 UTC (rev 6760) @@ -1,15 +1,15 @@ from java.lang.reflect import Modifier from org.python.compiler import JavaMaker as NamedProxyMaker +from org.python.core.util import StringUtil def make_proxynamer(classname): '''Creates a callable for use in __proxymaker__ that names the generated classes the given name - If the given name is an invalid Java class name, an exception will be raised at class definition - time.''' + If the given name is an invalid Java class name, a ValueError will be raised.''' + if not StringUtil.isJavaClassName(classname): + raise ValueError("%s is not a valid Java class name. See http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#40625" % classname) def make_proxymaker(superclass, interfaces, pythonClassName, pythonClassDict): - proxymaker = NamedProxyMaker(superclass, interfaces, pythonClassName, pythonClassDict) - proxymaker.proxyClassName = classname - return proxymaker + return NamedProxyMaker(superclass, interfaces, pythonClassName, pythonClassDict, classname) return make_proxymaker class StrictProxyMaker(NamedProxyMaker): @@ -30,4 +30,4 @@ handle it from __getattribute__ unlike from regular proxy classes. Also, if the overriden method is abstract and isn't handled by the Python class, a noop return will be performed.''' def visitMethod(self, meth): - self.addMethod(meth) + self.addOverrideMethod(meth) Modified: branches/customizable-proxymaker/Lib/test/test_class_jy.py =================================================================== --- branches/customizable-proxymaker/Lib/test/test_class_jy.py 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/Lib/test/test_class_jy.py 2009-09-06 09:42:02 UTC (rev 6760) @@ -39,7 +39,7 @@ self.assertEqual(str(cls), "<class '%s.%s'>" % (__name__, cls.__name__)) self.assertEqual(repr(cls), "<class '%s.%s'>" % (__name__, cls.__name__)) self.assert_(str(Bar()).startswith('<%s.Bar object at' % __name__)) - self.assert_(str(Baz()).startswith("org.python.proxies.%s$Baz" % __name__)) + self.assert_(str(Baz()).startswith("org.python.proxies.%s.Baz$" % __name__)) def test_builtin_attributes(self): for attr, val in dict(__name__='foo', __module__='bar', __dict__={}, Modified: branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/AdapterMaker.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -1,9 +1,8 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.compiler; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.HashSet; - import org.objectweb.asm.Label; public class AdapterMaker extends ProxyMaker { @@ -13,23 +12,19 @@ } @Override - protected void build() { - classfile.addInterface(mapClass(interfaces[0])); - visitMethods(interfaces[0], new HashSet<MethodDescr>()); - try { - addConstructor(Object.class.getConstructor()); - } catch (NoSuchMethodException e) { - throw new RuntimeException( - "Object.class doesn't have a no-arg constructor? What the dickens?", e); - } + public void visitConstructor(Constructor<?> constructor) { + doReturn(addOpenConstructor(constructor), superclass); } @Override - protected void addOverrideMethod(Method method) { + protected void visitMethod(Method method) { + if (!method.getDeclaringClass().isInterface()) { + return; + } Class<?>[] parameters = method.getParameterTypes(); Class<?> ret = method.getReturnType(); String name = method.getName(); - Code code = classfile.addMethod(name, makeSig(ret, parameters), ACC_PUBLIC); + Code code = classfile.addMethod(ACC_PUBLIC, ret, name, parameters); code.aload(0); code.getfield(classfile.name, name, $pyObj); code.dup(); Modified: branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/ClassFile.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -9,7 +9,6 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; - import org.python.core.imp; import org.python.util.Generic; @@ -34,18 +33,6 @@ * - The name of the class in internal form. * @param superclass * - The name of the superclass of the generated class in internal form. - */ - public ClassFile(String name, String superclass) { - this(name, superclass, imp.NO_MTIME, null); - } - - /** - * Creates a generator for a class of the given name. - * - * @param name - * - The name of the class in internal form. - * @param superclass - * - The name of the superclass of the generated class in internal form. * @param mtime * - the time the source was last modified, or {@link imp#NO_MTIME} if it's unknown. * @param sourceFilename @@ -77,7 +64,7 @@ * @param access * - the method's access level as constructed from {@link Opcodes}. */ - public Code addMethod(String name, String descriptor, int access, String... exceptions) { + protected Code addMethod(String name, String descriptor, int access, String... exceptions) { MethodVisitor mv = cw.visitMethod(access, name, descriptor, null, exceptions); Code pmv = new Code(mv, descriptor, access); methodVisitors.add(pmv); @@ -106,8 +93,6 @@ cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, name, null, superclass, interfaces.toArray(new String[interfaces.size()])); 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? av.visit("value", imp.getAPIVersion()); av.visitEnd(); Deleted: branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/JavaMaker.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -1,88 +0,0 @@ -// Copyright (c) Corporation for National Research Initiatives -package org.python.compiler; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import org.python.core.PyObject; - -public class JavaMaker extends ProxyMaker { - - public final String pythonClass, pythonModule; - - public PyObject dict; - - private final boolean proxyNeeded; - - public JavaMaker(Class<?> superclass, - Class<?>[] interfaces, - String qualifiedPythonClassName, - PyObject pythonClassDict) { - super(qualifiedPythonClassName, superclass, interfaces); - proxyNeeded = superclass != null || interfaces.length > 0; - int lastDot = qualifiedPythonClassName.lastIndexOf('.'); - if (lastDot != -1) { - pythonClass = qualifiedPythonClassName.substring(lastDot + 1); - pythonModule = qualifiedPythonClassName.substring(0, lastDot); - } else { - pythonClass = qualifiedPythonClassName; - pythonModule = ""; - } - this.dict = pythonClassDict; - } - - @Override - protected void build() { - super.build(); - - addInitProxy(); - } - - @Override - public void visitConstructor(Constructor<?> constructor) - { - /* Need a fancy constructor for the Java side of things */ - Code code = addOpenConstructor(constructor); - callInitProxy(constructor.getParameterTypes(), 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)); - code.visitInsn(RETURN); - } - - protected void addInitProxy() { - Code code = classfile.addMethod("__initProxy__", makeSig("V", $objArr), Modifier.PUBLIC); - - code.visitVarInsn(ALOAD, 0); - code.visitLdcInsn(pythonModule); - code.visitLdcInsn(pythonClass); - code.visitVarInsn(ALOAD, 1); - code.visitMethodInsn(INVOKESTATIC, "org/python/core/Py", "initProxy", - makeSig("V", $pyProxy, $str, $str, $objArr)); - code.visitInsn(RETURN); - - } - - @Override - public void visitMethod(Method method) { - if (Modifier.isAbstract(method.getModifiers())) { - super.visitMethod(method); - } else if (dict.__finditem__(method.getName().intern()) != null) { - super.visitMethod(method); - } else if (Modifier.isProtected(method.getModifiers())) { - addSuperMethod(method); - } - } - - public boolean isProxyNeeded() { - return proxyNeeded; - } -} Modified: branches/customizable-proxymaker/src/org/python/compiler/Module.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/Module.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/Module.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -24,6 +24,7 @@ import org.python.core.Py; import org.python.core.PyException; import org.python.core.PyRunnableBootstrap; +import org.python.core.imp; import org.python.core.util.StringUtil; class PyIntegerConstant extends Constant implements ClassConstants, Opcodes @@ -306,7 +307,8 @@ public Module(String name, String filename, boolean linenumbers, long mtime) { this.linenumbers = linenumbers; this.mtime = mtime; - classfile = new ClassFile(name, "org/python/core/PyFunctionTable", mtime, sfilename); + classfile = new ClassFile(ProxyCodeHelpers.mapClass(name), + "org/python/core/PyFunctionTable", mtime, sfilename); constants = new Hashtable<Constant,Constant>(); sfilename = filename; codes = new ArrayList<PyCodeConstant>(); @@ -622,13 +624,15 @@ } throw new ParseException(msg,node); } - public static void compile(mod node, OutputStream ostream, - String name, String filename, - boolean linenumbers, boolean printResults, - CompilerFlags cflags) - throws Exception - { - compile(node, ostream, name, filename, linenumbers, printResults, cflags, org.python.core.imp.NO_MTIME); + + public static void compile(mod node, + OutputStream ostream, + String name, + String filename, + boolean linenumbers, + boolean printResults, + CompilerFlags cflags) throws Exception { + compile(node, ostream, name, filename, linenumbers, printResults, cflags, imp.NO_MTIME); } public static void compile(mod node, OutputStream ostream, Modified: branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/ProxyCodeHelpers.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -251,7 +251,7 @@ public static void callSuper(Code code, String name, - String superclass, + Class<?> superclass, Class<?>[] parameters, Class<?> ret, boolean doReturn) { @@ -286,7 +286,7 @@ break; } } - code.invokespecial(superclass, name, makeSig(ret, parameters)); + code.invokespecial(mapClass(superclass), name, makeSig(ret, parameters)); if (doReturn) { doReturn(code, ret); Modified: branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/compiler/ProxyMaker.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -4,15 +4,20 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; + +import java.util.List; import java.util.Set; import org.objectweb.asm.Label; +import org.python.core.ClassDictInit; 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.core.PySystemState; import org.python.util.Generic; +import org.python.util.Preconditions; public class ProxyMaker extends ProxyCodeHelpers { /** @@ -42,72 +47,141 @@ return ret; } + /** The name of the class to build. */ + public final String proxyClassName; + protected VerifyingClassFile classfile; protected final Class<?> superclass; - protected final Class<?>[] interfaces; - private final Set<String> names = Generic.set(); + protected final List<Class<?>> interfaces = Generic.list(); private final Set<String> supernames = Generic.set(); - protected ClassFile classfile; - /** The name of the class to build. */ - public String proxyClassName; + private final boolean proxyNeeded; + public final String pythonClass, pythonModule; + public final PyObject dict; + /** * Creates a proxy class maker that produces classes named <code>proxyClassName</code> that * extends <code>superclass</code> and implements the interfaces in <code>interfaces</code>. */ - public ProxyMaker(String proxyClassName, Class<?> superclass, Class<?>... interfaces) { + ProxyMaker(String proxyClassName, Class<?> superclass, Class<?>... interfaces) { + this(superclass, interfaces, null, null, proxyClassName); + } + + public ProxyMaker(Class<?> superclass, + Class<?>[] interfaces, + String qualifiedPythonClassName, + PyObject pythonClassDict) { + this(superclass, interfaces, qualifiedPythonClassName, pythonClassDict, + qualifiedPythonClassName); + } + + public ProxyMaker(Class<?> superclass, + Class<?>[] interfaces, + String qualifiedPythonClassName, + PyObject pythonClassDict, + String proxyClassName) { this.proxyClassName = proxyClassName; + proxyNeeded = superclass != null || interfaces.length > 0; 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()); - } + Preconditions.checkArgument(!superclass.isInterface(), "Given an interface, " + + superclass.getName() + ", for a proxy superclass"); + Preconditions.checkArgument((superclass.getModifiers() & Modifier.FINAL) == 0, + "Can't subclass final class " + superclass.getName()); this.superclass = superclass; - if (interfaces == null) { - interfaces = new Class[0]; - } - for (Class<?> interfac : interfaces) { - if (!interfac.isInterface()) { - throw new IllegalArgumentException( - "All classes in the interfaces array must be interfaces, unlike " - + interfac.getName()); + for (Class<?> iface : interfaces) { + Preconditions.checkArgument(iface.isInterface(), + "Asked to add non-interface to proxy class as interface: " + iface.getName()); + if (iface.isAssignableFrom(superclass)) { + Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName()); } + this.interfaces.add(iface); } - this.interfaces = interfaces; + int lastDot = qualifiedPythonClassName.lastIndexOf('.'); + if (lastDot != -1) { + pythonClass = qualifiedPythonClassName.substring(lastDot + 1); + pythonModule = qualifiedPythonClassName.substring(0, lastDot); + } else { + pythonClass = qualifiedPythonClassName; + pythonModule = ""; + } + this.dict = pythonClassDict; } + public boolean isProxyNeeded() { + return proxyNeeded; + } + /** * Builds this proxy and returns its bytecode. */ public synchronized byte[] make() { - classfile = new ClassFile(proxyClassName, mapClass(superclass)); - build(); + classfile = new VerifyingClassFile(mapClass(proxyClassName), mapClass(superclass)); + addProxy(); + for (Constructor<?> constructor : superclass.getDeclaredConstructors()) { + if (Modifier.isPrivate(constructor.getModifiers())) { + continue; + } + classfile.noteSuperclassConstructor(constructor); + visitConstructor(constructor); + } + visitMethods(superclass); + for (Class<?> iface : interfaces) { + classfile.addInterface(mapClass(iface)); + visitMethods(iface); + } + didVisits(); + addClassDictInit(); + supernames.clear(); byte[] result = classfile.create(); classfile = null; return result; } /** - * 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. + * Called for every constructor on the proxy's superclass that can be overridden by + * the proxy class. */ - protected void build() { - addProxy(); - visitConstructors(); - classfile.addInterface("org/python/core/PyProxy"); + protected void visitConstructor(Constructor<?> constructor) + { + /* Need a fancy constructor for the Java side of things */ + callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor)); + } - visitMethods(); - addClassDictInit(); - names.clear(); - supernames.clear(); + /** + * Called for every method on the proxy's superclass and interfaces that can be overridden by + * the proxy class. If the proxy wants to perform Python lookup and calling for the method, + * {@link #addOverrideMethod(Method)} should be called. For abstract methods, a method matching + * this signature must be added to the proxy class. + * <p> + * ProxyMaker's implementation of this method calls <code>addOverrideMethod</code> if the given + * method is abstract or if the Python dict contains an item for the method's name. If the + * method is protected, a super method is added. Otherwise, it doesn't add a method with this + * signature on the proxy class. + */ + protected void visitMethod(Method method) { + if (Modifier.isAbstract(method.getModifiers())) { + addOverrideMethod(method); + } else if (dict.__finditem__(method.getName().intern()) != null) { + addOverrideMethod(method); + } else if (Modifier.isProtected(method.getModifiers())) { + addSuperMethod(method, false); + } } + protected void visitFinalMethod(Method method) { + if (Modifier.isProtected(method.getModifiers())) { + addSuperMethod(method, true); + } + } + /** + * Called as part of {@link #make} after the visit methods have been called for every + * constructor and method. + */ + protected void didVisits() {} + + /** * 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 @@ -115,7 +189,6 @@ */ protected void addOverrideMethod(Method method) { boolean isAbstract = Modifier.isAbstract(method.getModifiers()); - addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), method.getExceptionTypes(), Modifier.PUBLIC, isAbstract ? null : method.getDeclaringClass()); @@ -123,33 +196,113 @@ /** * 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. + * will call an object of the method's name in the Python object. */ - protected void addMethod(String name, - Class<?> ret, - Class<?>[] parameters, + protected void addMethod(String name, Class<?> ret, Class<?>[] parameters, Class<?>[] exceptions) { addMethod(name, ret, parameters, exceptions, Modifier.PUBLIC, null); } /** - * 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. + * Adds a "super" method to the class being generated. A super method is a public method that + * calls through to the method with the same signature on a superclass. If + * <code>prefixWithSuper</code> is given, super__ is prefixed to the method name on this class. + * <p> + * ProxyMaker automatically generates super methods with the prefix in two cases: + * <p> + * <ol> + * <li>for protected final methods to expose the method to Python without overriding a final + * method.</li> + * <li>for methods overridden for Python to give access to the superclass method from Python.</li> + * </ol> + * + * All other super method generation is under the control of subclasses. */ + protected void addSuperMethod(Method method, boolean prefixWithSuper) { + String methodName = method.getName(); + if (prefixWithSuper) { + methodName = "super__" + methodName; + } + addSuperMethod(methodName, method.getName(), method.getDeclaringClass(), + method.getParameterTypes(), method.getReturnType()); + } + + + /** + * 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)); + code.visitInsn(RETURN); + } + + /** + * 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) { + Code code = classfile.addMethod(ACC_PUBLIC, Void.TYPE, "<init>", constructor + .getParameterTypes(), constructor.getExceptionTypes()); + callSuper(code, "<init>", superclass, constructor.getParameterTypes(), Void.TYPE, false); + return code; + } + + /** + * Visits all methods declared on the given class and classes in its inheritance hierarchy. + * Methods visible to subclasses are added to the classfile. + */ + private void visitMethods(Class<?> klass) { + for (Method method : klass.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; + } else if (classfile.noteSuperclassMethod(method)) { + // No method with the same signature has been added, check if we want to + if (Modifier.isFinal(access)) { + visitFinalMethod(method); + } else { + visitMethod(method); + } + } + } + + Class<?> superClass = klass.getSuperclass(); + if (superClass != null) { + visitMethods(superClass); + } + + for (Class<?> iface : klass.getInterfaces()) { + visitMethods(iface); + } + } + + /** + * Adds a method of the given name to the class being implemented that attempts to find a + * callable with the given name in the Python instance and calls through to it. + * <p> + * If <code>declaringClass</code> is null, the Python instance must provide an implementation. + * If it isn't null, a super__ method will be added for this method, and if a Python callable + * isn't found under the method's name, the super method will be called. No checking is done to + * guarantee that the superclass has a method with the same signature. + */ private void addMethod(String name, Class<?> ret, Class<?>[] parameters, Class<?>[] exceptions, int access, Class<?> declaringClass) { - names.add(name); - Code code = classfile.addMethod(name, makeSig(ret, parameters), access, - mapClasses(exceptions)); + Code code = classfile.addMethod(access, ret, name, parameters, exceptions); code.aload(0); code.ldc(name); if (declaringClass != null) { @@ -160,12 +313,11 @@ code.aload(tmp); Label callPython = new Label(); code.ifnonnull(callPython); - String superClass = mapClass(declaringClass); - callSuper(code, name, superClass, parameters, ret, true); + callSuper(code, name, declaringClass, parameters, ret, true); code.label(callPython); code.aload(tmp); callMethod(code, name, parameters, ret, exceptions); - addSuperMethod("super__" + name, name, superClass, parameters, ret); + addSuperMethod("super__" + name, name, declaringClass, parameters, ret); } else { code.invokestatic("org/python/compiler/ProxyMaker", "findPython", makeSig($pyObj, $pyProxy, $str)); @@ -179,143 +331,9 @@ } } - 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()); - } - - 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 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); - } - } - - /** Adds a constructor that calls through to superclass and returns immediately. */ - protected void addConstructor(Constructor<?> constructor) { - Code code = addOpenConstructor(constructor); - doReturn(code, superclass); - } - - /** - * Adds a constructor that calls through to superclass and leaves the returned Code open for - * more operations. The caller of this method must add a return to the returned Code. - */ - protected Code addOpenConstructor(Constructor<?> constructor) { - String sig = makeSig(Void.TYPE, constructor.getParameterTypes()); - Code code = classfile.addMethod("<init>", sig, ACC_PUBLIC, - mapClasses(constructor.getExceptionTypes())); - callSuper(code, "<init>", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, - false); - return code; - } - - /** - * Visits constructors from this proxy's superclass. - */ - private void visitConstructors() { - for (Constructor<?> constructor : superclass.getDeclaredConstructors()) { - if (Modifier.isPrivate(constructor.getModifiers())) { - continue; - } - visitConstructor(constructor); - } - } - - /** - * Called for every method on the proxy's superclass and interfaces that can be overridden by - * the proxy class. If the proxy wants to perform Python lookup and calling for the method, - * {@link #addOverrideMethod(Method)} should be called. For abstract methods, addMethod must be - * called. - */ - protected void visitMethod(Method method) { - addOverrideMethod(method); - } - - - /** - * Called for every constructor on the proxy's superclass that can be overridden by - * the proxy class. - */ - protected void visitConstructor(Constructor<?> constructor) { - addConstructor(constructor); - } - - // Super methods are added for the following three reasons: - // - // 1) for a protected non-final method add a public method with no - // super__ prefix. This gives needed access to this method for - // subclasses - // - // 2) for protected final methods, add a public method with the - // super__ prefix. This avoids the danger of trying to override a - // final method - // - // 3) For any other method that is overridden, add a method with the - // super__ prefix. This gives access to super. version or the - // method. - // - protected void addSuperMethod(Method method) { - Class<?>[] parameters = method.getParameterTypes(); - Class<?> ret = method.getReturnType(); - String superClass = mapClass(method.getDeclaringClass()); - String superName = method.getName(); - String methodName = superName; - if (Modifier.isFinal(method.getModifiers())) { - methodName = "super__" + superName; - } - addSuperMethod(methodName, superName, superClass, parameters, ret); - } - private void addSuperMethod(String methodName, String superName, - String declClass, + Class<?> declClass, Class<?>[] parameters, Class<?> ret) { if (methodName.startsWith("super__")) { @@ -330,53 +348,59 @@ return; } catch (NoSuchMethodException e) { // OK, no one else defines it, so we need to - } catch (SecurityException e) { - return; } } supernames.add(methodName); - Code code = classfile.addMethod(methodName, makeSig(ret, parameters), Modifier.PUBLIC); + Code code = classfile.addMethod(ACC_PUBLIC, ret, methodName, parameters); callSuper(code, superName, declClass, parameters, ret, true); } - /** - * Adds the methods and fields necessary to implement PyProxy. - */ private void addProxy() { + if (dict == null) { + return; + } + classfile.addInterface(mapClass(PyProxy.class)); classfile.addField("__proxy", $pyObj, Modifier.PROTECTED); - Code code = classfile.addMethod("_setPyInstance", makeSig("V", $pyObj), Modifier.PUBLIC); + Code code = classfile.addMethod(ACC_PUBLIC, Void.TYPE, "_setPyInstance", PyObject.class); code.aload(0); code.aload(1); code.putfield(classfile.name, "__proxy", $pyObj); code.return_(); - - code = classfile.addMethod("_getPyInstance", makeSig($pyObj), Modifier.PUBLIC); + code = classfile.addMethod(ACC_PUBLIC, PyObject.class, "_getPyInstance"); code.aload(0); code.getfield(classfile.name, "__proxy", $pyObj); code.areturn(); - - String pySys = "Lorg/python/core/PySystemState;"; + String pySys = "Lorg/python/core/PySystemState;"; classfile.addField("__systemState", pySys, Modifier.PROTECTED | Modifier.TRANSIENT); - - code = classfile.addMethod("_setPySystemState", makeSig("V", pySys), Modifier.PUBLIC); + code = classfile.addMethod(ACC_PUBLIC, Void.TYPE, "_setPySystemState", PySystemState.class); code.aload(0); code.aload(1); code.putfield(classfile.name, "__systemState", pySys); code.return_(); - - code = classfile.addMethod("_getPySystemState", makeSig(pySys), Modifier.PUBLIC); + code = classfile.addMethod(ACC_PUBLIC, PySystemState.class, "_getPySystemState"); code.aload(0); code.getfield(classfile.name, "__systemState", pySys); code.areturn(); + code = classfile.addMethod(ACC_PUBLIC, Void.TYPE, "__initProxy__", Object[].class); + code.visitVarInsn(ALOAD, 0); + code.visitLdcInsn(pythonModule); + code.visitLdcInsn(pythonClass); + code.visitVarInsn(ALOAD, 1); + code.visitMethodInsn(INVOKESTATIC, "org/python/core/Py", "initProxy", + makeSig("V", $pyProxy, $str, $str, $objArr)); + code.visitInsn(RETURN); } /** * Adds the classDictInit static method to fill in __supernames__ on the class' dict */ private void addClassDictInit() { - classfile.addInterface(mapClass(org.python.core.ClassDictInit.class)); - Code code = classfile.addMethod("classDictInit", makeSig("V", $pyObj), - Modifier.PUBLIC | Modifier.STATIC); + if (dict == null) { + return; + } + classfile.addInterface(mapClass(ClassDictInit.class)); + Code code = classfile.addMethod(ACC_PUBLIC | ACC_STATIC, Void.TYPE, "classDictInit", + PyObject.class); code.aload(0); code.ldc("__supernames__"); @@ -387,61 +411,4 @@ code.invokevirtual("org/python/core/PyObject", "__setitem__", makeSig("V", $str, $pyObj)); code.return_(); } - - /** - * Visits all methods declared on the given class and classes in its inheritance hierarchy. - * Methods visible to subclasses are added to <code>seen</code>. - */ - void visitMethods(Class<?> klass, Set<MethodDescr> seen) { - for (Method method : klass.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; - } - - if (!seen.add(new MethodDescr(method))) { - // A method with the same signature has already been added, skip this. - continue; - } - - // TODO - Track accessible final methods in their own Set, protect from overrides - if (Modifier.isFinal(access)) { - if (Modifier.isProtected(access)) { - addSuperMethod(method); - } - continue; - } - visitMethod(method); - } - - Class<?> superClass = klass.getSuperclass(); - if (superClass != null) { - visitMethods(superClass, seen); - } - - for (Class<?> iface : klass.getInterfaces()) { - visitMethods(iface, seen); - } - } - - private Set<MethodDescr> visitMethods() { - Set<MethodDescr> seenmethods = Generic.set(); - visitMethods(superclass, seenmethods); - for (Class<?> iface : interfaces) { - if (iface.isAssignableFrom(superclass)) { - Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName()); - continue; - } - classfile.addInterface(mapClass(iface)); - visitMethods(iface, seenmethods); - } - return seenmethods; - } } Added: branches/customizable-proxymaker/src/org/python/compiler/VerifyingClassFile.java =================================================================== --- branches/customizable-proxymaker/src/org/python/compiler/VerifyingClassFile.java (rev 0) +++ branches/customizable-proxymaker/src/org/python/compiler/VerifyingClassFile.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -0,0 +1,109 @@ +package org.python.compiler; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import java.util.Set; + +import org.python.core.imp; +import org.python.util.Generic; + + +public class VerifyingClassFile extends ClassFile { + + public VerifyingClassFile(String name, String superclass) { + super(name, superclass, imp.NO_MTIME, null); + } + + public boolean noteSuperclassMethod(Method meth) { + return superclassMeths.add(new MethodDescr(meth)); + } + + public boolean hasMethod(String name, Class<?> returns, Class<?>[] parameters, + Class<?>[] thrown) { + // TODO Auto-generated method stub + return false; + } + + public boolean hasConstructor(Class<?>[] parameterClasses, Class<?>[] thrownClasses) { + // TODO Auto-generated method stub + return false; + } + + public void noteSuperclassConstructor(Constructor<?> constructor) { + // TODO Auto-generated method stub + + } + + public Code addMethod(int access, Class<?> returns, String name, Class<?>...parameters) { + return addMethod(access, returns, name, parameters, new Class<?>[0]); + } + + public Code addMethod(int access, + Class<?> returns, + String name, + Class<?>[] parameters, + Class<?>... exceptions) { + return addMethod(name, ProxyCodeHelpers.makeSig(returns, parameters), access, + ProxyCodeHelpers.mapClasses(exceptions)); + } + + private 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()); + } + + 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; + } + } + + private 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); + } + } + + protected final Set<MethodDescr> superclassMeths = Generic.set(); +} Modified: branches/customizable-proxymaker/src/org/python/core/MakeProxies.java =================================================================== --- branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-09-06 07:34:32 UTC (rev 6759) +++ branches/customizable-proxymaker/src/org/python/core/MakeProxies.java 2009-09-06 09:42:02 UTC (rev 6760) @@ -6,7 +6,7 @@ import java.util.List; import org.python.compiler.AdapterMaker; -import org.python.compiler.JavaMaker; +import org.python.compiler.ProxyMaker; import org.python.core.util.StringUtil; class MakeProxies { @@ -34,45 +34,44 @@ } Class<?>[] interfacesArr = interfaces.toArray(new Class<?>[interfaces.size()]); - JavaMaker javaMaker; - // Grab the proxy maker from the class if it exists, and if it does, use the proxy class - // name from the maker + ProxyMaker maker; + // Use the proxy maker from the class if it exists 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, qualifiedClassName, dict); - javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); - if (!StringUtil.isJavaClassName(javaMaker.proxyClassName)) { - throw Py.TypeError(javaMaker.proxyClassName + " 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"); + maker = Py.tojava(customProxyMaker.__call__(args), ProxyMaker.class); + if (!StringUtil.isJavaClassName(maker.proxyClassName)) { + throw Py.TypeError(maker.proxyClassName + " 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"); } // If we've gotten a proxy name, check if it's available on the classpath - Class<?> proxy = Py.findClass(javaMaker.proxyClassName); + Class<?> proxy = Py.findClass(maker.proxyClassName); // TODO - check proxy class version if (proxy != null) { return proxy; } } else { - javaMaker = new JavaMaker(superclass, interfacesArr, qualifiedClassName, dict); // This is a purely dynamic proxy - javaMaker.proxyClassName = proxyPrefix + qualifiedClassName + "$" + proxyNumber++; + maker = new ProxyMaker(superclass, interfacesArr, qualifiedClassName, dict, + proxyPrefix + qualifiedClassName + "$" + proxyNumber++); } - if (!javaMaker.isProxyNeeded()) { + if (!maker.isProxyNeeded()) { return null; } - byte[] bytecode = javaMaker.make(); + byte[] bytecode = maker.make(); if (customProxyMaker != null) { - saveBytecode(javaMaker.proxyClassName, bytecode, Py.getSystemState().javaproxy_dir); + saveBytecode(maker.proxyClassName, bytecode, Py.getSystemState().javaproxy_dir); } else { - saveDebugBytecode(javaMaker.proxyClassName, bytecode); + saveDebugBytecode(maker.proxyClassName, bytecode); } - return makeClass(superclass, interfaces, javaMaker.proxyClassName, bytecode); + return makeClass(superclass, interfaces, maker.proxyClassName, bytecode); } private static Class<?> makeClass(Class<?> referent, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |