diff options
author | Victor Stinner <vstinner@python.org> | 2022-11-09 13:06:36 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-09 13:06:36 (GMT) |
commit | c03e05c2e72f3ea5e797389e7d1042eef85ad37a (patch) | |
tree | 3a30ddb1b3b35a9b2ea7d233f242c72e74e71e71 /Include/cpython | |
parent | 0124b5dd28eff7bb80eb7244e97e402a036db13b (diff) | |
download | cpython-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 'Include/cpython')
-rw-r--r-- | Include/cpython/object.h | 44 |
1 files changed, 24 insertions, 20 deletions
diff --git a/Include/cpython/object.h b/Include/cpython/object.h index fa0cfb2..d07cb3b 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -305,37 +305,41 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); -/* Safely decref `op` and set `op` to `op2`. +/* Safely decref `dst` and set `dst` to `src`. * * As in case of Py_CLEAR "the obvious" code can be deadly: * - * Py_DECREF(op); - * op = op2; + * Py_DECREF(dst); + * dst = src; * * The safe way is: * - * Py_SETREF(op, op2); + * Py_SETREF(dst, src); * - * That arranges to set `op` to `op2` _before_ decref'ing, so that any code - * triggered as a side-effect of `op` getting torn down no longer believes - * `op` points to a valid object. + * That arranges to set `dst` to `src` _before_ decref'ing, so that any code + * triggered as a side-effect of `dst` getting torn down no longer believes + * `dst` points to a valid object. * - * Py_XSETREF is a variant of Py_SETREF that uses Py_XDECREF instead of - * Py_DECREF. + * gh-98724: Use the _tmp_dst_ptr variable to evaluate the 'dst' macro argument + * exactly once, to prevent the duplication of side effects in this macro. */ - -#define Py_SETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_DECREF(_py_tmp); \ +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ } while (0) -#define Py_XSETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_XDECREF(_py_tmp); \ +/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of + * Py_DECREF(). + */ +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ } while (0) |