From 79aeaf6b33d719398960065b54cdd1582169abec Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 21 Jun 2021 18:50:19 +0100 Subject: [PATCH 01/40] Turn specials array into struct and add 'lasti' to it. --- Include/cpython/frameobject.h | 3 +- Include/internal/pycore_frame.h | 41 +++-------- Objects/frameobject.c | 114 ++++++++++++++--------------- Objects/genobject.c | 19 ++--- Objects/typeobject.c | 3 +- Python/_warnings.c | 6 +- Python/ceval.c | 123 ++++++++++++++++---------------- Python/suggestions.c | 4 +- Python/traceback.c | 3 +- Tools/gdb/libpython.py | 23 +++--- 10 files changed, 157 insertions(+), 182 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 2bf458cab35451..a015535e3bbba2 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -22,12 +22,11 @@ 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 _py_frame_specials *f_specials; /* points to the specials */ 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? */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index e30e3c89bfb62b..3bd1ced1d9dde4 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -4,39 +4,16 @@ 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 -}; +typedef struct _py_frame_specials { + PyObject *globals; + PyObject *builtins; + PyObject *locals; + PyCodeObject *code; + int lasti; /* Last instruction if called */ + PyObject *stack[1]; +} _PyFrameSpecials; -static inline PyObject ** -_PyFrame_Specials(PyFrameObject *f) { - return &f->f_valuestack[-FRAME_SPECIALS_SIZE]; -} - -/* Returns a *borrowed* reference. */ -static inline PyObject * -_PyFrame_GetGlobals(PyFrameObject *f) -{ - return _PyFrame_Specials(f)[FRAME_SPECIALS_GLOBALS_OFFSET]; -} - -/* Returns a *borrowed* reference. */ -static inline PyObject * -_PyFrame_GetBuiltins(PyFrameObject *f) -{ - return _PyFrame_Specials(f)[FRAME_SPECIALS_BUILTINS_OFFSET]; -} - -/* Returns a *borrowed* reference. */ -static inline PyCodeObject * -_PyFrame_GetCode(PyFrameObject *f) -{ - return (PyCodeObject *)_PyFrame_Specials(f)[FRAME_SPECIALS_CODE_OFFSET]; -} +#define FRAME_SPECIALS_SIZE ((sizeof(_PyFrameSpecials)-1)/sizeof(PyObject *)) int _PyFrame_TakeLocals(PyFrameObject *f); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index f9090d8cb14d27..a5a9de3b2bbf91 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -33,7 +33,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_specials->locals; Py_INCREF(locals); return locals; } @@ -46,7 +46,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_specials->code, f->f_specials->lasti*2); } } @@ -65,16 +65,16 @@ frame_getlineno(PyFrameObject *f, void *closure) static PyObject * frame_getlasti(PyFrameObject *f, void *closure) { - if (f->f_lasti < 0) { + if (f->f_specials->lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(f->f_lasti*2); + return PyLong_FromLong(f->f_specials->lasti*2); } static PyObject * frame_getglobals(PyFrameObject *f, void *closure) { - PyObject *globals = _PyFrame_GetGlobals(f); + PyObject *globals = f->f_specials->globals; if (globals == NULL) { globals = Py_None; } @@ -85,7 +85,7 @@ frame_getglobals(PyFrameObject *f, void *closure) static PyObject * frame_getbuiltins(PyFrameObject *f, void *closure) { - PyObject *builtins = _PyFrame_GetBuiltins(f); + PyObject *builtins = f->f_specials->builtins; if (builtins == NULL) { builtins = Py_None; } @@ -390,7 +390,7 @@ frame_stack_pop(PyFrameObject *f) { assert(f->f_stackdepth > 0); f->f_stackdepth--; - PyObject *v = f->f_valuestack[f->f_stackdepth]; + PyObject *v = f->f_specials->stack[f->f_stackdepth]; Py_DECREF(v); } @@ -472,7 +472,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_specials->code->co_firstlineno) { PyErr_Format(PyExc_ValueError, "line %d comes before the current code block", new_lineno); @@ -481,8 +481,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_specials->code->co_code) / sizeof(_Py_CODEUNIT)); + int *lines = marklines(f->f_specials->code, len); if (lines == NULL) { return -1; } @@ -496,7 +496,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_specials->code, len); if (stacks == NULL) { PyMem_Free(lines); return -1; @@ -504,7 +504,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_specials->lasti]; int err = -1; const char *msg = "cannot find bytecode for specified line"; for (int i = 0; i < len; i++) { @@ -546,9 +546,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_specials->lasti = best_addr; return 0; } @@ -615,17 +615,16 @@ frame_dealloc(PyFrameObject *f) /* Kill all local variables including specials. */ if (f->f_localsptr) { /* 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 = f->f_specials->code; + Py_CLEAR(f->f_specials->globals); + Py_CLEAR(f->f_specials->builtins); + Py_CLEAR(f->f_specials->locals); 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_XDECREF(f->f_specials->stack[i]); } if (f->f_own_locals_memory) { PyMem_Free(f->f_localsptr); @@ -654,27 +653,24 @@ 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); + Py_VISIT(f->f_specials->globals); + Py_VISIT(f->f_specials->builtins); + Py_VISIT(f->f_specials->locals); + Py_VISIT(f->f_specials->code); /* locals */ - PyObject **localsplus = f->f_localsptr; - for (Py_ssize_t i = frame_nslots(f); --i >= 0; ++localsplus) { - Py_VISIT(*localsplus); + PyCodeObject *co = f->f_specials->code; + for (int i = 0; i < co->co_nlocalsplus; i++) { + Py_VISIT(f->f_localsptr[i]); } - /* stack */ for (int i = 0; i < f->f_stackdepth; i++) { - Py_VISIT(f->f_valuestack[i]); + Py_VISIT(f->f_specials->stack[i]); } return 0; } @@ -690,7 +686,7 @@ frame_tp_clear(PyFrameObject *f) f->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); - PyCodeObject *co = _PyFrame_GetCode(f); + PyCodeObject *co = f->f_specials->code; /* locals */ for (int i = 0; i < co->co_nlocalsplus; i++) { Py_CLEAR(f->f_localsptr[i]); @@ -698,7 +694,7 @@ frame_tp_clear(PyFrameObject *f) /* stack */ for (int i = 0; i < f->f_stackdepth; i++) { - Py_CLEAR(f->f_valuestack[i]); + Py_CLEAR(f->f_specials->stack[i]); } f->f_stackdepth = 0; return 0; @@ -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_specials->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_specials->code; return PyUnicode_FromFormat( "", f, code->co_filename, lineno, code->co_name); @@ -845,23 +841,24 @@ _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); + Py_ssize_t size = ((char*)f->f_specials->stack)-((char *)f->f_localsptr); + PyObject **copy = PyMem_Malloc(size); if (copy == NULL) { - for (int i = 0; i < size; i++) { + for (int i = 0; i < f->f_specials->code->co_nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; Py_XDECREF(o); } + Py_XDECREF(f->f_specials->builtins); + Py_XDECREF(f->f_specials->globals); + Py_XDECREF(f->f_specials->locals); + Py_XDECREF(f->f_specials->code); PyErr_NoMemory(); return -1; } - for (int i = 0; i < size; i++) { - PyObject *o = f->f_localsptr[i]; - copy[i] = o; - } + memcpy(copy, f->f_localsptr, size); f->f_own_locals_memory = 1; f->f_localsptr = copy; - f->f_valuestack = copy + size; + f->f_specials = (_PyFrameSpecials *)(copy + f->f_specials->code->co_nlocalsplus); return 0; } @@ -879,20 +876,19 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *l if (f == NULL) { return NULL; } - - PyObject **specials = f->f_localsptr + code->co_nlocalsplus; - f->f_valuestack = specials + FRAME_SPECIALS_SIZE; + _PyFrameSpecials *specials = (_PyFrameSpecials *)(f->f_localsptr + code->co_nlocalsplus); + f->f_specials = specials; 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); + specials->code = (PyCodeObject *)Py_NewRef(con->fc_code); + specials->builtins = Py_NewRef(con->fc_builtins); + specials->globals = Py_NewRef(con->fc_globals); + specials->locals = Py_XNewRef(locals); + specials->lasti = -1; 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; @@ -928,8 +924,8 @@ static int _PyFrame_OpAlreadyRan(PyFrameObject *f, 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(f->f_specials->code->co_code); + for (int i = 0; i < f->f_specials->lasti; i++) { if (_Py_OPCODE(code[i]) == opcode && _Py_OPARG(code[i]) == oparg) { return 1; } @@ -949,13 +945,13 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyErr_BadInternalCall(); return -1; } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = f->f_specials->locals; if (locals == NULL) { - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET] = PyDict_New(); + locals = f->f_specials->locals = PyDict_New(); if (locals == NULL) return -1; } - co = _PyFrame_GetCode(f); + co = f->f_specials->code; fast = f->f_localsptr; for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocalsPlusKind kind = co->co_localspluskinds[i]; @@ -1044,11 +1040,11 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) if (f == NULL || f->f_state == FRAME_CLEARED) { return; } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = f->f_specials->locals; if (locals == NULL) return; fast = f->f_localsptr; - co = _PyFrame_GetCode(f); + co = f->f_specials->code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1141,7 +1137,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); - PyCodeObject *code = _PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_specials->code; assert(code != NULL); Py_INCREF(code); return code; diff --git a/Objects/genobject.c b/Objects/genobject.c index db00d19a3464e0..05293068ac2e14 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" @@ -79,7 +80,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_frame->f_specials->lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -176,11 +177,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, } assert(_PyFrame_IsRunnable(f)); - assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + assert(f->f_specials->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_specials->stack[gen->gi_frame->f_stackdepth] = result; gen->gi_frame->f_stackdepth++; /* Generators always return to their most recent caller, not @@ -334,7 +335,7 @@ _PyGen_yf(PyGenObject *gen) PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (f->f_lasti < 0) { + if (f->f_specials->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 +343,10 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(f->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(f->f_specials->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; assert(f->f_stackdepth > 0); - yf = f->f_valuestack[f->f_stackdepth-1]; + yf = f->f_specials->stack[f->f_stackdepth-1]; Py_INCREF(yf); } @@ -461,12 +462,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, /* 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]; + ret = gen->gi_frame->f_specials->stack[gen->gi_frame->f_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_frame->f_specials->lasti >= 0); + gen->gi_frame->f_specials->lasti += 1; if (_PyGen_FetchStopIterationValue(&val) == 0) { ret = gen_send(gen, val); Py_DECREF(val); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fbe3d165a60971..47349dfa9dcb02 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(), _Py_union_type_or #include "frameobject.h" +#include "pycore_frame.h" #include "opcode.h" // MAKE_CELL #include "structmember.h" // PyMemberDef @@ -8867,7 +8868,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, if (firstarg != NULL && (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_specials->lasti >= 0) { assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); diff --git a/Python/_warnings.c b/Python/_warnings.c index 9c8815c1a3e204..f64e71ba28ef95 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_specials->globals; + *filename = f->f_specials->code->co_filename; Py_INCREF(*filename); *lineno = PyFrame_GetLineNumber(f); Py_DECREF(f); diff --git a/Python/ceval.c b/Python/ceval.c index b5e3dd53c8439a..1789137d61a539 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1244,7 +1244,7 @@ eval_frame_handle_pending(PyThreadState *tstate) if (cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \ goto tracing_dispatch; \ } \ - f->f_lasti = INSTR_OFFSET(); \ + specials->lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ DISPATCH_GOTO(); \ } @@ -1333,7 +1333,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 - specials->stack)) #define EMPTY() (STACK_LEVEL() == 0) #define TOP() (stack_pointer[-1]) #define SECOND() (stack_pointer[-2]) @@ -1399,9 +1399,9 @@ eval_frame_handle_pending(PyThreadState *tstate) #define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; } -#define GLOBALS() specials[FRAME_SPECIALS_GLOBALS_OFFSET] -#define BUILTINS() specials[FRAME_SPECIALS_BUILTINS_OFFSET] -#define LOCALS() specials[FRAME_SPECIALS_LOCALS_OFFSET] +#define GLOBALS() specials->globals +#define BUILTINS() specials->builtins +#define LOCALS() specials->locals PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) @@ -1420,7 +1420,8 @@ _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; + _PyFrameSpecials *specials; PyObject *retval = NULL; /* Return value */ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker; PyCodeObject *co; @@ -1450,8 +1451,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) /* push frame */ tstate->frame = f; - specials = f->f_valuestack - FRAME_SPECIALS_SIZE; - co = (PyCodeObject *)specials[FRAME_SPECIALS_CODE_OFFSET]; + specials = f->f_specials; + co = specials->code; if (cframe.use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1509,23 +1510,23 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) localsplus = f->f_localsptr; first_instr = co->co_firstinstr; /* - f->f_lasti refers to the index of the last instruction, + specials->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 specials->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 specials->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,specials->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 specials->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; + assert(specials->lasti >= -1); + next_instr = first_instr + specials->lasti + 1; + stack_pointer = specials->stack + f->f_stackdepth; /* Set f->f_stackdepth to -1. * Update when returning or calling trace function. Having f_stackdepth <= 0 ensures that invalid @@ -1557,7 +1558,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)); @@ -1597,8 +1598,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tracing_dispatch: { - int instr_prev = f->f_lasti; - f->f_lasti = INSTR_OFFSET(); + int instr_prev = specials->lasti; + specials->lasti = INSTR_OFFSET(); TRACING_NEXTOPARG(); if (PyDTrace_LINE_ENABLED()) @@ -1611,7 +1612,7 @@ _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); + f->f_stackdepth = (int)(stack_pointer - specials->stack); err = maybe_call_line_trace(tstate->c_tracefunc, tstate->c_traceobj, @@ -1621,8 +1622,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) goto error; } /* Reload possibly changed frame fields */ - JUMPTO(f->f_lasti); - stack_pointer = f->f_valuestack+f->f_stackdepth; + JUMPTO(specials->lasti); + stack_pointer = specials->stack+f->f_stackdepth; f->f_stackdepth = -1; NEXTOPARG(); } @@ -1634,11 +1635,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); + specials->lasti, opcode, oparg); } else { printf("%d: %d\n", - f->f_lasti, opcode); + specials->lasti, opcode); } } #endif @@ -2432,10 +2433,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; + assert(specials->lasti > 0); + specials->lasti -= 1; f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - f->f_valuestack); + f->f_stackdepth = (int)(stack_pointer - specials->stack); goto exiting; } @@ -2452,7 +2453,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) retval = w; } f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - f->f_valuestack); + f->f_stackdepth = (int)(stack_pointer - specials->stack); goto exiting; } @@ -2499,7 +2500,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); + specials->lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -2530,7 +2531,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); + specials->lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -4322,7 +4323,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) exception_unwind: f->f_state = FRAME_UNWINDING; - /* We can't use f->f_lasti here, as RERAISE may have set it */ + /* We can't use specials->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) { @@ -4337,7 +4338,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) } PyObject *exc, *val, *tb; if (lasti) { - PyObject *lasti = PyLong_FromLong(f->f_lasti); + PyObject *lasti = PyLong_FromLong(specials->lasti); if (lasti == NULL) { goto exception_unwind; } @@ -4363,7 +4364,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) JUMPTO(handler); /* Resume normal execution */ f->f_state = FRAME_EXECUTING; - f->f_lasti = handler; + specials->lasti = handler; NEXTOPARG(); goto dispatch_opcode; } /* main loop */ @@ -5014,9 +5015,14 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, else { ++tstate->recursion_depth; f->f_localsptr = NULL; - for (int i = 0; i < code->co_nlocalsplus + FRAME_SPECIALS_SIZE; i++) { + for (int i = 0; i < code->co_nlocalsplus; i++) { Py_XDECREF(localsarray[i]); } + _PyFrameSpecials *specials = f->f_specials; + Py_XDECREF(specials->locals); + Py_DECREF(specials->globals); + Py_DECREF(specials->builtins); + Py_DECREF(specials->code); Py_DECREF(f); --tstate->recursion_depth; } @@ -5390,7 +5396,7 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, static void initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame) { - PyCodeObject *code = _PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_specials->code; if (trace_info->code != code) { trace_info->code = code; _PyCode_InitAddressRange(code, &trace_info->bounds); @@ -5407,12 +5413,12 @@ call_trace(Py_tracefunc func, PyObject *obj, return 0; tstate->tracing++; tstate->cframe->use_tracing = 0; - if (frame->f_lasti < 0) { - frame->f_lineno = _PyFrame_GetCode(frame)->co_firstlineno; + if (frame->f_specials->lasti < 0) { + frame->f_lineno = frame->f_specials->code->co_firstlineno; } else { initialize_trace_info(&tstate->trace_info, frame); - frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); + frame->f_lineno = _PyCode_CheckLineNumber(frame->f_specials->lasti*2, &tstate->trace_info.bounds); } result = func(obj, frame, what, arg); frame->f_lineno = 0; @@ -5452,11 +5458,11 @@ 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); + int line = _PyCode_CheckLineNumber(frame->f_specials->lasti*2, &tstate->trace_info.bounds); if (line != -1 && frame->f_trace_lines) { /* Trace backward edges or first instruction of a new line */ - if (frame->f_lasti < instr_prev || - (line != lastline && frame->f_lasti*2 == tstate->trace_info.bounds.ar_start)) + if (frame->f_specials->lasti < instr_prev || + (line != lastline && frame->f_specials->lasti*2 == tstate->trace_info.bounds.ar_start)) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } @@ -5621,7 +5627,7 @@ _PyEval_GetBuiltins(PyThreadState *tstate) { PyFrameObject *frame = tstate->frame; if (frame != NULL) { - return _PyFrame_GetBuiltins(frame); + return frame->f_specials->builtins; } return tstate->interp->builtins; } @@ -5662,8 +5668,7 @@ PyEval_GetLocals(void) return NULL; } - PyObject *locals = current_frame->f_valuestack[ - FRAME_SPECIALS_LOCALS_OFFSET-FRAME_SPECIALS_SIZE]; + PyObject *locals = current_frame->f_specials->locals; assert(locals != NULL); return locals; } @@ -5676,7 +5681,7 @@ PyEval_GetGlobals(void) if (current_frame == NULL) { return NULL; } - return _PyFrame_GetGlobals(current_frame); + return current_frame->f_specials->globals; } int @@ -5687,7 +5692,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) 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_specials->code->co_flags; const int compilerflags = codeflags & PyCF_MASK; if (compilerflags) { result = 1; @@ -5928,15 +5933,14 @@ import_name(PyThreadState *tstate, PyFrameObject *f, PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemIdWithError(_PyFrame_GetBuiltins(f), &PyId___import__); + import_func = _PyDict_GetItemIdWithError(f->f_specials->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 = f->f_specials->locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { int ilevel = _PyLong_AsInt(level); @@ -5945,7 +5949,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, } res = PyImport_ImportModuleLevelObject( name, - _PyFrame_GetGlobals(f), + f->f_specials->globals, locals == NULL ? Py_None :locals, fromlist, ilevel); @@ -5955,7 +5959,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, Py_INCREF(import_func); stack[0] = name; - stack[1] = _PyFrame_GetGlobals(f); + stack[1] = f->f_specials->globals; stack[2] = locals == NULL ? Py_None : locals; stack[3] = fromlist; stack[4] = level; @@ -6292,10 +6296,9 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, } case STORE_NAME: { - PyObject *names = _PyFrame_GetCode(f)->co_names; + PyObject *names = f->f_specials->code->co_names; PyObject *name = GETITEM(names, oparg); - PyObject *locals = f->f_valuestack[ - FRAME_SPECIALS_LOCALS_OFFSET-FRAME_SPECIALS_SIZE]; + PyObject *locals = f->f_specials->locals; if (locals && PyDict_CheckExact(locals)) { PyObject *w = PyDict_GetItemWithError(locals, name); if ((w == v && PyDict_DelItem(locals, name) != 0) || @@ -6379,7 +6382,7 @@ dtrace_function_entry(PyFrameObject *f) const char *funcname; int lineno; - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_specials->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); lineno = PyFrame_GetLineNumber(f); @@ -6394,7 +6397,7 @@ dtrace_function_return(PyFrameObject *f) const char *funcname; int lineno; - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_specials->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); lineno = PyFrame_GetLineNumber(f); @@ -6414,17 +6417,17 @@ maybe_dtrace_line(PyFrameObject *frame, instruction window, reset the window. */ initialize_trace_info(trace_info, frame); - int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &trace_info->bounds); + int line = _PyCode_CheckLineNumber(frame->f_specials->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 != frame->f_lineno || frame->f_specials->lasti < instr_prev) { if (line != -1) { frame->f_lineno = line; - co_filename = PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_filename); + co_filename = PyUnicode_AsUTF8(frame->f_specials->code->co_filename); if (!co_filename) co_filename = "?"; - co_name = PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_name); + co_name = PyUnicode_AsUTF8(frame->f_specials->code->co_name); if (!co_name) co_name = "?"; PyDTrace_LINE(co_filename, co_name, line); diff --git a/Python/suggestions.c b/Python/suggestions.c index 3dfcbfe3ab0c37..069294150e630a 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -229,7 +229,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(_PyFrame_GetGlobals(frame)); + dir = PySequence_List(frame->f_specials->globals); if (dir == NULL) { return NULL; } @@ -239,7 +239,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(_PyFrame_GetBuiltins(frame)); + dir = PySequence_List(frame->f_specials->builtins); if (dir == NULL) { return NULL; } diff --git a/Python/traceback.c b/Python/traceback.c index f7dc5ad6864762..d6f9f60782765b 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -6,6 +6,7 @@ #include "code.h" #include "pycore_interp.h" // PyInterpreterState.gc #include "frameobject.h" // PyFrame_GetBack() +#include "pycore_frame.h" #include "structmember.h" // PyMemberDef #include "osdefs.h" // SEP #ifdef HAVE_FCNTL_H @@ -234,7 +235,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_specials->lasti*2, PyFrame_GetLineNumber(frame)); } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 0198500265be41..0a146f067adb9f 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' @@ -871,7 +866,7 @@ def __init__(self, gdbval, cast_to=None): 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) @@ -892,18 +887,22 @@ def iter_locals(self): 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): + f_specials = self.field('f_specials') + return convert(f_specials[name]) def _f_globals(self): - return self._f_specials(FRAME_SPECIALS_GLOBAL_OFFSET) + return self._f_special("globals") def _f_builtins(self): - return self._f_specials(FRAME_SPECIALS_BUILTINS_OFFSET) + return self._f_special("builtins") def _f_code(self): - return self._f_specials(FRAME_SPECIALS_CODE_OFFSET, PyCodeObjectPtr) + return self._f_special("code", PyCodeObjectPtr.from_pyobject_ptr) + + def _f_lasti(self): + return self._f_special("lasti", int_from_int) + def iter_globals(self): ''' From 3b6a4e8b91f97b96316e5cd76ab5a9bcde4fc557 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Jun 2021 12:03:36 +0100 Subject: [PATCH 02/40] Move stack-depth from frame object to specials struct. --- Include/cpython/frameobject.h | 1 - Include/internal/pycore_frame.h | 3 ++- Lib/test/test_sys.py | 2 +- Objects/frameobject.c | 20 ++++++++++---------- Objects/genobject.c | 14 +++++++------- Python/ceval.c | 22 +++++++++++----------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index a015535e3bbba2..fd302021912f29 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -26,7 +26,6 @@ struct _frame { PyObject *f_trace; /* Trace function */ /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; - int f_stackdepth; /* Depth of value stack */ 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? */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 3bd1ced1d9dde4..aa99aeff0f13b9 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -9,7 +9,8 @@ typedef struct _py_frame_specials { PyObject *builtins; PyObject *locals; PyCodeObject *code; - int lasti; /* Last instruction if called */ + int lasti; /* Last instruction if called */ + int stackdepth; /* Depth of value stack */ PyObject *stack[1]; } _PyFrameSpecials; diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a549d44c5210fc..19f85d21ba5893 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1275,7 +1275,7 @@ class C(object): pass # frame import inspect x = inspect.currentframe() - check(x, size('4P3i4cP')) + check(x, size('4Pi4cP')) # function def func(): pass check(func, size('14P')) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a5a9de3b2bbf91..7398b5af52fdc9 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -388,9 +388,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_specials->stack[f->f_stackdepth]; + assert(f->f_specials->stackdepth > 0); + f->f_specials->stackdepth--; + PyObject *v = f->f_specials->stack[f->f_specials->stackdepth]; Py_DECREF(v); } @@ -623,7 +623,7 @@ frame_dealloc(PyFrameObject *f) Py_CLEAR(f->f_localsptr[i]); } /* Free items on stack */ - for (int i = 0; i < f->f_stackdepth; i++) { + for (int i = 0; i < f->f_specials->stackdepth; i++) { Py_XDECREF(f->f_specials->stack[i]); } if (f->f_own_locals_memory) { @@ -632,7 +632,7 @@ frame_dealloc(PyFrameObject *f) } f->f_localsptr = NULL; } - f->f_stackdepth = 0; + f->f_specials->stackdepth = 0; Py_XDECREF(f->f_back); Py_CLEAR(f->f_trace); struct _Py_frame_state *state = get_frame_state(); @@ -669,7 +669,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) Py_VISIT(f->f_localsptr[i]); } /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { + for (int i = 0; i < f->f_specials->stackdepth; i++) { Py_VISIT(f->f_specials->stack[i]); } return 0; @@ -693,10 +693,10 @@ frame_tp_clear(PyFrameObject *f) } /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { + for (int i = 0; i < f->f_specials->stackdepth; i++) { Py_CLEAR(f->f_specials->stack[i]); } - f->f_stackdepth = 0; + f->f_specials->stackdepth = 0; return 0; } @@ -840,7 +840,7 @@ int _PyFrame_TakeLocals(PyFrameObject *f) { assert(f->f_own_locals_memory == 0); - assert(f->f_stackdepth == 0); + assert(f->f_specials->stackdepth == 0); Py_ssize_t size = ((char*)f->f_specials->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); if (copy == NULL) { @@ -885,7 +885,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *l specials->locals = Py_XNewRef(locals); specials->lasti = -1; f->f_trace = NULL; - f->f_stackdepth = 0; + f->f_specials->stackdepth = 0; f->f_trace_lines = 1; f->f_trace_opcodes = 0; f->f_gen = NULL; diff --git a/Objects/genobject.c b/Objects/genobject.c index 05293068ac2e14..0802e047df9d14 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -181,8 +181,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); - gen->gi_frame->f_specials->stack[gen->gi_frame->f_stackdepth] = result; - gen->gi_frame->f_stackdepth++; + gen->gi_frame->f_specials->stack[gen->gi_frame->f_specials->stackdepth] = result; + gen->gi_frame->f_specials->stackdepth++; /* Generators always return to their most recent caller, not * necessarily their creator. */ @@ -345,8 +345,8 @@ _PyGen_yf(PyGenObject *gen) if (code[(f->f_specials->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - assert(f->f_stackdepth > 0); - yf = f->f_specials->stack[f->f_stackdepth-1]; + assert(f->f_specials->stackdepth > 0); + yf = f->f_specials->stack[f->f_specials->stackdepth-1]; Py_INCREF(yf); } @@ -460,9 +460,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, 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_specials->stack[gen->gi_frame->f_stackdepth]; + assert(gen->gi_frame->f_specials->stackdepth > 0); + gen->gi_frame->f_specials->stackdepth--; + ret = gen->gi_frame->f_specials->stack[gen->gi_frame->f_specials->stackdepth]; assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ diff --git a/Python/ceval.c b/Python/ceval.c index 1789137d61a539..51223a1fa44464 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1526,14 +1526,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) */ assert(specials->lasti >= -1); next_instr = first_instr + specials->lasti + 1; - stack_pointer = specials->stack + f->f_stackdepth; - /* Set f->f_stackdepth to -1. + stack_pointer = specials->stack + specials->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; + specials->stackdepth = -1; f->f_state = FRAME_EXECUTING; #ifdef LLTRACE @@ -1612,7 +1612,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) int err; /* see maybe_call_line_trace() for expository comments */ - f->f_stackdepth = (int)(stack_pointer - specials->stack); + specials->stackdepth = (int)(stack_pointer - specials->stack); err = maybe_call_line_trace(tstate->c_tracefunc, tstate->c_traceobj, @@ -1623,8 +1623,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } /* Reload possibly changed frame fields */ JUMPTO(specials->lasti); - stack_pointer = specials->stack+f->f_stackdepth; - f->f_stackdepth = -1; + stack_pointer = specials->stack+specials->stackdepth; + specials->stackdepth = -1; NEXTOPARG(); } } @@ -2248,7 +2248,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) retval = POP(); assert(EMPTY()); f->f_state = FRAME_RETURNED; - f->f_stackdepth = 0; + specials->stackdepth = 0; goto exiting; } @@ -2436,7 +2436,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) assert(specials->lasti > 0); specials->lasti -= 1; f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - specials->stack); + specials->stackdepth = (int)(stack_pointer - specials->stack); goto exiting; } @@ -2453,7 +2453,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) retval = w; } f->f_state = FRAME_SUSPENDED; - f->f_stackdepth = (int)(stack_pointer - specials->stack); + specials->stackdepth = (int)(stack_pointer - specials->stack); goto exiting; } @@ -4377,7 +4377,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) PyObject *o = POP(); Py_XDECREF(o); } - f->f_stackdepth = 0; + specials->stackdepth = 0; f->f_state = FRAME_RAISED; exiting: if (cframe.use_tracing) { @@ -4996,7 +4996,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, return make_coro(con, f); } PyObject *retval = _PyEval_EvalFrame(tstate, f, 0); - assert(f->f_stackdepth == 0); + assert(f->f_specials->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 From 6f0ba40eef60cb267af485acc60c54be6855cf28 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Jun 2021 12:27:03 +0100 Subject: [PATCH 03/40] Rename 'specials' to 'frame' as it now includes most of the data for a frame, except for the locals. --- Include/cpython/frameobject.h | 2 +- Include/internal/pycore_frame.h | 6 +- Objects/frameobject.c | 118 ++++++++++++++--------------- Objects/genobject.c | 26 +++---- Objects/typeobject.c | 2 +- Python/_warnings.c | 4 +- Python/ceval.c | 130 ++++++++++++++++---------------- Python/suggestions.c | 4 +- Python/traceback.c | 2 +- Tools/gdb/libpython.py | 4 +- 10 files changed, 149 insertions(+), 149 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index fd302021912f29..1fb64348f1c18b 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -22,7 +22,7 @@ typedef signed char PyFrameState; struct _frame { PyObject_HEAD struct _frame *f_back; /* previous frame, or NULL */ - struct _py_frame_specials *f_specials; /* points to the specials */ + struct _py_frame *f_frame; /* points to the frame data */ PyObject *f_trace; /* Trace function */ /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index aa99aeff0f13b9..9ee3d362880fc1 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -4,7 +4,7 @@ extern "C" { #endif -typedef struct _py_frame_specials { +typedef struct _py_frame { PyObject *globals; PyObject *builtins; PyObject *locals; @@ -12,9 +12,9 @@ typedef struct _py_frame_specials { int lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ PyObject *stack[1]; -} _PyFrameSpecials; +} _PyFrame; -#define FRAME_SPECIALS_SIZE ((sizeof(_PyFrameSpecials)-1)/sizeof(PyObject *)) +#define FRAME_SPECIALS_SIZE ((sizeof(_PyFrame)-1)/sizeof(PyObject *)) int _PyFrame_TakeLocals(PyFrameObject *f); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 7398b5af52fdc9..9b480cc66e0fb7 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -33,7 +33,7 @@ frame_getlocals(PyFrameObject *f, void *closure) { if (PyFrame_FastToLocalsWithError(f) < 0) return NULL; - PyObject *locals = f->f_specials->locals; + PyObject *locals = f->f_frame->locals; Py_INCREF(locals); return locals; } @@ -46,7 +46,7 @@ PyFrame_GetLineNumber(PyFrameObject *f) return f->f_lineno; } else { - return PyCode_Addr2Line(f->f_specials->code, f->f_specials->lasti*2); + return PyCode_Addr2Line(f->f_frame->code, f->f_frame->lasti*2); } } @@ -65,16 +65,16 @@ frame_getlineno(PyFrameObject *f, void *closure) static PyObject * frame_getlasti(PyFrameObject *f, void *closure) { - if (f->f_specials->lasti < 0) { + if (f->f_frame->lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(f->f_specials->lasti*2); + return PyLong_FromLong(f->f_frame->lasti*2); } static PyObject * frame_getglobals(PyFrameObject *f, void *closure) { - PyObject *globals = f->f_specials->globals; + PyObject *globals = f->f_frame->globals; if (globals == NULL) { globals = Py_None; } @@ -85,7 +85,7 @@ frame_getglobals(PyFrameObject *f, void *closure) static PyObject * frame_getbuiltins(PyFrameObject *f, void *closure) { - PyObject *builtins = f->f_specials->builtins; + PyObject *builtins = f->f_frame->builtins; if (builtins == NULL) { builtins = Py_None; } @@ -388,9 +388,9 @@ first_line_not_before(int *lines, int len, int line) static void frame_stack_pop(PyFrameObject *f) { - assert(f->f_specials->stackdepth > 0); - f->f_specials->stackdepth--; - PyObject *v = f->f_specials->stack[f->f_specials->stackdepth]; + assert(f->f_frame->stackdepth > 0); + f->f_frame->stackdepth--; + PyObject *v = f->f_frame->stack[f->f_frame->stackdepth]; Py_DECREF(v); } @@ -472,7 +472,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } new_lineno = (int)l_new_lineno; - if (new_lineno < f->f_specials->code->co_firstlineno) { + if (new_lineno < f->f_frame->code->co_firstlineno) { PyErr_Format(PyExc_ValueError, "line %d comes before the current code block", new_lineno); @@ -481,8 +481,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(f->f_specials->code->co_code) / sizeof(_Py_CODEUNIT)); - int *lines = marklines(f->f_specials->code, len); + int len = (int)(PyBytes_GET_SIZE(f->f_frame->code->co_code) / sizeof(_Py_CODEUNIT)); + int *lines = marklines(f->f_frame->code, len); if (lines == NULL) { return -1; } @@ -496,7 +496,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - int64_t *stacks = mark_stacks(f->f_specials->code, len); + int64_t *stacks = mark_stacks(f->f_frame->code, len); if (stacks == NULL) { PyMem_Free(lines); return -1; @@ -504,7 +504,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_specials->lasti]; + int64_t start_stack = stacks[f->f_frame->lasti]; int err = -1; const char *msg = "cannot find bytecode for specified line"; for (int i = 0; i < len; i++) { @@ -548,7 +548,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } /* Finally set the new lasti and return OK. */ f->f_lineno = 0; - f->f_specials->lasti = best_addr; + f->f_frame->lasti = best_addr; return 0; } @@ -615,16 +615,16 @@ frame_dealloc(PyFrameObject *f) /* Kill all local variables including specials. */ if (f->f_localsptr) { /* Don't clear code object until the end */ - co = f->f_specials->code; - Py_CLEAR(f->f_specials->globals); - Py_CLEAR(f->f_specials->builtins); - Py_CLEAR(f->f_specials->locals); + co = f->f_frame->code; + Py_CLEAR(f->f_frame->globals); + Py_CLEAR(f->f_frame->builtins); + Py_CLEAR(f->f_frame->locals); 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_specials->stackdepth; i++) { - Py_XDECREF(f->f_specials->stack[i]); + for (int i = 0; i < f->f_frame->stackdepth; i++) { + Py_XDECREF(f->f_frame->stack[i]); } if (f->f_own_locals_memory) { PyMem_Free(f->f_localsptr); @@ -632,7 +632,7 @@ frame_dealloc(PyFrameObject *f) } f->f_localsptr = NULL; } - f->f_specials->stackdepth = 0; + f->f_frame->stackdepth = 0; Py_XDECREF(f->f_back); Py_CLEAR(f->f_trace); struct _Py_frame_state *state = get_frame_state(); @@ -658,19 +658,19 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - Py_VISIT(f->f_specials->globals); - Py_VISIT(f->f_specials->builtins); - Py_VISIT(f->f_specials->locals); - Py_VISIT(f->f_specials->code); + Py_VISIT(f->f_frame->globals); + Py_VISIT(f->f_frame->builtins); + Py_VISIT(f->f_frame->locals); + Py_VISIT(f->f_frame->code); /* locals */ - PyCodeObject *co = f->f_specials->code; + PyCodeObject *co = f->f_frame->code; for (int i = 0; i < co->co_nlocalsplus; i++) { Py_VISIT(f->f_localsptr[i]); } /* stack */ - for (int i = 0; i < f->f_specials->stackdepth; i++) { - Py_VISIT(f->f_specials->stack[i]); + for (int i = 0; i < f->f_frame->stackdepth; i++) { + Py_VISIT(f->f_frame->stack[i]); } return 0; } @@ -686,17 +686,17 @@ frame_tp_clear(PyFrameObject *f) f->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); - PyCodeObject *co = f->f_specials->code; + PyCodeObject *co = f->f_frame->code; /* locals */ for (int i = 0; i < co->co_nlocalsplus; i++) { Py_CLEAR(f->f_localsptr[i]); } /* stack */ - for (int i = 0; i < f->f_specials->stackdepth; i++) { - Py_CLEAR(f->f_specials->stack[i]); + for (int i = 0; i < f->f_frame->stackdepth; i++) { + Py_CLEAR(f->f_frame->stack[i]); } - f->f_specials->stackdepth = 0; + f->f_frame->stackdepth = 0; return 0; } @@ -725,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 = f->f_specials->code; + PyCodeObject *code = f->f_frame->code; res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); } return PyLong_FromSsize_t(res); @@ -738,7 +738,7 @@ static PyObject * frame_repr(PyFrameObject *f) { int lineno = PyFrame_GetLineNumber(f); - PyCodeObject *code = f->f_specials->code; + PyCodeObject *code = f->f_frame->code; return PyUnicode_FromFormat( "", f, code->co_filename, lineno, code->co_name); @@ -840,25 +840,25 @@ int _PyFrame_TakeLocals(PyFrameObject *f) { assert(f->f_own_locals_memory == 0); - assert(f->f_specials->stackdepth == 0); - Py_ssize_t size = ((char*)f->f_specials->stack)-((char *)f->f_localsptr); + assert(f->f_frame->stackdepth == 0); + Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); if (copy == NULL) { - for (int i = 0; i < f->f_specials->code->co_nlocalsplus; i++) { + for (int i = 0; i < f->f_frame->code->co_nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; Py_XDECREF(o); } - Py_XDECREF(f->f_specials->builtins); - Py_XDECREF(f->f_specials->globals); - Py_XDECREF(f->f_specials->locals); - Py_XDECREF(f->f_specials->code); + Py_XDECREF(f->f_frame->builtins); + Py_XDECREF(f->f_frame->globals); + Py_XDECREF(f->f_frame->locals); + Py_XDECREF(f->f_frame->code); PyErr_NoMemory(); return -1; } memcpy(copy, f->f_localsptr, size); f->f_own_locals_memory = 1; f->f_localsptr = copy; - f->f_specials = (_PyFrameSpecials *)(copy + f->f_specials->code->co_nlocalsplus); + f->f_frame = (_PyFrame *)(copy + f->f_frame->code->co_nlocalsplus); return 0; } @@ -876,16 +876,16 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *l if (f == NULL) { return NULL; } - _PyFrameSpecials *specials = (_PyFrameSpecials *)(f->f_localsptr + code->co_nlocalsplus); - f->f_specials = specials; + _PyFrame *frame = (_PyFrame *)(f->f_localsptr + code->co_nlocalsplus); + f->f_frame = frame; f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame); - specials->code = (PyCodeObject *)Py_NewRef(con->fc_code); - specials->builtins = Py_NewRef(con->fc_builtins); - specials->globals = Py_NewRef(con->fc_globals); - specials->locals = Py_XNewRef(locals); - specials->lasti = -1; + frame->code = (PyCodeObject *)Py_NewRef(con->fc_code); + frame->builtins = Py_NewRef(con->fc_builtins); + frame->globals = Py_NewRef(con->fc_globals); + frame->locals = Py_XNewRef(locals); + frame->lasti = -1; f->f_trace = NULL; - f->f_specials->stackdepth = 0; + f->f_frame->stackdepth = 0; f->f_trace_lines = 1; f->f_trace_opcodes = 0; f->f_gen = NULL; @@ -924,8 +924,8 @@ static int _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) { const _Py_CODEUNIT *code = - (const _Py_CODEUNIT *)PyBytes_AS_STRING(f->f_specials->code->co_code); - for (int i = 0; i < f->f_specials->lasti; i++) { + (const _Py_CODEUNIT *)PyBytes_AS_STRING(f->f_frame->code->co_code); + for (int i = 0; i < f->f_frame->lasti; i++) { if (_Py_OPCODE(code[i]) == opcode && _Py_OPARG(code[i]) == oparg) { return 1; } @@ -945,13 +945,13 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyErr_BadInternalCall(); return -1; } - locals = f->f_specials->locals; + locals = f->f_frame->locals; if (locals == NULL) { - locals = f->f_specials->locals = PyDict_New(); + locals = f->f_frame->locals = PyDict_New(); if (locals == NULL) return -1; } - co = f->f_specials->code; + co = f->f_frame->code; fast = f->f_localsptr; for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocalsPlusKind kind = co->co_localspluskinds[i]; @@ -1040,11 +1040,11 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) if (f == NULL || f->f_state == FRAME_CLEARED) { return; } - locals = f->f_specials->locals; + locals = f->f_frame->locals; if (locals == NULL) return; fast = f->f_localsptr; - co = f->f_specials->code; + co = f->f_frame->code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1137,7 +1137,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); - PyCodeObject *code = frame->f_specials->code; + PyCodeObject *code = frame->f_frame->code; assert(code != NULL); Py_INCREF(code); return code; diff --git a/Objects/genobject.c b/Objects/genobject.c index 0802e047df9d14..a13a79ef2ed4f1 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -80,7 +80,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_specials->lasti == -1) + gen->gi_frame->f_frame->lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -177,12 +177,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, } assert(_PyFrame_IsRunnable(f)); - assert(f->f_specials->lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + assert(f->f_frame->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_specials->stack[gen->gi_frame->f_specials->stackdepth] = result; - gen->gi_frame->f_specials->stackdepth++; + gen->gi_frame->f_frame->stack[gen->gi_frame->f_frame->stackdepth] = result; + gen->gi_frame->f_frame->stackdepth++; /* Generators always return to their most recent caller, not * necessarily their creator. */ @@ -335,7 +335,7 @@ _PyGen_yf(PyGenObject *gen) PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (f->f_specials->lasti < 0) { + if (f->f_frame->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 */ @@ -343,10 +343,10 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(f->f_specials->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(f->f_frame->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - assert(f->f_specials->stackdepth > 0); - yf = f->f_specials->stack[f->f_specials->stackdepth-1]; + assert(f->f_frame->stackdepth > 0); + yf = f->f_frame->stack[f->f_frame->stackdepth-1]; Py_INCREF(yf); } @@ -460,14 +460,14 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, if (!ret) { PyObject *val; /* Pop subiterator from stack */ - assert(gen->gi_frame->f_specials->stackdepth > 0); - gen->gi_frame->f_specials->stackdepth--; - ret = gen->gi_frame->f_specials->stack[gen->gi_frame->f_specials->stackdepth]; + assert(gen->gi_frame->f_frame->stackdepth > 0); + gen->gi_frame->f_frame->stackdepth--; + ret = gen->gi_frame->f_frame->stack[gen->gi_frame->f_frame->stackdepth]; assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ - assert(gen->gi_frame->f_specials->lasti >= 0); - gen->gi_frame->f_specials->lasti += 1; + assert(gen->gi_frame->f_frame->lasti >= 0); + gen->gi_frame->f_frame->lasti += 1; if (_PyGen_FetchStopIterationValue(&val) == 0) { ret = gen_send(gen, val); Py_DECREF(val); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 47349dfa9dcb02..45944075a50698 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8868,7 +8868,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, if (firstarg != NULL && (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_specials->lasti >= 0) { + if (f->f_frame->lasti >= 0) { assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); diff --git a/Python/_warnings.c b/Python/_warnings.c index f64e71ba28ef95..f53236a3cb526f 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -854,8 +854,8 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, *lineno = 1; } else { - globals = f->f_specials->globals; - *filename = f->f_specials->code->co_filename; + globals = f->f_frame->globals; + *filename = f->f_frame->code->co_filename; Py_INCREF(*filename); *lineno = PyFrame_GetLineNumber(f); Py_DECREF(f); diff --git a/Python/ceval.c b/Python/ceval.c index 51223a1fa44464..f01dfa00280a6d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1244,7 +1244,7 @@ eval_frame_handle_pending(PyThreadState *tstate) if (cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \ goto tracing_dispatch; \ } \ - specials->lasti = INSTR_OFFSET(); \ + frame->lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ DISPATCH_GOTO(); \ } @@ -1333,7 +1333,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 - specials->stack)) +#define STACK_LEVEL() ((int)(stack_pointer - frame->stack)) #define EMPTY() (STACK_LEVEL() == 0) #define TOP() (stack_pointer[-1]) #define SECOND() (stack_pointer[-2]) @@ -1399,9 +1399,9 @@ eval_frame_handle_pending(PyThreadState *tstate) #define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; } -#define GLOBALS() specials->globals -#define BUILTINS() specials->builtins -#define LOCALS() specials->locals +#define GLOBALS() frame->globals +#define BUILTINS() frame->builtins +#define LOCALS() frame->locals PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) @@ -1421,7 +1421,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ PyObject **localsplus; - _PyFrameSpecials *specials; + _PyFrame *frame; PyObject *retval = NULL; /* Return value */ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker; PyCodeObject *co; @@ -1451,8 +1451,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) /* push frame */ tstate->frame = f; - specials = f->f_specials; - co = specials->code; + frame = f->f_frame; + co = frame->code; if (cframe.use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1510,30 +1510,30 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) localsplus = f->f_localsptr; first_instr = co->co_firstinstr; /* - specials->lasti refers to the index of the last instruction, + frame->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 specials->lasti to itself, in order to repeatedly yield + YIELD_FROM sets frame->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 specials->lasti. A successful + direct succession without updating frame->lasti. A successful prediction effectively links the two codes together as if they - were a single new opcode; accordingly,specials->lasti will point to + were a single new opcode; accordingly,frame->lasti will point to the first code in the pair (for instance, GET_ITER followed by - FOR_ITER is effectively a single opcode and specials->lasti will point + FOR_ITER is effectively a single opcode and frame->lasti will point to the beginning of the combined pair.) */ - assert(specials->lasti >= -1); - next_instr = first_instr + specials->lasti + 1; - stack_pointer = specials->stack + specials->stackdepth; + assert(frame->lasti >= -1); + next_instr = first_instr + frame->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. */ - specials->stackdepth = -1; + frame->stackdepth = -1; f->f_state = FRAME_EXECUTING; #ifdef LLTRACE @@ -1598,8 +1598,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tracing_dispatch: { - int instr_prev = specials->lasti; - specials->lasti = INSTR_OFFSET(); + int instr_prev = frame->lasti; + frame->lasti = INSTR_OFFSET(); TRACING_NEXTOPARG(); if (PyDTrace_LINE_ENABLED()) @@ -1612,7 +1612,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) int err; /* see maybe_call_line_trace() for expository comments */ - specials->stackdepth = (int)(stack_pointer - specials->stack); + frame->stackdepth = (int)(stack_pointer - frame->stack); err = maybe_call_line_trace(tstate->c_tracefunc, tstate->c_traceobj, @@ -1622,9 +1622,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) goto error; } /* Reload possibly changed frame fields */ - JUMPTO(specials->lasti); - stack_pointer = specials->stack+specials->stackdepth; - specials->stackdepth = -1; + JUMPTO(frame->lasti); + stack_pointer = frame->stack+frame->stackdepth; + frame->stackdepth = -1; NEXTOPARG(); } } @@ -1635,11 +1635,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (lltrace) { if (HAS_ARG(opcode)) { printf("%d: %d, %d\n", - specials->lasti, opcode, oparg); + frame->lasti, opcode, oparg); } else { printf("%d: %d\n", - specials->lasti, opcode); + frame->lasti, opcode); } } #endif @@ -2248,7 +2248,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) retval = POP(); assert(EMPTY()); f->f_state = FRAME_RETURNED; - specials->stackdepth = 0; + frame->stackdepth = 0; goto exiting; } @@ -2433,10 +2433,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(specials->lasti > 0); - specials->lasti -= 1; + assert(frame->lasti > 0); + frame->lasti -= 1; f->f_state = FRAME_SUSPENDED; - specials->stackdepth = (int)(stack_pointer - specials->stack); + frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2453,7 +2453,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) retval = w; } f->f_state = FRAME_SUSPENDED; - specials->stackdepth = (int)(stack_pointer - specials->stack); + frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2500,7 +2500,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(POP_EXCEPT_AND_RERAISE): { PyObject *lasti = PEEK(4); if (PyLong_Check(lasti)) { - specials->lasti = PyLong_AsLong(lasti); + frame->lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -2531,7 +2531,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (oparg) { PyObject *lasti = PEEK(oparg+3); if (PyLong_Check(lasti)) { - specials->lasti = PyLong_AsLong(lasti); + frame->lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -4323,7 +4323,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) exception_unwind: f->f_state = FRAME_UNWINDING; - /* We can't use specials->lasti here, as RERAISE may have set it */ + /* We can't use frame->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) { @@ -4338,7 +4338,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) } PyObject *exc, *val, *tb; if (lasti) { - PyObject *lasti = PyLong_FromLong(specials->lasti); + PyObject *lasti = PyLong_FromLong(frame->lasti); if (lasti == NULL) { goto exception_unwind; } @@ -4364,7 +4364,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) JUMPTO(handler); /* Resume normal execution */ f->f_state = FRAME_EXECUTING; - specials->lasti = handler; + frame->lasti = handler; NEXTOPARG(); goto dispatch_opcode; } /* main loop */ @@ -4377,7 +4377,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) PyObject *o = POP(); Py_XDECREF(o); } - specials->stackdepth = 0; + frame->stackdepth = 0; f->f_state = FRAME_RAISED; exiting: if (cframe.use_tracing) { @@ -4996,7 +4996,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, return make_coro(con, f); } PyObject *retval = _PyEval_EvalFrame(tstate, f, 0); - assert(f->f_specials->stackdepth == 0); + assert(f->f_frame->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 @@ -5018,11 +5018,11 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, for (int i = 0; i < code->co_nlocalsplus; i++) { Py_XDECREF(localsarray[i]); } - _PyFrameSpecials *specials = f->f_specials; - Py_XDECREF(specials->locals); - Py_DECREF(specials->globals); - Py_DECREF(specials->builtins); - Py_DECREF(specials->code); + _PyFrame *frame = f->f_frame; + Py_XDECREF(frame->locals); + Py_DECREF(frame->globals); + Py_DECREF(frame->builtins); + Py_DECREF(frame->code); Py_DECREF(f); --tstate->recursion_depth; } @@ -5396,7 +5396,7 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, static void initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame) { - PyCodeObject *code = frame->f_specials->code; + PyCodeObject *code = frame->f_frame->code; if (trace_info->code != code) { trace_info->code = code; _PyCode_InitAddressRange(code, &trace_info->bounds); @@ -5413,12 +5413,12 @@ call_trace(Py_tracefunc func, PyObject *obj, return 0; tstate->tracing++; tstate->cframe->use_tracing = 0; - if (frame->f_specials->lasti < 0) { - frame->f_lineno = frame->f_specials->code->co_firstlineno; + if (frame->f_frame->lasti < 0) { + frame->f_lineno = frame->f_frame->code->co_firstlineno; } else { initialize_trace_info(&tstate->trace_info, frame); - frame->f_lineno = _PyCode_CheckLineNumber(frame->f_specials->lasti*2, &tstate->trace_info.bounds); + frame->f_lineno = _PyCode_CheckLineNumber(frame->f_frame->lasti*2, &tstate->trace_info.bounds); } result = func(obj, frame, what, arg); frame->f_lineno = 0; @@ -5458,11 +5458,11 @@ 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_specials->lasti*2, &tstate->trace_info.bounds); + int line = _PyCode_CheckLineNumber(frame->f_frame->lasti*2, &tstate->trace_info.bounds); if (line != -1 && frame->f_trace_lines) { /* Trace backward edges or first instruction of a new line */ - if (frame->f_specials->lasti < instr_prev || - (line != lastline && frame->f_specials->lasti*2 == tstate->trace_info.bounds.ar_start)) + if (frame->f_frame->lasti < instr_prev || + (line != lastline && frame->f_frame->lasti*2 == tstate->trace_info.bounds.ar_start)) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } @@ -5627,7 +5627,7 @@ _PyEval_GetBuiltins(PyThreadState *tstate) { PyFrameObject *frame = tstate->frame; if (frame != NULL) { - return frame->f_specials->builtins; + return frame->f_frame->builtins; } return tstate->interp->builtins; } @@ -5668,7 +5668,7 @@ PyEval_GetLocals(void) return NULL; } - PyObject *locals = current_frame->f_specials->locals; + PyObject *locals = current_frame->f_frame->locals; assert(locals != NULL); return locals; } @@ -5681,7 +5681,7 @@ PyEval_GetGlobals(void) if (current_frame == NULL) { return NULL; } - return current_frame->f_specials->globals; + return current_frame->f_frame->globals; } int @@ -5692,7 +5692,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) int result = cf->cf_flags != 0; if (current_frame != NULL) { - const int codeflags = current_frame->f_specials->code->co_flags; + const int codeflags = current_frame->f_frame->code->co_flags; const int compilerflags = codeflags & PyCF_MASK; if (compilerflags) { result = 1; @@ -5933,14 +5933,14 @@ import_name(PyThreadState *tstate, PyFrameObject *f, PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemIdWithError(f->f_specials->builtins, &PyId___import__); + import_func = _PyDict_GetItemIdWithError(f->f_frame->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_specials->locals; + PyObject *locals = f->f_frame->locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { int ilevel = _PyLong_AsInt(level); @@ -5949,7 +5949,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, } res = PyImport_ImportModuleLevelObject( name, - f->f_specials->globals, + f->f_frame->globals, locals == NULL ? Py_None :locals, fromlist, ilevel); @@ -5959,7 +5959,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, Py_INCREF(import_func); stack[0] = name; - stack[1] = f->f_specials->globals; + stack[1] = f->f_frame->globals; stack[2] = locals == NULL ? Py_None : locals; stack[3] = fromlist; stack[4] = level; @@ -6296,9 +6296,9 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, } case STORE_NAME: { - PyObject *names = f->f_specials->code->co_names; + PyObject *names = f->f_frame->code->co_names; PyObject *name = GETITEM(names, oparg); - PyObject *locals = f->f_specials->locals; + PyObject *locals = f->f_frame->locals; if (locals && PyDict_CheckExact(locals)) { PyObject *w = PyDict_GetItemWithError(locals, name); if ((w == v && PyDict_DelItem(locals, name) != 0) || @@ -6382,7 +6382,7 @@ dtrace_function_entry(PyFrameObject *f) const char *funcname; int lineno; - PyCodeObject *code = f->f_specials->code; + PyCodeObject *code = f->f_frame->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); lineno = PyFrame_GetLineNumber(f); @@ -6397,7 +6397,7 @@ dtrace_function_return(PyFrameObject *f) const char *funcname; int lineno; - PyCodeObject *code = f->f_specials->code; + PyCodeObject *code = f->f_frame->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); lineno = PyFrame_GetLineNumber(f); @@ -6417,17 +6417,17 @@ maybe_dtrace_line(PyFrameObject *frame, instruction window, reset the window. */ initialize_trace_info(trace_info, frame); - int line = _PyCode_CheckLineNumber(frame->f_specials->lasti*2, &trace_info->bounds); + int line = _PyCode_CheckLineNumber(frame->f_frame->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_specials->lasti < instr_prev) { + if (line != frame->f_lineno || frame->f_frame->lasti < instr_prev) { if (line != -1) { frame->f_lineno = line; - co_filename = PyUnicode_AsUTF8(frame->f_specials->code->co_filename); + co_filename = PyUnicode_AsUTF8(frame->f_frame->code->co_filename); if (!co_filename) co_filename = "?"; - co_name = PyUnicode_AsUTF8(frame->f_specials->code->co_name); + co_name = PyUnicode_AsUTF8(frame->f_frame->code->co_name); if (!co_name) co_name = "?"; PyDTrace_LINE(co_filename, co_name, line); diff --git a/Python/suggestions.c b/Python/suggestions.c index 069294150e630a..ae93a446b86411 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -229,7 +229,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(frame->f_specials->globals); + dir = PySequence_List(frame->f_frame->globals); if (dir == NULL) { return NULL; } @@ -239,7 +239,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(frame->f_specials->builtins); + dir = PySequence_List(frame->f_frame->builtins); if (dir == NULL) { return NULL; } diff --git a/Python/traceback.c b/Python/traceback.c index d6f9f60782765b..74cbb3ddc6520a 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -235,7 +235,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_specials->lasti*2, + return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_frame->lasti*2, PyFrame_GetLineNumber(frame)); } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 0a146f067adb9f..c6d7ee86980751 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -888,8 +888,8 @@ def iter_locals(self): yield (pyop_name, pyop_value) def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr): - f_specials = self.field('f_specials') - return convert(f_specials[name]) + f_frame = self.field('f_frame') + return convert(f_frame[name]) def _f_globals(self): return self._f_special("globals") From 8543c27d0889d83bdc4980eea3182913f0e2bc7b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Jun 2021 15:46:33 +0100 Subject: [PATCH 04/40] Refactor, pushing PyFrame upward toward _PyEval_Vector. --- Include/cpython/frameobject.h | 2 +- Include/internal/pycore_frame.h | 12 ++++++ Objects/frameobject.c | 73 ++++++++++++++++----------------- Python/ceval.c | 54 +++++++++++++++--------- 4 files changed, 83 insertions(+), 58 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 1fb64348f1c18b..71c48b38bec925 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -57,7 +57,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* -_PyFrame_New_NoTrack(PyThreadState *, PyFrameConstructor *, PyObject *, PyObject **); +_PyFrame_New_NoTrack(PyThreadState *, struct _py_frame *, int); /* The rest of the interface is specific for frame objects */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 9ee3d362880fc1..705377b6abf03e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -18,6 +18,18 @@ typedef struct _py_frame { int _PyFrame_TakeLocals(PyFrameObject *f); +static inline void +_PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals) +{ + frame->code = (PyCodeObject *)Py_NewRef(con->fc_code); + frame->builtins = Py_NewRef(con->fc_builtins); + frame->globals = Py_NewRef(con->fc_globals); + frame->locals = Py_XNewRef(locals); + frame->lasti = -1; +} + + + #ifdef __cplusplus } #endif diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 9b480cc66e0fb7..96ff66f719ae56 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -789,33 +789,39 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); -static inline PyFrameObject* -frame_alloc(PyCodeObject *code, PyObject **localsarray) +static _PyFrame * +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; } + _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals); + return frame; +} + +static inline PyFrameObject* +frame_alloc(_PyFrame *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->code); + Py_XDECREF(frame->builtins); + Py_XDECREF(frame->globals); + Py_XDECREF(frame->locals); + PyMem_Free(frame); } return NULL; } @@ -831,7 +837,8 @@ 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_localsptr = ((PyObject **)frame) - frame->code->co_nlocalsplus; f->f_own_locals_memory = owns; return f; } @@ -863,27 +870,13 @@ _PyFrame_TakeLocals(PyFrameObject *f) } PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject **localsarray) +_PyFrame_New_NoTrack(PyThreadState *tstate, _PyFrame *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; } - _PyFrame *frame = (_PyFrame *)(f->f_localsptr + code->co_nlocalsplus); - f->f_frame = frame; f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame); - frame->code = (PyCodeObject *)Py_NewRef(con->fc_code); - frame->builtins = Py_NewRef(con->fc_builtins); - frame->globals = Py_NewRef(con->fc_globals); - frame->locals = Py_XNewRef(locals); - frame->lasti = -1; f->f_trace = NULL; f->f_frame->stackdepth = 0; f->f_trace_lines = 1; @@ -913,7 +906,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, .fc_kwdefaults = NULL, .fc_closure = NULL }; - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, &desc, locals, NULL); + _PyFrame *frame = allocate_heap_frame(&desc, locals); + if (frame == NULL) { + return NULL; + } + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 1); if (f) { _PyObject_GC_TRACK(f); } @@ -972,7 +969,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyObject *value = fast[i]; if (f->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); @@ -989,7 +986,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) 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(). } @@ -1066,7 +1063,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; diff --git a/Python/ceval.c b/Python/ceval.c index f01dfa00280a6d..1bfefa859ec030 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4921,13 +4921,29 @@ _PyEval_MakeFrameVector(PyThreadState *tstate, { assert(is_tstate_valid(tstate)); assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults)); - + PyCodeObject *code = (PyCodeObject *)con->fc_code; + _PyFrame *frame; + int owns = 0; + 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; + } + frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals); /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, con, locals, localsarray); + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, owns); if (f == NULL) { return NULL; } - if (initialize_locals(tstate, con, f->f_localsptr, args, argcount, kwnames)) { + if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { Py_DECREF(f); return NULL; } @@ -4935,9 +4951,17 @@ _PyEval_MakeFrameVector(PyThreadState *tstate, } 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)); + PyFrameObject *f = _PyEval_MakeFrameVector( + tstate, con, locals, args, argcount, kwnames, NULL); + if (f == NULL) { + return NULL; + } PyObject *gen; int is_coro = ((PyCodeObject *)con->fc_code)->co_flags & CO_COROUTINE; @@ -4974,27 +4998,20 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, int is_coro = code->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); if (is_coro) { - localsarray = NULL; + return make_coro(tstate, con, locals, args, argcount, kwnames); } - else { - int size = code->co_nlocalsplus + code->co_stacksize + - FRAME_SPECIALS_SIZE; - localsarray = _PyThreadState_PushLocals(tstate, size); - if (localsarray == NULL) { - return NULL; - } + int size = code->co_nlocalsplus + code->co_stacksize + + FRAME_SPECIALS_SIZE; + localsarray = _PyThreadState_PushLocals(tstate, size); + if (localsarray == NULL) { + return NULL; } PyFrameObject *f = _PyEval_MakeFrameVector( tstate, con, locals, args, argcount, kwnames, localsarray); if (f == NULL) { - if (!is_coro) { - _PyThreadState_PopLocals(tstate, localsarray); - } + _PyThreadState_PopLocals(tstate, localsarray); return NULL; } - if (is_coro) { - return make_coro(con, f); - } PyObject *retval = _PyEval_EvalFrame(tstate, f, 0); assert(f->f_frame->stackdepth == 0); @@ -5003,7 +5020,6 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, 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); From 423d403ae2f50b1381b8e0ea580f58534fa47e39 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Jun 2021 17:52:40 +0100 Subject: [PATCH 05/40] Add pointer from stack frame to frame object and rename tstate.frame to tstate.pyframe as preparation for lazily created frames. --- Include/cpython/pystate.h | 2 +- Include/internal/pycore_frame.h | 2 ++ Modules/signalmodule.c | 2 +- Objects/frameobject.c | 2 +- Objects/genobject.c | 12 ++++++------ Python/ceval.c | 25 ++++++++++++++----------- Python/errors.c | 2 +- Python/pylifecycle.c | 2 +- Python/pystate.c | 10 +++++----- 9 files changed, 32 insertions(+), 27 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 56ea8d03453c10..50940b2b7bd337 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; + PyFrameObject *pyframe; int recursion_depth; int recursion_headroom; /* Allow 50 more calls to handle any errors. */ int stackcheck_counter; diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 705377b6abf03e..de31cc59f72db0 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -9,6 +9,7 @@ typedef struct _py_frame { PyObject *builtins; PyObject *locals; PyCodeObject *code; + PyFrameObject *frame_obj; int lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ PyObject *stack[1]; @@ -25,6 +26,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * frame->builtins = Py_NewRef(con->fc_builtins); frame->globals = Py_NewRef(con->fc_globals); frame->locals = Py_XNewRef(locals); + frame->frame_obj = NULL; frame->lasti = -1; } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index a4eeec9807c919..6614acc6424884 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1786,7 +1786,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) */ _Py_atomic_store(&is_tripped, 0); - PyObject *frame = (PyObject *)tstate->frame; + PyObject *frame = (PyObject *)tstate->pyframe; if (!frame) { frame = Py_None; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 96ff66f719ae56..a049b91b08c945 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -876,7 +876,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, _PyFrame *frame, int owns) if (f == NULL) { return NULL; } - f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame); + f->f_back = (PyFrameObject*)Py_XNewRef(tstate->pyframe); f->f_trace = NULL; f->f_frame->stackdepth = 0; f->f_trace_lines = 1; diff --git a/Objects/genobject.c b/Objects/genobject.c index a13a79ef2ed4f1..4bfc10d8095b15 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -186,9 +186,9 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* Generators always return to their most recent caller, not * necessarily their creator. */ - Py_XINCREF(tstate->frame); + Py_XINCREF(tstate->pyframe); assert(f->f_back == NULL); - f->f_back = tstate->frame; + f->f_back = tstate->pyframe; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -205,7 +205,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* Don't keep the reference to f_back 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); + assert(f->f_back == tstate->pyframe); Py_CLEAR(f->f_back); /* If the generator just returned (as opposed to yielding), signal @@ -423,14 +423,14 @@ _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; + PyFrameObject *f = tstate->pyframe; /* 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; + tstate->pyframe = gen->gi_frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ PyFrameState state = gen->gi_frame->f_state; @@ -438,7 +438,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); gen->gi_frame->f_state = state; - tstate->frame = f; + tstate->pyframe = f; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; diff --git a/Python/ceval.c b/Python/ceval.c index 1bfefa859ec030..ae95ed42cabae0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1407,6 +1407,8 @@ PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) { _Py_EnsureTstateNotNULL(tstate); + f->f_frame->frame_obj = f; + Py_INCREF(f); #if USE_COMPUTED_GOTOS /* Import the static jump table */ @@ -1450,7 +1452,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tstate->cframe = &cframe; /* push frame */ - tstate->frame = f; + tstate->pyframe = f; frame = f->f_frame; co = frame->code; @@ -4404,8 +4406,9 @@ MISS_WITH_CACHE(LOAD_GLOBAL) if (PyDTrace_FUNCTION_RETURN_ENABLED()) dtrace_function_return(f); _Py_LeaveRecursiveCall(tstate); - tstate->frame = f->f_back; - + tstate->pyframe = f->f_back; + frame->frame_obj = NULL; + Py_DECREF(f); return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } @@ -5635,13 +5638,13 @@ PyFrameObject * PyEval_GetFrame(void) { PyThreadState *tstate = _PyThreadState_GET(); - return tstate->frame; + return tstate->pyframe; } PyObject * _PyEval_GetBuiltins(PyThreadState *tstate) { - PyFrameObject *frame = tstate->frame; + PyFrameObject *frame = tstate->pyframe; if (frame != NULL) { return frame->f_frame->builtins; } @@ -5674,7 +5677,7 @@ PyObject * PyEval_GetLocals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + PyFrameObject *current_frame = tstate->pyframe; if (current_frame == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist"); return NULL; @@ -5693,7 +5696,7 @@ PyObject * PyEval_GetGlobals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + PyFrameObject *current_frame = tstate->pyframe; if (current_frame == NULL) { return NULL; } @@ -5704,7 +5707,7 @@ int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->frame; + PyFrameObject *current_frame = tstate->pyframe; int result = cf->cf_flags != 0; if (current_frame != NULL) { @@ -5754,7 +5757,7 @@ PyEval_GetFuncDesc(PyObject *func) #define C_TRACE(x, call) \ if (use_tracing && tstate->c_profilefunc) { \ if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \ - tstate, tstate->frame, \ + tstate, tstate->pyframe, \ PyTrace_C_CALL, func)) { \ x = NULL; \ } \ @@ -5764,13 +5767,13 @@ if (use_tracing && tstate->c_profilefunc) { \ if (x == NULL) { \ call_trace_protected(tstate->c_profilefunc, \ tstate->c_profileobj, \ - tstate, tstate->frame, \ + tstate, tstate->pyframe, \ PyTrace_C_EXCEPTION, func); \ /* XXX should pass (type, value, tb) */ \ } else { \ if (call_trace(tstate->c_profilefunc, \ tstate->c_profileobj, \ - tstate, tstate->frame, \ + tstate, tstate->pyframe, \ PyTrace_C_RETURN, func)) { \ Py_DECREF(x); \ x = NULL; \ diff --git a/Python/errors.c b/Python/errors.c index 118118ffe90b44..f5e207ca8dc0b9 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1406,7 +1406,7 @@ _PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj) } if (exc_tb == NULL) { - PyFrameObject *frame = tstate->frame; + PyFrameObject *frame = tstate->pyframe; if (frame != NULL) { exc_tb = _PyTraceBack_FromFrame(NULL, frame); if (exc_tb == NULL) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index d31a9c15fd1ec5..2543136115aa70 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2003,7 +2003,7 @@ Py_EndInterpreter(PyThreadState *tstate) if (tstate != _PyThreadState_GET()) { Py_FatalError("thread is not current"); } - if (tstate->frame != NULL) { + if (tstate->pyframe != NULL) { Py_FatalError("thread still has a frame"); } interp->finalizing = 1; diff --git a/Python/pystate.c b/Python/pystate.c index a94a615f39569f..5f37046aaedfb9 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -635,7 +635,7 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->interp = interp; - tstate->frame = NULL; + tstate->pyframe = NULL; tstate->recursion_depth = 0; tstate->recursion_headroom = 0; tstate->stackcheck_counter = 0; @@ -860,7 +860,7 @@ PyThreadState_Clear(PyThreadState *tstate) { int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; - if (verbose && tstate->frame != NULL) { + if (verbose && tstate->pyframe != NULL) { /* bpo-20526: After the main thread calls _PyRuntimeState_SetFinalizing() in Py_FinalizeEx(), threads must exit when trying to take the GIL. If a thread exit in the middle of @@ -872,7 +872,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 +1133,7 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - PyFrameObject *frame = tstate->frame; + PyFrameObject *frame = tstate->pyframe; Py_XINCREF(frame); return frame; } @@ -1254,7 +1254,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; + PyFrameObject *frame = t->pyframe; if (frame == NULL) { continue; } From 7bf6c30f5c6ad41f9c18e95aad40cb160f7d6796 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Jun 2021 17:43:11 +0100 Subject: [PATCH 06/40] More refactoring. Add tstate.frame for the top stack frame. --- Include/cpython/pystate.h | 1 + Include/internal/pycore_frame.h | 18 +++++ Objects/frameobject.c | 11 ++- Python/ceval.c | 124 +++++++++++++++++++++----------- Python/pystate.c | 1 + 5 files changed, 105 insertions(+), 50 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 50940b2b7bd337..f8a3063e89c235 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -79,6 +79,7 @@ struct _ts { /* Borrowed reference to the current frame (it can be NULL) */ PyFrameObject *pyframe; + struct _py_frame *frame; int recursion_depth; int recursion_headroom; /* Allow 50 more calls to handle any errors. */ int stackcheck_counter; diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index de31cc59f72db0..4b90edb69e2667 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -10,6 +10,7 @@ typedef struct _py_frame { PyObject *locals; PyCodeObject *code; PyFrameObject *frame_obj; + struct _py_frame *previous; int lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ PyObject *stack[1]; @@ -30,7 +31,24 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * frame->lasti = -1; } +static inline void +_PyFrame_ClearSpecials(_PyFrame *frame) +{ + Py_XDECREF(frame->locals); + Py_DECREF(frame->globals); + Py_DECREF(frame->builtins); + Py_DECREF(frame->code); +} + +static inline PyObject** +_PyFrame_GetLocalsArray(_PyFrame *frame) +{ + return ((PyObject **)frame) - frame->code->co_nlocalsplus; +} +/* Returns a borrowed reference */ +PyFrameObject * +_PyFrame_GetFrameObject(_PyFrame *frame); #ifdef __cplusplus } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a049b91b08c945..504abf655cc912 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -612,8 +612,8 @@ 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) { /* Don't clear code object until the end */ co = f->f_frame->code; Py_CLEAR(f->f_frame->globals); @@ -626,11 +626,8 @@ frame_dealloc(PyFrameObject *f) for (int i = 0; i < f->f_frame->stackdepth; i++) { Py_XDECREF(f->f_frame->stack[i]); } - if (f->f_own_locals_memory) { - PyMem_Free(f->f_localsptr); - f->f_own_locals_memory = 0; - } - f->f_localsptr = NULL; + PyMem_Free(f->f_localsptr); + f->f_own_locals_memory = 0; } f->f_frame->stackdepth = 0; Py_XDECREF(f->f_back); diff --git a/Python/ceval.c b/Python/ceval.c index ae95ed42cabae0..40c548cea27385 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1407,8 +1407,6 @@ PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) { _Py_EnsureTstateNotNULL(tstate); - f->f_frame->frame_obj = f; - Py_INCREF(f); #if USE_COMPUTED_GOTOS /* Import the static jump table */ @@ -4407,11 +4405,16 @@ MISS_WITH_CACHE(LOAD_GLOBAL) dtrace_function_return(f); _Py_LeaveRecursiveCall(tstate); tstate->pyframe = f->f_back; - frame->frame_obj = NULL; - Py_DECREF(f); return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } + +//PyObject* _Py_HOT_FUNCTION +//_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) +//{ +// return _PyEval_EvalNoFrame(tstate, f->f_frame, throwflag); +//} + static void format_missing(PyThreadState *tstate, const char *kind, PyCodeObject *co, PyObject *names, PyObject *qualname) @@ -4990,62 +4993,97 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, return gen; } +static _PyFrame * +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, + PyObject *locals, PyObject* const* args, + size_t argcount, PyObject *kwnames) +{ + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int size = code->co_nlocalsplus + code->co_stacksize + + FRAME_SPECIALS_SIZE; + PyObject **localsarray = _PyThreadState_PushLocals(tstate, size); + if (localsarray == NULL) { + return NULL; + } + _PyFrame * frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals); + if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { + _PyFrame_ClearSpecials(frame); + for (int i = 0; i < code->co_nlocalsplus; i++) { + Py_XDECREF(localsarray[i]); + } + return NULL; + } + frame->previous = tstate->frame; + tstate->frame = frame; + return frame; +} + +static int +_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) +{ + ++tstate->recursion_depth; + int err = 0; + PyCodeObject *code = frame->code; + PyObject **localsarray = ((PyObject **)frame)-code->co_nlocalsplus; + if (frame->frame_obj) { + PyFrameObject *f = frame->frame_obj; + frame->frame_obj = NULL; + if (Py_REFCNT(f) > 1) { + Py_DECREF(f); + _PyObject_GC_TRACK(f); + if (_PyFrame_TakeLocals(f)) { + err = -1; + } + goto exit; + } + Py_DECREF(f); + } + for (int i = 0; i < code->co_nlocalsplus; i++) { + Py_XDECREF(localsarray[i]); + } + _PyFrame_ClearSpecials(frame); +exit: + --tstate->recursion_depth; + tstate->frame = frame->previous; + _PyThreadState_PopLocals(tstate, localsarray); + return err; +} + 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) { return make_coro(tstate, con, locals, args, argcount, kwnames); } - int size = code->co_nlocalsplus + code->co_stacksize + - FRAME_SPECIALS_SIZE; - localsarray = _PyThreadState_PushLocals(tstate, size); - if (localsarray == NULL) { - return NULL; - } - PyFrameObject *f = _PyEval_MakeFrameVector( - tstate, con, locals, args, argcount, kwnames, localsarray); - if (f == NULL) { - _PyThreadState_PopLocals(tstate, localsarray); + _PyFrame * frame = _PyEvalFramePushAndInit( + tstate, con, locals, args, argcount, kwnames); + if (frame == NULL) { return NULL; } - PyObject *retval = _PyEval_EvalFrame(tstate, f, 0); - assert(f->f_frame->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(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); + PyObject *retval; + assert (tstate->interp->eval_frame != NULL); // { + /* Create the frame */ + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 0); + if (f == NULL) { + frame->frame_obj = NULL; + retval = NULL; } - } - else { - ++tstate->recursion_depth; - f->f_localsptr = NULL; - for (int i = 0; i < code->co_nlocalsplus; i++) { - Py_XDECREF(localsarray[i]); + else { + frame->frame_obj = f; + retval = _PyEval_EvalFrame(tstate, f, 0); + assert(frame->stackdepth == 0); } - _PyFrame *frame = f->f_frame; - Py_XDECREF(frame->locals); - Py_DECREF(frame->globals); - Py_DECREF(frame->builtins); - Py_DECREF(frame->code); - Py_DECREF(f); - --tstate->recursion_depth; + // } + if (_PyEvalFrameClearAndPop(tstate, frame)) { + Py_CLEAR(retval); } - _PyThreadState_PopLocals(tstate, localsarray); return retval; } diff --git a/Python/pystate.c b/Python/pystate.c index 5f37046aaedfb9..8d6631e3d9a9b4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -636,6 +636,7 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->interp = interp; tstate->pyframe = NULL; + tstate->frame = NULL; tstate->recursion_depth = 0; tstate->recursion_headroom = 0; tstate->stackcheck_counter = 0; From 861a8d9c047541bfe958d728767ac7c6c2c01646 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Jun 2021 17:44:50 +0100 Subject: [PATCH 07/40] Convert use of PyFrameObject to _PyFrame. --- Python/ceval.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 40c548cea27385..5ce59872acde94 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -81,7 +81,7 @@ 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 *); + _PyFrame *, const _Py_CODEUNIT *); static PyObject * special_lookup(PyThreadState *, PyObject *, _Py_Identifier *); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs); @@ -1888,7 +1888,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 { @@ -2087,7 +2087,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 { @@ -6322,7 +6322,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) + _PyFrame *frame, const _Py_CODEUNIT *next_instr) { PyObject *res; if (Py_REFCNT(v) == 2) { @@ -6337,14 +6337,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); @@ -6353,9 +6353,9 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, } case STORE_NAME: { - PyObject *names = f->f_frame->code->co_names; + PyObject *names = frame->code->co_names; PyObject *name = GETITEM(names, oparg); - PyObject *locals = f->f_frame->locals; + PyObject *locals = frame->locals; if (locals && PyDict_CheckExact(locals)) { PyObject *w = PyDict_GetItemWithError(locals, name); if ((w == v && PyDict_DelItem(locals, name) != 0) || From 35e793c883a8e4d826e2b64b165c5495f2c823ba Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 24 Jun 2021 12:04:10 +0100 Subject: [PATCH 08/40] Replace most remaining uses frame object in the interpreter with stack frame. --- Include/internal/pycore_frame.h | 9 +- Python/ceval.c | 203 ++++++++++++++++---------------- Tools/gdb/libpython.py | 4 +- 3 files changed, 113 insertions(+), 103 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 4b90edb69e2667..d058dc29e1c7bd 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -47,8 +47,13 @@ _PyFrame_GetLocalsArray(_PyFrame *frame) } /* Returns a borrowed reference */ -PyFrameObject * -_PyFrame_GetFrameObject(_PyFrame *frame); +static inline PyFrameObject * +_PyFrame_GetFrameObject(_PyFrame *frame) +{ + /* Will need to handle lazy frames */ + assert(frame->frame_obj != NULL); + return frame->frame_obj; +} #ifdef __cplusplus } diff --git a/Python/ceval.c b/Python/ceval.c index 5ce59872acde94..29ec2ca4f76e02 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -61,20 +61,20 @@ static int lltrace; static int prtrace(PyThreadState *, PyObject *, const char *); #endif static int call_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, + PyThreadState *, _PyFrame *, int, PyObject *); static int call_trace_protected(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *, + PyThreadState *, _PyFrame *, int, PyObject *); static void call_exc_trace(Py_tracefunc, PyObject *, - PyThreadState *, PyFrameObject *); + PyThreadState *, _PyFrame *); 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 *, _PyFrame *, int); +static void maybe_dtrace_line(_PyFrame *, PyTraceInfo *, int); +static void dtrace_function_entry(_PyFrame *); +static void dtrace_function_return(_PyFrame *); -static PyObject * import_name(PyThreadState *, PyFrameObject *, +static PyObject * import_name(PyThreadState *, _PyFrame *, PyObject *, PyObject *, PyObject *); static PyObject * import_from(PyThreadState *, PyObject *, PyObject *); static int import_all_from(PyThreadState *, PyObject *, PyObject *); @@ -1404,7 +1404,7 @@ eval_frame_handle_pending(PyThreadState *tstate) #define LOCALS() frame->locals PyObject* _Py_HOT_FUNCTION -_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) +_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag) { _Py_EnsureTstateNotNULL(tstate); @@ -1450,8 +1450,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) tstate->cframe = &cframe; /* push frame */ - tstate->pyframe = f; - frame = f->f_frame; + tstate->pyframe = fo; + frame = fo->f_frame; co = frame->code; if (cframe.use_tracing) { @@ -1471,7 +1471,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; @@ -1482,7 +1482,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; @@ -1491,7 +1491,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 */ @@ -1507,7 +1507,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) names = co->co_names; consts = co->co_consts; - localsplus = f->f_localsptr; + localsplus = fo->f_localsptr; first_instr = co->co_firstinstr; /* frame->lasti refers to the index of the last instruction, @@ -1534,7 +1534,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) We choose -1 rather than 0 to assist debugging. */ frame->stackdepth = -1; - f->f_state = FRAME_EXECUTING; + fo->f_state = FRAME_EXECUTING; #ifdef LLTRACE { @@ -1603,7 +1603,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) 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 */ @@ -1616,7 +1616,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) 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; @@ -2247,7 +2247,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(RETURN_VALUE): { retval = POP(); assert(EMPTY()); - f->f_state = FRAME_RETURNED; + fo->f_state = FRAME_RETURNED; frame->stackdepth = 0; goto exiting; } @@ -2405,7 +2405,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; } @@ -2435,7 +2435,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) /* and repeat... */ assert(frame->lasti > 0); frame->lasti -= 1; - f->f_state = FRAME_SUSPENDED; + fo->f_state = FRAME_SUSPENDED; frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2452,7 +2452,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } retval = w; } - f->f_state = FRAME_SUSPENDED; + fo->f_state = FRAME_SUSPENDED; frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -3495,7 +3495,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); @@ -3507,7 +3507,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(fo) < 0) { Py_DECREF(from); goto error; } @@ -3520,7 +3520,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) goto error; } err = import_all_from(tstate, locals, from); - PyFrame_LocalsToFast(f, 0); + PyFrame_LocalsToFast(fo, 0); Py_DECREF(from); if (err != 0) goto error; @@ -3829,7 +3829,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); } @@ -4268,7 +4268,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) default: fprintf(stderr, "XXX lineno: %d, opcode: %d\n", - PyFrame_GetLineNumber(f), + PyCode_Addr2Line(frame->code, frame->lasti*2), opcode); _PyErr_SetString(tstate, PyExc_SystemError, "unknown opcode"); goto error; @@ -4311,18 +4311,18 @@ MISS_WITH_CACHE(LOAD_GLOBAL) #endif /* Log traceback info. */ - PyTraceBack_Here(f); + PyTraceBack_Here(fo); 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(fo->f_state == FRAME_EXECUTING); + fo->f_state = FRAME_UNWINDING; call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, - tstate, f); + tstate, frame); } exception_unwind: - f->f_state = FRAME_UNWINDING; + fo->f_state = FRAME_UNWINDING; /* We can't use frame->lasti here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; @@ -4363,7 +4363,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) PUSH(exc); JUMPTO(handler); /* Resume normal execution */ - f->f_state = FRAME_EXECUTING; + fo->f_state = FRAME_EXECUTING; frame->lasti = handler; NEXTOPARG(); goto dispatch_opcode; @@ -4378,18 +4378,18 @@ MISS_WITH_CACHE(LOAD_GLOBAL) Py_XDECREF(o); } frame->stackdepth = 0; - f->f_state = FRAME_RAISED; + fo->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); } } @@ -4402,9 +4402,9 @@ MISS_WITH_CACHE(LOAD_GLOBAL) tstate->cframe->use_tracing = cframe.use_tracing; if (PyDTrace_FUNCTION_RETURN_ENABLED()) - dtrace_function_return(f); + dtrace_function_return(frame); _Py_LeaveRecursiveCall(tstate); - tstate->pyframe = f->f_back; + tstate->pyframe = fo->f_back; return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } @@ -4920,35 +4920,31 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, PyFrameObject * -_PyEval_MakeFrameVector(PyThreadState *tstate, +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)); PyCodeObject *code = (PyCodeObject *)con->fc_code; - _PyFrame *frame; - int owns = 0; + 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; + } + for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { + localsarray[i] = NULL; } - frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); _PyFrame_InitializeSpecials(frame, con, locals); /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, owns); + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 1); if (f == NULL) { return NULL; } + frame->frame_obj = f; if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { Py_DECREF(f); return NULL; @@ -4963,8 +4959,8 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, PyObject *kwnames) { assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); - PyFrameObject *f = _PyEval_MakeFrameVector( - tstate, con, locals, args, argcount, kwnames, NULL); + PyFrameObject *f = make_coro_frame( + tstate, con, locals, args, argcount, kwnames); if (f == NULL) { return NULL; } @@ -5400,7 +5396,7 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str) static void call_exc_trace(Py_tracefunc func, PyObject *self, PyThreadState *tstate, - PyFrameObject *f) + _PyFrame *f) { PyObject *type, *value, *traceback, *orig_traceback, *arg; int err; @@ -5430,7 +5426,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, _PyFrame *frame, int what, PyObject *arg) { PyObject *type, *value, *traceback; @@ -5451,9 +5447,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, _PyFrame *frame) { - PyCodeObject *code = frame->f_frame->code; + PyCodeObject *code = frame->code; if (trace_info->code != code) { trace_info->code = code; _PyCode_InitAddressRange(code, &trace_info->bounds); @@ -5462,7 +5458,7 @@ initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame) static int call_trace(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, PyFrameObject *frame, + PyThreadState *tstate, _PyFrame *frame, int what, PyObject *arg) { int result; @@ -5470,15 +5466,19 @@ call_trace(Py_tracefunc func, PyObject *obj, return 0; tstate->tracing++; tstate->cframe->use_tracing = 0; - if (frame->f_frame->lasti < 0) { - frame->f_lineno = frame->f_frame->code->co_firstlineno; + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + return -1; + } + if (frame->lasti < 0) { + f->f_lineno = frame->code->co_firstlineno; } else { initialize_trace_info(&tstate->trace_info, frame); - frame->f_lineno = _PyCode_CheckLineNumber(frame->f_frame->lasti*2, &tstate->trace_info.bounds); + f->f_lineno = _PyCode_CheckLineNumber(frame->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--; @@ -5505,7 +5505,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, _PyFrame *frame, int instr_prev) { int result = 0; @@ -5515,17 +5515,21 @@ 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_frame->lasti*2, &tstate->trace_info.bounds); - if (line != -1 && frame->f_trace_lines) { + int line = _PyCode_CheckLineNumber(frame->lasti*2, &tstate->trace_info.bounds); + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + return -1; + } + if (line != -1 && f->f_trace_lines) { /* Trace backward edges or first instruction of a new line */ - if (frame->f_frame->lasti < instr_prev || - (line != lastline && frame->f_frame->lasti*2 == tstate->trace_info.bounds.ar_start)) + if (frame->lasti < instr_prev || + (line != lastline && frame->lasti*2 == tstate->trace_info.bounds.ar_start)) { 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; @@ -5795,7 +5799,7 @@ PyEval_GetFuncDesc(PyObject *func) #define C_TRACE(x, call) \ if (use_tracing && tstate->c_profilefunc) { \ if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \ - tstate, tstate->pyframe, \ + tstate, tstate->frame, \ PyTrace_C_CALL, func)) { \ x = NULL; \ } \ @@ -5805,13 +5809,13 @@ if (use_tracing && tstate->c_profilefunc) { \ if (x == NULL) { \ call_trace_protected(tstate->c_profilefunc, \ tstate->c_profileobj, \ - tstate, tstate->pyframe, \ + tstate, tstate->frame, \ PyTrace_C_EXCEPTION, func); \ /* XXX should pass (type, value, tb) */ \ } else { \ if (call_trace(tstate->c_profilefunc, \ tstate->c_profileobj, \ - tstate, tstate->pyframe, \ + tstate, tstate->frame, \ PyTrace_C_RETURN, func)) { \ Py_DECREF(x); \ x = NULL; \ @@ -5983,21 +5987,21 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) } static PyObject * -import_name(PyThreadState *tstate, PyFrameObject *f, +import_name(PyThreadState *tstate, _PyFrame *frame, PyObject *name, PyObject *fromlist, PyObject *level) { _Py_IDENTIFIER(__import__); PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemIdWithError(f->f_frame->builtins, &PyId___import__); + import_func = _PyDict_GetItemIdWithError(frame->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_frame->locals; + PyObject *locals = frame->locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { int ilevel = _PyLong_AsInt(level); @@ -6006,7 +6010,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, } res = PyImport_ImportModuleLevelObject( name, - f->f_frame->globals, + frame->globals, locals == NULL ? Py_None :locals, fromlist, ilevel); @@ -6016,7 +6020,7 @@ import_name(PyThreadState *tstate, PyFrameObject *f, Py_INCREF(import_func); stack[0] = name; - stack[1] = f->f_frame->globals; + stack[1] = frame->globals; stack[2] = locals == NULL ? Py_None : locals; stack[3] = fromlist; stack[4] = level; @@ -6433,38 +6437,38 @@ _PyEval_RequestCodeExtraIndex(freefunc free) } static void -dtrace_function_entry(PyFrameObject *f) +dtrace_function_entry(_PyFrame *frame) { const char *filename; const char *funcname; int lineno; - PyCodeObject *code = f->f_frame->code; + PyCodeObject *code = frame->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyFrame_GetLineNumber(f); + lineno = PyCode_Addr2Line(frame->code, frame->lasti*2); PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); } static void -dtrace_function_return(PyFrameObject *f) +dtrace_function_return(_PyFrame *frame) { const char *filename; const char *funcname; int lineno; - PyCodeObject *code = f->f_frame->code; + PyCodeObject *code = frame->code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyFrame_GetLineNumber(f); + lineno = PyCode_Addr2Line(frame->code, frame->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(_PyFrame *frame, PyTraceInfo *trace_info, int instr_prev) { @@ -6474,25 +6478,26 @@ maybe_dtrace_line(PyFrameObject *frame, instruction window, reset the window. */ initialize_trace_info(trace_info, frame); - int line = _PyCode_CheckLineNumber(frame->f_frame->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_frame->lasti < instr_prev) { - if (line != -1) { - frame->f_lineno = line; - co_filename = PyUnicode_AsUTF8(frame->f_frame->code->co_filename); - if (!co_filename) + int lastline = _PyCode_CheckLineNumber(instr_prev*2, &trace_info->bounds); + int line = _PyCode_CheckLineNumber(frame->lasti*2, &trace_info->bounds); + if (line != -1) { + /* Trace backward edges or first instruction of a new line */ + if (frame->lasti < instr_prev || + (line != lastline && frame->lasti*2 == trace_info->bounds.ar_start)) + { + co_filename = PyUnicode_AsUTF8(frame->code->co_filename); + if (!co_filename) { co_filename = "?"; - co_name = PyUnicode_AsUTF8(frame->f_frame->code->co_name); - if (!co_name) + } + co_name = PyUnicode_AsUTF8(frame->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/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index c6d7ee86980751..8f4627a2d81918 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1637,13 +1637,13 @@ def is_gc_collect(self): def get_pyop(self): try: - f = self._gdbframe.read_var('f') + f = self._gdbframe.read_var('fo') frame = PyFrameObjectPtr.from_pyobject_ptr(f) 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(). + # of the caller, _PyEval_Vector(). orig_frame = frame caller = self._gdbframe.older() if caller: From 192094ef81fcbd0ad849fecdc9663b644bef2723 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 24 Jun 2021 14:37:56 +0100 Subject: [PATCH 09/40] Convert more uses of frameobject to frame. --- Include/internal/pycore_frame.h | 6 ++++ Objects/frameobject.c | 58 +++++++++++++++++++-------------- Python/ceval.c | 4 +-- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index d058dc29e1c7bd..c0dfbc5afba45e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -55,6 +55,12 @@ _PyFrame_GetFrameObject(_PyFrame *frame) return frame->frame_obj; } +int +_PyFrame_FastToLocalsWithError(_PyFrame *frame, int cleared); + +void +_PyFrame_LocalsToFast(_PyFrame *frame, int clear); + #ifdef __cplusplus } #endif diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 504abf655cc912..88142c3a62d37d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -915,11 +915,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, } static int -_PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) +_PyFrame_OpAlreadyRan(_PyFrame *frame, int opcode, int oparg) { const _Py_CODEUNIT *code = - (const _Py_CODEUNIT *)PyBytes_AS_STRING(f->f_frame->code->co_code); - for (int i = 0; i < f->f_frame->lasti; i++) { + (const _Py_CODEUNIT *)PyBytes_AS_STRING(frame->code->co_code); + for (int i = 0; i < frame->lasti; i++) { if (_Py_OPCODE(code[i]) == opcode && _Py_OPARG(code[i]) == oparg) { return 1; } @@ -928,25 +928,19 @@ _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) } int -PyFrame_FastToLocalsWithError(PyFrameObject *f) -{ +_PyFrame_FastToLocalsWithError(_PyFrame *frame, int cleared) { /* Merge fast locals into f->f_locals */ PyObject *locals; PyObject **fast; PyCodeObject *co; - - if (f == NULL) { - PyErr_BadInternalCall(); - return -1; - } - locals = f->f_frame->locals; + locals = frame->locals; if (locals == NULL) { - locals = f->f_frame->locals = PyDict_New(); + locals = frame->locals = PyDict_New(); if (locals == NULL) return -1; } - co = f->f_frame->code; - fast = f->f_localsptr; + co = frame->code; + fast = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocalsPlusKind kind = co->co_localspluskinds[i]; @@ -964,7 +958,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *value = fast[i]; - if (f->f_state != FRAME_CLEARED) { + if (!cleared) { if (kind & CO_FAST_FREE) { // The cell was set when the frame was created from // the function's closure. @@ -978,7 +972,7 @@ 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); } @@ -1011,6 +1005,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, f->f_state == FRAME_CLEARED); +} + void PyFrame_FastToLocals(PyFrameObject *f) { @@ -1024,21 +1028,18 @@ PyFrame_FastToLocals(PyFrameObject *f) } void -PyFrame_LocalsToFast(PyFrameObject *f, int clear) +_PyFrame_LocalsToFast(_PyFrame *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 = f->f_frame->locals; + locals = frame->locals; if (locals == NULL) return; - fast = f->f_localsptr; - co = f->f_frame->code; + fast = _PyFrame_GetLocalsArray(frame); + co = frame->code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1068,7 +1069,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; } @@ -1092,6 +1093,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_state == FRAME_CLEARED) { + return; + } + _PyFrame_LocalsToFast(f->f_frame, clear); +} + /* Clear out the free list */ void _PyFrame_ClearFreeList(PyInterpreterState *interp) diff --git a/Python/ceval.c b/Python/ceval.c index 29ec2ca4f76e02..c257415eda16a2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3507,7 +3507,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag case TARGET(IMPORT_STAR): { PyObject *from = POP(), *locals; int err; - if (PyFrame_FastToLocalsWithError(fo) < 0) { + if (_PyFrame_FastToLocalsWithError(frame, 0) < 0) { Py_DECREF(from); goto error; } @@ -3520,7 +3520,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag goto error; } err = import_all_from(tstate, locals, from); - PyFrame_LocalsToFast(fo, 0); + _PyFrame_LocalsToFast(frame, 0); Py_DECREF(from); if (err != 0) goto error; From 3f601a7691acbba1e8a40a1eb9d41935dae38027 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 29 Jun 2021 09:58:47 +0100 Subject: [PATCH 10/40] Move f_state from frame object to frame. --- Include/cpython/frameobject.h | 28 --------------------- Include/internal/pycore_frame.h | 28 +++++++++++++++++++++ Modules/_xxsubinterpretersmodule.c | 3 ++- Objects/frameobject.c | 14 +++++------ Objects/genobject.c | 40 +++++++++++++++--------------- Python/ceval.c | 18 +++++++------- 6 files changed, 66 insertions(+), 65 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 71c48b38bec925..9c5937be35cfa4 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -4,21 +4,6 @@ # 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 */ @@ -27,25 +12,12 @@ struct _frame { /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; 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; diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index c0dfbc5afba45e..4625c62db2522e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -4,6 +4,21 @@ extern "C" { #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; + typedef struct _py_frame { PyObject *globals; PyObject *builtins; @@ -13,9 +28,22 @@ typedef struct _py_frame { struct _py_frame *previous; int lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ + PyFrameState f_state; /* What state the frame is in */ PyObject *stack[1]; } _PyFrame; +static inline int _PyFrame_IsRunnable(_PyFrame *f) { + return f->f_state < FRAME_EXECUTING; +} + +static inline int _PyFrame_IsExecuting(_PyFrame *f) { + return f->f_state == FRAME_EXECUTING; +} + +static inline int _PyFrameHasCompleted(_PyFrame *f) { + return f->f_state > FRAME_EXECUTING; +} + #define FRAME_SPECIALS_SIZE ((sizeof(_PyFrame)-1)/sizeof(PyObject *)) int _PyFrame_TakeLocals(PyFrameObject *f); diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 9290255c66c3fa..1265b00acd7ded 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" @@ -1839,7 +1840,7 @@ _is_running(PyInterpreterState *interp) return 0; } - int executing = _PyFrame_IsExecuting(frame); + int executing = _PyFrame_IsExecuting(frame->f_frame); Py_DECREF(frame); return executing; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 88142c3a62d37d..6ba8cfb201df71 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -430,7 +430,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"); @@ -538,7 +538,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); } @@ -680,7 +680,7 @@ 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 = f->f_frame->code; @@ -700,7 +700,7 @@ frame_tp_clear(PyFrameObject *f) 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; @@ -880,7 +880,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, _PyFrame *frame, int owns) f->f_trace_opcodes = 0; f->f_gen = NULL; f->f_lineno = 0; - f->f_state = FRAME_CREATED; + f->f_frame->f_state = FRAME_CREATED; return f; } @@ -1012,7 +1012,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyErr_BadInternalCall(); return -1; } - return _PyFrame_FastToLocalsWithError(f->f_frame, f->f_state == FRAME_CLEARED); + return _PyFrame_FastToLocalsWithError(f->f_frame, f->f_frame->f_state == FRAME_CLEARED); } void @@ -1096,7 +1096,7 @@ _PyFrame_LocalsToFast(_PyFrame *frame, int clear) void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { - if (f == NULL || f->f_state == FRAME_CLEARED) { + if (f == NULL || f->f_frame->f_state == FRAME_CLEARED) { return; } _PyFrame_LocalsToFast(f->f_frame, clear); diff --git a/Objects/genobject.c b/Objects/genobject.c index 4bfc10d8095b15..fdd50b2e36b744 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -48,7 +48,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_frame == NULL || _PyFrameHasCompleted(gen->gi_frame->f_frame)) { /* Generator isn't paused, so no need to close */ return; } @@ -146,7 +146,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyObject *result; *presult = NULL; - if (f != NULL && _PyFrame_IsExecuting(f)) { + if (f != NULL && _PyFrame_IsExecuting(f->f_frame)) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -157,7 +157,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_ValueError, msg); return PYGEN_ERROR; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (f == NULL || _PyFrameHasCompleted(f->f_frame)) { if (PyCoro_CheckExact(gen) && !closing) { /* `gen` is an exhausted coroutine: raise an error, except when called from gen_close(), which should @@ -176,7 +176,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return PYGEN_ERROR; } - assert(_PyFrame_IsRunnable(f)); + assert(_PyFrame_IsRunnable(f->f_frame)); assert(f->f_frame->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; @@ -211,7 +211,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (!_PyFrameHasCompleted(f)) { + if (!_PyFrameHasCompleted(f->f_frame)) { *presult = result; return PYGEN_NEXT; } @@ -361,10 +361,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_frame->f_frame->f_state; + gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_frame->f_frame->f_state = state; Py_DECREF(yf); } if (err == 0) @@ -411,10 +411,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_frame->f_frame->f_state; + gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_frame->f_frame->f_state = state; Py_DECREF(yf); if (err < 0) return gen_send_ex(gen, Py_None, 1, 0); @@ -433,11 +433,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, tstate->pyframe = gen->gi_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_frame->f_frame->f_state; + gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); - gen->gi_frame->f_state = state; + gen->gi_frame->f_frame->f_state = state; tstate->pyframe = f; } else { /* `yf` is an iterator or a coroutine-like object. */ @@ -450,10 +450,10 @@ _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_frame->f_frame->f_state; + gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_frame->f_state = state; + gen->gi_frame->f_frame->f_state = state; Py_DECREF(meth); } Py_DECREF(yf); @@ -727,7 +727,7 @@ gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored)) if (gen->gi_frame == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame->f_frame)); } static PyGetSetDef gen_getsetlist[] = { @@ -963,7 +963,7 @@ cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored)) if (coro->cr_frame == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame->f_frame)); } static PyGetSetDef coro_getsetlist[] = { @@ -1879,7 +1879,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (f == NULL || _PyFrameHasCompleted(f->f_frame)) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index c257415eda16a2..5b46e12bf2d8e5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1534,7 +1534,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag We choose -1 rather than 0 to assist debugging. */ frame->stackdepth = -1; - fo->f_state = FRAME_EXECUTING; + frame->f_state = FRAME_EXECUTING; #ifdef LLTRACE { @@ -2247,7 +2247,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag case TARGET(RETURN_VALUE): { retval = POP(); assert(EMPTY()); - fo->f_state = FRAME_RETURNED; + frame->f_state = FRAME_RETURNED; frame->stackdepth = 0; goto exiting; } @@ -2435,7 +2435,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag /* and repeat... */ assert(frame->lasti > 0); frame->lasti -= 1; - fo->f_state = FRAME_SUSPENDED; + frame->f_state = FRAME_SUSPENDED; frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -2452,7 +2452,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag } retval = w; } - fo->f_state = FRAME_SUSPENDED; + frame->f_state = FRAME_SUSPENDED; frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; } @@ -4315,14 +4315,14 @@ MISS_WITH_CACHE(LOAD_GLOBAL) if (tstate->c_tracefunc != NULL) { /* Make sure state is set to FRAME_EXECUTING for tracing */ - assert(fo->f_state == FRAME_EXECUTING); - fo->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, frame); } exception_unwind: - fo->f_state = FRAME_UNWINDING; + frame->f_state = FRAME_UNWINDING; /* We can't use frame->lasti here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; @@ -4363,7 +4363,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) PUSH(exc); JUMPTO(handler); /* Resume normal execution */ - fo->f_state = FRAME_EXECUTING; + frame->f_state = FRAME_EXECUTING; frame->lasti = handler; NEXTOPARG(); goto dispatch_opcode; @@ -4378,7 +4378,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) Py_XDECREF(o); } frame->stackdepth = 0; - fo->f_state = FRAME_RAISED; + frame->f_state = FRAME_RAISED; exiting: if (cframe.use_tracing) { if (tstate->c_tracefunc) { From bd95c3297b5017ef3ee2d7072a0588af05de8a16 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 30 Jun 2021 15:48:36 +0100 Subject: [PATCH 11/40] Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation. --- Include/cpython/pystate.h | 1 - Include/internal/pycore_ceval.h | 1 + Include/internal/pycore_frame.h | 2 +- Modules/signalmodule.c | 11 ++++----- Objects/frameobject.c | 25 ++++++++++++++++----- Objects/genobject.c | 26 ++++++++++----------- Python/ceval.c | 40 ++++++++++++++++++++------------- Python/errors.c | 2 +- Python/pylifecycle.c | 2 +- Python/pystate.c | 11 ++++----- 10 files changed, 71 insertions(+), 50 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index f8a3063e89c235..d437db225b93ec 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -78,7 +78,6 @@ struct _ts { PyInterpreterState *interp; /* Borrowed reference to the current frame (it can be NULL) */ - PyFrameObject *pyframe; struct _py_frame *frame; int recursion_depth; int recursion_headroom; /* Allow 50 more calls to handle any errors. */ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index f573c3e5086807..2496d93c5c62bf 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -107,6 +107,7 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { #define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline() +struct _py_frame *_PyEval_GetFrame(void); #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 4625c62db2522e..a80e74e0bc1c45 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -84,7 +84,7 @@ _PyFrame_GetFrameObject(_PyFrame *frame) } int -_PyFrame_FastToLocalsWithError(_PyFrame *frame, int cleared); +_PyFrame_FastToLocalsWithError(_PyFrame *frame); void _PyFrame_LocalsToFast(_PyFrame *frame, int clear); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 6614acc6424884..60af628d8d2486 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->pyframe; - if (!frame) { - frame = Py_None; - } - + _PyFrame *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,8 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_WriteUnraisable(Py_None); continue; } - - PyObject *arglist = Py_BuildValue("(iO)", i, frame); + PyObject * f = frame == NULL ? Py_None : (PyObject *)_PyFrame_GetFrameObject(frame); + PyObject *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 6ba8cfb201df71..d08f0052a04ac1 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 */ @@ -102,6 +101,15 @@ 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; + } +} + /* Given the index of the effective opcode, scan back to construct the oparg with EXTENDED_ARG */ static unsigned int @@ -579,6 +587,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}, @@ -847,6 +856,9 @@ _PyFrame_TakeLocals(PyFrameObject *f) assert(f->f_frame->stackdepth == 0); Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); + if (f->f_frame->previous != NULL) { + f->f_back = (PyFrameObject *)Py_NewRef(f->f_frame->previous->frame_obj); + } if (copy == NULL) { for (int i = 0; i < f->f_frame->code->co_nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; @@ -873,7 +885,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, _PyFrame *frame, int owns) if (f == NULL) { return NULL; } - f->f_back = (PyFrameObject*)Py_XNewRef(tstate->pyframe); + f->f_back = NULL; f->f_trace = NULL; f->f_frame->stackdepth = 0; f->f_trace_lines = 1; @@ -928,7 +940,7 @@ _PyFrame_OpAlreadyRan(_PyFrame *frame, int opcode, int oparg) } int -_PyFrame_FastToLocalsWithError(_PyFrame *frame, int cleared) { +_PyFrame_FastToLocalsWithError(_PyFrame *frame) { /* Merge fast locals into f->f_locals */ PyObject *locals; PyObject **fast; @@ -958,7 +970,7 @@ _PyFrame_FastToLocalsWithError(_PyFrame *frame, int cleared) { PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *value = fast[i]; - if (!cleared) { + if (frame->f_state != FRAME_CLEARED) { if (kind & CO_FAST_FREE) { // The cell was set when the frame was created from // the function's closure. @@ -1012,7 +1024,7 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyErr_BadInternalCall(); return -1; } - return _PyFrame_FastToLocalsWithError(f->f_frame, f->f_frame->f_state == FRAME_CLEARED); + return _PyFrame_FastToLocalsWithError(f->f_frame); } void @@ -1153,6 +1165,9 @@ PyFrame_GetBack(PyFrameObject *frame) { assert(frame != NULL); PyFrameObject *back = frame->f_back; + if (back == NULL && frame->f_frame->previous != NULL) { + back = frame->f_frame->previous->frame_obj; + } Py_XINCREF(back); return back; } diff --git a/Objects/genobject.c b/Objects/genobject.c index fdd50b2e36b744..736435a56b8cbd 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -186,9 +186,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* Generators always return to their most recent caller, not * necessarily their creator. */ - Py_XINCREF(tstate->pyframe); assert(f->f_back == NULL); - f->f_back = tstate->pyframe; + f->f_frame->previous = tstate->frame; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -202,11 +201,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; + tstate->frame = f->f_frame->previous; /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - assert(f->f_back == tstate->pyframe); - Py_CLEAR(f->f_back); + f->f_frame->previous = NULL; + assert(f->f_back == NULL); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ @@ -423,14 +423,14 @@ _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->pyframe; + _PyFrame *f = tstate->frame; /* 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->pyframe = gen->gi_frame; + tstate->frame = gen->gi_frame->f_frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ PyFrameState state = gen->gi_frame->f_frame->f_state; @@ -438,7 +438,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); gen->gi_frame->f_frame->f_state = state; - tstate->pyframe = f; + tstate->frame = f; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; @@ -1153,11 +1153,11 @@ PyTypeObject _PyCoroWrapper_Type = { static PyObject * compute_cr_origin(int origin_depth) { - PyFrameObject *frame = PyEval_GetFrame(); + _PyFrame *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 */ @@ -1165,12 +1165,12 @@ 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->code; PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, - PyFrame_GetLineNumber(frame), + PyCode_Addr2Line(frame->code, frame->lasti*2), code->co_name); Py_DECREF(code); if (!frameinfo) { @@ -1178,7 +1178,7 @@ compute_cr_origin(int origin_depth) return NULL; } PyTuple_SET_ITEM(cr_origin, i, frameinfo); - frame = frame->f_back; + frame = frame->previous; } return cr_origin; diff --git a/Python/ceval.c b/Python/ceval.c index 5b46e12bf2d8e5..30e6059ce75f88 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1450,8 +1450,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag tstate->cframe = &cframe; /* push frame */ - tstate->pyframe = fo; frame = fo->f_frame; + tstate->frame = frame; co = frame->code; if (cframe.use_tracing) { @@ -3507,7 +3507,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag case TARGET(IMPORT_STAR): { PyObject *from = POP(), *locals; int err; - if (_PyFrame_FastToLocalsWithError(frame, 0) < 0) { + if (_PyFrame_FastToLocalsWithError(frame) < 0) { Py_DECREF(from); goto error; } @@ -4404,7 +4404,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) if (PyDTrace_FUNCTION_RETURN_ENABLED()) dtrace_function_return(frame); _Py_LeaveRecursiveCall(tstate); - tstate->pyframe = fo->f_back; + tstate->frame = frame->previous; return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } @@ -4967,9 +4967,7 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, 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); + assert(f->f_back == NULL); /* Create a new generator that owns the ready to run frame * and return that as the value. */ @@ -5676,19 +5674,29 @@ _PyEval_GetAsyncGenFinalizer(void) return tstate->async_gen_finalizer; } +_PyFrame * +_PyEval_GetFrame(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return tstate->frame; +} + PyFrameObject * PyEval_GetFrame(void) { PyThreadState *tstate = _PyThreadState_GET(); - return tstate->pyframe; + if (tstate->frame == NULL) { + return NULL; + } + return _PyFrame_GetFrameObject(tstate->frame); } PyObject * _PyEval_GetBuiltins(PyThreadState *tstate) { - PyFrameObject *frame = tstate->pyframe; + _PyFrame *frame = tstate->frame; if (frame != NULL) { - return frame->f_frame->builtins; + return frame->builtins; } return tstate->interp->builtins; } @@ -5719,17 +5727,17 @@ PyObject * PyEval_GetLocals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->pyframe; + _PyFrame *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_frame->locals; + PyObject *locals = current_frame->locals; assert(locals != NULL); return locals; } @@ -5738,22 +5746,22 @@ PyObject * PyEval_GetGlobals(void) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->pyframe; + _PyFrame *current_frame = tstate->frame; if (current_frame == NULL) { return NULL; } - return current_frame->f_frame->globals; + return current_frame->globals; } int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *current_frame = tstate->pyframe; + _PyFrame *current_frame = tstate->frame; int result = cf->cf_flags != 0; if (current_frame != NULL) { - const int codeflags = current_frame->f_frame->code->co_flags; + const int codeflags = current_frame->code->co_flags; const int compilerflags = codeflags & PyCF_MASK; if (compilerflags) { result = 1; diff --git a/Python/errors.c b/Python/errors.c index f5e207ca8dc0b9..99b6fffe459fb3 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1406,7 +1406,7 @@ _PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj) } if (exc_tb == NULL) { - PyFrameObject *frame = tstate->pyframe; + PyFrameObject *frame = PyThreadState_GetFrame(tstate); if (frame != NULL) { exc_tb = _PyTraceBack_FromFrame(NULL, frame); if (exc_tb == NULL) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2543136115aa70..d31a9c15fd1ec5 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2003,7 +2003,7 @@ Py_EndInterpreter(PyThreadState *tstate) if (tstate != _PyThreadState_GET()) { Py_FatalError("thread is not current"); } - if (tstate->pyframe != NULL) { + if (tstate->frame != NULL) { Py_FatalError("thread still has a frame"); } interp->finalizing = 1; diff --git a/Python/pystate.c b/Python/pystate.c index 8d6631e3d9a9b4..d378aec3f0f24a 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" @@ -635,7 +636,7 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->interp = interp; - tstate->pyframe = NULL; + tstate->frame = NULL; tstate->frame = NULL; tstate->recursion_depth = 0; tstate->recursion_headroom = 0; @@ -861,7 +862,7 @@ PyThreadState_Clear(PyThreadState *tstate) { int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; - if (verbose && tstate->pyframe != NULL) { + if (verbose && tstate->frame != NULL) { /* bpo-20526: After the main thread calls _PyRuntimeState_SetFinalizing() in Py_FinalizeEx(), threads must exit when trying to take the GIL. If a thread exit in the middle of @@ -1134,7 +1135,7 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - PyFrameObject *frame = tstate->pyframe; + PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->frame); Py_XINCREF(frame); return frame; } @@ -1255,7 +1256,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->pyframe; + _PyFrame *frame = t->frame; if (frame == NULL) { continue; } @@ -1263,7 +1264,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; From 9961abc2d27c2ccc522fecb8f7e7522766319881 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 30 Jun 2021 17:44:28 +0100 Subject: [PATCH 12/40] Add NULL check --- Python/pystate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Python/pystate.c b/Python/pystate.c index d378aec3f0f24a..4f2ec814395598 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1135,6 +1135,9 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); + if (tstate->frame == NULL) { + return NULL; + } PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->frame); Py_XINCREF(frame); return frame; From 32af707ce40bacd68e0c62a7e02b88c19d77f36f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 2 Jul 2021 10:52:19 +0100 Subject: [PATCH 13/40] Get lazy f_back working (it still leaks). --- Modules/_tracemalloc.c | 15 ++++++--------- Modules/signalmodule.c | 2 +- Objects/frameobject.c | 40 +++++++++++++++++++++++++--------------- Objects/genobject.c | 38 +++++++++++++++++++++++++------------- Python/ceval.c | 3 ++- 5 files changed, 59 insertions(+), 39 deletions(-) diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 04f6c243b5ca4d..a013f7dff81148 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 #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(_PyFrame *pyframe, frame_t *frame) { frame->filename = unknown_filename; - int lineno = PyFrame_GetLineNumber(pyframe); + int lineno = PyCode_Addr2Line(pyframe->code, pyframe->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->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); + _PyFrame *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); + _PyFrame *back = pyframe->previous; pyframe = back; } } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 60af628d8d2486..fd15d6a8eea7fd 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1818,7 +1818,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_WriteUnraisable(Py_None); continue; } - PyObject * f = frame == NULL ? Py_None : (PyObject *)_PyFrame_GetFrameObject(frame); + PyObject * f = frame == NULL ? Py_None : _PyFrame_GetFrameObject(frame); PyObject *arglist = Py_BuildValue("(iO)", i, f); PyObject *result; if (arglist) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d08f0052a04ac1..f12c83a26b00fa 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -108,6 +108,7 @@ frame_getback(PyFrameObject *f, void *closure) if (res == NULL) { Py_RETURN_NONE; } + return res; } /* Given the index of the effective opcode, @@ -623,23 +624,26 @@ frame_dealloc(PyFrameObject *f) /* Kill all local variables including specials, if we own them */ if (f->f_own_locals_memory) { + f->f_own_locals_memory = 0; + _PyFrame *frame = f->f_frame; /* Don't clear code object until the end */ - co = f->f_frame->code; - Py_CLEAR(f->f_frame->globals); - Py_CLEAR(f->f_frame->builtins); - Py_CLEAR(f->f_frame->locals); + co = frame->code; + frame->code = NULL; + Py_CLEAR(frame->globals); + Py_CLEAR(frame->builtins); + Py_CLEAR(frame->locals); 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_frame->stackdepth; i++) { - Py_XDECREF(f->f_frame->stack[i]); + /* stack */ + for (int i = 0; i < frame->stackdepth; i++) { + Py_CLEAR(frame->stack[i]); } + frame->stackdepth = 0; PyMem_Free(f->f_localsptr); - f->f_own_locals_memory = 0; } f->f_frame->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 @@ -664,11 +668,9 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - Py_VISIT(f->f_frame->globals); - Py_VISIT(f->f_frame->builtins); - Py_VISIT(f->f_frame->locals); - Py_VISIT(f->f_frame->code); - + if (f->f_own_locals_memory == 0) { + return 0; + } /* locals */ PyCodeObject *co = f->f_frame->code; for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -678,6 +680,11 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) for (int i = 0; i < f->f_frame->stackdepth; i++) { Py_VISIT(f->f_frame->stack[i]); } + + Py_VISIT(f->f_frame->globals); + Py_VISIT(f->f_frame->builtins); + Py_VISIT(f->f_frame->locals); + Py_VISIT(f->f_frame->code); return 0; } @@ -689,6 +696,7 @@ 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. */ + assert(f->f_own_locals_memory); f->f_frame->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); @@ -854,10 +862,12 @@ _PyFrame_TakeLocals(PyFrameObject *f) { assert(f->f_own_locals_memory == 0); assert(f->f_frame->stackdepth == 0); + assert(f->f_frame->frame_obj == NULL); Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); + assert(f->f_back == NULL); if (f->f_frame->previous != NULL) { - f->f_back = (PyFrameObject *)Py_NewRef(f->f_frame->previous->frame_obj); + f->f_back = (PyFrameObject *)Py_NewRef(_PyFrame_GetFrameObject(f->f_frame->previous)); } if (copy == NULL) { for (int i = 0; i < f->f_frame->code->co_nlocalsplus; i++) { diff --git a/Objects/genobject.c b/Objects/genobject.c index 736435a56b8cbd..c2b4b06860e069 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -181,8 +181,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); - gen->gi_frame->f_frame->stack[gen->gi_frame->f_frame->stackdepth] = result; - gen->gi_frame->f_frame->stackdepth++; + f->f_frame->stack[f->f_frame->stackdepth] = result; + f->f_frame->stackdepth++; /* Generators always return to their most recent caller, not * necessarily their creator. */ @@ -197,11 +197,16 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } + assert(f->f_frame->frame_obj == NULL); + Py_INCREF(f); + f->f_frame->frame_obj = f; result = _PyEval_EvalFrame(tstate, f, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; + f->f_frame->frame_obj = NULL; + Py_DECREF(f); - tstate->frame = f->f_frame->previous; + assert(tstate->frame == f->f_frame->previous); /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ @@ -329,13 +334,13 @@ PyObject * _PyGen_yf(PyGenObject *gen) { PyObject *yf = NULL; - PyFrameObject *f = gen->gi_frame; - if (f) { + if (gen->gi_frame) { + _PyFrame *frame = gen->gi_frame->f_frame; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (f->f_frame->lasti < 0) { + if (frame->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 */ @@ -343,10 +348,10 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(f->f_frame->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(frame->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - assert(f->f_frame->stackdepth > 0); - yf = f->f_frame->stack[f->f_frame->stackdepth-1]; + assert(frame->stackdepth > 0); + yf = frame->stack[frame->stackdepth-1]; Py_INCREF(yf); } @@ -423,22 +428,30 @@ _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(); - _PyFrame *f = tstate->frame; + _PyFrame *frame = gen->gi_frame->f_frame; + /* 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->f_frame; + frame->previous = tstate->frame; + 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_frame->f_state; gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; + assert(frame->frame_obj == NULL); + Py_INCREF(gen->gi_frame); + frame->frame_obj = gen->gi_frame; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); + frame->frame_obj = NULL; + Py_DECREF(gen->gi_frame); gen->gi_frame->f_frame->f_state = state; - tstate->frame = f; + tstate->frame = frame->previous; + frame->previous = NULL; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; @@ -1172,7 +1185,6 @@ compute_cr_origin(int origin_depth) code->co_filename, PyCode_Addr2Line(frame->code, frame->lasti*2), code->co_name); - Py_DECREF(code); if (!frameinfo) { Py_DECREF(cr_origin); return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 30e6059ce75f88..af7c8bc12b5ef7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4944,7 +4944,7 @@ make_coro_frame(PyThreadState *tstate, if (f == NULL) { return NULL; } - frame->frame_obj = f; + assert(frame->frame_obj == NULL); if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { Py_DECREF(f); return NULL; @@ -5031,6 +5031,7 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) } goto exit; } + assert(f->f_own_locals_memory == 0); Py_DECREF(f); } for (int i = 0; i < code->co_nlocalsplus; i++) { From ac7dbe8c9761d83b8da0e5ebea92a15c7e25716d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 2 Jul 2021 14:24:53 +0100 Subject: [PATCH 14/40] Use frames not frameobjects in sys._getframe() --- Include/internal/pycore_frame.h | 1 + Modules/signalmodule.c | 2 +- Python/sysmodule.c | 16 +++++++--------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index a80e74e0bc1c45..a3a5596a76f5f4 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -62,6 +62,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * static inline void _PyFrame_ClearSpecials(_PyFrame *frame) { + assert(frame->frame_obj == NULL); Py_XDECREF(frame->locals); Py_DECREF(frame->globals); Py_DECREF(frame->builtins); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index fd15d6a8eea7fd..60af628d8d2486 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1818,7 +1818,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_WriteUnraisable(Py_None); continue; } - PyObject * f = frame == NULL ? Py_None : _PyFrame_GetFrameObject(frame); + PyObject * f = frame == NULL ? Py_None : (PyObject *)_PyFrame_GetFrameObject(frame); PyObject *arglist = Py_BuildValue("(iO)", i, f); PyObject *result; if (arglist) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b71a096eaa0fcb..7087a696554008 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); + _PyFrame *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_NewRef((PyObject *)_PyFrame_GetFrameObject(frame)); } /*[clinic input] From f33d2915d446b44a28e87bfc22b7f4995cadfc5a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 2 Jul 2021 15:26:06 +0100 Subject: [PATCH 15/40] NULL out frame->previous when leaving frame. --- Objects/frameobject.c | 10 ++++++---- Objects/genobject.c | 5 +++-- Python/ceval.c | 2 ++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index f12c83a26b00fa..2096ec31743ff2 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -865,10 +865,6 @@ _PyFrame_TakeLocals(PyFrameObject *f) assert(f->f_frame->frame_obj == NULL); Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); - assert(f->f_back == NULL); - if (f->f_frame->previous != NULL) { - f->f_back = (PyFrameObject *)Py_NewRef(_PyFrame_GetFrameObject(f->f_frame->previous)); - } if (copy == NULL) { for (int i = 0; i < f->f_frame->code->co_nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; @@ -878,13 +874,19 @@ _PyFrame_TakeLocals(PyFrameObject *f) Py_XDECREF(f->f_frame->globals); Py_XDECREF(f->f_frame->locals); Py_XDECREF(f->f_frame->code); + f->f_frame->previous = NULL; PyErr_NoMemory(); return -1; } + assert(f->f_back == NULL); + if (f->f_frame->previous != NULL) { + f->f_back = (PyFrameObject *)Py_NewRef(_PyFrame_GetFrameObject(f->f_frame->previous)); + } memcpy(copy, f->f_localsptr, size); f->f_own_locals_memory = 1; f->f_localsptr = copy; f->f_frame = (_PyFrame *)(copy + f->f_frame->code->co_nlocalsplus); + f->f_frame->previous = NULL; return 0; } diff --git a/Objects/genobject.c b/Objects/genobject.c index c2b4b06860e069..846df3ae439ebd 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -436,7 +436,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, will be reported correctly to the user. */ /* XXX We should probably be updating the current frame somewhere in ceval.c. */ - frame->previous = tstate->frame; + _PyFrame *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'. */ @@ -450,7 +451,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, frame->frame_obj = NULL; Py_DECREF(gen->gi_frame); gen->gi_frame->f_frame->f_state = state; - tstate->frame = frame->previous; + tstate->frame = prev; frame->previous = NULL; } else { /* `yf` is an iterator or a coroutine-like object. */ diff --git a/Python/ceval.c b/Python/ceval.c index af7c8bc12b5ef7..243b27d373c1f9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5033,12 +5033,14 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) } assert(f->f_own_locals_memory == 0); Py_DECREF(f); + assert(_PyObject_IsFreed((PyObject *)f) || Py_REFCNT(f) == 0); } for (int i = 0; i < code->co_nlocalsplus; i++) { Py_XDECREF(localsarray[i]); } _PyFrame_ClearSpecials(frame); exit: + assert(frame->frame_obj == NULL); --tstate->recursion_depth; tstate->frame = frame->previous; _PyThreadState_PopLocals(tstate, localsarray); From 5c23a368b406f0628988b6f14edca29e255543f6 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 5 Jul 2021 10:56:16 +0100 Subject: [PATCH 16/40] Frames now include nlocalspuls, so they have valid layout after code has been deleted. --- Include/internal/pycore_frame.h | 6 ++++-- Objects/frameobject.c | 8 ++++---- Python/ceval.c | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index a3a5596a76f5f4..12c62996b254f5 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -28,6 +28,7 @@ typedef struct _py_frame { struct _py_frame *previous; int 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]; } _PyFrame; @@ -49,12 +50,13 @@ static inline int _PyFrameHasCompleted(_PyFrame *f) { int _PyFrame_TakeLocals(PyFrameObject *f); static inline void -_PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals) +_PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals, int nlocalsplus) { frame->code = (PyCodeObject *)Py_NewRef(con->fc_code); frame->builtins = Py_NewRef(con->fc_builtins); frame->globals = Py_NewRef(con->fc_globals); frame->locals = Py_XNewRef(locals); + frame->nlocalsplus = nlocalsplus; frame->frame_obj = NULL; frame->lasti = -1; } @@ -72,7 +74,7 @@ _PyFrame_ClearSpecials(_PyFrame *frame) static inline PyObject** _PyFrame_GetLocalsArray(_PyFrame *frame) { - return ((PyObject **)frame) - frame->code->co_nlocalsplus; + return ((PyObject **)frame) - frame->nlocalsplus; } /* Returns a borrowed reference */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 2096ec31743ff2..91a9ecc0e2156d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -817,7 +817,7 @@ allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) localsarray[i] = NULL; } _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); - _PyFrame_InitializeSpecials(frame, con, locals); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); return frame; } @@ -852,7 +852,7 @@ frame_alloc(_PyFrame *frame, int owns) _Py_NewReference((PyObject *)f); } f->f_frame = frame; - f->f_localsptr = ((PyObject **)frame) - frame->code->co_nlocalsplus; + f->f_localsptr = ((PyObject **)frame) - frame->nlocalsplus; f->f_own_locals_memory = owns; return f; } @@ -866,7 +866,7 @@ _PyFrame_TakeLocals(PyFrameObject *f) Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); PyObject **copy = PyMem_Malloc(size); if (copy == NULL) { - for (int i = 0; i < f->f_frame->code->co_nlocalsplus; i++) { + for (int i = 0; i < f->f_frame->nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; Py_XDECREF(o); } @@ -885,7 +885,7 @@ _PyFrame_TakeLocals(PyFrameObject *f) memcpy(copy, f->f_localsptr, size); f->f_own_locals_memory = 1; f->f_localsptr = copy; - f->f_frame = (_PyFrame *)(copy + f->f_frame->code->co_nlocalsplus); + f->f_frame = (_PyFrame *)(copy + f->f_frame->nlocalsplus); f->f_frame->previous = NULL; return 0; } diff --git a/Python/ceval.c b/Python/ceval.c index 243b27d373c1f9..e8909ea55ded1d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4938,7 +4938,7 @@ make_coro_frame(PyThreadState *tstate, localsarray[i] = NULL; } _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); - _PyFrame_InitializeSpecials(frame, con, locals); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); /* Create the frame */ PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 1); if (f == NULL) { @@ -5000,7 +5000,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, return NULL; } _PyFrame * frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); - _PyFrame_InitializeSpecials(frame, con, locals); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { _PyFrame_ClearSpecials(frame); for (int i = 0; i < code->co_nlocalsplus; i++) { From 910e991e9f8d3a2735ab07235c0ab8fbb34d9f97 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 5 Jul 2021 15:38:13 +0100 Subject: [PATCH 17/40] Move ownership of frame in generator from frame object ot generator object. --- Include/genobject.h | 4 +- Include/internal/pycore_frame.h | 5 +- Objects/frameobject.c | 17 ++- Objects/genobject.c | 186 +++++++++++++++++++++----------- Python/ceval.c | 33 +++--- Python/pystate.c | 1 - 6 files changed, 163 insertions(+), 83 deletions(-) diff --git a/Include/genobject.h b/Include/genobject.h index 094d4e14fbe7cf..41efd32c4616c1 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 _py_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_frame.h b/Include/internal/pycore_frame.h index 12c62996b254f5..c3a75ac42099c5 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -64,7 +64,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * static inline void _PyFrame_ClearSpecials(_PyFrame *frame) { - assert(frame->frame_obj == NULL); + Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->locals); Py_DECREF(frame->globals); Py_DECREF(frame->builtins); @@ -86,6 +86,9 @@ _PyFrame_GetFrameObject(_PyFrame *frame) return frame->frame_obj; } +int +_PyFrame_Clear(_PyFrame * frame); + int _PyFrame_FastToLocalsWithError(_PyFrame *frame); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 91a9ecc0e2156d..1156e7015e4b11 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -671,8 +671,10 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) if (f->f_own_locals_memory == 0) { return 0; } + assert(f->f_frame->frame_obj == NULL); /* locals */ PyCodeObject *co = f->f_frame->code; + assert(f->f_localsptr == _PyFrame_GetLocalsArray(f->f_frame)); for (int i = 0; i < co->co_nlocalsplus; i++) { Py_VISIT(f->f_localsptr[i]); } @@ -702,6 +704,7 @@ frame_tp_clear(PyFrameObject *f) Py_CLEAR(f->f_trace); PyCodeObject *co = f->f_frame->code; /* locals */ + assert(f->f_localsptr == _PyFrame_GetLocalsArray(f->f_frame)); for (int i = 0; i < co->co_nlocalsplus; i++) { Py_CLEAR(f->f_localsptr[i]); } @@ -861,11 +864,16 @@ int _PyFrame_TakeLocals(PyFrameObject *f) { assert(f->f_own_locals_memory == 0); - assert(f->f_frame->stackdepth == 0); - assert(f->f_frame->frame_obj == NULL); - Py_ssize_t size = ((char*)f->f_frame->stack)-((char *)f->f_localsptr); + _PyFrame *frame = f->f_frame; + assert(frame->frame_obj == NULL); + Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); PyObject **copy = PyMem_Malloc(size); if (copy == NULL) { + for (int i = 0; i < f->f_frame->stackdepth; i++) { + PyObject *o = f->f_frame->stack[i]; + Py_XDECREF(o); + } + f->f_frame->stackdepth = 0; for (int i = 0; i < f->f_frame->nlocalsplus; i++) { PyObject *o = f->f_localsptr[i]; Py_XDECREF(o); @@ -887,6 +895,9 @@ _PyFrame_TakeLocals(PyFrameObject *f) f->f_localsptr = copy; f->f_frame = (_PyFrame *)(copy + f->f_frame->nlocalsplus); f->f_frame->previous = NULL; + if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { + _PyObject_GC_TRACK((PyObject *)f); + } return 0; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 846df3ae439ebd..b758e302bdd8a5 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -32,10 +32,28 @@ 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); + _PyFrame *frame = gen->gi_xframe; + if (frame != NULL) { + assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + Py_VISIT(frame->frame_obj); + /* 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]); + } + + Py_VISIT(frame->globals); + Py_VISIT(frame->builtins); + Py_VISIT(frame->locals); + Py_VISIT(frame->code); + } /* 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); @@ -48,7 +66,7 @@ _PyGen_Finalize(PyObject *self) PyObject *res = NULL; PyObject *error_type, *error_value, *error_traceback; - if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame->f_frame)) { + if (gen->gi_xframe == NULL || _PyFrameHasCompleted(gen->gi_xframe)) { /* Generator isn't paused, so no need to close */ return; } @@ -80,7 +98,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_frame->lasti == -1) + gen->gi_xframe->lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -123,9 +141,15 @@ 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); + _PyFrame *frame = gen->gi_xframe; + if (frame != NULL) { + if (frame->frame_obj != NULL) { + frame->frame_obj->f_gen = NULL; + } + gen->gi_xframe = NULL; + frame->previous = NULL; + _PyFrame_Clear(frame); + PyMem_Free(_PyFrame_GetLocalsArray(frame)); } if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin); @@ -142,11 +166,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = gen->gi_frame; + _PyFrame *frame = gen->gi_xframe; PyObject *result; *presult = NULL; - if (f != NULL && _PyFrame_IsExecuting(f->f_frame)) { + if (frame != NULL && _PyFrame_IsExecuting(frame)) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -157,7 +181,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_ValueError, msg); return PYGEN_ERROR; } - if (f == NULL || _PyFrameHasCompleted(f->f_frame)) { + 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 @@ -176,18 +200,18 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return PYGEN_ERROR; } - assert(_PyFrame_IsRunnable(f->f_frame)); - assert(f->f_frame->lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + assert(_PyFrame_IsRunnable(frame)); + assert(frame->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); - f->f_frame->stack[f->f_frame->stackdepth] = result; - f->f_frame->stackdepth++; + frame->stack[frame->stackdepth] = result; + frame->stackdepth++; /* Generators always return to their most recent caller, not * necessarily their creator. */ - assert(f->f_back == NULL); - f->f_frame->previous = tstate->frame; + assert(frame->frame_obj->f_back == NULL); + frame->previous = tstate->frame; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -197,26 +221,27 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } - assert(f->f_frame->frame_obj == NULL); - Py_INCREF(f); - f->f_frame->frame_obj = f; - result = _PyEval_EvalFrame(tstate, f, exc); + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + result = NULL; + } + else { + result = _PyEval_EvalFrame(tstate, f, exc); + } tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - f->f_frame->frame_obj = NULL; - Py_DECREF(f); assert(tstate->frame == f->f_frame->previous); /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - f->f_frame->previous = NULL; + frame->previous = NULL; assert(f->f_back == NULL); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (!_PyFrameHasCompleted(f->f_frame)) { + if (!_PyFrameHasCompleted(frame)) { *presult = result; return PYGEN_NEXT; } @@ -251,10 +276,16 @@ 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); + if (f) { + f->f_gen = NULL; + } + gen->gi_xframe = NULL; + if (_PyFrame_Clear(frame)) { + Py_XDECREF(result); + result = NULL; + } + PyMem_Free(_PyFrame_GetLocalsArray(frame)); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } @@ -335,8 +366,8 @@ _PyGen_yf(PyGenObject *gen) { PyObject *yf = NULL; - if (gen->gi_frame) { - _PyFrame *frame = gen->gi_frame->f_frame; + if (gen->gi_xframe) { + _PyFrame *frame = gen->gi_xframe; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); @@ -366,10 +397,10 @@ gen_close(PyGenObject *gen, PyObject *args) int err = 0; if (yf) { - PyFrameState state = gen->gi_frame->f_frame->f_state; - gen->gi_frame->f_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_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(yf); } if (err == 0) @@ -416,10 +447,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_frame->f_state; - gen->gi_frame->f_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_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); @@ -428,7 +459,7 @@ _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(); - _PyFrame *frame = gen->gi_frame->f_frame; + _PyFrame *frame = gen->gi_xframe; /* Since we are fast-tracking things by skipping the eval loop, @@ -441,16 +472,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, 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_frame->f_state; - gen->gi_frame->f_frame->f_state = FRAME_EXECUTING; - assert(frame->frame_obj == NULL); - Py_INCREF(gen->gi_frame); - frame->frame_obj = gen->gi_frame; + 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); - frame->frame_obj = NULL; - Py_DECREF(gen->gi_frame); - gen->gi_frame->f_frame->f_state = state; + gen->gi_xframe->f_state = state; tstate->frame = prev; frame->previous = NULL; } else { @@ -464,24 +490,24 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } - PyFrameState state = gen->gi_frame->f_frame->f_state; - gen->gi_frame->f_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_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_frame->stackdepth > 0); - gen->gi_frame->f_frame->stackdepth--; - ret = gen->gi_frame->f_frame->stack[gen->gi_frame->f_frame->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_frame->lasti >= 0); - gen->gi_frame->f_frame->lasti += 1; + assert(gen->gi_xframe->lasti >= 0); + gen->gi_xframe->lasti += 1; if (_PyGen_FetchStopIterationValue(&val) == 0) { ret = gen_send(gen, val); Py_DECREF(val); @@ -738,10 +764,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->f_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[] = { @@ -752,11 +796,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 */ }; @@ -839,8 +883,16 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, Py_DECREF(f); return NULL; } - gen->gi_frame = f; + + /* 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; f->f_gen = (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; @@ -974,12 +1026,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->f_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")}, @@ -988,11 +1047,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 */ @@ -1362,6 +1421,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, @@ -1370,11 +1434,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}, @@ -1882,7 +1946,7 @@ static PyObject * async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) { PyGenObject *gen = (PyGenObject*)o->agt_gen; - PyFrameObject *f = gen->gi_frame; + _PyFrame *frame = gen->gi_xframe; PyObject *retval; if (o->agt_state == AWAITABLE_STATE_CLOSED) { @@ -1892,7 +1956,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (f == NULL || _PyFrameHasCompleted(f->f_frame)) { + if (frame == NULL || _PyFrameHasCompleted(frame)) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index e8909ea55ded1d..5d3e35fb41fa9a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4949,6 +4949,7 @@ make_coro_frame(PyThreadState *tstate, Py_DECREF(f); return NULL; } + _PyObject_GC_TRACK(f); return f; } @@ -4982,8 +4983,6 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, return NULL; } - _PyObject_GC_TRACK(f); - return gen; } @@ -5013,37 +5012,41 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, return frame; } -static int -_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) +int +_PyFrame_Clear(_PyFrame * frame) { - ++tstate->recursion_depth; - int err = 0; - PyCodeObject *code = frame->code; - PyObject **localsarray = ((PyObject **)frame)-code->co_nlocalsplus; + PyObject **localsarray = ((PyObject **)frame)-frame->nlocalsplus; if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; if (Py_REFCNT(f) > 1) { Py_DECREF(f); - _PyObject_GC_TRACK(f); if (_PyFrame_TakeLocals(f)) { - err = -1; + return -1; } - goto exit; + return 0; } - assert(f->f_own_locals_memory == 0); Py_DECREF(f); assert(_PyObject_IsFreed((PyObject *)f) || Py_REFCNT(f) == 0); } - for (int i = 0; i < code->co_nlocalsplus; i++) { + assert(frame->stackdepth == 0); + for (int i = 0; i < frame->nlocalsplus; i++) { Py_XDECREF(localsarray[i]); } _PyFrame_ClearSpecials(frame); -exit: + return 0; +} + +static int +_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) +{ + ++tstate->recursion_depth; + assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + int err = _PyFrame_Clear(frame); assert(frame->frame_obj == NULL); --tstate->recursion_depth; tstate->frame = frame->previous; - _PyThreadState_PopLocals(tstate, localsarray); + _PyThreadState_PopLocals(tstate, _PyFrame_GetLocalsArray(frame)); return err; } diff --git a/Python/pystate.c b/Python/pystate.c index 4f2ec814395598..09a5178e2fb552 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -636,7 +636,6 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->interp = interp; - tstate->frame = NULL; tstate->frame = NULL; tstate->recursion_depth = 0; tstate->recursion_headroom = 0; From 22e1c9b9673b60ccb2080f9824f0e78b6a2f771e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 6 Jul 2021 09:01:02 +0100 Subject: [PATCH 18/40] Remove localsptr field from frame object. --- Include/cpython/frameobject.h | 1 - Lib/test/test_sys.py | 2 +- Objects/frameobject.c | 25 ++++++++++++------------- Objects/typeobject.c | 4 ++-- Python/ceval.c | 2 +- Tools/gdb/libpython.py | 9 +++++++-- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 9c5937be35cfa4..63fda1e0872d0e 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -15,7 +15,6 @@ struct _frame { 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 */ }; /* Standard object interface */ diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 19f85d21ba5893..fdeb5891649a6d 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1275,7 +1275,7 @@ class C(object): pass # frame import inspect x = inspect.currentframe() - check(x, size('4Pi4cP')) + check(x, size('4Pi3c')) # function def func(): pass check(func, size('14P')) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 1156e7015e4b11..a9abd72dd2aa57 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -632,15 +632,16 @@ frame_dealloc(PyFrameObject *f) Py_CLEAR(frame->globals); Py_CLEAR(frame->builtins); Py_CLEAR(frame->locals); + PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_CLEAR(f->f_localsptr[i]); + Py_CLEAR(locals[i]); } /* stack */ for (int i = 0; i < frame->stackdepth; i++) { Py_CLEAR(frame->stack[i]); } frame->stackdepth = 0; - PyMem_Free(f->f_localsptr); + PyMem_Free(locals); } f->f_frame->stackdepth = 0; Py_CLEAR(f->f_back); @@ -673,10 +674,9 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) } assert(f->f_frame->frame_obj == NULL); /* locals */ - PyCodeObject *co = f->f_frame->code; - assert(f->f_localsptr == _PyFrame_GetLocalsArray(f->f_frame)); - for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_VISIT(f->f_localsptr[i]); + PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); + for (int i = 0; i < f->f_frame->nlocalsplus; i++) { + Py_VISIT(locals[i]); } /* stack */ for (int i = 0; i < f->f_frame->stackdepth; i++) { @@ -704,9 +704,9 @@ frame_tp_clear(PyFrameObject *f) Py_CLEAR(f->f_trace); PyCodeObject *co = f->f_frame->code; /* locals */ - assert(f->f_localsptr == _PyFrame_GetLocalsArray(f->f_frame)); - 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 */ @@ -855,7 +855,6 @@ frame_alloc(_PyFrame *frame, int owns) _Py_NewReference((PyObject *)f); } f->f_frame = frame; - f->f_localsptr = ((PyObject **)frame) - frame->nlocalsplus; f->f_own_locals_memory = owns; return f; } @@ -868,6 +867,7 @@ _PyFrame_TakeLocals(PyFrameObject *f) assert(frame->frame_obj == NULL); Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); PyObject **copy = PyMem_Malloc(size); + PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); if (copy == NULL) { for (int i = 0; i < f->f_frame->stackdepth; i++) { PyObject *o = f->f_frame->stack[i]; @@ -875,7 +875,7 @@ _PyFrame_TakeLocals(PyFrameObject *f) } f->f_frame->stackdepth = 0; for (int i = 0; i < f->f_frame->nlocalsplus; i++) { - PyObject *o = f->f_localsptr[i]; + PyObject *o = locals[i]; Py_XDECREF(o); } Py_XDECREF(f->f_frame->builtins); @@ -890,9 +890,8 @@ _PyFrame_TakeLocals(PyFrameObject *f) if (f->f_frame->previous != NULL) { f->f_back = (PyFrameObject *)Py_NewRef(_PyFrame_GetFrameObject(f->f_frame->previous)); } - memcpy(copy, f->f_localsptr, size); + memcpy(copy, locals, size); f->f_own_locals_memory = 1; - f->f_localsptr = copy; f->f_frame = (_PyFrame *)(copy + f->f_frame->nlocalsplus); f->f_frame->previous = NULL; if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 45944075a50698..4badbe13a37ba4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8863,7 +8863,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, return -1; } - PyObject *firstarg = f->f_localsptr[0]; + PyObject *firstarg = _PyFrame_GetLocalsArray(f->f_frame)[0]; // The first argument might be a cell. if (firstarg != NULL && (co->co_localspluskinds[0] & CO_FAST_CELL)) { // "firstarg" is a cell here unless (very unlikely) super() @@ -8888,7 +8888,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/Python/ceval.c b/Python/ceval.c index 5d3e35fb41fa9a..8cf5aa7292c803 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1507,7 +1507,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag names = co->co_names; consts = co->co_consts; - localsplus = fo->f_localsptr; + localsplus = _PyFrame_GetLocalsArray(frame); first_instr = co->co_firstinstr; /* frame->lasti refers to the index of the last instruction, diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 8f4627a2d81918..2c96801f9faa9a 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -879,9 +879,11 @@ def iter_locals(self): if self.is_optimized_out(): return - f_localsplus = self.field('f_localsptr') + obj_ptr_ptr = gdb.lookup_type("PyObject").pointer().pointer() + base = self.field('f_frame').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]) @@ -900,6 +902,9 @@ def _f_builtins(self): def _f_code(self): return self._f_special("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("lasti", int_from_int) From f84a3f05c660f764363f84ce01e0a577057b81bf Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 6 Jul 2021 10:03:16 +0100 Subject: [PATCH 19/40] Add new _PyEval_EvalNoFrame function for evaluating frames directly. --- Include/internal/pycore_ceval.h | 4 ++ Objects/frameobject.c | 4 +- Objects/genobject.c | 8 +-- Python/ceval.c | 16 ++--- Tools/gdb/libpython.py | 113 +++++++++++++++++++++++++++----- 5 files changed, 110 insertions(+), 35 deletions(-) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 2496d93c5c62bf..e740f8b2883748 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -46,6 +46,10 @@ _PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) return tstate->interp->eval_frame(tstate, f, throwflag); } +PyObject* +_PyEval_EvalNoFrame(PyThreadState *tstate, + struct _py_frame *frame, int throwflag); + extern PyObject * _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *desc, PyObject *locals, diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a9abd72dd2aa57..4caa139e2702ab 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -702,7 +702,7 @@ frame_tp_clear(PyFrameObject *f) f->f_frame->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); - PyCodeObject *co = f->f_frame->code; + /* locals */ PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); for (int i = 0; i < f->f_frame->nlocalsplus; i++) { @@ -1188,7 +1188,7 @@ PyFrame_GetBack(PyFrameObject *frame) assert(frame != NULL); PyFrameObject *back = frame->f_back; if (back == NULL && frame->f_frame->previous != NULL) { - back = frame->f_frame->previous->frame_obj; + back = _PyFrame_GetFrameObject(frame->f_frame->previous); } Py_XINCREF(back); return back; diff --git a/Objects/genobject.c b/Objects/genobject.c index b758e302bdd8a5..cc0a89cdd09330 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -221,13 +221,9 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } + PyFrameObject *f = _PyFrame_GetFrameObject(frame); - if (f == NULL) { - result = NULL; - } - else { - result = _PyEval_EvalFrame(tstate, f, exc); - } + result = _PyEval_EvalNoFrame(tstate, frame, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 8cf5aa7292c803..3bbe4e06122b9b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1404,7 +1404,7 @@ eval_frame_handle_pending(PyThreadState *tstate) #define LOCALS() frame->locals PyObject* _Py_HOT_FUNCTION -_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag) +_PyEval_EvalNoFrame(PyThreadState *tstate, _PyFrame *frame, int throwflag) { _Py_EnsureTstateNotNULL(tstate); @@ -1421,7 +1421,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ PyObject **localsplus; - _PyFrame *frame; PyObject *retval = NULL; /* Return value */ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker; PyCodeObject *co; @@ -1450,7 +1449,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *fo, int throwflag tstate->cframe = &cframe; /* push frame */ - frame = fo->f_frame; tstate->frame = frame; co = frame->code; @@ -4311,7 +4309,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) #endif /* Log traceback info. */ - PyTraceBack_Here(fo); + PyTraceBack_Here(_PyFrame_GetFrameObject(frame)); if (tstate->c_tracefunc != NULL) { /* Make sure state is set to FRAME_EXECUTING for tracing */ @@ -4409,11 +4407,11 @@ MISS_WITH_CACHE(LOAD_GLOBAL) } -//PyObject* _Py_HOT_FUNCTION -//_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) -//{ -// return _PyEval_EvalNoFrame(tstate, f->f_frame, throwflag); -//} +PyObject* +_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) +{ + return _PyEval_EvalNoFrame(tstate, f->f_frame, throwflag); +} static void format_missing(PyThreadState *tstate, const char *kind, diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 2c96801f9faa9a..2dcba4c779886f 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -101,7 +101,7 @@ def _sizeof_void_p(): FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)' UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame' -EVALFRAME = '_PyEval_EvalFrameDefault' +EVALFRAME = '_PyEval_EvalNoFrame' class NullPyObjectPtr(RuntimeError): pass @@ -860,17 +860,96 @@ class PyFrameObjectPtr(PyObjectPtr): def __init__(self, gdbval, cast_to=None): 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 = 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 @@ -880,7 +959,7 @@ def iter_locals(self): return obj_ptr_ptr = gdb.lookup_type("PyObject").pointer().pointer() - base = self.field('f_frame').cast(obj_ptr_ptr) + 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(localsplus[i]) @@ -890,8 +969,7 @@ def iter_locals(self): yield (pyop_name, pyop_value) def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr): - f_frame = self.field('f_frame') - return convert(f_frame[name]) + return convert(self._gdbval[name]) def _f_globals(self): return self._f_special("globals") @@ -964,11 +1042,6 @@ def current_line_num(self): ''' 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: @@ -1025,6 +1098,9 @@ def write_repr(self, out, visited): 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) @@ -1642,22 +1718,23 @@ def is_gc_collect(self): def get_pyop(self): try: - f = self._gdbframe.read_var('fo') - 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 + # 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 - except ValueError: + except ValueError as v: + print("ERROR:", v.args, file=sys.stderr) return None @classmethod @@ -1703,7 +1780,7 @@ def print_summary(self): if self.is_evalframe(): pyop = self.get_pyop() if pyop: - line = pyop.get_truncated_repr(MAX_OUTPUT_LEN) + line = PyObjectPtr.get_truncated_repr(pyop, MAX_OUTPUT_LEN) write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line)) if not pyop.is_optimized_out(): line = pyop.current_line() From 1180a44282888cd8c2b9801000da32e412a7fafa Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 6 Jul 2021 12:39:08 +0100 Subject: [PATCH 20/40] Allow for lazily created frames. --- Include/cpython/frameobject.h | 2 +- Include/internal/pycore_frame.h | 16 +++++--- Modules/signalmodule.c | 12 +++++- Objects/frameobject.c | 49 ++++++++-------------- Objects/genobject.c | 18 +++----- Python/ceval.c | 73 ++++++++++++++++++++++++++++----- Python/pystate.c | 3 ++ Python/sysmodule.c | 2 +- 8 files changed, 110 insertions(+), 65 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 63fda1e0872d0e..caf814daeeb0ec 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -28,7 +28,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* -_PyFrame_New_NoTrack(PyThreadState *, struct _py_frame *, int); +_PyFrame_New_NoTrack(struct _py_frame *, int); /* The rest of the interface is specific for frame objects */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index c3a75ac42099c5..268da474a2fbf0 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -47,7 +47,7 @@ static inline int _PyFrameHasCompleted(_PyFrame *f) { #define FRAME_SPECIALS_SIZE ((sizeof(_PyFrame)-1)/sizeof(PyObject *)) -int _PyFrame_TakeLocals(PyFrameObject *f); +void _PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *locals); static inline void _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals, int nlocalsplus) @@ -77,17 +77,23 @@ _PyFrame_GetLocalsArray(_PyFrame *frame) return ((PyObject **)frame) - frame->nlocalsplus; } +/* Returns a borrowed reference */ +PyFrameObject * +_PyFrame_MakeAndSetFrameObject(_PyFrame *frame); + /* Returns a borrowed reference */ static inline PyFrameObject * _PyFrame_GetFrameObject(_PyFrame *frame) { - /* Will need to handle lazy frames */ - assert(frame->frame_obj != NULL); - return frame->frame_obj; + PyFrameObject *res = frame->frame_obj; + if (res != NULL) { + return res; + } + return _PyFrame_MakeAndSetFrameObject(frame); } int -_PyFrame_Clear(_PyFrame * frame); +_PyFrame_Clear(_PyFrame * frame, int take); int _PyFrame_FastToLocalsWithError(_PyFrame *frame); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 60af628d8d2486..a0539684b51d17 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1818,8 +1818,16 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_WriteUnraisable(Py_None); continue; } - PyObject * f = frame == NULL ? Py_None : (PyObject *)_PyFrame_GetFrameObject(frame); - PyObject *arglist = Py_BuildValue("(iO)", i, f); + 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 4caa139e2702ab..b8f3318b76be7d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -859,49 +859,34 @@ frame_alloc(_PyFrame *frame, int owns) return f; } -int -_PyFrame_TakeLocals(PyFrameObject *f) +void +_PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *frame) { assert(f->f_own_locals_memory == 0); - _PyFrame *frame = f->f_frame; assert(frame->frame_obj == NULL); - Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); - PyObject **copy = PyMem_Malloc(size); - PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); - if (copy == NULL) { - for (int i = 0; i < f->f_frame->stackdepth; i++) { - PyObject *o = f->f_frame->stack[i]; - Py_XDECREF(o); + + f->f_own_locals_memory = 1; + f->f_frame = frame; + assert(f->f_back == NULL); + if (frame->previous != NULL) { + PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous); + if (back == NULL) { + /* Memory error here. Nothing we can do about it */ + PyErr_Clear(); + _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL); } - f->f_frame->stackdepth = 0; - for (int i = 0; i < f->f_frame->nlocalsplus; i++) { - PyObject *o = locals[i]; - Py_XDECREF(o); + else { + f->f_back = (PyFrameObject *)Py_NewRef(back); } - Py_XDECREF(f->f_frame->builtins); - Py_XDECREF(f->f_frame->globals); - Py_XDECREF(f->f_frame->locals); - Py_XDECREF(f->f_frame->code); - f->f_frame->previous = NULL; - PyErr_NoMemory(); - return -1; } - assert(f->f_back == NULL); - if (f->f_frame->previous != NULL) { - f->f_back = (PyFrameObject *)Py_NewRef(_PyFrame_GetFrameObject(f->f_frame->previous)); - } - memcpy(copy, locals, size); - f->f_own_locals_memory = 1; - f->f_frame = (_PyFrame *)(copy + f->f_frame->nlocalsplus); - f->f_frame->previous = NULL; + frame->previous = NULL; if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { _PyObject_GC_TRACK((PyObject *)f); } - return 0; } PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(PyThreadState *tstate, _PyFrame *frame, int owns) +_PyFrame_New_NoTrack(_PyFrame *frame, int owns) { PyFrameObject *f = frame_alloc(frame, owns); if (f == NULL) { @@ -941,7 +926,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, if (frame == NULL) { return NULL; } - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 1); + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); if (f) { _PyObject_GC_TRACK(f); } diff --git a/Objects/genobject.c b/Objects/genobject.c index cc0a89cdd09330..8db95b39cc4686 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -148,8 +148,7 @@ gen_dealloc(PyGenObject *gen) } gen->gi_xframe = NULL; frame->previous = NULL; - _PyFrame_Clear(frame); - PyMem_Free(_PyFrame_GetLocalsArray(frame)); + _PyFrame_Clear(frame, 1); } if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin); @@ -221,18 +220,15 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } - - PyFrameObject *f = _PyFrame_GetFrameObject(frame); result = _PyEval_EvalNoFrame(tstate, frame, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - assert(tstate->frame == f->f_frame->previous); + assert(tstate->frame == frame->previous); /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ frame->previous = NULL; - assert(f->f_back == NULL); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ @@ -273,15 +269,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - if (f) { - f->f_gen = NULL; + if (frame->frame_obj) { + frame->frame_obj->f_gen = NULL; } gen->gi_xframe = NULL; - if (_PyFrame_Clear(frame)) { - Py_XDECREF(result); - result = NULL; - } - PyMem_Free(_PyFrame_GetLocalsArray(frame)); + _PyFrame_Clear(frame, 1); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } diff --git a/Python/ceval.c b/Python/ceval.c index 3bbe4e06122b9b..082083c0be8e54 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4309,7 +4309,10 @@ MISS_WITH_CACHE(LOAD_GLOBAL) #endif /* Log traceback info. */ - PyTraceBack_Here(_PyFrame_GetFrameObject(frame)); + 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 */ @@ -4938,7 +4941,7 @@ make_coro_frame(PyThreadState *tstate, _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 1); + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); if (f == NULL) { return NULL; } @@ -5010,18 +5013,57 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, return frame; } +PyFrameObject * +_PyFrame_MakeAndSetFrameObject(_PyFrame *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 _PyFrame * +copy_frame_to_heap(_PyFrame *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); + _PyFrame *res = (_PyFrame *)(copy + frame->nlocalsplus); + return res; +} + int -_PyFrame_Clear(_PyFrame * frame) +_PyFrame_Clear(_PyFrame * 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) { - Py_DECREF(f); - if (_PyFrame_TakeLocals(f)) { - return -1; + if (!take) { + frame = copy_frame_to_heap(frame); + if (frame == NULL) { + return -1; + } } + _PyFrame_TakeLocals(f, frame); + Py_DECREF(f); return 0; } Py_DECREF(f); @@ -5032,6 +5074,9 @@ _PyFrame_Clear(_PyFrame * frame) Py_XDECREF(localsarray[i]); } _PyFrame_ClearSpecials(frame); + if (take) { + PyMem_Free(_PyFrame_GetLocalsArray(frame)); + } return 0; } @@ -5040,12 +5085,14 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) { ++tstate->recursion_depth; assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); - int err = _PyFrame_Clear(frame); + if (_PyFrame_Clear(frame, 0)) { + return -1; + } assert(frame->frame_obj == NULL); --tstate->recursion_depth; tstate->frame = frame->previous; _PyThreadState_PopLocals(tstate, _PyFrame_GetLocalsArray(frame)); - return err; + return 0; } PyObject * @@ -5068,7 +5115,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, PyObject *retval; assert (tstate->interp->eval_frame != NULL); // { /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, frame, 0); + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0); if (f == NULL) { frame->frame_obj = NULL; retval = NULL; @@ -5080,7 +5127,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, } // } if (_PyEvalFrameClearAndPop(tstate, frame)) { - Py_CLEAR(retval); + retval = NULL; } return retval; } @@ -5692,7 +5739,11 @@ PyEval_GetFrame(void) if (tstate->frame == NULL) { return NULL; } - return _PyFrame_GetFrameObject(tstate->frame); + PyFrameObject *f = _PyFrame_GetFrameObject(tstate->frame); + if (f == NULL) { + PyErr_Clear(); + } + return f; } PyObject * diff --git a/Python/pystate.c b/Python/pystate.c index 09a5178e2fb552..03d05c5b62a234 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1138,6 +1138,9 @@ PyThreadState_GetFrame(PyThreadState *tstate) return NULL; } PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->frame); + if (frame == NULL) { + PyErr_Clear(); + } Py_XINCREF(frame); return frame; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 7087a696554008..d40a51a86fb4da 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1830,7 +1830,7 @@ sys__getframe_impl(PyObject *module, int depth) "call stack is not deep enough"); return NULL; } - return _Py_NewRef((PyObject *)_PyFrame_GetFrameObject(frame)); + return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(frame)); } /*[clinic input] From c76de89f1bb26fc334d5b6b31d0c2b06b8aa4bba Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 6 Jul 2021 16:15:35 +0100 Subject: [PATCH 21/40] Do not create frame objects for Python calls. --- Include/internal/pycore_frame.h | 2 ++ Lib/test/test_faulthandler.py | 2 ++ Modules/_xxsubinterpretersmodule.c | 5 ++--- Objects/frameobject.c | 3 --- Python/ceval.c | 16 +++------------- Python/traceback.c | 23 +++++++---------------- 6 files changed, 16 insertions(+), 35 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 268da474a2fbf0..dedc8ad97f23bf 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -57,8 +57,10 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * frame->globals = Py_NewRef(con->fc_globals); frame->locals = Py_XNewRef(locals); frame->nlocalsplus = nlocalsplus; + frame->stackdepth = 0; frame->frame_obj = NULL; frame->lasti = -1; + frame->f_state = FRAME_CREATED; } static inline void diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index ee3f41a108a14c..f37cbd911fd6a4 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -56,6 +56,8 @@ def temporary_filename(): os_helper.unlink(filename) class FaultHandlerTests(unittest.TestCase): + + maxDiff = None 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/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 1265b00acd7ded..f6f1c9ab197c32 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1835,13 +1835,12 @@ _is_running(PyInterpreterState *interp) } assert(!PyErr_Occurred()); - PyFrameObject *frame = PyThreadState_GetFrame(tstate); + _PyFrame *frame = tstate->frame; if (frame == NULL) { return 0; } - int executing = _PyFrame_IsExecuting(frame->f_frame); - Py_DECREF(frame); + int executing = _PyFrame_IsExecuting(frame); return executing; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index b8f3318b76be7d..cd2aba1f3cdc8e 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -698,7 +698,6 @@ 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. */ - assert(f->f_own_locals_memory); f->f_frame->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); @@ -894,12 +893,10 @@ _PyFrame_New_NoTrack(_PyFrame *frame, int owns) } f->f_back = NULL; f->f_trace = NULL; - f->f_frame->stackdepth = 0; f->f_trace_lines = 1; f->f_trace_opcodes = 0; f->f_gen = NULL; f->f_lineno = 0; - f->f_frame->f_state = FRAME_CREATED; return f; } diff --git a/Python/ceval.c b/Python/ceval.c index 082083c0be8e54..43897c6eab1e15 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5113,19 +5113,9 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, return NULL; } PyObject *retval; - assert (tstate->interp->eval_frame != NULL); // { - /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0); - if (f == NULL) { - frame->frame_obj = NULL; - retval = NULL; - } - else { - frame->frame_obj = f; - retval = _PyEval_EvalFrame(tstate, f, 0); - assert(frame->stackdepth == 0); - } - // } + assert (tstate->interp->eval_frame != NULL); + retval = _PyEval_EvalNoFrame(tstate, frame, 0); + assert(frame->stackdepth == 0); if (_PyEvalFrameClearAndPop(tstate, frame)) { retval = NULL; } diff --git a/Python/traceback.c b/Python/traceback.c index 74cbb3ddc6520a..1a491627e26c46 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -751,9 +751,9 @@ _Py_DumpASCII(int fd, PyObject *text) This function is signal safe. */ static void -dump_frame(int fd, PyFrameObject *frame) +dump_frame(int fd, _PyFrame *frame) { - PyCodeObject *code = PyFrame_GetCode(frame); + PyCodeObject *code = frame->code; PUTS(fd, " File "); if (code->co_filename != NULL && PyUnicode_Check(code->co_filename)) @@ -765,7 +765,7 @@ dump_frame(int fd, PyFrameObject *frame) PUTS(fd, "???"); } - int lineno = PyFrame_GetLineNumber(frame); + int lineno = PyCode_Addr2Line(code, frame->lasti*2); PUTS(fd, ", line "); if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); @@ -784,20 +784,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; + _PyFrame *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, "\n"); return; @@ -806,22 +805,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++; } } From 15aeef1603c152f23aa6d8a99b3e75981bde65e7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 6 Jul 2021 18:28:19 +0100 Subject: [PATCH 22/40] Don't create frame objects for generators. --- Include/cpython/frameobject.h | 2 - Include/internal/pycore_ceval.h | 2 + Include/internal/pycore_frame.h | 4 ++ Lib/test/test_sys.py | 2 +- Objects/frameobject.c | 7 ++- Objects/genobject.c | 92 +++++++++++++++++++++++++++++---- Python/ceval.c | 37 ++++--------- 7 files changed, 101 insertions(+), 45 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index caf814daeeb0ec..22838e235e6f46 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -9,8 +9,6 @@ struct _frame { struct _frame *f_back; /* previous frame, or NULL */ struct _py_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_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index e740f8b2883748..7a7f1578b771d2 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -113,6 +113,8 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { struct _py_frame *_PyEval_GetFrame(void); +PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _py_frame *); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index dedc8ad97f23bf..6e8080a367ebc6 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -25,6 +25,8 @@ typedef struct _py_frame { PyObject *locals; PyCodeObject *code; PyFrameObject *frame_obj; + /* Borrowed reference to a generator, or NULL */ + PyObject *generator; struct _py_frame *previous; int lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ @@ -59,6 +61,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * frame->nlocalsplus = nlocalsplus; frame->stackdepth = 0; frame->frame_obj = NULL; + frame->generator = NULL; frame->lasti = -1; frame->f_state = FRAME_CREATED; } @@ -66,6 +69,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * static inline void _PyFrame_ClearSpecials(_PyFrame *frame) { + frame->generator = NULL; Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->locals); Py_DECREF(frame->globals); diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index fdeb5891649a6d..b6ad84fe84d84d 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1275,7 +1275,7 @@ class C(object): pass # frame import inspect x = inspect.currentframe() - check(x, size('4Pi3c')) + check(x, size('3Pi3c')) # function def func(): pass check(func, size('14P')) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index cd2aba1f3cdc8e..10fb1a6a298500 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -724,9 +724,9 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) "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; @@ -895,7 +895,6 @@ _PyFrame_New_NoTrack(_PyFrame *frame, int owns) f->f_trace = NULL; f->f_trace_lines = 1; f->f_trace_opcodes = 0; - f->f_gen = NULL; f->f_lineno = 0; return f; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 8db95b39cc4686..ded67dd1e8d347 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -143,9 +143,6 @@ gen_dealloc(PyGenObject *gen) } _PyFrame *frame = gen->gi_xframe; if (frame != NULL) { - if (frame->frame_obj != NULL) { - frame->frame_obj->f_gen = NULL; - } gen->gi_xframe = NULL; frame->previous = NULL; _PyFrame_Clear(frame, 1); @@ -207,9 +204,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, frame->stack[frame->stackdepth] = result; frame->stackdepth++; - /* Generators always return to their most recent caller, not - * necessarily their creator. */ - assert(frame->frame_obj->f_back == NULL); frame->previous = tstate->frame; gen->gi_exc_state.previous_item = tstate->exc_info; @@ -225,7 +219,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, gen->gi_exc_state.previous_item = NULL; assert(tstate->frame == frame->previous); - /* Don't keep the reference to f_back any longer than necessary. It + /* 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. */ frame->previous = NULL; @@ -269,9 +263,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - if (frame->frame_obj) { - frame->frame_obj->f_gen = NULL; - } + frame->generator = NULL; gen->gi_xframe = NULL; _PyFrame_Clear(frame, 1); *presult = result; @@ -862,6 +854,84 @@ PyTypeObject PyGen_Type = { _PyGen_Finalize, /* tp_finalize */ }; +static PyObject * +make_gen(PyTypeObject *type, PyFrameConstructor *con, _PyFrame *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->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, _PyFrame *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) @@ -878,7 +948,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, gen->gi_xframe = f->f_frame; gen->gi_xframe->frame_obj = f; f->f_own_locals_memory = 0; - f->f_gen = (PyObject *) gen; + gen->gi_xframe->generator = (PyObject *) gen; assert(PyObject_GC_IsTracked((PyObject *)f)); gen->gi_code = PyFrame_GetCode(f); diff --git a/Python/ceval.c b/Python/ceval.c index 43897c6eab1e15..3cd53fb477ca52 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4920,7 +4920,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, } -PyFrameObject * +_PyFrame * make_coro_frame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, @@ -4940,18 +4940,12 @@ make_coro_frame(PyThreadState *tstate, } _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); - /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); - if (f == NULL) { - return NULL; - } assert(frame->frame_obj == NULL); if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { - Py_DECREF(f); + _PyFrame_Clear(frame, 1); return NULL; } - _PyObject_GC_TRACK(f); - return f; + return frame; } static PyObject * @@ -4961,25 +4955,11 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, PyObject *kwnames) { assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); - PyFrameObject *f = make_coro_frame( - tstate, con, locals, args, argcount, kwnames); - if (f == NULL) { + _PyFrame* frame = make_coro_frame(tstate, con, locals, args, argcount, kwnames); + if (frame == NULL) { return NULL; } - PyObject *gen; - int is_coro = ((PyCodeObject *)con->fc_code)->co_flags & CO_COROUTINE; - - assert(f->f_back == NULL); - - /* 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); - } + PyObject *gen = _Py_MakeCoro(con, frame); if (gen == NULL) { return NULL; } @@ -5069,10 +5049,13 @@ _PyFrame_Clear(_PyFrame * frame, int take) Py_DECREF(f); assert(_PyObject_IsFreed((PyObject *)f) || Py_REFCNT(f) == 0); } - assert(frame->stackdepth == 0); 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]); + } _PyFrame_ClearSpecials(frame); if (take) { PyMem_Free(_PyFrame_GetLocalsArray(frame)); From 1d2e1ce2b086f87f7c0666959737758c1c0b68d3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 7 Jul 2021 12:02:49 +0100 Subject: [PATCH 23/40] Fix memory leak --- Python/errors.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/errors.c b/Python/errors.c index 99b6fffe459fb3..1f84215a136a49 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1412,6 +1412,7 @@ _PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj) if (exc_tb == NULL) { _PyErr_Clear(tstate); } + Py_DECREF(frame); } } From d619baeae1c8fe4e9f3c9d246213406cf4c0564d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 8 Jul 2021 17:21:56 +0100 Subject: [PATCH 24/40] Restore support for PEP 523. --- Include/cpython/ceval.h | 2 +- Include/cpython/pystate.h | 2 +- Include/internal/pycore_ceval.h | 6 +----- Objects/genobject.c | 2 +- Python/ceval.c | 15 ++++----------- Tools/gdb/libpython.py | 2 +- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index 06338928f6738b..1221f901312f80 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 _py_frame *f, int exc); PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds); PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index d437db225b93ec..872394f5576b15 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -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 _py_frame *, int); PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc( PyInterpreterState *interp); diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 7a7f1578b771d2..a0551b64c84f42 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -41,15 +41,11 @@ extern PyObject *_PyEval_BuiltinsFromGlobals( static inline PyObject* -_PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) +_PyEval_EvalFrame(PyThreadState *tstate, struct _py_frame *f, int throwflag) { return tstate->interp->eval_frame(tstate, f, throwflag); } -PyObject* -_PyEval_EvalNoFrame(PyThreadState *tstate, - struct _py_frame *frame, int throwflag); - extern PyObject * _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *desc, PyObject *locals, diff --git a/Objects/genobject.c b/Objects/genobject.c index cd5c623ebe50ea..8f5089894f6829 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -214,7 +214,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } - result = _PyEval_EvalNoFrame(tstate, frame, exc); + result = _PyEval_EvalFrame(tstate, frame, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index ad836eb317afaa..0987cb9c36c278 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1077,14 +1077,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); } @@ -1403,7 +1403,7 @@ eval_frame_handle_pending(PyThreadState *tstate) #define LOCALS() frame->locals PyObject* _Py_HOT_FUNCTION -_PyEval_EvalNoFrame(PyThreadState *tstate, _PyFrame *frame, int throwflag) +_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) { _Py_EnsureTstateNotNULL(tstate); @@ -4433,13 +4433,6 @@ MISS_WITH_CACHE(LOAD_GLOBAL) return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); } - -PyObject* -_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) -{ - return _PyEval_EvalNoFrame(tstate, f->f_frame, throwflag); -} - static void format_missing(PyThreadState *tstate, const char *kind, PyCodeObject *co, PyObject *names, PyObject *qualname) @@ -5121,7 +5114,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, } PyObject *retval; assert (tstate->interp->eval_frame != NULL); - retval = _PyEval_EvalNoFrame(tstate, frame, 0); + retval = _PyEval_EvalFrame(tstate, frame, 0); assert(frame->stackdepth == 0); if (_PyEvalFrameClearAndPop(tstate, frame)) { retval = NULL; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 2dcba4c779886f..fcb3c6b0354525 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -101,7 +101,7 @@ def _sizeof_void_p(): FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)' UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame' -EVALFRAME = '_PyEval_EvalNoFrame' +EVALFRAME = '_PyEval_EvalFrameDefault' class NullPyObjectPtr(RuntimeError): pass From d147d033c057617d136f9cd3b646112bc255cff8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 09:38:53 +0100 Subject: [PATCH 25/40] Streamline pushing and popping stack frames a bit. --- Include/internal/pycore_pystate.h | 6 ++- Python/ceval.c | 20 +++------- Python/pystate.c | 65 +++++++++++++++++++------------ 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 6601ce2f80bf7e..7489f36fe36a76 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -147,8 +147,10 @@ PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); -PyObject **_PyThreadState_PushLocals(PyThreadState *, int size); -void _PyThreadState_PopLocals(PyThreadState *, PyObject **); +struct _py_frame *_PyThreadState_PushFrame( + PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals); + +void _PyThreadState_PopFrame(PyThreadState *tstate, struct _py_frame *frame); #ifdef __cplusplus } diff --git a/Python/ceval.c b/Python/ceval.c index 0987cb9c36c278..cbeb47ea2dc986 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4989,20 +4989,13 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - PyCodeObject *code = (PyCodeObject *)con->fc_code; - int size = code->co_nlocalsplus + code->co_stacksize + - FRAME_SPECIALS_SIZE; - PyObject **localsarray = _PyThreadState_PushLocals(tstate, size); - if (localsarray == NULL) { + _PyFrame * frame = _PyThreadState_PushFrame(tstate, con, locals); + if (frame == NULL) { return NULL; } - _PyFrame * frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); - _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + PyObject **localsarray = _PyFrame_GetLocalsArray(frame); if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { - _PyFrame_ClearSpecials(frame); - for (int i = 0; i < code->co_nlocalsplus; i++) { - Py_XDECREF(localsarray[i]); - } + _PyFrame_Clear(frame, 0); return NULL; } frame->previous = tstate->frame; @@ -5091,7 +5084,7 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) assert(frame->frame_obj == NULL); --tstate->recursion_depth; tstate->frame = frame->previous; - _PyThreadState_PopLocals(tstate, _PyFrame_GetLocalsArray(frame)); + _PyThreadState_PopFrame(tstate, frame); return 0; } @@ -5112,9 +5105,8 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, if (frame == NULL) { return NULL; } - PyObject *retval; assert (tstate->interp->eval_frame != NULL); - retval = _PyEval_EvalFrame(tstate, frame, 0); + PyObject *retval = _PyEval_EvalFrame(tstate, frame, 0); assert(frame->stackdepth == 0); if (_PyEvalFrameClearAndPop(tstate, frame)) { retval = NULL; diff --git a/Python/pystate.c b/Python/pystate.c index 03d05c5b62a234..93fc8241a9ccbc 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -686,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 */ @@ -2016,42 +2016,57 @@ _Py_GetConfig(void) #define MINIMUM_OVERHEAD 1000 -PyObject ** -_PyThreadState_PushLocals(PyThreadState *tstate, int size) +_Py_NO_INLINE PyObject ** +_PyThreadState_PushChunk(PyThreadState *tstate, int size) { - assert(((unsigned)size) < INT_MAX/sizeof(PyObject*)/2); - PyObject **res = tstate->datastack_top; - PyObject **top = res + 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; +} + +_PyFrame * +_PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals) +{ + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int nlocalsplus = code->co_nlocalsplus; + int size = nlocalsplus + code->co_stacksize + + FRAME_SPECIALS_SIZE; + 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 = _PyThreadState_PushChunk(tstate, 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; + _PyFrame * frame = (_PyFrame *)(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, _PyFrame * frame) { + PyObject **locals = _PyFrame_GetLocalsArray(frame); if (locals == &tstate->datastack_chunk->data[0]) { _PyStackChunk *chunk = tstate->datastack_chunk; _PyStackChunk *previous = chunk->previous; From 618b094c607ef442605d28c98d8ca3c006f8b5cc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 11:37:01 +0100 Subject: [PATCH 26/40] Add f_ prefix back to several frame fields to ease porting C code that introspects frames. --- Include/internal/pycore_frame.h | 28 ++++----- Modules/_tracemalloc.c | 4 +- Objects/frameobject.c | 70 +++++++++++----------- Objects/genobject.c | 26 ++++----- Objects/typeobject.c | 2 +- Python/_warnings.c | 4 +- Python/ceval.c | 100 ++++++++++++++++---------------- Python/suggestions.c | 4 +- Python/traceback.c | 8 +-- Tools/gdb/libpython.py | 8 +-- 10 files changed, 127 insertions(+), 127 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 6e8080a367ebc6..4d6bac7a6fc64e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -20,15 +20,15 @@ enum _framestate { typedef signed char PyFrameState; typedef struct _py_frame { - PyObject *globals; - PyObject *builtins; - PyObject *locals; - PyCodeObject *code; + 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 _py_frame *previous; - int lasti; /* Last instruction if called */ + int f_lasti; /* Last instruction if called */ int stackdepth; /* Depth of value stack */ int nlocalsplus; PyFrameState f_state; /* What state the frame is in */ @@ -54,15 +54,15 @@ void _PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *locals); static inline void _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals, int nlocalsplus) { - frame->code = (PyCodeObject *)Py_NewRef(con->fc_code); - frame->builtins = Py_NewRef(con->fc_builtins); - frame->globals = Py_NewRef(con->fc_globals); - frame->locals = Py_XNewRef(locals); + 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->lasti = -1; + frame->f_lasti = -1; frame->f_state = FRAME_CREATED; } @@ -71,10 +71,10 @@ _PyFrame_ClearSpecials(_PyFrame *frame) { frame->generator = NULL; Py_XDECREF(frame->frame_obj); - Py_XDECREF(frame->locals); - Py_DECREF(frame->globals); - Py_DECREF(frame->builtins); - Py_DECREF(frame->code); + Py_XDECREF(frame->f_locals); + Py_DECREF(frame->f_globals); + Py_DECREF(frame->f_builtins); + Py_DECREF(frame->f_code); } static inline PyObject** diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index a013f7dff81148..6532267b2f16f2 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -302,13 +302,13 @@ static void tracemalloc_get_frame(_PyFrame *pyframe, frame_t *frame) { frame->filename = unknown_filename; - int lineno = PyCode_Addr2Line(pyframe->code, pyframe->lasti*2); + int lineno = PyCode_Addr2Line(pyframe->f_code, pyframe->f_lasti*2); if (lineno < 0) { lineno = 0; } frame->lineno = (unsigned int)lineno; - PyObject *filename = pyframe->code->co_filename; + PyObject *filename = pyframe->f_code->co_filename; if (filename == NULL) { #ifdef TRACE_DEBUG diff --git a/Objects/frameobject.c b/Objects/frameobject.c index da61ad896f5b55..4278e8d1888126 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -32,7 +32,7 @@ frame_getlocals(PyFrameObject *f, void *closure) { if (PyFrame_FastToLocalsWithError(f) < 0) return NULL; - PyObject *locals = f->f_frame->locals; + PyObject *locals = f->f_frame->f_locals; Py_INCREF(locals); return locals; } @@ -45,7 +45,7 @@ PyFrame_GetLineNumber(PyFrameObject *f) return f->f_lineno; } else { - return PyCode_Addr2Line(f->f_frame->code, f->f_frame->lasti*2); + return PyCode_Addr2Line(f->f_frame->f_code, f->f_frame->f_lasti*2); } } @@ -64,16 +64,16 @@ frame_getlineno(PyFrameObject *f, void *closure) static PyObject * frame_getlasti(PyFrameObject *f, void *closure) { - if (f->f_frame->lasti < 0) { + if (f->f_frame->f_lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(f->f_frame->lasti*2); + return PyLong_FromLong(f->f_frame->f_lasti*2); } static PyObject * frame_getglobals(PyFrameObject *f, void *closure) { - PyObject *globals = f->f_frame->globals; + PyObject *globals = f->f_frame->f_globals; if (globals == NULL) { globals = Py_None; } @@ -84,7 +84,7 @@ frame_getglobals(PyFrameObject *f, void *closure) static PyObject * frame_getbuiltins(PyFrameObject *f, void *closure) { - PyObject *builtins = f->f_frame->builtins; + PyObject *builtins = f->f_frame->f_builtins; if (builtins == NULL) { builtins = Py_None; } @@ -481,7 +481,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } new_lineno = (int)l_new_lineno; - if (new_lineno < f->f_frame->code->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); @@ -490,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(f->f_frame->code->co_code) / sizeof(_Py_CODEUNIT)); - int *lines = marklines(f->f_frame->code, 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; } @@ -505,7 +505,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - int64_t *stacks = mark_stacks(f->f_frame->code, len); + int64_t *stacks = mark_stacks(f->f_frame->f_code, len); if (stacks == NULL) { PyMem_Free(lines); return -1; @@ -513,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_frame->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++) { @@ -557,7 +557,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } /* Finally set the new lasti and return OK. */ f->f_lineno = 0; - f->f_frame->lasti = best_addr; + f->f_frame->f_lasti = best_addr; return 0; } @@ -627,11 +627,11 @@ frame_dealloc(PyFrameObject *f) f->f_own_locals_memory = 0; _PyFrame *frame = f->f_frame; /* Don't clear code object until the end */ - co = frame->code; - frame->code = NULL; - Py_CLEAR(frame->globals); - Py_CLEAR(frame->builtins); - Py_CLEAR(frame->locals); + 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(locals[i]); @@ -683,10 +683,10 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) Py_VISIT(f->f_frame->stack[i]); } - Py_VISIT(f->f_frame->globals); - Py_VISIT(f->f_frame->builtins); - Py_VISIT(f->f_frame->locals); - Py_VISIT(f->f_frame->code); + Py_VISIT(f->f_frame->f_globals); + Py_VISIT(f->f_frame->f_builtins); + Py_VISIT(f->f_frame->f_locals); + Py_VISIT(f->f_frame->f_code); return 0; } @@ -741,7 +741,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) Py_ssize_t res; res = sizeof(PyFrameObject); if (f->f_own_locals_memory) { - PyCodeObject *code = f->f_frame->code; + PyCodeObject *code = f->f_frame->f_code; res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); } return PyLong_FromSsize_t(res); @@ -754,7 +754,7 @@ static PyObject * frame_repr(PyFrameObject *f) { int lineno = PyFrame_GetLineNumber(f); - PyCodeObject *code = f->f_frame->code; + PyCodeObject *code = f->f_frame->f_code; return PyUnicode_FromFormat( "", f, code->co_filename, lineno, code->co_name); @@ -833,10 +833,10 @@ frame_alloc(_PyFrame *frame, int owns) f = PyObject_GC_New(PyFrameObject, &PyFrame_Type); if (f == NULL) { if (owns) { - Py_XDECREF(frame->code); - Py_XDECREF(frame->builtins); - Py_XDECREF(frame->globals); - Py_XDECREF(frame->locals); + 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; @@ -933,8 +933,8 @@ static int _PyFrame_OpAlreadyRan(_PyFrame *frame, int opcode, int oparg) { const _Py_CODEUNIT *code = - (const _Py_CODEUNIT *)PyBytes_AS_STRING(frame->code->co_code); - for (int i = 0; i < frame->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; } @@ -948,13 +948,13 @@ _PyFrame_FastToLocalsWithError(_PyFrame *frame) { PyObject *locals; PyObject **fast; PyCodeObject *co; - locals = frame->locals; + locals = frame->f_locals; if (locals == NULL) { - locals = frame->locals = PyDict_New(); + locals = frame->f_locals = PyDict_New(); if (locals == NULL) return -1; } - co = frame->code; + 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); @@ -1050,11 +1050,11 @@ _PyFrame_LocalsToFast(_PyFrame *frame, int clear) PyObject **fast; PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; - locals = frame->locals; + locals = frame->f_locals; if (locals == NULL) return; fast = _PyFrame_GetLocalsArray(frame); - co = frame->code; + co = frame->f_code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1156,7 +1156,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); - PyCodeObject *code = frame->f_frame->code; + PyCodeObject *code = frame->f_frame->f_code; assert(code != NULL); Py_INCREF(code); return code; diff --git a/Objects/genobject.c b/Objects/genobject.c index 8f5089894f6829..e4cd5e463d42fc 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -49,10 +49,10 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) Py_VISIT(frame->stack[i]); } - Py_VISIT(frame->globals); - Py_VISIT(frame->builtins); - Py_VISIT(frame->locals); - Py_VISIT(frame->code); + Py_VISIT(frame->f_globals); + Py_VISIT(frame->f_builtins); + Py_VISIT(frame->f_locals); + Py_VISIT(frame->f_code); } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ @@ -98,7 +98,7 @@ _PyGen_Finalize(PyObject *self) issue a RuntimeWarning. */ if (gen->gi_code != NULL && ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && - gen->gi_xframe->lasti == -1) + gen->gi_xframe->f_lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -197,7 +197,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, } assert(_PyFrame_IsRunnable(frame)); - assert(frame->lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + 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); @@ -351,7 +351,7 @@ _PyGen_yf(PyGenObject *gen) PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (frame->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 */ @@ -359,7 +359,7 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(frame->lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(frame->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; assert(frame->stackdepth > 0); yf = frame->stack[frame->stackdepth-1]; @@ -486,8 +486,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ - assert(gen->gi_xframe->lasti >= 0); - gen->gi_xframe->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); @@ -865,7 +865,7 @@ make_gen(PyTypeObject *type, PyFrameConstructor *con, _PyFrame *frame) } gen->gi_xframe = frame; frame->generator = (PyObject *)gen; - gen->gi_code = frame->code; + gen->gi_code = frame->f_code; Py_INCREF(gen->gi_code); gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_type = NULL; @@ -1298,10 +1298,10 @@ compute_cr_origin(int origin_depth) } frame = _PyEval_GetFrame(); for (int i = 0; i < frame_count; ++i) { - PyCodeObject *code = frame->code; + PyCodeObject *code = frame->f_code; PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, - PyCode_Addr2Line(frame->code, frame->lasti*2), + PyCode_Addr2Line(frame->f_code, frame->f_lasti*2), code->co_name); if (!frameinfo) { Py_DECREF(cr_origin); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c3475d64e6742a..42626b10b9b082 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8884,7 +8884,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, 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_frame->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); diff --git a/Python/_warnings.c b/Python/_warnings.c index 4f8ba16101d29c..cf2110d31c3b5e 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -854,8 +854,8 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, *lineno = 1; } else { - globals = f->f_frame->globals; - *filename = f->f_frame->code->co_filename; + 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 627cd79635a5d1..c1ff6f8a9f4349 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1243,7 +1243,7 @@ eval_frame_handle_pending(PyThreadState *tstate) if (cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \ goto tracing_dispatch; \ } \ - frame->lasti = INSTR_OFFSET(); \ + frame->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ DISPATCH_GOTO(); \ } @@ -1398,9 +1398,9 @@ eval_frame_handle_pending(PyThreadState *tstate) #define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; } -#define GLOBALS() frame->globals -#define BUILTINS() frame->builtins -#define LOCALS() frame->locals +#define GLOBALS() frame->f_globals +#define BUILTINS() frame->f_builtins +#define LOCALS() frame->f_locals PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) @@ -1449,7 +1449,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) /* push frame */ tstate->frame = frame; - co = frame->code; + co = frame->f_code; if (cframe.use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1507,22 +1507,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) localsplus = _PyFrame_GetLocalsArray(frame); first_instr = co->co_firstinstr; /* - frame->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 frame->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 frame->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,frame->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 frame->lasti will point + FOR_ITER is effectively a single opcode and frame->f_lasti will point to the beginning of the combined pair.) */ - assert(frame->lasti >= -1); - next_instr = first_instr + frame->lasti + 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. @@ -1595,8 +1595,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) tracing_dispatch: { - int instr_prev = frame->lasti; - frame->lasti = INSTR_OFFSET(); + int instr_prev = frame->f_lasti; + frame->f_lasti = INSTR_OFFSET(); TRACING_NEXTOPARG(); if (PyDTrace_LINE_ENABLED()) @@ -1619,7 +1619,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) goto error; } /* Reload possibly changed frame fields */ - JUMPTO(frame->lasti); + JUMPTO(frame->f_lasti); stack_pointer = frame->stack+frame->stackdepth; frame->stackdepth = -1; TRACING_NEXTOPARG(); @@ -1632,11 +1632,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) if (lltrace) { if (HAS_ARG(opcode)) { printf("%d: %d, %d\n", - frame->lasti, opcode, oparg); + frame->f_lasti, opcode, oparg); } else { printf("%d: %d\n", - frame->lasti, opcode); + frame->f_lasti, opcode); } } #endif @@ -2430,8 +2430,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) assert (gen_status == PYGEN_NEXT); /* receiver remains on stack, retval is value to be yielded */ /* and repeat... */ - assert(frame->lasti > 0); - frame->lasti -= 1; + assert(frame->f_lasti > 0); + frame->f_lasti -= 1; frame->f_state = FRAME_SUSPENDED; frame->stackdepth = (int)(stack_pointer - frame->stack); goto exiting; @@ -2497,7 +2497,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) case TARGET(POP_EXCEPT_AND_RERAISE): { PyObject *lasti = PEEK(4); if (PyLong_Check(lasti)) { - frame->lasti = PyLong_AsLong(lasti); + frame->f_lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -2528,7 +2528,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) if (oparg) { PyObject *lasti = PEEK(oparg+3); if (PyLong_Check(lasti)) { - frame->lasti = PyLong_AsLong(lasti); + frame->f_lasti = PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -4298,7 +4298,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) default: fprintf(stderr, "XXX lineno: %d, opcode: %d\n", - PyCode_Addr2Line(frame->code, frame->lasti*2), + PyCode_Addr2Line(frame->f_code, frame->f_lasti*2), opcode); _PyErr_SetString(tstate, PyExc_SystemError, "unknown opcode"); goto error; @@ -4356,7 +4356,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) exception_unwind: frame->f_state = FRAME_UNWINDING; - /* We can't use frame->lasti here, as RERAISE may have set it */ + /* 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) { @@ -4371,7 +4371,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) } PyObject *exc, *val, *tb; if (lasti) { - PyObject *lasti = PyLong_FromLong(frame->lasti); + PyObject *lasti = PyLong_FromLong(frame->f_lasti); if (lasti == NULL) { goto exception_unwind; } @@ -4397,7 +4397,7 @@ MISS_WITH_CACHE(LOAD_GLOBAL) JUMPTO(handler); /* Resume normal execution */ frame->f_state = FRAME_EXECUTING; - frame->lasti = handler; + frame->f_lasti = handler; NEXTOPARG(); goto dispatch_opcode; } /* main loop */ @@ -5478,7 +5478,7 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, static void initialize_trace_info(PyTraceInfo *trace_info, _PyFrame *frame) { - PyCodeObject *code = frame->code; + PyCodeObject *code = frame->f_code; if (trace_info->code != code) { trace_info->code = code; _PyCode_InitAddressRange(code, &trace_info->bounds); @@ -5499,12 +5499,12 @@ call_trace(Py_tracefunc func, PyObject *obj, if (f == NULL) { return -1; } - if (frame->lasti < 0) { - f->f_lineno = frame->code->co_firstlineno; + if (frame->f_lasti < 0) { + f->f_lineno = frame->f_code->co_firstlineno; } else { initialize_trace_info(&tstate->trace_info, frame); - f->f_lineno = _PyCode_CheckLineNumber(frame->lasti*2, &tstate->trace_info.bounds); + f->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); } result = func(obj, f, what, arg); f->f_lineno = 0; @@ -5544,14 +5544,14 @@ 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->lasti*2, &tstate->trace_info.bounds); + int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &tstate->trace_info.bounds); 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->lasti < instr_prev || line != lastline) { + if (frame->f_lasti < instr_prev || line != lastline) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } } @@ -5729,7 +5729,7 @@ _PyEval_GetBuiltins(PyThreadState *tstate) { _PyFrame *frame = tstate->frame; if (frame != NULL) { - return frame->builtins; + return frame->f_builtins; } return tstate->interp->builtins; } @@ -5770,7 +5770,7 @@ PyEval_GetLocals(void) return NULL; } - PyObject *locals = current_frame->locals; + PyObject *locals = current_frame->f_locals; assert(locals != NULL); return locals; } @@ -5783,7 +5783,7 @@ PyEval_GetGlobals(void) if (current_frame == NULL) { return NULL; } - return current_frame->globals; + return current_frame->f_globals; } int @@ -5794,7 +5794,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) int result = cf->cf_flags != 0; if (current_frame != NULL) { - const int codeflags = current_frame->code->co_flags; + const int codeflags = current_frame->f_code->co_flags; const int compilerflags = codeflags & PyCF_MASK; if (compilerflags) { result = 1; @@ -6035,14 +6035,14 @@ import_name(PyThreadState *tstate, _PyFrame *frame, PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemIdWithError(frame->builtins, &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 = frame->locals; + PyObject *locals = frame->f_locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { int ilevel = _PyLong_AsInt(level); @@ -6051,7 +6051,7 @@ import_name(PyThreadState *tstate, _PyFrame *frame, } res = PyImport_ImportModuleLevelObject( name, - frame->globals, + frame->f_globals, locals == NULL ? Py_None :locals, fromlist, ilevel); @@ -6061,7 +6061,7 @@ import_name(PyThreadState *tstate, _PyFrame *frame, Py_INCREF(import_func); stack[0] = name; - stack[1] = frame->globals; + stack[1] = frame->f_globals; stack[2] = locals == NULL ? Py_None : locals; stack[3] = fromlist; stack[4] = level; @@ -6398,9 +6398,9 @@ unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, } case STORE_NAME: { - PyObject *names = frame->code->co_names; + PyObject *names = frame->f_code->co_names; PyObject *name = GETITEM(names, oparg); - PyObject *locals = frame->locals; + PyObject *locals = frame->f_locals; if (locals && PyDict_CheckExact(locals)) { PyObject *w = PyDict_GetItemWithError(locals, name); if ((w == v && PyDict_DelItem(locals, name) != 0) || @@ -6484,10 +6484,10 @@ dtrace_function_entry(_PyFrame *frame) const char *funcname; int lineno; - PyCodeObject *code = frame->code; + PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyCode_Addr2Line(frame->code, frame->lasti*2); + lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*2); PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); } @@ -6499,10 +6499,10 @@ dtrace_function_return(_PyFrame *frame) const char *funcname; int lineno; - PyCodeObject *code = frame->code; + PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyCode_Addr2Line(frame->code, frame->lasti*2); + lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*2); PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); } @@ -6520,17 +6520,17 @@ maybe_dtrace_line(_PyFrame *frame, */ initialize_trace_info(trace_info, frame); int lastline = _PyCode_CheckLineNumber(instr_prev*2, &trace_info->bounds); - int line = _PyCode_CheckLineNumber(frame->lasti*2, &trace_info->bounds); + int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &trace_info->bounds); if (line != -1) { /* Trace backward edges or first instruction of a new line */ - if (frame->lasti < instr_prev || - (line != lastline && frame->lasti*2 == trace_info->bounds.ar_start)) + if (frame->f_lasti < instr_prev || + (line != lastline && frame->f_lasti*2 == trace_info->bounds.ar_start)) { - co_filename = PyUnicode_AsUTF8(frame->code->co_filename); + co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename); if (!co_filename) { co_filename = "?"; } - co_name = PyUnicode_AsUTF8(frame->code->co_name); + co_name = PyUnicode_AsUTF8(frame->f_code->co_name); if (!co_name) { co_name = "?"; } diff --git a/Python/suggestions.c b/Python/suggestions.c index ae93a446b86411..04e8a09775ddb6 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -229,7 +229,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(frame->f_frame->globals); + dir = PySequence_List(frame->f_frame->f_globals); if (dir == NULL) { return NULL; } @@ -239,7 +239,7 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) return suggestions; } - dir = PySequence_List(frame->f_frame->builtins); + dir = PySequence_List(frame->f_frame->f_builtins); if (dir == NULL) { return NULL; } diff --git a/Python/traceback.c b/Python/traceback.c index 73f6b07579d33b..99e030378252b4 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -236,7 +236,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_frame->lasti*2, + return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_frame->f_lasti*2, PyFrame_GetLineNumber(frame)); } @@ -537,7 +537,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen if (!_Py_DisplaySourceLine(f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT, &truncation, &source_line)) { int code_offset = tb->tb_lasti; - PyCodeObject* code = frame->f_frame->code; + PyCodeObject* code = frame->f_frame->f_code; int start_line; int end_line; @@ -813,7 +813,7 @@ _Py_DumpASCII(int fd, PyObject *text) static void dump_frame(int fd, _PyFrame *frame) { - PyCodeObject *code = frame->code; + PyCodeObject *code = frame->f_code; PUTS(fd, " File "); if (code->co_filename != NULL && PyUnicode_Check(code->co_filename)) @@ -825,7 +825,7 @@ dump_frame(int fd, _PyFrame *frame) PUTS(fd, "???"); } - int lineno = PyCode_Addr2Line(code, frame->lasti*2); + int lineno = PyCode_Addr2Line(code, frame->f_lasti*2); PUTS(fd, ", line "); if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index fcb3c6b0354525..250ae6111a90c7 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -972,19 +972,19 @@ def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr): return convert(self._gdbval[name]) def _f_globals(self): - return self._f_special("globals") + return self._f_special("f_globals") def _f_builtins(self): - return self._f_special("builtins") + return self._f_special("f_builtins") def _f_code(self): - return self._f_special("code", PyCodeObjectPtr.from_pyobject_ptr) + 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("lasti", int_from_int) + return self._f_special("f_lasti", int_from_int) def iter_globals(self): From e5da338085a75f2b436eb62d4938938b0cc35e40 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 12:08:30 +0100 Subject: [PATCH 27/40] Add NEWS --- .../2021-07-09-12-08-17.bpo-44590.a2ntVX.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-07-09-12-08-17.bpo-44590.a2ntVX.rst 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 00000000000000..ed4d969a11ab43 --- /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. From dda0b0cf30c1233bd62404277a5391d8113e0ba0 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 12:19:44 +0100 Subject: [PATCH 28/40] Remove debugging artifact. --- Lib/test/test_faulthandler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index f37cbd911fd6a4..de986a3cbea97a 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -57,7 +57,6 @@ def temporary_filename(): class FaultHandlerTests(unittest.TestCase): - maxDiff = None def get_output(self, code, filename=None, fd=None): """ Run the specified code in Python (in a new child process) and read the From 5ecc067027a97602ac23ee81a0233ea8bc619568 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 13:29:09 +0100 Subject: [PATCH 29/40] Make symbol private --- Python/ceval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index c1ff6f8a9f4349..996dfa7aa1bc44 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4944,8 +4944,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, } - -_PyFrame * +static _PyFrame * make_coro_frame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, From 3b65a0f97902e2f896cc471ebda6357585b48516 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 14:00:30 +0100 Subject: [PATCH 30/40] Fix use-after-free error. --- Objects/frameobject.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 4278e8d1888126..d430862694b46d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -640,10 +640,8 @@ frame_dealloc(PyFrameObject *f) for (int i = 0; i < frame->stackdepth; i++) { Py_CLEAR(frame->stack[i]); } - frame->stackdepth = 0; PyMem_Free(locals); } - f->f_frame->stackdepth = 0; Py_CLEAR(f->f_back); Py_CLEAR(f->f_trace); struct _Py_frame_state *state = get_frame_state(); From 596213ddfd16a9fb662faee9d11112026d0546f1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 15:34:46 +0100 Subject: [PATCH 31/40] Add some explanatory comments. --- Include/internal/pycore_frame.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 4d6bac7a6fc64e..a09ebd91bb4b15 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -77,17 +77,23 @@ _PyFrame_ClearSpecials(_PyFrame *frame) Py_DECREF(frame->f_code); } +/* Gets the pointer to the locals array + * that precedes this frame. + */ static inline PyObject** _PyFrame_GetLocalsArray(_PyFrame *frame) { return ((PyObject **)frame) - frame->nlocalsplus; } -/* Returns a borrowed reference */ +/* For use by _PyFrame_GetFrameObject + Do not call directly. */ PyFrameObject * _PyFrame_MakeAndSetFrameObject(_PyFrame *frame); -/* Returns a borrowed reference */ +/* Gets the PyFrameObject for this frame, lazily + * creating it if necessary. + * Returns a borrowed referennce */ static inline PyFrameObject * _PyFrame_GetFrameObject(_PyFrame *frame) { @@ -98,6 +104,15 @@ _PyFrame_GetFrameObject(_PyFrame *frame) return _PyFrame_MakeAndSetFrameObject(frame); } +/* Clears all references in the frame. + * If take is non-zero, then the frame + * may be transfered to the frame object + * 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. + */ int _PyFrame_Clear(_PyFrame * frame, int take); From 386275ee1822e9688385d68bb047163bf6a5fe24 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jul 2021 17:52:25 +0100 Subject: [PATCH 32/40] Remove debugging artifact. --- Tools/gdb/libpython.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 250ae6111a90c7..c67c6226b8f074 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1733,8 +1733,7 @@ def get_pyop(self): if not frame.is_optimized_out(): return frame return orig_frame - except ValueError as v: - print("ERROR:", v.args, file=sys.stderr) + except ValueError: return None @classmethod From 039bca731cbfea403fe63402738d7b3fa70efa17 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 15 Jul 2021 17:12:34 +0100 Subject: [PATCH 33/40] Rename _PyFrame to InterpreterFrame. --- Include/cpython/ceval.h | 2 +- Include/cpython/frameobject.h | 4 +- Include/cpython/pystate.h | 4 +- Include/genobject.h | 2 +- Include/internal/pycore_ceval.h | 6 +-- Include/internal/pycore_frame.h | 34 +++++++------- Include/internal/pycore_pystate.h | 4 +- Modules/_tracemalloc.c | 6 +-- Modules/_xxsubinterpretersmodule.c | 2 +- Modules/signalmodule.c | 2 +- Objects/frameobject.c | 20 ++++---- Objects/genobject.c | 20 ++++---- Python/ceval.c | 74 +++++++++++++++--------------- Python/pystate.c | 8 ++-- Python/sysmodule.c | 2 +- Python/traceback.c | 6 +-- 16 files changed, 99 insertions(+), 97 deletions(-) diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index 1221f901312f80..44b78f6d223120 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, struct _py_frame *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 22838e235e6f46..e4cfac518bb58c 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -7,7 +7,7 @@ struct _frame { PyObject_HEAD struct _frame *f_back; /* previous frame, or NULL */ - struct _py_frame *f_frame; /* points to the frame data */ + struct _interpreter_frame *f_frame; /* points to the frame data */ PyObject *f_trace; /* Trace function */ int f_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ @@ -26,7 +26,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* -_PyFrame_New_NoTrack(struct _py_frame *, int); +_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 872394f5576b15..ab4bf8bf8483c7 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) */ - struct _py_frame *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, struct _py_frame *, 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 41efd32c4616c1..55a8b34afd60ed 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -16,7 +16,7 @@ extern "C" { #define _PyGenObject_HEAD(prefix) \ PyObject_HEAD \ /* Note: gi_frame can be NULL if the generator is "finished" */ \ - struct _py_frame *prefix##_xframe; \ + struct _interpreter_frame *prefix##_xframe; \ /* The code object backing the generator */ \ PyCodeObject *prefix##_code; \ /* List of weak reference. */ \ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index a0551b64c84f42..d2592a02f6051c 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -41,7 +41,7 @@ extern PyObject *_PyEval_BuiltinsFromGlobals( static inline PyObject* -_PyEval_EvalFrame(PyThreadState *tstate, struct _py_frame *f, int throwflag) +_PyEval_EvalFrame(PyThreadState *tstate, struct _interpreter_frame *f, int throwflag) { return tstate->interp->eval_frame(tstate, f, throwflag); } @@ -107,9 +107,9 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { #define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline() -struct _py_frame *_PyEval_GetFrame(void); +struct _interpreter_frame *_PyEval_GetFrame(void); -PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _py_frame *); +PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _interpreter_frame *); #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index a09ebd91bb4b15..e53dc216f21d3b 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -19,7 +19,7 @@ enum _framestate { typedef signed char PyFrameState; -typedef struct _py_frame { +typedef struct _interpreter_frame { PyObject *f_globals; PyObject *f_builtins; PyObject *f_locals; @@ -27,32 +27,34 @@ typedef struct _py_frame { PyFrameObject *frame_obj; /* Borrowed reference to a generator, or NULL */ PyObject *generator; - struct _py_frame *previous; + 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]; -} _PyFrame; +} InterpreterFrame; -static inline int _PyFrame_IsRunnable(_PyFrame *f) { +static inline int _PyFrame_IsRunnable(InterpreterFrame *f) { return f->f_state < FRAME_EXECUTING; } -static inline int _PyFrame_IsExecuting(_PyFrame *f) { +static inline int _PyFrame_IsExecuting(InterpreterFrame *f) { return f->f_state == FRAME_EXECUTING; } -static inline int _PyFrameHasCompleted(_PyFrame *f) { +static inline int _PyFrameHasCompleted(InterpreterFrame *f) { return f->f_state > FRAME_EXECUTING; } -#define FRAME_SPECIALS_SIZE ((sizeof(_PyFrame)-1)/sizeof(PyObject *)) +#define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) -void _PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *locals); +void _PyFrame_TakeLocals(PyFrameObject *f, InterpreterFrame *locals); static inline void -_PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject *locals, int nlocalsplus) +_PyFrame_InitializeSpecials( + InterpreterFrame *frame, PyFrameConstructor *con, + PyObject *locals, int nlocalsplus) { frame->f_code = (PyCodeObject *)Py_NewRef(con->fc_code); frame->f_builtins = Py_NewRef(con->fc_builtins); @@ -67,7 +69,7 @@ _PyFrame_InitializeSpecials(_PyFrame *frame, PyFrameConstructor *con, PyObject * } static inline void -_PyFrame_ClearSpecials(_PyFrame *frame) +_PyFrame_ClearSpecials(InterpreterFrame *frame) { frame->generator = NULL; Py_XDECREF(frame->frame_obj); @@ -81,7 +83,7 @@ _PyFrame_ClearSpecials(_PyFrame *frame) * that precedes this frame. */ static inline PyObject** -_PyFrame_GetLocalsArray(_PyFrame *frame) +_PyFrame_GetLocalsArray(InterpreterFrame *frame) { return ((PyObject **)frame) - frame->nlocalsplus; } @@ -89,13 +91,13 @@ _PyFrame_GetLocalsArray(_PyFrame *frame) /* For use by _PyFrame_GetFrameObject Do not call directly. */ PyFrameObject * -_PyFrame_MakeAndSetFrameObject(_PyFrame *frame); +_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame); /* Gets the PyFrameObject for this frame, lazily * creating it if necessary. * Returns a borrowed referennce */ static inline PyFrameObject * -_PyFrame_GetFrameObject(_PyFrame *frame) +_PyFrame_GetFrameObject(InterpreterFrame *frame) { PyFrameObject *res = frame->frame_obj; if (res != NULL) { @@ -114,13 +116,13 @@ _PyFrame_GetFrameObject(_PyFrame *frame) * frames. */ int -_PyFrame_Clear(_PyFrame * frame, int take); +_PyFrame_Clear(InterpreterFrame * frame, int take); int -_PyFrame_FastToLocalsWithError(_PyFrame *frame); +_PyFrame_FastToLocalsWithError(InterpreterFrame *frame); void -_PyFrame_LocalsToFast(_PyFrame *frame, int clear); +_PyFrame_LocalsToFast(InterpreterFrame *frame, int clear); #ifdef __cplusplus } diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 7489f36fe36a76..5cad7bcaafe02b 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -147,10 +147,10 @@ PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); -struct _py_frame *_PyThreadState_PushFrame( +struct _interpreter_frame *_PyThreadState_PushFrame( PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals); -void _PyThreadState_PopFrame(PyThreadState *tstate, struct _py_frame *frame); +void _PyThreadState_PopFrame(PyThreadState *tstate, struct _interpreter_frame *frame); #ifdef __cplusplus } diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 6532267b2f16f2..fc3d7f51ee29a1 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -299,7 +299,7 @@ hashtable_compare_traceback(const void *key1, const void *key2) static void -tracemalloc_get_frame(_PyFrame *pyframe, frame_t *frame) +tracemalloc_get_frame(InterpreterFrame *pyframe, frame_t *frame) { frame->filename = unknown_filename; int lineno = PyCode_Addr2Line(pyframe->f_code, pyframe->f_lasti*2); @@ -393,7 +393,7 @@ traceback_get_frames(traceback_t *traceback) return; } - _PyFrame *pyframe = tstate->frame; + InterpreterFrame *pyframe = tstate->frame; for (; pyframe != NULL;) { if (traceback->nframe < _Py_tracemalloc_config.max_nframe) { tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]); @@ -404,7 +404,7 @@ traceback_get_frames(traceback_t *traceback) traceback->total_nframe++; } - _PyFrame *back = pyframe->previous; + InterpreterFrame *back = pyframe->previous; pyframe = back; } } diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index f6f1c9ab197c32..11b55f7f0c7d1b 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1835,7 +1835,7 @@ _is_running(PyInterpreterState *interp) } assert(!PyErr_Occurred()); - _PyFrame *frame = tstate->frame; + InterpreterFrame *frame = tstate->frame; if (frame == NULL) { return 0; } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index a0539684b51d17..243c8c82cdc647 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1787,7 +1787,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) */ _Py_atomic_store(&is_tripped, 0); - _PyFrame *frame = tstate->frame; + 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)) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d430862694b46d..f922a7a2aa9a7c 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -625,7 +625,7 @@ frame_dealloc(PyFrameObject *f) /* Kill all local variables including specials, if we own them */ if (f->f_own_locals_memory) { f->f_own_locals_memory = 0; - _PyFrame *frame = f->f_frame; + InterpreterFrame *frame = f->f_frame; /* Don't clear code object until the end */ co = frame->f_code; frame->f_code = NULL; @@ -803,7 +803,7 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); -static _PyFrame * +static InterpreterFrame * allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)con->fc_code; @@ -816,13 +816,13 @@ allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { localsarray[i] = NULL; } - _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + InterpreterFrame *frame = (InterpreterFrame *)(localsarray + code->co_nlocalsplus); _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); return frame; } static inline PyFrameObject* -frame_alloc(_PyFrame *frame, int owns) +frame_alloc(InterpreterFrame *frame, int owns) { PyFrameObject *f; struct _Py_frame_state *state = get_frame_state(); @@ -857,7 +857,7 @@ frame_alloc(_PyFrame *frame, int owns) } void -_PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *frame) +_PyFrame_TakeLocals(PyFrameObject *f, InterpreterFrame *frame) { assert(f->f_own_locals_memory == 0); assert(frame->frame_obj == NULL); @@ -883,7 +883,7 @@ _PyFrame_TakeLocals(PyFrameObject *f, _PyFrame *frame) } PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(_PyFrame *frame, int owns) +_PyFrame_New_NoTrack(InterpreterFrame *frame, int owns) { PyFrameObject *f = frame_alloc(frame, owns); if (f == NULL) { @@ -916,7 +916,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, .fc_kwdefaults = NULL, .fc_closure = NULL }; - _PyFrame *frame = allocate_heap_frame(&desc, locals); + InterpreterFrame *frame = allocate_heap_frame(&desc, locals); if (frame == NULL) { return NULL; } @@ -928,7 +928,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, } static int -_PyFrame_OpAlreadyRan(_PyFrame *frame, int opcode, int oparg) +_PyFrame_OpAlreadyRan(InterpreterFrame *frame, int opcode, int oparg) { const _Py_CODEUNIT *code = (const _Py_CODEUNIT *)PyBytes_AS_STRING(frame->f_code->co_code); @@ -941,7 +941,7 @@ _PyFrame_OpAlreadyRan(_PyFrame *frame, int opcode, int oparg) } int -_PyFrame_FastToLocalsWithError(_PyFrame *frame) { +_PyFrame_FastToLocalsWithError(InterpreterFrame *frame) { /* Merge fast locals into f->f_locals */ PyObject *locals; PyObject **fast; @@ -1041,7 +1041,7 @@ PyFrame_FastToLocals(PyFrameObject *f) } void -_PyFrame_LocalsToFast(_PyFrame *frame, int clear) +_PyFrame_LocalsToFast(InterpreterFrame *frame, int clear) { /* Merge locals into fast locals */ PyObject *locals; diff --git a/Objects/genobject.c b/Objects/genobject.c index e4cd5e463d42fc..c9b2bf572512bd 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -35,7 +35,7 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; if (frame != NULL) { assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); Py_VISIT(frame->frame_obj); @@ -141,7 +141,7 @@ gen_dealloc(PyGenObject *gen) and GC_Del. */ Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer); } - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; if (frame != NULL) { gen->gi_xframe = NULL; frame->previous = NULL; @@ -162,7 +162,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) { PyThreadState *tstate = _PyThreadState_GET(); - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; PyObject *result; *presult = NULL; @@ -347,7 +347,7 @@ _PyGen_yf(PyGenObject *gen) PyObject *yf = NULL; if (gen->gi_xframe) { - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); @@ -439,7 +439,7 @@ _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(); - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; /* Since we are fast-tracking things by skipping the eval loop, @@ -447,7 +447,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, will be reported correctly to the user. */ /* XXX We should probably be updating the current frame somewhere in ceval.c. */ - _PyFrame *prev = tstate->frame; + InterpreterFrame *prev = tstate->frame; frame->previous = prev; tstate->frame = frame; /* Close the generator that we are currently iterating with @@ -855,7 +855,7 @@ PyTypeObject PyGen_Type = { }; static PyObject * -make_gen(PyTypeObject *type, PyFrameConstructor *con, _PyFrame *frame) +make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame) { PyGenObject *gen = PyObject_GC_New(PyGenObject, type); if (gen == NULL) { @@ -890,7 +890,7 @@ static PyObject * compute_cr_origin(int origin_depth); PyObject * -_Py_MakeCoro(PyFrameConstructor *con, _PyFrame *frame) +_Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame) { int coro_flags = ((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); @@ -1284,7 +1284,7 @@ PyTypeObject _PyCoroWrapper_Type = { static PyObject * compute_cr_origin(int origin_depth) { - _PyFrame *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) { @@ -2004,7 +2004,7 @@ static PyObject * async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) { PyGenObject *gen = (PyGenObject*)o->agt_gen; - _PyFrame *frame = gen->gi_xframe; + InterpreterFrame *frame = gen->gi_xframe; PyObject *retval; if (o->agt_state == AWAITABLE_STATE_CLOSED) { diff --git a/Python/ceval.c b/Python/ceval.c index af9ef713be4043..f192a4b3504912 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 *, _PyFrame *, + PyThreadState *, InterpreterFrame *, int, PyObject *); static int call_trace_protected(Py_tracefunc, PyObject *, - PyThreadState *, _PyFrame *, + PyThreadState *, InterpreterFrame *, int, PyObject *); static void call_exc_trace(Py_tracefunc, PyObject *, - PyThreadState *, _PyFrame *); + PyThreadState *, InterpreterFrame *); static int maybe_call_line_trace(Py_tracefunc, PyObject *, - PyThreadState *, _PyFrame *, int); -static void maybe_dtrace_line(_PyFrame *, PyTraceInfo *, int); -static void dtrace_function_entry(_PyFrame *); -static void dtrace_function_return(_PyFrame *); + 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 *, _PyFrame *, +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 *, - _PyFrame *, 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); @@ -1393,7 +1393,7 @@ eval_frame_handle_pending(PyThreadState *tstate) #define LOCALS() frame->f_locals PyObject* _Py_HOT_FUNCTION -_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyFrame *frame, int throwflag) +_PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int throwflag) { _Py_EnsureTstateNotNULL(tstate); @@ -5047,7 +5047,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, } -static _PyFrame * +static InterpreterFrame * make_coro_frame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, @@ -5065,7 +5065,7 @@ make_coro_frame(PyThreadState *tstate, for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { localsarray[i] = NULL; } - _PyFrame *frame = (_PyFrame *)(localsarray + code->co_nlocalsplus); + 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)) { @@ -5082,7 +5082,7 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, PyObject *kwnames) { assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); - _PyFrame* frame = make_coro_frame(tstate, con, locals, args, argcount, kwnames); + InterpreterFrame *frame = make_coro_frame(tstate, con, locals, args, argcount, kwnames); if (frame == NULL) { return NULL; } @@ -5094,12 +5094,12 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, return gen; } -static _PyFrame * +static InterpreterFrame * _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - _PyFrame * frame = _PyThreadState_PushFrame(tstate, con, locals); + InterpreterFrame * frame = _PyThreadState_PushFrame(tstate, con, locals); if (frame == NULL) { return NULL; } @@ -5114,7 +5114,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, } PyFrameObject * -_PyFrame_MakeAndSetFrameObject(_PyFrame *frame) +_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame) { assert(frame->frame_obj == NULL); PyObject *error_type, *error_value, *error_traceback; @@ -5132,8 +5132,8 @@ _PyFrame_MakeAndSetFrameObject(_PyFrame *frame) return f; } -static _PyFrame * -copy_frame_to_heap(_PyFrame *frame) +static InterpreterFrame * +copy_frame_to_heap(InterpreterFrame *frame) { Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); @@ -5144,12 +5144,12 @@ copy_frame_to_heap(_PyFrame *frame) } PyObject **locals = _PyFrame_GetLocalsArray(frame); memcpy(copy, locals, size); - _PyFrame *res = (_PyFrame *)(copy + frame->nlocalsplus); + InterpreterFrame *res = (InterpreterFrame *)(copy + frame->nlocalsplus); return res; } int -_PyFrame_Clear(_PyFrame * frame, int take) +_PyFrame_Clear(InterpreterFrame * frame, int take) { PyObject **localsarray = ((PyObject **)frame)-frame->nlocalsplus; if (frame->frame_obj) { @@ -5184,7 +5184,7 @@ _PyFrame_Clear(_PyFrame * frame, int take) } static int -_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyFrame * frame) +_PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame) { ++tstate->recursion_depth; assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); @@ -5210,7 +5210,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, if (is_coro) { return make_coro(tstate, con, locals, args, argcount, kwnames); } - _PyFrame * frame = _PyEvalFramePushAndInit( + InterpreterFrame * frame = _PyEvalFramePushAndInit( tstate, con, locals, args, argcount, kwnames); if (frame == NULL) { return NULL; @@ -5527,7 +5527,7 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str) static void call_exc_trace(Py_tracefunc func, PyObject *self, PyThreadState *tstate, - _PyFrame *f) + InterpreterFrame *f) { PyObject *type, *value, *traceback, *orig_traceback, *arg; int err; @@ -5557,7 +5557,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self, static int call_trace_protected(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, _PyFrame *frame, + PyThreadState *tstate, InterpreterFrame *frame, int what, PyObject *arg) { PyObject *type, *value, *traceback; @@ -5578,7 +5578,7 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, } static void -initialize_trace_info(PyTraceInfo *trace_info, _PyFrame *frame) +initialize_trace_info(PyTraceInfo *trace_info, InterpreterFrame *frame) { PyCodeObject *code = frame->f_code; if (trace_info->code != code) { @@ -5589,7 +5589,7 @@ initialize_trace_info(PyTraceInfo *trace_info, _PyFrame *frame) static int call_trace(Py_tracefunc func, PyObject *obj, - PyThreadState *tstate, _PyFrame *frame, + PyThreadState *tstate, InterpreterFrame *frame, int what, PyObject *arg) { int result; @@ -5636,7 +5636,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, _PyFrame *frame, int instr_prev) + PyThreadState *tstate, InterpreterFrame *frame, int instr_prev) { int result = 0; @@ -5805,7 +5805,7 @@ _PyEval_GetAsyncGenFinalizer(void) return tstate->async_gen_finalizer; } -_PyFrame * +InterpreterFrame * _PyEval_GetFrame(void) { PyThreadState *tstate = _PyThreadState_GET(); @@ -5829,7 +5829,7 @@ PyEval_GetFrame(void) PyObject * _PyEval_GetBuiltins(PyThreadState *tstate) { - _PyFrame *frame = tstate->frame; + InterpreterFrame *frame = tstate->frame; if (frame != NULL) { return frame->f_builtins; } @@ -5862,7 +5862,7 @@ PyObject * PyEval_GetLocals(void) { PyThreadState *tstate = _PyThreadState_GET(); - _PyFrame *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; if (current_frame == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist"); return NULL; @@ -5881,7 +5881,7 @@ PyObject * PyEval_GetGlobals(void) { PyThreadState *tstate = _PyThreadState_GET(); - _PyFrame *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; if (current_frame == NULL) { return NULL; } @@ -5892,7 +5892,7 @@ int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { PyThreadState *tstate = _PyThreadState_GET(); - _PyFrame *current_frame = tstate->frame; + InterpreterFrame *current_frame = tstate->frame; int result = cf->cf_flags != 0; if (current_frame != NULL) { @@ -6130,7 +6130,7 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) } static PyObject * -import_name(PyThreadState *tstate, _PyFrame *frame, +import_name(PyThreadState *tstate, InterpreterFrame *frame, PyObject *name, PyObject *fromlist, PyObject *level) { _Py_IDENTIFIER(__import__); @@ -6469,7 +6469,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevop static PyObject * unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, - _PyFrame *frame, const _Py_CODEUNIT *next_instr) + InterpreterFrame *frame, const _Py_CODEUNIT *next_instr) { PyObject *res; if (Py_REFCNT(v) == 2) { @@ -6580,7 +6580,7 @@ _PyEval_RequestCodeExtraIndex(freefunc free) } static void -dtrace_function_entry(_PyFrame *frame) +dtrace_function_entry(InterpreterFrame *frame) { const char *filename; const char *funcname; @@ -6595,7 +6595,7 @@ dtrace_function_entry(_PyFrame *frame) } static void -dtrace_function_return(_PyFrame *frame) +dtrace_function_return(InterpreterFrame *frame) { const char *filename; const char *funcname; @@ -6611,7 +6611,7 @@ dtrace_function_return(_PyFrame *frame) /* DTrace equivalent of maybe_call_line_trace. */ static void -maybe_dtrace_line(_PyFrame *frame, +maybe_dtrace_line(InterpreterFrame *frame, PyTraceInfo *trace_info, int instr_prev) { diff --git a/Python/pystate.c b/Python/pystate.c index 93fc8241a9ccbc..f6d3cc403b7df8 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1261,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) { - _PyFrame *frame = t->frame; + InterpreterFrame *frame = t->frame; if (frame == NULL) { continue; } @@ -2037,7 +2037,7 @@ _PyThreadState_PushChunk(PyThreadState *tstate, int size) return res; } -_PyFrame * +InterpreterFrame * _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)con->fc_code; @@ -2055,7 +2055,7 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObjec else { tstate->datastack_top = top; } - _PyFrame * frame = (_PyFrame *)(localsarray + nlocalsplus); + InterpreterFrame * frame = (InterpreterFrame *)(localsarray + nlocalsplus); _PyFrame_InitializeSpecials(frame, con, locals, nlocalsplus); for (int i=0; i < nlocalsplus; i++) { localsarray[i] = NULL; @@ -2064,7 +2064,7 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObjec } void -_PyThreadState_PopFrame(PyThreadState *tstate, _PyFrame * frame) +_PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame * frame) { PyObject **locals = _PyFrame_GetLocalsArray(frame); if (locals == &tstate->datastack_chunk->data[0]) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 93fe5f7e0c35f5..5dfa917e8ffb20 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1815,7 +1815,7 @@ sys__getframe_impl(PyObject *module, int depth) /*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/ { PyThreadState *tstate = _PyThreadState_GET(); - _PyFrame *frame = tstate->frame; + InterpreterFrame *frame = tstate->frame; if (_PySys_Audit(tstate, "sys._getframe", NULL) < 0) { return NULL; diff --git a/Python/traceback.c b/Python/traceback.c index 19198b764143d1..0344ee1d4c8eae 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -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 @@ -995,7 +995,7 @@ _Py_DumpASCII(int fd, PyObject *text) This function is signal safe. */ static void -dump_frame(int fd, _PyFrame *frame) +dump_frame(int fd, InterpreterFrame *frame) { PyCodeObject *code = frame->f_code; PUTS(fd, " File "); @@ -1033,7 +1033,7 @@ dump_frame(int fd, _PyFrame *frame) static void dump_traceback(int fd, PyThreadState *tstate, int write_header) { - _PyFrame *frame; + InterpreterFrame *frame; unsigned int depth; if (write_header) { From 2cad33b1a9a9b9091296d636fd0ec4c4b32e26ad Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 19 Jul 2021 10:00:40 +0100 Subject: [PATCH 34/40] Remove use-after-free in assert. --- Python/ceval.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index f192a4b3504912..5ecb85427c2d5e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5167,7 +5167,6 @@ _PyFrame_Clear(InterpreterFrame * frame, int take) return 0; } Py_DECREF(f); - assert(_PyObject_IsFreed((PyObject *)f) || Py_REFCNT(f) == 0); } for (int i = 0; i < frame->nlocalsplus; i++) { Py_XDECREF(localsarray[i]); From decf20926038fedbe1cd149b3a844ab9bc8b4164 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 19 Jul 2021 14:34:24 +0100 Subject: [PATCH 35/40] Make name of frame argument consistent across _PyEval_Vector, _PyEval_EvalFrame and _PyEval_EvalFrameDefault to assist gdb plugin. --- Include/internal/pycore_ceval.h | 4 ++-- Python/ceval.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index d2592a02f6051c..66ddc991a9b11a 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, struct _interpreter_frame *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 * diff --git a/Python/ceval.c b/Python/ceval.c index 5ecb85427c2d5e..e2201ec6f802a1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5209,7 +5209,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, if (is_coro) { return make_coro(tstate, con, locals, args, argcount, kwnames); } - InterpreterFrame * frame = _PyEvalFramePushAndInit( + InterpreterFrame *frame = _PyEvalFramePushAndInit( tstate, con, locals, args, argcount, kwnames); if (frame == NULL) { return NULL; From 666b61899c56cab0aa0440bb8894e44e1ab1ae45 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 19 Jul 2021 16:44:01 +0100 Subject: [PATCH 36/40] Allow for old gdbs still using Python 2. --- Tools/gdb/libpython.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index c67c6226b8f074..8b09563c18c9b1 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1113,6 +1113,21 @@ def print_traceback(self): 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' @@ -1779,7 +1794,7 @@ def print_summary(self): if self.is_evalframe(): pyop = self.get_pyop() if pyop: - line = PyObjectPtr.get_truncated_repr(pyop, MAX_OUTPUT_LEN) + line = pyop.get_truncated_repr(MAX_OUTPUT_LEN) write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line)) if not pyop.is_optimized_out(): line = pyop.current_line() From 90ed5b6b0fb1c46349e2701075433782417cbe75 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 21 Jul 2021 12:15:10 +0100 Subject: [PATCH 37/40] Various small clarifications as suggested by Pablo. --- Include/internal/pycore_frame.h | 13 +++++++++---- Include/internal/pycore_pystate.h | 5 ----- Objects/frameobject.c | 9 ++++++--- Objects/typeobject.c | 1 + Python/ceval.c | 2 +- Python/pystate.c | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index e53dc216f21d3b..99efd197abffaa 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -49,7 +49,7 @@ static inline int _PyFrameHasCompleted(InterpreterFrame *f) { #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) -void _PyFrame_TakeLocals(PyFrameObject *f, InterpreterFrame *locals); +void _PyFrame_TakeInterpreterFrame(PyFrameObject *f, InterpreterFrame *locals); static inline void _PyFrame_InitializeSpecials( @@ -107,13 +107,13 @@ _PyFrame_GetFrameObject(InterpreterFrame *frame) } /* Clears all references in the frame. - * If take is non-zero, then the frame - * may be transfered to the frame object + * 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. + * frames like the ones in generators and coroutines. */ int _PyFrame_Clear(InterpreterFrame * frame, int take); @@ -124,6 +124,11 @@ _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 } #endif diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 5cad7bcaafe02b..4b894f3eff4967 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -147,11 +147,6 @@ PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); -struct _interpreter_frame *_PyThreadState_PushFrame( - PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals); - -void _PyThreadState_PopFrame(PyThreadState *tstate, struct _interpreter_frame *frame); - #ifdef __cplusplus } #endif diff --git a/Objects/frameobject.c b/Objects/frameobject.c index f922a7a2aa9a7c..8235483c5317dc 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -857,7 +857,7 @@ frame_alloc(InterpreterFrame *frame, int owns) } void -_PyFrame_TakeLocals(PyFrameObject *f, InterpreterFrame *frame) +_PyFrame_TakeInterpreterFrame(PyFrameObject *f, InterpreterFrame *frame) { assert(f->f_own_locals_memory == 0); assert(frame->frame_obj == NULL); @@ -866,17 +866,20 @@ _PyFrame_TakeLocals(PyFrameObject *f, InterpreterFrame *frame) 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. Nothing we can do about it */ + /* Memory error here. */ + assert(_PyErr_GetTopmostException(_PyThreadState_GET())->exc_type == 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; } - frame->previous = NULL; if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { _PyObject_GC_TRACK((PyObject *)f); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f45e11cbc99670..e19ec7da5577ed 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8887,6 +8887,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, return -1; } + 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)) { diff --git a/Python/ceval.c b/Python/ceval.c index e2201ec6f802a1..93fa5d4032154b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5162,7 +5162,7 @@ _PyFrame_Clear(InterpreterFrame * frame, int take) return -1; } } - _PyFrame_TakeLocals(f, frame); + _PyFrame_TakeInterpreterFrame(f, frame); Py_DECREF(f); return 0; } diff --git a/Python/pystate.c b/Python/pystate.c index f6d3cc403b7df8..ba69b19a326cb0 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2042,7 +2042,7 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObjec { PyCodeObject *code = (PyCodeObject *)con->fc_code; int nlocalsplus = code->co_nlocalsplus; - int size = nlocalsplus + code->co_stacksize + + size_t size = nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; PyObject **localsarray = tstate->datastack_top; PyObject **top = localsarray + size; From 593a348dadcb8e3c87fdf9766a75846db051b86d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 21 Jul 2021 13:23:57 +0100 Subject: [PATCH 38/40] Refactor interpreter frame code into its own file. Improve a few names. --- Include/internal/pycore_frame.h | 17 ++-- Makefile.pre.in | 1 + Objects/frameobject.c | 45 +---------- Objects/genobject.c | 17 +--- PCbuild/pythoncore.vcxproj | 1 + Python/ceval.c | 69 ---------------- Python/frame.c | 135 ++++++++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 139 deletions(-) create mode 100644 Python/frame.c diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 99efd197abffaa..cd465d73bc6bdb 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -49,7 +49,8 @@ static inline int _PyFrameHasCompleted(InterpreterFrame *f) { #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) -void _PyFrame_TakeInterpreterFrame(PyFrameObject *f, InterpreterFrame *locals); +InterpreterFrame * +_PyInterpreterFrame_HeapAlloc(PyFrameConstructor *con, PyObject *locals); static inline void _PyFrame_InitializeSpecials( @@ -68,17 +69,6 @@ _PyFrame_InitializeSpecials( frame->f_state = FRAME_CREATED; } -static inline void -_PyFrame_ClearSpecials(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); -} - /* Gets the pointer to the locals array * that precedes this frame. */ @@ -118,6 +108,9 @@ _PyFrame_GetFrameObject(InterpreterFrame *frame) int _PyFrame_Clear(InterpreterFrame * frame, int take); +int +_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg); + int _PyFrame_FastToLocalsWithError(InterpreterFrame *frame); diff --git a/Makefile.pre.in b/Makefile.pre.in index 97f21d454464bd..aaf519f80ed837 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/Objects/frameobject.c b/Objects/frameobject.c index 8235483c5317dc..d1b8048fceadf7 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -671,21 +671,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) return 0; } assert(f->f_frame->frame_obj == NULL); - /* locals */ - PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); - for (int i = 0; i < f->f_frame->nlocalsplus; i++) { - Py_VISIT(locals[i]); - } - /* stack */ - for (int i = 0; i < f->f_frame->stackdepth; i++) { - Py_VISIT(f->f_frame->stack[i]); - } - - Py_VISIT(f->f_frame->f_globals); - Py_VISIT(f->f_frame->f_builtins); - Py_VISIT(f->f_frame->f_locals); - Py_VISIT(f->f_frame->f_code); - return 0; + return _PyFrame_Traverse(f->f_frame, visit, arg); } static int @@ -856,35 +842,6 @@ frame_alloc(InterpreterFrame *frame, int owns) return f; } -void -_PyFrame_TakeInterpreterFrame(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_GetTopmostException(_PyThreadState_GET())->exc_type == 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); - } -} - PyFrameObject* _Py_HOT_FUNCTION _PyFrame_New_NoTrack(InterpreterFrame *frame, int owns) { diff --git a/Objects/genobject.c b/Objects/genobject.c index c9b2bf572512bd..4d7f3de504bc67 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -38,21 +38,10 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) InterpreterFrame *frame = gen->gi_xframe; if (frame != NULL) { assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); - Py_VISIT(frame->frame_obj); - /* locals */ - PyObject **locals = _PyFrame_GetLocalsArray(frame); - for (int i = 0; i < frame->nlocalsplus; i++) { - Py_VISIT(locals[i]); + int err = _PyFrame_Traverse(frame, visit, arg); + if (err) { + return err; } - /* stack */ - for (int i = 0; i < frame->stackdepth; i++) { - Py_VISIT(frame->stack[i]); - } - - Py_VISIT(frame->f_globals); - Py_VISIT(frame->f_builtins); - Py_VISIT(frame->f_locals); - Py_VISIT(frame->f_code); } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 66d35e0cb24d0e..29de7779dd9349 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -455,6 +455,7 @@ + diff --git a/Python/ceval.c b/Python/ceval.c index 93fa5d4032154b..ab16ab94f017e0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5113,75 +5113,6 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, return frame; } -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; -} - -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; - } - } - _PyFrame_TakeInterpreterFrame(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]); - } - _PyFrame_ClearSpecials(frame); - if (take) { - PyMem_Free(_PyFrame_GetLocalsArray(frame)); - } - return 0; -} - static int _PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame) { diff --git a/Python/frame.c b/Python/frame.c new file mode 100644 index 00000000000000..1b6179b4a2eb03 --- /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 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_GetTopmostException(_PyThreadState_GET())->exc_type == 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; +} From b775f13c01e80be8f29d116f007b316f7c81c42b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 21 Jul 2021 14:20:48 +0100 Subject: [PATCH 39/40] Tidy up assert. --- Python/frame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/frame.c b/Python/frame.c index 1b6179b4a2eb03..ae4284346ea12f 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -85,7 +85,7 @@ take_ownership(PyFrameObject *f, InterpreterFrame *frame) PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous); if (back == NULL) { /* Memory error here. */ - assert(_PyErr_GetTopmostException(_PyThreadState_GET())->exc_type == PyExc_MemoryError); + assert(PyErr_ExceptionMatches(PyExc_MemoryError)); /* Nothing we can do about it */ PyErr_Clear(); _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL); From e8476b2de2ad1b99ac99adad5a459d16bd8001e4 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 21 Jul 2021 18:44:17 +0100 Subject: [PATCH 40/40] Fix warning on Windows. --- Python/pystate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index ba69b19a326cb0..6a15b54d1dd84f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2016,8 +2016,8 @@ _Py_GetConfig(void) #define MINIMUM_OVERHEAD 1000 -_Py_NO_INLINE PyObject ** -_PyThreadState_PushChunk(PyThreadState *tstate, int size) +static PyObject ** +push_chunk(PyThreadState *tstate, int size) { assert(tstate->datastack_top + size >= tstate->datastack_limit); @@ -2044,10 +2044,11 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObjec 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) { - localsarray = _PyThreadState_PushChunk(tstate, size); + localsarray = push_chunk(tstate, (int)size); if (localsarray == NULL) { return NULL; }