[pypy-svn] r78987 - in pypy/branch/rlist-jit/pypy/module/cpyext: . include test

arigo at codespeak.net arigo at codespeak.net
Thu Nov 11 10:09:52 CET 2010


Author: arigo
Date: Thu Nov 11 10:09:49 2010
New Revision: 78987

Modified:
   pypy/branch/rlist-jit/pypy/module/cpyext/api.py
   pypy/branch/rlist-jit/pypy/module/cpyext/include/tupleobject.h
   pypy/branch/rlist-jit/pypy/module/cpyext/test/test_tupleobject.py
   pypy/branch/rlist-jit/pypy/module/cpyext/tupleobject.py
Log:
Implement PyTupleObject similarly to PyStringObject to support
PyTuple_SetItem().  Intermediate check-in: segfaults.


Modified: pypy/branch/rlist-jit/pypy/module/cpyext/api.py
==============================================================================
--- pypy/branch/rlist-jit/pypy/module/cpyext/api.py	(original)
+++ pypy/branch/rlist-jit/pypy/module/cpyext/api.py	Thu Nov 11 10:09:49 2010
@@ -367,7 +367,7 @@
         }.items():
         GLOBALS['Py%s_Type#' % (cpyname, )] = ('PyTypeObject*', pypyexpr)
 
-    for cpyname in 'Method List Int Long Dict Tuple Class'.split():
+    for cpyname in 'Method List Int Long Dict Class'.split():
         FORWARD_DECLS.append('typedef struct { PyObject_HEAD } '
                              'Py%sObject' % (cpyname, ))
 build_exported_objects()

Modified: pypy/branch/rlist-jit/pypy/module/cpyext/include/tupleobject.h
==============================================================================
--- pypy/branch/rlist-jit/pypy/module/cpyext/include/tupleobject.h	(original)
+++ pypy/branch/rlist-jit/pypy/module/cpyext/include/tupleobject.h	Thu Nov 11 10:09:49 2010
@@ -10,9 +10,18 @@
 /* defined in varargswrapper.c */
 PyObject * PyTuple_Pack(Py_ssize_t, ...);
 
-#define PyTuple_SET_ITEM PyTuple_SetItem
-#define PyTuple_GET_ITEM PyTuple_GetItem
+typedef struct {
+    PyObject_HEAD
+    PyObject **items;
+    Py_ssize_t size;
+} PyTupleObject;
 
+/* Macro, trading safety for speed */
+#define PyTuple_GET_ITEM(op, i) (((PyTupleObject *)(op))->items[i])
+#define PyTuple_GET_SIZE(op)    (((PyTupleObject *)(op))->size)
+
+/* Macro, *only* to be used to fill in brand new tuples */
+#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->items[i] = v)
 
 #ifdef __cplusplus
 }

Modified: pypy/branch/rlist-jit/pypy/module/cpyext/test/test_tupleobject.py
==============================================================================
--- pypy/branch/rlist-jit/pypy/module/cpyext/test/test_tupleobject.py	(original)
+++ pypy/branch/rlist-jit/pypy/module/cpyext/test/test_tupleobject.py	Thu Nov 11 10:09:49 2010
@@ -6,25 +6,28 @@
 
 class TestTupleObject(BaseApiTest):
     def test_tupleobject(self, space, api):
+        XXX
         assert not api.PyTuple_Check(space.w_None)
-        assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1
+        #assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1     XXX
         atuple = space.newtuple([0, 1, 'yay'])
         assert api.PyTuple_Size(atuple) == 3
         assert api.PyTuple_GET_SIZE(atuple) == 3
-        raises(TypeError, api.PyTuple_Size(space.newlist([])))
+        #raises(TypeError, api.PyTuple_Size(space.newlist([])))     XXX
         api.PyErr_Clear()
     
     def test_tuple_resize(self, space, api):
-        py_tuple = api.PyTuple_New(3)
+        XXX
+        ref_tup = api.PyTuple_New(3)
         ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-        ar[0] = rffi.cast(PyObject, make_ref(space, py_tuple))
