diff options
author | Mark Shannon <mark@hotpy.org> | 2021-07-26 10:22:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-26 10:22:16 (GMT) |
commit | ae0a2b756255629140efcbe57fc2e714f0267aa3 (patch) | |
tree | 8710e8c7a398c9ec0add227fab607f367242a7e5 | |
parent | 0363a4014d90df17a29042de008ef0b659f92505 (diff) | |
download | cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.zip cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.gz cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.bz2 |
bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it.
* Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector.
* Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation.
* Move ownership of InterpreterFrame in generator from frame object to generator object.
* Do not create frame objects for Python calls.
* Do not create frame objects for generators.
-rw-r--r-- | Include/cpython/ceval.h | 2 | ||||
-rw-r--r-- | Include/cpython/frameobject.h | 37 | ||||
-rw-r--r-- | Include/cpython/pystate.h | 4 | ||||
-rw-r--r-- | Include/genobject.h | 4 | ||||
-rw-r--r-- | Include/internal/pycore_ceval.h | 7 | ||||
-rw-r--r-- | Include/internal/pycore_frame.h | 126 | ||||
-rw-r--r-- | Include/internal/pycore_pystate.h | 3 | ||||
-rw-r--r-- | Lib/test/test_faulthandler.py | 1 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 | ||||
-rw-r--r-- | Makefile.pre.in | 1 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2021-07-09-12-08-17.bpo-44590.a2ntVX.rst | 5 | ||||
-rw-r--r-- | Modules/_tracemalloc.c | 15 | ||||
-rw-r--r-- | Modules/_xxsubinterpretersmodule.c | 4 | ||||
-rw-r--r-- | Modules/signalmodule.c | 19 | ||||
-rw-r--r-- | Objects/frameobject.c | 296 | ||||
-rw-r--r-- | Objects/genobject.c | 265 | ||||
-rw-r--r-- | Objects/typeobject.c | 8 | ||||
-rw-r--r-- | PCbuild/pythoncore.vcxproj | 1 | ||||
-rw-r--r-- | Python/_warnings.c | 6 | ||||
-rw-r--r-- | Python/ceval.c | 431 | ||||
-rw-r--r-- | Python/errors.c | 3 | ||||
-rw-r--r-- | Python/frame.c | 135 | ||||
-rw-r--r-- | Python/pystate.c | 81 | ||||
-rw-r--r-- | Python/suggestions.c | 4 | ||||
-rw-r--r-- | Python/sysmodule.c | 16 | ||||
-rw-r--r-- | Python/traceback.c | 29 | ||||
-rwxr-xr-x | Tools/gdb/libpython.py | 149 |
27 files changed, 1036 insertions, 618 deletions
diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index 0633892..44b78f6 100644 --- a/Include/cpython/ceval.h +++ b/Include/cpython/ceval.h @@ -19,7 +19,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, PyFrameObject *f, int exc); +PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _interpreter_frame *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 2bf458c..e4cfac5 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -4,50 +4,17 @@ # error "this header file must not be included directly" #endif -/* These values are chosen so that the inline functions below all - * compare f_state to zero. - */ -enum _framestate { - FRAME_CREATED = -2, - FRAME_SUSPENDED = -1, - FRAME_EXECUTING = 0, - FRAME_RETURNED = 1, - FRAME_UNWINDING = 2, - FRAME_RAISED = 3, - FRAME_CLEARED = 4 -}; - -typedef signed char PyFrameState; - struct _frame { PyObject_HEAD struct _frame *f_back; /* previous frame, or NULL */ - PyObject **f_valuestack; /* points after the last local */ + struct _interpreter_frame *f_frame; /* points to the frame data */ PyObject *f_trace; /* Trace function */ - /* Borrowed reference to a generator, or NULL */ - PyObject *f_gen; - int f_stackdepth; /* Depth of value stack */ - int f_lasti; /* Last instruction if called */ int f_lineno; /* Current line number. Only valid if non-zero */ - PyFrameState f_state; /* What state the frame is in */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ char f_own_locals_memory; /* This frame owns the memory for the locals */ - PyObject **f_localsptr; /* Pointer to locals, cells, free */ }; -static inline int _PyFrame_IsRunnable(struct _frame *f) { - return f->f_state < FRAME_EXECUTING; -} - -static inline int _PyFrame_IsExecuting(struct _frame *f) { - return f->f_state == FRAME_EXECUTING; -} - -static inline int _PyFrameHasCompleted(struct _frame *f) { - return f->f_state > FRAME_EXECUTING; -} - /* Standard object interface */ PyAPI_DATA(PyTypeObject) PyFrame_Type; @@ -59,7 +26,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* -_PyFrame_New_NoTrack(PyThreadState *, PyFrameConstructor *, PyObject *, PyObject **); +_PyFrame_New_NoTrack(struct _interpreter_frame *, int); /* The rest of the interface is specific for frame objects */ diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 56ea8d0..ab4bf8b 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -78,7 +78,7 @@ struct _ts { PyInterpreterState *interp; /* Borrowed reference to the current frame (it can be NULL) */ - PyFrameObject *frame; + struct _interpreter_frame *frame; int recursion_depth; int recursion_headroom; /* Allow 50 more calls to handle any errors. */ int stackcheck_counter; @@ -223,7 +223,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); /* Frame evaluation API */ -typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *, int); +typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _interpreter_frame *, int); PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc( PyInterpreterState *interp); diff --git a/Include/genobject.h b/Include/genobject.h index 094d4e1..55a8b34 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -16,9 +16,9 @@ extern "C" { #define _PyGenObject_HEAD(prefix) \ PyObject_HEAD \ /* Note: gi_frame can be NULL if the generator is "finished" */ \ - PyFrameObject *prefix##_frame; \ + struct _interpreter_frame *prefix##_xframe; \ /* The code object backing the generator */ \ - PyCodeObject *prefix##_code; \ + PyCodeObject *prefix##_code; \ /* List of weak reference. */ \ PyObject *prefix##_weakreflist; \ /* Name of the generator. */ \ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index f573c3e..66ddc99 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -41,9 +41,9 @@ extern PyObject *_PyEval_BuiltinsFromGlobals( static inline PyObject* -_PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) +_PyEval_EvalFrame(PyThreadState *tstate, struct _interpreter_frame *frame, int throwflag) { - return tstate->interp->eval_frame(tstate, f, throwflag); + return tstate->interp->eval_frame(tstate, frame, throwflag); } extern PyObject * @@ -107,6 +107,9 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { #define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline() +struct _interpreter_frame *_PyEval_GetFrame(void); + +PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _interpreter_frame *); #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index e30e3c8..cd465d7 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -4,41 +4,123 @@ extern "C" { #endif -enum { - FRAME_SPECIALS_GLOBALS_OFFSET = 0, - FRAME_SPECIALS_BUILTINS_OFFSET = 1, - FRAME_SPECIALS_LOCALS_OFFSET = 2, - FRAME_SPECIALS_CODE_OFFSET = 3, - FRAME_SPECIALS_SIZE = 4 +/* These values are chosen so that the inline functions below all + * compare f_state to zero. + */ +enum _framestate { + FRAME_CREATED = -2, + FRAME_SUSPENDED = -1, + FRAME_EXECUTING = 0, + FRAME_RETURNED = 1, + FRAME_UNWINDING = 2, + FRAME_RAISED = 3, + FRAME_CLEARED = 4 }; -static inline PyObject ** -_PyFrame_Specials(PyFrameObject *f) { - return &f->f_valuestack[-FRAME_SPECIALS_SIZE]; +typedef signed char PyFrameState; + +typedef struct _interpreter_frame { + PyObject *f_globals; + PyObject *f_builtins; + PyObject *f_locals; + PyCodeObject *f_code; + PyFrameObject *frame_obj; + /* Borrowed reference to a generator, or NULL */ + PyObject *generator; + struct _interpreter_frame *previous; + int f_lasti; /* Last instruction if called */ + int stackdepth; /* Depth of value stack */ + int nlocalsplus; + PyFrameState f_state; /* What state the frame is in */ + PyObject *stack[1]; +} InterpreterFrame; + +static inline int _PyFrame_IsRunnable(InterpreterFrame *f) { + return f->f_state < FRAME_EXECUTING; +} + +static inline int _PyFrame_IsExecuting(InterpreterFrame *f) { + return f->f_state == FRAME_EXECUTING; } -/* Returns a *borrowed* reference. */ -static inline PyObject * -_PyFrame_GetGlobals(PyFrameObject *f) +static inline int _PyFrameHasCompleted(InterpreterFrame *f) { + return f->f_state > FRAME_EXECUTING; +} + +#define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) + +InterpreterFrame * +_PyInterpreterFrame_HeapAlloc(PyFrameConstructor *con, PyObject *locals); + +static inline void +_PyFrame_InitializeSpecials( + InterpreterFrame *frame, PyFrameConstructor *con, + PyObject *locals, int nlocalsplus) { - return _PyFrame_Specials(f)[FRAME_SPECIALS_GLOBALS_OFFSET]; + frame->f_code = (PyCodeObject *)Py_NewRef(con->fc_code); + frame->f_builtins = Py_NewRef(con->fc_builtins); + frame->f_globals = Py_NewRef(con->fc_globals); + frame->f_locals = Py_XNewRef(locals); + frame->nlocalsplus = nlocalsplus; + frame->stackdepth = 0; + frame->frame_obj = NULL; + frame->generator = NULL; + frame->f_lasti = -1; + frame->f_state = FRAME_CREATED; } -/* Returns a *borrowed* reference. */ -static inline PyObject * -_PyFrame_GetBuiltins(PyFrameObject *f) +/* Gets the pointer to the locals array + * that precedes this frame. + */ +static inline PyObject** +_PyFrame_GetLocalsArray(InterpreterFrame *frame) { - return _PyFrame_Specials(f)[FRAME_SPECIALS_BUILTINS_OFFSET]; + return ((PyObject **)frame) - frame->nlocalsplus; } -/* Returns a *borrowed* reference. */ -static inline PyCodeObject * -_PyFrame_GetCode(PyFrameObject *f) +/* For use by _PyFrame_GetFrameObject + Do not call directly. */ +PyFrameObject * +_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame); + +/* Gets the PyFrameObject for this frame, lazily + * creating it if necessary. + * Returns a borrowed referennce */ +static inline PyFrameObject * +_PyFrame_GetFrameObject(InterpreterFrame *frame) { - return (PyCodeObject *)_PyFrame_Specials(f)[FRAME_SPECIALS_CODE_OFFSET]; + PyFrameObject *res = frame->frame_obj; + if (res != NULL) { + return res; + } + return _PyFrame_MakeAndSetFrameObject(frame); } -int _PyFrame_TakeLocals(PyFrameObject *f); +/* Clears all references in the frame. + * If take is non-zero, then the InterpreterFrame frame + * may be transfered to the frame object it references + * instead of being cleared. Either way + * the caller no longer owns the references + * in the frame. + * take should be set to 1 for heap allocated + * frames like the ones in generators and coroutines. + */ +int +_PyFrame_Clear(InterpreterFrame * frame, int take); + +int +_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg); + +int +_PyFrame_FastToLocalsWithError(InterpreterFrame *frame); + +void +_PyFrame_LocalsToFast(InterpreterFrame *frame, int clear); + +InterpreterFrame *_PyThreadState_PushFrame( + PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals); + +void _PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame *frame); #ifdef __cplusplus } diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 6601ce2..4b894f3 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -147,9 +147,6 @@ PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); -PyObject **_PyThreadState_PushLocals(PyThreadState *, int size); -void _PyThreadState_PopLocals(PyThreadState *, PyObject **); - #ifdef __cplusplus } #endif diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index ee3f41a..de986a3 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -56,6 +56,7 @@ def temporary_filename(): os_helper.unlink(filename) class FaultHandlerTests(unittest.TestCase): + def get_output(self, code, filename=None, fd=None): """ Run the specified code in Python (in a new child process) and read the diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 7d9a36a..dba4928 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1275,7 +1275,7 @@ class SizeofTest(unittest.TestCase): # frame import inspect x = inspect.currentframe() - check(x, size('4P3i4cP')) + check(x, size('3Pi3c')) # function def func(): pass check(func, size('14Pi')) diff --git a/Makefile.pre.in b/Makefile.pre.in index 5d48500..bf023a0 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -350,6 +350,7 @@ PYTHON_OBJS= \ Python/context.o \ Python/dynamic_annotations.o \ Python/errors.o \ + Python/frame.o \ Python/frozenmain.o \ Python/future.o \ Python/getargs.o \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-09-12-08-17.bpo-44590.a2ntVX.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-09-12-08-17.bpo-44590.a2ntVX.rst new file mode 100644 index 0000000..ed4d969 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-07-09-12-08-17.bpo-44590.a2ntVX.rst @@ -0,0 +1,5 @@ +All necessary data for executing a Python function (local variables, stack, +etc) is now kept in a per-thread stack. Frame objects are lazily allocated +on demand. This increases performance by about 7% on the standard benchmark +suite. Introspection and debugging are unaffected as frame objects are +always available when needed. Patch by Mark Shannon. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 04f6c24..fc3d7f5 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -3,7 +3,7 @@ #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_traceback.h" #include "pycore_hashtable.h" -#include "frameobject.h" // PyFrame_GetBack() +#include <pycore_frame.h> #include "clinic/_tracemalloc.c.h" /*[clinic input] @@ -299,18 +299,16 @@ hashtable_compare_traceback(const void *key1, const void *key2) static void -tracemalloc_get_frame(PyFrameObject *pyframe, frame_t *frame) +tracemalloc_get_frame(InterpreterFrame *pyframe, frame_t *frame) { frame->filename = unknown_filename; - int lineno = PyFrame_GetLineNumber(pyframe); + int lineno = PyCode_Addr2Line(pyframe->f_code, pyframe->f_lasti*2); if (lineno < 0) { lineno = 0; } frame->lineno = (unsigned int)lineno; - PyCodeObject *code = PyFrame_GetCode(pyframe); - PyObject *filename = code->co_filename; - Py_DECREF(code); + PyObject *filename = pyframe->f_code->co_filename; if (filename == NULL) { #ifdef TRACE_DEBUG @@ -395,7 +393,7 @@ traceback_get_frames(traceback_t *traceback) return; } - PyFrameObject *pyframe = PyThreadState_GetFrame(tstate); + InterpreterFrame *pyframe = tstate->frame; for (; pyframe != NULL;) { if (traceback->nframe < _Py_tracemalloc_config.max_nframe) { tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]); @@ -406,8 +404,7 @@ traceback_get_frames(traceback_t *traceback) traceback->total_nframe++; } - PyFrameObject *back = PyFrame_GetBack(pyframe); - Py_DECREF(pyframe); + InterpreterFrame *back = pyframe->previous; pyframe = back; } } diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 9290255..11b55f7 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -4,6 +4,7 @@ #include "Python.h" #include "frameobject.h" +#include "pycore_frame.h" #include "interpreteridobject.h" @@ -1834,13 +1835,12 @@ _is_running(PyInterpreterState *interp) } assert(!PyErr_Occurred()); - PyFrameObject *frame = PyThreadState_GetFrame(tstate); + InterpreterFrame *frame = tstate->frame; if (frame == NULL) { return 0; } int executing = _PyFrame_IsExecuting(frame); - Py_DECREF(frame); return executing; } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index a4eeec9..243c8c8 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -7,6 +7,7 @@ #include "pycore_atomic.h" // _Py_atomic_int #include "pycore_call.h" // _PyObject_Call() #include "pycore_ceval.h" // _PyEval_SignalReceived() +#include "pycore_frame.h" #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_pylifecycle.h" // NSIG @@ -1786,11 +1787,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) */ _Py_atomic_store(&is_tripped, 0); - PyObject *frame = (PyObject *)tstate->frame; - if (!frame) { - frame = Py_None; - } - + InterpreterFrame *frame = tstate->frame; signal_state_t *state = &signal_global_state; for (int i = 1; i < NSIG; i++) { if (!_Py_atomic_load_relaxed(&Handlers[i].tripped)) { @@ -1821,8 +1818,16 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_WriteUnraisable(Py_None); continue; } - - PyObject *arglist = Py_BuildValue("(iO)", i, frame); + PyObject *arglist = NULL; + if (frame == NULL) { + arglist = Py_BuildValue("(iO)", i, Py_None); + } + else { + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f != NULL) { + arglist = Py_BuildValue("(iO)", i, f); + } + } PyObject *result; if (arglist) { result = _PyObject_Call(tstate, func, arglist, NULL); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 813ec56..d1b8048 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -14,7 +14,6 @@ #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { - {"f_back", T_OBJECT, OFF(f_back), READONLY}, {"f_trace_lines", T_BOOL, OFF(f_trace_lines), 0}, {"f_trace_opcodes", T_BOOL, OFF(f_trace_opcodes), 0}, {NULL} /* Sentinel */ @@ -33,7 +32,7 @@ frame_getlocals(PyFrameObject *f, void *closure) { if (PyFrame_FastToLocalsWithError(f) < 0) return NULL; - PyObject *locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + PyObject *locals = f->f_frame->f_locals; Py_INCREF(locals); return locals; } @@ -46,7 +45,7 @@ PyFrame_GetLineNumber(PyFrameObject *f) return f->f_lineno; } else { - return PyCode_Addr2Line(_PyFrame_GetCode(f), f->f_lasti*2); + return PyCode_Addr2Line(f->f_frame->f_code, f->f_frame->f_lasti*2); } } @@ -65,16 +64,16 @@ frame_getlineno(PyFrameObject *f, void *closure) static PyObject * frame_getlasti(PyFrameObject *f, void *closure) { - if (f->f_lasti < 0) { + if (f->f_frame->f_lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(f->f_lasti*2); + return PyLong_FromLong(f->f_frame->f_lasti*2); } static PyObject * frame_getglobals(PyFrameObject *f, void *closure) { - PyObject *globals = _PyFrame_GetGlobals(f); + PyObject *globals = f->f_frame->f_globals; if (globals == NULL) { globals = Py_None; } @@ -85,7 +84,7 @@ frame_getglobals(PyFrameObject *f, void *closure) static PyObject * frame_getbuiltins(PyFrameObject *f, void *closure) { - PyObject *builtins = _PyFrame_GetBuiltins(f); + PyObject *builtins = f->f_frame->f_builtins; if (builtins == NULL) { builtins = Py_None; } @@ -102,6 +101,16 @@ frame_getcode(PyFrameObject *f, void *closure) return (PyObject *)PyFrame_GetCode(f); } +static PyObject * +frame_getback(PyFrameObject *f, void *closure) +{ + PyObject *res = (PyObject *)PyFrame_GetBack(f); + if (res == NULL) { + Py_RETURN_NONE; + } + return res; +} + /* Given the index of the effective opcode, scan back to construct the oparg with EXTENDED_ARG */ static unsigned int @@ -388,9 +397,9 @@ first_line_not_before(int *lines, int len, int line) static void frame_stack_pop(PyFrameObject *f) { - assert(f->f_stackdepth > 0); - f->f_stackdepth--; - PyObject *v = f->f_valuestack[f->f_stackdepth]; + assert(f->f_frame->stackdepth > 0); + f->f_frame->stackdepth--; + PyObject *v = f->f_frame->stack[f->f_frame->stackdepth]; Py_DECREF(v); } @@ -430,7 +439,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore * In addition, jumps are forbidden when not tracing, * as this is a debugging feature. */ - switch(f->f_state) { + switch(f->f_frame->f_state) { case FRAME_CREATED: PyErr_Format(PyExc_ValueError, "can't jump from the 'call' trace event of a new frame"); @@ -472,7 +481,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } new_lineno = (int)l_new_lineno; - if (new_lineno < _PyFrame_GetCode(f)->co_firstlineno) { + if (new_lineno < f->f_frame->f_code->co_firstlineno) { PyErr_Format(PyExc_ValueError, "line %d comes before the current code block", new_lineno); @@ -481,8 +490,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore /* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this * should never overflow. */ - int len = (int)(PyBytes_GET_SIZE(_PyFrame_GetCode(f)->co_code) / sizeof(_Py_CODEUNIT)); - int *lines = marklines(_PyFrame_GetCode(f), len); + int len = (int)(PyBytes_GET_SIZE(f->f_frame->f_code->co_code) / sizeof(_Py_CODEUNIT)); + int *lines = marklines(f->f_frame->f_code, len); if (lines == NULL) { return -1; } @@ -496,7 +505,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - int64_t *stacks = mark_stacks(_PyFrame_GetCode(f), len); + int64_t *stacks = mark_stacks(f->f_frame->f_code, len); if (stacks == NULL) { PyMem_Free(lines); return -1; @@ -504,7 +513,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore int64_t best_stack = OVERFLOWED; int best_addr = -1; - int64_t start_stack = stacks[f->f_lasti]; + int64_t start_stack = stacks[f->f_frame->f_lasti]; int err = -1; const char *msg = "cannot find bytecode for specified line"; for (int i = 0; i < len; i++) { @@ -538,7 +547,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } /* Unwind block stack. */ - if (f->f_state == FRAME_SUSPENDED) { + if (f->f_frame->f_state == FRAME_SUSPENDED) { /* Account for value popped by yield */ start_stack = pop_value(start_stack); } @@ -546,9 +555,9 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore frame_stack_pop(f); start_stack = pop_value(start_stack); } - /* Finally set the new f_lasti and return OK. */ + /* Finally set the new lasti and return OK. */ f->f_lineno = 0; - f->f_lasti = best_addr; + f->f_frame->f_lasti = best_addr; return 0; } @@ -579,6 +588,7 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure) static PyGetSetDef frame_getsetlist[] = { + {"f_back", (getter)frame_getback, NULL, NULL}, {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, @@ -612,29 +622,27 @@ frame_dealloc(PyFrameObject *f) Py_TRASHCAN_SAFE_BEGIN(f) PyCodeObject *co = NULL; - /* Kill all local variables including specials. */ - if (f->f_localsptr) { + /* Kill all local variables including specials, if we own them */ + if (f->f_own_locals_memory) { + f->f_own_locals_memory = 0; + InterpreterFrame *frame = f->f_frame; /* Don't clear code object until the end */ - co = _PyFrame_GetCode(f); - PyObject **specials = _PyFrame_Specials(f); - Py_CLEAR(specials[FRAME_SPECIALS_GLOBALS_OFFSET]); - Py_CLEAR(specials[FRAME_SPECIALS_BUILTINS_OFFSET]); - Py_CLEAR(specials[FRAME_SPECIALS_LOCALS_OFFSET]); + co = frame->f_code; + frame->f_code = NULL; + Py_CLEAR(frame->f_globals); + Py_CLEAR(frame->f_builtins); + Py_CLEAR(frame->f_locals); + PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_CLEAR(f->f_localsptr[i]); - } - /* Free items on stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_XDECREF(f->f_valuestack[i]); + Py_CLEAR(locals[i]); } - if (f->f_own_locals_memory) { - PyMem_Free(f->f_localsptr); - f->f_own_locals_memory = 0; + /* stack */ + for (int i = 0; i < frame->stackdepth; i++) { + Py_CLEAR(frame->stack[i]); } - f->f_localsptr = NULL; + PyMem_Free(locals); } - f->f_stackdepth = 0; - Py_XDECREF(f->f_back); + Py_CLEAR(f->f_back); Py_CLEAR(f->f_trace); struct _Py_frame_state *state = get_frame_state(); #ifdef Py_DEBUG @@ -654,29 +662,16 @@ frame_dealloc(PyFrameObject *f) Py_TRASHCAN_SAFE_END(f) } -static inline Py_ssize_t -frame_nslots(PyFrameObject *frame) -{ - return frame->f_valuestack - frame->f_localsptr; -} - static int frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - - /* locals */ - PyObject **localsplus = f->f_localsptr; - for (Py_ssize_t i = frame_nslots(f); --i >= 0; ++localsplus) { - Py_VISIT(*localsplus); - } - - /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_VISIT(f->f_valuestack[i]); + if (f->f_own_locals_memory == 0) { + return 0; } - return 0; + assert(f->f_frame->frame_obj == NULL); + return _PyFrame_Traverse(f->f_frame, visit, arg); } static int @@ -687,34 +682,35 @@ frame_tp_clear(PyFrameObject *f) * frame may also point to this frame, believe itself to still be * active, and try cleaning up this frame again. */ - f->f_state = FRAME_CLEARED; + f->f_frame->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); - PyCodeObject *co = _PyFrame_GetCode(f); + /* locals */ - for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_CLEAR(f->f_localsptr[i]); + PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); + for (int i = 0; i < f->f_frame->nlocalsplus; i++) { + Py_CLEAR(locals[i]); } /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_CLEAR(f->f_valuestack[i]); + for (int i = 0; i < f->f_frame->stackdepth; i++) { + Py_CLEAR(f->f_frame->stack[i]); } - f->f_stackdepth = 0; + f->f_frame->stackdepth = 0; return 0; } static PyObject * frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { - if (_PyFrame_IsExecuting(f)) { + if (_PyFrame_IsExecuting(f->f_frame)) { PyErr_SetString(PyExc_RuntimeError, "cannot clear an executing frame"); return NULL; } - if (f->f_gen) { - _PyGen_Finalize(f->f_gen); - assert(f->f_gen == NULL); + if (f->f_frame->generator) { + _PyGen_Finalize(f->f_frame->generator); + assert(f->f_frame->generator == NULL); } (void)frame_tp_clear(f); Py_RETURN_NONE; @@ -729,7 +725,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) Py_ssize_t res; res = sizeof(PyFrameObject); if (f->f_own_locals_memory) { - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_frame->f_code; res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); } return PyLong_FromSsize_t(res); @@ -742,7 +738,7 @@ static PyObject * frame_repr(PyFrameObject *f) { int lineno = PyFrame_GetLineNumber(f); - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_frame->f_code; return PyUnicode_FromFormat( "<frame at %p, file %R, line %d, code %S>", f, code->co_filename, lineno, code->co_name); @@ -793,33 +789,39 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); -static inline PyFrameObject* -frame_alloc(PyCodeObject *code, PyObject **localsarray) +static InterpreterFrame * +allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) { - int owns; - PyFrameObject *f; + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; + PyObject **localsarray = PyMem_Malloc(sizeof(PyObject *)*size); if (localsarray == NULL) { - int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; - localsarray = PyMem_Malloc(sizeof(PyObject *)*size); - if (localsarray == NULL) { - PyErr_NoMemory(); - return NULL; - } - for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { - localsarray[i] = NULL; - } - owns = 1; + PyErr_NoMemory(); + return NULL; } - else { - owns = 0; + for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { + localsarray[i] = NULL; } + InterpreterFrame *frame = (InterpreterFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + return frame; +} + +static inline PyFrameObject* +frame_alloc(InterpreterFrame *frame, int owns) +{ + PyFrameObject *f; struct _Py_frame_state *state = get_frame_state(); if (state->free_list == NULL) { f = PyObject_GC_New(PyFrameObject, &PyFrame_Type); if (f == NULL) { if (owns) { - PyMem_Free(localsarray); + Py_XDECREF(frame->f_code); + Py_XDECREF(frame->f_builtins); + Py_XDECREF(frame->f_globals); + Py_XDECREF(frame->f_locals); + PyMem_Free(frame); } return NULL; } @@ -835,66 +837,23 @@ frame_alloc(PyCodeObject *code, PyObject **localsarray) state->free_list = state->free_list->f_back; _Py_NewReference((PyObject *)f); } - f->f_localsptr = localsarray; + f->f_frame = frame; f->f_own_locals_memory = owns; return f; } -int -_PyFrame_TakeLocals(PyFrameObject *f) -{ - assert(f->f_own_locals_memory == 0); - assert(f->f_stackdepth == 0); - Py_ssize_t size = frame_nslots(f); - PyObject **copy = PyMem_Malloc(sizeof(PyObject *)*size); - if (copy == NULL) { - for (int i = 0; i < size; i++) { - PyObject *o = f->f_localsptr[i]; - Py_XDECREF(o); - } - PyErr_NoMemory(); - return -1; - } - for (int i = 0; i < size; i++) { - PyObject *o = f->f_localsptr[i]; - copy[i] = o; - } - f->f_own_locals_memory = 1; - f->f_localsptr = copy; - f->f_valuestack = copy + size; - return 0; -} - PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject **localsarray) +_PyFrame_New_NoTrack(InterpreterFrame *frame, int owns) { - assert(con != NULL); - assert(con->fc_globals != NULL); - assert(con->fc_builtins != NULL); - assert(con->fc_code != NULL); - assert(locals == NULL || PyMapping_Check(locals)); - PyCodeObject *code = (PyCodeObject *)con->fc_code; - - PyFrameObject *f = frame_alloc(code, localsarray); + PyFrameObject *f = frame_alloc(frame, owns); if (f == NULL) { return NULL; } - - PyObject **specials = f->f_localsptr + code->co_nlocalsplus; - f->f_valuestack = specials + FRAME_SPECIALS_SIZE; - f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame); - specials[FRAME_SPECIALS_CODE_OFFSET] = Py_NewRef(con->fc_code); - specials[FRAME_SPECIALS_BUILTINS_OFFSET] = Py_NewRef(con->fc_builtins); - specials[FRAME_SPECIALS_GLOBALS_OFFSET] = Py_NewRef(con->fc_globals); - specials[FRAME_SPECIALS_LOCALS_OFFSET] = Py_XNewRef(locals); + f->f_back = NULL; f->f_trace = NULL; - f->f_stackdepth = 0; f->f_trace_lines = 1; f->f_trace_opcodes = 0; - f->f_gen = NULL; - f->f_lasti = -1; f->f_lineno = 0; - f->f_state = FRAME_CREATED; return f; } @@ -917,7 +876,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, .fc_kwdefaults = NULL, .fc_closure = NULL }; - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, &desc, locals, NULL); + InterpreterFrame *frame = allocate_heap_frame(&desc, locals); + if (frame == NULL) { + return NULL; + } + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); if (f) { _PyObject_GC_TRACK(f); } @@ -925,11 +888,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, } static int -_PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) +_PyFrame_OpAlreadyRan(InterpreterFrame *frame, int opcode, int oparg) { const _Py_CODEUNIT *code = - (const _Py_CODEUNIT *)PyBytes_AS_STRING(_PyFrame_GetCode(f)->co_code); - for (int i = 0; i < f->f_lasti; i++) { + (const _Py_CODEUNIT *)PyBytes_AS_STRING(frame->f_code->co_code); + for (int i = 0; i < frame->f_lasti; i++) { if (_Py_OPCODE(code[i]) == opcode && _Py_OPARG(code[i]) == oparg) { return 1; } @@ -938,25 +901,19 @@ _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) } int -PyFrame_FastToLocalsWithError(PyFrameObject *f) -{ +_PyFrame_FastToLocalsWithError(InterpreterFrame *frame) { /* Merge fast locals into f->f_locals */ PyObject *locals; PyObject **fast; PyCodeObject *co; - - if (f == NULL) { - PyErr_BadInternalCall(); - return -1; - } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = frame->f_locals; if (locals == NULL) { - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET] = PyDict_New(); + locals = frame->f_locals = PyDict_New(); if (locals == NULL) return -1; } - co = _PyFrame_GetCode(f); - fast = f->f_localsptr; + co = frame->f_code; + fast = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); @@ -974,9 +931,9 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *value = fast[i]; - if (f->f_state != FRAME_CLEARED) { + if (frame->f_state != FRAME_CLEARED) { if (kind & CO_FAST_FREE) { - // The cell was set by _PyEval_MakeFrameVector() from + // The cell was set when the frame was created from // the function's closure. assert(value != NULL && PyCell_Check(value)); value = PyCell_GET(value); @@ -988,12 +945,12 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) // run yet. if (value != NULL) { if (PyCell_Check(value) && - _PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) { + _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { // (likely) MAKE_CELL must have executed already. value = PyCell_GET(value); } // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL), - // with the initial value set by _PyEval_MakeFrameVector()... + // with the initial value set when the frame was created... // (unlikely) ...or it was set to some initial value by // an earlier call to PyFrame_LocalsToFast(). } @@ -1021,6 +978,16 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) return 0; } +int +PyFrame_FastToLocalsWithError(PyFrameObject *f) +{ + if (f == NULL) { + PyErr_BadInternalCall(); + return -1; + } + return _PyFrame_FastToLocalsWithError(f->f_frame); +} + void PyFrame_FastToLocals(PyFrameObject *f) { @@ -1034,21 +1001,18 @@ PyFrame_FastToLocals(PyFrameObject *f) } void -PyFrame_LocalsToFast(PyFrameObject *f, int clear) +_PyFrame_LocalsToFast(InterpreterFrame *frame, int clear) { /* Merge locals into fast locals */ PyObject *locals; PyObject **fast; PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; - if (f == NULL || f->f_state == FRAME_CLEARED) { - return; - } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = frame->f_locals; if (locals == NULL) return; - fast = f->f_localsptr; - co = _PyFrame_GetCode(f); + fast = _PyFrame_GetLocalsArray(frame); + co = frame->f_code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1070,7 +1034,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyObject *oldvalue = fast[i]; PyObject *cell = NULL; if (kind == CO_FAST_FREE) { - // The cell was set by _PyEval_MakeFrameVector() from + // The cell was set when the frame was created from // the function's closure. assert(oldvalue != NULL && PyCell_Check(oldvalue)); cell = oldvalue; @@ -1078,7 +1042,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) else if (kind & CO_FAST_CELL && oldvalue != NULL) { /* Same test as in PyFrame_FastToLocals() above. */ if (PyCell_Check(oldvalue) && - _PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) { + _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { // (likely) MAKE_CELL must have executed already. cell = oldvalue; } @@ -1102,6 +1066,15 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyErr_Restore(error_type, error_value, error_traceback); } +void +PyFrame_LocalsToFast(PyFrameObject *f, int clear) +{ + if (f == NULL || f->f_frame->f_state == FRAME_CLEARED) { + return; + } + _PyFrame_LocalsToFast(f->f_frame, clear); +} + /* Clear out the free list */ void _PyFrame_ClearFreeList(PyInterpreterState *interp) @@ -1141,7 +1114,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); - PyCodeObject *code = _PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_frame->f_code; assert(code != NULL); Py_INCREF(code); return code; @@ -1153,6 +1126,9 @@ PyFrame_GetBack(PyFrameObject *frame) { assert(frame != NULL); PyFrameObject *back = frame->f_back; + if (back == NULL && frame->f_frame->previous != NULL) { + back = _PyFrame_GetFrameObject(frame->f_frame->previous); + } Py_XINCREF(back); return back; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 8cc965a..86cd9cf 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -6,6 +6,7 @@ #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() #include "frameobject.h" +#include "pycore_frame.h" #include "structmember.h" // PyMemberDef #include "opcode.h" @@ -31,10 +32,17 @@ exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { - Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); + InterpreterFrame *frame = gen->gi_xframe; + if (frame != NULL) { + assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + int err = _PyFrame_Traverse(frame, visit, arg); + if (err) { + return err; + } + } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ return exc_state_traverse(&gen->gi_exc_state, visit, arg); @@ -47,7 +55,7 @@ _PyGen_Finalize(PyObject *self) PyObject *res = NULL; PyObject *error_type, *error_value, *error_traceback; - if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame)) { + if (gen->gi_xframe == NULL || _PyFrameHasCompleted(gen->gi_xframe)) { /* Generator isn't paused, so no need to close */ return; } @@ -79,7 +87,7 @@ _PyGen_Finalize(PyObject *self) issue a RuntimeWarning. */ if (gen->gi_code != NULL && ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && - gen->gi_frame->f_lasti == -1) + gen->gi_xframe->f_lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -122,9 +130,11 @@ gen_dealloc(PyGenObject *gen) and GC_Del. */ Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer); } - if (gen->gi_frame != NULL) { - gen->gi_frame->f_gen = NULL; - Py_CLEAR(gen->gi_frame); + InterpreterFrame *frame = gen->gi_xframe; + if (frame != NULL) { + gen->gi_xframe = NULL; + frame->previous = NULL; + _PyFrame_Clear(frame, 1); } if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin); @@ -141,11 +151,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = gen->gi_frame; + InterpreterFrame *frame = gen->gi_xframe; PyObject *result; *presult = NULL; - if (f != NULL && _PyFrame_IsExecuting(f)) { + if (frame != NULL && _PyFrame_IsExecuting(frame)) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -156,7 +166,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_ValueError, msg); return PYGEN_ERROR; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (frame == NULL || _PyFrameHasCompleted(frame)) { if (PyCoro_CheckExact(gen) && !closing) { /* `gen` is an exhausted coroutine: raise an error, except when called from gen_close(), which should @@ -175,19 +185,15 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return PYGEN_ERROR; } - assert(_PyFrame_IsRunnable(f)); - assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + assert(_PyFrame_IsRunnable(frame)); + assert(frame->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); - gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result; - gen->gi_frame->f_stackdepth++; + frame->stack[frame->stackdepth] = result; + frame->stackdepth++; - /* Generators always return to their most recent caller, not - * necessarily their creator. */ - Py_XINCREF(tstate->frame); - assert(f->f_back == NULL); - f->f_back = tstate->frame; + frame->previous = tstate->frame; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -197,20 +203,20 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } - result = _PyEval_EvalFrame(tstate, f, exc); + result = _PyEval_EvalFrame(tstate, frame, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - /* Don't keep the reference to f_back any longer than necessary. It + assert(tstate->frame == frame->previous); + /* Don't keep the reference to previous any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - assert(f->f_back == tstate->frame); - Py_CLEAR(f->f_back); + frame->previous = NULL; /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (!_PyFrameHasCompleted(f)) { + if (!_PyFrameHasCompleted(frame)) { *presult = result; return PYGEN_NEXT; } @@ -245,10 +251,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* generator can't be rerun, so release the frame */ /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - gen->gi_frame->f_gen = NULL; - gen->gi_frame = NULL; - Py_DECREF(f); + frame->generator = NULL; + gen->gi_xframe = NULL; + _PyFrame_Clear(frame, 1); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } @@ -328,13 +334,13 @@ PyObject * _PyGen_yf(PyGenObject *gen) { PyObject *yf = NULL; - PyFrameObject *f = gen->gi_frame; - if (f) { + if (gen->gi_xframe) { + InterpreterFrame *frame = gen->gi_xframe; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (f->f_lasti < 0) { + if (frame->f_lasti < 0) { /* Return immediately if the frame didn't start yet. YIELD_FROM always come after LOAD_CONST: a code object should not start with YIELD_FROM */ @@ -342,10 +348,10 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(f->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(frame->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - assert(f->f_stackdepth > 0); - yf = f->f_valuestack[f->f_stackdepth-1]; + assert(frame->stackdepth > 0); + yf = frame->stack[frame->stackdepth-1]; Py_INCREF(yf); } @@ -360,10 +366,10 @@ gen_close(PyGenObject *gen, PyObject *args) int err = 0; if (yf) { - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(yf); } if (err == 0) @@ -410,10 +416,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, We have to allow some awaits to work it through, hence the `close_on_genexit` parameter here. */ - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(yf); if (err < 0) return gen_send_ex(gen, Py_None, 1, 0); @@ -422,22 +428,26 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { /* `yf` is a generator or a coroutine. */ PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = tstate->frame; + InterpreterFrame *frame = gen->gi_xframe; + /* Since we are fast-tracking things by skipping the eval loop, we need to update the current frame so the stack trace will be reported correctly to the user. */ /* XXX We should probably be updating the current frame somewhere in ceval.c. */ - tstate->frame = gen->gi_frame; + InterpreterFrame *prev = tstate->frame; + frame->previous = prev; + tstate->frame = frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); - gen->gi_frame->f_state = state; - tstate->frame = f; + gen->gi_xframe->f_state = state; + tstate->frame = prev; + frame->previous = NULL; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; @@ -449,24 +459,24 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(meth); } Py_DECREF(yf); if (!ret) { PyObject *val; /* Pop subiterator from stack */ - assert(gen->gi_frame->f_stackdepth > 0); - gen->gi_frame->f_stackdepth--; - ret = gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth]; + assert(gen->gi_xframe->stackdepth > 0); + gen->gi_xframe->stackdepth--; + ret = gen->gi_xframe->stack[gen->gi_xframe->stackdepth]; assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ - assert(gen->gi_frame->f_lasti >= 0); - gen->gi_frame->f_lasti += 1; + assert(gen->gi_xframe->f_lasti >= 0); + gen->gi_xframe->f_lasti += 1; if (_PyGen_FetchStopIterationValue(&val) == 0) { ret = gen_send(gen, val); Py_DECREF(val); @@ -723,10 +733,28 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored)) static PyObject * gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored)) { - if (gen->gi_frame == NULL) { + if (gen->gi_xframe == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_xframe)); +} + +static PyObject * +_gen_getframe(PyGenObject *gen, const char *const name) +{ + if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { + return NULL; + } + if (gen->gi_xframe == NULL) { + Py_RETURN_NONE; + } + return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(gen->gi_xframe)); +} + +static PyObject * +gen_getframe(PyGenObject *gen, void *Py_UNUSED(ignored)) +{ + return _gen_getframe(gen, "gi_frame"); } static PyGetSetDef gen_getsetlist[] = { @@ -737,11 +765,11 @@ static PyGetSetDef gen_getsetlist[] = { {"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, PyDoc_STR("object being iterated by yield from, or None")}, {"gi_running", (getter)gen_getrunning, NULL, NULL}, + {"gi_frame", (getter)gen_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY|PY_AUDIT_READ}, {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY|PY_AUDIT_READ}, {NULL} /* Sentinel */ }; @@ -815,6 +843,84 @@ PyTypeObject PyGen_Type = { }; static PyObject * +make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame) +{ + PyGenObject *gen = PyObject_GC_New(PyGenObject, type); + if (gen == NULL) { + assert(frame->frame_obj == NULL); + _PyFrame_Clear(frame, 1); + return NULL; + } + gen->gi_xframe = frame; + frame->generator = (PyObject *)gen; + gen->gi_code = frame->f_code; + Py_INCREF(gen->gi_code); + gen->gi_weakreflist = NULL; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; + gen->gi_exc_state.previous_item = NULL; + if (con->fc_name != NULL) + gen->gi_name = con->fc_name; + else + gen->gi_name = gen->gi_code->co_name; + Py_INCREF(gen->gi_name); + if (con->fc_qualname != NULL) + gen->gi_qualname = con->fc_qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); + _PyObject_GC_TRACK(gen); + return (PyObject *)gen; +} + +static PyObject * +compute_cr_origin(int origin_depth); + +PyObject * +_Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame) +{ + int coro_flags = ((PyCodeObject *)con->fc_code)->co_flags & + (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); + assert(coro_flags); + if (coro_flags == CO_GENERATOR) { + return make_gen(&PyGen_Type, con, frame); + } + if (coro_flags == CO_ASYNC_GENERATOR) { + PyAsyncGenObject *o; + o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, con, frame); + if (o == NULL) { + return NULL; + } + o->ag_finalizer = NULL; + o->ag_closed = 0; + o->ag_hooks_inited = 0; + o->ag_running_async = 0; + return (PyObject*)o; + } + assert (coro_flags == CO_COROUTINE); + PyObject *coro = make_gen(&PyCoro_Type, con, frame); + if (!coro) { + return NULL; + } + PyThreadState *tstate = _PyThreadState_GET(); + int origin_depth = tstate->coroutine_origin_tracking_depth; + + if (origin_depth == 0) { + ((PyCoroObject *)coro)->cr_origin = NULL; + } else { + PyObject *cr_origin = compute_cr_origin(origin_depth); + ((PyCoroObject *)coro)->cr_origin = cr_origin; + if (!cr_origin) { + Py_DECREF(coro); + return NULL; + } + } + + return coro; +} + +static PyObject * gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, PyObject *name, PyObject *qualname) { @@ -823,8 +929,16 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, Py_DECREF(f); return NULL; } - gen->gi_frame = f; - f->f_gen = (PyObject *) gen; + + /* Take ownership of the frame */ + assert(f->f_frame->frame_obj == NULL); + assert(f->f_own_locals_memory); + gen->gi_xframe = f->f_frame; + gen->gi_xframe->frame_obj = f; + f->f_own_locals_memory = 0; + gen->gi_xframe->generator = (PyObject *) gen; + assert(PyObject_GC_IsTracked((PyObject *)f)); + gen->gi_code = PyFrame_GetCode(f); gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_type = NULL; @@ -958,12 +1072,19 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored)) static PyObject * cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored)) { - if (coro->cr_frame == NULL) { + if (coro->cr_xframe == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_xframe)); +} + +static PyObject * +cr_getframe(PyCoroObject *coro, void *Py_UNUSED(ignored)) +{ + return _gen_getframe((PyGenObject *)coro, "cr_frame"); } + static PyGetSetDef coro_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the coroutine")}, @@ -972,11 +1093,11 @@ static PyGetSetDef coro_getsetlist[] = { {"cr_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, {"cr_running", (getter)cr_getrunning, NULL, NULL}, + {"cr_frame", (getter)cr_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef coro_memberlist[] = { - {"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY|PY_AUDIT_READ}, {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ}, {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY}, {NULL} /* Sentinel */ @@ -1150,11 +1271,11 @@ PyTypeObject _PyCoroWrapper_Type = { static PyObject * compute_cr_origin(int origin_depth) { - PyFrameObject *frame = PyEval_GetFrame(); + InterpreterFrame *frame = _PyEval_GetFrame(); /* First count how many frames we have */ int frame_count = 0; for (; frame && frame_count < origin_depth; ++frame_count) { - frame = frame->f_back; + frame = frame->previous; } /* Now collect them */ @@ -1162,20 +1283,19 @@ compute_cr_origin(int origin_depth) if (cr_origin == NULL) { return NULL; } - frame = PyEval_GetFrame(); + frame = _PyEval_GetFrame(); for (int i = 0; i < frame_count; ++i) { - PyCodeObject *code = PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_code; PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, - PyFrame_GetLineNumber(frame), + PyCode_Addr2Line(frame->f_code, frame->f_lasti*2), code->co_name); - Py_DECREF(code); if (!frameinfo) { Py_DECREF(cr_origin); return NULL; } PyTuple_SET_ITEM(cr_origin, i, frameinfo); - frame = frame->f_back; + frame = frame->previous; } return cr_origin; @@ -1346,6 +1466,11 @@ async_gen_athrow(PyAsyncGenObject *o, PyObject *args) return async_gen_athrow_new(o, args); } +static PyObject * +ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored)) +{ + return _gen_getframe((PyGenObject *)ag, "ag_frame"); +} static PyGetSetDef async_gen_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, @@ -1354,11 +1479,11 @@ static PyGetSetDef async_gen_getsetlist[] = { PyDoc_STR("qualified name of the async generator")}, {"ag_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, + {"ag_frame", (getter)ag_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef async_gen_memberlist[] = { - {"ag_frame", T_OBJECT, offsetof(PyAsyncGenObject, ag_frame), READONLY|PY_AUDIT_READ}, {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async), READONLY}, {"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY|PY_AUDIT_READ}, @@ -1865,7 +1990,7 @@ static PyObject * async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) { PyGenObject *gen = (PyGenObject*)o->agt_gen; - PyFrameObject *f = gen->gi_frame; + InterpreterFrame *frame = gen->gi_xframe; PyObject *retval; if (o->agt_state == AWAITABLE_STATE_CLOSED) { @@ -1875,7 +2000,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (frame == NULL || _PyFrameHasCompleted(frame)) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 42a9978..43b4d07 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -11,6 +11,7 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_unionobject.h" // _Py_union_type_or #include "frameobject.h" +#include "pycore_frame.h" #include "opcode.h" // MAKE_CELL #include "structmember.h" // PyMemberDef @@ -8867,12 +8868,13 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, return -1; } - PyObject *firstarg = f->f_localsptr[0]; + assert(f->f_frame->nlocalsplus > 0); + PyObject *firstarg = _PyFrame_GetLocalsArray(f->f_frame)[0]; // The first argument might be a cell. if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) { // "firstarg" is a cell here unless (very unlikely) super() // was called from the C-API before the first MAKE_CELL op. - if (f->f_lasti >= 0) { + if (f->f_frame->f_lasti >= 0) { assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); @@ -8892,7 +8894,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); assert(PyUnicode_Check(name)); if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) { - PyObject *cell = f->f_localsptr[i]; + PyObject *cell = _PyFrame_GetLocalsArray(f->f_frame)[i]; if (cell == NULL || !PyCell_Check(cell)) { PyErr_SetString(PyExc_RuntimeError, "super(): bad __class__ cell"); diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 7b2a9b5..ebc0f28 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -456,6 +456,7 @@ <ClCompile Include="..\Python\errors.c" /> <ClCompile Include="..\Python\fileutils.c" /> <ClCompile Include="..\Python\formatter_unicode.c" /> + <ClCompile Include="..\Python\frame.c" /> <ClCompile Include="..\Python\frozen.c" /> <ClCompile Include="..\Python\future.c" /> <ClCompile Include="..\Python\getargs.c" /> diff --git a/Python/_warnings.c b/Python/_warnings.c index 9f68da2..cf2110d 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -854,10 +854,8 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, *lineno = 1; } else { - globals = _PyFrame_GetGlobals(f); - PyCodeObject *code = PyFrame_GetCode(f); - *filename = code->co_filename; - Py_DECREF(code); + globals = f->f_frame->f_globals; + *filename = f->f_frame->f_code->co_filename; Py_INCREF(*filename); *lineno = PyFrame_GetLineNumber(f); Py_DECREF(f); diff --git a/Python/ceval.c b/Python/ceval.c index 90112aa..02eef9b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -62,27 +62,27 @@ static int lltrace; static int prtrace(PyThreadState *, PyObject *, const char *); #endif static int call_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, + PyThreadState *, InterpreterFrame *, int, PyObject *); static int call_trace_protected(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, + PyThreadState *, InterpreterFrame *, int, PyObject *); static void call_exc_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *); + PyThreadState *, InterpreterFrame *); static int maybe_call_line_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, int); -static void maybe_dtrace_line(PyFrameObject *, PyTraceInfo *, int); -static void dtrace_function_entry(PyFrameObject *); -static void dtrace_function_return(PyFrameObject *); + PyThreadState *, InterpreterFrame *, int); +static void maybe_dtrace_line(InterpreterFrame *, PyTraceInfo *, int); +static void dtrace_function_entry(InterpreterFrame *); +static void dtrace_function_return(InterpreterFrame *); -static PyObject * import_name(PyThreadState *, PyFrameObject *, +static PyObject * import_name(PyThreadState *, InterpreterFrame *, PyObject *, PyObject *, PyObject *); static PyObject * import_from(PyThreadState *, PyObject *, PyObject *); static int import_all_from(PyThreadState *, PyObject *, PyObject *); static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyObject *); static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyThreadState *, PyObject *, PyObject *, - PyFrameObject *, const _Py_CODEUNIT *); + InterpreterFrame *, const _Py_CODEUNIT *); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs); static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int); @@ -1065,14 +1065,14 @@ PyEval_EvalFrame(PyFrameObject *f) { /* Function kept for backward compatibility */ PyThreadState *tstate = _PyThreadState_GET(); - return _PyEval_EvalFrame(tstate, f, 0); + return _PyEval_EvalFrame(tstate, f->f_frame, 0); } PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { PyThreadState *tstate = _PyThreadState_GET(); - return _PyEval_EvalFrame(tstate, f, throwflag); + return _PyEval_EvalFrame(tstate, f->f_frame, throwflag); } @@ -1231,7 +1231,7 @@ eval_frame_handle_pending(PyThreadState *tstate) if (cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \ goto tracing_dispatch; \ } \ - f->f_lasti = INSTR_OFFSET(); \ + frame->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ DISPATCH_GOTO(); \ } @@ -1320,7 +1320,7 @@ eval_frame_handle_pending(PyThreadState *tstate) /* The stack can grow at most MAXINT deep, as co_nlocals and co_stacksize are ints. */ -#define STACK_LEVEL() ((int)(stack_pointer - f->f_valuestack)) +#define STACK_LEVEL() ((int)(stack_pointer - frame->stack)) #define EMPTY() (STACK_LEVEL() == 0) #define TOP() (stack_pointer[-1]) #define SECOND() (stack_pointer[-2]) @@ -1388,12 +1388,12 @@ eval_frame_handle_pending(PyThreadState *tstate) #define UPDATE_PREV_INSTR_OPARG(instr, oparg) ((uint8_t*)(instr))[-1] = (oparg) -#define GLOBALS() specials[FRAME_SPECIALS_GLOBALS_OFFSET] -#define BUILTINS() specials[FRAME_SPECIALS_BUILTINS_OFFSET] -#define LOCALS() specials[FRAME_SPECIALS_LOCALS_OFFSET] +#define GLOBALS() frame->f_globals +#define BUILTINS() frame->f_builtins +#define LOCALS() frame->f_locals PyObject* _Py_HOT_FUNCTION -_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) +_PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int throwflag) { _Py_EnsureTstateNotNULL(tstate); @@ -1409,7 +1409,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) _Py_CODEUNIT *next_instr; int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ - PyObject **localsplus, **specials; + PyObject **localsplus; PyObject *retval = NULL; /* Return value */ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker; PyCodeObject *co; @@ -1438,9 +1438,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tstate->cframe = &cframe; /* push frame */ - tstate->frame = f; - specials = f->f_valuestack - FRAME_SPECIALS_SIZE; - co = (PyCodeObject *)specials[FRAME_SPECIALS_CODE_OFFSET]; + tstate->frame = frame; + co = frame->f_code; if (cframe.use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1459,7 +1458,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) whenever an exception is detected. */ if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj, - tstate, f, + tstate, frame, PyTrace_CALL, Py_None)) { /* Trace function raised an error */ goto exit_eval_frame; @@ -1470,7 +1469,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) return itself and isn't called for "line" events */ if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj, - tstate, f, + tstate, frame, PyTrace_CALL, Py_None)) { /* Profile function raised an error */ goto exit_eval_frame; @@ -1479,7 +1478,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } if (PyDTrace_FUNCTION_ENTRY_ENABLED()) - dtrace_function_entry(f); + dtrace_function_entry(frame); /* Increment the warmup counter and quicken if warm enough * _Py_Quicken is idempotent so we don't worry about overflow */ @@ -1495,34 +1494,34 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) names = co->co_names; consts = co->co_consts; - localsplus = f->f_localsptr; + localsplus = _PyFrame_GetLocalsArray(frame); first_instr = co->co_firstinstr; /* - f->f_lasti refers to the index of the last instruction, + frame->f_lasti refers to the index of the last instruction, unless it's -1 in which case next_instr should be first_instr. - YIELD_FROM sets f_lasti to itself, in order to repeatedly yield + YIELD_FROM sets frame->f_lasti to itself, in order to repeatedly yield multiple values. When the PREDICT() macros are enabled, some opcode pairs follow in - direct succession without updating f->f_lasti. A successful + direct succession without updating frame->f_lasti. A successful prediction effectively links the two codes together as if they - were a single new opcode; accordingly,f->f_lasti will point to + were a single new opcode; accordingly,frame->f_lasti will point to the first code in the pair (for instance, GET_ITER followed by - FOR_ITER is effectively a single opcode and f->f_lasti will point + FOR_ITER is effectively a single opcode and frame->f_lasti will point to the beginning of the combined pair.) */ - assert(f->f_lasti >= -1); - next_instr = first_instr + f->f_lasti + 1; - stack_pointer = f->f_valuestack + f->f_stackdepth; - /* Set f->f_stackdepth to -1. + assert(frame->f_lasti >= -1); + next_instr = first_instr + frame->f_lasti + 1; + stack_pointer = frame->stack + frame->stackdepth; + /* Set stackdepth to -1. * Update when returning or calling trace function. Having f_stackdepth <= 0 ensures that invalid values are not visible to the cycle GC. We choose -1 rather than 0 to assist debugging. */ - f->f_stackdepth = -1; - f->f_state = FRAME_EXECUTING; + frame->stackdepth = -1; + frame->f_state = FRAME_EXECUTING; #ifdef LLTRACE { @@ -1546,7 +1545,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) #endif for (;;) { - assert(stack_pointer >= f->f_valuestack); /* else underflow */ + assert(STACK_LEVEL() >= 0); /* else underflow */ assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */ assert(!_PyErr_Occurred(tstate)); @@ -1586,12 +1585,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tracing_dispatch: { - int instr_prev = f->f_lasti; - f->f_lasti = INSTR_OFFSET(); + int instr_prev = frame->f_lasti; + frame->f_lasti = INSTR_OFFSET(); TRACING_NEXTOPARG(); if (PyDTrace_LINE_ENABLED()) - maybe_dtrace_line(f, &tstate->trace_info, instr_prev); + maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); /* line-by-line tracing support */ @@ -1600,19 +1599,19 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) int err; /* see maybe_call_line_trace() for expository comments */ - f->f_stackdepth = (int)(stack_pointer - f->f_valuestack); + frame->stackdepth = (int)(stack_pointer - frame->stack); err = maybe_call_line_trace(tstate->c_tracefunc, tstate->c_traceobj, - tstate, f, instr_prev); + tstate, frame, instr_prev); if (err) { /* trace function raised an exception */ goto error; } /* Reload possibly changed frame fields */ - JUMPTO(f->f_lasti); - stack_pointer = f->f_valuestack+f->f_stackdepth; - f->f_stackdepth = -1; + JUMPTO(frame->f_lasti); + stack_pointer = frame->stack+frame->stackdepth; + frame->stackdepth = -1; TRACING_NEXTOPARG(); } } @@ -1623,11 +1622,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (lltrace) { if (HAS_ARG(opcode)) { printf("%d: %d, %d\n", - f->f_lasti, opcode, oparg); + frame->f_lasti, opcode, oparg); } else { printf("%d: %d\n", - f->f_lasti, opcode); + frame->f_lasti, opcode); } } #endif @@ -1876,7 +1875,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) speedup on microbenchmarks. */ if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { - sum = unicode_concatenate(tstate, left, right, f, next_instr); + sum = unicode_concatenate(tstate, left, right, frame, next_instr); /* unicode_concatenate consumed the ref to left */ } else { @@ -2162,7 +2161,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) PyObject *left = TOP(); PyObject *sum; if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { - sum = unicode_concatenate(tstate, left, right, f, next_instr); + sum = unicode_concatenate(tstate, left, right, frame, next_instr); /* unicode_concatenate consumed the ref to left */ } else { @@ -2322,8 +2321,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(RETURN_VALUE): { retval = POP(); assert(EMPTY()); - f->f_state = FRAME_RETURNED; - f->f_stackdepth = 0; + frame->f_state = FRAME_RETURNED; + frame->stackdepth = 0; goto exiting; } @@ -2480,7 +2479,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (retval == NULL) { if (tstate->c_tracefunc != NULL && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f); + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); if (_PyGen_FetchStopIterationValue(&retval) == 0) { gen_status = PYGEN_RETURN; } @@ -2508,10 +2507,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) assert (gen_status == PYGEN_NEXT); /* receiver remains on stack, retval is value to be yielded */ /* and repeat... */ - assert(f->f_lasti > 0); - f->f_lasti -= 1; - f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - f->f_valuestack); + assert(frame->f_lasti > 0); + frame->f_lasti -= 1; + frame->f_state = FRAME_SUSPENDED; + frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2527,8 +2526,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } retval = w; } - f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - f->f_valuestack); + frame->f_state = FRAME_SUSPENDED; + frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2575,7 +2574,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(POP_EXCEPT_AND_RERAISE): { PyObject *lasti = PEEK(4); if (PyLong_Check(lasti)) { - f->f_lasti = PyLong_AsLong(lasti); + frame->f_lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -2606,7 +2605,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (oparg) { PyObject *lasti = PEEK(oparg+3); if (PyLong_Check(lasti)) { - f->f_lasti = PyLong_AsLong(lasti); + frame->f_lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -3579,7 +3578,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) PyObject *fromlist = POP(); PyObject *level = TOP(); PyObject *res; - res = import_name(tstate, f, name, fromlist, level); + res = import_name(tstate, frame, name, fromlist, level); Py_DECREF(level); Py_DECREF(fromlist); SET_TOP(res); @@ -3591,7 +3590,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(IMPORT_STAR): { PyObject *from = POP(), *locals; int err; - if (PyFrame_FastToLocalsWithError(f) < 0) { + if (_PyFrame_FastToLocalsWithError(frame) < 0) { Py_DECREF(from); goto error; } @@ -3604,7 +3603,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) goto error; } err = import_all_from(tstate, locals, from); - PyFrame_LocalsToFast(f, 0); + _PyFrame_LocalsToFast(frame, 0); Py_DECREF(from); if (err != 0) goto error; @@ -3918,7 +3917,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) goto error; } else if (tstate->c_tracefunc != NULL) { - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f); + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); } _PyErr_Clear(tstate); } @@ -4381,7 +4380,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) default: fprintf(stderr, "XXX lineno: %d, opcode: %d\n", - PyFrame_GetLineNumber(f), + PyCode_Addr2Line(frame->f_code, frame->f_lasti*2), opcode); _PyErr_SetString(tstate, PyExc_SystemError, "unknown opcode"); goto error; @@ -4450,19 +4449,22 @@ error: #endif /* Log traceback info. */ - PyTraceBack_Here(f); + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f != NULL) { + PyTraceBack_Here(f); + } if (tstate->c_tracefunc != NULL) { /* Make sure state is set to FRAME_EXECUTING for tracing */ - assert(f->f_state == FRAME_EXECUTING); - f->f_state = FRAME_UNWINDING; + assert(frame->f_state == FRAME_EXECUTING); + frame->f_state = FRAME_UNWINDING; call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, - tstate, f); + tstate, frame); } exception_unwind: - f->f_state = FRAME_UNWINDING; - /* We can't use f->f_lasti here, as RERAISE may have set it */ + frame->f_state = FRAME_UNWINDING; + /* We can't use frame->f_lasti here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; if (get_exception_handler(co, offset, &level, &handler, &lasti) == 0) { @@ -4477,7 +4479,7 @@ exception_unwind: } PyObject *exc, *val, *tb; if (lasti) { - PyObject *lasti = PyLong_FromLong(f->f_lasti); + PyObject *lasti = PyLong_FromLong(frame->f_lasti); if (lasti == NULL) { goto exception_unwind; } @@ -4502,8 +4504,8 @@ exception_unwind: PUSH(exc); JUMPTO(handler); /* Resume normal execution */ - f->f_state = FRAME_EXECUTING; - f->f_lasti = handler; + frame->f_state = FRAME_EXECUTING; + frame->f_lasti = handler; NEXTOPARG(); goto dispatch_opcode; } /* main loop */ @@ -4516,19 +4518,19 @@ exception_unwind: PyObject *o = POP(); Py_XDECREF(o); } - f->f_stackdepth = 0; - f->f_state = FRAME_RAISED; + frame->stackdepth = 0; + frame->f_state = FRAME_RAISED; exiting: if (cframe.use_tracing) { if (tstate->c_tracefunc) { if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj, - tstate, f, PyTrace_RETURN, retval)) { + tstate, frame, PyTrace_RETURN, retval)) { Py_CLEAR(retval); } } if (tstate->c_profilefunc) { if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj, - tstate, f, PyTrace_RETURN, retval)) { + tstate, frame, PyTrace_RETURN, retval)) { Py_CLEAR(retval); } } @@ -4541,10 +4543,9 @@ exit_eval_frame: tstate->cframe->use_tracing = cframe.use_tracing; if (PyDTrace_FUNCTION_RETURN_ENABLED()) - dtrace_function_return(f); + dtrace_function_return(frame); _Py_LeaveRecursiveCall(tstate); - tstate->frame = f->f_back; - + tstate->frame = frame->previous; return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } @@ -5051,116 +5052,110 @@ fail: /* Jump here from prelude on failure */ } - -PyFrameObject * -_PyEval_MakeFrameVector(PyThreadState *tstate, +static InterpreterFrame * +make_coro_frame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, - PyObject *kwnames, PyObject** localsarray) + PyObject *kwnames) { assert(is_tstate_valid(tstate)); assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults)); - - /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, con, locals, localsarray); - if (f == NULL) { + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; + PyObject **localsarray = PyMem_Malloc(sizeof(PyObject *)*size); + if (localsarray == NULL) { + PyErr_NoMemory(); return NULL; } - if (initialize_locals(tstate, con, f->f_localsptr, args, argcount, kwnames)) { - Py_DECREF(f); + for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { + localsarray[i] = NULL; + } + InterpreterFrame *frame = (InterpreterFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + assert(frame->frame_obj == NULL); + if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { + _PyFrame_Clear(frame, 1); return NULL; } - return f; + return frame; } static PyObject * -make_coro(PyFrameConstructor *con, PyFrameObject *f) +make_coro(PyThreadState *tstate, PyFrameConstructor *con, + PyObject *locals, + PyObject* const* args, size_t argcount, + PyObject *kwnames) { assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); - PyObject *gen; - int is_coro = ((PyCodeObject *)con->fc_code)->co_flags & CO_COROUTINE; - - /* Don't need to keep the reference to f_back, it will be set - * when the generator is resumed. */ - Py_CLEAR(f->f_back); - - /* Create a new generator that owns the ready to run frame - * and return that as the value. */ - if (is_coro) { - gen = PyCoro_New(f, con->fc_name, con->fc_qualname); - } else if (((PyCodeObject *)con->fc_code)->co_flags & CO_ASYNC_GENERATOR) { - gen = PyAsyncGen_New(f, con->fc_name, con->fc_qualname); - } else { - gen = PyGen_NewWithQualName(f, con->fc_name, con->fc_qualname); + InterpreterFrame *frame = make_coro_frame(tstate, con, locals, args, argcount, kwnames); + if (frame == NULL) { + return NULL; } + PyObject *gen = _Py_MakeCoro(con, frame); if (gen == NULL) { return NULL; } - _PyObject_GC_TRACK(f); - return gen; } +static InterpreterFrame * +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, + PyObject *locals, PyObject* const* args, + size_t argcount, PyObject *kwnames) +{ + InterpreterFrame * frame = _PyThreadState_PushFrame(tstate, con, locals); + if (frame == NULL) { + return NULL; + } + PyObject **localsarray = _PyFrame_GetLocalsArray(frame); + if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { + _PyFrame_Clear(frame, 0); + return NULL; + } + frame->previous = tstate->frame; + tstate->frame = frame; + return frame; +} + +static int +_PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame) +{ + ++tstate->recursion_depth; + assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + if (_PyFrame_Clear(frame, 0)) { + return -1; + } + assert(frame->frame_obj == NULL); + --tstate->recursion_depth; + tstate->frame = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + return 0; +} + PyObject * _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - PyObject **localsarray; PyCodeObject *code = (PyCodeObject *)con->fc_code; int is_coro = code->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); if (is_coro) { - localsarray = NULL; - } - else { - int size = code->co_nlocalsplus + code->co_stacksize + - FRAME_SPECIALS_SIZE; - localsarray = _PyThreadState_PushLocals(tstate, size); - if (localsarray == NULL) { - return NULL; - } + return make_coro(tstate, con, locals, args, argcount, kwnames); } - PyFrameObject *f = _PyEval_MakeFrameVector( - tstate, con, locals, args, argcount, kwnames, localsarray); - if (f == NULL) { - if (!is_coro) { - _PyThreadState_PopLocals(tstate, localsarray); - } + InterpreterFrame *frame = _PyEvalFramePushAndInit( + tstate, con, locals, args, argcount, kwnames); + if (frame == NULL) { return NULL; } - if (is_coro) { - return make_coro(con, f); - } - PyObject *retval = _PyEval_EvalFrame(tstate, f, 0); - assert(f->f_stackdepth == 0); - - /* decref'ing the frame can cause __del__ methods to get invoked, - which can call back into Python. While we're done with the - current Python frame (f), the associated C stack is still in use, - so recursion_depth must be boosted for the duration. - */ - assert (!is_coro); - assert(f->f_own_locals_memory == 0); - if (Py_REFCNT(f) > 1) { - Py_DECREF(f); - _PyObject_GC_TRACK(f); - if (_PyFrame_TakeLocals(f)) { - Py_CLEAR(retval); - } - } - else { - ++tstate->recursion_depth; - f->f_localsptr = NULL; - for (int i = 0; i < code->co_nlocalsplus + FRAME_SPECIALS_SIZE; i++) { - Py_XDECREF(localsarray[i]); - } - Py_DECREF(f); - --tstate->recursion_depth; + assert (tstate->interp->eval_frame != NULL); + PyObject *retval = _PyEval_EvalFrame(tstate, frame, 0); + assert(frame->stackdepth == 0); + if (_PyEvalFrameClearAndPop(tstate, frame)) { + retval = NULL; } - _PyThreadState_PopLocals(tstate, localsarray); return retval; } @@ -5467,7 +5462,7 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str) static void call_exc_trace(Py_tracefunc func, PyObject *self, PyThreadState *tstate, - PyFrameObject *f) + InterpreterFrame *f) { PyObject *type, *value, *traceback, *orig_traceback, *arg; int err; @@ -5497,7 +5492,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self, static int call_trace_protected(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, PyFrameObject *frame, + PyThreadState *tstate, InterpreterFrame *frame, int what, PyObject *arg) { PyObject *type, *value, *traceback; @@ -5518,9 +5513,9 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, } static void -initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame) +initialize_trace_info(PyTraceInfo *trace_info, InterpreterFrame *frame) { - PyCodeObject *code = _PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_code; if (trace_info->code != code) { trace_info->code = code; _PyCode_InitAddressRange(code, &trace_info->bounds); @@ -5529,7 +5524,7 @@ initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame) static int call_trace(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, PyFrameObject *frame, + PyThreadState *tstate, InterpreterFrame *frame, int what, PyObject *arg) { int result; @@ -5537,15 +5532,19 @@ call_trace(Py_tracefunc func, PyObject *obj, return 0; tstate->tracing++; tstate->cframe->use_tracing = 0; + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + return -1; + } if (frame->f_lasti < 0) { - frame->f_lineno = _PyFrame_GetCode(frame)->co_firstlineno; + f->f_lineno = frame->f_code->co_firstlineno; } else { initialize_trace_info(&tstate->trace_info, frame); - frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); + f->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); } - result = func(obj, frame, what, arg); - frame->f_lineno = 0; + result = func(obj, f, what, arg); + f->f_lineno = 0; tstate->cframe->use_tracing = ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL)); tstate->tracing--; @@ -5572,7 +5571,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args) /* See Objects/lnotab_notes.txt for a description of how tracing works. */ static int maybe_call_line_trace(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, PyFrameObject *frame, int instr_prev) + PyThreadState *tstate, InterpreterFrame *frame, int instr_prev) { int result = 0; @@ -5583,14 +5582,18 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, initialize_trace_info(&tstate->trace_info, frame); int lastline = _PyCode_CheckLineNumber(instr_prev*2, &tstate->trace_info.bounds); int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); - if (line != -1 && frame->f_trace_lines) { + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + return -1; + } + if (line != -1 && f->f_trace_lines) { /* Trace backward edges or if line number has changed */ if (frame->f_lasti < instr_prev || line != lastline) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } } /* Always emit an opcode event if we're tracing all opcodes. */ - if (frame->f_trace_opcodes) { + if (f->f_trace_opcodes) { result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None); } return result; @@ -5737,19 +5740,33 @@ _PyEval_GetAsyncGenFinalizer(void) return tstate->async_gen_finalizer; } +InterpreterFrame * +_PyEval_GetFrame(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return tstate->frame; +} + PyFrameObject * PyEval_GetFrame(void) { PyThreadState *tstate = _PyThreadState_GET(); - return tstate->frame; + if (tstate->frame == NULL) { + return NULL; + } + PyFrameObject *f = _PyFrame_GetFrameObject(tstate->frame); + if (f == NULL) { + PyErr_Clear(); + } + return f; } PyObject * _PyEval_GetBuiltins(PyThreadState *tstate) { - PyFrameObject *frame = tstate->frame; + InterpreterFrame *frame = tstate->frame; if (frame != NULL) { - return _PyFrame_GetBuiltins(frame); + return frame->f_builtins; } return tstate->interp->builtins; } @@ -5780,18 +5797,17 @@ PyObject * PyEval_GetLocals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; if (current_frame == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist"); return NULL; } - if (PyFrame_FastToLocalsWithError(current_frame) < 0) { + if (_PyFrame_FastToLocalsWithError(current_frame) < 0) { return NULL; } - PyObject *locals = current_frame->f_valuestack[ - FRAME_SPECIALS_LOCALS_OFFSET-FRAME_SPECIALS_SIZE]; + PyObject *locals = current_frame->f_locals; assert(locals != NULL); return locals; } @@ -5800,22 +5816,22 @@ PyObject * PyEval_GetGlobals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; if (current_frame == NULL) { return NULL; } - return _PyFrame_GetGlobals(current_frame); + return current_frame->f_globals; } int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; int result = cf->cf_flags != 0; if (current_frame != NULL) { - const int codeflags = _PyFrame_GetCode(current_frame)->co_flags; + const int codeflags = current_frame->f_code->co_flags; const int compilerflags = codeflags & PyCF_MASK; if (compilerflags) { result = 1; @@ -6049,22 +6065,21 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) } static PyObject * -import_name(PyThreadState *tstate, PyFrameObject *f, +import_name(PyThreadState *tstate, InterpreterFrame *frame, PyObject *name, PyObject *fromlist, PyObject *level) { _Py_IDENTIFIER(__import__); PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemIdWithError(_PyFrame_GetBuiltins(f), &PyId___import__); + import_func = _PyDict_GetItemIdWithError(frame->f_builtins, &PyId___import__); if (import_func == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found"); } return NULL; } - PyObject *locals = f->f_valuestack[ - FRAME_SPECIALS_LOCALS_OFFSET-FRAME_SPECIALS_SIZE]; + PyObject *locals = frame->f_locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { int ilevel = _PyLong_AsInt(level); @@ -6073,7 +6088,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, } res = PyImport_ImportModuleLevelObject( name, - _PyFrame_GetGlobals(f), + frame->f_globals, locals == NULL ? Py_None :locals, fromlist, ilevel); @@ -6083,7 +6098,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, Py_INCREF(import_func); stack[0] = name; - stack[1] = _PyFrame_GetGlobals(f); + stack[1] = frame->f_globals; stack[2] = locals == NULL ? Py_None : locals; stack[3] = fromlist; stack[4] = level; @@ -6389,7 +6404,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevop static PyObject * unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, - PyFrameObject *f, const _Py_CODEUNIT *next_instr) + InterpreterFrame *frame, const _Py_CODEUNIT *next_instr) { PyObject *res; if (Py_REFCNT(v) == 2) { @@ -6404,14 +6419,14 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, switch (opcode) { case STORE_FAST: { - PyObject **localsplus = f->f_localsptr; + PyObject **localsplus = _PyFrame_GetLocalsArray(frame); if (GETLOCAL(oparg) == v) SETLOCAL(oparg, NULL); break; } case STORE_DEREF: { - PyObject *c = f->f_localsptr[oparg]; + PyObject *c = _PyFrame_GetLocalsArray(frame)[oparg]; if (PyCell_GET(c) == v) { PyCell_SET(c, NULL); Py_DECREF(v); @@ -6420,10 +6435,9 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, } case STORE_NAME: { - PyObject *names = _PyFrame_GetCode(f)->co_names; + PyObject *names = frame->f_code->co_names; PyObject *name = GETITEM(names, oparg); - PyObject *locals = f->f_valuestack[ - FRAME_SPECIALS_LOCALS_OFFSET-FRAME_SPECIALS_SIZE]; + PyObject *locals = frame->f_locals; if (locals && PyDict_CheckExact(locals)) { PyObject *w = PyDict_GetItemWithError(locals, name); if ((w == v && PyDict_DelItem(locals, name) != 0) || @@ -6501,38 +6515,38 @@ _PyEval_RequestCodeExtraIndex(freefunc free) } static void -dtrace_function_entry(PyFrameObject *f) +dtrace_function_entry(InterpreterFrame *frame) { const char *filename; const char *funcname; int lineno; - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyFrame_GetLineNumber(f); + lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*2); PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); } static void -dtrace_function_return(PyFrameObject *f) +dtrace_function_return(InterpreterFrame *frame) { const char *filename; const char *funcname; int lineno; - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyFrame_GetLineNumber(f); + lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*2); PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); } /* DTrace equivalent of maybe_call_line_trace. */ static void -maybe_dtrace_line(PyFrameObject *frame, +maybe_dtrace_line(InterpreterFrame *frame, PyTraceInfo *trace_info, int instr_prev) { @@ -6542,25 +6556,26 @@ maybe_dtrace_line(PyFrameObject *frame, instruction window, reset the window. */ initialize_trace_info(trace_info, frame); + int lastline = _PyCode_CheckLineNumber(instr_prev*2, &trace_info->bounds); int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &trace_info->bounds); - /* If the last instruction falls at the start of a line or if - it represents a jump backwards, update the frame's line - number and call the trace function. */ - if (line != frame->f_lineno || frame->f_lasti < instr_prev) { - if (line != -1) { - frame->f_lineno = line; - co_filename = PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_filename); - if (!co_filename) + if (line != -1) { + /* Trace backward edges or first instruction of a new line */ + if (frame->f_lasti < instr_prev || + (line != lastline && frame->f_lasti*2 == trace_info->bounds.ar_start)) + { + co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename); + if (!co_filename) { co_filename = "?"; - co_name = PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_name); - if (!co_name) + } + co_name = PyUnicode_AsUTF8(frame->f_code->co_name); + if (!co_name) { co_name = "?"; + } PyDTrace_LINE(co_filename, co_name, line); } } } - /* Implement Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() as functions for the limited API. */ diff --git a/Python/errors.c b/Python/errors.c index 118118f..1f84215 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1406,12 +1406,13 @@ _PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj) } if (exc_tb == NULL) { - PyFrameObject *frame = tstate->frame; + PyFrameObject *frame = PyThreadState_GetFrame(tstate); if (frame != NULL) { exc_tb = _PyTraceBack_FromFrame(NULL, frame); if (exc_tb == NULL) { _PyErr_Clear(tstate); } + Py_DECREF(frame); } } diff --git a/Python/frame.c b/Python/frame.c new file mode 100644 index 0000000..ae42843 --- /dev/null +++ b/Python/frame.c @@ -0,0 +1,135 @@ + +#include "Python.h" +#include "frameobject.h" +#include "pycore_frame.h" +#include "pycore_object.h" // _PyObject_GC_UNTRACK() + +int +_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg) +{ + Py_VISIT(frame->frame_obj); + Py_VISIT(frame->f_globals); + Py_VISIT(frame->f_builtins); + Py_VISIT(frame->f_locals); + Py_VISIT(frame->f_code); + /* locals */ + PyObject **locals = _PyFrame_GetLocalsArray(frame); + for (int i = 0; i < frame->nlocalsplus; i++) { + Py_VISIT(locals[i]); + } + /* stack */ + for (int i = 0; i <frame->stackdepth; i++) { + Py_VISIT(frame->stack[i]); + } + return 0; +} + +PyFrameObject * +_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame) +{ + assert(frame->frame_obj == NULL); + PyObject *error_type, *error_value, *error_traceback; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0); + if (f == NULL) { + Py_XDECREF(error_type); + Py_XDECREF(error_value); + Py_XDECREF(error_traceback); + } + else { + PyErr_Restore(error_type, error_value, error_traceback); + } + frame->frame_obj = f; + return f; +} + + +static InterpreterFrame * +copy_frame_to_heap(InterpreterFrame *frame) +{ + + Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); + PyObject **copy = PyMem_Malloc(size); + if (copy == NULL) { + PyErr_NoMemory(); + return NULL; + } + PyObject **locals = _PyFrame_GetLocalsArray(frame); + memcpy(copy, locals, size); + InterpreterFrame *res = (InterpreterFrame *)(copy + frame->nlocalsplus); + return res; +} + +static inline void +clear_specials(InterpreterFrame *frame) +{ + frame->generator = NULL; + Py_XDECREF(frame->frame_obj); + Py_XDECREF(frame->f_locals); + Py_DECREF(frame->f_globals); + Py_DECREF(frame->f_builtins); + Py_DECREF(frame->f_code); +} + +static void +take_ownership(PyFrameObject *f, InterpreterFrame *frame) +{ + assert(f->f_own_locals_memory == 0); + assert(frame->frame_obj == NULL); + + f->f_own_locals_memory = 1; + f->f_frame = frame; + assert(f->f_back == NULL); + if (frame->previous != NULL) { + /* Link PyFrameObjects.f_back and remove link through InterpreterFrame.previous */ + PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous); + if (back == NULL) { + /* Memory error here. */ + assert(PyErr_ExceptionMatches(PyExc_MemoryError)); + /* Nothing we can do about it */ + PyErr_Clear(); + _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL); + } + else { + f->f_back = (PyFrameObject *)Py_NewRef(back); + } + frame->previous = NULL; + } + if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { + _PyObject_GC_TRACK((PyObject *)f); + } +} + +int +_PyFrame_Clear(InterpreterFrame * frame, int take) +{ + PyObject **localsarray = ((PyObject **)frame)-frame->nlocalsplus; + if (frame->frame_obj) { + PyFrameObject *f = frame->frame_obj; + frame->frame_obj = NULL; + if (Py_REFCNT(f) > 1) { + if (!take) { + frame = copy_frame_to_heap(frame); + if (frame == NULL) { + return -1; + } + } + take_ownership(f, frame); + Py_DECREF(f); + return 0; + } + Py_DECREF(f); + } + for (int i = 0; i < frame->nlocalsplus; i++) { + Py_XDECREF(localsarray[i]); + } + assert(frame->stackdepth >= 0); + for (int i = 0; i < frame->stackdepth; i++) { + Py_DECREF(frame->stack[i]); + } + clear_specials(frame); + if (take) { + PyMem_Free(_PyFrame_GetLocalsArray(frame)); + } + return 0; +} diff --git a/Python/pystate.c b/Python/pystate.c index a94a615..6a15b54 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_ceval.h" +#include "pycore_frame.h" #include "pycore_initconfig.h" #include "pycore_object.h" // _PyType_InitCache() #include "pycore_pyerrors.h" @@ -685,7 +686,7 @@ new_threadstate(PyInterpreterState *interp, int init) PyMem_RawFree(tstate); return NULL; } - /* If top points to entry 0, then _PyThreadState_PopLocals will try to pop this chunk */ + /* If top points to entry 0, then _PyThreadState_PopFrame will try to pop this chunk */ tstate->datastack_top = &tstate->datastack_chunk->data[1]; tstate->datastack_limit = (PyObject **)(((char *)tstate->datastack_chunk) + DATA_STACK_CHUNK_SIZE); /* Mark trace_info as uninitialized */ @@ -872,7 +873,7 @@ PyThreadState_Clear(PyThreadState *tstate) "PyThreadState_Clear: warning: thread still has a frame\n"); } - /* Don't clear tstate->frame: it is a borrowed reference */ + /* Don't clear tstate->pyframe: it is a borrowed reference */ Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); @@ -1133,7 +1134,13 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - PyFrameObject *frame = tstate->frame; + if (tstate->frame == NULL) { + return NULL; + } + PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->frame); + if (frame == NULL) { + PyErr_Clear(); + } Py_XINCREF(frame); return frame; } @@ -1254,7 +1261,7 @@ _PyThread_CurrentFrames(void) for (i = runtime->interpreters.head; i != NULL; i = i->next) { PyThreadState *t; for (t = i->tstate_head; t != NULL; t = t->next) { - PyFrameObject *frame = t->frame; + InterpreterFrame *frame = t->frame; if (frame == NULL) { continue; } @@ -1262,7 +1269,7 @@ _PyThread_CurrentFrames(void) if (id == NULL) { goto fail; } - int stat = PyDict_SetItem(result, id, (PyObject *)frame); + int stat = PyDict_SetItem(result, id, (PyObject *)_PyFrame_GetFrameObject(frame)); Py_DECREF(id); if (stat < 0) { goto fail; @@ -2009,42 +2016,58 @@ _Py_GetConfig(void) #define MINIMUM_OVERHEAD 1000 -PyObject ** -_PyThreadState_PushLocals(PyThreadState *tstate, int size) +static PyObject ** +push_chunk(PyThreadState *tstate, int size) +{ + assert(tstate->datastack_top + size >= tstate->datastack_limit); + + int allocate_size = DATA_STACK_CHUNK_SIZE; + while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) { + allocate_size *= 2; + } + _PyStackChunk *new = allocate_chunk(allocate_size, tstate->datastack_chunk); + if (new == NULL) { + return NULL; + } + tstate->datastack_chunk->top = tstate->datastack_top - &tstate->datastack_chunk->data[0]; + tstate->datastack_chunk = new; + tstate->datastack_limit = (PyObject **)(((char *)new) + allocate_size); + PyObject **res = &new->data[0]; + tstate->datastack_top = res + size; + return res; +} + +InterpreterFrame * +_PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals) { - assert(((unsigned)size) < INT_MAX/sizeof(PyObject*)/2); - PyObject **res = tstate->datastack_top; - PyObject **top = res + size; + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int nlocalsplus = code->co_nlocalsplus; + size_t size = nlocalsplus + code->co_stacksize + + FRAME_SPECIALS_SIZE; + assert(size < INT_MAX/sizeof(PyObject *)); + PyObject **localsarray = tstate->datastack_top; + PyObject **top = localsarray + size; if (top >= tstate->datastack_limit) { - int allocate_size = DATA_STACK_CHUNK_SIZE; - while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) { - allocate_size *= 2; - } - _PyStackChunk *new = allocate_chunk(allocate_size, tstate->datastack_chunk); - if (new == NULL) { - goto error; + localsarray = push_chunk(tstate, (int)size); + if (localsarray == NULL) { + return NULL; } - tstate->datastack_chunk->top = tstate->datastack_top - &tstate->datastack_chunk->data[0]; - tstate->datastack_chunk = new; - tstate->datastack_limit = (PyObject **)(((char *)new) + allocate_size); - res = &new->data[0]; - tstate->datastack_top = res + size; } else { tstate->datastack_top = top; } - for (int i=0; i < size; i++) { - res[i] = NULL; + InterpreterFrame * frame = (InterpreterFrame *)(localsarray + nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals, nlocalsplus); + for (int i=0; i < nlocalsplus; i++) { + localsarray[i] = NULL; } - return res; -error: - _PyErr_SetString(tstate, PyExc_MemoryError, "Out of memory"); - return NULL; + return frame; } void -_PyThreadState_PopLocals(PyThreadState *tstate, PyObject **locals) +_PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame * frame) { + PyObject **locals = _PyFrame_GetLocalsArray(frame); if (locals == &tstate->datastack_chunk->data[0]) { _PyStackChunk *chunk = tstate->datastack_chunk; _PyStackChunk *previous = chunk->previous; diff --git a/Python/suggestions.c b/Python/suggestions.c index 77bdb6c..81976ff 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -232,7 +232,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(_PyFrame_GetGlobals(frame)); + dir = PySequence_List(frame->f_frame->f_globals); if (dir == NULL) { return NULL; } @@ -242,7 +242,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(_PyFrame_GetBuiltins(frame)); + dir = PySequence_List(frame->f_frame->f_builtins); if (dir == NULL) { return NULL; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f809e2f..5dfa917 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -29,6 +29,7 @@ Data members: #include "code.h" #include "frameobject.h" // PyFrame_GetBack() +#include "pycore_frame.h" #include "pydtrace.h" #include "osdefs.h" // DELIM #include "stdlib_module_names.h" // _Py_stdlib_module_names @@ -1814,25 +1815,22 @@ sys__getframe_impl(PyObject *module, int depth) /*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/ { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = PyThreadState_GetFrame(tstate); + InterpreterFrame *frame = tstate->frame; - if (_PySys_Audit(tstate, "sys._getframe", "O", f) < 0) { - Py_DECREF(f); + if (_PySys_Audit(tstate, "sys._getframe", NULL) < 0) { return NULL; } - while (depth > 0 && f != NULL) { - PyFrameObject *back = PyFrame_GetBack(f); - Py_DECREF(f); - f = back; + while (depth > 0 && frame != NULL) { + frame = frame->previous; --depth; } - if (f == NULL) { + if (frame == NULL) { _PyErr_SetString(tstate, PyExc_ValueError, "call stack is not deep enough"); return NULL; } - return (PyObject*)f; + return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(frame)); } /*[clinic input] diff --git a/Python/traceback.c b/Python/traceback.c index 6d23013..204121b 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -240,7 +240,7 @@ _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame) assert(tb_next == NULL || PyTraceBack_Check(tb_next)); assert(frame != NULL); - return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_lasti*2, + return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_frame->f_lasti*2, PyFrame_GetLineNumber(frame)); } @@ -521,7 +521,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, i * When displaying a new traceback line, for certain syntactical constructs * (e.g a subscript, an arithmetic operation) we try to create a representation * that separates the primary source of error from the rest. - * + * * Example specialization of BinOp nodes: * Traceback (most recent call last): * File "/home/isidentical/cpython/cpython/t.py", line 10, in <module> @@ -710,7 +710,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen } int code_offset = tb->tb_lasti; - PyCodeObject* code = _PyFrame_GetCode(frame); + PyCodeObject* code = frame->f_frame->f_code; int start_line; int end_line; @@ -1024,9 +1024,9 @@ _Py_DumpASCII(int fd, PyObject *text) This function is signal safe. */ static void -dump_frame(int fd, PyFrameObject *frame) +dump_frame(int fd, InterpreterFrame *frame) { - PyCodeObject *code = PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_code; PUTS(fd, " File "); if (code->co_filename != NULL && PyUnicode_Check(code->co_filename)) @@ -1038,7 +1038,7 @@ dump_frame(int fd, PyFrameObject *frame) PUTS(fd, "???"); } - int lineno = PyFrame_GetLineNumber(frame); + int lineno = PyCode_Addr2Line(code, frame->f_lasti*2); PUTS(fd, ", line "); if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); @@ -1057,20 +1057,19 @@ dump_frame(int fd, PyFrameObject *frame) } PUTS(fd, "\n"); - Py_DECREF(code); } static void dump_traceback(int fd, PyThreadState *tstate, int write_header) { - PyFrameObject *frame; + InterpreterFrame *frame; unsigned int depth; if (write_header) { PUTS(fd, "Stack (most recent call first):\n"); } - frame = PyThreadState_GetFrame(tstate); + frame = tstate->frame; if (frame == NULL) { PUTS(fd, "<no Python frame>\n"); return; @@ -1079,22 +1078,14 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) depth = 0; while (1) { if (MAX_FRAME_DEPTH <= depth) { - Py_DECREF(frame); PUTS(fd, " ...\n"); break; } - if (!PyFrame_Check(frame)) { - Py_DECREF(frame); - break; - } dump_frame(fd, frame); - PyFrameObject *back = PyFrame_GetBack(frame); - Py_DECREF(frame); - - if (back == NULL) { + frame = frame->previous; + if (frame == NULL) { break; } - frame = back; depth++; } } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 0198500..8b09563 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -854,11 +854,6 @@ class PyNoneStructPtr(PyObjectPtr): def proxyval(self, visited): return None -FRAME_SPECIALS_GLOBAL_OFFSET = 0 -FRAME_SPECIALS_BUILTINS_OFFSET = 1 -FRAME_SPECIALS_CODE_OFFSET = 3 -FRAME_SPECIALS_SIZE = 4 - class PyFrameObjectPtr(PyObjectPtr): _typename = 'PyFrameObject' @@ -866,16 +861,95 @@ class PyFrameObjectPtr(PyObjectPtr): PyObjectPtr.__init__(self, gdbval, cast_to) if not self.is_optimized_out(): + self._frame = PyFramePtr(self.field('f_frame')) + + def iter_locals(self): + ''' + Yield a sequence of (name,value) pairs of PyObjectPtr instances, for + the local variables of this frame + ''' + if self.is_optimized_out(): + return + return self._frame.iter_locals() + + def iter_globals(self): + ''' + Yield a sequence of (name,value) pairs of PyObjectPtr instances, for + the global variables of this frame + ''' + if self.is_optimized_out(): + return () + return self._frame.iter_globals() + + def iter_builtins(self): + ''' + Yield a sequence of (name,value) pairs of PyObjectPtr instances, for + the builtin variables + ''' + if self.is_optimized_out(): + return () + return self._frame.iter_builtins() + + def get_var_by_name(self, name): + + if self.is_optimized_out(): + return None, None + return self._frame.get_var_by_name(name) + + def filename(self): + '''Get the path of the current Python source file, as a string''' + if self.is_optimized_out(): + return FRAME_INFO_OPTIMIZED_OUT + return self._frame.filename() + + def current_line_num(self): + '''Get current line number as an integer (1-based) + + Translated from PyFrame_GetLineNumber and PyCode_Addr2Line + + See Objects/lnotab_notes.txt + ''' + if self.is_optimized_out(): + return None + return self._frame.current_line_num() + + def current_line(self): + '''Get the text of the current source line as a string, with a trailing + newline character''' + if self.is_optimized_out(): + return FRAME_INFO_OPTIMIZED_OUT + return self._frame.current_line() + + def write_repr(self, out, visited): + if self.is_optimized_out(): + out.write(FRAME_INFO_OPTIMIZED_OUT) + return + return self._frame.write_repr(out, visited) + + def print_traceback(self): + if self.is_optimized_out(): + sys.stdout.write(' %s\n' % FRAME_INFO_OPTIMIZED_OUT) + return + return self._frame.print_traceback() + +class PyFramePtr: + + def __init__(self, gdbval): + self._gdbval = gdbval + + if not self.is_optimized_out(): self.co = self._f_code() self.co_name = self.co.pyop_field('co_name') self.co_filename = self.co.pyop_field('co_filename') - self.f_lineno = int_from_int(self.field('f_lineno')) - self.f_lasti = int_from_int(self.field('f_lasti')) + self.f_lasti = self._f_lasti() self.co_nlocals = int_from_int(self.co.field('co_nlocals')) pnames = self.co.field('co_localsplusnames') self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames) + def is_optimized_out(self): + return self._gdbval.is_optimized_out + def iter_locals(self): ''' Yield a sequence of (name,value) pairs of PyObjectPtr instances, for @@ -884,26 +958,34 @@ class PyFrameObjectPtr(PyObjectPtr): if self.is_optimized_out(): return - f_localsplus = self.field('f_localsptr') + obj_ptr_ptr = gdb.lookup_type("PyObject").pointer().pointer() + base = self._gdbval.cast(obj_ptr_ptr) + localsplus = base - self._f_nlocalsplus() for i in safe_range(self.co_nlocals): - pyop_value = PyObjectPtr.from_pyobject_ptr(f_localsplus[i]) + pyop_value = PyObjectPtr.from_pyobject_ptr(localsplus[i]) if pyop_value.is_null(): continue pyop_name = PyObjectPtr.from_pyobject_ptr(self.co_localsplusnames[i]) yield (pyop_name, pyop_value) - def _f_specials(self, index, cls=PyObjectPtr): - f_valuestack = self.field('f_valuestack') - return cls.from_pyobject_ptr(f_valuestack[index - FRAME_SPECIALS_SIZE]) + def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr): + return convert(self._gdbval[name]) def _f_globals(self): - return self._f_specials(FRAME_SPECIALS_GLOBAL_OFFSET) + return self._f_special("f_globals") def _f_builtins(self): - return self._f_specials(FRAME_SPECIALS_BUILTINS_OFFSET) + return self._f_special("f_builtins") def _f_code(self): - return self._f_specials(FRAME_SPECIALS_CODE_OFFSET, PyCodeObjectPtr) + return self._f_special("f_code", PyCodeObjectPtr.from_pyobject_ptr) + + def _f_nlocalsplus(self): + return self._f_special("nlocalsplus", int_from_int) + + def _f_lasti(self): + return self._f_special("f_lasti", int_from_int) + def iter_globals(self): ''' @@ -960,11 +1042,6 @@ class PyFrameObjectPtr(PyObjectPtr): ''' if self.is_optimized_out(): return None - f_trace = self.field('f_trace') - if long(f_trace) != 0: - # we have a non-NULL f_trace: - return self.f_lineno - try: return self.co.addr2line(self.f_lasti*2) except Exception: @@ -1021,6 +1098,9 @@ class PyFrameObjectPtr(PyObjectPtr): out.write(')') + def as_address(self): + return long(self._gdbval) + def print_traceback(self): if self.is_optimized_out(): sys.stdout.write(' %s\n' % FRAME_INFO_OPTIMIZED_OUT) @@ -1033,6 +1113,21 @@ class PyFrameObjectPtr(PyObjectPtr): lineno, self.co_name.proxyval(visited))) + def get_truncated_repr(self, maxlen): + ''' + Get a repr-like string for the data, but truncate it at "maxlen" bytes + (ending the object graph traversal as soon as you do) + ''' + out = TruncatedStringIO(maxlen) + try: + self.write_repr(out, set()) + except StringTruncated: + # Truncation occurred: + return out.getvalue() + '...(truncated)' + + # No truncation occurred: + return out.getvalue() + class PySetObjectPtr(PyObjectPtr): _typename = 'PySetObject' @@ -1638,18 +1733,18 @@ class Frame(object): def get_pyop(self): try: - f = self._gdbframe.read_var('f') - frame = PyFrameObjectPtr.from_pyobject_ptr(f) + frame = self._gdbframe.read_var('frame') + frame = PyFramePtr(frame) if not frame.is_optimized_out(): return frame - # gdb is unable to get the "f" argument of PyEval_EvalFrameEx() - # because it was "optimized out". Try to get "f" from the frame - # of the caller, PyEval_EvalCodeEx(). + # gdb is unable to get the "frame" argument of PyEval_EvalFrameEx() + # because it was "optimized out". Try to get "frame" from the frame + # of the caller, _PyEval_Vector(). orig_frame = frame caller = self._gdbframe.older() if caller: - f = caller.read_var('f') - frame = PyFrameObjectPtr.from_pyobject_ptr(f) + frame = caller.read_var('frame') + frame = PyFramePtr(frame) if not frame.is_optimized_out(): return frame return orig_frame |