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