+        ar[0] = rffi.cast(PyObject, ref_tup)
         api._PyTuple_Resize(ar, 2)
-        py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 2
+        assert ar[0] == rffi.cast(PyObject, ref_tup)
+        # ^^^ our _PyTuple_Resize does not actually need to change the ptr so far
+        assert api.PyTuple_Size(ar[0]) == 2
+        assert api.PyTuple_GET_SIZE(ar[0]) == 2
         
         api._PyTuple_Resize(ar, 10)
-        py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 10
+        assert api.PyTuple_Size(ar[0]) == 10
         
         api.Py_DecRef(ar[0])
         lltype.free(ar, flavor='raw')

Modified: pypy/branch/rlist-jit/pypy/module/cpyext/tupleobject.py
==============================================================================
--- pypy/branch/rlist-jit/pypy/module/cpyext/tupleobject.py	(original)
+++ pypy/branch/rlist-jit/pypy/module/cpyext/tupleobject.py	Thu Nov 11 10:09:49 2010
@@ -1,55 +1,129 @@
 from pypy.interpreter.error import OperationError
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
-                                    build_type_checkers)
+                                    build_type_checkers, PyObjectFields,
+                                    cpython_struct, bootstrap_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
-    borrow_from, make_ref, from_ref)
+    borrow_from, make_ref, from_ref, make_typedescr)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.objspace.std.tupleobject import W_TupleObject
 
+##
+## Implementation of PyTupleObject
+## ===============================
+##
+## We have the same problem as PyStringObject: a PyTupleObject can be
+## initially used in a read-write way with PyTuple_New(), PyTuple_SetItem()
+## and _PyTuple_Resize().
+##
+## The 'size' and 'items' fields of a PyTupleObject are always valid.
+## Apart from that detail, see the big comment in stringobject.py for
+## more information.
+##
+
+ARRAY_OF_PYOBJ = rffi.CArrayPtr(PyObject)
+PyTupleObjectStruct = lltype.ForwardReference()
+PyTupleObject = lltype.Ptr(PyTupleObjectStruct)
+PyTupleObjectFields = PyObjectFields + \
+    (("items", ARRAY_OF_PYOBJ), ("size", Py_ssize_t))
+cpython_struct("PyTupleObject", PyTupleObjectFields, PyTupleObjectStruct)
+
+ at bootstrap_function
+def init_tupleobject(space):
+    "Type description of PyTupleObject"
+    make_typedescr(space.w_tuple.instancetypedef,
+                   basestruct=PyTupleObject.TO,
+                   attach=tuple_attach,
+                   dealloc=tuple_dealloc,
+                   realize=tuple_realize)
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
 
+def new_empty_tuple(space, length):
+    """
+    Allocate a PyTupleObject and its array, but without a corresponding
+    interpreter object.  The array items may be mutated, until
+    tuple_realize() is called.
+    """
+    typedescr = get_typedescr(space.w_tuple.instancetypedef)
+    py_obj = typedescr.allocate(space, space.w_tuple)
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+
+    py_tup.c_items = lltype.malloc(ARRAY_OF_PYOBJ.TO, length,
+                                   flavor='raw', zero=True)
+    py_tup.c_size = length
+    return py_tup
+
+def tuple_attach(space, py_obj, w_obj):
+    """
+    Fills a newly allocated PyTupleObject with the given tuple object.
+    """
+    items_w = space.fixedview(w_obj)
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    py_tup.c_items = lltype.malloc(ARRAY_OF_PYOBJ.TO, len(items_w),
+                                    flavor='raw')
+    py_tup.c_size = len(items_w)
+    for i in range(len(items_w)):
+        py_tup.c_items[i] = make_ref(space, items_w[i])
+
+def tuple_realize(space, py_obj):
+    """
+    Creates the tuple in the interpreter. The PyTupleObject items array
+    must not be modified after this call.
+    """
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    # If your CPython extension creates a self-referential tuple
+    # with PyTuple_SetItem(), you loose.
+    c_items = py_tup.c_items
+    items_w = [from_ref(space, c_items[i]) for i in range(py_tup.c_size)]
+    w_obj = space.newtuple(items_w)
+    track_reference(space, py_obj, w_obj)
+    return w_obj
+
+ at cpython_api([PyObject], lltype.Void, external=False)
+def tuple_dealloc(space, py_obj):
+    """Frees allocated PyTupleObject resources.
+    """
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    for i in range(py_tup.c_size):
+        Py_DecRef(space, py_tup.c_items[i])
+    lltype.free(py_tup.c_items, flavor="raw")
+    from pypy.module.cpyext.object import PyObject_dealloc
+    PyObject_dealloc(space, py_obj)
+
+#_______________________________________________________________________
+
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
-    return space.newtuple([space.w_None] * size)
+    return rffi.cast(PyObject, new_empty_tuple(space, size))
 
 @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!!!
