summaryrefslogtreecommitdiffstats
path: root/Objects/frameobject.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2020-07-17 10:44:23 (GMT)
committerGitHub <noreply@github.com>2020-07-17 10:44:23 (GMT)
commitcb9879b948a19c9434316f8ab6aba9c4601a8173 (patch)
tree2456a68d16bda26efc7f4f00f6fe5b4f4889f42c /Objects/frameobject.c
parent8e836bb21ce73f0794fd769db5883c29680dfe47 (diff)
downloadcpython-cb9879b948a19c9434316f8ab6aba9c4601a8173.zip
cpython-cb9879b948a19c9434316f8ab6aba9c4601a8173.tar.gz
cpython-cb9879b948a19c9434316f8ab6aba9c4601a8173.tar.bz2
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.
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r--Objects/frameobject.c90
1 files changed, 45 insertions, 45 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 7c2bce3..8838b80 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -300,17 +300,20 @@ first_line_not_before(int *lines, int len, int line)
static void
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);
}
static void
frame_block_unwind(PyFrameObject *f)
{
+ assert(f->f_stackdepth >= 0);
assert(f->f_iblock > 0);
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) {
frame_stack_pop(f);
delta--;
@@ -352,33 +355,36 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
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.
- * Forbidding jumps from the 'call' event of a new frame is a side effect
- * of allowing to set f_lineno only from trace functions. */
- if (f->f_lasti == -1) {
- PyErr_Format(PyExc_ValueError,
+ /*
+ * This code preserves the historical restrictions on
+ * setting the line number of a frame.
+ * Jumps are forbidden on a 'return' trace event (except after a yield).
+ * Jumps from 'call' trace events are also forbidden.
+ * 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");
- return -1;
- }
-
- /* 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;
- }
-
- /* 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,
+ return -1;
+ case FRAME_RETURNED:
+ case FRAME_UNWINDING:
+ case FRAME_RAISED:
+ case FRAME_CLEARED:
+ PyErr_SetString(PyExc_ValueError,
"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;
@@ -585,11 +591,10 @@ frame_dealloc(PyFrameObject *f)
}
/* Free stack */
- if (f->f_stacktop != NULL) {
- for (PyObject **p = valuestack; p < f->f_stacktop; p++) {
- Py_XDECREF(*p);
- }
+ for (int i = 0; i < f->f_stackdepth; i++) {
+ Py_XDECREF(f->f_valuestack[i]);
}
+ f->f_stackdepth = 0;
Py_XDECREF(f->f_back);
Py_DECREF(f->f_builtins);
@@ -647,10 +652,8 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
}
/* stack */
- if (f->f_stacktop != NULL) {
- for (PyObject **p = f->f_valuestack; p < f->f_stacktop; p++) {
- Py_VISIT(*p);
- }
+ for (int i = 0; i < f->f_stackdepth; i++) {
+ Py_VISIT(f->f_valuestack[i]);
}
return 0;
}
@@ -663,9 +666,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.
*/
- PyObject **oldtop = f->f_stacktop;
- f->f_stacktop = NULL;
- f->f_executing = 0;
+ f->f_state = FRAME_CLEARED;
Py_CLEAR(f->f_trace);
@@ -676,18 +677,17 @@ frame_tp_clear(PyFrameObject *f)
}
/* stack */
- if (oldtop != NULL) {
- for (PyObject **p = f->f_valuestack; p < oldtop; p++) {
- Py_CLEAR(*p);
- }
+ for (int i = 0; i < f->f_stackdepth; i++) {
+ Py_CLEAR(f->f_valuestack[i]);
}
+ f->f_stackdepth = 0;
return 0;
}
static PyObject *
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{
- if (f->f_executing) {
+ if (_PyFrame_IsExecuting(f)) {
PyErr_SetString(PyExc_RuntimeError,
"cannot clear an executing frame");
return NULL;
@@ -898,7 +898,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
return NULL;
}
- f->f_stacktop = f->f_valuestack;
+ f->f_stackdepth = 0;
f->f_builtins = builtins;
Py_XINCREF(back);
f->f_back = back;
@@ -927,7 +927,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
f->f_lasti = -1;
f->f_lineno = code->co_firstlineno;
f->f_iblock = 0;
- f->f_executing = 0;
+ f->f_state = FRAME_CREATED;
f->f_gen = NULL;
f->f_trace_opcodes = 0;
f->f_trace_lines = 1;