Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r83927:5a49cbc33223
Date: 2016-04-26 23:11 +0200
http://bitbucket.org/pypy/pypy/changeset/5a49cbc33223/

Log:    Merged in devin.jeanpierre/pypy-macros (pull request #435)

        Implement PyList_SET_ITEM with CPython's behavior, instead of
        SetItem's.

diff --git a/pypy/module/cpyext/include/listobject.h 
b/pypy/module/cpyext/include/listobject.h
--- a/pypy/module/cpyext/include/listobject.h
+++ b/pypy/module/cpyext/include/listobject.h
@@ -1,2 +1,1 @@
 #define PyList_GET_ITEM PyList_GetItem
-#define PyList_SET_ITEM PyList_SetItem
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -3,7 +3,7 @@
 from pypy.module.cpyext.api import (cpython_api, CANNOT_FAIL, Py_ssize_t,
                                     build_type_checkers)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.module.cpyext.pyobject import Py_DecRef, PyObject
+from pypy.module.cpyext.pyobject import Py_DecRef, PyObject, make_ref
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.interpreter.error import OperationError
 
@@ -21,6 +21,25 @@
     """
     return space.newlist([None] * len)
 
+@cpython_api([PyObject, Py_ssize_t, PyObject], PyObject, error=CANNOT_FAIL,
+             result_borrowed=True)
+def PyList_SET_ITEM(space, w_list, index, w_item):
+    """Macro form of PyList_SetItem() without error checking. This is normally
+    only used to fill in new lists where there is no previous content.
+
+    This function "steals" a reference to item, and, unlike PyList_SetItem(),
+    does not discard a reference to any item that it being replaced; any
+    reference in list at position i will be leaked.
+    """
+    assert isinstance(w_list, W_ListObject)
+    assert 0 <= index < w_list.length
+    # Deliberately leak, so that it can be safely decref'd.
+    make_ref(space, w_list.getitem(index))
+    Py_DecRef(space, w_item)
+    w_list.setitem(index, w_item)
+    return w_item
+
+
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
 def PyList_SetItem(space, w_list, index, w_item):
     """Set the item at index index in list to item.  Return 0 on success
diff --git a/pypy/module/cpyext/test/test_listobject.py 
b/pypy/module/cpyext/test/test_listobject.py
--- a/pypy/module/cpyext/test/test_listobject.py
+++ b/pypy/module/cpyext/test/test_listobject.py
@@ -136,3 +136,45 @@
         l = [1, 2, 3]
         module.setlistitem(l,0)
         assert l == [None, 2, 3]
+
+    def test_get_item_macro(self):
+        module = self.import_extension('foo', [
+             ("test_get_item", "METH_NOARGS",
+             """
+                PyObject* o = PyList_New(1);
+
+                PyObject* o2 = PyInt_FromLong(0);
+                PyList_SET_ITEM(o, 0, o2);
+                o2 = NULL;
+
+                PyObject* o3 = PyList_GET_ITEM(o, 0);
+                Py_INCREF(o3);
+                Py_CLEAR(o);
+                return o3;
+             """)])
+        assert module.test_get_item() == 0
+
+    def test_set_item_macro(self):
+        """PyList_SET_ITEM leaks a reference to the target."""
+        module = self.import_extension('foo', [
+             ("test_refcount_diff_after_setitem", "METH_NOARGS",
+             """
+                PyObject* o = PyList_New(0);
+                PyObject* o2 = PyList_New(0);
+
+                PyList_Append(o, o2);  // does not steal o2
+
+                Py_ssize_t refcount = Py_REFCNT(o2);
+
+                // Steal a reference to o2, but leak the old reference to o2.
+                // The net result should be no change in refcount.
+                PyList_SET_ITEM(o, 0, o2);
+
+                Py_ssize_t new_refcount = Py_REFCNT(o2);
+
+                Py_CLEAR(o);
+                Py_DECREF(o2); // append incref'd.
+                // Py_CLEAR(o2);  // naive implementation would fail here.
+                return PyLong_FromSsize_t(new_refcount - refcount);
+             """)])
+        assert module.test_refcount_diff_after_setitem() == 0
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to