Message4037

Author marcdownie
Recipients marcdownie
Date 2009-01-13.01:22:05
SpamBayes Score 0.0021955743
Marked as misclassified No
Message-id <1231809728.19.0.469520766525.issue1234@psf.upfronthosting.co.za>
In-reply-to
Content
Convoluted (but, alas, real world) inheritance hierarchies can cause PyJavaType's 
mro computation to NPE.

If we have the following class/interface structure:

package somepackage;
public interface A extends B {}

package somepackage;
public interface B extends B {
	public interface C extends A,D {}

	public interface D extends B {}
}

package somepackage;
public class AImpl implements A {}

Admittedly this is rather odd, but it's legal Java (at the very least, ecj has 
happily compiled something like this for years).

Then, with the appropriate CLASSPATH:

from Jython 2.5b1+ (trunk:5923, Jan 12 2009, 19:12:49) 
[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_07
Type "help", "copyright", "credits" or "license" for more information.
>>> from somepackage import AImpl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
java.lang.NullPointerException
	at org.python.core.PyJavaType.init(PyJavaType.java:93)
	at org.python.core.PyType.createType(PyType.java:990)
	at org.python.core.PyType.addFromClass(PyType.java:963)
	at org.python.core.PyType.fromClass(PyType.java:1003)
	at org.python.core.PyJavaType.init(PyJavaType.java:322)
	at org.python.core.PyType.createType(PyType.java:990)
	at org.python.core.PyType.addFromClass(PyType.java:963)
	at org.python.core.PyType.fromClass(PyType.java:1003)
	at org.python.core.PyJavaType.init(PyJavaType.java:87)
	at org.python.core.PyType.createType(PyType.java:990)
	at org.python.core.PyType.addFromClass(PyType.java:963)
	at org.python.core.PyType.fromClass(PyType.java:1003)
	at org.python.core.PyJavaType.init(PyJavaType.java:87)
	at org.python.core.PyType.createType(PyType.java:990)
	at org.python.core.PyType.addFromClass(PyType.java:963)
	at org.python.core.PyType.fromClass(PyType.java:1003)
	at 
org.python.core.adapter.ClassicPyObjectAdapter$6.adapt(ClassicPyObjectAdapter.java:7
6)
	at 
org.python.core.adapter.ExtensiblePyObjectAdapter.adapt(ExtensiblePyObjectAdapter.ja
va:44)
	at 
org.python.core.adapter.ClassicPyObjectAdapter.adapt(ClassicPyObjectAdapter.java:120
)
	at org.python.core.Py.java2py(Py.java:1481)
	at org.python.core.PyJavaPackage.addClass(PyJavaPackage.java:89)
	at org.python.core.PyJavaPackage.__findattr_ex__(PyJavaPackage.java:138)
	at org.python.core.PyObject.__findattr__(PyObject.java:848)
	at org.python.core.imp.import_name(imp.java:706)
	at org.python.core.imp.importName(imp.java:741)
	at org.python.core.ImportFunction.__call__(__builtin__.java:1265)
	at org.python.core.PyObject.__call__(PyObject.java:339)
	at org.python.core.__builtin__.__import__(__builtin__.java:1236)
	at org.python.core.imp.importFromAs(imp.java:817)
	at org.python.core.imp.importFrom(imp.java:794)
	at org.python.pycode._pyx2.f$0(<stdin>:1)
	at org.python.pycode._pyx2.call_function(<stdin>)
	at org.python.core.PyTableCode.call(PyTableCode.java:199)
	at org.python.core.PyCode.call(PyCode.java:14)
	at org.python.core.Py.runCode(Py.java:1206)
	at org.python.core.Py.exec(Py.java:1237)
	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:133)
	at 
org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:90)
	at 
org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java:71)
	at 
org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java:46)
	at org.python.util.InteractiveConsole.push(InteractiveConsole.java:110)
	at org.python.util.InteractiveConsole.interact(InteractiveConsole.java:90)
	at org.python.util.jython.run(jython.java:293)
	at org.python.util.jython.main(jython.java:112)

java.lang.NullPointerException: java.lang.NullPointerException
>>> 

It's ((PyType)obj).mro that's null on that line. 

If I add diagnostics to PyJavaType.init (and avoid the NPE), I can produce this:
>>> from somepackage import AImpl
 entering mro init for <somepackage.AImpl>
 entering mro init for <somepackage.A>
 entering mro init for <somepackage.B>
 exiting <somepackage.B> mro is <[Lorg.python.core.PyObject;@3f62e847>
 entering mro init for <somepackage.B$C>
 entering mro init for <somepackage.B$D>
 exiting <somepackage.B$D> mro is <[Lorg.python.core.PyObject;@5dd22889>
 WARNING: <somepackage.A> has null mro
 exiting <somepackage.B$C> mro is <[Lorg.python.core.PyObject;@40e51e67>
 exiting <somepackage.A> mro is <[Lorg.python.core.PyObject;@3107eafc>
 exiting <somepackage.AImpl> mro is <[Lorg.python.core.PyObject;@28ee8874>
>>> 

You can see that somepackage.A's mro is being used before it has finished computing.

The workaround is simply to take interface C's definition outside B.
History
Date User Action Args
2009-01-13 01:22:08marcdowniesetrecipients: + marcdownie
2009-01-13 01:22:08marcdowniesetmessageid: <1231809728.19.0.469520766525.issue1234@psf.upfronthosting.co.za>
2009-01-13 01:22:07marcdownielinkissue1234 messages
2009-01-13 01:22:06marcdowniecreate