[pypy-svn] r72909 - in pypy/branch/cpython-extension/pypy/module/cpyext: . include test

xoraxax at codespeak.net xoraxax at codespeak.net
Fri Mar 26 16:58:49 CET 2010


Author: xoraxax
Date: Fri Mar 26 16:58:47 2010
New Revision: 72909

Added:
   pypy/branch/cpython-extension/pypy/module/cpyext/test/test_borrow.py   (contents, props changed)
Removed:
   pypy/branch/cpython-extension/pypy/module/cpyext/include/typeobject.c
Modified:
   pypy/branch/cpython-extension/pypy/module/cpyext/api.py
   pypy/branch/cpython-extension/pypy/module/cpyext/include/object.h
   pypy/branch/cpython-extension/pypy/module/cpyext/macros.py
   pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py
   pypy/branch/cpython-extension/pypy/module/cpyext/state.py
   pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
   pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py
   pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py
Log:
Largish pile of changes, mainly to fix memory allocation/deallocation.

 * Rewrite PyType_Ready in Python.
 * Add a forgotten borrowing test.
 * DECREF all non-heaptypes at the end of the test run.
 * Break various dependency cycles by introducing a bootstrapping
   function. The hardest cycle is the dependency of pto creation
   on tuple objects for tp_bases.
 * Created debug_refcount function and added output of function names.
 * Reworked borrowing by adding a register_container function that
   needs to be called in functions that borrow.



Modified: pypy/branch/cpython-extension/pypy/module/cpyext/api.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/api.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/api.py	Fri Mar 26 16:58:47 2010
@@ -8,6 +8,7 @@
 from pypy.rpython.tool import rffi_platform
 from pypy.rpython.lltypesystem import ll2ctypes
 from pypy.rpython.annlowlevel import llhelper
+from pypy.rlib.objectmodel import we_are_translated
 from pypy.translator.c.database import LowLevelDatabase
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 from pypy.tool.udir import udir
@@ -20,6 +21,8 @@
 # CPython 2.4 compatibility
 from py.builtin import BaseException
 
+DEBUG_REFCOUNT = True
+DEBUG_WRAPPER = False
 
 Py_ssize_t = lltype.Signed
 ADDR = lltype.Signed
@@ -144,6 +147,7 @@
         func.api_func = api_function
         unwrapper.api_func = api_function
         unwrapper.func = func
+        unwrapper.__name__ = "uw(%s)" % (func.__name__, )
         if external:
             FUNCTIONS[func.func_name] = api_function
         INTERPLEVEL_API[func.func_name] = unwrapper
@@ -197,6 +201,7 @@
         if name in TYPES:
             TYPES[name].become(TYPE)
 
+
 class NullPointerException(Exception):
     pass
 
@@ -217,8 +222,18 @@
     hints["padding"] = hints["padding"] + tuple(pad_fields)
     return lltype.Struct(hints["c_name"], hints=hints, *new_fields)
 
+def debug_refcount(*args, **kwargs):
+    frame_stackdepth = kwargs.pop("frame_stackdepth", 2)
+    assert not kwargs
+    if DEBUG_REFCOUNT:
+        frame = sys._getframe(frame_stackdepth)
+        print >>sys.stderr, "%25s" % (frame.f_code.co_name, ), 
+        for arg in args:
+            print >>sys.stderr, arg,
+        print >>sys.stderr
+
 def make_ref(space, w_obj, borrowed=False, steal=False):
-    from pypy.module.cpyext.macros import Py_INCREF, Py_DECREF, DEBUG_REFCOUNT
+    from pypy.module.cpyext.macros import Py_INCREF, Py_DECREF
     if w_obj is None:
         return lltype.nullptr(PyObject.TO)
     assert isinstance(w_obj, W_Root)
@@ -230,12 +245,7 @@
         w_type = space.type(w_obj)
         if space.is_w(w_type, space.w_type):
             py_obj = allocate_type_obj(space, w_obj)
-            py_obj.c_obj_refcnt = 1
-            if space.is_w(w_type, w_obj):
-                pto = py_obj
-                Py_INCREF(space, pto)
-            else:
-                pto = make_ref(space, w_type)
+            # c_obj_type and c_obj_refcnt are set by allocate_type_obj
         elif isinstance(w_obj, W_PyCObject):
             w_type = space.type(w_obj)
             assert isinstance(w_type, W_PyCTypeObject)
@@ -246,6 +256,7 @@
             T = get_padded_type(PyObject.TO, basicsize)
             py_obj = lltype.malloc(T, None, flavor="raw")
             py_obj.c_obj_refcnt = 1
+            py_obj.c_obj_type = rffi.cast(PyObject, pto)
         elif isinstance(w_obj, W_StringObject):
             py_obj = lltype.malloc(PyStringObject.TO, None, flavor='raw')
             py_obj.c_size = len(space.str_w(w_obj))
@@ -253,17 +264,18 @@
             pto = make_ref(space, space.w_str)
             py_obj = rffi.cast(PyObject, py_obj)
             py_obj.c_obj_refcnt = 1
+            py_obj.c_obj_type = rffi.cast(PyObject, pto)
         else:
             py_obj = lltype.malloc(PyObject.TO, None, flavor="raw")
             py_obj.c_obj_refcnt = 1
             pto = make_ref(space, space.type(w_obj))
-        py_obj.c_obj_type = rffi.cast(PyObject, pto)
+            py_obj.c_obj_type = rffi.cast(PyObject, pto)
+        # XXX remove this weird casting?
         ctypes_obj = ll2ctypes.lltype2ctypes(py_obj)
         ptr = ctypes.cast(ctypes_obj, ctypes.c_void_p).value
         py_obj = ll2ctypes.ctypes2lltype(PyObject, ctypes_obj)
         py_obj = rffi.cast(PyObject, py_obj)
-        if DEBUG_REFCOUNT:
-            print >>sys.stderr, "MAKREF", py_obj, w_obj
+        debug_refcount("MAKREF", py_obj, w_obj)
         state.py_objects_w2r[w_obj] = py_obj
         state.py_objects_r2w[ptr] = w_obj
         if borrowed and ptr not in state.borrowed_objects:
@@ -307,10 +319,20 @@
     return obj
 
 
- at cpython_api([PyObject, PyObject], lltype.Void, external=False)
-def add_borrowed_object(space, container, obj):
+ at cpython_api([PyObject], lltype.Void, external=False)
+def register_container(space, container):
     state = space.fromcache(State)
     container_ptr = rffi.cast(ADDR, container)
+    assert not state.last_container, "Last container was not fetched"
+    state.last_container = container_ptr
+    print "Setting last_container to", hex(container_ptr)
+
+def add_borrowed_object(space, obj):
+    state = space.fromcache(State)
+    container_ptr = state.last_container
+    state.last_container = 0
+    if not container_ptr:
+        raise NullPointerException
     borrowees = state.borrow_mapping.get(container_ptr)
     if borrowees is None:
         state.borrow_mapping[container_ptr] = borrowees = {}
@@ -327,7 +349,8 @@
     def wrapper(*args):
         boxed_args = []
         # XXX use unrolling_iterable here
-        print >>sys.stderr, callable,
+        if DEBUG_WRAPPER:
+            print >>sys.stderr, callable,
         for i, typ in enumerate(callable.api_func.argtypes):
             arg = args[i]
             if (typ is PyObject and
@@ -340,7 +363,8 @@
         state = space.fromcache(State)
         try:
             retval = callable(space, *boxed_args)
-            print >>sys.stderr, " DONE"
+            if DEBUG_WRAPPER:
+                print >>sys.stderr, " DONE"
         except OperationError, e:
             failed = True
             e.normalize_exception(space)
@@ -363,13 +387,37 @@
             return error_value
 
         if callable.api_func.restype is PyObject:
-            retval = make_ref(space, retval, borrowed=callable.api_func.borrowed)
-        if callable.api_func.restype is rffi.INT_real:
+            borrowed = callable.api_func.borrowed
+            retval = make_ref(space, retval, borrowed=borrowed)
+            if borrowed:
+                try:
+                    add_borrowed_object(space, retval)
+                except NullPointerException, e:
+                    if not we_are_translated():
+                        assert False, "Container not registered by %s" % (callable, )
+                    else:
+                        raise
+        elif callable.api_func.restype is rffi.INT_real:
             retval = rffi.cast(rffi.INT_real, retval)
         return retval
     wrapper.__name__ = "wrapper for %r" % (callable, )
     return wrapper
 
+def bootstrap_types(space):
+    from pypy.module.cpyext.typeobject import PyTypeObjectPtr, PyPyType_Ready
+    # bootstrap this damn cycle
+    type_pto = make_ref(space, space.w_type)
+    type_pto = rffi.cast(PyTypeObjectPtr, type_pto)
+    object_pto = make_ref(space, space.w_object)
+    object_pto = rffi.cast(PyTypeObjectPtr, object_pto)
+    type_pto.c_tp_base = object_pto
+    type_pto.c_obj_type = make_ref(space, space.w_type)
+    object_pto.c_obj_type = make_ref(space, space.w_type)
+    PyPyType_Ready(space, object_pto, space.w_object)
+    PyPyType_Ready(space, type_pto, space.w_type)
+    type_pto.c_tp_bases = make_ref(space, space.newtuple([space.w_object]))
+    object_pto.c_tp_bases = make_ref(space, space.newtuple([]))
+
 #_____________________________________________________
 # Build the bridge DLL, Allow extension DLLs to call
 # back into Pypy space functions
@@ -463,6 +511,7 @@
         outputfilename=str(udir / "module_cache" / "pypyapi"),
         standalone=False)
 
+    bootstrap_types(space)
     # load the bridge, and init structure
     import ctypes
     bridge = ctypes.CDLL(str(modulename))
@@ -477,10 +526,6 @@
         ptr = ctypes.c_void_p.in_dll(bridge, name)
         ptr.value = ctypes.cast(ll2ctypes.lltype2ctypes(make_ref(space, w_obj)),
             ctypes.c_void_p).value
-        # hack, init base of the type type
-        if name == "PyType_Type":
-            pto = rffi.cast(PyTypeObjectPtr, ptr)
-            pto.c_tp_base = make_ref(space, w_object)
 
     # implement structure initialization code
     for name, func in FUNCTIONS.iteritems():

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/include/object.h
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/include/object.h	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/include/object.h	Fri Mar 26 16:58:47 2010
@@ -377,8 +377,6 @@
 
 PyAPI_DATA(PyTypeObject *) PyType_Type; /* built-in 'type' */
 PyAPI_DATA(PyTypeObject *) PyBaseObject_Type;
-// defined in typeobject.c
-int PyType_Ready(PyTypeObject *);
 
 /* objimpl.h ----------------------------------------------*/
 

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/macros.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/macros.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/macros.py	Fri Mar 26 16:58:47 2010
@@ -2,18 +2,16 @@
 
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, PyObject, make_ref, from_ref, \
-        ADDR
+        ADDR, debug_refcount
 from pypy.module.cpyext.state import State
 
-DEBUG_REFCOUNT = False
 
 # XXX Optimize these functions and put them into macro definitions
 @cpython_api([PyObject], lltype.Void)
 def Py_DECREF(space, obj):
     from pypy.module.cpyext.typeobject import string_dealloc
     obj.c_obj_refcnt -= 1
-    if DEBUG_REFCOUNT:
-        print >>sys.stderr, "DECREF", obj, obj.c_obj_refcnt
+    debug_refcount("DECREF", obj, obj.c_obj_refcnt, frame_stackdepth=3)
     if obj.c_obj_refcnt == 0:
         state = space.fromcache(State)
         ptr = rffi.cast(ADDR, obj)
@@ -37,6 +35,10 @@
                         del state.borrowed_objects[containee_ptr]
                     except KeyError:
                         pass
+                else:
+                    if DEBUG_REFCOUNT:
+                        print >>sys.stderr, "Borrowed object is already gone:", \
+                                hex(containee)
             del state.borrow_mapping[ptr]
     else:
         assert obj.c_obj_refcnt > 0
@@ -44,8 +46,8 @@
 @cpython_api([PyObject], lltype.Void)
 def Py_INCREF(space, obj):
     obj.c_obj_refcnt += 1
-    if DEBUG_REFCOUNT:
-        print >>sys.stderr, "INCREF", obj, obj.c_obj_refcnt
+    assert obj.c_obj_refcnt > 0
+    debug_refcount("INCREF", obj, obj.c_obj_refcnt, frame_stackdepth=3)
 
 @cpython_api([PyObject], lltype.Void)
 def Py_XDECREF(space, obj):
@@ -55,7 +57,6 @@
 def _Py_Dealloc(space, obj):
     from pypy.module.cpyext.typeobject import PyTypeObjectPtr
     from pypy.module.cpyext.methodobject import generic_cpy_call
