# HG changeset patch # Parent 8fa1dc66de5d452212bebbac2ac688e4fc570579 diff -r 8fa1dc66de5d Include/frameobject.h --- a/Include/frameobject.h Sun Dec 04 08:14:18 2011 -0500 +++ b/Include/frameobject.h Thu Dec 15 15:31:52 2011 -0600 @@ -36,7 +36,8 @@ non-generator frames. See the SAVE_EXC_STATE and SWAP_EXC_STATE macros in ceval.c for details of their use. */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; - + int f_why; /* So generators can see why. */ + PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ /* Call PyFrame_GetLineNumber() instead of reading this field diff -r 8fa1dc66de5d Include/opcode.h --- a/Include/opcode.h Sun Dec 04 08:14:18 2011 -0500 +++ b/Include/opcode.h Thu Dec 15 15:31:52 2011 -0600 @@ -146,6 +146,15 @@ remaining private.*/ #define EXCEPT_HANDLER 257 +/* Status code for main loop (reason for stack unwind) */ +#define WHY_NOT 0 /*0x0001 No error */ +#define WHY_EXCEPTION 1 /*0x0002 Exception occurred */ +#define WHY_RERAISE 2 /*0x0004 Exception re-raised by 'finally' */ +#define WHY_RETURN 3 /*0x0008 'return' statement */ +#define WHY_BREAK 4 /*0x0010 'break' statement */ +#define WHY_CONTINUE 5 /*0x0020 'continue' statement */ +#define WHY_YIELD 6 /*0x0040 'yield' operator */ +#define WHY_SILENCED 7 /*0x0080 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 8fa1dc66de5d Objects/frameobject.c --- a/Objects/frameobject.c Sun Dec 04 08:14:18 2011 -0500 +++ b/Objects/frameobject.c Thu Dec 15 15:31:52 2011 -0600 @@ -711,6 +711,7 @@ f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; + f->f_why = 0; _PyObject_GC_TRACK(f); return f; diff -r 8fa1dc66de5d Objects/genobject.c --- a/Objects/genobject.c Sun Dec 04 08:14:18 2011 -0500 +++ b/Objects/genobject.c Thu Dec 15 15:31:52 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; + int why; if (gen->gi_running) { PyErr_SetString(PyExc_ValueError, @@ -77,42 +82,83 @@ Py_XINCREF(tstate->frame); assert(f->f_back == NULL); f->f_back = tstate->frame; + + if (!exc) { + if (f->f_exc_type != NULL && f->f_exc_type != Py_None) { + /* We were (are) in an except handler when we left, + restore the exception state which was put aside. */ + swap_exc_state(tstate, f); + } + else { + save_exc_state(tstate, f); + } + } gen->gi_running = 1; result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; - + /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ 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; + + 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, 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); } - 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); + default: { /* WHY_EXCEPTION */ + /* + If the generator just returned (as opposed to yielding), + signal that the generator is exhausted. + + 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; + result = NULL; + break; + } +/* default:*/ +/* PyErr_Format(PyExc_SystemError,*/ +/* "Unhandled why case in generator: %d", why);*/ } return result; @@ -421,3 +467,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 8fa1dc66de5d Python/ceval.c --- a/Python/ceval.c Sun Dec 04 08:14:18 2011 -0500 +++ b/Python/ceval.c Thu Dec 15 15:31:52 2011 -0600 @@ -738,22 +738,8 @@ 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 do_raise(PyObject *, PyObject *); static int unpack_iterable(PyObject *, int, int, PyObject **); /* Records whether tracing is on for any thread. Counts the number of @@ -797,7 +783,7 @@ register unsigned char *next_instr; register int opcode; /* Current opcode */ register int oparg; /* Current opcode argument, if any */ - register enum why_code why; /* Reason for block stack unwind */ + register int why = 0; /* Reason for block stack unwind */ register int err; /* Error status -- nonzero if error */ register PyObject *x; /* Result object -- NULL if error */ register PyObject *v; /* Temporary objects popped off stack */ @@ -1184,17 +1170,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 @@ -1855,23 +1830,35 @@ DISPATCH(); PREDICTED(END_FINALLY); - TARGET(END_FINALLY) - 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) + TARGET(END_FINALLY) + v = POP(); + if (PyLong_Check(v)) { + why = PyLong_AsLong(v); + switch(why) { + case WHY_RETURN: + case WHY_CONTINUE: retval = POP(); - if (why == WHY_SILENCED) { + 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. */ + 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: %d", why); } } else if (PyExceptionClass_Check(v)) { @@ -2609,7 +2596,7 @@ else if (err > 0) { err = 0; /* There was an exception and a True return */ - PUSH(PyLong_FromLong((long) WHY_SILENCED)); + PUSH(PyLong_FromLong(WHY_SILENCED)); } PREDICT(END_FINALLY); break; @@ -2879,6 +2866,7 @@ /* 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]; @@ -2942,9 +2930,9 @@ break; } if (b->b_type == SETUP_FINALLY) { - if (why & (WHY_RETURN | WHY_CONTINUE)) + if (why == WHY_RETURN || why == WHY_CONTINUE) PUSH(retval); - PUSH(PyLong_FromLong((long)why)); + PUSH(PyLong_FromLong(why)); why = WHY_NOT; JUMPTO(b->b_handler); break; @@ -2970,25 +2958,6 @@ 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); - } if (tstate->use_tracing) { if (tstate->c_tracefunc) { @@ -3026,6 +2995,7 @@ exit_eval_frame: Py_LeaveRecursiveCall(); tstate->frame = f->f_back; + f->f_why = why; /* for generator */ return retval; } @@ -3405,63 +3375,10 @@ } -/* 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. */ -static enum why_code +static int do_raise(PyObject *exc, PyObject *cause) { PyObject *type = NULL, *value = NULL;