diff --git a/Include/object.h b/Include/object.h index c0371c82906..8b2afc2bc5b 100644 --- a/Include/object.h +++ b/Include/object.h @@ -729,8 +729,8 @@ you can count such references to the type object.) */ #ifdef Py_REF_DEBUG PyAPI_DATA(Py_ssize_t) _Py_RefTotal; -PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname, - int lineno, PyObject *op); +PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno, + PyObject *op); PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void); #define _Py_INC_REFTOTAL _Py_RefTotal++ #define _Py_DEC_REFTOTAL _Py_RefTotal-- diff --git a/Include/pydtrace.h b/Include/pydtrace.h index 037961d429c..cfe192fc5d2 100644 --- a/Include/pydtrace.h +++ b/Include/pydtrace.h @@ -25,29 +25,29 @@ extern "C" { /* Without DTrace, compile to nothing. */ -static inline void PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {} -static inline void PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {} -static inline void PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {} -static inline void PyDTrace_GC_START(int arg0) {} -static inline void PyDTrace_GC_DONE(int arg0) {} -static inline void PyDTrace_INSTANCE_NEW_START(int arg0) {} -static inline void PyDTrace_INSTANCE_NEW_DONE(int arg0) {} -static inline void PyDTrace_INSTANCE_DELETE_START(int arg0) {} -static inline void PyDTrace_INSTANCE_DELETE_DONE(int arg0) {} -static inline void PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {} -static inline void PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {} +Py_STATIC_INLINE(void) PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {} +Py_STATIC_INLINE(void) PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {} +Py_STATIC_INLINE(void) PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {} +Py_STATIC_INLINE(void) PyDTrace_GC_START(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_GC_DONE(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_INSTANCE_NEW_START(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_INSTANCE_NEW_DONE(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_INSTANCE_DELETE_START(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_INSTANCE_DELETE_DONE(int arg0) {} +Py_STATIC_INLINE(void) PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {} +Py_STATIC_INLINE(void) PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {} -static inline int PyDTrace_LINE_ENABLED(void) { return 0; } -static inline int PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; } -static inline int PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; } -static inline int PyDTrace_GC_START_ENABLED(void) { return 0; } -static inline int PyDTrace_GC_DONE_ENABLED(void) { return 0; } -static inline int PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; } -static inline int PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; } -static inline int PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; } -static inline int PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; } -static inline int PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; } -static inline int PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_LINE_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_GC_START_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_GC_DONE_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; } +Py_STATIC_INLINE(int) PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; } #endif /* !WITH_DTRACE */ diff --git a/Include/pyport.h b/Include/pyport.h index f4b547a50b8..2f87f53700a 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -164,20 +164,37 @@ typedef int Py_ssize_clean_t; */ #if defined(_MSC_VER) -#if defined(PY_LOCAL_AGGRESSIVE) -/* enable more aggressive optimization for visual studio */ -#pragma optimize("agtw", on) +# if defined(PY_LOCAL_AGGRESSIVE) + /* enable more aggressive optimization for visual studio */ +# pragma optimize("agtw", on) #endif -/* ignore warnings if the compiler decides not to inline a function */ -#pragma warning(disable: 4710) -/* fastest possible local call under MSVC */ -#define Py_LOCAL(type) static type __fastcall -#define Py_LOCAL_INLINE(type) static __inline type __fastcall + /* ignore warnings if the compiler decides not to inline a function */ +# pragma warning(disable: 4710) + /* fastest possible local call under MSVC */ +# define Py_LOCAL(type) static type __fastcall +# define Py_LOCAL_INLINE(type) static __inline type __fastcall #else -#define Py_LOCAL(type) static type -#define Py_LOCAL_INLINE(type) static inline type +# define Py_LOCAL(type) static type +# define Py_LOCAL_INLINE(type) static inline type #endif +/* Declare a "static inline" function. Typical usage: + + Py_STATIC_INLINE(int) add(int a, int b) { return a + b; } + + If the compiler supports it, try to always inline the function even if no + optimization level was specified. */ +#if defined(__GNUC__) || defined(__clang__) +# define Py_STATIC_INLINE(TYPE) \ + __attribute__((always_inline)) static inline TYPE +#elif defined(_MSC_VER) +# define Py_STATIC_INLINE(TYPE) \ + static __forceinline TYPE +#else +# define Py_STATIC_INLINE(TYPE) static inline TYPE +#endif + + /* Py_MEMCPY is kept for backwards compatibility, * see https://bugs.python.org/issue28126 */ #define Py_MEMCPY memcpy diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 49297f461da..a732f4f82f3 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -315,6 +315,23 @@ class CAPITest(unittest.TestCase): self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping) self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping) + @unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'), + 'need _testcapi.negative_refcount') + def test_negative_refcount(self): + # bpo-35059: Check that Py_DECREF() reports the correct filename + # when calling _Py_NegativeRefcount() to abort Python. + code = textwrap.dedent(""" + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.negative_refcount() + """) + rc, out, err = assert_python_failure('-c', code) + self.assertRegex(err, + br'_testcapimodule\.c:[0-9]+ object at .* ' + br'has negative ref count', err) + class TestPendingCalls(unittest.TestCase): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4381e93ca91..b2cda514230 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4818,6 +4818,25 @@ fail: } +#ifdef Py_REF_DEBUG +static PyObject * +negative_refcount(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *obj = PyUnicode_FromString("negative_refcount"); + if (obj == NULL) { + return NULL; + } + assert(Py_REFCNT(obj) == 1); + + Py_REFCNT(obj) = 0; + /* Py_DECREF() must call _Py_NegativeRefcount() and abort Python */ + Py_DECREF(obj); + + Py_RETURN_NONE; +} +#endif + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, @@ -5043,6 +5062,9 @@ static PyMethodDef TestMethods[] = { {"EncodeLocaleEx", encode_locale_ex, METH_VARARGS}, {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS}, {"get_coreconfig", get_coreconfig, METH_NOARGS}, +#ifdef Py_REF_DEBUG + {"negative_refcount", negative_refcount, METH_NOARGS}, +#endif {NULL, NULL} /* sentinel */ }; diff --git a/Objects/object.c b/Objects/object.c index 4597b1266ae..82560710452 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -200,14 +200,14 @@ void dec_count(PyTypeObject *tp) #ifdef Py_REF_DEBUG /* Log a fatal error; doesn't return. */ void -_Py_NegativeRefcount(const char *fname, int lineno, PyObject *op) +_Py_NegativeRefcount(const char *filename, int lineno, PyObject *op) { char buf[300]; PyOS_snprintf(buf, sizeof(buf), "%s:%i object at %p has negative ref count " "%" PY_FORMAT_SIZE_T "d", - fname, lineno, op, op->ob_refcnt); + filename, lineno, op, op->ob_refcnt); Py_FatalError(buf); }