-    state = space.fromcache(State)
     pto = obj.c_obj_type
     pto = rffi.cast(PyTypeObjectPtr, pto)
     print >>sys.stderr, "Calling dealloc slot of", obj, \

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/modsupport.py	Fri Mar 26 16:58:47 2010
@@ -1,6 +1,7 @@
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, cpython_struct, PyObject, \
-        METH_STATIC, METH_CLASS, METH_COEXIST, general_check, CANNOT_FAIL
+        METH_STATIC, METH_CLASS, METH_COEXIST, general_check, CANNOT_FAIL, \
+        register_container
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import PyCFunction_NewEx, PyDescr_NewMethod
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
@@ -23,7 +24,7 @@
     return w_mod
 
 @cpython_api([rffi.CCHARP, lltype.Ptr(PyMethodDef), rffi.CCHARP,
-              PyObject, rffi.INT_real], PyObject, borrowed=True)
+              PyObject, rffi.INT_real], PyObject, borrowed=False) # we cannot borrow here
 def Py_InitModule4(space, name, methods, doc, w_self, apiver):
     modname = rffi.charp2str(name)
     w_mod = PyImport_AddModule(space, modname)
@@ -74,10 +75,12 @@
     w_type = space.gettypeobject(Module.typedef)
     return general_check(space, w_obj, w_type)
 
- at cpython_api([PyObject], PyObject)
+ at cpython_api([PyObject], PyObject, borrowed=True)
 def PyModule_GetDict(space, w_mod):
     if PyModule_Check(space, w_mod):
         assert isinstance(w_mod, Module)
-        return w_mod.getdict()
+        w_dict = w_mod.getdict()
+        register_container(space, w_mod)
+        return w_dict
     else:
         PyErr_BadInternalCall(space)

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/state.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/state.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/state.py	Fri Mar 26 16:58:47 2010
@@ -1,6 +1,7 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.lib.identity_dict import identity_dict
 from pypy.interpreter.error import OperationError
+from pypy.rpython.lltypesystem import lltype
 
 
 class State:
@@ -13,6 +14,8 @@
         self.py_objects_r2w = {} # { addr of raw PyObject -> w_obj }
         self.borrow_mapping = {} # { addr of container -> { addr of containee -> None } }
         self.borrowed_objects = {} # { addr of containee -> None }
+        self.non_heaptypes = [] # list of wrapped objects
+        self.last_container = 0 # addr of last container
         self.exc_type = None
         self.exc_value = None
 

Added: pypy/branch/cpython-extension/pypy/module/cpyext/test/test_borrow.py
==============================================================================
--- (empty file)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/test/test_borrow.py	Fri Mar 26 16:58:47 2010
@@ -0,0 +1,43 @@
+import py
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.state import State
+from pypy.module.cpyext.api import make_ref, add_borrowed_object
+
+
+class TestBorrowing(BaseApiTest):
+    def test_borrowing(self, space, api):
+        state = space.fromcache(State)
+        w_int = space.wrap(1)
+        w_tuple = space.newtuple([w_int])
+        api.Py_INCREF(w_tuple)
+        api.register_container(w_tuple)
+        one_pyo = make_ref(space, w_int, borrowed=True)
+        add_borrowed_object(space, one_pyo)
+        print state.borrowed_objects
+        api.Py_DECREF(w_tuple)
+        state.print_refcounts()
+        py.test.raises(AssertionError, api.Py_DECREF, one_pyo)
+
+class AppTestStringObject(AppTestCpythonExtensionBase):
+    def test_tuple_borrowing(self):
+        module = self.import_extension('foo', [
+            ("test_borrowing", "METH_NOARGS",
+             """
+                PyObject *t = PyTuple_New(1);
+                PyObject *f = PyFloat_FromDouble(42.0);
+                PyObject *g = NULL;
+                printf("Refcnt1: %i\\n", Py_REFCNT(f));
+                PyTuple_SetItem(t, 0, f); // steals reference
+                printf("Refcnt2: %i\\n", Py_REFCNT(f));
+                f = PyTuple_GetItem(t, 0); // borrows reference
+                printf("Refcnt3: %i\\n", Py_REFCNT(f));
+                g = PyTuple_GetItem(t, 0); // borrows reference again
+                printf("Refcnt4: %i\\n", Py_REFCNT(f));
+                printf("COMPARE: %i\\n", f == g);
+                Py_DECREF(t);
+                Py_RETURN_TRUE;
+             """),
+            ])
+        assert module.test_borrowing() # the test should not leak
+

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/test/test_cpyext.py	Fri Mar 26 16:58:47 2010
@@ -118,7 +118,7 @@
         self.w_import_module = self.space.wrap(self.import_module)
         self.w_import_extension = self.space.wrap(self.import_extension)
         self.freeze_refcnts()
