[pypy-commit] pypy cpyext-gc-support: Work on tuples

arigo noreply at buildbot.pypy.org
Tue Oct 20 14:00:08 EDT 2015


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

Log:	Work on tuples

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
@@ -593,9 +593,10 @@
             ZZZ
     _PyXxx_Type = func_with_new_name(_PyXxx_Type, '_' + py_type_name)
 
-    def check(space, w_obj):
+    def check(space, py_obj):
         "Implements the Py_Xxx_Check function"
-        w_obj_type = space.type(w_obj)
+        from pypy.module.cpyext.pyobject import from_pyobj
+        w_obj_type = from_pyobj(space, py_obj.c_ob_type)
         w_type = get_w_type(space)
         return (space.is_w(w_obj_type, w_type) or
                 space.is_true(space.issubtype(w_obj_type, w_type)))
diff --git a/pypy/module/cpyext/include/intobject.h b/pypy/module/cpyext/include/intobject.h
--- a/pypy/module/cpyext/include/intobject.h
+++ b/pypy/module/cpyext/include/intobject.h
@@ -7,11 +7,16 @@
 extern "C" {
 #endif
 
+
 typedef struct {
     PyObject_HEAD
     long ob_ival;
 } PyIntObject;
 
+/* Macro, trading safety for speed */
+#define PyInt_AS_LONG(op) (((PyIntObject *)(op))->ob_ival)
+
+
 #ifdef __cplusplus
 }
 #endif
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
@@ -15,8 +15,13 @@
 /* defined in varargswrapper.c */
 PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
 
-#define PyTuple_SET_ITEM PyTuple_SetItem
-#define PyTuple_GET_ITEM PyTuple_GetItem
+
+/* Macro, trading safety for speed */
+#define PyTuple_GET_ITEM(op, i) (((PyTupleObject *)(op))->ob_item[i])
+#define PyTuple_GET_SIZE(op)    Py_SIZE(op)
+
+/* Macro, *only* to be used to fill in brand new tuples */
+#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->ob_item[i] = v)
 
 
 #ifdef __cplusplus
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
@@ -30,11 +30,12 @@
         # --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()).--
+        # --from a W_IntObject, we allocate a PyIntObject and then 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
@@ -93,7 +94,7 @@
         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)
+        return _PyInt_AS_LONG(py_obj)
     else:
         w_obj = from_pyobj(space, py_obj)
         return space.int_w(space.int(w_obj))   # XXX win64: check range
@@ -119,7 +120,7 @@
         raise OperationError(space.w_TypeError,
                              space.wrap("an integer is required, got NULL"))
     if PyInt_Check(space, py_obj):
-        return rffi.cast(rffi.ULONG, PyInt_AS_LONG(space, py_obj))
+        return rffi.cast(rffi.ULONG, _PyInt_AS_LONG(py_obj))
     else:
         w_obj = from_pyobj(space, py_obj)
         num = space.bigint_w(space.int(w_obj))
@@ -136,15 +137,14 @@
         raise OperationError(space.w_TypeError,
                              space.wrap("an integer is required, got NULL"))
     if PyInt_Check(space, py_obj):
-        return rffi.cast(rffi.ULONGLONG, PyInt_AS_LONG(space, py_obj))
+        return rffi.cast(rffi.ULONGLONG, _PyInt_AS_LONG(py_obj))
     else:
         w_obj = from_pyobj(space, py_obj)
         num = space.bigint_w(space.int(w_obj))
         return num.ulonglongmask()
 
 
- at cpython_api([PyObject], lltype.Signed, error=CANNOT_FAIL)
-def PyInt_AS_LONG(space, py_obj):
+def _PyInt_AS_LONG(py_obj):
     """Return the value of the object w_int. No error checking is performed."""
     py_int = rffi.cast(PyIntObject, py_obj)
     return py_int.c_ob_ival
@@ -159,7 +159,7 @@
         raise OperationError(space.w_TypeError,
                              space.wrap("an integer is required, got NULL"))
     if PyInt_Check(space, py_obj):
