[pypy-commit] pypy cpyext-gc-support: Start to pass a few tests in test_intobject, after performance

arigo noreply at buildbot.pypy.org
Tue Oct 20 12:31:17 EDT 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cpyext-gc-support
Changeset: r80357:45142ab484f1
Date: 2015-10-20 18:18 +0200
http://bitbucket.org/pypy/pypy/changeset/45142ab484f1/

Log:	Start to pass a few tests in test_intobject, after performance
	improvements to intobject.py and other fixes

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -381,6 +381,14 @@
 INTERPLEVEL_API = {}
 FUNCTIONS = {}
 
+def constant_pyobj(space, name):
+    if we_are_translated():
+        ZZZ   # should return the C symbol "Py" + name, constant-folded
+    else:
+        from pypy.module.cpyext.pyobject import as_pyobj
+        w_obj = INTERPLEVEL_API[name]
+        return as_pyobj(space, w_obj)
+
 # These are C symbols which cpyext will export, but which are defined in .c
 # files somewhere in the implementation of cpyext (rather than being defined in
 # RPython).
@@ -496,7 +504,7 @@
         GLOBALS['%s#' % (cpyname, )] = ('PyTypeObject*', pypyexpr)
 
     for cpyname in '''PyMethodObject PyListObject PyLongObject
-                      PyDictObject PyTupleObject PyClassObject'''.split():
+                      PyDictObject PyClassObject'''.split():
         FORWARD_DECLS.append('typedef struct { PyObject_HEAD } %s'
                              % (cpyname, ))
 build_exported_objects()
@@ -559,25 +567,31 @@
             if name in TYPES:
                 TYPES[name].become(TYPE)
 
-def build_type_checkers(type_name, cls=None):
+def build_type_checkers3(type_name, cls=None):
     """
     Builds two api functions: Py_XxxCheck() and Py_XxxCheckExact().
     - if `cls` is None, the type is space.w_[type].
     - if `cls` is a string, it is the name of a space attribute, e.g. 'w_str'.
     - else `cls` must be a W_Class with a typedef.
     """
+    py_type_name = "Py" + type_name + "_Type"
+    check_name = "Py" + type_name + "_Check"
+
     if cls is None:
-        attrname = "w_" + type_name.lower()
-        def get_w_type(space):
-            return getattr(space, attrname)
-    elif isinstance(cls, str):
+        cls = "w_" + type_name.lower()
+    if isinstance(cls, str):
         def get_w_type(space):
             return getattr(space, cls)
+        def _PyXxx_Type(space):
+            return rffi.cast(PyTypeObjectPtr,
+                             constant_pyobj(space, py_type_name))
     else:
         @specialize.memo()
         def get_w_type(space):
             return space.gettypeobject(cls.typedef)
-    check_name = "Py" + type_name + "_Check"
+        def _PyXxx_Type():
+            ZZZ
+    _PyXxx_Type = func_with_new_name(_PyXxx_Type, '_' + py_type_name)
 
     def check(space, w_obj):
         "Implements the Py_Xxx_Check function"
@@ -587,15 +601,18 @@
                 space.is_true(space.issubtype(w_obj_type, w_type)))
     def check_exact(space, py_obj):
         "Implements the Py_Xxx_CheckExact function"
-        py_type = get_w_type(space).cpyext_c_type_object
-        assert py_type
-        return py_obj.c_ob_type == py_type
+        #py_type = get_w_type(space).cpyext_c_type_object
+        return py_obj.c_ob_type == _PyXxx_Type(space)
 
     check = cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)(
         func_with_new_name(check, check_name))
     check_exact = cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)(
         func_with_new_name(check_exact, check_name + "Exact"))
-    return check, check_exact
+    return check, check_exact, _PyXxx_Type
+
+def build_type_checkers(type_name, cls=None):
+    # backward compatibility
+    return build_type_checkers3(type_name, cls=cls)[:2]
 
 pypy_debug_catch_fatal_exception = rffi.llexternal('pypy_debug_catch_fatal_exception', [], lltype.Void)
 
@@ -793,7 +810,8 @@
     struct PyPyAPI {
     %(members)s
     } _pypyAPI;
-    RPY_EXTERN struct PyPyAPI* pypyAPI = &_pypyAPI;
+    RPY_EXTERN struct PyPyAPI* pypyAPI;
+    struct PyPyAPI* pypyAPI = &_pypyAPI;
     """ % dict(members=structmembers)
 
     functions = generate_decls_and_callbacks(db, export_symbols)
@@ -848,6 +866,7 @@
 
         INTERPLEVEL_API[name] = w_obj
 
+        orgname = name
         name = name.replace('Py', 'cpyexttest')
         if isptr:
             ptr = ctypes.c_void_p.in_dll(bridge, name)
diff --git a/pypy/module/cpyext/include/tupleobject.h b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -7,6 +7,11 @@
 extern "C" {
 #endif
 
+typedef struct {
+    PyObject_VAR_HEAD
+    PyObject *ob_item[1];
+} PyTupleObject;
+
 /* defined in varargswrapper.c */
 PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
 
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -2,10 +2,10 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.error import OperationError
 from pypy.module.cpyext.api import (
-    cpython_api, cpython_struct, build_type_checkers, bootstrap_function,
+    cpython_api, cpython_struct, build_type_checkers3, bootstrap_function,
     PyObject, PyObjectFields, CONST_STRING, CANNOT_FAIL, Py_ssize_t)
-from pypy.module.cpyext.pyobject import (
-    setup_class_for_cpyext, track_reference, RefcountState, from_ref)
+from pypy.module.cpyext.pyobject import (setup_class_for_cpyext, new_pyobj,
+                                         from_pyobj)
 from rpython.rlib.rarithmetic import r_uint, intmask, LONG_TEST, r_ulonglong
 from pypy.objspace.std.intobject import W_IntObject
 import sys
@@ -16,15 +16,39 @@
     (("ob_ival", rffi.LONG),)
 cpython_struct("PyIntObject", PyIntObjectFields, PyIntObjectStruct)
 
+PyInt_Check, PyInt_CheckExact, _PyInt_Type = build_type_checkers3("Int")
+
+
 @bootstrap_function
 def init_intobject(space):
     "Type description of PyIntObject"
     from pypy.objspace.std.intobject import W_AbstractIntObject, W_IntObject
-    setup_class_for_cpyext(W_AbstractIntObject,
-                           basestruct=PyIntObject.TO,
-                           fill_pyobj=int_fill_pyobj,
-                           fill_pypy=int_fill_pypy,
-                           realize_subclass_of=W_IntObject)
+    setup_class_for_cpyext(
+        # --the base class of all 'int' objects inside PyPy--
+        W_AbstractIntObject,
+
+        # --the structure type derived from PyObject--
+        basestruct=PyIntObjectStruct,
+
+        # --after a PyIntObject is allocated, we call this function to
+        #   fill it.  It gets attached as RRC_PERMANENT_LIGHT by default,
+        #   which means the association is permanent (the PyIntObject is
+        #   alive and won't appear to move as long as the W_IntObject is
+        #   alive) and light (the PyIntObject can be freed with free()).--
+        fill_pyobj=int_fill_pyobj,
+
+        # --reverse direction: from a PyIntObject, we make a W_IntObject
+        #   by instantiating a custom subclass of W_IntObject--
+        realize_subclass_of=W_IntObject,
+
+        # --and then we call this function to initialize the W_IntObject--
+        fill_pypy=int_fill_pypy,
+
+        # --in this case, and if PyInt_CheckExact() returns True, then
+        #   the link can be light, i.e. the original PyIntObject might
+        #   be freed with free() by the GC--
+        alloc_pypy_light_if=PyInt_CheckExact,
+        )
 
 def int_fill_pyobj(space, w_obj, py_int):
     """
@@ -41,8 +65,6 @@
     intval = rffi.cast(lltype.Signed, py_int.c_ob_ival)
     W_IntObject.__init__(w_obj, intval)
 
-PyInt_Check, PyInt_CheckExact = build_type_checkers("Int")
-
 @cpython_api([], lltype.Signed, error=CANNOT_FAIL)
 def PyInt_GetMax(space):
     """Return the system's idea of the largest integer it can handle (LONG_MAX,
@@ -52,19 +74,23 @@
 @cpython_api([lltype.Signed], PyObject)
 def PyInt_FromLong(space, ival):
     """Create a new integer object with a value of ival.
-    
     """
-    return space.wrap(ival)
+    py_int = new_pyobj(PyIntObjectStruct, _PyInt_Type(space))
+    py_int.c_ob_ival = ival
+    return rffi.cast(PyObject, py_int)
 
 @cpython_api([PyObject], lltype.Signed, error=-1)
-def PyInt_AsLong(space, w_obj):
+def PyInt_AsLong(space, py_obj):
     """Will first attempt to cast the object to a PyIntObject, if it is not
     already one, and then return its value. If there is an error, -1 is
     returned, and the caller should check PyErr_Occurred() to find out whether
     there was an error, or whether the value just happened to be -1."""
-    if w_obj is None:
+    if not py_obj:
         raise OperationError(space.w_TypeError,
                              space.wrap("an integer is required, got NULL"))
+    if PyInt_Check(space, py_obj):
+        return PyInt_AS_LONG(space, py_obj)
+    w_obj = from_pyobj(space, py_obj)
     return space.int_w(space.int(w_obj))
 
 @cpython_api([PyObject], lltype.Unsigned, error=-1)
@@ -108,9 +134,10 @@
         return num.ulonglongmask()
 
 @cpython_api([PyObject], lltype.Signed, error=CANNOT_FAIL)
-def PyInt_AS_LONG(space, w_int):
+def PyInt_AS_LONG(space, py_obj):
     """Return the value of the object w_int. No error checking is performed."""
-    return space.int_w(w_int)
+    py_int = rffi.cast(PyIntObject, py_obj)
+    return py_int.c_ob_ival
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
 def PyInt_AsSsize_t(space, w_obj):
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -14,130 +14,21 @@
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib import rawrefcount
 
-#________________________________________________________
-# type description ZZZ SEE BELOW
-
-class BaseCpyTypedescr(object):
-    basestruct = PyObject.TO
-    W_BaseObject = W_ObjectObject
-
-    def get_dealloc(self, space):
-        from pypy.module.cpyext.typeobject import subtype_dealloc
-        return llhelper(
-            subtype_dealloc.api_func.functype,
-            subtype_dealloc.api_func.get_wrapper(space))
-
-    def allocate(self, space, w_type, itemcount=0):
-        # similar to PyType_GenericAlloc?
-        # except that it's not related to any pypy object.
-
-        pytype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_type))
-        # Don't increase refcount for non-heaptypes
-        if pytype:
-            flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
-            if not flags & Py_TPFLAGS_HEAPTYPE:
-                Py_DecRef(space, w_type)
-
-        if pytype:
-            size = pytype.c_tp_basicsize
-        else:
-            size = rffi.sizeof(self.basestruct)
-        if itemcount:
-            size += itemcount * pytype.c_tp_itemsize
-        buf = lltype.malloc(rffi.VOIDP.TO, size,
-                            flavor='raw', zero=True)
-        pyobj = rffi.cast(PyObject, buf)
-        pyobj.c_ob_refcnt = 1
-        pyobj.c_ob_type = pytype
-        return pyobj
-
-    def attach(self, space, pyobj, w_obj):
-        pass
-
-    def realize(self, space, obj):
-        w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
-        w_obj = space.allocate_instance(self.W_BaseObject, w_type)
-        track_reference(space, obj, w_obj)
-        if w_type is not space.gettypefor(self.W_BaseObject):
-            state = space.fromcache(RefcountState)
-            state.set_lifeline(w_obj, obj)
-        return w_obj
-
-def make_typedescr(typedef, **kw):
-    return #ZZZ
-    """NOT_RPYTHON
-
-    basestruct: The basic structure to allocate
-    alloc     : allocate and basic initialization of a raw PyObject
-    attach    : Function called to tie a raw structure to a pypy object
-    realize   : Function called to create a pypy object from a raw struct
-    dealloc   : a cpython_api(external=False), similar to PyObject_dealloc
-    """
-
-    tp_basestruct = kw.pop('basestruct', PyObject.TO)
-    tp_alloc      = kw.pop('alloc', None)
-    tp_attach     = kw.pop('attach', None)
-    tp_realize    = kw.pop('realize', None)
-    tp_dealloc    = kw.pop('dealloc', None)
-    assert not kw, "Extra arguments to make_typedescr"
-
-    null_dealloc = lltype.nullptr(lltype.FuncType([PyObject], lltype.Void))
-
-    class CpyTypedescr(BaseCpyTypedescr):
-        basestruct = tp_basestruct
-
-        if tp_alloc:
-            def allocate(self, space, w_type, itemcount=0):
-                return tp_alloc(space, w_type)
-
-        if tp_dealloc:
-            def get_dealloc(self, space):
-                return llhelper(
-                    tp_dealloc.api_func.functype,
-                    tp_dealloc.api_func.get_wrapper(space))
-
-        if tp_attach:
-            def attach(self, space, pyobj, w_obj):
-                tp_attach(space, pyobj, w_obj)
-
-        if tp_realize:
-            def realize(self, space, ref):
-                return tp_realize(space, ref)
-    if typedef:
-        CpyTypedescr.__name__ = "CpyTypedescr_%s" % (typedef.name,)
-
-    typedescr_cache[typedef] = CpyTypedescr()
-
-#@bootstrap_function ZZZ
-def init_pyobject(space):
-    from pypy.module.cpyext.object import PyObject_dealloc
-    # typedescr for the 'object' type
-    make_typedescr(space.w_object.instancetypedef,
-                   dealloc=PyObject_dealloc)
-    # almost all types, which should better inherit from object.
-    make_typedescr(None)
-
- at specialize.memo()
-def _get_typedescr_1(typedef):
-    ZZZ
-    try:
-        return typedescr_cache[typedef]
-    except KeyError:
-        if typedef.bases:
-            return _get_typedescr_1(typedef.bases[0])
-        return typedescr_cache[None]
-
-def get_typedescr(typedef):
-    ZZZ
-    if typedef is None:
-        return typedescr_cache[None]
-    else:
-        return _get_typedescr_1(typedef)
-
 
 #________________________________________________________
 # type description
 
+def make_typedescr(arg0, *args, **kwds):
+    print "ZZZ: make_typedescr(%r)" % (arg0,)
+def get_typedescr(*args, **kwds):
+    ZZZ
+RefcountState = "ZZZ"
+
+RRC_PERMANENT       = 'P'  # the link pyobj<->pypy is permanent
+RRC_PERMANENT_LIGHT = 'p'  # same, but tp_dealloc can be replaced with free()
+RRC_TRANSIENT       = 'T'  # the pypy object is transient and can go away
+RRC_TRANSIENT_LIGHT = 't'  # same, but tp_dealloc can be replaced with free()
+
 def setup_class_for_cpyext(W_Class, **kw):
     """NOT_RPYTHON
 
@@ -156,6 +47,7 @@
     tp_fill_pypy    = kw.pop('fill_pypy', None)
     force_create_pyobj  = kw.pop('force_create_pyobj', False)
     realize_subclass_of = kw.pop('realize_subclass_of', None)
+    alloc_pypy_light_if = kw.pop('alloc_pypy_light_if', None)
     #tp_dealloc    = kw.pop('dealloc', None)
     assert not kw, "Extra arguments to make_typedescr: %s" % kw.keys()
 
@@ -167,7 +59,7 @@
             def tp_alloc_pyobj(space, w_obj):
                 ob = lltype.malloc(tp_basestruct, flavor='raw',
                                    track_allocation=False)
-                return ob, True
+                return ob, RRC_PERMANENT_LIGHT
         tp_alloc_pyobj._always_inline_ = True
         #
         if not tp_fill_pyobj:
@@ -175,10 +67,11 @@
                 pass
         #
         def cpyext_create_pyobj(self, space):
-            py_obj, is_light = tp_alloc_pyobj(space, self)
+            py_obj, strength = tp_alloc_pyobj(space, self)
             ob = rffi.cast(PyObject, py_obj)
-            ob_type = get_c_ob_type(space, space.type(self))
-            init_link_from_pypy(self, ob, ob_type, is_light)
+            ob.c_ob_refcnt = 0
+            ob.c_ob_type = get_c_ob_type(space, space.type(self))
+            rawrefcount_init_link(self, ob, strength)
             tp_fill_pyobj(space, self, py_obj)
             return ob
         W_Class.cpyext_create_pyobj = cpyext_create_pyobj
@@ -203,7 +96,11 @@
                 realize_subclass_of)
             def tp_alloc_pypy(space, pyobj):
                 w_obj = W_CPyExtPlaceHolder(pyobj)
-                return w_obj, True
+                strength = RRC_TRANSIENT
+                if alloc_pypy_light_if is not None:
+                    if alloc_pypy_light_if(space, pyobj):
+                        strength = RRC_TRANSIENT_LIGHT
+                return w_obj, strength
         tp_alloc_pypy._always_inline_ = True
         #
         if not tp_fill_pypy:
@@ -211,8 +108,8 @@
                 pass
         #
         def cpyext_create_pypy(space, pyobj):
-            w_obj, is_transient = tp_alloc_pypy(space, pyobj)
-            init_link_from_pyobj(w_obj, pyobj, is_transient)
+            w_obj, strength = tp_alloc_pypy(space, pyobj)
+            rawrefcount_init_link(w_obj, pyobj, strength)
             tp_fill_pypy(space, w_obj, pyobj)
             return w_obj
         #
@@ -223,25 +120,30 @@
     W_Class.cpyext_basestruct = tp_basestruct
 
 
-def init_link_from_pypy(w_obj, ob, ob_type, is_light):
-    if is_light:
-        ob.c_ob_refcnt = rawrefcount.REFCNT_FROM_PYPY_LIGHT
+def rawrefcount_init_link(w_obj, ob, strength):
+    if strength == RRC_PERMANENT:
+        ob.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY
+        rawrefcount.create_link_pypy(w_obj, ob)
+    #
+    elif strength == RRC_PERMANENT_LIGHT:
+        ob.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY_LIGHT
+        rawrefcount.create_link_pypy(w_obj, ob)
+    #
+    elif strength == RRC_TRANSIENT:
+        ob.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY
+        rawrefcount.create_link_pyobj(w_obj, ob)
+    #
+    elif strength == RRC_TRANSIENT_LIGHT:
+        ob.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY_LIGHT
+        rawrefcount.create_link_pyobj(w_obj, ob)
+    #
     else:
-        ob.c_ob_refcnt = rawrefcount.REFCNT_FROM_PYPY
-    ob.c_ob_type = ob_type
-    rawrefcount.create_link_pypy(w_obj, ob)
+        assert False, "rawrefcount_init_link: strength=%r" % (strength,)
 
-def init_link_from_pyobj(w_obj, ob, is_transient):
-    ob.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY
-    if is_transient:
-        rawrefcount.create_link_pyobj(w_obj, ob)
-    else:
-        rawrefcount.create_link_pypy(w_obj, ob)
 
 def setup_prebuilt_pyobj(w_obj, py_obj):
     assert lltype.typeOf(py_obj) == PyObject
-    init_link_from_pypy(w_obj, py_obj, lltype.nullptr(PyTypeObjectPtr.TO),
-                        False)
+    rawrefcount_init_link(w_obj, py_obj, RRC_PERMANENT)
     if isinstance(w_obj, W_TypeObject):
         w_obj.cpyext_c_type_object = rffi.cast(PyTypeObjectPtr, py_obj)
 
@@ -316,108 +218,6 @@
 #________________________________________________________
 # refcounted object support
 
-class RefcountState:
-    def __init__(self, space):
-        ZZZ
-        self.space = space
-        self.py_objects_w2r = {} # { w_obj -> raw PyObject }
-        self.py_objects_r2w = {} # { addr of raw PyObject -> w_obj }
-
-        self.lifeline_dict = RWeakKeyDictionary(W_Root, PyOLifeline)
-
-        self.borrow_mapping = {None: {}}
-        # { w_container -> { w_containee -> None } }
-        # the None entry manages references borrowed during a call to
-        # generic_cpy_call()
-
-        # For tests
-        self.non_heaptypes_w = []
-
-    def _cleanup_(self):
-        assert self.borrow_mapping == {None: {}}
-        self.py_objects_r2w.clear() # is not valid anymore after translation
-
-    def init_r2w_from_w2r(self):
-        """Rebuilds the dict py_objects_r2w on startup"""
-        for w_obj, obj in self.py_objects_w2r.items():
-            ptr = rffi.cast(ADDR, obj)
-            self.py_objects_r2w[ptr] = w_obj
-
-    def print_refcounts(self):
-        print "REFCOUNTS"
-        for w_obj, obj in self.py_objects_w2r.items():
-            print "%r: %i" % (w_obj, obj.c_ob_refcnt)
-
-    def get_from_lifeline(self, w_obj):
-        lifeline = self.lifeline_dict.get(w_obj)
-        if lifeline is not None: # make old PyObject ready for use in C code
-            py_obj = lifeline.pyo
-            assert py_obj.c_ob_refcnt == 0
-            return py_obj
-        else:
-            return lltype.nullptr(PyObject.TO)
-
-    def set_lifeline(self, w_obj, py_obj):
-        self.lifeline_dict.set(w_obj,
-                               PyOLifeline(self.space, py_obj))
-
-    def make_borrowed(self, w_container, w_borrowed):
-        """
-        Create a borrowed reference, which will live as long as the container
-        has a living reference (as a PyObject!)
-        """
-        ref = make_ref(self.space, w_borrowed)
-        obj_ptr = rffi.cast(ADDR, ref)
-
-        borrowees = self.borrow_mapping.setdefault(w_container, {})
-        if w_borrowed in borrowees:
-            Py_DecRef(self.space, w_borrowed) # cancel incref from make_ref()
-        else:
-            borrowees[w_borrowed] = None
-
-        return ref
-
-    def reset_borrowed_references(self):
-        "Used in tests"
-        for w_container, w_borrowed in self.borrow_mapping.items():
-            Py_DecRef(self.space, w_borrowed)
-        self.borrow_mapping = {None: {}}
-
-    def delete_borrower(self, w_obj):
-        """
-        Called when a potential container for borrowed references has lost its
-        last reference.  Removes the borrowed references it contains.
-        """
-        if w_obj in self.borrow_mapping: # move to lifeline __del__
-            for w_containee in self.borrow_mapping[w_obj]:
-                self.forget_borrowee(w_containee)
-            del self.borrow_mapping[w_obj]
-
-    def swap_borrow_container(self, container):
-        """switch the current default contained with the given one."""
-        if container is None:
-            old_container = self.borrow_mapping[None]
-            self.borrow_mapping[None] = {}
-            return old_container
-        else:
-            old_container = self.borrow_mapping[None]
-            self.borrow_mapping[None] = container
-            for w_containee in old_container:
-                self.forget_borrowee(w_containee)
-
-    def forget_borrowee(self, w_obj):
-        "De-register an object from the list of borrowed references"
-        ref = self.py_objects_w2r.get(w_obj, lltype.nullptr(PyObject.TO))
-        if not ref:
-            if DEBUG_REFCOUNT:
-                print >>sys.stderr, "Borrowed object is already gone!"
-            return
-
-        Py_DecRef(self.space, ref)
-
-class InvalidPointerException(Exception):
-    pass
-
 DEBUG_REFCOUNT = False
 
 def debug_refcount(*args, **kwargs):
@@ -568,6 +368,15 @@
     return w_obj
 
 
+ at specialize.ll()
+def new_pyobj(PYOBJ_TYPE, ob_type):
+    ob = lltype.malloc(PYOBJ_TYPE, flavor='raw', track_allocation=False)
+    ob.c_ob_refcnt = 1
+    ob.c_ob_type = ob_type
+    ob.c_ob_pypy_link = 0
+    return ob
+
+
 def make_ref(space, w_obj):
     ZZZ
 
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -15,7 +15,7 @@
 from pypy.module.cpyext import api
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.pyobject import RefcountState, debug_collect
-from pypy.module.cpyext.pyobject import Py_DecRef, InvalidPointerException
+from pypy.module.cpyext.pyobject import Py_DecRef
 from rpython.tool.identity_dict import identity_dict
 from rpython.tool import leakfinder
 from rpython.rlib import rawrefcount
diff --git a/pypy/module/cpyext/test/test_intobject.py b/pypy/module/cpyext/test/test_intobject.py
--- a/pypy/module/cpyext/test/test_intobject.py
+++ b/pypy/module/cpyext/test/test_intobject.py
@@ -1,3 +1,4 @@
+from pypy.module.cpyext.pyobject import from_pyobj
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 import sys
@@ -12,7 +13,7 @@
             y = api.PyInt_AS_LONG(space.wrap(i))
             assert x == i
             assert y == i
-            w_x = api.PyInt_FromLong(x + 1)
+            w_x = from_pyobj(space, api.PyInt_FromLong(x + 1))
             assert space.type(w_x) is space.w_int
             assert space.eq_w(w_x, space.wrap(i + 1))
 
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -1,16 +1,33 @@
 from pypy.interpreter.error import OperationError
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
-                                    build_type_checkers)
+    cpython_struct, PyObjectFields, build_type_checkers, bootstrap_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
-    borrow_from, make_ref, from_ref)
+                                         setup_class_for_cpyext)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.tupleobject import W_TupleObject, W_AbstractTupleObject
+
+PyTupleObjectStruct = lltype.ForwardReference()
+PyTupleObject = lltype.Ptr(PyTupleObjectStruct)
+PyTupleObjectFields = PyObjectFields + \
+    (("ob_item", rffi.CArray(PyObject)),)
+cpython_struct("PyTupleObject", PyTupleObjectFields, PyTupleObjectStruct)
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
 
+ at bootstrap_function
+def init_intobject(space):
+    "Type description of PyTupleObject"
+    setup_class_for_cpyext(W_AbstractTupleObject,
+                           basestruct=PyTupleObject.TO,
+                           )
+                           #fill_pyobj=int_fill_pyobj,
+                           #fill_pypy=int_fill_pypy,
+                           #realize_subclass_of=W_IntObject)
+
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
+    ZZZ
     return W_TupleObject([space.w_None] * size)
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
@@ -30,10 +47,13 @@
     assert isinstance(w_t, W_TupleObject)
     w_t.wrappeditems[pos] = w_obj
 
- at cpython_api([PyObject, Py_ssize_t], PyObject)
+ at cpython_api([PyObject, Py_ssize_t], PyObject, result_borrowed=True)
 def PyTuple_GetItem(space, w_t, pos):
-    if not PyTuple_Check(space, w_t):
+    if not isinstance(w_t, W_AbstractTupleObject):
         PyErr_BadInternalCall(space)
+    #if w_t.cpyext_returned_items_can_be_borrowed:
+    ZZZ.x.x.x
+    xxxxxxx
     w_obj = space.getitem(w_t, space.wrap(pos))
     return borrow_from(w_t, w_obj)
 
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -2,6 +2,7 @@
 
 from rpython.rlib import jit
 from rpython.rlib.objectmodel import specialize, instantiate
+from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rlib.rstring import rsplit
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rtyper.lltypesystem import rffi, lltype
@@ -22,7 +23,7 @@
 from pypy.module.cpyext.pyobject import (
     PyObject, make_ref, create_ref, get_typedescr, from_pyobj, as_pyobj,
     setup_class_for_cpyext, get_pyobj_and_incref, get_pyobj_and_xincref,
-    track_reference, RefcountState, borrow_from, Py_DecRef)
+    track_reference, RefcountState, borrow_from, Py_DecRef, RRC_PERMANENT)
 from pypy.module.cpyext.slotdefs import (
     slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function)
 from pypy.module.cpyext.state import State
@@ -200,11 +201,9 @@
     w_subtype = args_w[0]
     w_args = space.newtuple(args_w[1:])
 
-    subtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_subtype))
-    try:
-        w_obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds)
-    finally:
-        Py_DecRef(space, w_subtype)
+    subtype = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_subtype))
+    w_obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds)
+    keepalive_until_here(w_subtype)
     return w_obj
 
 @specialize.memo()
@@ -463,7 +462,7 @@
     pto = lltype.malloc(PyTypeObject, flavor='raw', zero=True,
                         track_allocation=False)
     pto.c_tp_flags |= Py_TPFLAGS_READYING
-    return pto, False
+    return pto, RRC_PERMANENT
 
 def type_fill_pyobj(space, w_type, pto):
     """
@@ -561,7 +560,7 @@
 
     w_metatype = from_pyobj(space, pto.c_ob_type)
     w_type = space.allocate_instance(W_TypeObject, w_metatype)
-    return w_type, False
+    return w_type, RRC_PERMANENT
 
 def type_fill_pypy(space, w_type, py_obj):
     pto = rffi.cast(PyTypeObjectPtr, py_obj)
diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py
--- a/pypy/objspace/std/specialisedtupleobject.py
+++ b/pypy/objspace/std/specialisedtupleobject.py
@@ -132,6 +132,8 @@
 Cls_oo = make_specialised_class((object, object))
 Cls_ff = make_specialised_class((float, float))
 
+Cls_oo.cpyext_returned_items_can_be_borrowed = True
+
 def makespecialisedtuple(space, list_w):
     from pypy.objspace.std.intobject import W_IntObject
     from pypy.objspace.std.floatobject import W_FloatObject
diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py
--- a/pypy/objspace/std/tupleobject.py
+++ b/pypy/objspace/std/tupleobject.py
@@ -32,6 +32,7 @@
 
 class W_AbstractTupleObject(W_Root):
     __slots__ = ()
+    cpyext_returned_items_can_be_borrowed = False
 
     def __repr__(self):
         """representation for debugging purposes"""
@@ -248,6 +249,7 @@
 
 class W_TupleObject(W_AbstractTupleObject):
     _immutable_fields_ = ['wrappeditems[*]']
+    cpyext_returned_items_can_be_borrowed = True
 
     def __init__(self, wrappeditems):
         make_sure_not_resized(wrappeditems)
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -112,8 +112,11 @@
         else:
             ob.c_ob_pypy_link = 0
             if ob.c_ob_refcnt >= REFCNT_FROM_PYPY_LIGHT:
-                assert ob.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
-                lltype.free(ob, flavor='raw', track_allocation=track_allocation)
+                ob.c_ob_refcnt -= REFCNT_FROM_PYPY_LIGHT
+                ob.c_ob_pypy_link = 0
+                if ob.c_ob_refcnt == 0:
+                    lltype.free(ob, flavor='raw',
+                                track_allocation=track_allocation)
             else:
                 assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
                 assert ob.c_ob_refcnt < int(REFCNT_FROM_PYPY_LIGHT * 0.99)


More information about the pypy-commit mailing list