[Python-checkins] python/dist/src/Objects frameobject.c,2.67,2.68

mwh@users.sourceforge.net mwh@users.sourceforge.net
2002年12月17日 08:16:10 -0800


Update of /cvsroot/python/python/dist/src/Objects
In directory sc8-pr-cvs1:/tmp/cvs-serv23499/Objects
Modified Files:
	frameobject.c 
Log Message:
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.
Index: frameobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/frameobject.c,v
retrieving revision 2.67
retrieving revision 2.68
diff -C2 -d -r2.67 -r2.68
*** frameobject.c	11 Sep 2002 15:36:32 -0000	2.67
--- frameobject.c	17 Dec 2002 16:15:34 -0000	2.68
***************
*** 9,12 ****
--- 9,15 ----
 #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)
 
***************
*** 45,48 ****
--- 48,305 ----
 }
 
+ /* 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)
***************
*** 78,82 ****
 static PyGetSetDef frame_getsetlist[] = {
 	{"f_locals",	(getter)frame_getlocals, NULL, NULL},
! 	{"f_lineno",	(getter)frame_getlineno, NULL, NULL},
 	{"f_trace",	(getter)frame_gettrace, (setter)frame_settrace, NULL},
 	{0}
--- 335,340 ----
 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},
 	{0}

AltStyle によって変換されたページ (->オリジナル) /