bpo-40941: Unify implicit and explicit state in the frame and generator objects into a single value. (GH-20803)
* Merge gen and frame state variables into one. * Replace stack pointer with depth in PyFrameObject. Makes code easier to read and saves a word of memory.
This commit is contained in:
parent
8e836bb21c
commit
cb9879b948
|
@ -4,6 +4,21 @@
|
||||||
# error "this header file must not be included directly"
|
# error "this header file must not be included directly"
|
||||||
#endif
|
#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 {
|
typedef struct {
|
||||||
int b_type; /* what kind of block this is */
|
int b_type; /* what kind of block this is */
|
||||||
int b_handler; /* where to jump to find handler */
|
int b_handler; /* where to jump to find handler */
|
||||||
|
@ -18,11 +33,8 @@ struct _frame {
|
||||||
PyObject *f_globals; /* global symbol table (PyDictObject) */
|
PyObject *f_globals; /* global symbol table (PyDictObject) */
|
||||||
PyObject *f_locals; /* local symbol table (any mapping) */
|
PyObject *f_locals; /* local symbol table (any mapping) */
|
||||||
PyObject **f_valuestack; /* points after the last local */
|
PyObject **f_valuestack; /* points after the last local */
|
||||||
/* Next free slot in f_valuestack. Frame creation sets to f_valuestack.
|
|
||||||
Frame evaluation usually NULLs it, but a frame that yields sets it
|
|
||||||
to the current stack top. */
|
|
||||||
PyObject **f_stacktop;
|
|
||||||
PyObject *f_trace; /* Trace function */
|
PyObject *f_trace; /* Trace function */
|
||||||
|
int f_stackdepth; /* Depth of value stack */
|
||||||
char f_trace_lines; /* Emit per-line trace events? */
|
char f_trace_lines; /* Emit per-line trace events? */
|
||||||
char f_trace_opcodes; /* Emit per-opcode trace events? */
|
char f_trace_opcodes; /* Emit per-opcode trace events? */
|
||||||
|
|
||||||
|
@ -37,11 +49,22 @@ struct _frame {
|
||||||
bytecode index. */
|
bytecode index. */
|
||||||
int f_lineno; /* Current line number */
|
int f_lineno; /* Current line number */
|
||||||
int f_iblock; /* index in f_blockstack */
|
int f_iblock; /* index in f_blockstack */
|
||||||
char f_executing; /* whether the frame is still executing */
|
PyFrameState f_state; /* What state the frame is in */
|
||||||
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
|
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
|
||||||
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
|
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 */
|
/* Standard object interface */
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ extern "C" {
|
||||||
PyObject_HEAD \
|
PyObject_HEAD \
|
||||||
/* Note: gi_frame can be NULL if the generator is "finished" */ \
|
/* Note: gi_frame can be NULL if the generator is "finished" */ \
|
||||||
PyFrameObject *prefix##_frame; \
|
PyFrameObject *prefix##_frame; \
|
||||||
/* True if generator is being executed. */ \
|
|
||||||
char prefix##_running; \
|
|
||||||
/* The code object backing the generator */ \
|
/* The code object backing the generator */ \
|
||||||
PyObject *prefix##_code; \
|
PyObject *prefix##_code; \
|
||||||
/* List of weak reference. */ \
|
/* List of weak reference. */ \
|
||||||
|
|
|
@ -881,7 +881,7 @@ And more, added later.
|
||||||
>>> i.gi_running = 42
|
>>> i.gi_running = 42
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: readonly attribute
|
AttributeError: attribute 'gi_running' of 'generator' objects is not writable
|
||||||
>>> def g():
|
>>> def g():
|
||||||
... yield me.gi_running
|
... yield me.gi_running
|
||||||
>>> me = g()
|
>>> me = g()
|
||||||
|
|
|
@ -1236,7 +1236,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
nfrees = len(x.f_code.co_freevars)
|
nfrees = len(x.f_code.co_freevars)
|
||||||
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
|
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
|
||||||
ncells + nfrees - 1
|
ncells + nfrees - 1
|
||||||
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
||||||
# function
|
# function
|
||||||
def func(): pass
|
def func(): pass
|
||||||
check(func, size('13P'))
|
check(func, size('13P'))
|
||||||
|
@ -1253,7 +1253,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check(bar, size('PP'))
|
check(bar, size('PP'))
|
||||||
# generator
|
# generator
|
||||||
def get_gen(): yield 1
|
def get_gen(): yield 1
|
||||||
check(get_gen(), size('Pb2PPP4P'))
|
check(get_gen(), size('P2PPP4P'))
|
||||||
# iterator
|
# iterator
|
||||||
check(iter('abc'), size('lP'))
|
check(iter('abc'), size('lP'))
|
||||||
# callable-iterator
|
# callable-iterator
|
||||||
|
|
|
@ -938,6 +938,9 @@ class TestPEP380Operation(unittest.TestCase):
|
||||||
res.append(g1.throw(MyErr))
|
res.append(g1.throw(MyErr))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
pass
|
pass
|
||||||
|
except:
|
||||||
|
self.assertEqual(res, [0, 1, 2, 3])
|
||||||
|
raise
|
||||||
# Check with close
|
# Check with close
|
||||||
class MyIt(object):
|
class MyIt(object):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
|
|
@ -1847,7 +1847,7 @@ _is_running(PyInterpreterState *interp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int executing = (int)(frame->f_executing);
|
int executing = _PyFrame_IsExecuting(frame);
|
||||||
Py_DECREF(frame);
|
Py_DECREF(frame);
|
||||||
|
|
||||||
return executing;
|
return executing;
|
||||||
|
|
|
@ -300,17 +300,20 @@ first_line_not_before(int *lines, int len, int line)
|
||||||
static void
|
static void
|
||||||
frame_stack_pop(PyFrameObject *f)
|
frame_stack_pop(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
PyObject *v = (*--f->f_stacktop);
|
assert(f->f_stackdepth >= 0);
|
||||||
|
f->f_stackdepth--;
|
||||||
|
PyObject *v = f->f_valuestack[f->f_stackdepth];
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
frame_block_unwind(PyFrameObject *f)
|
frame_block_unwind(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
|
assert(f->f_stackdepth >= 0);
|
||||||
assert(f->f_iblock > 0);
|
assert(f->f_iblock > 0);
|
||||||
f->f_iblock--;
|
f->f_iblock--;
|
||||||
PyTryBlock *b = &f->f_blockstack[f->f_iblock];
|
PyTryBlock *b = &f->f_blockstack[f->f_iblock];
|
||||||
intptr_t delta = (f->f_stacktop - f->f_valuestack) - b->b_level;
|
intptr_t delta = f->f_stackdepth - b->b_level;
|
||||||
while (delta > 0) {
|
while (delta > 0) {
|
||||||
frame_stack_pop(f);
|
frame_stack_pop(f);
|
||||||
delta--;
|
delta--;
|
||||||
|
@ -352,33 +355,36 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Upon the 'call' trace event of a new frame, f->f_lasti is -1 and
|
/*
|
||||||
* f->f_trace is NULL, check first on the first condition.
|
* This code preserves the historical restrictions on
|
||||||
* Forbidding jumps from the 'call' event of a new frame is a side effect
|
* setting the line number of a frame.
|
||||||
* of allowing to set f_lineno only from trace functions. */
|
* Jumps are forbidden on a 'return' trace event (except after a yield).
|
||||||
if (f->f_lasti == -1) {
|
* Jumps from 'call' trace events are also forbidden.
|
||||||
PyErr_Format(PyExc_ValueError,
|
* In addition, jumps are forbidden when not tracing,
|
||||||
|
* as this is a debugging feature.
|
||||||
|
*/
|
||||||
|
switch(f->f_state) {
|
||||||
|
case FRAME_CREATED:
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
"can't jump from the 'call' trace event of a new frame");
|
"can't jump from the 'call' trace event of a new frame");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
case FRAME_RETURNED:
|
||||||
|
case FRAME_UNWINDING:
|
||||||
/* You can only do this from within a trace function, not via
|
case FRAME_RAISED:
|
||||||
* _getframe or similar hackery. */
|
case FRAME_CLEARED:
|
||||||
if (!f->f_trace) {
|
PyErr_SetString(PyExc_ValueError,
|
||||||
PyErr_Format(PyExc_ValueError,
|
|
||||||
"f_lineno can only be set by a trace function");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Forbid jumps upon a 'return' trace event (except after executing a
|
|
||||||
* YIELD_VALUE or YIELD_FROM opcode, f_stacktop is not NULL in that case)
|
|
||||||
* and upon an 'exception' trace event.
|
|
||||||
* Jumps from 'call' trace events have already been forbidden above for new
|
|
||||||
* frames, so this check does not change anything for 'call' events. */
|
|
||||||
if (f->f_stacktop == NULL) {
|
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"can only jump from a 'line' trace event");
|
"can only jump from a 'line' trace event");
|
||||||
return -1;
|
return -1;
|
||||||
|
case FRAME_EXECUTING:
|
||||||
|
case FRAME_SUSPENDED:
|
||||||
|
/* You can only do this from within a trace function, not via
|
||||||
|
* _getframe or similar hackery. */
|
||||||
|
if (!f->f_trace) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"f_lineno can only be set by a trace function");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int new_lineno;
|
int new_lineno;
|
||||||
|
@ -585,11 +591,10 @@ frame_dealloc(PyFrameObject *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free stack */
|
/* Free stack */
|
||||||
if (f->f_stacktop != NULL) {
|
for (int i = 0; i < f->f_stackdepth; i++) {
|
||||||
for (PyObject **p = valuestack; p < f->f_stacktop; p++) {
|
Py_XDECREF(f->f_valuestack[i]);
|
||||||
Py_XDECREF(*p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
f->f_stackdepth = 0;
|
||||||
|
|
||||||
Py_XDECREF(f->f_back);
|
Py_XDECREF(f->f_back);
|
||||||
Py_DECREF(f->f_builtins);
|
Py_DECREF(f->f_builtins);
|
||||||
|
@ -647,10 +652,8 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stack */
|
/* stack */
|
||||||
if (f->f_stacktop != NULL) {
|
for (int i = 0; i < f->f_stackdepth; i++) {
|
||||||
for (PyObject **p = f->f_valuestack; p < f->f_stacktop; p++) {
|
Py_VISIT(f->f_valuestack[i]);
|
||||||
Py_VISIT(*p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -663,9 +666,7 @@ frame_tp_clear(PyFrameObject *f)
|
||||||
* frame may also point to this frame, believe itself to still be
|
* frame may also point to this frame, believe itself to still be
|
||||||
* active, and try cleaning up this frame again.
|
* active, and try cleaning up this frame again.
|
||||||
*/
|
*/
|
||||||
PyObject **oldtop = f->f_stacktop;
|
f->f_state = FRAME_CLEARED;
|
||||||
f->f_stacktop = NULL;
|
|
||||||
f->f_executing = 0;
|
|
||||||
|
|
||||||
Py_CLEAR(f->f_trace);
|
Py_CLEAR(f->f_trace);
|
||||||
|
|
||||||
|
@ -676,18 +677,17 @@ frame_tp_clear(PyFrameObject *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stack */
|
/* stack */
|
||||||
if (oldtop != NULL) {
|
for (int i = 0; i < f->f_stackdepth; i++) {
|
||||||
for (PyObject **p = f->f_valuestack; p < oldtop; p++) {
|
Py_CLEAR(f->f_valuestack[i]);
|
||||||
Py_CLEAR(*p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
f->f_stackdepth = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
|
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
|
||||||
{
|
{
|
||||||
if (f->f_executing) {
|
if (_PyFrame_IsExecuting(f)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"cannot clear an executing frame");
|
"cannot clear an executing frame");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -898,7 +898,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->f_stacktop = f->f_valuestack;
|
f->f_stackdepth = 0;
|
||||||
f->f_builtins = builtins;
|
f->f_builtins = builtins;
|
||||||
Py_XINCREF(back);
|
Py_XINCREF(back);
|
||||||
f->f_back = back;
|
f->f_back = back;
|
||||||
|
@ -927,7 +927,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
f->f_lasti = -1;
|
f->f_lasti = -1;
|
||||||
f->f_lineno = code->co_firstlineno;
|
f->f_lineno = code->co_firstlineno;
|
||||||
f->f_iblock = 0;
|
f->f_iblock = 0;
|
||||||
f->f_executing = 0;
|
f->f_state = FRAME_CREATED;
|
||||||
f->f_gen = NULL;
|
f->f_gen = NULL;
|
||||||
f->f_trace_opcodes = 0;
|
f->f_trace_opcodes = 0;
|
||||||
f->f_trace_lines = 1;
|
f->f_trace_lines = 1;
|
||||||
|
|
|
@ -47,7 +47,7 @@ _PyGen_Finalize(PyObject *self)
|
||||||
PyObject *res = NULL;
|
PyObject *res = NULL;
|
||||||
PyObject *error_type, *error_value, *error_traceback;
|
PyObject *error_type, *error_value, *error_traceback;
|
||||||
|
|
||||||
if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) {
|
if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame)) {
|
||||||
/* Generator isn't paused, so no need to close */
|
/* Generator isn't paused, so no need to close */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
PyFrameObject *f = gen->gi_frame;
|
PyFrameObject *f = gen->gi_frame;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
if (gen->gi_running) {
|
if (f != NULL && _PyFrame_IsExecuting(f)) {
|
||||||
const char *msg = "generator already executing";
|
const char *msg = "generator already executing";
|
||||||
if (PyCoro_CheckExact(gen)) {
|
if (PyCoro_CheckExact(gen)) {
|
||||||
msg = "coroutine already executing";
|
msg = "coroutine already executing";
|
||||||
|
@ -154,7 +154,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
PyErr_SetString(PyExc_ValueError, msg);
|
PyErr_SetString(PyExc_ValueError, msg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (f == NULL || f->f_stacktop == NULL) {
|
if (f == NULL || _PyFrameHasCompleted(f)) {
|
||||||
if (PyCoro_CheckExact(gen) && !closing) {
|
if (PyCoro_CheckExact(gen) && !closing) {
|
||||||
/* `gen` is an exhausted coroutine: raise an error,
|
/* `gen` is an exhausted coroutine: raise an error,
|
||||||
except when called from gen_close(), which should
|
except when called from gen_close(), which should
|
||||||
|
@ -176,6 +176,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(_PyFrame_IsRunnable(f));
|
||||||
if (f->f_lasti == -1) {
|
if (f->f_lasti == -1) {
|
||||||
if (arg && arg != Py_None) {
|
if (arg && arg != Py_None) {
|
||||||
const char *msg = "can't send non-None value to a "
|
const char *msg = "can't send non-None value to a "
|
||||||
|
@ -194,7 +195,8 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
/* Push arg onto the frame's value stack */
|
/* Push arg onto the frame's value stack */
|
||||||
result = arg ? arg : Py_None;
|
result = arg ? arg : Py_None;
|
||||||
Py_INCREF(result);
|
Py_INCREF(result);
|
||||||
*(f->f_stacktop++) = result;
|
gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result;
|
||||||
|
gen->gi_frame->f_stackdepth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generators always return to their most recent caller, not
|
/* Generators always return to their most recent caller, not
|
||||||
|
@ -203,7 +205,6 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
assert(f->f_back == NULL);
|
assert(f->f_back == NULL);
|
||||||
f->f_back = tstate->frame;
|
f->f_back = tstate->frame;
|
||||||
|
|
||||||
gen->gi_running = 1;
|
|
||||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||||
tstate->exc_info = &gen->gi_exc_state;
|
tstate->exc_info = &gen->gi_exc_state;
|
||||||
|
|
||||||
|
@ -215,7 +216,6 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
result = _PyEval_EvalFrame(tstate, f, exc);
|
result = _PyEval_EvalFrame(tstate, f, exc);
|
||||||
tstate->exc_info = gen->gi_exc_state.previous_item;
|
tstate->exc_info = gen->gi_exc_state.previous_item;
|
||||||
gen->gi_exc_state.previous_item = NULL;
|
gen->gi_exc_state.previous_item = NULL;
|
||||||
gen->gi_running = 0;
|
|
||||||
|
|
||||||
/* Don't keep the reference to f_back any longer than necessary. It
|
/* 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
|
* may keep a chain of frames alive or it could create a reference
|
||||||
|
@ -225,7 +225,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
|
|
||||||
/* If the generator just returned (as opposed to yielding), signal
|
/* If the generator just returned (as opposed to yielding), signal
|
||||||
* that the generator is exhausted. */
|
* that the generator is exhausted. */
|
||||||
if (result && f->f_stacktop == NULL) {
|
if (result && _PyFrameHasCompleted(f)) {
|
||||||
if (result == Py_None) {
|
if (result == Py_None) {
|
||||||
/* Delay exception instantiation if we can */
|
/* Delay exception instantiation if we can */
|
||||||
if (PyAsyncGen_CheckExact(gen)) {
|
if (PyAsyncGen_CheckExact(gen)) {
|
||||||
|
@ -264,7 +264,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
|
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result || f->f_stacktop == NULL) {
|
if (!result || _PyFrameHasCompleted(f)) {
|
||||||
/* generator can't be rerun, so release the frame */
|
/* generator can't be rerun, so release the frame */
|
||||||
/* first clean reference cycle through stored exception traceback */
|
/* first clean reference cycle through stored exception traceback */
|
||||||
_PyErr_ClearExcState(&gen->gi_exc_state);
|
_PyErr_ClearExcState(&gen->gi_exc_state);
|
||||||
|
@ -327,7 +327,7 @@ _PyGen_yf(PyGenObject *gen)
|
||||||
PyObject *yf = NULL;
|
PyObject *yf = NULL;
|
||||||
PyFrameObject *f = gen->gi_frame;
|
PyFrameObject *f = gen->gi_frame;
|
||||||
|
|
||||||
if (f && f->f_stacktop) {
|
if (f) {
|
||||||
PyObject *bytecode = f->f_code->co_code;
|
PyObject *bytecode = f->f_code->co_code;
|
||||||
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
|
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
|
||||||
|
|
||||||
|
@ -341,7 +341,8 @@ _PyGen_yf(PyGenObject *gen)
|
||||||
|
|
||||||
if (code[f->f_lasti + sizeof(_Py_CODEUNIT)] != YIELD_FROM)
|
if (code[f->f_lasti + sizeof(_Py_CODEUNIT)] != YIELD_FROM)
|
||||||
return NULL;
|
return NULL;
|
||||||
yf = f->f_stacktop[-1];
|
assert(f->f_stackdepth > 0);
|
||||||
|
yf = f->f_valuestack[f->f_stackdepth-1];
|
||||||
Py_INCREF(yf);
|
Py_INCREF(yf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,9 +357,10 @@ gen_close(PyGenObject *gen, PyObject *args)
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (yf) {
|
if (yf) {
|
||||||
gen->gi_running = 1;
|
PyFrameState state = gen->gi_frame->f_state;
|
||||||
|
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||||
err = gen_close_iter(yf);
|
err = gen_close_iter(yf);
|
||||||
gen->gi_running = 0;
|
gen->gi_frame->f_state = state;
|
||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
}
|
}
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
|
@ -405,9 +407,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
||||||
We have to allow some awaits to work it through, hence the
|
We have to allow some awaits to work it through, hence the
|
||||||
`close_on_genexit` parameter here.
|
`close_on_genexit` parameter here.
|
||||||
*/
|
*/
|
||||||
gen->gi_running = 1;
|
PyFrameState state = gen->gi_frame->f_state;
|
||||||
|
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||||
err = gen_close_iter(yf);
|
err = gen_close_iter(yf);
|
||||||
gen->gi_running = 0;
|
gen->gi_frame->f_state = state;
|
||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return gen_send_ex(gen, Py_None, 1, 0);
|
return gen_send_ex(gen, Py_None, 1, 0);
|
||||||
|
@ -418,7 +421,6 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
PyFrameObject *f = tstate->frame;
|
PyFrameObject *f = tstate->frame;
|
||||||
|
|
||||||
gen->gi_running = 1;
|
|
||||||
/* Since we are fast-tracking things by skipping the eval loop,
|
/* Since we are fast-tracking things by skipping the eval loop,
|
||||||
we need to update the current frame so the stack trace
|
we need to update the current frame so the stack trace
|
||||||
will be reported correctly to the user. */
|
will be reported correctly to the user. */
|
||||||
|
@ -427,10 +429,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
||||||
tstate->frame = gen->gi_frame;
|
tstate->frame = gen->gi_frame;
|
||||||
/* Close the generator that we are currently iterating with
|
/* Close the generator that we are currently iterating with
|
||||||
'yield from' or awaiting on with 'await'. */
|
'yield from' or awaiting on with 'await'. */
|
||||||
|
PyFrameState state = gen->gi_frame->f_state;
|
||||||
|
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||||
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
|
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
|
||||||
typ, val, tb);
|
typ, val, tb);
|
||||||
|
gen->gi_frame->f_state = state;
|
||||||
tstate->frame = f;
|
tstate->frame = f;
|
||||||
gen->gi_running = 0;
|
|
||||||
} else {
|
} else {
|
||||||
/* `yf` is an iterator or a coroutine-like object. */
|
/* `yf` is an iterator or a coroutine-like object. */
|
||||||
PyObject *meth;
|
PyObject *meth;
|
||||||
|
@ -442,16 +446,19 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
goto throw_here;
|
goto throw_here;
|
||||||
}
|
}
|
||||||
gen->gi_running = 1;
|
PyFrameState state = gen->gi_frame->f_state;
|
||||||
|
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||||
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
||||||
gen->gi_running = 0;
|
gen->gi_frame->f_state = state;
|
||||||
Py_DECREF(meth);
|
Py_DECREF(meth);
|
||||||
}
|
}
|
||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
PyObject *val;
|
PyObject *val;
|
||||||
/* Pop subiterator from stack */
|
/* Pop subiterator from stack */
|
||||||
ret = *(--gen->gi_frame->f_stacktop);
|
assert(gen->gi_frame->f_stackdepth > 0);
|
||||||
|
gen->gi_frame->f_stackdepth--;
|
||||||
|
ret = gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth];
|
||||||
assert(ret == yf);
|
assert(ret == yf);
|
||||||
Py_DECREF(ret);
|
Py_DECREF(ret);
|
||||||
/* Termination repetition of YIELD_FROM */
|
/* Termination repetition of YIELD_FROM */
|
||||||
|
@ -701,6 +708,16 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
|
||||||
return yf;
|
return yf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
if (gen->gi_frame == NULL) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame));
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef gen_getsetlist[] = {
|
static PyGetSetDef gen_getsetlist[] = {
|
||||||
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
||||||
PyDoc_STR("name of the generator")},
|
PyDoc_STR("name of the generator")},
|
||||||
|
@ -708,12 +725,12 @@ static PyGetSetDef gen_getsetlist[] = {
|
||||||
PyDoc_STR("qualified name of the generator")},
|
PyDoc_STR("qualified name of the generator")},
|
||||||
{"gi_yieldfrom", (getter)gen_getyieldfrom, NULL,
|
{"gi_yieldfrom", (getter)gen_getyieldfrom, NULL,
|
||||||
PyDoc_STR("object being iterated by yield from, or None")},
|
PyDoc_STR("object being iterated by yield from, or None")},
|
||||||
|
{"gi_running", (getter)gen_getrunning, NULL, NULL},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMemberDef gen_memberlist[] = {
|
static PyMemberDef gen_memberlist[] = {
|
||||||
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY},
|
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY},
|
||||||
{"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY},
|
|
||||||
{"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
|
{"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
@ -791,7 +808,6 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
|
||||||
f->f_gen = (PyObject *) gen;
|
f->f_gen = (PyObject *) gen;
|
||||||
Py_INCREF(f->f_code);
|
Py_INCREF(f->f_code);
|
||||||
gen->gi_code = (PyObject *)(f->f_code);
|
gen->gi_code = (PyObject *)(f->f_code);
|
||||||
gen->gi_running = 0;
|
|
||||||
gen->gi_weakreflist = NULL;
|
gen->gi_weakreflist = NULL;
|
||||||
gen->gi_exc_state.exc_type = NULL;
|
gen->gi_exc_state.exc_type = NULL;
|
||||||
gen->gi_exc_state.exc_value = NULL;
|
gen->gi_exc_state.exc_value = NULL;
|
||||||
|
@ -921,6 +937,15 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
|
||||||
return yf;
|
return yf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
if (coro->cr_frame == NULL) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame));
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef coro_getsetlist[] = {
|
static PyGetSetDef coro_getsetlist[] = {
|
||||||
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
||||||
PyDoc_STR("name of the coroutine")},
|
PyDoc_STR("name of the coroutine")},
|
||||||
|
@ -928,12 +953,12 @@ static PyGetSetDef coro_getsetlist[] = {
|
||||||
PyDoc_STR("qualified name of the coroutine")},
|
PyDoc_STR("qualified name of the coroutine")},
|
||||||
{"cr_await", (getter)coro_get_cr_await, NULL,
|
{"cr_await", (getter)coro_get_cr_await, NULL,
|
||||||
PyDoc_STR("object being awaited on, or None")},
|
PyDoc_STR("object being awaited on, or None")},
|
||||||
|
{"cr_running", (getter)cr_getrunning, NULL, NULL},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMemberDef coro_memberlist[] = {
|
static PyMemberDef coro_memberlist[] = {
|
||||||
{"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY},
|
{"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY},
|
||||||
{"cr_running", T_BOOL, offsetof(PyCoroObject, cr_running), READONLY},
|
|
||||||
{"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY},
|
{"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY},
|
||||||
{"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
|
{"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
|
@ -1828,7 +1853,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f == NULL || f->f_stacktop == NULL) {
|
if (f == NULL || _PyFrameHasCompleted(f)) {
|
||||||
o->agt_state = AWAITABLE_STATE_CLOSED;
|
o->agt_state = AWAITABLE_STATE_CLOSED;
|
||||||
PyErr_SetNone(PyExc_StopIteration);
|
PyErr_SetNone(PyExc_StopIteration);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -1349,10 +1349,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0);
|
assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0);
|
||||||
next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1;
|
next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1;
|
||||||
}
|
}
|
||||||
stack_pointer = f->f_stacktop;
|
stack_pointer = f->f_valuestack + f->f_stackdepth;
|
||||||
assert(stack_pointer != NULL);
|
/* Set f->f_stackdepth to -1.
|
||||||
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
* Update when returning or calling trace function.
|
||||||
f->f_executing = 1;
|
Having f_stackdepth <= 0 ensures that invalid
|
||||||
|
values are not visible to the cycle GC.
|
||||||
|
We choose -1 rather than 0 to assist debugging.
|
||||||
|
*/
|
||||||
|
f->f_stackdepth = -1;
|
||||||
|
f->f_state = FRAME_EXECUTING;
|
||||||
|
|
||||||
if (co->co_opcache_flag < OPCACHE_MIN_RUNS) {
|
if (co->co_opcache_flag < OPCACHE_MIN_RUNS) {
|
||||||
co->co_opcache_flag++;
|
co->co_opcache_flag++;
|
||||||
|
@ -1440,7 +1445,7 @@ main_loop:
|
||||||
int err;
|
int err;
|
||||||
/* see maybe_call_line_trace
|
/* see maybe_call_line_trace
|
||||||
for expository comments */
|
for expository comments */
|
||||||
f->f_stacktop = stack_pointer;
|
f->f_stackdepth = stack_pointer-f->f_valuestack;
|
||||||
|
|
||||||
err = maybe_call_line_trace(tstate->c_tracefunc,
|
err = maybe_call_line_trace(tstate->c_tracefunc,
|
||||||
tstate->c_traceobj,
|
tstate->c_traceobj,
|
||||||
|
@ -1448,10 +1453,8 @@ main_loop:
|
||||||
&instr_lb, &instr_ub, &instr_prev);
|
&instr_lb, &instr_ub, &instr_prev);
|
||||||
/* Reload possibly changed frame fields */
|
/* Reload possibly changed frame fields */
|
||||||
JUMPTO(f->f_lasti);
|
JUMPTO(f->f_lasti);
|
||||||
if (f->f_stacktop != NULL) {
|
stack_pointer = f->f_valuestack+f->f_stackdepth;
|
||||||
stack_pointer = f->f_stacktop;
|
f->f_stackdepth = -1;
|
||||||
f->f_stacktop = NULL;
|
|
||||||
}
|
|
||||||
if (err)
|
if (err)
|
||||||
/* trace function raised an exception */
|
/* trace function raised an exception */
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2076,6 +2079,8 @@ main_loop:
|
||||||
retval = POP();
|
retval = POP();
|
||||||
assert(f->f_iblock == 0);
|
assert(f->f_iblock == 0);
|
||||||
assert(EMPTY());
|
assert(EMPTY());
|
||||||
|
f->f_state = FRAME_RETURNED;
|
||||||
|
f->f_stackdepth = 0;
|
||||||
goto exiting;
|
goto exiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2242,10 +2247,11 @@ main_loop:
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
/* receiver remains on stack, retval is value to be yielded */
|
/* receiver remains on stack, retval is value to be yielded */
|
||||||
f->f_stacktop = stack_pointer;
|
|
||||||
/* and repeat... */
|
/* and repeat... */
|
||||||
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
|
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
|
||||||
f->f_lasti -= sizeof(_Py_CODEUNIT);
|
f->f_lasti -= sizeof(_Py_CODEUNIT);
|
||||||
|
f->f_state = FRAME_SUSPENDED;
|
||||||
|
f->f_stackdepth = stack_pointer-f->f_valuestack;
|
||||||
goto exiting;
|
goto exiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2261,8 +2267,8 @@ main_loop:
|
||||||
}
|
}
|
||||||
retval = w;
|
retval = w;
|
||||||
}
|
}
|
||||||
|
f->f_state = FRAME_SUSPENDED;
|
||||||
f->f_stacktop = stack_pointer;
|
f->f_stackdepth = stack_pointer-f->f_valuestack;
|
||||||
goto exiting;
|
goto exiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3762,11 +3768,15 @@ error:
|
||||||
/* Log traceback info. */
|
/* Log traceback info. */
|
||||||
PyTraceBack_Here(f);
|
PyTraceBack_Here(f);
|
||||||
|
|
||||||
if (tstate->c_tracefunc != NULL)
|
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;
|
||||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
||||||
tstate, f);
|
tstate, f);
|
||||||
|
}
|
||||||
exception_unwind:
|
exception_unwind:
|
||||||
|
f->f_state = FRAME_UNWINDING;
|
||||||
/* Unwind stacks if an exception occurred */
|
/* Unwind stacks if an exception occurred */
|
||||||
while (f->f_iblock > 0) {
|
while (f->f_iblock > 0) {
|
||||||
/* Pop the current block. */
|
/* Pop the current block. */
|
||||||
|
@ -3825,6 +3835,7 @@ exception_unwind:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Resume normal execution */
|
/* Resume normal execution */
|
||||||
|
f->f_state = FRAME_EXECUTING;
|
||||||
goto main_loop;
|
goto main_loop;
|
||||||
}
|
}
|
||||||
} /* unwind stack */
|
} /* unwind stack */
|
||||||
|
@ -3841,7 +3852,8 @@ exception_unwind:
|
||||||
PyObject *o = POP();
|
PyObject *o = POP();
|
||||||
Py_XDECREF(o);
|
Py_XDECREF(o);
|
||||||
}
|
}
|
||||||
|
f->f_stackdepth = 0;
|
||||||
|
f->f_state = FRAME_RAISED;
|
||||||
exiting:
|
exiting:
|
||||||
if (tstate->use_tracing) {
|
if (tstate->use_tracing) {
|
||||||
if (tstate->c_tracefunc) {
|
if (tstate->c_tracefunc) {
|
||||||
|
@ -3863,7 +3875,6 @@ exit_eval_frame:
|
||||||
if (PyDTrace_FUNCTION_RETURN_ENABLED())
|
if (PyDTrace_FUNCTION_RETURN_ENABLED())
|
||||||
dtrace_function_return(f);
|
dtrace_function_return(f);
|
||||||
_Py_LeaveRecursiveCall(tstate);
|
_Py_LeaveRecursiveCall(tstate);
|
||||||
f->f_executing = 0;
|
|
||||||
tstate->frame = f->f_back;
|
tstate->frame = f->f_back;
|
||||||
|
|
||||||
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);
|
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);
|
||||||
|
|
Loading…
Reference in New Issue