mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
The Py_CLEAR(), Py_SETREF() and Py_XSETREF() macros now only evaluate their arguments once. If an argument has side effects, these side effects are no longer duplicated. Use temporary variables to avoid duplicating side effects of macro arguments. If available, use _Py_TYPEOF() to avoid type punning. Otherwise, use memcpy() for the assignment to prevent a miscompilation with strict aliasing caused by type punning. Add _Py_TYPEOF() macro: __typeof__() on GCC and clang. Add test_py_clear() and test_py_setref() unit tests to _testcapi.
This commit is contained in:
parent
7031275776
commit
b11a384dc7
7 changed files with 236 additions and 30 deletions
|
|
@ -598,15 +598,44 @@ static inline void Py_DECREF(PyObject *op)
|
|||
* one of those can't cause problems -- but in part that relies on that
|
||||
* Python integers aren't currently weakly referencable. Best practice is
|
||||
* to use Py_CLEAR() even if you can't think of a reason for why you need to.
|
||||
*
|
||||
* gh-98724: Use a temporary variable to only evaluate the macro argument once,
|
||||
* to avoid the duplication of side effects if the argument has side effects.
|
||||
*
|
||||
* gh-99701: If the PyObject* type is used with casting arguments to PyObject*,
|
||||
* the code can be miscompiled with strict aliasing because of type punning.
|
||||
* With strict aliasing, a compiler considers that two pointers of different
|
||||
* types cannot read or write the same memory which enables optimization
|
||||
* opportunities.
|
||||
*
|
||||
* If available, use _Py_TYPEOF() to use the 'op' type for temporary variables,
|
||||
* and so avoid type punning. Otherwise, use memcpy() which causes type erasure
|
||||
* and so prevents the compiler to reuse an old cached 'op' value after
|
||||
* Py_CLEAR().
|
||||
*/
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
PyObject *_py_tmp = _PyObject_CAST(op); \
|
||||
if (_py_tmp != NULL) { \
|
||||
(op) = NULL; \
|
||||
Py_DECREF(_py_tmp); \
|
||||
} \
|
||||
#ifdef _Py_TYPEOF
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
_Py_TYPEOF(op)* _tmp_op_ptr = &(op); \
|
||||
_Py_TYPEOF(op) _tmp_old_op = (*_tmp_op_ptr); \
|
||||
if (_tmp_old_op != NULL) { \
|
||||
*_tmp_op_ptr = _Py_NULL; \
|
||||
Py_DECREF(_tmp_old_op); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
PyObject **_tmp_op_ptr = _Py_CAST(PyObject**, &(op)); \
|
||||
PyObject *_tmp_old_op = (*_tmp_op_ptr); \
|
||||
if (_tmp_old_op != NULL) { \
|
||||
PyObject *_null_ptr = _Py_NULL; \
|
||||
memcpy(_tmp_op_ptr, &_null_ptr, sizeof(PyObject*)); \
|
||||
Py_DECREF(_tmp_old_op); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
/* Function to use in case the object pointer can be NULL: */
|
||||
static inline void Py_XINCREF(PyObject *op)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue