From 7c59d7c9860cdbaf4a9c26c9142aebd3259d046e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Apr 2020 16:32:48 +0200 Subject: [PATCH] bpo-40421: Add pyframe.h header file (GH-19755) Add a new separated pyframe.h header file of the PyFrame public C API: it is included by Python.h. Add PyFrame_GetLineNumber() to the limited C API. Replace "struct _frame" with "PyFrameObject" in header files. PyFrameObject is now defined as struct _frame by pyframe.h which is included early enough in Python.h. --- Doc/c-api/reflection.rst | 2 ++ Doc/whatsnew/3.9.rst | 3 +++ Include/Python.h | 1 + Include/ceval.h | 8 +++----- Include/cpython/ceval.h | 2 +- Include/cpython/frameobject.h | 7 ++----- Include/cpython/pystate.h | 6 +++--- Include/cpython/traceback.h | 2 +- Include/frameobject.h | 4 +--- Include/genobject.h | 12 +++++------- Include/internal/pycore_ceval.h | 3 +-- Include/internal/pycore_traceback.h | 2 +- Include/pyframe.h | 20 ++++++++++++++++++++ Include/pystate.h | 3 +-- Include/traceback.h | 4 +--- Makefile.pre.in | 1 + Objects/frameobject.c | 7 +++++-- PCbuild/pythoncore.vcxproj | 15 ++++++++------- PCbuild/pythoncore.vcxproj.filters | 3 +++ 19 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 Include/pyframe.h diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 4d3d25e6621..498219fd9aa 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -35,6 +35,8 @@ Reflection Return the line number that *frame* is currently executing. + *frame* must not be ``NULL``. + .. c:function:: const char* PyEval_GetFuncName(PyObject *func) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 13cd09b0b8b..8b8aa9a514c 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -537,6 +537,9 @@ Optimizations Build and C API Changes ======================= +* Add :c:func:`PyFrame_GetLineNumber` to the limited C API. + (Contributed by Victor Stinner in :issue:`40421`.) + * New :c:func:`PyThreadState_GetInterpreter` and :c:func:`PyInterpreterState_Get` functions to get the interpreter. New :c:func:`PyThreadState_GetFrame` function to get the current frame of a diff --git a/Include/Python.h b/Include/Python.h index 7fdb9df7065..868e31bfa4e 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -114,6 +114,7 @@ #include "classobject.h" #include "fileobject.h" #include "pycapsule.h" +#include "pyframe.h" #include "traceback.h" #include "sliceobject.h" #include "cellobject.h" diff --git a/Include/ceval.h b/Include/ceval.h index a70c421c18d..df5253900ee 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -28,12 +28,10 @@ Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallFunction( Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallMethod( PyObject *obj, const char *name, const char *format, ...); -struct _frame; /* Avoid including frameobject.h */ - PyAPI_FUNC(PyObject *) PyEval_GetBuiltins(void); PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); -PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); +PyAPI_FUNC(PyFrameObject *) PyEval_GetFrame(void); PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); PyAPI_FUNC(int) Py_MakePendingCalls(void); @@ -80,8 +78,8 @@ PyAPI_FUNC(void) Py_LeaveRecursiveCall(void); PyAPI_FUNC(const char *) PyEval_GetFuncName(PyObject *); PyAPI_FUNC(const char *) PyEval_GetFuncDesc(PyObject *); -PyAPI_FUNC(PyObject *) PyEval_EvalFrame(struct _frame *); -PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(struct _frame *f, int exc); +PyAPI_FUNC(PyObject *) PyEval_EvalFrame(PyFrameObject *); +PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(PyFrameObject *f, int exc); /* Interface for threads. diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index 020f78712ff..e1922a677bd 100644 --- a/Include/cpython/ceval.h +++ b/Include/cpython/ceval.h @@ -23,7 +23,7 @@ PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *); flag was set, else return 0. */ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); -PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _frame *f, int exc); +PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int exc); PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds); PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 4ced96746aa..e819cefd13c 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -14,7 +14,7 @@ typedef struct { int b_level; /* value stack level to pop to */ } PyTryBlock; -typedef struct _frame { +struct _frame { PyObject_VAR_HEAD struct _frame *f_back; /* previous frame, or NULL */ PyCodeObject *f_code; /* code segment */ @@ -44,7 +44,7 @@ typedef struct _frame { char f_executing; /* whether the frame is still executing */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ -} PyFrameObject; +}; /* Standard object interface */ @@ -79,9 +79,6 @@ PyAPI_FUNC(int) PyFrame_ClearFreeList(void); PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); -/* Return the line of code the frame is currently executing. */ -PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); - #ifdef __cplusplus } #endif diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 9b28f66fdba..f292da1d3c6 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -16,7 +16,7 @@ PyAPI_FUNC(PyObject *) _PyInterpreterState_GetMainModule(PyInterpreterState *); /* State unique per thread */ /* Py_tracefunc return -1 when raising an exception, or 0 for success. */ -typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); +typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *); /* The following values are used for 'what' for tracefunc functions * @@ -56,7 +56,7 @@ struct _ts { PyInterpreterState *interp; /* Borrowed reference to the current frame (it can be NULL) */ - struct _frame *frame; + PyFrameObject *frame; int recursion_depth; char overflowed; /* The stack has overflowed. Allow 50 more calls to handle the runtime error. */ @@ -184,7 +184,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); /* Frame evaluation API */ -typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _frame *, int); +typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *, int); PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc( PyInterpreterState *interp); diff --git a/Include/cpython/traceback.h b/Include/cpython/traceback.h index 746097daaf9..837470c3ba2 100644 --- a/Include/cpython/traceback.h +++ b/Include/cpython/traceback.h @@ -9,7 +9,7 @@ extern "C" { typedef struct _traceback { PyObject_HEAD struct _traceback *tb_next; - struct _frame *tb_frame; + PyFrameObject *tb_frame; int tb_lasti; int tb_lineno; } PyTracebackObject; diff --git a/Include/frameobject.h b/Include/frameobject.h index 1460e2210e3..c118af1201a 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -6,9 +6,7 @@ extern "C" { #endif -/* There are currently no frame related APIs in the stable ABI - * (they're all in the full CPython-specific API) - */ +#include "pyframe.h" #ifndef Py_LIMITED_API # define Py_CPYTHON_FRAMEOBJECT_H diff --git a/Include/genobject.h b/Include/genobject.h index b87a6485631..a7393a9a835 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -10,14 +10,12 @@ extern "C" { #include "pystate.h" /* _PyErr_StackItem */ -struct _frame; /* Avoid including frameobject.h */ - /* _PyGenObject_HEAD defines the initial segment of generator and coroutine objects. */ #define _PyGenObject_HEAD(prefix) \ PyObject_HEAD \ /* Note: gi_frame can be NULL if the generator is "finished" */ \ - struct _frame *prefix##_frame; \ + PyFrameObject *prefix##_frame; \ /* True if generator is being executed. */ \ char prefix##_running; \ /* The code object backing the generator */ \ @@ -40,8 +38,8 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_Check(op) PyObject_TypeCheck(op, &PyGen_Type) #define PyGen_CheckExact(op) Py_IS_TYPE(op, &PyGen_Type) -PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); -PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *, +PyAPI_FUNC(PyObject *) PyGen_New(PyFrameObject *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, PyObject *name, PyObject *qualname); PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); @@ -60,7 +58,7 @@ PyAPI_DATA(PyTypeObject) _PyCoroWrapper_Type; #define PyCoro_CheckExact(op) Py_IS_TYPE(op, &PyCoro_Type) PyObject *_PyCoro_GetAwaitableIter(PyObject *o); -PyAPI_FUNC(PyObject *) PyCoro_New(struct _frame *, +PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *, PyObject *name, PyObject *qualname); /* Asynchronous Generators */ @@ -86,7 +84,7 @@ PyAPI_DATA(PyTypeObject) _PyAsyncGenASend_Type; PyAPI_DATA(PyTypeObject) _PyAsyncGenWrappedValue_Type; PyAPI_DATA(PyTypeObject) _PyAsyncGenAThrow_Type; -PyAPI_FUNC(PyObject *) PyAsyncGen_New(struct _frame *, +PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *, PyObject *name, PyObject *qualname); #define PyAsyncGen_CheckExact(op) Py_IS_TYPE(op, &PyAsyncGen_Type) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 050dd59ed15..2df796deade 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -11,7 +11,6 @@ extern "C" { /* Forward declarations */ struct pyruntimestate; struct _ceval_runtime_state; -struct _frame; #include "pycore_interp.h" /* PyInterpreterState.eval_frame */ @@ -36,7 +35,7 @@ PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth( void _PyEval_Fini(void); static inline PyObject* -_PyEval_EvalFrame(PyThreadState *tstate, struct _frame *f, int throwflag) +_PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) { return tstate->interp->eval_frame(tstate, f, throwflag); } diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h index 99443d7ba27..1f092411a72 100644 --- a/Include/internal/pycore_traceback.h +++ b/Include/internal/pycore_traceback.h @@ -89,7 +89,7 @@ PyAPI_FUNC(void) _Py_DumpHexadecimal( PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame( PyObject *tb_next, - struct _frame *frame); + PyFrameObject *frame); #ifdef __cplusplus } diff --git a/Include/pyframe.h b/Include/pyframe.h new file mode 100644 index 00000000000..d3404cde4a1 --- /dev/null +++ b/Include/pyframe.h @@ -0,0 +1,20 @@ +/* Limited C API of PyFrame API + * + * Include "frameobject.h" to get the PyFrameObject structure. + */ + +#ifndef Py_PYFRAME_H +#define Py_PYFRAME_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _frame PyFrameObject; + +/* Return the line of code the frame is currently executing. */ +PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYFRAME_H */ diff --git a/Include/pystate.h b/Include/pystate.h index 65b0a24e87f..34cad02c3a9 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -13,7 +13,6 @@ removed (with effort). */ /* Forward declarations for PyFrameObject, PyThreadState and PyInterpreterState */ -struct _frame; struct _ts; struct _is; @@ -88,7 +87,7 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(unsigned long, PyObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 /* New in 3.9 */ PyAPI_FUNC(PyInterpreterState*) PyThreadState_GetInterpreter(PyThreadState *tstate); -PyAPI_FUNC(struct _frame*) PyThreadState_GetFrame(PyThreadState *tstate); +PyAPI_FUNC(PyFrameObject*) PyThreadState_GetFrame(PyThreadState *tstate); PyAPI_FUNC(uint64_t) PyThreadState_GetID(PyThreadState *tstate); #endif diff --git a/Include/traceback.h b/Include/traceback.h index 0efbae8a76a..781e5a6eec4 100644 --- a/Include/traceback.h +++ b/Include/traceback.h @@ -4,11 +4,9 @@ extern "C" { #endif -struct _frame; - /* Traceback interface */ -PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *); +PyAPI_FUNC(int) PyTraceBack_Here(PyFrameObject *); PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); /* Reveal traceback type so we can typecheck traceback objects */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 200fd319ebb..5db381f90f9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1047,6 +1047,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/pydtrace.h \ $(srcdir)/Include/pyerrors.h \ $(srcdir)/Include/pyfpe.h \ + $(srcdir)/Include/pyframe.h \ $(srcdir)/Include/pyhash.h \ $(srcdir)/Include/pylifecycle.h \ $(srcdir)/Include/pymacconfig.h \ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index bdd7862b3c9..d0a15e77512 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -34,10 +34,13 @@ frame_getlocals(PyFrameObject *f, void *closure) int PyFrame_GetLineNumber(PyFrameObject *f) { - if (f->f_trace) + assert(f != NULL); + if (f->f_trace) { return f->f_lineno; - else + } + else { return PyCode_Addr2Line(f->f_code, f->f_lasti); + } } static PyObject * diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d20e749b051..f54f1554130 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -110,6 +110,8 @@ + + @@ -165,8 +167,8 @@ - + @@ -214,7 +216,6 @@ - @@ -223,20 +224,20 @@ + + - - + - - - + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 8c02622fd55..c8758dab3b3 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -249,6 +249,9 @@ Include + + Include + Include