-        #self.check_and_print_leaks("Object %r leaked some time ago (refcount %i) -- Not executing test!")
+        #self.check_and_print_leaks()
 
     def teardown_method(self, func):
         try:
@@ -127,6 +127,9 @@
             self.space.delitem(self.space.sys.get('modules'),
                                self.space.wrap('foo'))
             Py_DECREF(self.space, w_mod)
+            state = self.space.fromcache(State)
+            for w_obj in state.non_heaptypes:
+                Py_DECREF(self.space, w_obj)
         except OperationError:
             pass
         state = self.space.fromcache(State)

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/tupleobject.py	Fri Mar 26 16:58:47 2010
@@ -1,6 +1,6 @@
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, PyObject, Py_ssize_t, \
-        general_check, CANNOT_FAIL, add_borrowed_object
+        general_check, CANNOT_FAIL, register_container
 from pypy.module.cpyext.macros import Py_DECREF
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject
@@ -29,5 +29,5 @@
         PyErr_BadInternalCall(space)
     assert isinstance(w_t, W_TupleObject)
     w_obj = w_t.wrappeditems[pos]
-    add_borrowed_object(space, w_t, w_obj)
+    register_container(space, w_t)
     return w_obj

Modified: pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py
==============================================================================
--- pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py	(original)
+++ pypy/branch/cpython-extension/pypy/module/cpyext/typeobject.py	Fri Mar 26 16:58:47 2010
@@ -12,12 +12,12 @@
 from pypy.module.cpyext.api import cpython_api, cpython_api_c, cpython_struct, \
     PyObject, PyVarObjectFields, Py_ssize_t, Py_TPFLAGS_READYING, \
     Py_TPFLAGS_READY, Py_TPFLAGS_HEAPTYPE, make_ref, \
-    PyStringObject, ADDR
+    PyStringObject, ADDR, from_ref
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.modsupport import PyMethodDef, convert_method_defs
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.methodobject import generic_cpy_call
-from pypy.module.cpyext.macros import Py_DECREF, Py_XDECREF
+from pypy.module.cpyext.macros import Py_INCREF, Py_DECREF, Py_XDECREF
 
 PyTypeObject = lltype.ForwardReference()
 PyTypeObjectPtr = lltype.Ptr(PyTypeObject)
@@ -250,25 +250,37 @@
 
 @cpython_api([PyObject], lltype.Void, external=False)
 def type_dealloc(space, obj):
+    state = space.fromcache(State)
     obj_pto = rffi.cast(PyTypeObjectPtr, obj)
     type_pto = rffi.cast(PyTypeObjectPtr, obj.c_obj_type)
     base_pyo = rffi.cast(PyObject, obj_pto.c_tp_base)
     Py_XDECREF(space, base_pyo)
-    # XXX XDECREF tp_dict, tp_bases, tp_mro, tp_cache,
+    Py_XDECREF(space, obj_pto.c_tp_bases)
+    # XXX XDECREF tp_dict, tp_mro, tp_cache,
     #             tp_subclasses
     # free tp_doc
-    lltype.free(obj_pto.c_tp_name, flavor="raw")
-    obj_pto_voidp = rffi.cast(rffi.VOIDP_real, obj_pto)
-    generic_cpy_call(space, type_pto.c_tp_free, obj_pto_voidp)
-    pto = rffi.cast(PyObject, type_pto)
-    Py_DECREF(space, pto)
+    if obj_pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
+        lltype.free(obj_pto.c_tp_name, flavor="raw")
+        obj_pto_voidp = rffi.cast(rffi.VOIDP_real, obj_pto)
+        generic_cpy_call(space, type_pto.c_tp_free, obj_pto_voidp)
+        pto = rffi.cast(PyObject, type_pto)
+        Py_DECREF(space, pto)
+
 
 def allocate_type_obj(space, w_type):
     """ Allocates a pto from a w_type which must be a PyPy type. """
