# HG changeset patch # Parent 20f2f5193b048a8cbe728fcee8e127e6b923a7f4 diff -r 20f2f5193b04 Include/opcode.h --- a/Include/opcode.h Sun Dec 18 03:26:31 2011 +0100 +++ b/Include/opcode.h Sun Dec 18 23:04:11 2011 -0600 @@ -143,9 +143,18 @@ /* 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) */ +#define WHY_NOT 0 /* No error */ +#define WHY_EXCEPTION 1 /* Exception occurred */ +#define WHY_RERAISE 2 /* Exception re-raised by 'finally' */ +#define WHY_RETURN 3 /* 'return' statement */ +#define WHY_BREAK 4 /* 'break' statement */ +#define WHY_CONTINUE 5 /* 'continue' statement */ +#define WHY_YIELD 6 /* 'yield' operator */ +#define WHY_SILENCED 7 /* 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 Sun Dec 18 23:04:11 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/genobject.c --- a/Objects/genobject.c Sun Dec 18 03:26:31 2011 +0100 +++ b/Objects/genobject.c Sun Dec 18 23:04:11 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, @@ -78,8 +83,20 @@ 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); + why = tstate->why_exit; gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It @@ -88,31 +105,63 @@ 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); + 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); + case 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: { + /* Should never get here. */ + PyErr_Format(PyExc_SystemError, + "unhandled why case in generator: %d", why); + result = NULL; + } } return result; @@ -421,3 +470,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 Sun Dec 18 23:04:11 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 = WHY_NOT; /* 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 */ @@ -1121,6 +1107,7 @@ return NULL; tstate->frame = f; + co = f->f_code; if (tstate->use_tracing) { if (tstate->c_tracefunc != NULL) { @@ -1156,7 +1143,6 @@ } } - co = f->f_code; names = co->co_names; consts = co->co_consts; fastlocals = f->f_localsplus; @@ -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 @@ -1858,20 +1833,30 @@ 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) - 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; + why = PyLong_AS_LONG(v); + 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: %d", why); + retval = NULL; } } else if (PyExceptionClass_Check(v)) { @@ -2609,7 +2594,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; @@ -2942,9 +2927,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; @@ -2969,26 +2954,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 +2992,8 @@ exit_eval_frame: Py_LeaveRecursiveCall(); tstate->frame = f->f_back; +/* if (co->co_flags & CO_GENERATOR)*/ + tstate->why_exit = why; return retval; } @@ -3405,63 +3373,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; diff -r 20f2f5193b04 Python/pystate.c --- a/Python/pystate.c Sun Dec 18 03:26:31 2011 +0100 +++ b/Python/pystate.c Sun Dec 18 23:04:11 2011 -0600 @@ -201,6 +201,7 @@ tstate->exc_type = NULL; tstate->exc_value = NULL; tstate->exc_traceback = NULL; + tstate->why_exit = 0; tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL;