Issue1234

classification
Title: Java classes' MRO resolution can throw an NPE
Type: crash Severity: normal
Components: Core Versions: 2.5b1
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: cgroves, marcdownie
Priority: Keywords:

Created on 2009-01-13.01:22:07 by marcdownie, last changed 2009-01-26.02:21:53 by cgroves.

Messages
msg4037 (view) Author: Marc Downie (marcdownie) Date: 2009-01-13.01:22:05
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.
msg4078 (view) Author: Charlie Groves (cgroves) Date: 2009-01-26.02:21:52
Should be fixed as of r5981:

Jython 2.5b1+ (trunk:5978:5979M, Jan 25 2009, 18:08: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
>>> AImpl.__mro__
(<type 'somepackage.AImpl'>, <type 'somepackage.A'>, <type
'somepackage.B'>, <type 'java.lang.Object'>, <type 'object'>)
>>> from somepackage import B
>>> B.C.__mro__
(<type 'somepackage.B$C'>, <type 'somepackage.A'>, <type
'somepackage.B$D'>, <type 'somepackage.B'>, <type 'java.lang.Object'>,
<type 'object'>)
>>>
History
Date User Action Args
2009-01-26 02:21:53cgrovessetstatus: open -> closed
resolution: fixed
messages: + msg4078
nosy: + cgroves
2009-01-13 01:22:08marcdowniecreate