+    state = space.fromcache(State)
     from pypy.module.cpyext.object import PyObject_dealloc, PyObject_Del
     assert not isinstance(w_type, W_PyCTypeObject)
     assert isinstance(w_type, W_TypeObject)
+
     pto = lltype.malloc(PyTypeObject, None, flavor="raw")
+    pto.c_obj_refcnt = 1
+    # put the type object early into the dict
+    # to support dependency cycles like object/type
+    state = space.fromcache(State)
+    state.py_objects_w2r[w_type] = rffi.cast(PyObject, pto)
+
     if space.is_w(w_type, space.w_object):
         pto.c_tp_dealloc = PyObject_dealloc.api_func.get_llhelper(space)
     elif space.is_w(w_type, space.w_type):
@@ -284,25 +296,64 @@
     bases_w = w_type.bases_w
     assert len(bases_w) <= 1
     pto.c_tp_base = lltype.nullptr(PyTypeObject)
-    if bases_w and not space.is_w(w_type, space.w_type): # avoid endless recursion
-        ref = make_ref(space, bases_w[0])
-        pto.c_tp_base = rffi.cast(PyTypeObjectPtr, ref)
+    pto.c_tp_bases = lltype.nullptr(PyObject.TO)
+    if not space.is_w(w_type, space.w_type) and not \
+            space.is_w(w_type, space.w_object):
+        if bases_w:
+            ref = make_ref(space, bases_w[0])
+            pto.c_tp_base = rffi.cast(PyTypeObjectPtr, ref)
+        pto.c_obj_type = make_ref(space, space.type(space.w_type))
+        PyPyType_Ready(space, pto, w_type)
+    else:
+        pto.c_obj_type = lltype.nullptr(PyObject.TO)
+
     #  XXX fill slots in pto
     return pto
 
- at cpython_api_c()
+ at cpython_api([PyTypeObjectPtr], rffi.INT_real, error=-1)
 def PyType_Ready(space, pto):
-    "Implemented in typeobject.c"
+    return PyPyType_Ready(space, pto, None)
+
+def PyPyType_Ready(space, pto, w_obj):
+    try:
+        pto.c_tp_dict = lltype.nullptr(PyObject.TO) # not supported
+        if pto.c_tp_flags & Py_TPFLAGS_READY:
+            return 0
+        assert pto.c_tp_flags & Py_TPFLAGS_READYING == 0
+        pto.c_tp_flags |= Py_TPFLAGS_READYING
+        base = pto.c_tp_base
+        if not base and not (w_obj is not None and
+            space.is_w(w_obj, space.w_object)):
+            base_pyo = make_ref(space, space.w_object)
+            base = pto.c_tp_base = rffi.cast(PyTypeObjectPtr, base_pyo)
+        if base and not base.c_tp_flags & Py_TPFLAGS_READY:
+            PyPyType_Ready(space, base, None)
+        if base and not pto.c_obj_type: # will be filled later
+            pto.c_obj_type = base.c_obj_type
+        if not pto.c_tp_bases and not (space.is_w(w_obj, space.w_object)
+                or space.is_w(w_obj, space.w_type)):
+            if not base:
+                bases = space.newtuple([])
+            else:
+                bases = space.newtuple([from_ref(space, base)])
+            pto.c_tp_bases = make_ref(space, bases)
+        if w_obj is None:
+            PyPyType_Register(space, pto)
+    finally:
+        pto.c_tp_flags &= ~Py_TPFLAGS_READYING
+    pto.c_tp_flags = (pto.c_tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY
+    return 0
 
- at cpython_api([PyTypeObjectPtr], rffi.INT_real, error=-1)
 def PyPyType_Register(space, pto):
     state = space.fromcache(State)
     ptr = rffi.cast(ADDR, pto)
     if ptr not in state.py_objects_r2w:
         w_obj = space.allocate_instance(W_PyCTypeObject,
                 space.gettypeobject(W_PyCTypeObject.typedef))
+        state.non_heaptypes.append(w_obj)
+        pyo = rffi.cast(PyObject, pto)
         state.py_objects_r2w[ptr] = w_obj
-        state.py_objects_w2r[w_obj] = pto
+        state.py_objects_w2r[w_obj] = pyo
         w_obj.__init__(space, pto)
         w_obj.ready()
     return 1



More information about the Pypy-commit mailing list