summaryrefslogtreecommitdiffstats
path: root/Modules/_testcapimodule.c
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2022-11-09 13:06:36 (GMT)
committerGitHub <noreply@github.com>2022-11-09 13:06:36 (GMT)
commitc03e05c2e72f3ea5e797389e7d1042eef85ad37a (patch)
tree3a30ddb1b3b35a9b2ea7d233f242c72e74e71e71 /Modules/_testcapimodule.c
parent0124b5dd28eff7bb80eb7244e97e402a036db13b (diff)
downloadcpython-c03e05c2e72f3ea5e797389e7d1042eef85ad37a.zip
cpython-c03e05c2e72f3ea5e797389e7d1042eef85ad37a.tar.gz
cpython-c03e05c2e72f3ea5e797389e7d1042eef85ad37a.tar.bz2
gh-98724: Fix Py_CLEAR() macro side effects (#99100)
The Py_CLEAR(), Py_SETREF() and Py_XSETREF() macros now only evaluate their argument once. If an argument has side effects, these side effects are no longer duplicated. Add test_py_clear() and test_py_setref() unit tests to _testcapi.
Diffstat (limited to 'Modules/_testcapimodule.c')
-rw-r--r--Modules/_testcapimodule.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 0615c73..7b018b1 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -5138,6 +5138,91 @@ test_set_type_size(PyObject *self, PyObject *Py_UNUSED(ignored))
}
+// Test Py_CLEAR() macro
+static PyObject*
+test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ // simple case with a variable
+ PyObject *obj = PyList_New(0);
+ if (obj == NULL) {
+ return NULL;
+ }
+ Py_CLEAR(obj);
+ assert(obj == NULL);
+
+ // gh-98724: complex case, Py_CLEAR() argument has a side effect
+ PyObject* array[1];
+ array[0] = PyList_New(0);
+ if (array[0] == NULL) {
+ return NULL;
+ }
+
+ PyObject **p = array;
+ Py_CLEAR(*p++);
+ assert(array[0] == NULL);
+ assert(p == array + 1);
+
+ Py_RETURN_NONE;
+}
+
+
+// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear()
+static PyObject*
+test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ // Py_SETREF() simple case with a variable
+ PyObject *obj = PyList_New(0);
+ if (obj == NULL) {
+ return NULL;
+ }
+ Py_SETREF(obj, NULL);
+ assert(obj == NULL);
+
+ // Py_XSETREF() simple case with a variable
+ PyObject *obj2 = PyList_New(0);
+ if (obj2 == NULL) {
+ return NULL;
+ }
+ Py_XSETREF(obj2, NULL);
+ assert(obj2 == NULL);
+ // test Py_XSETREF() when the argument is NULL
+ Py_XSETREF(obj2, NULL);
+ assert(obj2 == NULL);
+
+ // gh-98724: complex case, Py_SETREF() argument has a side effect
+ PyObject* array[1];
+ array[0] = PyList_New(0);
+ if (array[0] == NULL) {
+ return NULL;
+ }
+
+ PyObject **p = array;
+ Py_SETREF(*p++, NULL);
+ assert(array[0] == NULL);
+ assert(p == array + 1);
+
+ // gh-98724: complex case, Py_XSETREF() argument has a side effect
+ PyObject* array2[1];
+ array2[0] = PyList_New(0);
+ if (array2[0] == NULL) {
+ return NULL;
+ }
+
+ PyObject **p2 = array2;
+ Py_XSETREF(*p2++, NULL);
+ assert(array2[0] == NULL);
+ assert(p2 == array2 + 1);
+
+ // test Py_XSETREF() when the argument is NULL
+ p2 = array2;
+ Py_XSETREF(*p2++, NULL);
+ assert(array2[0] == NULL);
+ assert(p2 == array2 + 1);
+
+ Py_RETURN_NONE;
+}
+
+
#define TEST_REFCOUNT() \
do { \
PyObject *obj = PyList_New(0); \
@@ -6311,6 +6396,8 @@ static PyMethodDef TestMethods[] = {
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
{"without_gc", without_gc, METH_O},
{"test_set_type_size", test_set_type_size, METH_NOARGS},
+ {"test_py_clear", test_py_clear, METH_NOARGS},
+ {"test_py_setref", test_py_setref, METH_NOARGS},
{"test_refcount_macros", test_refcount_macros, METH_NOARGS},
{"test_refcount_funcs", test_refcount_funcs, METH_NOARGS},
{"test_py_is_macros", test_py_is_macros, METH_NOARGS},