mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
This is Richie Hindle's patch
[ 643835 ] Set Next Statement for Python debuggers with a few tweaks by me: adding an unsigned or two, mentioning that not all jumps are allowed in the doc for pdb, adding a NEWS item and a note to whatsnew, and AuCTeX doing something cosmetic to libpdb.tex.
This commit is contained in:
parent
f680cc460c
commit
cfd3884882
7 changed files with 607 additions and 13 deletions
|
@ -8,6 +8,9 @@
|
|||
#include "opcode.h"
|
||||
#include "structmember.h"
|
||||
|
||||
#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[] = {
|
||||
|
@ -44,6 +47,260 @@ frame_getlineno(PyFrameObject *f, void *closure)
|
|||
return PyInt_FromLong(lineno);
|
||||
}
|
||||
|
||||
/* 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
|
||||
* state of the stack (obvious because you could remove the line and the code
|
||||
* would still work without any stack errors), but there are some constructs
|
||||
* that limit jumping:
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
static int
|
||||
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 */
|
||||
char *code = NULL; /* The bytecode for the frame... */
|
||||
int code_len = 0; /* ...and its length */
|
||||
char *lnotab = NULL; /* Iterating over co_lnotab */
|
||||
int 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) */
|
||||
int setup_op = 0; /* (ditto) */
|
||||
|
||||
/* f_lineno must be an integer. */
|
||||
if (!PyInt_Check(p_new_lineno)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"lineno must be an integer");
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Find the bytecode offset for the start of the given line, or the
|
||||
* first code-owning line after it. */
|
||||
PyString_AsStringAndSize(f->f_code->co_lnotab, &lnotab, &lnotab_len);
|
||||
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 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;
|
||||
}
|
||||
|
||||
/* We're now ready to look at the bytecode. */
|
||||
PyString_AsStringAndSize(f->f_code->co_code, &code, &code_len);
|
||||
min_addr = MIN(new_lasti, f->f_lasti);
|
||||
max_addr = MAX(new_lasti, f->f_lasti);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
blockstack[blockstack_top++] = addr;
|
||||
in_finally[blockstack_top-1] = 0;
|
||||
break;
|
||||
|
||||
case POP_BLOCK:
|
||||
setup_op = code[blockstack[blockstack_top-1]];
|
||||
if (setup_op == SETUP_FINALLY) {
|
||||
in_finally[blockstack_top-1] = 1;
|
||||
}
|
||||
else {
|
||||
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. */
|
||||
setup_op = code[blockstack[blockstack_top-1]];
|
||||
if (setup_op == SETUP_FINALLY) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op >= HAVE_ARGUMENT) {
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
delta_iblock++;
|
||||
break;
|
||||
|
||||
case POP_BLOCK:
|
||||
delta_iblock--;
|
||||
break;
|
||||
}
|
||||
|
||||
min_delta_iblock = MIN(min_delta_iblock, delta_iblock);
|
||||
|
||||
if (op >= HAVE_ARGUMENT) {
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally set the new f_lineno and f_lasti and return OK. */
|
||||
f->f_lineno = new_lineno;
|
||||
f->f_lasti = new_lasti;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
frame_gettrace(PyFrameObject *f, void *closure)
|
||||
{
|
||||
|
@ -77,7 +334,8 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
|
|||
|
||||
static PyGetSetDef frame_getsetlist[] = {
|
||||
{"f_locals", (getter)frame_getlocals, NULL, NULL},
|
||||
{"f_lineno", (getter)frame_getlineno, NULL, NULL},
|
||||
{"f_lineno", (getter)frame_getlineno,
|
||||
(setter)frame_setlineno, NULL},
|
||||
{"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL},
|
||||
{0}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue