diff -r c53d24ee3963 Include/frameobject.h --- a/Include/frameobject.h Sat Jul 25 02:45:18 2015 +0200 +++ b/Include/frameobject.h Sat Jul 25 13:13:00 2015 +0200 @@ -27,15 +27,6 @@ to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ - - /* In a generator, we need to be able to swap between the exception - state inside the generator and the exception state of the calling - frame (which shouldn't be impacted when the generator "yields" - from an except handler). - These three fields exist exactly for that, and are unused for - non-generator frames. See the save_exc_state and swap_exc_state - functions in ceval.c for details of their use. */ - PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; diff -r c53d24ee3963 Include/genobject.h --- a/Include/genobject.h Sat Jul 25 02:45:18 2015 +0200 +++ b/Include/genobject.h Sat Jul 25 13:13:00 2015 +0200 @@ -25,7 +25,8 @@ /* Name of the generator. */ \ PyObject *prefix##_name; \ /* Qualified name of the generator. */ \ - PyObject *prefix##_qualname; + PyObject *prefix##_qualname; \ + PyExcState prefix##_exc_state; typedef struct { /* The gi_ prefix is intended to remind of generator-iterator. */ diff -r c53d24ee3963 Include/pystate.h --- a/Include/pystate.h Sat Jul 25 02:45:18 2015 +0200 +++ b/Include/pystate.h Sat Jul 25 13:13:00 2015 +0200 @@ -66,6 +66,20 @@ #ifdef Py_LIMITED_API typedef struct _ts PyThreadState; #else + +typedef struct _excstate { + /* In a generator, we need to be able to swap between the exception + state inside the generator and the exception state of the calling + frame (which shouldn't be impacted when the generator "yields" + from an except handler). + These three fields exist exactly for that. */ + PyObject *exc_type, *exc_value, *exc_traceback; + + struct _excstate *exc_previous; + +} PyExcState; + + typedef struct _ts { /* See Python/ceval.c for comments explaining most fields */ @@ -94,9 +108,7 @@ PyObject *curexc_value; PyObject *curexc_traceback; - PyObject *exc_type; - PyObject *exc_value; - PyObject *exc_traceback; + PyExcState exc_state; PyObject *dict; /* Stores per-thread state */ @@ -139,6 +151,8 @@ /* XXX signal handlers should also be here */ + PyExcState *exc_info; + } PyThreadState; #endif diff -r c53d24ee3963 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Sat Jul 25 02:45:18 2015 +0200 +++ b/Lib/test/test_sys.py Sat Jul 25 13:13:00 2015 +0200 @@ -925,7 +925,7 @@ nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ ncells + nfrees - 1 - check(x, vsize('12P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) + check(x, vsize('9P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size('12P')) @@ -942,7 +942,7 @@ check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2PPP')) + check(get_gen(), size('Pb2PPP4P')) # iterator check(iter('abc'), size('lP')) # callable-iterator diff -r c53d24ee3963 Objects/frameobject.c --- a/Objects/frameobject.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Objects/frameobject.c Sat Jul 25 13:13:00 2015 +0200 @@ -386,8 +386,7 @@ * ob_type, ob_size, f_code, f_valuestack; - * f_locals, f_trace, - f_exc_type, f_exc_value, f_exc_traceback are NULL; + * f_locals, f_trace are NULL; * f_localsplus does not require re-allocation and the local variables in f_localsplus are NULL. @@ -443,9 +442,6 @@ Py_DECREF(f->f_globals); Py_CLEAR(f->f_locals); Py_CLEAR(f->f_trace); - Py_CLEAR(f->f_exc_type); - Py_CLEAR(f->f_exc_value); - Py_CLEAR(f->f_exc_traceback); co = f->f_code; if (co->co_zombieframe == NULL) @@ -474,9 +470,6 @@ Py_VISIT(f->f_globals); Py_VISIT(f->f_locals); Py_VISIT(f->f_trace); - Py_VISIT(f->f_exc_type); - Py_VISIT(f->f_exc_value); - Py_VISIT(f->f_exc_traceback); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -507,9 +500,6 @@ f->f_stacktop = NULL; f->f_executing = 0; - Py_CLEAR(f->f_exc_type); - Py_CLEAR(f->f_exc_value); - Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace); /* locals */ @@ -703,7 +693,6 @@ f->f_localsplus[i] = NULL; f->f_locals = NULL; f->f_trace = NULL; - f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_stacktop = f->f_valuestack; f->f_builtins = builtins; diff -r c53d24ee3963 Objects/genobject.c --- a/Objects/genobject.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Objects/genobject.c Sat Jul 25 13:13:00 2015 +0200 @@ -14,6 +14,9 @@ Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); + Py_VISIT(gen->gi_exc_state.exc_type); + Py_VISIT(gen->gi_exc_state.exc_value); + Py_VISIT(gen->gi_exc_state.exc_traceback); return 0; } @@ -74,6 +77,9 @@ Py_CLEAR(gen->gi_code); Py_CLEAR(gen->gi_name); Py_CLEAR(gen->gi_qualname); + Py_CLEAR(gen->gi_exc_state.exc_type); + Py_CLEAR(gen->gi_exc_state.exc_value); + Py_CLEAR(gen->gi_exc_state.exc_traceback); PyObject_GC_Del(gen); } @@ -122,7 +128,11 @@ f->f_back = tstate->frame; gen->gi_running = 1; + gen->gi_exc_state.exc_previous = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; result = PyEval_EvalFrameEx(f, exc); + tstate->exc_info = gen->gi_exc_state.exc_previous; + gen->gi_exc_state.exc_previous = NULL; gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It @@ -196,12 +206,12 @@ /* 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; + t = gen->gi_exc_state.exc_type; + v = gen->gi_exc_state.exc_value; + tb = gen->gi_exc_state.exc_traceback; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); @@ -654,6 +664,10 @@ gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; + gen->gi_exc_state.exc_previous = NULL; if (name != NULL) gen->gi_name = name; else diff -r c53d24ee3963 Python/ceval.c --- a/Python/ceval.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Python/ceval.c Sat Jul 25 13:13:00 2015 +0200 @@ -756,9 +756,6 @@ 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 int do_raise(PyObject *, PyObject *); static int unpack_iterable(PyObject *, int, int, PyObject **); @@ -1102,17 +1099,19 @@ #define UNWIND_EXCEPT_HANDLER(b) \ { \ PyObject *type, *value, *traceback; \ + PyExcState *exc_info; \ assert(STACK_LEVEL() >= (b)->b_level + 3); \ while (STACK_LEVEL() > (b)->b_level + 3) { \ value = POP(); \ Py_XDECREF(value); \ } \ - type = tstate->exc_type; \ - value = tstate->exc_value; \ - traceback = tstate->exc_traceback; \ - tstate->exc_type = POP(); \ - tstate->exc_value = POP(); \ - tstate->exc_traceback = POP(); \ + exc_info = tstate->exc_info; \ + type = exc_info->exc_type; \ + value = exc_info->exc_value; \ + traceback = exc_info->exc_traceback; \ + exc_info->exc_type = POP(); \ + exc_info->exc_value = POP(); \ + exc_info->exc_traceback = POP(); \ Py_XDECREF(type); \ Py_XDECREF(value); \ Py_XDECREF(traceback); \ @@ -1191,17 +1190,6 @@ f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_executing = 1; - if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { - if (!throwflag && 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_GetItemId(f->f_globals, &PyId___ltrace__) != NULL; #endif @@ -3445,12 +3433,13 @@ || b->b_type == SETUP_FINALLY)) { PyObject *exc, *val, *tb; int handler = b->b_handler; + PyExcState *exc_info = tstate->exc_info; /* Beware, this invalidates all b->b_* fields */ PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL()); - PUSH(tstate->exc_traceback); - PUSH(tstate->exc_value); - if (tstate->exc_type != NULL) { - PUSH(tstate->exc_type); + PUSH(exc_info->exc_traceback); + PUSH(exc_info->exc_value); + if (exc_info->exc_type != NULL) { + PUSH(exc_info->exc_type); } else { Py_INCREF(Py_None); @@ -3468,10 +3457,10 @@ else PyException_SetTraceback(val, Py_None); Py_INCREF(exc); - tstate->exc_type = exc; + exc_info->exc_type = exc; Py_INCREF(val); - tstate->exc_value = val; - tstate->exc_traceback = tb; + exc_info->exc_value = val; + exc_info->exc_traceback = tb; if (tb == NULL) tb = Py_None; Py_INCREF(tb); @@ -3515,26 +3504,6 @@ assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: - if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { - - /* 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) { @@ -3999,60 +3968,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. */ static int @@ -4063,10 +3978,11 @@ if (exc == NULL) { /* Reraise */ PyThreadState *tstate = PyThreadState_GET(); + PyExcState *exc_info = tstate->exc_info; PyObject *tb; - type = tstate->exc_type; - value = tstate->exc_value; - tb = tstate->exc_traceback; + type = exc_info->exc_type; + value = exc_info->exc_value; + tb = exc_info->exc_traceback; if (type == Py_None) { PyErr_SetString(PyExc_RuntimeError, "No active exception to reraise"); diff -r c53d24ee3963 Python/errors.c --- a/Python/errors.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Python/errors.c Sat Jul 25 13:13:00 2015 +0200 @@ -52,6 +52,14 @@ Py_XDECREF(oldtraceback); } +static PyExcState *get_exc_info(PyThreadState *tstate) { + PyExcState *exc_info = tstate->exc_info; + while ((exc_info->exc_type == NULL || exc_info->exc_type == Py_None) && + exc_info->exc_previous != NULL) + exc_info = exc_info->exc_previous; + return exc_info; +} + void PyErr_SetObject(PyObject *exception, PyObject *value) { @@ -67,7 +75,7 @@ return; } Py_XINCREF(value); - exc_value = tstate->exc_value; + exc_value = get_exc_info(tstate)->exc_value; if (exc_value != NULL && exc_value != Py_None) { /* Implicit exception chaining */ Py_INCREF(exc_value); @@ -355,10 +363,10 @@ PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { PyThreadState *tstate = PyThreadState_GET(); - - *p_type = tstate->exc_type; - *p_value = tstate->exc_value; - *p_traceback = tstate->exc_traceback; + PyExcState *exc_info = get_exc_info(tstate); + *p_type = exc_info->exc_type; + *p_value = exc_info->exc_value; + *p_traceback = exc_info->exc_traceback; Py_XINCREF(*p_type); Py_XINCREF(*p_value); @@ -371,13 +379,13 @@ PyObject *oldtype, *oldvalue, *oldtraceback; PyThreadState *tstate = PyThreadState_GET(); - oldtype = tstate->exc_type; - oldvalue = tstate->exc_value; - oldtraceback = tstate->exc_traceback; + oldtype = tstate->exc_info->exc_type; + oldvalue = tstate->exc_info->exc_value; + oldtraceback = tstate->exc_info->exc_traceback; - tstate->exc_type = p_type; - tstate->exc_value = p_value; - tstate->exc_traceback = p_traceback; + tstate->exc_info->exc_type = p_type; + tstate->exc_info->exc_value = p_value; + tstate->exc_info->exc_traceback = p_traceback; Py_XDECREF(oldtype); Py_XDECREF(oldvalue); diff -r c53d24ee3963 Python/pystate.c --- a/Python/pystate.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Python/pystate.c Sat Jul 25 13:13:00 2015 +0200 @@ -198,9 +198,11 @@ tstate->curexc_value = NULL; tstate->curexc_traceback = NULL; - tstate->exc_type = NULL; - tstate->exc_value = NULL; - tstate->exc_traceback = NULL; + tstate->exc_state.exc_type = NULL; + tstate->exc_state.exc_value = NULL; + tstate->exc_state.exc_traceback = NULL; + tstate->exc_state.exc_previous = NULL; + tstate->exc_info = &tstate->exc_state; tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; @@ -382,9 +384,13 @@ Py_CLEAR(tstate->curexc_value); Py_CLEAR(tstate->curexc_traceback); - Py_CLEAR(tstate->exc_type); - Py_CLEAR(tstate->exc_value); - Py_CLEAR(tstate->exc_traceback); + Py_CLEAR(tstate->exc_state.exc_type); + Py_CLEAR(tstate->exc_state.exc_value); + Py_CLEAR(tstate->exc_state.exc_traceback); + assert(tstate->exc_info.exc_previous == NULL); + if (Py_VerboseFlag && tstate->exc_info != &tstate->exc_state) + fprintf(stderr, + "PyThreadState_Clear: warning: thread still has a generator\n"); tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; diff -r c53d24ee3963 Python/sysmodule.c --- a/Python/sysmodule.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Python/sysmodule.c Sat Jul 25 13:13:00 2015 +0200 @@ -241,14 +241,16 @@ static PyObject * sys_exc_info(PyObject *self, PyObject *noargs) { - PyThreadState *tstate; - tstate = PyThreadState_GET(); + PyExcState *exc_info = PyThreadState_GET()->exc_info; + while ((exc_info->exc_type == NULL || exc_info->exc_type == Py_None) && + exc_info->exc_previous != NULL) + exc_info = exc_info->exc_previous; return Py_BuildValue( "(OOO)", - tstate->exc_type != NULL ? tstate->exc_type : Py_None, - tstate->exc_value != NULL ? tstate->exc_value : Py_None, - tstate->exc_traceback != NULL ? - tstate->exc_traceback : Py_None); + exc_info->exc_type != NULL ? exc_info->exc_type : Py_None, + exc_info->exc_value != NULL ? exc_info->exc_value : Py_None, + exc_info->exc_traceback != NULL ? + exc_info->exc_traceback : Py_None); } PyDoc_STRVAR(exc_info_doc,