diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc index 9689adf36f20..72b73023c07d 100644 --- a/python/google/protobuf/pyext/descriptor.cc +++ b/python/google/protobuf/pyext/descriptor.cc @@ -58,6 +58,43 @@ : 0) \ : PyBytes_AsStringAndSize(ob, (charpp), (sizep))) +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + Py_INCREF(frame->f_code); + return frame->f_code; +} + +static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +{ + Py_XINCREF(frame->f_back); + return frame->f_back; +} +#endif + +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } +#else + PyFrame_FastToLocals(frame); +#endif + Py_INCREF(frame->f_locals); + return frame->f_locals; +} +#endif + +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +{ + Py_INCREF(frame->f_globals); + return frame->f_globals; +} +#endif + namespace google { namespace protobuf { namespace python { @@ -96,48 +133,66 @@ bool _CalledFromGeneratedFile(int stacklevel) { // This check is not critical and is somewhat difficult to implement correctly // in PyPy. PyFrameObject* frame = PyEval_GetFrame(); + PyCodeObject* frame_code = nullptr; + PyObject* frame_globals = nullptr; + PyObject* frame_locals = nullptr; + bool result = false; + if (frame == nullptr) { - return false; + goto exit; } + Py_INCREF(frame); while (stacklevel-- > 0) { - frame = frame->f_back; + PyFrameObject* next_frame = PyFrame_GetBack(frame); + Py_DECREF(frame); + frame = next_frame; if (frame == nullptr) { - return false; + goto exit; } } - if (frame->f_code->co_filename == nullptr) { - return false; + frame_code = PyFrame_GetCode(frame); + if (frame_code->co_filename == nullptr) { + goto exit; } char* filename; Py_ssize_t filename_size; - if (PyString_AsStringAndSize(frame->f_code->co_filename, + if (PyString_AsStringAndSize(frame_code->co_filename, &filename, &filename_size) < 0) { // filename is not a string. PyErr_Clear(); - return false; + goto exit; } if ((filename_size < 3) || (strcmp(&filename[filename_size - 3], ".py") != 0)) { // Cython's stack does not have .py file name and is not at global module // scope. - return true; + result = true; + goto exit; } if (filename_size < 7) { // filename is too short. - return false; + goto exit; } if (strcmp(&filename[filename_size - 7], "_pb2.py") != 0) { // Filename is not ending with _pb2. - return false; + goto exit; } - if (frame->f_globals != frame->f_locals) { + frame_globals = PyFrame_GetGlobals(frame); + frame_locals = PyFrame_GetLocals(frame); + if (frame_globals != frame_locals) { // Not at global module scope - return false; + goto exit; } #endif - return true; + result = true; +exit: + Py_XDECREF(frame_globals); + Py_XDECREF(frame_locals); + Py_XDECREF(frame_code); + Py_XDECREF(frame); + return result; } // If the calling code is not a _pb2.py file, raise AttributeError.