# HG changeset patch # Parent 20f2f5193b048a8cbe728fcee8e127e6b923a7f4 diff -r 20f2f5193b04 Include/frameobject.h --- a/Include/frameobject.h Sun Dec 18 03:26:31 2011 +0100 +++ b/Include/frameobject.h Sat Dec 24 00:00:41 2011 -0600 @@ -46,6 +46,7 @@ bytecode index. */ int f_lineno; /* Current line number */ int f_iblock; /* index in f_blockstack */ + int f_why_exit; /* Exit block reason */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject; diff -r 20f2f5193b04 Include/opcode.h --- a/Include/opcode.h Sun Dec 18 03:26:31 2011 +0100 +++ b/Include/opcode.h Sat Dec 24 00:00:41 2011 -0600 @@ -143,9 +143,21 @@ /* EXCEPT_HANDLER is a special, implicit block type which is created when entering an except handler. It is not an opcode but we define it here as we want it to be available to both frameobject.c and ceval.c, while - remaining private.*/ + remaining private. (Same goes for the WHY_... values.) */ #define EXCEPT_HANDLER 257 +/* Eval loop and block exit status. (reason for stack unwind) */ +/* Status code for main loop (reason for stack unwind) */ +enum why_code { + WHY_NOT, /* No error */ + WHY_EXCEPTION, /* Exception occurred */ + WHY_RERAISE, /* Exception re-raised by 'finally' */ + WHY_RETURN, /* 'return' statement */ + WHY_BREAK, /* 'break' statement */ + WHY_CONTINUE, /* 'continue' statement */ + WHY_YIELD, /* 'yield' operator */ + WHY_SILENCED /* Exception silenced by 'with' */ +}; enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD}; diff -r 20f2f5193b04 Include/pystate.h --- a/Include/pystate.h Sun Dec 18 03:26:31 2011 +0100 +++ b/Include/pystate.h Sat Dec 24 00:00:41 2011 -0600 @@ -109,6 +109,8 @@ int tick_counter; int gilstate_counter; + + int why_exit; /* Reason for evalframe exit. */ PyObject *async_exc; /* Asynchronous exception to raise */ long thread_id; /* Thread id where this tstate was created */ diff -r 20f2f5193b04 Objects/frameobject.c --- a/Objects/frameobject.c Sun Dec 18 03:26:31 2011 +0100 +++ b/Objects/frameobject.c Sat Dec 24 00:00:41 2011 -0600 @@ -711,6 +711,7 @@ f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; + f->f_why_exit = WHY_NOT; _PyObject_GC_TRACK(f); return f; diff -r 20f2f5193b04 Objects/genobject.c --- a/Objects/genobject.c Sun Dec 18 03:26:31 2011 +0100 +++ b/Objects/genobject.c Sat Dec 24 00:00:41 2011 -0600 @@ -5,6 +5,10 @@ #include "structmember.h" #include "opcode.h" +static void save_exc_state(PyThreadState *, PyFrameObject *); +static void swap_exc_state(PyThreadState *, PyFrameObject *); +static void restore_and_clear_exc_state(PyThreadState *, PyFrameObject *); + static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { @@ -45,6 +49,7 @@ PyThreadState *tstate = PyThreadState_GET(); PyFrameObject *f = gen->gi_frame; PyObject *result; + enum why_code why; if (gen->gi_running) { PyErr_SetString(PyExc_ValueError, @@ -73,11 +78,22 @@ } /* Generators always return to their most recent caller, not - * necessarily their creator. */ + necessarily their creator. */ Py_XINCREF(tstate->frame); assert(f->f_back == NULL); f->f_back = tstate->frame; + if (!exc) { + if (f->f_exc_type != NULL) + /* We were in an except handler when we last yielded, + restore the saved exception state to the tstate obj + and save the calling frames exception state. */ + swap_exc_state(tstate, f); + else if (tstate->exc_type != NULL) + /* Save the calling frames exception state. */ + save_exc_state(tstate, f); + } + gen->gi_running = 1; result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; @@ -88,31 +104,60 @@ assert(f->f_back == tstate->frame); Py_CLEAR(f->f_back); - /* If the generator just returned (as opposed to yielding), signal - * that the generator is exhausted. */ - if (result == Py_None && f->f_stacktop == NULL) { - Py_DECREF(result); - result = NULL; - /* Set exception if not called by gen_iternext() */ - if (arg) - PyErr_SetNone(PyExc_StopIteration); + why = f->f_why_exit; + + if (why != WHY_EXCEPTION) { + /* The purpose of this block is to put aside the generator's exception + state and restore that of the calling frame. If the current + exception state is from the caller, we clear the exception values + on the generator frame, so they are not swapped back in latter. + + The origin of the current exception state is determined by checking + for except handler blocks, which we must be in iff a new exception + state came into existence in this frame. (An uncaught exception + would have why == WHY_EXCEPTION). */ + int i; + for (i = 0; i < f->f_iblock; i++) + if (f->f_blockstack[i].b_type == EXCEPT_HANDLER) + break; + if (i == f->f_iblock) + /* We did not create this exception. */ + restore_and_clear_exc_state(tstate, f); + else + swap_exc_state(tstate, f); } - if (!result || f->f_stacktop == NULL) { - /* generator can't be rerun, so release the frame */ - /* first clean reference cycle through stored exception traceback */ - PyObject *t, *v, *tb; - t = f->f_exc_type; - v = f->f_exc_value; - tb = f->f_exc_traceback; - f->f_exc_type = NULL; - f->f_exc_value = NULL; - f->f_exc_traceback = NULL; - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); - Py_DECREF(f); - gen->gi_frame = NULL; + switch (why) { + case WHY_YIELD: + break; + case WHY_RETURN: + Py_DECREF(result); + PyErr_SetNone(PyExc_StopIteration); + case WHY_RERAISE: + case WHY_EXCEPTION: { + /* If the generator just returned (as opposed to yielding), + signal that the generator is exhausted. The generator + can't be rerun, so release the frame but first clean + reference cycle through stored exception traceback. */ + PyObject *t, *v, *tb; + t = f->f_exc_type; + v = f->f_exc_value; + tb = f->f_exc_traceback; + f->f_exc_type = NULL; + f->f_exc_value = NULL; + f->f_exc_traceback = NULL; + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); + Py_DECREF(f); + gen->gi_frame = NULL; + result = NULL; + break; } + default: + /* Should never get here. */ + PyErr_Format(PyExc_SystemError, + "unhandled why case in generator: %d", why); + result = NULL; } return result; @@ -421,3 +466,58 @@ /* No blocks except loops, it's safe to skip finalization. */ return 0; } + + +/* These 3 functions deal with the exception state of generators. */ + +static void +save_exc_state(PyThreadState *tstate, PyFrameObject *f) +{ + PyObject *type, *value, *traceback; + Py_XINCREF(tstate->exc_type); + Py_XINCREF(tstate->exc_value); + Py_XINCREF(tstate->exc_traceback); + type = f->f_exc_type; + value = f->f_exc_value; + traceback = f->f_exc_traceback; + f->f_exc_type = tstate->exc_type; + f->f_exc_value = tstate->exc_value; + f->f_exc_traceback = tstate->exc_traceback; + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); +} + +static void +swap_exc_state(PyThreadState *tstate, PyFrameObject *f) +{ + PyObject *tmp; + tmp = tstate->exc_type; + tstate->exc_type = f->f_exc_type; + f->f_exc_type = tmp; + tmp = tstate->exc_value; + tstate->exc_value = f->f_exc_value; + f->f_exc_value = tmp; + tmp = tstate->exc_traceback; + tstate->exc_traceback = f->f_exc_traceback; + f->f_exc_traceback = tmp; +} + +static void +restore_and_clear_exc_state(PyThreadState *tstate, PyFrameObject *f) +{ + PyObject *type, *value, *tb; + type = tstate->exc_type; + value = tstate->exc_value; + tb = tstate->exc_traceback; + tstate->exc_type = f->f_exc_type; + tstate->exc_value = f->f_exc_value; + tstate->exc_traceback = f->f_exc_traceback; + f->f_exc_type = NULL; + f->f_exc_value = NULL; + f->f_exc_traceback = NULL; + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tb); +} + diff -r 20f2f5193b04 Python/ceval.c --- a/Python/ceval.c Sun Dec 18 03:26:31 2011 +0100 +++ b/Python/ceval.c Sat Dec 24 00:00:41 2011 -0600 @@ -738,21 +738,7 @@ return 0; } -/* Status code for main loop (reason for stack unwind) */ -enum why_code { - WHY_NOT = 0x0001, /* No error */ - WHY_EXCEPTION = 0x0002, /* Exception occurred */ - WHY_RERAISE = 0x0004, /* Exception re-raised by 'finally' */ - WHY_RETURN = 0x0008, /* 'return' statement */ - WHY_BREAK = 0x0010, /* 'break' statement */ - WHY_CONTINUE = 0x0020, /* 'continue' statement */ - WHY_YIELD = 0x0040, /* 'yield' operator */ - WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */ -}; - -static void save_exc_state(PyThreadState *, PyFrameObject *); -static void swap_exc_state(PyThreadState *, PyFrameObject *); -static void restore_and_clear_exc_state(PyThreadState *, PyFrameObject *); + static enum why_code do_raise(PyObject *, PyObject *); static int unpack_iterable(PyObject *, int, int, PyObject **); @@ -986,6 +972,7 @@ /* shut up the compiler */ opcode = 0; + #endif /* Code access macros */ @@ -1121,6 +1108,7 @@ return NULL; tstate->frame = f; + why = WHY_NOT; if (tstate->use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1184,17 +1172,6 @@ assert(stack_pointer != NULL); f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ - if (co->co_flags & CO_GENERATOR && !throwflag) { - if (f->f_exc_type != NULL && f->f_exc_type != Py_None) { - /* We were in an except handler when we left, - restore the exception state which was put aside - (see YIELD_VALUE). */ - swap_exc_state(tstate, f); - } - else - save_exc_state(tstate, f); - } - #ifdef LLTRACE lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; #endif @@ -1859,19 +1836,29 @@ v = POP(); if (PyLong_Check(v)) { why = (enum why_code) PyLong_AS_LONG(v); - assert(why != WHY_YIELD); - if (why == WHY_RETURN || - why == WHY_CONTINUE) - retval = POP(); - if (why == WHY_SILENCED) { - /* An exception was silenced by 'with', we must - manually unwind the EXCEPT_HANDLER block which was - created when the exception was caught, otherwise - the stack will be in an inconsistent state. */ - PyTryBlock *b = PyFrame_BlockPop(f); - assert(b->b_type == EXCEPT_HANDLER); - UNWIND_EXCEPT_HANDLER(b); - why = WHY_NOT; + switch (why) { + case WHY_RETURN: + case WHY_CONTINUE: + retval = POP(); + break; + case WHY_SILENCED: { + /* An exception was silenced by 'with', we must + manually unwind the EXCEPT_HANDLER block which was + created when the exception was caught, otherwise + the stack will be in an inconsistent state. */ + PyTryBlock *b = PyFrame_BlockPop(f); + assert(b->b_type == EXCEPT_HANDLER); + UNWIND_EXCEPT_HANDLER(b); + why = WHY_NOT; + break; + } + case WHY_BREAK: + break; + default: + /* Should never get here. */ + PyErr_Format(PyExc_SystemError, + "unhandled why case in ceval.c: %d", why); + retval = NULL; } } else if (PyExceptionClass_Check(v)) { @@ -2555,7 +2542,7 @@ } else if (PyLong_Check(u)) { (void)POP(); - switch(PyLong_AsLong(u)) { + switch((enum why_code) PyLong_AsLong(u)) { case WHY_RETURN: case WHY_CONTINUE: /* Retval in TOP. */ @@ -2814,7 +2801,7 @@ } /* switch */ - on_error: +on_error: READ_TIMESTAMP(inst1); @@ -2871,40 +2858,26 @@ tstate->c_traceobj, f); } - /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ - - if (why == WHY_RERAISE) - why = WHY_EXCEPTION; - /* Unwind stacks if a (pseudo) exception occurred */ fast_block_end: - while (why != WHY_NOT && f->f_iblock > 0) { - /* Peek at the current block. */ - PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1]; - - assert(why != WHY_YIELD); - if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { - why = WHY_NOT; - JUMPTO(PyLong_AS_LONG(retval)); - Py_DECREF(retval); - break; - } - /* Now we have to pop the block. */ - f->f_iblock--; - - if (b->b_type == EXCEPT_HANDLER) { - UNWIND_EXCEPT_HANDLER(b); - continue; - } - UNWIND_BLOCK(b); - if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { - why = WHY_NOT; - JUMPTO(b->b_handler); - break; - } - if (why == WHY_EXCEPTION && (b->b_type == SETUP_EXCEPT - || b->b_type == SETUP_FINALLY)) { + + switch (why) { + /* The why doesn't change while we are unwinding so + switch on the why outside the while loop. */ + + case WHY_EXCEPTION: + case WHY_RERAISE: + while (f->f_iblock > 0) { + PyTryBlock *b = &f->f_blockstack[--f->f_iblock]; + if (b->b_type == EXCEPT_HANDLER) { + UNWIND_EXCEPT_HANDLER(b); + continue; + } + UNWIND_BLOCK(b); + if (b->b_type == SETUP_LOOP) + continue; + /* SETUP_EXCEPT or SETUP_FINALLY block */ PyObject *exc, *val, *tb; int handler = b->b_handler; /* Beware, this invalidates all b->b_* fields */ @@ -2941,25 +2914,88 @@ JUMPTO(handler); break; } - if (b->b_type == SETUP_FINALLY) { - if (why & (WHY_RETURN | WHY_CONTINUE)) + break; + + case WHY_RETURN: + while (f->f_iblock > 0) { + PyTryBlock *b = &f->f_blockstack[--f->f_iblock]; + if (b->b_type == EXCEPT_HANDLER) { + UNWIND_EXCEPT_HANDLER(b); + continue; + } + UNWIND_BLOCK(b); + if (b->b_type == SETUP_FINALLY) { PUSH(retval); - PUSH(PyLong_FromLong((long)why)); + PUSH(PyLong_FromLong((long) why)); + JUMPTO(b->b_handler); + why = WHY_NOT; + break; + } + /* continue SETUP_LOOP, SETUP_EXCEPT */ + } + break; + + case WHY_BREAK: + while (f->f_iblock > 0) { + PyTryBlock *b = &f->f_blockstack[--f->f_iblock]; + if (b->b_type == EXCEPT_HANDLER) { + UNWIND_EXCEPT_HANDLER(b); + continue; + } + UNWIND_BLOCK(b); + if (b->b_type == SETUP_EXCEPT) + continue; + if (b->b_type == SETUP_FINALLY) + PUSH(PyLong_FromLong((long) why)); + /* and SETUP_LOOP */ + JUMPTO(b->b_handler); why = WHY_NOT; - JUMPTO(b->b_handler); break; } - } /* unwind stack */ - - /* End the loop if we still have an error (or return) */ - + break; + + case WHY_CONTINUE: + while (f->f_iblock > 0) { + PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1]; + if (b->b_type == SETUP_LOOP) { + JUMPTO(PyLong_AsLong(retval)); + Py_DECREF(retval); + why = WHY_NOT; + break; + } + f->f_iblock--; + if (b->b_type == EXCEPT_HANDLER) { + UNWIND_EXCEPT_HANDLER(b); + continue; + } + UNWIND_BLOCK(b); + if (b->b_type == SETUP_FINALLY) { + PUSH(retval); + PUSH(PyLong_FromLong((long) why)); + JUMPTO(b->b_handler); + why = WHY_NOT; + break; + } + /* continue SETUP_EXCEPT */ + } + break; + + default: + /* Should never get here. */ + PyErr_Format(PyExc_SystemError, + "unhandled why case while exiting block: %d", why); + retval = NULL; + + } /* fast_block_end */ + + /* End the main loop if we still have an error (or return) */ if (why != WHY_NOT) break; + READ_TIMESTAMP(loop1); } /* main loop */ - assert(why != WHY_YIELD); /* Pop remaining stack entries. */ while (!EMPTY()) { v = POP(); @@ -2969,26 +3005,7 @@ if (why != WHY_RETURN) retval = NULL; -fast_yield: - if (co->co_flags & CO_GENERATOR && (why == WHY_YIELD || why == WHY_RETURN)) { - /* The purpose of this block is to put aside the generator's exception - state and restore that of the calling frame. If the current - exception state is from the caller, we clear the exception values - on the generator frame, so they are not swapped back in latter. The - origin of the current exception state is determined by checking for - except handler blocks, which we must be in iff a new exception - state came into existence in this frame. (An uncaught exception - would have why == WHY_EXCEPTION, and we wouldn't be here). */ - int i; - for (i = 0; i < f->f_iblock; i++) - if (f->f_blockstack[i].b_type == EXCEPT_HANDLER) - break; - if (i == f->f_iblock) - /* We did not create this exception. */ - restore_and_clear_exc_state(tstate, f); - else - swap_exc_state(tstate, f); - } +fast_yield: /* Skips over clearing the value stack. */ if (tstate->use_tracing) { if (tstate->c_tracefunc) { @@ -3026,6 +3043,7 @@ exit_eval_frame: Py_LeaveRecursiveCall(); tstate->frame = f->f_back; + f->f_why_exit = why; return retval; } @@ -3405,59 +3423,6 @@ } -/* These 3 functions deal with the exception state of generators. */ - -static void -save_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - PyObject *type, *value, *traceback; - Py_XINCREF(tstate->exc_type); - Py_XINCREF(tstate->exc_value); - Py_XINCREF(tstate->exc_traceback); - type = f->f_exc_type; - value = f->f_exc_value; - traceback = f->f_exc_traceback; - f->f_exc_type = tstate->exc_type; - f->f_exc_value = tstate->exc_value; - f->f_exc_traceback = tstate->exc_traceback; - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); -} - -static void -swap_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - PyObject *tmp; - tmp = tstate->exc_type; - tstate->exc_type = f->f_exc_type; - f->f_exc_type = tmp; - tmp = tstate->exc_value; - tstate->exc_value = f->f_exc_value; - f->f_exc_value = tmp; - tmp = tstate->exc_traceback; - tstate->exc_traceback = f->f_exc_traceback; - f->f_exc_traceback = tmp; -} - -static void -restore_and_clear_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - PyObject *type, *value, *tb; - type = tstate->exc_type; - value = tstate->exc_value; - tb = tstate->exc_traceback; - tstate->exc_type = f->f_exc_type; - tstate->exc_value = f->f_exc_value; - tstate->exc_traceback = f->f_exc_traceback; - f->f_exc_type = NULL; - f->f_exc_value = NULL; - f->f_exc_traceback = NULL; - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(tb); -} - /* Logic for the raise statement (too complicated for inlining). This *consumes* a reference count to each of its arguments. */