-        return rffi.cast(Py_ssize_t, PyInt_AS_LONG(space, py_obj))
+        return rffi.cast(Py_ssize_t, _PyInt_AS_LONG(py_obj))
     else:
         w_obj = from_pyobj(space, py_obj)
         return space.int_w(space.int(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
@@ -88,8 +88,6 @@
     if tp_alloc_pyobj or tp_fill_pyobj or realize_subclass_of:
         if realize_subclass_of is None:
             realize_subclass_of = W_Class
-        assert 'typedef' in realize_subclass_of.__dict__, (
-            "no 'typedef' exactly on %s" % (realize_subclass_of,))
         #
         if not tp_alloc_pypy:
             W_CPyExtPlaceHolder = get_cpyextplaceholder_subclass(
@@ -290,6 +288,9 @@
         return lltype.nullptr(PyObject.TO)
 
 
+def pyobj_has_w_obj(pyobj):
+    return rawrefcount.to_obj(W_Root, pyobj) is not None
+
 @specialize.ll()
 def from_pyobj(space, pyobj):
     assert is_pyobj(pyobj)
@@ -369,8 +370,13 @@
 
 
 @specialize.ll()
-def new_pyobj(PYOBJ_TYPE, ob_type):
-    ob = lltype.malloc(PYOBJ_TYPE, flavor='raw', track_allocation=False)
+def new_pyobj(PYOBJ_TYPE, ob_type, length=None):
+    if length is None:
+        ob = lltype.malloc(PYOBJ_TYPE, flavor='raw', track_allocation=False)
+    else:
+        ob = lltype.malloc(PYOBJ_TYPE, length, flavor='raw',
+                           track_allocation=False)
+        ob.c_ob_size = length
     ob.c_ob_refcnt = 1
     ob.c_ob_type = ob_type
     ob.c_ob_pypy_link = 0
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -1,6 +1,8 @@
 import py
 
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+from pypy.module.cpyext.pyobject import PyObject, PyObjectP, from_pyobj
+from pypy.module.cpyext.pyobject import pyobj_has_w_obj, get_pyobj_and_incref
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from rpython.rtyper.lltypesystem import rffi, lltype
 
@@ -9,13 +11,66 @@
 
     def test_tupleobject(self, space, api):
         assert not api.PyTuple_Check(space.w_None)
-        assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1
-        atuple = space.newtuple([0, 1, 'yay'])
+        py_none = get_pyobj_and_incref(space, space.w_None)
+        assert api.PyTuple_SetItem(space.w_None, 0, py_none) == -1
+        atuple = space.newtuple([space.wrap(0), space.wrap(1),
+                                 space.wrap('yay')])
         assert api.PyTuple_Size(atuple) == 3
-        assert api.PyTuple_GET_SIZE(atuple) == 3
+        #assert api.PyTuple_GET_SIZE(atuple) == 3   --- a C macro
         raises(TypeError, api.PyTuple_Size(space.newlist([])))
         api.PyErr_Clear()
-    
+
+    def test_tupleobject_spec_ii(self, space, api):
+        atuple = space.newtuple([space.wrap(10), space.wrap(11)])
+        assert api.PyTuple_Size(atuple) == 2
+        w_obj1 = from_pyobj(space, api.PyTuple_GetItem(atuple, 0))
+        w_obj2 = from_pyobj(space, api.PyTuple_GetItem(atuple, 1))
+        assert space.eq_w(w_obj1, space.wrap(10))
+        assert space.eq_w(w_obj2, space.wrap(11))
+
+    def test_tupleobject_spec_oo(self, space, api):
+        w_obj1 = space.newlist([])
+        w_obj2 = space.newlist([])
+        atuple = space.newtuple([w_obj1, w_obj2])
+        assert api.PyTuple_Size(atuple) == 2
+        assert from_pyobj(space, api.PyTuple_GetItem(atuple, 0)) is w_obj1
+        assert from_pyobj(space, api.PyTuple_GetItem(atuple, 1)) is w_obj2
+
+    def test_new_setitem(self, space, api):
+        w_obj1 = space.newlist([])
+        pyobj1 = get_pyobj_and_incref(space, w_obj1)
+        w_obj2 = space.newlist([])
+        pyobj2 = get_pyobj_and_incref(space, w_obj2)
+        py_tuple = api.PyTuple_New(2)
+        assert not pyobj_has_w_obj(py_tuple)
+
+        assert pyobj1.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+        assert pyobj2.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+        api.PyTuple_SetItem(py_tuple, 0, pyobj1)
+        api.PyTuple_SetItem(py_tuple, 1, pyobj2)
+        assert pyobj1.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+        assert pyobj2.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+
+        assert api.PyTuple_GetItem(py_tuple, 0) == pyobj1
+        assert pyobj1.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+
+        api.PyTuple_SetItem(py_tuple, 0, pyobj2)
+        assert pyobj1.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 0
+        assert pyobj2.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+
+        assert not pyobj_has_w_obj(py_tuple)
+        w_tup = from_pyobj(space, py_tuple)
+        assert w_tup is from_pyobj(space, py_tuple)
+        assert api.PyTuple_GetItem(py_tuple, 1) == pyobj2
+        assert pyobj1.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 0
+        assert pyobj2.c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+
+        assert space.getitem(w_tup, space.wrap(0)) is w_obj2
+        assert space.getitem(w_tup, space.wrap(1)) is w_obj2
+
+        assert api.PyTuple_SetItem(py_tuple, 0, pyobj1) == -1
+        api.PyErr_Clear()
+
     def test_tuple_resize(self, space, api):
         py_tuple = api.PyTuple_New(3)
         ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
@@ -31,18 +86,6 @@
         api.Py_DecRef(ar[0])
         lltype.free(ar, flavor='raw')
 
-    def test_setitem(self, space, api):
-        atuple = space.newtuple([space.wrap(0), space.wrap("hello")])
-        assert api.PyTuple_Size(atuple) == 2
-        assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
-        assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap("hello"))
-        w_obj = space.wrap(1)
-        api.Py_IncRef(w_obj)
-        api.PyTuple_SetItem(atuple, 1, w_obj)
-        assert api.PyTuple_Size(atuple) == 2
-        assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
-        assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap(1))
-
     def test_getslice(self, space, api):
         w_tuple = space.newtuple([space.wrap(i) for i in range(10)])
         w_slice = api.PyTuple_GetSlice(w_tuple, 3, -3)
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,79 +1,114 @@
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import OperationError, oefmt
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
-    cpython_struct, PyObjectFields, build_type_checkers, bootstrap_function)
+    cpython_struct, PyVarObjectFields, build_type_checkers3, bootstrap_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
-                                         setup_class_for_cpyext)
+    setup_class_for_cpyext, as_pyobj, get_pyobj_and_incref, from_pyobj,
+    pyobj_has_w_obj, RRC_PERMANENT, RRC_PERMANENT_LIGHT, new_pyobj)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject, W_AbstractTupleObject
 
 PyTupleObjectStruct = lltype.ForwardReference()
 PyTupleObject = lltype.Ptr(PyTupleObjectStruct)
