Author: Matti Picus <matti.pi...@gmail.com> Branch: cpyext-obj-stealing Changeset: r91135:8a55a93fe3df Date: 2017-04-26 22:22 +0300 http://bitbucket.org/pypy/pypy/changeset/8a55a93fe3df/
Log: make listobject set/get refcount semantics exactly match cpython 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, make_ref +from pypy.module.cpyext.pyobject import decref, PyObject, make_ref, w_obj_has_pyobj from pypy.objspace.std.listobject import W_ListObject from pypy.interpreter.error import oefmt @@ -24,18 +24,15 @@ @cpython_api([rffi.VOIDP, 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 + """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. + "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 @@ -48,11 +45,19 @@ This function "steals" a reference to item and discards a reference to an item already in the list at the affected position. """ + from pypy.module.cpyext.sequence import CPyListStrategy + cpy_strategy = space.fromcache(CPyListStrategy) if not isinstance(w_list, W_ListObject): + decref(space, w_item) PyErr_BadInternalCall(space) if index < 0 or index >= w_list.length(): + decref(space, w_item) raise oefmt(space.w_IndexError, "list assignment index out of range") - #Py_DecRef(space, w_item) + if w_list.strategy is not cpy_strategy: + w_list.ensure_object_strategy() # make sure we can use borrowed obj + w_old = w_list.getitem(index) + if w_obj_has_pyobj(w_old): + decref(space, w_old) w_list.setitem(index, w_item) return 0 @@ -63,11 +68,11 @@ supported. If pos is out of bounds, return NULL and set an IndexError exception.""" from pypy.module.cpyext.sequence import CPyListStrategy + cpy_strategy = space.fromcache(CPyListStrategy) if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) if index < 0 or index >= w_list.length(): raise oefmt(space.w_IndexError, "list index out of range") - cpy_strategy = space.fromcache(CPyListStrategy) if w_list.strategy is not cpy_strategy: w_list.ensure_object_strategy() # make sure we can return a borrowed obj w_res = w_list.getitem(index) @@ -78,6 +83,7 @@ def PyList_Append(space, w_list, w_item): if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) + make_ref(space, w_item) w_list.append(w_item) return 0 @@ -86,6 +92,7 @@ """Insert the item item into list list in front of index index. Return 0 if successful; return -1 and set an exception if unsuccessful. Analogous to list.insert(index, item).""" + make_ref(space, w_item) space.call_method(space.w_list, "insert", w_list, space.newint(index), w_item) return 0 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 @@ -249,6 +249,8 @@ w_obj = rawrefcount.to_obj(W_Root, pyobj) return w_obj is not None and w_obj is not w_marker_deallocating +def w_obj_has_pyobj(w_obj): + return bool(rawrefcount.from_obj(PyObject, w_obj)) def is_pyobj(x): if x is None or isinstance(x, W_Root): 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 @@ -200,22 +200,29 @@ ("test_refcount_diff_after_setitem", "METH_NOARGS", """ PyObject* o = PyList_New(0); - PyObject* o2 = PyList_New(0); + PyObject* o2 = PyLong_FromLong(0); + Py_INCREF(o2); Py_ssize_t refcount, new_refcount; - PyList_Append(o, o2); // does not steal o2 + refcount = Py_REFCNT(o2); // 1 - refcount = Py_REFCNT(o2); + PyList_Append(o, o2); - // Steal a reference to o2, but leak the old reference to o2. + new_refcount = Py_REFCNT(o2); + + if (new_refcount != refcount + 1) + return PyLong_FromSsize_t(-10); + refcount = new_refcount; + + // Steal a reference to o2, leak the old reference to o2. // The net result should be no change in refcount. PyList_SET_ITEM(o, 0, o2); new_refcount = Py_REFCNT(o2); - Py_CLEAR(o); Py_DECREF(o2); // append incref'd. - // Py_CLEAR(o2); // naive implementation would fail here. + Py_DECREF(o); + Py_DECREF(o2); return PyLong_FromSsize_t(new_refcount - refcount); """)]) assert module.test_refcount_diff_after_setitem() == 0 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit