Author: Antonio Cuni <[email protected]>
Branch: cpyext-avoid-roundtrip
Changeset: r92757:7860795a7204
Date: 2017-10-14 00:06 +0200
http://bitbucket.org/pypy/pypy/changeset/7860795a7204/
Log: we cannot cache the empty tuple as CPython does, else we end up
having the same py_obj for different w_objs
diff --git a/pypy/module/cpyext/src/tupleobject.c
b/pypy/module/cpyext/src/tupleobject.c
--- a/pypy/module/cpyext/src/tupleobject.c
+++ b/pypy/module/cpyext/src/tupleobject.c
@@ -1,5 +1,13 @@
-/* Tuple object implementation, stolen&adapted from CPython */
+/* Tuple object implementation, stolen&adapted from CPython.
+
+ One important difference is that CPython caches the empty tuple separately
+ and always return the very same object, while we always return a fresh one.
+ The reasons is that space.newtuple([]) always return different objects, and
+ we want to ensure that the following is always true:
+
+ w_a != w_b ==> as_pyobj(w_b) != as_pyobj(w_b)
+ */
#include "Python.h"
@@ -12,9 +20,6 @@
#endif
#if PyTuple_MAXSAVESIZE > 0
-/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty
- tuple () of which at most one instance will be allocated.
-*/
static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
#endif
@@ -29,11 +34,6 @@
return NULL;
}
#if PyTuple_MAXSAVESIZE > 0
- if (size == 0 && free_list[0]) {
- op = free_list[0];
- Py_INCREF(op);
- return (PyObject *) op;
- }
if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
free_list[size] = (PyTupleObject *) op->ob_item[0];
numfree[size]--;
@@ -56,13 +56,6 @@
}
for (i=0; i < size; i++)
op->ob_item[i] = NULL;
-#if PyTuple_MAXSAVESIZE > 0
- if (size == 0) {
- free_list[0] = op;
- ++numfree[0];
- Py_INCREF(op); /* extra INCREF so that this is never freed */
- }
-#endif
_PyObject_GC_TRACK(op);
return (PyObject *) op;
}
@@ -76,7 +69,7 @@
register Py_ssize_t len = Py_SIZE(op);
PyObject_GC_UnTrack(op);
Py_TRASHCAN_SAFE_BEGIN(op)
- if (len > 0) {
+ if (len >= 0) {
i = len;
while (--i >= 0)
Py_XDECREF(op->ob_item[i]);
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,6 @@
import py
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref,
from_ref, decref
+from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref,
from_ref, decref, as_pyobj
from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from rpython.rtyper.lltypesystem import rffi, lltype
@@ -27,6 +27,31 @@
py.test.raises(FatalError, from_ref, space, py_tuple)
decref(space, py_tuple)
+ def test_freelist(self, space, api):
+ state = space.fromcache(State)
+ # check that we don't cache the empty tuple
+ py_a = state.C.PyTuple_New(0)
+ py_b = state.C.PyTuple_New(0)
+ assert py_a != py_b
+ assert py_a.c_ob_refcnt == 1
+ assert py_b.c_ob_refcnt == 1
+ decref(space, py_a)
+ decref(space, py_b)
+ #
+ # check that the freelist is working
+ py_c = state.C.PyTuple_New(0)
+ assert py_c == py_b
+ decref(space, py_c)
+
+ def test_empty_tuple_as_pyobj(self, space, api):
+ state = space.fromcache(State)
+ w_a = space.newtuple([])
+ w_b = space.newtuple([])
+ assert w_a is not w_b
+ py_a = as_pyobj(space, w_a)
+ py_b = as_pyobj(space, w_b)
+ assert py_a != py_b
+
def test_tuple_resize(self, space, api):
state = space.fromcache(State)
w_42 = space.wrap(42)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit