diff options
author | Mark Shannon <mark@hotpy.org> | 2021-07-26 10:22:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-26 10:22:16 (GMT) |
commit | ae0a2b756255629140efcbe57fc2e714f0267aa3 (patch) | |
tree | 8710e8c7a398c9ec0add227fab607f367242a7e5 /Objects | |
parent | 0363a4014d90df17a29042de008ef0b659f92505 (diff) | |
download | cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.zip cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.gz cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.bz2 |
bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it.
* Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector.
* Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation.
* Move ownership of InterpreterFrame in generator from frame object to generator object.
* Do not create frame objects for Python calls.
* Do not create frame objects for generators.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/frameobject.c | 296 | ||||
-rw-r--r-- | Objects/genobject.c | 265 | ||||
-rw-r--r-- | Objects/typeobject.c | 8 |
3 files changed, 336 insertions, 233 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 813ec56..d1b8048 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -14,7 +14,6 @@ #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { - {"f_back", T_OBJECT, OFF(f_back), READONLY}, {"f_trace_lines", T_BOOL, OFF(f_trace_lines), 0}, {"f_trace_opcodes", T_BOOL, OFF(f_trace_opcodes), 0}, {NULL} /* Sentinel */ @@ -33,7 +32,7 @@ frame_getlocals(PyFrameObject *f, void *closure) { if (PyFrame_FastToLocalsWithError(f) < 0) return NULL; - PyObject *locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + PyObject *locals = f->f_frame->f_locals; Py_INCREF(locals); return locals; } @@ -46,7 +45,7 @@ PyFrame_GetLineNumber(PyFrameObject *f) return f->f_lineno; } else { - return PyCode_Addr2Line(_PyFrame_GetCode(f), f->f_lasti*2); + return PyCode_Addr2Line(f->f_frame->f_code, f->f_frame->f_lasti*2); } } @@ -65,16 +64,16 @@ frame_getlineno(PyFrameObject *f, void *closure) static PyObject * frame_getlasti(PyFrameObject *f, void *closure) { - if (f->f_lasti < 0) { + if (f->f_frame->f_lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(f->f_lasti*2); + return PyLong_FromLong(f->f_frame->f_lasti*2); } static PyObject * frame_getglobals(PyFrameObject *f, void *closure) { - PyObject *globals = _PyFrame_GetGlobals(f); + PyObject *globals = f->f_frame->f_globals; if (globals == NULL) { globals = Py_None; } @@ -85,7 +84,7 @@ frame_getglobals(PyFrameObject *f, void *closure) static PyObject * frame_getbuiltins(PyFrameObject *f, void *closure) { - PyObject *builtins = _PyFrame_GetBuiltins(f); + PyObject *builtins = f->f_frame->f_builtins; if (builtins == NULL) { builtins = Py_None; } @@ -102,6 +101,16 @@ frame_getcode(PyFrameObject *f, void *closure) return (PyObject *)PyFrame_GetCode(f); } +static PyObject * +frame_getback(PyFrameObject *f, void *closure) +{ + PyObject *res = (PyObject *)PyFrame_GetBack(f); + if (res == NULL) { + Py_RETURN_NONE; + } + return res; +} + /* Given the index of the effective opcode, scan back to construct the oparg with EXTENDED_ARG */ static unsigned int @@ -388,9 +397,9 @@ first_line_not_before(int *lines, int len, int line) static void frame_stack_pop(PyFrameObject *f) { - assert(f->f_stackdepth > 0); - f->f_stackdepth--; - PyObject *v = f->f_valuestack[f->f_stackdepth]; + assert(f->f_frame->stackdepth > 0); + f->f_frame->stackdepth--; + PyObject *v = f->f_frame->stack[f->f_frame->stackdepth]; Py_DECREF(v); } @@ -430,7 +439,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore * In addition, jumps are forbidden when not tracing, * as this is a debugging feature. */ - switch(f->f_state) { + switch(f->f_frame->f_state) { case FRAME_CREATED: PyErr_Format(PyExc_ValueError, "can't jump from the 'call' trace event of a new frame"); @@ -472,7 +481,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } new_lineno = (int)l_new_lineno; - if (new_lineno < _PyFrame_GetCode(f)->co_firstlineno) { + if (new_lineno < f->f_frame->f_code->co_firstlineno) { PyErr_Format(PyExc_ValueError, "line %d comes before the current code block", new_lineno); @@ -481,8 +490,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore /* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this * should never overflow. */ - int len = (int)(PyBytes_GET_SIZE(_PyFrame_GetCode(f)->co_code) / sizeof(_Py_CODEUNIT)); - int *lines = marklines(_PyFrame_GetCode(f), len); + int len = (int)(PyBytes_GET_SIZE(f->f_frame->f_code->co_code) / sizeof(_Py_CODEUNIT)); + int *lines = marklines(f->f_frame->f_code, len); if (lines == NULL) { return -1; } @@ -496,7 +505,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - int64_t *stacks = mark_stacks(_PyFrame_GetCode(f), len); + int64_t *stacks = mark_stacks(f->f_frame->f_code, len); if (stacks == NULL) { PyMem_Free(lines); return -1; @@ -504,7 +513,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore int64_t best_stack = OVERFLOWED; int best_addr = -1; - int64_t start_stack = stacks[f->f_lasti]; + int64_t start_stack = stacks[f->f_frame->f_lasti]; int err = -1; const char *msg = "cannot find bytecode for specified line"; for (int i = 0; i < len; i++) { @@ -538,7 +547,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } /* Unwind block stack. */ - if (f->f_state == FRAME_SUSPENDED) { + if (f->f_frame->f_state == FRAME_SUSPENDED) { /* Account for value popped by yield */ start_stack = pop_value(start_stack); } @@ -546,9 +555,9 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore frame_stack_pop(f); start_stack = pop_value(start_stack); } - /* Finally set the new f_lasti and return OK. */ + /* Finally set the new lasti and return OK. */ f->f_lineno = 0; - f->f_lasti = best_addr; + f->f_frame->f_lasti = best_addr; return 0; } @@ -579,6 +588,7 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure) static PyGetSetDef frame_getsetlist[] = { + {"f_back", (getter)frame_getback, NULL, NULL}, {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, @@ -612,29 +622,27 @@ frame_dealloc(PyFrameObject *f) Py_TRASHCAN_SAFE_BEGIN(f) PyCodeObject *co = NULL; - /* Kill all local variables including specials. */ - if (f->f_localsptr) { + /* Kill all local variables including specials, if we own them */ + if (f->f_own_locals_memory) { + f->f_own_locals_memory = 0; + InterpreterFrame *frame = f->f_frame; /* Don't clear code object until the end */ - co = _PyFrame_GetCode(f); - PyObject **specials = _PyFrame_Specials(f); - Py_CLEAR(specials[FRAME_SPECIALS_GLOBALS_OFFSET]); - Py_CLEAR(specials[FRAME_SPECIALS_BUILTINS_OFFSET]); - Py_CLEAR(specials[FRAME_SPECIALS_LOCALS_OFFSET]); + co = frame->f_code; + frame->f_code = NULL; + Py_CLEAR(frame->f_globals); + Py_CLEAR(frame->f_builtins); + Py_CLEAR(frame->f_locals); + PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_CLEAR(f->f_localsptr[i]); - } - /* Free items on stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_XDECREF(f->f_valuestack[i]); + Py_CLEAR(locals[i]); } - if (f->f_own_locals_memory) { - PyMem_Free(f->f_localsptr); - f->f_own_locals_memory = 0; + /* stack */ + for (int i = 0; i < frame->stackdepth; i++) { + Py_CLEAR(frame->stack[i]); } - f->f_localsptr = NULL; + PyMem_Free(locals); } - f->f_stackdepth = 0; - Py_XDECREF(f->f_back); + Py_CLEAR(f->f_back); Py_CLEAR(f->f_trace); struct _Py_frame_state *state = get_frame_state(); #ifdef Py_DEBUG @@ -654,29 +662,16 @@ frame_dealloc(PyFrameObject *f) Py_TRASHCAN_SAFE_END(f) } -static inline Py_ssize_t -frame_nslots(PyFrameObject *frame) -{ - return frame->f_valuestack - frame->f_localsptr; -} - static int frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - - /* locals */ - PyObject **localsplus = f->f_localsptr; - for (Py_ssize_t i = frame_nslots(f); --i >= 0; ++localsplus) { - Py_VISIT(*localsplus); - } - - /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_VISIT(f->f_valuestack[i]); + if (f->f_own_locals_memory == 0) { + return 0; } - return 0; + assert(f->f_frame->frame_obj == NULL); + return _PyFrame_Traverse(f->f_frame, visit, arg); } static int @@ -687,34 +682,35 @@ frame_tp_clear(PyFrameObject *f) * frame may also point to this frame, believe itself to still be * active, and try cleaning up this frame again. */ - f->f_state = FRAME_CLEARED; + f->f_frame->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); - PyCodeObject *co = _PyFrame_GetCode(f); + /* locals */ - for (int i = 0; i < co->co_nlocalsplus; i++) { - Py_CLEAR(f->f_localsptr[i]); + PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); + for (int i = 0; i < f->f_frame->nlocalsplus; i++) { + Py_CLEAR(locals[i]); } /* stack */ - for (int i = 0; i < f->f_stackdepth; i++) { - Py_CLEAR(f->f_valuestack[i]); + for (int i = 0; i < f->f_frame->stackdepth; i++) { + Py_CLEAR(f->f_frame->stack[i]); } - f->f_stackdepth = 0; + f->f_frame->stackdepth = 0; return 0; } static PyObject * frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { - if (_PyFrame_IsExecuting(f)) { + if (_PyFrame_IsExecuting(f->f_frame)) { PyErr_SetString(PyExc_RuntimeError, "cannot clear an executing frame"); return NULL; } - if (f->f_gen) { - _PyGen_Finalize(f->f_gen); - assert(f->f_gen == NULL); + if (f->f_frame->generator) { + _PyGen_Finalize(f->f_frame->generator); + assert(f->f_frame->generator == NULL); } (void)frame_tp_clear(f); Py_RETURN_NONE; @@ -729,7 +725,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) Py_ssize_t res; res = sizeof(PyFrameObject); if (f->f_own_locals_memory) { - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_frame->f_code; res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); } return PyLong_FromSsize_t(res); @@ -742,7 +738,7 @@ static PyObject * frame_repr(PyFrameObject *f) { int lineno = PyFrame_GetLineNumber(f); - PyCodeObject *code = _PyFrame_GetCode(f); + PyCodeObject *code = f->f_frame->f_code; return PyUnicode_FromFormat( "<frame at %p, file %R, line %d, code %S>", f, code->co_filename, lineno, code->co_name); @@ -793,33 +789,39 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); -static inline PyFrameObject* -frame_alloc(PyCodeObject *code, PyObject **localsarray) +static InterpreterFrame * +allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) { - int owns; - PyFrameObject *f; + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; + PyObject **localsarray = PyMem_Malloc(sizeof(PyObject *)*size); if (localsarray == NULL) { - int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; - localsarray = PyMem_Malloc(sizeof(PyObject *)*size); - if (localsarray == NULL) { - PyErr_NoMemory(); - return NULL; - } - for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { - localsarray[i] = NULL; - } - owns = 1; + PyErr_NoMemory(); + return NULL; } - else { - owns = 0; + for (Py_ssize_t i=0; i < code->co_nlocalsplus; i++) { + localsarray[i] = NULL; } + InterpreterFrame *frame = (InterpreterFrame *)(localsarray + code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + return frame; +} + +static inline PyFrameObject* +frame_alloc(InterpreterFrame *frame, int owns) +{ + PyFrameObject *f; struct _Py_frame_state *state = get_frame_state(); if (state->free_list == NULL) { f = PyObject_GC_New(PyFrameObject, &PyFrame_Type); if (f == NULL) { if (owns) { - PyMem_Free(localsarray); + Py_XDECREF(frame->f_code); + Py_XDECREF(frame->f_builtins); + Py_XDECREF(frame->f_globals); + Py_XDECREF(frame->f_locals); + PyMem_Free(frame); } return NULL; } @@ -835,66 +837,23 @@ frame_alloc(PyCodeObject *code, PyObject **localsarray) state->free_list = state->free_list->f_back; _Py_NewReference((PyObject *)f); } - f->f_localsptr = localsarray; + f->f_frame = frame; f->f_own_locals_memory = owns; return f; } -int -_PyFrame_TakeLocals(PyFrameObject *f) -{ - assert(f->f_own_locals_memory == 0); - assert(f->f_stackdepth == 0); - Py_ssize_t size = frame_nslots(f); - PyObject **copy = PyMem_Malloc(sizeof(PyObject *)*size); - if (copy == NULL) { - for (int i = 0; i < size; i++) { - PyObject *o = f->f_localsptr[i]; - Py_XDECREF(o); - } - PyErr_NoMemory(); - return -1; - } - for (int i = 0; i < size; i++) { - PyObject *o = f->f_localsptr[i]; - copy[i] = o; - } - f->f_own_locals_memory = 1; - f->f_localsptr = copy; - f->f_valuestack = copy + size; - return 0; -} - PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals, PyObject **localsarray) +_PyFrame_New_NoTrack(InterpreterFrame *frame, int owns) { - assert(con != NULL); - assert(con->fc_globals != NULL); - assert(con->fc_builtins != NULL); - assert(con->fc_code != NULL); - assert(locals == NULL || PyMapping_Check(locals)); - PyCodeObject *code = (PyCodeObject *)con->fc_code; - - PyFrameObject *f = frame_alloc(code, localsarray); + PyFrameObject *f = frame_alloc(frame, owns); if (f == NULL) { return NULL; } - - PyObject **specials = f->f_localsptr + code->co_nlocalsplus; - f->f_valuestack = specials + FRAME_SPECIALS_SIZE; - f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame); - specials[FRAME_SPECIALS_CODE_OFFSET] = Py_NewRef(con->fc_code); - specials[FRAME_SPECIALS_BUILTINS_OFFSET] = Py_NewRef(con->fc_builtins); - specials[FRAME_SPECIALS_GLOBALS_OFFSET] = Py_NewRef(con->fc_globals); - specials[FRAME_SPECIALS_LOCALS_OFFSET] = Py_XNewRef(locals); + f->f_back = NULL; f->f_trace = NULL; - f->f_stackdepth = 0; f->f_trace_lines = 1; f->f_trace_opcodes = 0; - f->f_gen = NULL; - f->f_lasti = -1; f->f_lineno = 0; - f->f_state = FRAME_CREATED; return f; } @@ -917,7 +876,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, .fc_kwdefaults = NULL, .fc_closure = NULL }; - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, &desc, locals, NULL); + InterpreterFrame *frame = allocate_heap_frame(&desc, locals); + if (frame == NULL) { + return NULL; + } + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); if (f) { _PyObject_GC_TRACK(f); } @@ -925,11 +888,11 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, } static int -_PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) +_PyFrame_OpAlreadyRan(InterpreterFrame *frame, int opcode, int oparg) { const _Py_CODEUNIT *code = - (const _Py_CODEUNIT *)PyBytes_AS_STRING(_PyFrame_GetCode(f)->co_code); - for (int i = 0; i < f->f_lasti; i++) { + (const _Py_CODEUNIT *)PyBytes_AS_STRING(frame->f_code->co_code); + for (int i = 0; i < frame->f_lasti; i++) { if (_Py_OPCODE(code[i]) == opcode && _Py_OPARG(code[i]) == oparg) { return 1; } @@ -938,25 +901,19 @@ _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) } int -PyFrame_FastToLocalsWithError(PyFrameObject *f) -{ +_PyFrame_FastToLocalsWithError(InterpreterFrame *frame) { /* Merge fast locals into f->f_locals */ PyObject *locals; PyObject **fast; PyCodeObject *co; - - if (f == NULL) { - PyErr_BadInternalCall(); - return -1; - } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = frame->f_locals; if (locals == NULL) { - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET] = PyDict_New(); + locals = frame->f_locals = PyDict_New(); if (locals == NULL) return -1; } - co = _PyFrame_GetCode(f); - fast = f->f_localsptr; + co = frame->f_code; + fast = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); @@ -974,9 +931,9 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *value = fast[i]; - if (f->f_state != FRAME_CLEARED) { + if (frame->f_state != FRAME_CLEARED) { if (kind & CO_FAST_FREE) { - // The cell was set by _PyEval_MakeFrameVector() from + // The cell was set when the frame was created from // the function's closure. assert(value != NULL && PyCell_Check(value)); value = PyCell_GET(value); @@ -988,12 +945,12 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) // run yet. if (value != NULL) { if (PyCell_Check(value) && - _PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) { + _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { // (likely) MAKE_CELL must have executed already. value = PyCell_GET(value); } // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL), - // with the initial value set by _PyEval_MakeFrameVector()... + // with the initial value set when the frame was created... // (unlikely) ...or it was set to some initial value by // an earlier call to PyFrame_LocalsToFast(). } @@ -1021,6 +978,16 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) return 0; } +int +PyFrame_FastToLocalsWithError(PyFrameObject *f) +{ + if (f == NULL) { + PyErr_BadInternalCall(); + return -1; + } + return _PyFrame_FastToLocalsWithError(f->f_frame); +} + void PyFrame_FastToLocals(PyFrameObject *f) { @@ -1034,21 +1001,18 @@ PyFrame_FastToLocals(PyFrameObject *f) } void -PyFrame_LocalsToFast(PyFrameObject *f, int clear) +_PyFrame_LocalsToFast(InterpreterFrame *frame, int clear) { /* Merge locals into fast locals */ PyObject *locals; PyObject **fast; PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; - if (f == NULL || f->f_state == FRAME_CLEARED) { - return; - } - locals = _PyFrame_Specials(f)[FRAME_SPECIALS_LOCALS_OFFSET]; + locals = frame->f_locals; if (locals == NULL) return; - fast = f->f_localsptr; - co = _PyFrame_GetCode(f); + fast = _PyFrame_GetLocalsArray(frame); + co = frame->f_code; PyErr_Fetch(&error_type, &error_value, &error_traceback); for (int i = 0; i < co->co_nlocalsplus; i++) { @@ -1070,7 +1034,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyObject *oldvalue = fast[i]; PyObject *cell = NULL; if (kind == CO_FAST_FREE) { - // The cell was set by _PyEval_MakeFrameVector() from + // The cell was set when the frame was created from // the function's closure. assert(oldvalue != NULL && PyCell_Check(oldvalue)); cell = oldvalue; @@ -1078,7 +1042,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) else if (kind & CO_FAST_CELL && oldvalue != NULL) { /* Same test as in PyFrame_FastToLocals() above. */ if (PyCell_Check(oldvalue) && - _PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) { + _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) { // (likely) MAKE_CELL must have executed already. cell = oldvalue; } @@ -1102,6 +1066,15 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyErr_Restore(error_type, error_value, error_traceback); } +void +PyFrame_LocalsToFast(PyFrameObject *f, int clear) +{ + if (f == NULL || f->f_frame->f_state == FRAME_CLEARED) { + return; + } + _PyFrame_LocalsToFast(f->f_frame, clear); +} + /* Clear out the free list */ void _PyFrame_ClearFreeList(PyInterpreterState *interp) @@ -1141,7 +1114,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); - PyCodeObject *code = _PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_frame->f_code; assert(code != NULL); Py_INCREF(code); return code; @@ -1153,6 +1126,9 @@ PyFrame_GetBack(PyFrameObject *frame) { assert(frame != NULL); PyFrameObject *back = frame->f_back; + if (back == NULL && frame->f_frame->previous != NULL) { + back = _PyFrame_GetFrameObject(frame->f_frame->previous); + } Py_XINCREF(back); return back; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 8cc965a..86cd9cf 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -6,6 +6,7 @@ #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() #include "frameobject.h" +#include "pycore_frame.h" #include "structmember.h" // PyMemberDef #include "opcode.h" @@ -31,10 +32,17 @@ exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { - Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); + InterpreterFrame *frame = gen->gi_xframe; + if (frame != NULL) { + assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + int err = _PyFrame_Traverse(frame, visit, arg); + if (err) { + return err; + } + } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ return exc_state_traverse(&gen->gi_exc_state, visit, arg); @@ -47,7 +55,7 @@ _PyGen_Finalize(PyObject *self) PyObject *res = NULL; PyObject *error_type, *error_value, *error_traceback; - if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame)) { + if (gen->gi_xframe == NULL || _PyFrameHasCompleted(gen->gi_xframe)) { /* Generator isn't paused, so no need to close */ return; } @@ -79,7 +87,7 @@ _PyGen_Finalize(PyObject *self) issue a RuntimeWarning. */ if (gen->gi_code != NULL && ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && - gen->gi_frame->f_lasti == -1) + gen->gi_xframe->f_lasti == -1) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -122,9 +130,11 @@ gen_dealloc(PyGenObject *gen) and GC_Del. */ Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer); } - if (gen->gi_frame != NULL) { - gen->gi_frame->f_gen = NULL; - Py_CLEAR(gen->gi_frame); + InterpreterFrame *frame = gen->gi_xframe; + if (frame != NULL) { + gen->gi_xframe = NULL; + frame->previous = NULL; + _PyFrame_Clear(frame, 1); } if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin); @@ -141,11 +151,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) { PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = gen->gi_frame; + InterpreterFrame *frame = gen->gi_xframe; PyObject *result; *presult = NULL; - if (f != NULL && _PyFrame_IsExecuting(f)) { + if (frame != NULL && _PyFrame_IsExecuting(frame)) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -156,7 +166,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_ValueError, msg); return PYGEN_ERROR; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (frame == NULL || _PyFrameHasCompleted(frame)) { if (PyCoro_CheckExact(gen) && !closing) { /* `gen` is an exhausted coroutine: raise an error, except when called from gen_close(), which should @@ -175,19 +185,15 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return PYGEN_ERROR; } - assert(_PyFrame_IsRunnable(f)); - assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); + assert(_PyFrame_IsRunnable(frame)); + assert(frame->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); - gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result; - gen->gi_frame->f_stackdepth++; + frame->stack[frame->stackdepth] = result; + frame->stackdepth++; - /* Generators always return to their most recent caller, not - * necessarily their creator. */ - Py_XINCREF(tstate->frame); - assert(f->f_back == NULL); - f->f_back = tstate->frame; + frame->previous = tstate->frame; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -197,20 +203,20 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } - result = _PyEval_EvalFrame(tstate, f, exc); + result = _PyEval_EvalFrame(tstate, frame, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - /* Don't keep the reference to f_back any longer than necessary. It + assert(tstate->frame == frame->previous); + /* Don't keep the reference to previous any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - assert(f->f_back == tstate->frame); - Py_CLEAR(f->f_back); + frame->previous = NULL; /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (!_PyFrameHasCompleted(f)) { + if (!_PyFrameHasCompleted(frame)) { *presult = result; return PYGEN_NEXT; } @@ -245,10 +251,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* generator can't be rerun, so release the frame */ /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - gen->gi_frame->f_gen = NULL; - gen->gi_frame = NULL; - Py_DECREF(f); + frame->generator = NULL; + gen->gi_xframe = NULL; + _PyFrame_Clear(frame, 1); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } @@ -328,13 +334,13 @@ PyObject * _PyGen_yf(PyGenObject *gen) { PyObject *yf = NULL; - PyFrameObject *f = gen->gi_frame; - if (f) { + if (gen->gi_xframe) { + InterpreterFrame *frame = gen->gi_xframe; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); - if (f->f_lasti < 0) { + if (frame->f_lasti < 0) { /* Return immediately if the frame didn't start yet. YIELD_FROM always come after LOAD_CONST: a code object should not start with YIELD_FROM */ @@ -342,10 +348,10 @@ _PyGen_yf(PyGenObject *gen) return NULL; } - if (code[(f->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) + if (code[(frame->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - assert(f->f_stackdepth > 0); - yf = f->f_valuestack[f->f_stackdepth-1]; + assert(frame->stackdepth > 0); + yf = frame->stack[frame->stackdepth-1]; Py_INCREF(yf); } @@ -360,10 +366,10 @@ gen_close(PyGenObject *gen, PyObject *args) int err = 0; if (yf) { - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(yf); } if (err == 0) @@ -410,10 +416,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, We have to allow some awaits to work it through, hence the `close_on_genexit` parameter here. */ - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(yf); if (err < 0) return gen_send_ex(gen, Py_None, 1, 0); @@ -422,22 +428,26 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { /* `yf` is a generator or a coroutine. */ PyThreadState *tstate = _PyThreadState_GET(); - PyFrameObject *f = tstate->frame; + InterpreterFrame *frame = gen->gi_xframe; + /* Since we are fast-tracking things by skipping the eval loop, we need to update the current frame so the stack trace will be reported correctly to the user. */ /* XXX We should probably be updating the current frame somewhere in ceval.c. */ - tstate->frame = gen->gi_frame; + InterpreterFrame *prev = tstate->frame; + frame->previous = prev; + tstate->frame = frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); - gen->gi_frame->f_state = state; - tstate->frame = f; + gen->gi_xframe->f_state = state; + tstate->frame = prev; + frame->previous = NULL; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; @@ -449,24 +459,24 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } - PyFrameState state = gen->gi_frame->f_state; - gen->gi_frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_xframe->f_state; + gen->gi_xframe->f_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_frame->f_state = state; + gen->gi_xframe->f_state = state; Py_DECREF(meth); } Py_DECREF(yf); if (!ret) { PyObject *val; /* Pop subiterator from stack */ - assert(gen->gi_frame->f_stackdepth > 0); - gen->gi_frame->f_stackdepth--; - ret = gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth]; + assert(gen->gi_xframe->stackdepth > 0); + gen->gi_xframe->stackdepth--; + ret = gen->gi_xframe->stack[gen->gi_xframe->stackdepth]; assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ - assert(gen->gi_frame->f_lasti >= 0); - gen->gi_frame->f_lasti += 1; + assert(gen->gi_xframe->f_lasti >= 0); + gen->gi_xframe->f_lasti += 1; if (_PyGen_FetchStopIterationValue(&val) == 0) { ret = gen_send(gen, val); Py_DECREF(val); @@ -723,10 +733,28 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored)) static PyObject * gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored)) { - if (gen->gi_frame == NULL) { + if (gen->gi_xframe == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_xframe)); +} + +static PyObject * +_gen_getframe(PyGenObject *gen, const char *const name) +{ + if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { + return NULL; + } + if (gen->gi_xframe == NULL) { + Py_RETURN_NONE; + } + return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(gen->gi_xframe)); +} + +static PyObject * +gen_getframe(PyGenObject *gen, void *Py_UNUSED(ignored)) +{ + return _gen_getframe(gen, "gi_frame"); } static PyGetSetDef gen_getsetlist[] = { @@ -737,11 +765,11 @@ static PyGetSetDef gen_getsetlist[] = { {"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, PyDoc_STR("object being iterated by yield from, or None")}, {"gi_running", (getter)gen_getrunning, NULL, NULL}, + {"gi_frame", (getter)gen_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY|PY_AUDIT_READ}, {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY|PY_AUDIT_READ}, {NULL} /* Sentinel */ }; @@ -815,6 +843,84 @@ PyTypeObject PyGen_Type = { }; static PyObject * +make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame) +{ + PyGenObject *gen = PyObject_GC_New(PyGenObject, type); + if (gen == NULL) { + assert(frame->frame_obj == NULL); + _PyFrame_Clear(frame, 1); + return NULL; + } + gen->gi_xframe = frame; + frame->generator = (PyObject *)gen; + gen->gi_code = frame->f_code; + Py_INCREF(gen->gi_code); + gen->gi_weakreflist = NULL; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; + gen->gi_exc_state.previous_item = NULL; + if (con->fc_name != NULL) + gen->gi_name = con->fc_name; + else + gen->gi_name = gen->gi_code->co_name; + Py_INCREF(gen->gi_name); + if (con->fc_qualname != NULL) + gen->gi_qualname = con->fc_qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); + _PyObject_GC_TRACK(gen); + return (PyObject *)gen; +} + +static PyObject * +compute_cr_origin(int origin_depth); + +PyObject * +_Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame) +{ + int coro_flags = ((PyCodeObject *)con->fc_code)->co_flags & + (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); + assert(coro_flags); + if (coro_flags == CO_GENERATOR) { + return make_gen(&PyGen_Type, con, frame); + } + if (coro_flags == CO_ASYNC_GENERATOR) { + PyAsyncGenObject *o; + o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, con, frame); + if (o == NULL) { + return NULL; + } + o->ag_finalizer = NULL; + o->ag_closed = 0; + o->ag_hooks_inited = 0; + o->ag_running_async = 0; + return (PyObject*)o; + } + assert (coro_flags == CO_COROUTINE); + PyObject *coro = make_gen(&PyCoro_Type, con, frame); + if (!coro) { + return NULL; + } + PyThreadState *tstate = _PyThreadState_GET(); + int origin_depth = tstate->coroutine_origin_tracking_depth; + + if (origin_depth == 0) { + ((PyCoroObject *)coro)->cr_origin = NULL; + } else { + PyObject *cr_origin = compute_cr_origin(origin_depth); + ((PyCoroObject *)coro)->cr_origin = cr_origin; + if (!cr_origin) { + Py_DECREF(coro); + return NULL; + } + } + + return coro; +} + +static PyObject * gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, PyObject *name, PyObject *qualname) { @@ -823,8 +929,16 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, Py_DECREF(f); return NULL; } - gen->gi_frame = f; - f->f_gen = (PyObject *) gen; + + /* Take ownership of the frame */ + assert(f->f_frame->frame_obj == NULL); + assert(f->f_own_locals_memory); + gen->gi_xframe = f->f_frame; + gen->gi_xframe->frame_obj = f; + f->f_own_locals_memory = 0; + gen->gi_xframe->generator = (PyObject *) gen; + assert(PyObject_GC_IsTracked((PyObject *)f)); + gen->gi_code = PyFrame_GetCode(f); gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_type = NULL; @@ -958,12 +1072,19 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored)) static PyObject * cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored)) { - if (coro->cr_frame == NULL) { + if (coro->cr_xframe == NULL) { Py_RETURN_FALSE; } - return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame)); + return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_xframe)); +} + +static PyObject * +cr_getframe(PyCoroObject *coro, void *Py_UNUSED(ignored)) +{ + return _gen_getframe((PyGenObject *)coro, "cr_frame"); } + static PyGetSetDef coro_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the coroutine")}, @@ -972,11 +1093,11 @@ static PyGetSetDef coro_getsetlist[] = { {"cr_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, {"cr_running", (getter)cr_getrunning, NULL, NULL}, + {"cr_frame", (getter)cr_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef coro_memberlist[] = { - {"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY|PY_AUDIT_READ}, {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ}, {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY}, {NULL} /* Sentinel */ @@ -1150,11 +1271,11 @@ PyTypeObject _PyCoroWrapper_Type = { static PyObject * compute_cr_origin(int origin_depth) { - PyFrameObject *frame = PyEval_GetFrame(); + InterpreterFrame *frame = _PyEval_GetFrame(); /* First count how many frames we have */ int frame_count = 0; for (; frame && frame_count < origin_depth; ++frame_count) { - frame = frame->f_back; + frame = frame->previous; } /* Now collect them */ @@ -1162,20 +1283,19 @@ compute_cr_origin(int origin_depth) if (cr_origin == NULL) { return NULL; } - frame = PyEval_GetFrame(); + frame = _PyEval_GetFrame(); for (int i = 0; i < frame_count; ++i) { - PyCodeObject *code = PyFrame_GetCode(frame); + PyCodeObject *code = frame->f_code; PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, - PyFrame_GetLineNumber(frame), + PyCode_Addr2Line(frame->f_code, frame->f_lasti*2), code->co_name); - Py_DECREF(code); if (!frameinfo) { Py_DECREF(cr_origin); return NULL; } PyTuple_SET_ITEM(cr_origin, i, frameinfo); - frame = frame->f_back; + frame = frame->previous; } return cr_origin; @@ -1346,6 +1466,11 @@ async_gen_athrow(PyAsyncGenObject *o, PyObject *args) return async_gen_athrow_new(o, args); } +static PyObject * +ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored)) +{ + return _gen_getframe((PyGenObject *)ag, "ag_frame"); +} static PyGetSetDef async_gen_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, @@ -1354,11 +1479,11 @@ static PyGetSetDef async_gen_getsetlist[] = { PyDoc_STR("qualified name of the async generator")}, {"ag_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, + {"ag_frame", (getter)ag_getframe, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef async_gen_memberlist[] = { - {"ag_frame", T_OBJECT, offsetof(PyAsyncGenObject, ag_frame), READONLY|PY_AUDIT_READ}, {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async), READONLY}, {"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY|PY_AUDIT_READ}, @@ -1865,7 +1990,7 @@ static PyObject * async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) { PyGenObject *gen = (PyGenObject*)o->agt_gen; - PyFrameObject *f = gen->gi_frame; + InterpreterFrame *frame = gen->gi_xframe; PyObject *retval; if (o->agt_state == AWAITABLE_STATE_CLOSED) { @@ -1875,7 +2000,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (f == NULL || _PyFrameHasCompleted(f)) { + if (frame == NULL || _PyFrameHasCompleted(frame)) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 42a9978..43b4d07 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -11,6 +11,7 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_unionobject.h" // _Py_union_type_or #include "frameobject.h" +#include "pycore_frame.h" #include "opcode.h" // MAKE_CELL #include "structmember.h" // PyMemberDef @@ -8867,12 +8868,13 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, return -1; } - PyObject *firstarg = f->f_localsptr[0]; + assert(f->f_frame->nlocalsplus > 0); + PyObject *firstarg = _PyFrame_GetLocalsArray(f->f_frame)[0]; // The first argument might be a cell. if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) { // "firstarg" is a cell here unless (very unlikely) super() // was called from the C-API before the first MAKE_CELL op. - if (f->f_lasti >= 0) { + if (f->f_frame->f_lasti >= 0) { assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); @@ -8892,7 +8894,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); assert(PyUnicode_Check(name)); if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) { - PyObject *cell = f->f_localsptr[i]; + PyObject *cell = _PyFrame_GetLocalsArray(f->f_frame)[i]; if (cell == NULL || !PyCell_Check(cell)) { PyErr_SetString(PyExc_RuntimeError, "super(): bad __class__ cell"); |