-PyTupleObjectFields = PyObjectFields + \
+PyTupleObjectFields = PyVarObjectFields + \
     (("ob_item", rffi.CArray(PyObject)),)
 cpython_struct("PyTupleObject", PyTupleObjectFields, PyTupleObjectStruct)
 
-PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
+PyTuple_Check, PyTuple_CheckExact, _PyTuple_Type = build_type_checkers3("Tuple")
+
 
 @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)
+    setup_class_for_cpyext(
+        W_AbstractTupleObject,
+        basestruct=PyTupleObjectStruct,
+
+        # --from a W_TupleObject, we call this function to allocate and
+        #   fill a PyTupleObject --
+        alloc_pyobj=tuple_alloc_pyobj,
+
+        # --reverse direction: from a PyTupleObject, we make a W_TupleObject
+        #   by instantiating a custom subclass of W_TupleObject--
+        realize_subclass_of=W_TupleObject,
+
+        # --and then we call this function to initialize the W_TupleObject--
+        fill_pypy=tuple_fill_pypy,
+        )
+
+def tuple_alloc_pyobj(space, w_obj):
+    """
+    Makes a PyTupleObject from a W_AbstractTupleObject.
+    """
+    assert isinstance(w_obj, W_AbstractTupleObject)
+    lst_w = w_obj.tolist()
+    ob = lltype.malloc(PyTupleObjectStruct, len(lst_w), flavor='raw',
+                       track_allocation=False)
+    ob.c_ob_size = len(lst_w)
+    if w_obj.cpyext_returned_items_can_be_borrowed:
+        for i in range(len(lst_w)):
+            ob.c_ob_item[i] = as_pyobj(space, lst_w[i])
+        return ob, RRC_PERMANENT_LIGHT
+    else:
+        for i in range(len(lst_w)):
+            ob.c_ob_item[i] = get_pyobj_and_incref(space, lst_w[i])
+        return ob, RRC_PERMANENT
+
+def tuple_fill_pypy(space, w_obj, py_obj):
+    """
+    Fills in a W_TupleObject from a PyTupleObject.
+    """
+    py_tuple = rffi.cast(PyTupleObject, py_obj)
+    objects_w = [from_pyobj(space, py_tuple.c_ob_item[i])
+                 for i in range(py_tuple.c_ob_size)]
+    W_TupleObject.__init__(w_obj, objects_w)
+
 
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
-    ZZZ
-    return W_TupleObject([space.w_None] * size)
+    py_tuple = new_pyobj(PyTupleObjectStruct, _PyTuple_Type(space), size)
+    for i in range(size):
+        py_tuple.c_ob_item[i] = lltype.nullptr(PyObject.TO)
+    return py_tuple
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
-def PyTuple_SetItem(space, w_t, pos, w_obj):
-    if not PyTuple_Check(space, w_t):
-        # XXX this should also steal a reference, test it!!!
+def PyTuple_SetItem(space, py_t, pos, py_obj):
+    if not PyTuple_Check(space, py_t) or py_t.c_ob_refcnt != 1:
+        Py_DecRef(space, py_obj)
         PyErr_BadInternalCall(space)
