diff options
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r-- | Objects/frameobject.c | 920 |
1 files changed, 361 insertions, 559 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d7acb41..4c91dd0 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1,32 +1,61 @@ /* Frame object implementation */ #include "Python.h" -#include "pycore_object.h" -#include "pycore_pystate.h" #include "code.h" #include "frameobject.h" #include "opcode.h" #include "structmember.h" +#undef MIN +#undef MAX +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { - {"f_back", T_OBJECT, OFF(f_back), READONLY}, - {"f_code", T_OBJECT, OFF(f_code), READONLY}, - {"f_builtins", T_OBJECT, OFF(f_builtins), READONLY}, - {"f_globals", T_OBJECT, OFF(f_globals), READONLY}, - {"f_lasti", T_INT, OFF(f_lasti), READONLY}, - {"f_trace_lines", T_BOOL, OFF(f_trace_lines), 0}, - {"f_trace_opcodes", T_BOOL, OFF(f_trace_opcodes), 0}, + {"f_back", T_OBJECT, OFF(f_back), RO}, + {"f_code", T_OBJECT, OFF(f_code), RO}, + {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, + {"f_globals", T_OBJECT, OFF(f_globals), RO}, + {"f_lasti", T_INT, OFF(f_lasti), RO}, {NULL} /* Sentinel */ }; +#define WARN_GET_SET(NAME) \ +static PyObject * frame_get_ ## NAME(PyFrameObject *f) { \ + if (PyErr_WarnPy3k(#NAME " has been removed in 3.x", 2) < 0) \ + return NULL; \ + if (f->NAME) { \ + Py_INCREF(f->NAME); \ + return f->NAME; \ + } \ + Py_RETURN_NONE; \ +} \ +static int frame_set_ ## NAME(PyFrameObject *f, PyObject *new) { \ + if (PyErr_WarnPy3k(#NAME " has been removed in 3.x", 2) < 0) \ + return -1; \ + if (f->NAME) { \ + Py_CLEAR(f->NAME); \ + } \ + if (new == Py_None) \ + new = NULL; \ + Py_XINCREF(new); \ + f->NAME = new; \ + return 0; \ +} + + +WARN_GET_SET(f_exc_traceback) +WARN_GET_SET(f_exc_type) +WARN_GET_SET(f_exc_value) + + static PyObject * frame_getlocals(PyFrameObject *f, void *closure) { - if (PyFrame_FastToLocalsWithError(f) < 0) - return NULL; + PyFrame_FastToLocals(f); Py_INCREF(f->f_locals); return f->f_locals; } @@ -43,309 +72,9 @@ PyFrame_GetLineNumber(PyFrameObject *f) static PyObject * frame_getlineno(PyFrameObject *f, void *closure) { - return PyLong_FromLong(PyFrame_GetLineNumber(f)); -} - - -/* Given the index of the effective opcode, - scan back to construct the oparg with EXTENDED_ARG */ -static unsigned int -get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i) -{ - _Py_CODEUNIT word; - unsigned int oparg = _Py_OPARG(codestr[i]); - if (i >= 1 && _Py_OPCODE(word = codestr[i-1]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 8; - if (i >= 2 && _Py_OPCODE(word = codestr[i-2]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 16; - if (i >= 3 && _Py_OPCODE(word = codestr[i-3]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 24; - } - } - } - return oparg; -} - -typedef struct _codetracker { - unsigned char *code; - Py_ssize_t code_len; - unsigned char *lnotab; - Py_ssize_t lnotab_len; - int start_line; - int offset; - int line; - int addr; - int line_addr; -} codetracker; - -/* Reset the mutable parts of the tracker */ -static void -reset(codetracker *tracker) -{ - tracker->offset = 0; - tracker->addr = 0; - tracker->line_addr = 0; - tracker->line = tracker->start_line; -} - -/* Initialise the tracker */ -static void -init_codetracker(codetracker *tracker, PyCodeObject *code_obj) -{ - PyBytes_AsStringAndSize(code_obj->co_code, - (char **)&tracker->code, &tracker->code_len); - PyBytes_AsStringAndSize(code_obj->co_lnotab, - (char **)&tracker->lnotab, &tracker->lnotab_len); - tracker->start_line = code_obj->co_firstlineno; - reset(tracker); -} - -static void -advance_tracker(codetracker *tracker) -{ - tracker->addr += sizeof(_Py_CODEUNIT); - if (tracker->offset >= tracker->lnotab_len) { - return; - } - while (tracker->offset < tracker->lnotab_len && - tracker->addr >= tracker->line_addr + tracker->lnotab[tracker->offset]) { - tracker->line_addr += tracker->lnotab[tracker->offset]; - tracker->line += (signed char)tracker->lnotab[tracker->offset+1]; - tracker->offset += 2; - } -} - - -static void -retreat_tracker(codetracker *tracker) -{ - tracker->addr -= sizeof(_Py_CODEUNIT); - while (tracker->addr < tracker->line_addr) { - tracker->offset -= 2; - tracker->line_addr -= tracker->lnotab[tracker->offset]; - tracker->line -= (signed char)tracker->lnotab[tracker->offset+1]; - } -} - -static int -move_to_addr(codetracker *tracker, int addr) -{ - while (addr > tracker->addr) { - advance_tracker(tracker); - if (tracker->addr >= tracker->code_len) { - return -1; - } - } - while (addr < tracker->addr) { - retreat_tracker(tracker); - if (tracker->addr < 0) { - return -1; - } - } - return 0; -} - -static int -first_line_not_before(codetracker *tracker, int line) -{ - int result = INT_MAX; - reset(tracker); - while (tracker->addr < tracker->code_len) { - if (tracker->line == line) { - return line; - } - if (tracker->line > line && tracker->line < result) { - result = tracker->line; - } - advance_tracker(tracker); - } - if (result == INT_MAX) { - return -1; - } - return result; -} - -static int -move_to_nearest_start_of_line(codetracker *tracker, int line) -{ - if (line > tracker->line) { - while (line != tracker->line) { - advance_tracker(tracker); - if (tracker->addr >= tracker->code_len) { - return -1; - } - } - } - else { - while (line != tracker->line) { - retreat_tracker(tracker); - if (tracker->addr < 0) { - return -1; - } - } - while (tracker->addr > tracker->line_addr) { - retreat_tracker(tracker); - } - } - return 0; -} - -typedef struct _blockitem -{ - unsigned char kind; - int end_addr; - int start_line; -} blockitem; - -typedef struct _blockstack -{ - blockitem stack[CO_MAXBLOCKS]; - int depth; -} blockstack; - - -static void -init_blockstack(blockstack *blocks) -{ - blocks->depth = 0; -} - -static void -push_block(blockstack *blocks, unsigned char kind, - int end_addr, int start_line) -{ - assert(blocks->depth < CO_MAXBLOCKS); - blocks->stack[blocks->depth].kind = kind; - blocks->stack[blocks->depth].end_addr = end_addr; - blocks->stack[blocks->depth].start_line = start_line; - blocks->depth++; -} - -static unsigned char -pop_block(blockstack *blocks) -{ - assert(blocks->depth > 0); - blocks->depth--; - return blocks->stack[blocks->depth].kind; -} - -static blockitem * -top_block(blockstack *blocks) -{ - assert(blocks->depth > 0); - return &blocks->stack[blocks->depth-1]; -} - -static inline int -is_try_except(unsigned char op, int target_op) -{ - return op == SETUP_FINALLY && (target_op == DUP_TOP || target_op == POP_TOP); -} - -static inline int -is_async_for(unsigned char op, int target_op) -{ - return op == SETUP_FINALLY && target_op == END_ASYNC_FOR; -} - -static inline int -is_try_finally(unsigned char op, int target_op) -{ - return op == SETUP_FINALLY && !is_try_except(op, target_op) && !is_async_for(op, target_op); -} - -/* Kind for finding except blocks in the jump to line code */ -#define TRY_EXCEPT 250 - -static int -block_stack_for_line(codetracker *tracker, int line, blockstack *blocks) -{ - if (line < tracker->start_line) { - return -1; - } - init_blockstack(blocks); - reset(tracker); - while (tracker->addr < tracker->code_len) { - if (tracker->line == line) { - return 0; - } - if (blocks->depth > 0 && tracker->addr == top_block(blocks)->end_addr) { - unsigned char kind = pop_block(blocks); - assert(kind != SETUP_FINALLY); - if (kind == TRY_EXCEPT) { - push_block(blocks, POP_EXCEPT, -1, tracker->line); - } - if (kind == SETUP_WITH || kind == SETUP_ASYNC_WITH) { - push_block(blocks, WITH_EXCEPT_START, -1, tracker->line); - } - } - unsigned char op = tracker->code[tracker->addr]; - if (op == SETUP_FINALLY || op == SETUP_ASYNC_WITH || op == SETUP_WITH || op == FOR_ITER) { - unsigned int oparg = get_arg((const _Py_CODEUNIT *)tracker->code, - tracker->addr / sizeof(_Py_CODEUNIT)); - int target_addr = tracker->addr + oparg + sizeof(_Py_CODEUNIT); - int target_op = tracker->code[target_addr]; - if (is_async_for(op, target_op)) { - push_block(blocks, FOR_ITER, target_addr, tracker->line); - } - else if (op == FOR_ITER) { - push_block(blocks, FOR_ITER, target_addr-sizeof(_Py_CODEUNIT), tracker->line); - } - else if (is_try_except(op, target_op)) { - push_block(blocks, TRY_EXCEPT, target_addr-sizeof(_Py_CODEUNIT), tracker->line); - } - else if (is_try_finally(op, target_op)) { - int addr = tracker->addr; - // Skip over duplicate 'finally' blocks if line is after body. - move_to_addr(tracker, target_addr); - if (tracker->line > line) { - // Target is in body, rewind to start. - move_to_addr(tracker, addr); - push_block(blocks, op, target_addr, tracker->line); - } - else { - // Now in finally block. - push_block(blocks, RERAISE, -1, tracker->line); - } - } - else { - push_block(blocks, op, target_addr, tracker->line); - } - } - else if (op == RERAISE) { - assert(blocks->depth > 0); - unsigned char kind = top_block(blocks)->kind; - if (kind == RERAISE || kind == WITH_EXCEPT_START || kind == POP_EXCEPT) { - pop_block(blocks); - } - - } - advance_tracker(tracker); - } - return -1; + return PyInt_FromLong(PyFrame_GetLineNumber(f)); } -static void -frame_stack_pop(PyFrameObject *f) -{ - PyObject *v = (*--f->f_stacktop); - Py_DECREF(v); -} - -static void -frame_block_unwind(PyFrameObject *f) -{ - assert(f->f_iblock > 0); - f->f_iblock--; - PyTryBlock *b = &f->f_blockstack[f->f_iblock]; - int delta = (f->f_stacktop - f->f_valuestack) - b->b_level; - while (delta > 0) { - frame_stack_pop(f); - delta--; - } -} - - /* Setter for f_lineno - you can set f_lineno from within a trace function in * order to jump to a given line of code, subject to some restrictions. Most * lines are OK to jump to because they don't make any assumptions about the @@ -356,25 +85,45 @@ frame_block_unwind(PyFrameObject *f) * o Lines with an 'except' statement on them can't be jumped to, because * they expect an exception to be on the top of the stack. * o Lines that live in a 'finally' block can't be jumped from or to, since - * we cannot be sure which state the interpreter was in or would be in - * during execution of the finally block. - * o 'try', 'with' and 'async with' blocks can't be jumped into because - * the blockstack needs to be set up before their code runs. - * o 'for' and 'async for' loops can't be jumped into because the + * the END_FINALLY expects to clean up the stack after the 'try' block. + * o 'try'/'for'/'while' blocks can't be jumped into because the blockstack + * needs to be set up before their code runs, and for 'for' loops the * iterator needs to be on the stack. * o Jumps cannot be made from within a trace function invoked with a * 'return' or 'exception' event since the eval loop has been exited at * that time. */ static int -frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored)) -{ +frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) +{ + int new_lineno = 0; /* The new value of f_lineno */ + int new_lasti = 0; /* The new value of f_lasti */ + int new_iblock = 0; /* The new value of f_iblock */ + unsigned char *code = NULL; /* The bytecode for the frame... */ + Py_ssize_t code_len = 0; /* ...and its length */ + unsigned char *lnotab = NULL; /* Iterating over co_lnotab */ + Py_ssize_t lnotab_len = 0; /* (ditto) */ + int offset = 0; /* (ditto) */ + int line = 0; /* (ditto) */ + int addr = 0; /* (ditto) */ + int min_addr = 0; /* Scanning the SETUPs and POPs */ + int max_addr = 0; /* (ditto) */ + int delta_iblock = 0; /* (ditto) */ + int min_delta_iblock = 0; /* (ditto) */ + int min_iblock = 0; /* (ditto) */ + int f_lasti_setup_addr = 0; /* Policing no-jump-into-finally */ + int new_lasti_setup_addr = 0; /* (ditto) */ + int blockstack[CO_MAXBLOCKS]; /* Walking the 'finally' blocks */ + int in_finally[CO_MAXBLOCKS]; /* (ditto) */ + int blockstack_top = 0; /* (ditto) */ + unsigned char setup_op = 0; /* (ditto) */ + if (p_new_lineno == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } /* f_lineno must be an integer. */ - if (!PyLong_CheckExact(p_new_lineno)) { + if (!PyInt_Check(p_new_lineno)) { PyErr_SetString(PyExc_ValueError, "lineno must be an integer"); return -1; @@ -399,8 +148,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } /* 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. + * YIELD_VALUE 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) { @@ -409,131 +158,233 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - - codetracker tracker; - init_codetracker(&tracker, f->f_code); - move_to_addr(&tracker, f->f_lasti); - int current_line = tracker.line; - assert(current_line >= 0); - int new_lineno; - - { - /* Fail if the line falls outside the code block and - select first line with actual code. */ - int overflow; - long l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); - if (overflow -#if SIZEOF_LONG > SIZEOF_INT - || l_new_lineno > INT_MAX - || l_new_lineno < INT_MIN -#endif - ) { - PyErr_SetString(PyExc_ValueError, - "lineno out of range"); - return -1; - } - new_lineno = (int)l_new_lineno; - - if (new_lineno < f->f_code->co_firstlineno) { - PyErr_Format(PyExc_ValueError, - "line %d comes before the current code block", - new_lineno); - return -1; - } - - new_lineno = first_line_not_before(&tracker, new_lineno); - if (new_lineno < 0) { - PyErr_Format(PyExc_ValueError, - "line %d comes after the current code block", - (int)l_new_lineno); - return -1; + /* Fail if the line comes before the start of the code block. */ + new_lineno = (int) PyInt_AsLong(p_new_lineno); + if (new_lineno < f->f_code->co_firstlineno) { + PyErr_Format(PyExc_ValueError, + "line %d comes before the current code block", + new_lineno); + return -1; + } + else if (new_lineno == f->f_code->co_firstlineno) { + new_lasti = 0; + new_lineno = f->f_code->co_firstlineno; + } + else { + /* Find the bytecode offset for the start of the given + * line, or the first code-owning line after it. */ + char *tmp; + PyString_AsStringAndSize(f->f_code->co_lnotab, + &tmp, &lnotab_len); + lnotab = (unsigned char *) tmp; + addr = 0; + line = f->f_code->co_firstlineno; + new_lasti = -1; + for (offset = 0; offset < lnotab_len; offset += 2) { + addr += lnotab[offset]; + line += lnotab[offset+1]; + if (line >= new_lineno) { + new_lasti = addr; + new_lineno = line; + break; + } } } - if (tracker.code[f->f_lasti] == YIELD_VALUE || tracker.code[f->f_lasti] == YIELD_FROM) { - PyErr_SetString(PyExc_ValueError, - "can't jump from a 'yield' statement"); + /* If we didn't reach the requested line, return an error. */ + if (new_lasti == -1) { + PyErr_Format(PyExc_ValueError, + "line %d comes after the current code block", + new_lineno); return -1; } - /* Find block stack for current line and target line. */ - blockstack current_stack, new_stack; - block_stack_for_line(&tracker, new_lineno, &new_stack); - block_stack_for_line(&tracker, current_line, ¤t_stack); + /* We're now ready to look at the bytecode. */ + PyString_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len); + min_addr = MIN(new_lasti, f->f_lasti); + max_addr = MAX(new_lasti, f->f_lasti); /* The trace function is called with a 'return' trace event after the * execution of a yield statement. */ - if (tracker.code[tracker.addr] == DUP_TOP || tracker.code[tracker.addr] == POP_TOP) { + assert(f->f_lasti != -1); + if (code[f->f_lasti] == YIELD_VALUE) { + PyErr_SetString(PyExc_ValueError, + "can't jump from a yield statement"); + return -1; + } + + /* You can't jump onto a line with an 'except' statement on it - + * they expect to have an exception on the top of the stack, which + * won't be true if you jump to them. They always start with code + * that either pops the exception using POP_TOP (plain 'except:' + * lines do this) or duplicates the exception on the stack using + * DUP_TOP (if there's an exception type specified). See compile.c, + * 'com_try_except' for the full details. There aren't any other + * cases (AFAIK) where a line's code can start with DUP_TOP or + * POP_TOP, but if any ever appear, they'll be subject to the same + * restriction (but with a different error message). */ + if (code[new_lasti] == DUP_TOP || code[new_lasti] == POP_TOP) { PyErr_SetString(PyExc_ValueError, "can't jump to 'except' line as there's no exception"); return -1; } - /* Validate change of block stack. */ - if (new_stack.depth > 0) { - blockitem *current_block_at_new_depth = &(current_stack.stack[new_stack.depth-1]); - if (new_stack.depth > current_stack.depth || - top_block(&new_stack)->start_line != current_block_at_new_depth->start_line) { - unsigned char target_kind = top_block(&new_stack)->kind; - char *msg; - if (target_kind == POP_EXCEPT) { - msg = "can't jump into an 'except' block as there's no exception"; - } - else if (target_kind == RERAISE) { - msg = "can't jump into a 'finally' block"; + /* You can't jump into or out of a 'finally' block because the 'try' + * block leaves something on the stack for the END_FINALLY to clean + * up. So we walk the bytecode, maintaining a simulated blockstack. + * When we reach the old or new address and it's in a 'finally' block + * we note the address of the corresponding SETUP_FINALLY. The jump + * is only legal if neither address is in a 'finally' block or + * they're both in the same one. 'blockstack' is a stack of the + * bytecode addresses of the SETUP_X opcodes, and 'in_finally' tracks + * whether we're in a 'finally' block at each blockstack level. */ + f_lasti_setup_addr = -1; + new_lasti_setup_addr = -1; + memset(blockstack, '\0', sizeof(blockstack)); + memset(in_finally, '\0', sizeof(in_finally)); + blockstack_top = 0; + for (addr = 0; addr < code_len; addr++) { + unsigned char op = code[addr]; + switch (op) { + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + case SETUP_WITH: + blockstack[blockstack_top++] = addr; + in_finally[blockstack_top-1] = 0; + break; + + case POP_BLOCK: + assert(blockstack_top > 0); + setup_op = code[blockstack[blockstack_top-1]]; + if (setup_op == SETUP_FINALLY || setup_op == SETUP_WITH) { + in_finally[blockstack_top-1] = 1; } else { - msg = "can't jump into the middle of a block"; + blockstack_top--; + } + break; + + case END_FINALLY: + /* Ignore END_FINALLYs for SETUP_EXCEPTs - they exist + * in the bytecode but don't correspond to an actual + * 'finally' block. (If blockstack_top is 0, we must + * be seeing such an END_FINALLY.) */ + if (blockstack_top > 0) { + setup_op = code[blockstack[blockstack_top-1]]; + if (setup_op == SETUP_FINALLY || setup_op == SETUP_WITH) { + blockstack_top--; + } + } + break; + } + + /* For the addresses we're interested in, see whether they're + * within a 'finally' block and if so, remember the address + * of the SETUP_FINALLY. */ + if (addr == new_lasti || addr == f->f_lasti) { + int i = 0; + int setup_addr = -1; + for (i = blockstack_top-1; i >= 0; i--) { + if (in_finally[i]) { + setup_addr = blockstack[i]; + break; + } + } + + if (setup_addr != -1) { + if (addr == new_lasti) { + new_lasti_setup_addr = setup_addr; + } + + if (addr == f->f_lasti) { + f_lasti_setup_addr = setup_addr; + } } - PyErr_SetString(PyExc_ValueError, msg); - return -1; } - } - /* Check for illegal jumps out of finally or except blocks. */ - for (int depth = new_stack.depth; depth < current_stack.depth; depth++) { - switch(current_stack.stack[depth].kind) { - case RERAISE: - PyErr_SetString(PyExc_ValueError, - "can't jump out of a 'finally' block"); - return -1; - case POP_EXCEPT: - PyErr_SetString(PyExc_ValueError, - "can't jump out of an 'except' block"); - return -1; + if (op >= HAVE_ARGUMENT) { + addr += 2; } } - /* Unwind block stack. */ - while (current_stack.depth > new_stack.depth) { - unsigned char kind = pop_block(¤t_stack); - switch(kind) { - case FOR_ITER: - frame_stack_pop(f); - break; + /* Verify that the blockstack tracking code didn't get lost. */ + assert(blockstack_top == 0); + + /* After all that, are we jumping into / out of a 'finally' block? */ + if (new_lasti_setup_addr != f_lasti_setup_addr) { + PyErr_SetString(PyExc_ValueError, + "can't jump into or out of a 'finally' block"); + return -1; + } + + + /* Police block-jumping (you can't jump into the middle of a block) + * and ensure that the blockstack finishes up in a sensible state (by + * popping any blocks we're jumping out of). We look at all the + * blockstack operations between the current position and the new + * one, and keep track of how many blocks we drop out of on the way. + * By also keeping track of the lowest blockstack position we see, we + * can tell whether the jump goes into any blocks without coming out + * again - in that case we raise an exception below. */ + delta_iblock = 0; + for (addr = min_addr; addr < max_addr; addr++) { + unsigned char op = code[addr]; + switch (op) { + case SETUP_LOOP: + case SETUP_EXCEPT: case SETUP_FINALLY: - case TRY_EXCEPT: - frame_block_unwind(f); - break; case SETUP_WITH: - case SETUP_ASYNC_WITH: - frame_block_unwind(f); - // Pop the exit function - frame_stack_pop(f); + delta_iblock++; break; - default: - PyErr_SetString(PyExc_SystemError, - "unexpected block kind"); - return -1; + + case POP_BLOCK: + delta_iblock--; + break; + } + + min_delta_iblock = MIN(min_delta_iblock, delta_iblock); + + if (op >= HAVE_ARGUMENT) { + addr += 2; } } - move_to_addr(&tracker, f->f_lasti); - move_to_nearest_start_of_line(&tracker, new_lineno); + /* Derive the absolute iblock values from the deltas. */ + min_iblock = f->f_iblock + min_delta_iblock; + if (new_lasti > f->f_lasti) { + /* Forwards jump. */ + new_iblock = f->f_iblock + delta_iblock; + } + else { + /* Backwards jump. */ + new_iblock = f->f_iblock - delta_iblock; + } + + /* Are we jumping into a block? */ + if (new_iblock > min_iblock) { + PyErr_SetString(PyExc_ValueError, + "can't jump into the middle of a block"); + return -1; + } + + /* Pop any blocks that we're jumping out of. */ + while (f->f_iblock > new_iblock) { + PyTryBlock *b = &f->f_blockstack[--f->f_iblock]; + while ((f->f_stacktop - f->f_valuestack) > b->b_level) { + PyObject *v = (*--f->f_stacktop); + Py_DECREF(v); + } + if (b->b_type == SETUP_WITH) { + /* Pop the exit function. */ + PyObject *v = (*--f->f_stacktop); + Py_DECREF(v); + } + } /* Finally set the new f_lineno and f_lasti and return OK. */ f->f_lineno = new_lineno; - f->f_lasti = tracker.addr; + f->f_lasti = new_lasti; return 0; } @@ -564,12 +415,24 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure) return 0; } +static PyObject * +frame_getrestricted(PyFrameObject *f, void *closure) +{ + return PyBool_FromLong(PyFrame_IsRestricted(f)); +} static PyGetSetDef frame_getsetlist[] = { {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, + {"f_restricted",(getter)frame_getrestricted,NULL, NULL}, + {"f_exc_traceback", (getter)frame_get_f_exc_traceback, + (setter)frame_set_f_exc_traceback, NULL}, + {"f_exc_type", (getter)frame_get_f_exc_type, + (setter)frame_set_f_exc_type, NULL}, + {"f_exc_value", (getter)frame_get_f_exc_value, + (setter)frame_set_f_exc_value, NULL}, {0} }; @@ -588,13 +451,14 @@ static PyGetSetDef frame_getsetlist[] = { * ob_type, ob_size, f_code, f_valuestack; - * f_locals, f_trace are NULL; + * f_locals, f_trace, + f_exc_type, f_exc_value, f_exc_traceback are NULL; * f_localsplus does not require re-allocation and the local variables in f_localsplus are NULL. 2. We also maintain a separate free list of stack frames (just like - floats are allocated in a special way -- see floatobject.c). When + integers are allocated in a special way -- see intobject.c). When a stack frame is on the free list, only the following members have a meaning: ob_type == &Frametype @@ -620,15 +484,13 @@ static int numfree = 0; /* number of frames currently in free_list */ /* max value for numfree */ #define PyFrame_MAXFREELIST 200 -static void _Py_HOT_FUNCTION +static void frame_dealloc(PyFrameObject *f) { PyObject **p, **valuestack; PyCodeObject *co; - if (_PyObject_GC_IS_TRACKED(f)) - _PyObject_GC_UNTRACK(f); - + PyObject_GC_UnTrack(f); Py_TRASHCAN_SAFE_BEGIN(f) /* Kill all local variables */ valuestack = f->f_valuestack; @@ -646,6 +508,9 @@ frame_dealloc(PyFrameObject *f) Py_DECREF(f->f_globals); Py_CLEAR(f->f_locals); Py_CLEAR(f->f_trace); + Py_CLEAR(f->f_exc_type); + Py_CLEAR(f->f_exc_value); + Py_CLEAR(f->f_exc_traceback); co = f->f_code; if (co->co_zombieframe == NULL) @@ -666,7 +531,7 @@ static int frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { PyObject **fastlocals, **p; - Py_ssize_t i, slots; + int i, slots; Py_VISIT(f->f_back); Py_VISIT(f->f_code); @@ -674,6 +539,9 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) Py_VISIT(f->f_globals); Py_VISIT(f->f_locals); Py_VISIT(f->f_trace); + Py_VISIT(f->f_exc_type); + Py_VISIT(f->f_exc_value); + Py_VISIT(f->f_exc_traceback); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -689,11 +557,11 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) return 0; } -static int -frame_tp_clear(PyFrameObject *f) +static void +frame_clear(PyFrameObject *f) { PyObject **fastlocals, **p, **oldtop; - Py_ssize_t i, slots; + int i, slots; /* Before anything else, make sure that this frame is clearly marked * as being defunct! Else, e.g., a generator reachable from this @@ -702,8 +570,10 @@ frame_tp_clear(PyFrameObject *f) */ oldtop = f->f_stacktop; f->f_stacktop = NULL; - f->f_executing = 0; + Py_CLEAR(f->f_exc_type); + Py_CLEAR(f->f_exc_value); + Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace); /* locals */ @@ -717,30 +587,10 @@ frame_tp_clear(PyFrameObject *f) for (p = f->f_valuestack; p < oldtop; p++) Py_CLEAR(*p); } - return 0; } static PyObject * -frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) -{ - if (f->f_executing) { - 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); - } - (void)frame_tp_clear(f); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(clear__doc__, -"F.clear(): clear most references held by the frame"); - -static PyObject * -frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) +frame_sizeof(PyFrameObject *f) { Py_ssize_t res, extras, ncells, nfrees; @@ -751,24 +601,13 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) /* subtract one as it is already included in PyFrameObject */ res = sizeof(PyFrameObject) + (extras-1) * sizeof(PyObject *); - return PyLong_FromSsize_t(res); + return PyInt_FromSsize_t(res); } PyDoc_STRVAR(sizeof__doc__, "F.__sizeof__() -> size of F in memory, in bytes"); -static PyObject * -frame_repr(PyFrameObject *f) -{ - int lineno = PyFrame_GetLineNumber(f); - return PyUnicode_FromFormat( - "<frame at %p, file %R, line %d, code %S>", - f, f->f_code->co_filename, lineno, f->f_code->co_name); -} - static PyMethodDef frame_methods[] = { - {"clear", (PyCFunction)frame_clear, METH_NOARGS, - clear__doc__}, {"__sizeof__", (PyCFunction)frame_sizeof, METH_NOARGS, sizeof__doc__}, {NULL, NULL} /* sentinel */ @@ -780,11 +619,11 @@ PyTypeObject PyFrame_Type = { sizeof(PyFrameObject), sizeof(PyObject *), (destructor)frame_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)frame_repr, /* tp_repr */ + 0, /* tp_compare */ + 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -797,7 +636,7 @@ PyTypeObject PyFrame_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)frame_traverse, /* tp_traverse */ - (inquiry)frame_tp_clear, /* tp_clear */ + (inquiry)frame_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ @@ -809,11 +648,19 @@ PyTypeObject PyFrame_Type = { 0, /* tp_dict */ }; -_Py_IDENTIFIER(__builtins__); +static PyObject *builtin_object; + +int _PyFrame_Init() +{ + builtin_object = PyString_InternFromString("__builtins__"); + if (builtin_object == NULL) + return 0; + return 1; +} -PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, - PyObject *globals, PyObject *locals) +PyFrameObject * +PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, + PyObject *locals) { PyFrameObject *back = tstate->frame; PyFrameObject *f; @@ -828,17 +675,16 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, } #endif if (back == NULL || back->f_globals != globals) { - builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__); + builtins = PyDict_GetItem(globals, builtin_object); if (builtins) { if (PyModule_Check(builtins)) { builtins = PyModule_GetDict(builtins); - assert(builtins != NULL); + assert(!builtins || PyDict_Check(builtins)); } + else if (!PyDict_Check(builtins)) + builtins = NULL; } if (builtins == NULL) { - if (PyErr_Occurred()) { - return NULL; - } /* No builtins! Make up a minimal one Give them 'None', at least. */ builtins = PyDict_New(); @@ -855,7 +701,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, /* If we share the globals, we share the builtins. Save a lookup and a call. */ builtins = back->f_builtins; - assert(builtins != NULL); + assert(builtins != NULL && PyDict_Check(builtins)); Py_INCREF(builtins); } if (code->co_zombieframe != NULL) { @@ -884,13 +730,11 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, f = free_list; free_list = free_list->f_back; if (Py_SIZE(f) < extras) { - PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras); - if (new_f == NULL) { - PyObject_GC_Del(f); + f = PyObject_GC_Resize(PyFrameObject, f, extras); + if (f == NULL) { Py_DECREF(builtins); return NULL; } - f = new_f; } _Py_NewReference((PyObject *)f); } @@ -902,6 +746,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, f->f_localsplus[i] = NULL; f->f_locals = NULL; f->f_trace = NULL; + f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_stacktop = f->f_valuestack; f->f_builtins = builtins; @@ -928,29 +773,16 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, Py_INCREF(locals); f->f_locals = locals; } + f->f_tstate = tstate; f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; - f->f_executing = 0; - f->f_gen = NULL; - f->f_trace_opcodes = 0; - f->f_trace_lines = 1; + _PyObject_GC_TRACK(f); return f; } -PyFrameObject* -PyFrame_New(PyThreadState *tstate, PyCodeObject *code, - PyObject *globals, PyObject *locals) -{ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, locals); - if (f) - _PyObject_GC_TRACK(f); - return f; -} - - /* Block management */ void @@ -986,9 +818,12 @@ PyFrame_BlockPop(PyFrameObject *f) If deref is true, then the values being copied are cell variables and the value is extracted from the cell variable before being put in dict. + + Exceptions raised while modifying the dict are silently ignored, + because there is no good way to report them. */ -static int +static void map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, int deref) { @@ -996,28 +831,23 @@ map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, assert(PyTuple_Check(map)); assert(PyDict_Check(dict)); assert(PyTuple_Size(map) >= nmap); - for (j=0; j < nmap; j++) { + for (j = nmap; --j >= 0; ) { PyObject *key = PyTuple_GET_ITEM(map, j); PyObject *value = values[j]; - assert(PyUnicode_Check(key)); - if (deref && value != NULL) { + assert(PyString_Check(key)); + if (deref) { assert(PyCell_Check(value)); value = PyCell_GET(value); } if (value == NULL) { - if (PyObject_DelItem(dict, key) != 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) - PyErr_Clear(); - else - return -1; - } + if (PyObject_DelItem(dict, key) != 0) + PyErr_Clear(); } else { if (PyObject_SetItem(dict, key, value) != 0) - return -1; + PyErr_Clear(); } } - return 0; } /* Copy values from the "locals" dict into the fast locals. @@ -1049,10 +879,10 @@ dict_to_map(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, assert(PyTuple_Check(map)); assert(PyDict_Check(dict)); assert(PyTuple_Size(map) >= nmap); - for (j=0; j < nmap; j++) { + for (j = nmap; --j >= 0; ) { PyObject *key = PyTuple_GET_ITEM(map, j); PyObject *value = PyObject_GetItem(dict, key); - assert(PyUnicode_Check(key)); + assert(PyString_Check(key)); /* We only care about NULLs if clear is true. */ if (value == NULL) { PyErr_Clear(); @@ -1073,49 +903,42 @@ dict_to_map(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, } } -int -PyFrame_FastToLocalsWithError(PyFrameObject *f) +void +PyFrame_FastToLocals(PyFrameObject *f) { /* Merge fast locals into f->f_locals */ PyObject *locals, *map; PyObject **fast; + PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; Py_ssize_t j; - Py_ssize_t ncells, nfreevars; - - if (f == NULL) { - PyErr_BadInternalCall(); - return -1; - } + int ncells, nfreevars; + if (f == NULL) + return; locals = f->f_locals; if (locals == NULL) { locals = f->f_locals = PyDict_New(); - if (locals == NULL) - return -1; + if (locals == NULL) { + PyErr_Clear(); /* Can't report it :-( */ + return; + } } co = f->f_code; map = co->co_varnames; - if (!PyTuple_Check(map)) { - PyErr_Format(PyExc_SystemError, - "co_varnames must be a tuple, not %s", - Py_TYPE(map)->tp_name); - return -1; - } + if (!PyTuple_Check(map)) + return; + PyErr_Fetch(&error_type, &error_value, &error_traceback); fast = f->f_localsplus; j = PyTuple_GET_SIZE(map); if (j > co->co_nlocals) j = co->co_nlocals; - if (co->co_nlocals) { - if (map_to_dict(map, j, locals, fast, 0) < 0) - return -1; - } + if (co->co_nlocals) + map_to_dict(map, j, locals, fast, 0); ncells = PyTuple_GET_SIZE(co->co_cellvars); nfreevars = PyTuple_GET_SIZE(co->co_freevars); if (ncells || nfreevars) { - if (map_to_dict(co->co_cellvars, ncells, - locals, fast + co->co_nlocals, 1)) - return -1; - + map_to_dict(co->co_cellvars, ncells, + locals, fast + co->co_nlocals, 1); /* If the namespace is unoptimized, then one of the following cases applies: 1. It does not contain free variables, because it @@ -1125,24 +948,11 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) into the locals dict used by the class. */ if (co->co_flags & CO_OPTIMIZED) { - if (map_to_dict(co->co_freevars, nfreevars, - locals, fast + co->co_nlocals + ncells, 1) < 0) - return -1; + map_to_dict(co->co_freevars, nfreevars, + locals, fast + co->co_nlocals + ncells, 1); } } - return 0; -} - -void -PyFrame_FastToLocals(PyFrameObject *f) -{ - int res; - - assert(!PyErr_Occurred()); - - res = PyFrame_FastToLocalsWithError(f); - if (res < 0) - PyErr_Clear(); + PyErr_Restore(error_type, error_value, error_traceback); } void @@ -1154,7 +964,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; Py_ssize_t j; - Py_ssize_t ncells, nfreevars; + int ncells, nfreevars; if (f == NULL) return; locals = f->f_locals; @@ -1203,17 +1013,9 @@ PyFrame_ClearFreeList(void) } void -_PyFrame_Fini(void) +PyFrame_Fini(void) { (void)PyFrame_ClearFreeList(); + Py_XDECREF(builtin_object); + builtin_object = NULL; } - -/* Print summary info about the state of the optimized allocator */ -void -_PyFrame_DebugMallocStats(FILE *out) -{ - _PyDebugAllocatorStats(out, - "free PyFrameObject", - numfree, sizeof(PyFrameObject)); -} - |