Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow minimal frames in frame stack.
  • Loading branch information
markshannon committed Mar 15, 2023
commit 06e914b5af0c72b2862d37f1e02a04f0992894e0
1 change: 1 addition & 0 deletions Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# error "this header file must not be included directly"
#endif


PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
PyAPI_FUNC(void) PyEval_SetProfileAllThreads(Py_tracefunc, PyObject *);
PyAPI_DATA(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
Expand Down
9 changes: 0 additions & 9 deletions Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,6 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,

PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int);

/* -- Caveat emptor --
* The concept of entry frames is an implementation detail of the CPython
* interpreter. This API is considered unstable and is provided for the
* convenience of debuggers, profilers and state-inspecting tools. Notice that
* this API can be changed in future minor versions if the underlying frame
* mechanism change or the concept of an 'entry frame' or its semantics becomes
* obsolete or outdated. */

PyAPI_FUNC(int) _PyFrame_IsEntryFrame(PyFrameObject *frame);

PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
4 changes: 3 additions & 1 deletion Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *);
#define PyTrace_OPCODE 7


struct _PyInterpreterFrame;

typedef struct {
PyCodeObject *code; // The code object for the bounds. May be NULL.
PyCodeAddressRange bounds; // Only valid if code != NULL.
Expand All @@ -79,7 +81,7 @@ typedef struct _PyCFrame {
*/
uint8_t use_tracing; // 0 or 255 (or'ed into opcode, hence 8-bit type)
/* Pointer to the currently executing frame (it can be NULL) */
struct _PyInterpreterFrame *current_frame;
struct _PyFrame *current_frame;
struct _PyCFrame *previous;
} _PyCFrame;

Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ extern PyStatus _PyPerfTrampoline_AfterFork_Child(void);
extern _PyPerf_Callbacks _Py_perfmap_callbacks;
#endif

struct _PyInterpreterFrame;

static inline PyObject*
_PyEval_EvalFrame(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag)
{
Expand Down
28 changes: 18 additions & 10 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ enum _frameowner {
FRAME_OWNED_BY_CSTACK = 3,
};

typedef struct _PyInterpreterFrame {
typedef struct _PyFrame {
PyObject *f_executable; /* Strong reference */
struct _PyInterpreterFrame *previous;
struct _PyFrame *previous;
} _PyFrame;

typedef struct _PyInterpreterFrame {
_PyFrame base;
PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
Expand All @@ -70,8 +74,8 @@ typedef struct _PyInterpreterFrame {
((int)((IF)->prev_instr - _PyCode_CODE(_PyFrame_GetCode(IF))))

static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) {
assert(PyCode_Check(f->f_executable));
return (PyCodeObject *)f->f_executable;
assert(PyCode_Check(f->base.f_executable));
return (PyCodeObject *)f->base.f_executable;
}

static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
Expand Down Expand Up @@ -118,7 +122,7 @@ _PyFrame_Initialize(
PyObject *locals, PyCodeObject *code, int null_locals_from)
{
frame->f_funcobj = (PyObject *)func;
frame->f_executable = Py_NewRef(code);
frame->base.f_executable = Py_NewRef(code);
frame->f_builtins = func->func_builtins;
frame->f_globals = func->func_globals;
frame->f_locals = locals;
Expand Down Expand Up @@ -163,8 +167,12 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer)
* Frames owned by a generator are always complete.
*/
static inline bool
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
_PyFrame_IsIncomplete(_PyFrame *frameptr)
{
if (!PyCode_Check(frameptr->f_executable)) {
return true;
}
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)frameptr;
if (frame->owner == FRAME_OWNED_BY_CSTACK) {
return true;
}
Expand All @@ -173,12 +181,12 @@ _PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
}

static inline _PyInterpreterFrame *
_PyFrame_GetFirstComplete(_PyInterpreterFrame *frame)
_PyFrame_GetFirstComplete(_PyFrame *frame)
{
while (frame && _PyFrame_IsIncomplete(frame)) {
while (frame != NULL && _PyFrame_IsIncomplete(frame)) {
frame = frame->previous;
}
return frame;
return (_PyInterpreterFrame *)frame;
}

static inline _PyInterpreterFrame *
Expand All @@ -199,7 +207,7 @@ static inline PyFrameObject *
_PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
{

assert(!_PyFrame_IsIncomplete(frame));
assert(!_PyFrame_IsIncomplete(&frame->base));
PyFrameObject *res = frame->frame_obj;
if (res != NULL) {
return res;
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ def test_getitem_with_error(self):
rc, out, err = assert_python_failure('-c', code)
err = decode_stderr(err)
if 'SystemError: ' not in err:
print(err)
self.assertRegex(err,
r'Fatal Python error: _Py_CheckSlotResult: '
r'Slot __getitem__ of type dict succeeded '
Expand Down
2 changes: 1 addition & 1 deletion Modules/_tracemalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ traceback_get_frames(traceback_t *traceback)
if (traceback->total_nframe < UINT16_MAX) {
traceback->total_nframe++;
}
pyframe = _PyFrame_GetFirstComplete(pyframe->previous);
pyframe = _PyFrame_GetFirstComplete(pyframe->base.previous);
}
}

Expand Down
3 changes: 1 addition & 2 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,7 @@ _is_running(PyInterpreterState *interp)
}

assert(!PyErr_Occurred());
_PyInterpreterFrame *frame = tstate->cframe->current_frame;
if (frame == NULL) {
if (tstate->cframe->current_frame == NULL) {
return 0;
}
return 1;
Expand Down
51 changes: 22 additions & 29 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ first_line_not_before(int *lines, int len, int line)
static PyFrameState
_PyFrame_GetState(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
if (frame->f_frame->stacktop == 0) {
return FRAME_CLEARED;
}
Expand Down Expand Up @@ -881,8 +881,8 @@ frame_dealloc(PyFrameObject *f)
assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data);
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
/* Don't clear code object until the end */
co = frame->f_executable;
frame->f_executable = NULL;
co = frame->base.f_executable;
frame->base.f_executable = NULL;
Py_CLEAR(frame->f_funcobj);
Py_CLEAR(frame->f_locals);
PyObject **locals = _PyFrame_GetLocalsArray(frame);
Expand Down Expand Up @@ -1024,7 +1024,7 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
PyCodeObject *code = (PyCodeObject *)func->func_code;
_PyFrame_Initialize(frame, (PyFunctionObject*)Py_NewRef(func),
Py_XNewRef(locals), code, 0);
frame->previous = NULL;
frame->base.previous = NULL;
}

PyFrameObject*
Expand Down Expand Up @@ -1078,7 +1078,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
// This frame needs to be "complete", so pretend that the first RESUME ran:
f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
assert(!_PyFrame_IsIncomplete(f->f_frame));
assert(!_PyFrame_IsIncomplete(&f->f_frame->base));
Py_DECREF(func);
_PyObject_GC_TRACK(f);
return f;
Expand Down Expand Up @@ -1279,7 +1279,7 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
int
PyFrame_FastToLocalsWithError(PyFrameObject *f)
{
assert(!_PyFrame_IsIncomplete(f->f_frame));
assert(!_PyFrame_IsIncomplete(&f->f_frame->base));
if (f == NULL) {
PyErr_BadInternalCall();
return -1;
Expand All @@ -1295,7 +1295,7 @@ void
PyFrame_FastToLocals(PyFrameObject *f)
{
int res;
assert(!_PyFrame_IsIncomplete(f->f_frame));
assert(!_PyFrame_IsIncomplete(&f->f_frame->base));
assert(!PyErr_Occurred());

res = PyFrame_FastToLocalsWithError(f);
Expand Down Expand Up @@ -1380,27 +1380,20 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
void
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
{
assert(!_PyFrame_IsIncomplete(f->f_frame));
if (_PyFrame_IsIncomplete(&f->f_frame->base)) {
return;
}
if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) {
_PyFrame_LocalsToFast(f->f_frame, clear);
_PyFrame_LocalsToFast((_PyInterpreterFrame *)f->f_frame, clear);
f->f_fast_as_locals = 0;
}
}

int
_PyFrame_IsEntryFrame(PyFrameObject *frame)
{
assert(frame != NULL);
_PyInterpreterFrame *f = frame->f_frame;
assert(!_PyFrame_IsIncomplete(f));
return f->previous && f->previous->owner == FRAME_OWNED_BY_CSTACK;
}

PyCodeObject *
PyFrame_GetCode(PyFrameObject *frame)
{
assert(frame != NULL);
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
PyCodeObject *code = _PyFrame_GetCode(frame->f_frame);
assert(code != NULL);
return (PyCodeObject*)Py_NewRef(code);
Expand All @@ -1411,13 +1404,13 @@ PyFrameObject*
PyFrame_GetBack(PyFrameObject *frame)
{
assert(frame != NULL);
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
PyFrameObject *back = frame->f_back;
if (back == NULL) {
_PyInterpreterFrame *prev = frame->f_frame->previous;
prev = _PyFrame_GetFirstComplete(prev);
if (prev) {
back = _PyFrame_GetFrameObject(prev);
_PyFrame *prev = frame->f_frame->base.previous;
_PyInterpreterFrame *py_prev = _PyFrame_GetFirstComplete(prev);
if (py_prev) {
back = _PyFrame_GetFrameObject(py_prev);
}
}
return (PyFrameObject*)Py_XNewRef(back);
Expand All @@ -1426,28 +1419,28 @@ PyFrame_GetBack(PyFrameObject *frame)
PyObject*
PyFrame_GetLocals(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
return frame_getlocals(frame, NULL);
}

PyObject*
PyFrame_GetGlobals(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
return frame_getglobals(frame, NULL);
}

PyObject*
PyFrame_GetBuiltins(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
return frame_getbuiltins(frame, NULL);
}

int
PyFrame_GetLasti(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
int lasti = _PyInterpreterFrame_LASTI(frame->f_frame);
if (lasti < 0) {
return -1;
Expand All @@ -1458,7 +1451,7 @@ PyFrame_GetLasti(PyFrameObject *frame)
PyObject *
PyFrame_GetGenerator(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
assert(!_PyFrame_IsIncomplete(&frame->f_frame->base));
if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) {
return NULL;
}
Expand Down
22 changes: 11 additions & 11 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ gen_dealloc(PyGenObject *gen)
if (gen->gi_frame_state < FRAME_CLEARED) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
gen->gi_frame_state = FRAME_CLEARED;
frame->previous = NULL;
frame->base.previous = NULL;
_PyFrame_ClearExceptCode(frame);
}
if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) {
Expand Down Expand Up @@ -230,7 +230,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
assert(tstate->exc_info == prev_exc_info);
assert(gen->gi_exc_state.previous_item == NULL);
assert(gen->gi_frame_state != FRAME_EXECUTING);
assert(frame->previous == NULL);
assert(frame->base.previous == NULL);

/* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */
Expand Down Expand Up @@ -459,9 +459,9 @@ _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. */
_PyInterpreterFrame *prev = tstate->cframe->current_frame;
frame->previous = prev;
tstate->cframe->current_frame = frame;
_PyFrame *prev = tstate->cframe->current_frame;
frame->base.previous = prev;
tstate->cframe->current_frame = &frame->base;
/* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */
PyFrameState state = gen->gi_frame_state;
Expand All @@ -470,7 +470,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
typ, val, tb);
gen->gi_frame_state = state;
tstate->cframe->current_frame = prev;
frame->previous = NULL;
frame->base.previous = NULL;
} else {
/* `yf` is an iterator or a coroutine-like object. */
PyObject *meth;
Expand Down Expand Up @@ -921,11 +921,11 @@ _Py_MakeCoro(PyFunctionObject *func)
if (origin_depth == 0) {
((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
} else {
_PyInterpreterFrame *frame = tstate->cframe->current_frame;
_PyFrame *frame = tstate->cframe->current_frame;
assert(frame);
assert(_PyFrame_IsIncomplete(frame));
frame = _PyFrame_GetFirstComplete(frame->previous);
PyObject *cr_origin = compute_cr_origin(origin_depth, frame);
_PyInterpreterFrame * pyframe = _PyFrame_GetFirstComplete(frame->previous);
PyObject *cr_origin = compute_cr_origin(origin_depth, pyframe);
((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
if (!cr_origin) {
Py_DECREF(coro);
Expand Down Expand Up @@ -1311,7 +1311,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
/* First count how many frames we have */
int frame_count = 0;
for (; frame && frame_count < origin_depth; ++frame_count) {
frame = _PyFrame_GetFirstComplete(frame->previous);
frame = _PyFrame_GetFirstComplete(frame->base.previous);
}

/* Now collect them */
Expand All @@ -1330,7 +1330,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
return NULL;
}
PyTuple_SET_ITEM(cr_origin, i, frameinfo);
frame = _PyFrame_GetFirstComplete(frame->previous);
frame = _PyFrame_GetFirstComplete(frame->base.previous);
}

return cr_origin;
Expand Down
Loading