-    _setitem_tuple(w_t, pos, w_obj)
-    Py_DecRef(space, w_obj) # SetItem steals a reference!
+    py_tuple = rffi.cast(PyTupleObject, py_t)
+    if pos < 0 or pos >= py_tuple.c_ob_size:
+        raise oefmt(w_IndexError, "tuple assignment index out of range")
+
+    olditem = py_tuple.c_ob_item[pos]
+    py_tuple.c_ob_item[pos] = py_obj
+
+    if olditem:
+        Py_DecRef(space, olditem)
     return 0
 
-def _setitem_tuple(w_t, pos, w_obj):
-    # this function checks that w_t is really a W_TupleObject.  It
-    # should only ever be called with a freshly built tuple from
-    # PyTuple_New(), which always return a W_TupleObject, even if there
-    # are also other implementations of tuples.
-    assert isinstance(w_t, W_TupleObject)
-    w_t.wrappeditems[pos] = w_obj
+ at cpython_api([PyObject, Py_ssize_t], PyObject, result_borrowed=True)
+def PyTuple_GetItem(space, py_t, pos):
+    if not PyTuple_Check(space, py_t):
+        PyErr_BadInternalCall(space)
+    py_tuple = rffi.cast(PyTupleObject, py_t)
+    if pos < 0 or pos >= py_tuple.c_ob_size:
+        raise oefmt(w_IndexError, "tuple assignment index out of range")
 
- at cpython_api([PyObject, Py_ssize_t], PyObject, result_borrowed=True)
-def PyTuple_GetItem(space, w_t, pos):
-    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)
-
- at cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
-def PyTuple_GET_SIZE(space, w_t):
-    """Return the size of the tuple p, which must be non-NULL and point to a tuple;
-    no error checking is performed. """
-    return space.int_w(space.len(w_t))
+    return py_tuple.c_ob_item[pos]     # borrowed
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
-def PyTuple_Size(space, ref):
+def PyTuple_Size(space, py_t):
     """Take a pointer to a tuple object, and return the size of that tuple."""
-    if not PyTuple_Check(space, ref):
-        raise OperationError(space.w_TypeError,
-                             space.wrap("expected tuple object"))
-    return PyTuple_GET_SIZE(space, ref)
+    if not PyTuple_Check(space, py_t):
+        PyErr_BadInternalCall(space)
+    py_tuple = rffi.cast(PyTupleObject, py_t)
+    return py_tuple.c_ob_size
 
 
 @cpython_api([PyObjectP, Py_ssize_t], rffi.INT_real, error=-1)
 def _PyTuple_Resize(space, ref, newsize):
+    ZZZ
     """Can be used to resize a tuple.  newsize will be the new length of the tuple.
     Because tuples are supposed to be immutable, this should only be used if there
     is only one reference to the object.  Do not use this if the tuple may already
@@ -101,6 +136,7 @@
 
 @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
 def PyTuple_GetSlice(space, w_obj, low, high):
+    ZZZ
     """Take a slice of the tuple pointed to by p from low to high and return it
     as a new tuple.
     """
diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py
--- a/rpython/rtyper/lltypesystem/ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/ll2ctypes.py
@@ -515,8 +515,10 @@
                 struct_use_ctypes_storage(struct_container, struct_storage)
                 struct_container._setparentstructure(container, field_name)
             elif isinstance(FIELDTYPE, lltype.Array):
-                assert FIELDTYPE._hints.get('nolength', False) == False
-                arraycontainer = _array_of_known_length(FIELDTYPE)
+                if FIELDTYPE._hints.get('nolength', False):
+                    arraycontainer = _array_of_unknown_length(FIELDTYPE)
+                else:
+                    arraycontainer = _array_of_known_length(FIELDTYPE)
                 arraycontainer._storage = ctypes.pointer(
                     getattr(ctypes_storage.contents, field_name))
                 arraycontainer._setparentstructure(container, field_name)
@@ -934,7 +936,8 @@
                 REAL_TYPE = T.TO
                 if T.TO._arrayfld is not None:
                     carray = getattr(cobj.contents, T.TO._arrayfld)
-                    container = lltype._struct(T.TO, carray.length)
+                    length = getattr(carray, 'length', 9999)   # XXX
+                    container = lltype._struct(T.TO, length)
                 else:
                     # special treatment of 'OBJECT' subclasses
                     if get_rtyper() and lltype._castdepth(REAL_TYPE, OBJECT) >= 0:


More information about the pypy-commit mailing list