-        PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_t.wrappeditems[pos] = w_obj
-    Py_DecRef(space, w_obj) # SetItem steals a reference!
+def PyTuple_SetItem(space, ref, pos, ref_item):
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    # XXX -- then if it fails it should also steal a reference, test it!!!
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    # XXX raise some exception if PyTuple_SetItem() is called on an
+    # tuple that already has a corresponding interpreter object
+    ref_old = ref_tup.c_items[pos]
+    ref_tup.c_items[pos] = ref      # SetItem steals a reference!
+    Py_DecRef(space, ref_old)
     return 0
 
 @cpython_api([PyObject, Py_ssize_t], PyObject)
-def PyTuple_GetItem(space, w_t, pos):
-    if not PyTuple_Check(space, w_t):
-        PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_obj = w_t.wrappeditems[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. """
-    assert isinstance(w_t, W_TupleObject)
-    return len(w_t.wrappeditems)
+def PyTuple_GetItem(space, ref, pos):
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    return ref_tup.c_items[pos]     # borrowed reference
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
 def PyTuple_Size(space, ref):
     """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)
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    return ref_tup.c_size
 
 
 @cpython_api([PyObjectP, Py_ssize_t], rffi.INT_real, error=-1)
-def _PyTuple_Resize(space, ref, newsize):
+def _PyTuple_Resize(space, refp, newsize):
     """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
@@ -60,18 +134,17 @@
     this function. If the object referenced by *p is replaced, the original
     *p is destroyed.  On failure, returns -1 and sets *p to NULL, and
     raises MemoryError or SystemError."""
-    py_tuple = from_ref(space, ref[0])
-    if not PyTuple_Check(space, py_tuple):
-        PyErr_BadInternalCall(space)
-    assert isinstance(py_tuple, W_TupleObject)
-    py_newtuple = PyTuple_New(space, newsize)
-    
-    to_cp = newsize
-    oldsize = len(py_tuple.wrappeditems)
-    if oldsize < newsize:
-        to_cp = oldsize
-    for i in range(to_cp):
-        py_newtuple.wrappeditems[i] = py_tuple.wrappeditems[i]
-    Py_DecRef(space, ref[0])
-    ref[0] = make_ref(space, py_newtuple)
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    # XXX -- then if it fails it should reset refp[0] to null
+    ref_tup = rffi.cast(PyTupleObject, refp[0])
+    c_newitems = lltype.malloc(ARRAY_OF_PYOBJ.TO, newsize,
+                               flavor='raw', zero=True)
+    c_olditems = ref_tup.c_items
+    for i in range(min(oldsize, newsize)):
+        c_newitems[i] = c_olditems[i]
+    # decref items deleted by shrinkage
+    for i in range(newsize, oldsize):
+        Py_DecRef(space, c_olditems[i])
+    ref_tup.c_items = c_newitems
+    lltype.free(c_olditems, flavor='raw')
     return 0



More information about the Pypy-commit mailing list