[pypy-commit] pypy py3.5: hg merge py3.5-refactor-sys_exc_info

arigo pypy.commits at gmail.com
Fri Nov 18 09:20:31 EST 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r88466:bdac33b95aeb
Date: 2016-11-18 15:19 +0100
http://bitbucket.org/pypy/pypy/changeset/bdac33b95aeb/

Log:	hg merge py3.5-refactor-sys_exc_info

	In an except: or finally: block, move the current exception to the
	executioncontext, and restore it afterwards, like CPython. This
	might make the exception object escape a bit more, but it allows
	other operations to work without forcing any frame.

diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -622,8 +622,8 @@
 
     ops.PRINT_EXPR: -1,
 
-    ops.WITH_CLEANUP_START: 1,
-    ops.WITH_CLEANUP_FINISH: -2,
+    ops.WITH_CLEANUP_START: 0,
+    ops.WITH_CLEANUP_FINISH: -1,
     ops.LOAD_BUILD_CLASS: 1,
     ops.POP_BLOCK: 0,
     ops.POP_EXCEPT: -1,
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -869,7 +869,7 @@
         with a: pass
         """
         code = compile_with_astcompiler(source, 'exec', self.space)
-        assert code.co_stacksize == 6  # i.e. <= 7, there is no systematic leak
+        assert code.co_stacksize == 5  # i.e. <= 7, there is no systematic leak
 
     def test_stackeffect_bug5(self):
         source = """if 1:
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -333,13 +333,12 @@
             tb = tb.next
         self._application_traceback = tb
 
-    def record_context(self, space, frame):
-        """Record a __context__ for this exception if one exists,
-        searching from the current frame.
+    def record_context(self, space, ec):
+        """Record a __context__ for this exception if one exists.
         """
         if self._context_recorded:
             return
-        last = frame._exc_info_unroll(space)
+        last = ec.sys_exc_info()
         try:
             if last is not None:
                 self.chain_exceptions(space, last)
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -28,6 +28,10 @@
     def __init__(self, space):
         self.space = space
         self.topframeref = jit.vref_None
+        # this is exposed to app-level as 'sys.exc_info()'.  At any point in
+        # time it is the exception caught by the topmost 'except ... as e:'
+        # app-level block.
+        self.sys_exc_operror = None
         self.w_tracefunc = None
         self.is_tracing = 0
         self.compiler = space.createcompiler()
@@ -222,7 +226,6 @@
             self._trace(frame, 'exception', None, operationerr)
         #operationerr.print_detailed_traceback(self.space)
 
-    @jit.dont_look_inside
     def sys_exc_info(self):
         """Implements sys.exc_info().
         Return an OperationError instance or None.
@@ -230,23 +233,10 @@
         # NOTE: the result is not the wrapped sys.exc_info() !!!
 
         """
-        return self.gettopframe()._exc_info_unroll(self.space)
+        return self.sys_exc_operror
 
     def set_sys_exc_info(self, operror):
-        frame = self.gettopframe_nohidden()
-        if frame:     # else, the exception goes nowhere and is lost
-            frame.last_exception = operror
-
-    def clear_sys_exc_info(self):
-        # Find the frame out of which sys_exc_info() would return its result,
-        # and hack this frame's last_exception to become the cleared
-        # OperationError (which is different from None!).
-        frame = self.gettopframe_nohidden()
-        while frame:
-            if frame.last_exception is not None:
-                frame.last_exception = get_cleared_operation_error(self.space)
-                break
-            frame = self.getnextframe_nohidden(frame)
+        self.sys_exc_operror = operror
 
     @jit.dont_look_inside
     def settrace(self, w_func):
@@ -356,7 +346,6 @@
                     event == 'c_exception'):
                 return
 
-            last_exception = frame.last_exception
             if event == 'leaveframe':
                 event = 'return'
 
@@ -372,7 +361,6 @@
                     raise
 
             finally:
-                frame.last_exception = last_exception
                 self.is_tracing -= 1
 
     def checksignals(self):
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -22,6 +22,7 @@
         self._qualname = qualname   # may be null, use get_qualname()
         if self.pycode.co_flags & CO_YIELD_INSIDE_TRY:
             self.register_finalizer(self.space)
+        self.saved_operr = None
 
     def get_name(self):
         # 'name' is a byte string that is valid utf-8
@@ -45,42 +46,6 @@
                            self.get_qualname(),
                            unicode(addrstring)))
 
-    def descr__reduce__(self, space):
-        from pypy.interpreter.mixedmodule import MixedModule
-        w_mod = space.getbuiltinmodule('_pickle_support')
-        mod = space.interp_w(MixedModule, w_mod)
-        new_inst = mod.get(self.KIND + '_new')
-        w = space.wrap
-        if self.frame:
-            w_frame = self.frame._reduce_state(space)
-        else:
-            w_frame = space.w_None
-
-        tup = [w_frame, w(self.running)]
-        return space.newtuple([new_inst, space.newtuple([]),
-                               space.newtuple(tup)])
-
-    def descr__setstate__(self, space, w_args):
-        from rpython.rlib.objectmodel import instantiate
-        args_w = space.unpackiterable(w_args)
-        w_framestate, w_running = args_w
-        if space.is_w(w_framestate, space.w_None):
-            self.frame = None
-            self.space = space
-            self.pycode = None
-            self._name = None
-            self._qualname = None
-        else:
-            frame = instantiate(space.FrameClass)   # XXX fish
-            frame.descr__setstate__(space, w_framestate)
-            if isinstance(self, GeneratorIterator):
-                GeneratorIterator.__init__(self, frame)
-            elif isinstance(self, Coroutine):
-                Coroutine.__init__(self, frame)
-            else:
-                assert False
-        self.running = self.space.is_true(w_running)
-
     def descr_send(self, w_arg):
         """send(arg) -> send 'arg' into generator/coroutine,
 return next yielded value or raise StopIteration."""
@@ -136,6 +101,10 @@
         space = self.space
         if self.running:
             raise oefmt(space.w_ValueError, "%s already executing", self.KIND)
+        ec = space.getexecutioncontext()
+        current_exc_info = ec.sys_exc_info()
+        if self.saved_operr is not None:
+            ec.set_sys_exc_info(self.saved_operr)
         self.running = True
         try:
             w_result = frame.execute_frame(self, w_arg_or_err)
@@ -150,6 +119,8 @@
         finally:
             frame.f_backref = jit.vref_None
             self.running = False
+            self.saved_operr = ec.sys_exc_info()
+            ec.set_sys_exc_info(current_exc_info)
         return w_result
 
     def resume_execute_frame(self, frame, w_arg_or_err):
@@ -162,7 +133,7 @@
             try:
                 self.next_yield_from(frame, w_yf, w_arg_or_err)
             except OperationError as operr:
-                operr.record_context(space, frame)
+                operr.record_context(space, space.getexecutioncontext())
                 return frame.handle_generator_error(operr)
             # Normal case: the call above raises Yield.
             # We reach this point if the iterable is exhausted.
@@ -227,7 +198,7 @@
                                            self.KIND))
             e2.chain_exceptions(space, e)
             e2.set_cause(space, e.get_w_value(space))
-            e2.record_context(space, self.frame)
+            e2.record_context(space, space.getexecutioncontext())
             raise e2
         else:
             space.warn(space.wrap(u"generator '%s' raised StopIteration"
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -21,7 +21,7 @@
 
 # Define some opcodes used
 for op in '''DUP_TOP POP_TOP SETUP_LOOP SETUP_EXCEPT SETUP_FINALLY SETUP_WITH
-POP_BLOCK END_FINALLY'''.split():
+SETUP_ASYNC_WITH POP_BLOCK END_FINALLY'''.split():
     globals()[op] = stdlib_opcode.opmap[op]
 HAVE_ARGUMENT = stdlib_opcode.HAVE_ARGUMENT
 
@@ -66,7 +66,6 @@
     f_generator_wref         = rweakref.dead_ref  # for generators/coroutines
     f_generator_nowref       = None               # (only one of the two attrs)
     last_instr               = -1
-    last_exception           = None
     f_backref                = jit.vref_None
     
     escaped                  = False  # see mark_as_escaped()
@@ -249,8 +248,20 @@
         """Start this frame's execution."""
         if self._is_generator_or_coroutine():
             return self.initialize_as_generator(name, qualname)
+        elif we_are_translated():
+            return self.execute_frame()
         else:
-            return self.execute_frame()
+            # untranslated: check that sys_exc_info is exactly
+            # restored after running any Python function
+            executioncontext = self.space.getexecutioncontext()
+            exc_on_enter = executioncontext.sys_exc_info()
+            try:
+                w_res = self.execute_frame()
+            except OperationError:
+                assert exc_on_enter is executioncontext.sys_exc_info()
+                raise
+            assert exc_on_enter is executioncontext.sys_exc_info()
+            return w_res
     run._always_inline_ = True
 
     def initialize_as_generator(self, name, qualname):
@@ -326,10 +337,6 @@
                 raise self._convert_unexpected_exception(e)
             finally:
                 executioncontext.return_trace(self, w_exitvalue)
-            # it used to say self.last_exception = None
-            # this is now done by the code in pypyjit module
-            # since we don't want to invalidate the virtualizable
-            # for no good reason
             got_exception = False
         finally:
             executioncontext.leave(self, w_exitvalue, got_exception)
@@ -458,128 +465,6 @@
                 self.space, arguments, keywords, keywords_w, w_star,
                 w_starstar, methodcall=methodcall)
 
-    @jit.dont_look_inside
-    def descr__reduce__(self, space):
-        from pypy.interpreter.mixedmodule import MixedModule
-        w_mod    = space.getbuiltinmodule('_pickle_support')
-        mod      = space.interp_w(MixedModule, w_mod)
-        new_inst = mod.get('frame_new')
-        w_tup_state = self._reduce_state(space)
-        nt = space.newtuple
-        return nt([new_inst, nt([]), w_tup_state])
-
-    @jit.dont_look_inside
-    def _reduce_state(self, space):
-        from pypy.module._pickle_support import maker # helper fns
-        w = space.wrap
-        nt = space.newtuple
-
-        if self.get_w_f_trace() is None:
-            f_lineno = self.get_last_lineno()
-        else:
-            f_lineno = self.getorcreatedebug().f_lineno
-
-        nlocals = self.pycode.co_nlocals
-        values_w = self.locals_cells_stack_w
-        w_locals_cells_stack = maker.slp_into_tuple_with_nulls(space, values_w)
-
-        w_blockstack = nt([block._get_state_(space) for block in self.get_blocklist()])
-        if self.last_exception is None:
-            w_exc_value = space.w_None
-            w_tb = space.w_None
-        else:
-            w_exc_value = self.last_exception.get_w_value(space)
-            w_tb = w(self.last_exception.get_traceback())
-
-        d = self.getorcreatedebug()
-        tup_state = [
-            w(self.f_backref()),
-            w(self.get_builtin()),
-            w(self.pycode),
-            w_locals_cells_stack,
-            w_blockstack,
-            w_exc_value, # last_exception
-            w_tb,        #
-            self.get_w_globals(),
-            w(self.last_instr),
-            w(self.frame_finished_execution),
-            w(f_lineno),
-            space.w_None,           #XXX placeholder for f_locals
-
-            #f_restricted requires no additional data!
-            space.w_None,
-
-            w(d.instr_lb),
-            w(d.instr_ub),
-            w(d.instr_prev_plus_one),
-            w(self.valuestackdepth),
-            ]
-        return nt(tup_state)
-
-    @jit.dont_look_inside
-    def descr__setstate__(self, space, w_args):
-        from pypy.module._pickle_support import maker # helper fns
-        from pypy.interpreter.pycode import PyCode
-        from pypy.interpreter.module import Module
-        args_w = space.unpackiterable(w_args, 17)
-        w_f_back, w_builtin, w_pycode, w_locals_cells_stack, w_blockstack, w_exc_value, w_tb,\
-            w_globals, w_last_instr, w_finished, w_f_lineno, w_f_locals, \
-            w_f_trace, w_instr_lb, w_instr_ub, w_instr_prev_plus_one, w_stackdepth = args_w
-
-        new_frame = self
-        pycode = space.interp_w(PyCode, w_pycode)
-
-        values_w = maker.slp_from_tuple_with_nulls(space, w_locals_cells_stack)
-        nfreevars = len(pycode.co_freevars)
-        closure = None
-        if nfreevars:
-            base = pycode.co_nlocals + len(pycode.co_cellvars)
-            closure = values_w[base: base + nfreevars]
-
-        # do not use the instance's __init__ but the base's, because we set
-        # everything like cells from here
-        # XXX hack
-        from pypy.interpreter.function import Function
-        outer_func = Function(space, None, closure=closure,
-                             forcename="fake")
-        PyFrame.__init__(self, space, pycode, w_globals, outer_func)
-        f_back = space.interp_w(PyFrame, w_f_back, can_be_None=True)
-        new_frame.f_backref = jit.non_virtual_ref(f_back)
-
-        if space.config.objspace.honor__builtins__:
-            new_frame.builtin = space.interp_w(Module, w_builtin)
-        else:
-            assert space.interp_w(Module, w_builtin) is space.builtin
-        new_frame.set_blocklist([unpickle_block(space, w_blk)
-                                 for w_blk in space.unpackiterable(w_blockstack)])
-        self.locals_cells_stack_w = values_w[:]
-        valuestackdepth = space.int_w(w_stackdepth)
-        if not self._check_stack_index(valuestackdepth):
-            raise oefmt(space.w_ValueError, "invalid stackdepth")
-        assert valuestackdepth >= 0
-        self.valuestackdepth = valuestackdepth
-        if space.is_w(w_exc_value, space.w_None):
-            new_frame.last_exception = None
-        else:
-            from pypy.interpreter.pytraceback import PyTraceback
-            tb = space.interp_w(PyTraceback, w_tb)
-            new_frame.last_exception = OperationError(space.type(w_exc_value),
-                                                      w_exc_value, tb
-                                                      )
-        new_frame.last_instr = space.int_w(w_last_instr)
-        new_frame.frame_finished_execution = space.is_true(w_finished)
-        d = new_frame.getorcreatedebug()
-        d.f_lineno = space.int_w(w_f_lineno)
-
-        if space.is_w(w_f_trace, space.w_None):
-            d.w_f_trace = None
-        else:
-            d.w_f_trace = w_f_trace
-
-        d.instr_lb = space.int_w(w_instr_lb)   #the three for tracing
-        d.instr_ub = space.int_w(w_instr_ub)
-        d.instr_prev_plus_one = space.int_w(w_instr_prev_plus_one)
-
     def hide(self):
         return self.pycode.hidden_applevel
 
@@ -724,7 +609,7 @@
             return space.wrap(self.getorcreatedebug().f_lineno)
 
     def fset_f_lineno(self, space, w_new_lineno):
-        "Returns the line number of the instruction currently being executed."
+        "Change the line number of the instruction currently being executed."
         try:
             new_lineno = space.int_w(w_new_lineno)
         except OperationError:
@@ -763,48 +648,62 @@
                         "can't jump to 'except' line as there's no exception")
 
         # Don't jump into or out of a finally block.
-        f_lasti_setup_addr = -1
-        new_lasti_setup_addr = -1
-        blockstack = []
+        # Unlike CPython, we can't jump into or out of an except block
+        # either---there would be a mess with SysExcInfoRestorer.
+        f_lasti_handler_addr = -1
+        new_lasti_handler_addr = -1
+        blockstack = [-1]    # list of odd length:
+                             #   * addr of most recent outermost handler
+                             # [ * addr of start of outermost block
+                             #   * addr of most recent handler in that block 
+                             #       (last two items repeated) ]
         addr = 0
         while addr < len(code):
             op = ord(code[addr])
-            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH):
-                blockstack.append([addr, False])
+            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
+                      SETUP_ASYNC_WITH):
+                blockstack.append(addr)
+                blockstack.append(-1)
             elif op == POP_BLOCK:
-                setup_op = ord(code[blockstack[-1][0]])
-                if setup_op == SETUP_FINALLY or setup_op == SETUP_WITH:
-                    blockstack[-1][1] = True
-                else:
-                    blockstack.pop()
-            elif op == END_FINALLY:
-                if len(blockstack) > 0:
-                    setup_op = ord(code[blockstack[-1][0]])
-                    if setup_op == SETUP_FINALLY or setup_op == SETUP_WITH:
-                        blockstack.pop()
+                if len(blockstack) < 3:
+                    raise oefmt(space.w_SystemError,
+                                "blocks not properly nested in this bytecode")
+                blockstack.pop()
+                setup_op = ord(code[blockstack.pop()])
+                if setup_op != SETUP_LOOP:
+                    blockstack[-1] = addr
+            elif op == END_FINALLY:    # "async for" nests blocks
+                blockstack[-1] = -1    # strangely, careful here
 
             if addr == new_lasti or addr == self.last_instr:
-                for ii in range(len(blockstack)):
-                    setup_addr, in_finally = blockstack[~ii]
-                    if in_finally:
-                        if addr == new_lasti:
-                            new_lasti_setup_addr = setup_addr
-                        if addr == self.last_instr:
-                            f_lasti_setup_addr = setup_addr
-                        break
+                ii = len(blockstack) - 1
+                while ii > 0 and blockstack[ii] == -1:
+                    ii -= 2
+                assert ii >= 0
+                handler_addr = blockstack[ii]
+                if addr == new_lasti:
+                    new_lasti_handler_addr = handler_addr
+                if addr == self.last_instr:
+                    f_lasti_handler_addr = handler_addr
 
             if op >= HAVE_ARGUMENT:
                 addr += 3
             else:
                 addr += 1
 
-        assert len(blockstack) == 0
+        if len(blockstack) != 1:
+            raise oefmt(space.w_SystemError,
+                        "blocks not properly nested in this bytecode")
 
-        if new_lasti_setup_addr != f_lasti_setup_addr:
+        if new_lasti_handler_addr != f_lasti_handler_addr:
             raise oefmt(space.w_ValueError,
-                        "can't jump into or out of a 'finally' block %d -> %d",
-                        f_lasti_setup_addr, new_lasti_setup_addr)
+                        "can't jump into or out of an 'expect' or "
+                        "'finally' block (%d -> %d)",
+                        f_lasti_handler_addr, new_lasti_handler_addr)
 
+        # now we know we're not jumping into or out of a place which
+        # needs a SysExcInfoRestorer.  Check that we're not jumping
+        # *into* a block, but only (potentially) out of some blocks.
         if new_lasti < self.last_instr:
             min_addr = new_lasti
             max_addr = self.last_instr
@@ -812,42 +711,40 @@
             min_addr = self.last_instr
             max_addr = new_lasti
 
-        delta_iblock = min_delta_iblock = 0
+        delta_iblock = min_delta_iblock = 0    # see below for comment
         addr = min_addr
         while addr < max_addr:
             op = ord(code[addr])
 
-            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH):
+            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
+                      SETUP_ASYNC_WITH):
                 delta_iblock += 1
             elif op == POP_BLOCK:
                 delta_iblock -= 1
                 if delta_iblock < min_delta_iblock:
                     min_delta_iblock = delta_iblock
 
-            if op >= stdlib_opcode.HAVE_ARGUMENT:
+            if op >= HAVE_ARGUMENT:
                 addr += 3
             else:
                 addr += 1
 
-        f_iblock = 0
-        block = self.lastblock
-        while block:
-            f_iblock += 1
-            block = block.previous
-        min_iblock = f_iblock + min_delta_iblock
+        # 'min_delta_iblock' is <= 0; its absolute value is the number of
+        # blocks we exit.  'go_iblock' is the delta number of blocks
+        # between the last_instr and the new_lasti, in this order.
         if new_lasti > self.last_instr:
-            new_iblock = f_iblock + delta_iblock
+            go_iblock = delta_iblock
         else:
-            new_iblock = f_iblock - delta_iblock
+            go_iblock = -delta_iblock
 
-        if new_iblock > min_iblock:
+        if go_iblock > min_delta_iblock:
             raise oefmt(space.w_ValueError,
                         "can't jump into the middle of a block")
+        assert go_iblock <= 0
 
-        while f_iblock > new_iblock:
+        for ii in range(-go_iblock):
             block = self.pop_block()
-            block.cleanup(self)
-            f_iblock -= 1
+            block.cleanupstack(self)
 
         self.getorcreatedebug().f_lineno = new_lineno
         self.last_instr = new_lasti
@@ -880,55 +777,11 @@
     def fdel_f_trace(self, space):
         self.getorcreatedebug().w_f_trace = None
 
-    def fget_f_exc_type(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return f.last_exception.w_type
-        return space.w_None
-
-    def fget_f_exc_value(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return f.last_exception.get_w_value(space)
-        return space.w_None
-
-    def fget_f_exc_traceback(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return space.wrap(f.last_exception.get_traceback())
-        return space.w_None
-
     def fget_f_restricted(self, space):
         if space.config.objspace.honor__builtins__:
             return space.wrap(self.builtin is not space.builtin)
         return space.w_False
 
-    @jit.unroll_safe
-    @specialize.arg(2)
-    def _exc_info_unroll(self, space, for_hidden=False):
-        """Return the most recent OperationError being handled in the
-        call stack
-        """
-        frame = self
-        while frame:
-            last = frame.last_exception
-            if last is not None:
-                if last is get_cleared_operation_error(self.space):
-                    break
-                if for_hidden or not frame.hide():
-                    return last
-            frame = frame.f_backref()
-        return None
-
     def get_generator(self):
         if self.space.config.translation.rweakref:
             return self.f_generator_wref()
@@ -936,10 +789,9 @@
             return self.f_generator_nowref
 
     def descr_clear(self, space):
+        """F.clear(): clear most references held by the frame"""
         # Clears a random subset of the attributes (e.g. the fast
-        # locals, but not f_locals).  Also clears last_exception, which
-        # is not quite like CPython when it clears f_exc_* (however
-        # there might not be an observable difference).
+        # locals, but not f_locals).
         if not self.frame_finished_execution:
             if not self._is_generator_or_coroutine():
                 raise oefmt(space.w_RuntimeError,
@@ -953,7 +805,6 @@
                 # awaited" in this case too.  Does it make any sense?
                 gen.descr_close()
 
-        self.last_exception = None
         debug = self.getdebug()
         if debug is not None:
             debug.w_f_trace = None
@@ -967,6 +818,7 @@
                 w_newvalue = None
             self.locals_cells_stack_w[i] = w_newvalue
         self.valuestackdepth = 0
+        self.lastblock = None    # the FrameBlock chained list
 
     def _convert_unexpected_exception(self, e):
         from pypy.interpreter import error
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -63,17 +63,14 @@
         try:
             while True:
                 next_instr = self.handle_bytecode(co_code, next_instr, ec)
-        except Return:
-            self.last_exception = None
-            return self.popvalue()
-        except Yield:
+        except ExitFrame:
             return self.popvalue()
 
     def handle_bytecode(self, co_code, next_instr, ec):
         try:
             next_instr = self.dispatch_bytecode(co_code, next_instr, ec)
         except OperationError as operr:
-            operr.record_context(self.space, self)
+            operr.record_context(self.space, ec)
             next_instr = self.handle_operation_error(ec, operr)
         except RaiseWithExplicitTraceback as e:
             next_instr = self.handle_operation_error(ec, e.operr,
@@ -688,12 +685,11 @@
         if nbargs > 2:
             raise BytecodeCorruption("bad RAISE_VARARGS oparg")
         if nbargs == 0:
-            last_operr = self._exc_info_unroll(space, for_hidden=True)
+            last_operr = self.space.getexecutioncontext().sys_exc_info()
             if last_operr is None:
                 raise oefmt(space.w_RuntimeError,
                             "No active exception to reraise")
             # re-raise, no new traceback obj will be attached
-            self.last_exception = last_operr
             raise RaiseWithExplicitTraceback(last_operr)
         if nbargs == 2:
             w_cause = self.popvalue()
@@ -748,12 +744,20 @@
 
     def POP_EXCEPT(self, oparg, next_instr):
         block = self.pop_block()
-        block.cleanup(self)
-        return
+        assert isinstance(block, SysExcInfoRestorer)
+        block.cleanupstack(self)   # restores ec.sys_exc_operror
 
     def POP_BLOCK(self, oparg, next_instr):
         block = self.pop_block()
-        block.cleanup(self)  # the block knows how to clean up the value stack
+        block.pop_block(self)  # the block knows how to clean up the value stack
+
+    def save_and_change_sys_exc_info(self, operationerr):
+        ec = self.space.getexecutioncontext()
+        last_exception = ec.sys_exc_info()
+        block = SysExcInfoRestorer(last_exception, self.lastblock)
+        self.lastblock = block
+        if operationerr is not None:   # otherwise, don't change sys_exc_info
+            ec.set_sys_exc_info(operationerr)
 
     def end_finally(self):
         # unlike CPython, there are two statically distinct cases: the
@@ -763,8 +767,14 @@
         #   [exception value we are now handling]
         #   [wrapped SApplicationException]
         # In the case of a finally: block, the stack contains only one
-        # item (unlike CPython which can have 1, 2 or 3 items):
+        # item (unlike CPython which can have 1, 2, 3 or 5 items, and
+        # even in one case a non-fixed number of items):
         #   [wrapped subclass of SuspendedUnroller]
+
+        block = self.pop_block()
+        assert isinstance(block, SysExcInfoRestorer)
+        block.cleanupstack(self)   # restores ec.sys_exc_operror
+
         w_top = self.popvalue()
         if self.space.is_w(w_top, self.space.w_None):
             # case of a finally: block with no exception
@@ -1148,8 +1158,8 @@
         w_exit = self.space.get(w_descr, w_manager)
         self.settopvalue(w_exit)
         w_result = self.space.get_and_call_function(w_enter, w_manager)
-        block = WithBlock(self.valuestackdepth,
-                          next_instr + offsettoend, self.lastblock)
+        block = FinallyBlock(self.valuestackdepth,
+                             next_instr + offsettoend, self.lastblock)
         self.lastblock = block
         self.pushvalue(w_result)
 
@@ -1161,39 +1171,28 @@
         if isinstance(w_unroller, SApplicationException):
             # app-level exception
             operr = w_unroller.operr
-            old_last_exception = self.last_exception
-            self.last_exception = operr
             w_traceback = self.space.wrap(operr.get_traceback())
-            try:
-                w_res = self.call_contextmanager_exit_function(
-                    w_exitfunc,
-                    operr.w_type,
-                    operr.get_w_value(self.space),
-                    w_traceback)
-            except:
-                self.last_exception = old_last_exception
-                raise
-            # push a marker that also contains the old_last_exception,
-            # which must be restored as 'self.last_exception' but only
-            # in WITH_CLEANUP_FINISH
-            self.pushvalue(SApplicationException(old_last_exception))
+            w_res = self.call_contextmanager_exit_function(
+                w_exitfunc,
+                operr.w_type,
+                operr.get_w_value(self.space),
+                w_traceback)
         else:
             w_res = self.call_contextmanager_exit_function(
                 w_exitfunc,
                 self.space.w_None,
                 self.space.w_None,
                 self.space.w_None)
-            self.pushvalue(self.space.w_None)
         self.pushvalue(w_res)
+        # in the stack now:  [w_res, w_unroller-or-w_None..]
 
     def WITH_CLEANUP_FINISH(self, oparg, next_instr):
         w_suppress = self.popvalue()
-        w_marker = self.popvalue()
-        if isinstance(w_marker, SApplicationException):
-            self.last_exception = w_marker.operr    # may be None
-            if self.space.is_true(w_suppress):
-                # __exit__() returned True -> Swallow the exception.
-                self.settopvalue(self.space.w_None)
+        if self.space.is_true(w_suppress):
+            # __exit__() returned True -> Swallow the exception.
+            self.settopvalue(self.space.w_None)
+        # this is always followed by END_FINALLY
+        # in the stack now: [w_unroller-or-w_None..]
 
     @jit.unroll_safe
     def call_function(self, oparg, w_starstar=None, has_vararg=False):
@@ -1474,8 +1473,8 @@
 
     def SETUP_ASYNC_WITH(self, offsettoend, next_instr):
         res = self.popvalue()
-        block = WithBlock(self.valuestackdepth,
-                          next_instr + offsettoend, self.lastblock)
+        block = FinallyBlock(self.valuestackdepth,
+                             next_instr + offsettoend, self.lastblock)
         self.lastblock = block
         self.pushvalue(res)
 
@@ -1557,12 +1556,15 @@
 
 ### ____________________________________________________________ ###
 
+class ExitFrame(Exception):
+    pass
 
-class Return(Exception):
+
+class Return(ExitFrame):
     """Raised when exiting a frame via a 'return' statement."""
 
 
-class Yield(Exception):
+class Yield(ExitFrame):
     """Raised when exiting a frame via a 'yield' statement."""
 
 
@@ -1654,7 +1656,7 @@
     def cleanupstack(self, frame):
         frame.dropvaluesuntil(self.valuestackdepth)
 
-    def cleanup(self, frame):
+    def pop_block(self, frame):
         "Clean up a frame when we normally exit the block."
         self.cleanupstack(frame)
 
@@ -1691,28 +1693,29 @@
             return r_uint(self.handlerposition)
 
 
-class ExceptHandlerBlock(FrameBlock):
+class SysExcInfoRestorer(FrameBlock):
     """
-    This is a special, implicit block type which is created when entering an
-    except handler. It does not belong to any opcode
+    This is a special, implicit block type which is created when entering a
+    finally or except handler. It does not belong to any opcode
     """
 
     _immutable_ = True
-    _opname = 'EXCEPT_HANDLER_BLOCK' # it's not associated to any opcode
+    _opname = 'SYS_EXC_INFO_RESTORER' # it's not associated to any opcode
     handling_mask = 0 # this block is never handled, only popped by POP_EXCEPT
 
+    def __init__(self, operr, previous):
+        self.operr = operr
+        self.previous = previous
+
+    def pop_block(self, frame):
+        assert False # never called
+
     def handle(self, frame, unroller):
         assert False # never called
 
     def cleanupstack(self, frame):
-        frame.dropvaluesuntil(self.valuestackdepth+1)
-        w_last_exception = frame.popvalue()
-        if not isinstance(w_last_exception, W_OperationError):
-            msg = "expected an OperationError, got %s" % (
-                frame.space.str_w(w_last_exception))
-            raise BytecodeCorruption(msg)
-        frame.last_exception = w_last_exception.operr
-        FrameBlock.cleanupstack(self, frame)
+        ec = frame.space.getexecutioncontext()
+        ec.set_sys_exc_info(self.operr)
 
 
 class ExceptBlock(FrameBlock):
@@ -1726,22 +1729,18 @@
         # push the exception to the value stack for inspection by the
         # exception handler (the code after the except:)
         self.cleanupstack(frame)
+        # the stack setup is slightly different than in CPython:
+        # instead of the traceback, we store the unroller object,
+        # wrapped.
         assert isinstance(unroller, SApplicationException)
         operationerr = unroller.operr
         operationerr.normalize_exception(frame.space)
-        # the stack setup is slightly different than in CPython:
-        # instead of the traceback, we store the unroller object,
-        # wrapped.
-        # this is popped by POP_EXCEPT, which is present only in py3k
-        w_last_exception = W_OperationError(frame.last_exception)
-        w_last_exception = frame.space.wrap(w_last_exception)
-        frame.pushvalue(w_last_exception)
-        block = ExceptHandlerBlock(self.valuestackdepth, 0, frame.lastblock)
-        frame.lastblock = block
         frame.pushvalue(frame.space.wrap(unroller))
         frame.pushvalue(operationerr.get_w_value(frame.space))
         frame.pushvalue(operationerr.w_type)
-        frame.last_exception = operationerr
+        # set the current value of sys_exc_info to operationerr,
+        # saving the old value in a custom type of FrameBlock
+        frame.save_and_change_sys_exc_info(operationerr)
         return r_uint(self.handlerposition)   # jump to the handler
 
 
@@ -1751,7 +1750,6 @@
     _immutable_ = True
     _opname = 'SETUP_FINALLY'
     handling_mask = -1     # handles every kind of SuspendedUnroller
-    restore_last_exception = True # set to False by WithBlock
 
     def handle(self, frame, unroller):
         # any abnormal reason for unrolling a finally: triggers the end of
@@ -1763,53 +1761,48 @@
             operationerr = unroller.operr
             operationerr.normalize_exception(frame.space)
         frame.pushvalue(frame.space.wrap(unroller))
-        if operationerr and self.restore_last_exception:
-            frame.last_exception = operationerr
+        # set the current value of sys_exc_info to operationerr,
+        # saving the old value in a custom type of FrameBlock
+        frame.save_and_change_sys_exc_info(operationerr)
         return r_uint(self.handlerposition)   # jump to the handler
 
+    def pop_block(self, frame):
+        self.cleanupstack(frame)
+        frame.save_and_change_sys_exc_info(None)
 
-class WithBlock(FinallyBlock):
 
-    _immutable_ = True
-    restore_last_exception = False
-
-    def handle(self, frame, unroller):
-        if isinstance(unroller, SApplicationException):
-            unroller.operr.normalize_exception(frame.space)
-        return FinallyBlock.handle(self, frame, unroller)
-
-block_classes = {'EXCEPT_HANDLER_BLOCK': ExceptHandlerBlock,
+block_classes = {'SYS_EXC_INFO_RESTORER': SysExcInfoRestorer,
                  'SETUP_LOOP': LoopBlock,
                  'SETUP_EXCEPT': ExceptBlock,
                  'SETUP_FINALLY': FinallyBlock,
-                 'SETUP_WITH': WithBlock,
+                 'SETUP_WITH': FinallyBlock,
                  }
 
 
-class W_OperationError(W_Root):
-    """
-    Tiny applevel wrapper around an OperationError.
-    """
-
-    def __init__(self, operr):
-        self.operr = operr
-
-    def descr_reduce(self, space):
-        from pypy.interpreter.mixedmodule import MixedModule
-        w_mod = space.getbuiltinmodule('_pickle_support')
-        mod = space.interp_w(MixedModule, w_mod)
-        w_new_inst = mod.get('operationerror_new')
-        w_args = space.newtuple([])
-        operr = self.operr
-        if operr is None:
-            return space.newtuple([w_new_inst, w_args])
-        w_state = space.newtuple([operr.w_type, operr.get_w_value(space),
-                                  operr.get_traceback()])
-        return space.newtuple([w_new_inst, w_args, w_state])
-
-    def descr_setstate(self, space, w_state):
-        w_type, w_value, w_tb = space.fixedview(w_state, 3)
-        self.operr = OperationError(w_type, w_value, w_tb)
+##class W_OperationError(W_Root):
+##    """
+##    Tiny applevel wrapper around an OperationError.
+##    """
+##
+##    def __init__(self, operr):
+##        self.operr = operr
+##
+##    def descr_reduce(self, space):
+##        from pypy.interpreter.mixedmodule import MixedModule
+##        w_mod = space.getbuiltinmodule('_pickle_support')
+##        mod = space.interp_w(MixedModule, w_mod)
+##        w_new_inst = mod.get('operationerror_new')
+##        w_args = space.newtuple([])
+##        operr = self.operr
+##        if operr is None:
+##            return space.newtuple([w_new_inst, w_args])
+##        w_state = space.newtuple([operr.w_type, operr.get_w_value(space),
+##                                  operr.get_traceback()])
+##        return space.newtuple([w_new_inst, w_args, w_state])
+##
+##    def descr_setstate(self, space, w_state):
+##        w_type, w_value, w_tb = space.fixedview(w_state, 3)
+##        self.operr = OperationError(w_type, w_value, w_tb)
 
 def source_as_str(space, w_source, funcname, what, flags):
     """Return source code as str0 with adjusted compiler flags
diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py
--- a/pypy/interpreter/test/test_executioncontext.py
+++ b/pypy/interpreter/test/test_executioncontext.py
@@ -2,9 +2,8 @@
 from pypy.interpreter import executioncontext
 from pypy.interpreter.error import OperationError
 
-class Finished(OperationError):
-    def __init__(self):
-        OperationError.__init__(self, "exception_class", "exception_value")
+class Finished(Exception):
+    pass
 
 
 class TestExecutionContext:
diff --git a/pypy/interpreter/test/test_generator.py b/pypy/interpreter/test/test_generator.py
--- a/pypy/interpreter/test/test_generator.py
+++ b/pypy/interpreter/test/test_generator.py
@@ -457,6 +457,46 @@
         assert closed == [True]
         """
 
+    def test_exc_info_in_generator(self):
+        import sys
+        def g():
+            try:
+                raise ValueError
+            except ValueError:
+                yield sys.exc_info()[0]
+                yield sys.exc_info()[0]
+        try:
+            raise IndexError
+        except IndexError:
+            gen = g()
+            assert sys.exc_info()[0] is IndexError
+            assert next(gen) is ValueError
+            assert sys.exc_info()[0] is IndexError
+            assert next(gen) is ValueError
+            assert sys.exc_info()[0] is IndexError
+            raises(StopIteration, next, gen)
+            assert sys.exc_info()[0] is IndexError
+
+    def test_exc_info_in_generator_2(self):
+        import sys
+        def g():
+            yield sys.exc_info()[0]
+            try:
+                raise LookupError
+            except LookupError:
+                yield sys.exc_info()[0]
+            yield sys.exc_info()[0]
+        try:
+            raise IndexError
+        except IndexError:
+            gen = g()     # the IndexError is not captured at all
+        try:
+            raise ValueError
+        except ValueError:
+            assert next(gen) is ValueError
+            assert next(gen) is LookupError
+            assert next(gen) is ValueError
+
 
 def test_should_not_inline(space):
     from pypy.interpreter.generator import should_not_inline
diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py
--- a/pypy/interpreter/test/test_pyframe.py
+++ b/pypy/interpreter/test/test_pyframe.py
@@ -87,6 +87,40 @@
         sys.settrace(None)
         # assert did not crash
 
+    def test_f_lineno_set_2(self):
+        counter = [0]
+        errors = []
+
+        def tracer(f, event, *args):
+            if event == 'line':
+                counter[0] += 1
+                if counter[0] == 2:
+                    try:
+                        f.f_lineno += 2
+                    except ValueError as e:
+                        errors.append(e)
+            return tracer
+
+        # obscure: call open beforehand, py3k's open invokes some app
+        # level code that confuses our tracing (likely due to the
+        # testing env, otherwise it's not a problem)
+        f = open(self.tempfile1, 'w')
+        def function():
+            try:
+                raise ValueError
+            except ValueError:
+                x = 42
+            return x
+
+        import sys
+        sys.settrace(tracer)
+        x = function()
+        sys.settrace(None)
+        assert x == 42
+        assert len(errors) == 1
+        assert str(errors[0]).startswith(
+            "can't jump into or out of an 'expect' or 'finally' block")
+
     def test_f_lineno_set_firstline(self):
         r"""
         seen = []
@@ -148,30 +182,6 @@
         assert f1bis is f1
         assert f0.f_back is f1
 
-    def test_f_exc_xxx(self):
-        import sys
-
-        class OuterException(Exception):
-            pass
-        class InnerException(Exception):
-            pass
-
-        def g(exc_info):
-            f = sys._getframe()
-            assert f.f_exc_type is None
-            assert f.f_exc_value is None
-            assert f.f_exc_traceback is None
-            try:
-                raise InnerException
-            except:
-                assert f.f_exc_type is exc_info[0]
-                assert f.f_exc_value is exc_info[1]
-                assert f.f_exc_traceback is exc_info[2]
-        try:
-            raise OuterException
-        except:
-            g(sys.exc_info())
-
     def test_virtualref_through_traceback(self):
         import sys
         def g():
diff --git a/pypy/interpreter/test/test_raise.py b/pypy/interpreter/test/test_raise.py
--- a/pypy/interpreter/test/test_raise.py
+++ b/pypy/interpreter/test/test_raise.py
@@ -79,6 +79,25 @@
             assert sys.exc_info()[0] is ValueError
         assert sys.exc_info() == (None, None, None)
 
+    def test_revert_exc_info_2_finally(self):
+        import sys
+        assert sys.exc_info() == (None, None, None)
+        try:
+            try:
+                raise ValueError
+            finally:
+                try:
+                    try:
+                        raise IndexError
+                    finally:
+                        assert sys.exc_info()[0] is IndexError
+                except IndexError:
+                    pass
+                assert sys.exc_info()[0] is ValueError
+        except ValueError:
+            pass
+        assert sys.exc_info() == (None, None, None)
+
     def test_reraise_1(self):
         raises(IndexError, """
             import sys
diff --git a/pypy/interpreter/test/test_zzpickle_and_slow.py b/pypy/interpreter/test/test_zzpickle_and_slow.py
--- a/pypy/interpreter/test/test_zzpickle_and_slow.py
+++ b/pypy/interpreter/test/test_zzpickle_and_slow.py
@@ -351,6 +351,7 @@
         assert list(result) == [2,3,4]
 
     def test_pickle_generator(self):
+        skip("not supported any more for now")
         self.skip_on_cpython()
         import types
         mod = types.ModuleType('mod')
@@ -375,6 +376,7 @@
             del sys.modules['mod']
 
     def test_pickle_generator_blk(self):
+        skip("not supported any more for now")
         self.skip_on_cpython()
         # same as above but with the generator inside a block
         import types
@@ -448,6 +450,7 @@
 
 
     def test_pickle_generator_crash(self):
+        skip("not supported any more for now")
         self.skip_on_cpython()
         import pickle
 
@@ -464,7 +467,7 @@
         assert 'finished' in repr(y)
         assert y.gi_code is None
 
-class AppTestGeneratorCloning:
+class XAppTestGeneratorCloning:
 
     def setup_class(cls):
         try:
@@ -528,7 +531,7 @@
             raises(StopIteration, next, g3)
             raises(StopIteration, next, g4)
 
-class AppTestFramePickling(object):
+class XAppTestFramePickling(object):
     pytestmark = py.test.mark.skipif("config.option.runappdirect")
     spaceconfig = {
         "usemodules": ["struct"]
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -464,7 +464,7 @@
 from pypy.interpreter.eval import Code
 from pypy.interpreter.pycode import PyCode, CO_VARARGS, CO_VARKEYWORDS
 from pypy.interpreter.pyframe import PyFrame
-from pypy.interpreter.pyopcode import SuspendedUnroller, W_OperationError
+from pypy.interpreter.pyopcode import SuspendedUnroller
 from pypy.interpreter.module import Module
 from pypy.interpreter.function import (Function, Method, StaticMethod,
     ClassMethod, BuiltinFunction, descr_function_get)
@@ -607,8 +607,8 @@
 PyCode.typedef.acceptable_as_base_class = False
 
 PyFrame.typedef = TypeDef('frame',
-    __reduce__ = interp2app(PyFrame.descr__reduce__),
-    __setstate__ = interp2app(PyFrame.descr__setstate__),
+    #__reduce__ = interp2app(PyFrame.descr__reduce__),  --- logic not updated
+    #__setstate__ = interp2app(PyFrame.descr__setstate__),
     clear = interp2app(PyFrame.descr_clear),
     f_builtins = GetSetProperty(PyFrame.fget_f_builtins),
     f_lineno = GetSetProperty(PyFrame.fget_f_lineno, PyFrame.fset_f_lineno),
@@ -616,9 +616,6 @@
     f_lasti = GetSetProperty(PyFrame.fget_f_lasti),
     f_trace = GetSetProperty(PyFrame.fget_f_trace, PyFrame.fset_f_trace,
                              PyFrame.fdel_f_trace),
-    f_exc_type = GetSetProperty(PyFrame.fget_f_exc_type),
-    f_exc_value = GetSetProperty(PyFrame.fget_f_exc_value),
-    f_exc_traceback = GetSetProperty(PyFrame.fget_f_exc_traceback),
     f_restricted = GetSetProperty(PyFrame.fget_f_restricted),
     f_code = GetSetProperty(PyFrame.fget_code),
     f_locals = GetSetProperty(PyFrame.fget_getdictscope),
@@ -787,8 +784,8 @@
 
 GeneratorIterator.typedef = TypeDef("generator",
     __repr__   = interp2app(GeneratorIterator.descr__repr__),
-    __reduce__   = interp2app(GeneratorIterator.descr__reduce__),
-    __setstate__ = interp2app(GeneratorIterator.descr__setstate__),
+    #__reduce__   = interp2app(GeneratorIterator.descr__reduce__),
+    #__setstate__ = interp2app(GeneratorIterator.descr__setstate__),
     __next__   = interp2app(GeneratorIterator.descr_next,
                             descrmismatch='__next__'),
     send       = interp2app(GeneratorIterator.descr_send,
@@ -813,8 +810,8 @@
 
 Coroutine.typedef = TypeDef("coroutine",
     __repr__   = interp2app(Coroutine.descr__repr__),
-    __reduce__   = interp2app(Coroutine.descr__reduce__),
-    __setstate__ = interp2app(Coroutine.descr__setstate__),
+    #__reduce__   = interp2app(Coroutine.descr__reduce__),
+    #__setstate__ = interp2app(Coroutine.descr__setstate__),
     send       = interp2app(Coroutine.descr_send,
                             descrmismatch='send'),
     throw      = interp2app(Coroutine.descr_throw,
@@ -878,8 +875,8 @@
 SuspendedUnroller.typedef = TypeDef("SuspendedUnroller")
 SuspendedUnroller.typedef.acceptable_as_base_class = False
 
-W_OperationError.typedef = TypeDef("OperationError",
-    __reduce__ = interp2app(W_OperationError.descr_reduce),
-    __setstate__ = interp2app(W_OperationError.descr_setstate),
-)
-W_OperationError.typedef.acceptable_as_base_class = False
+## W_OperationError.typedef = TypeDef("OperationError",
+##     __reduce__ = interp2app(W_OperationError.descr_reduce),
+##     __setstate__ = interp2app(W_OperationError.descr_setstate),
+## )
+## W_OperationError.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_pickle_support/__init__.py b/pypy/module/_pickle_support/__init__.py
--- a/pypy/module/_pickle_support/__init__.py
+++ b/pypy/module/_pickle_support/__init__.py
@@ -22,5 +22,4 @@
         'intrangeiter_new': 'maker.intrangeiter_new',
         'builtin_code': 'maker.builtin_code',
         'builtin_function' : 'maker.builtin_function',
-        'operationerror_new': 'maker.operationerror_new',
     }
diff --git a/pypy/module/_pickle_support/maker.py b/pypy/module/_pickle_support/maker.py
--- a/pypy/module/_pickle_support/maker.py
+++ b/pypy/module/_pickle_support/maker.py
@@ -69,10 +69,6 @@
     new_iter = W_IntRangeIterator(space, current, remaining, step)
     return space.wrap(new_iter)
 
-def operationerror_new(space):
-    from pypy.interpreter.pyopcode import W_OperationError
-    return W_OperationError(None)
-
 @unwrap_spec(identifier=str)
 def builtin_code(space, identifier):
     from pypy.interpreter import gateway
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -26,7 +26,6 @@
                            'valuestackdepth',
                            'locals_cells_stack_w[*]',
                            'debugdata',
-                           'last_exception',
                            'lastblock',
                            'w_globals',
                            ]
@@ -98,7 +97,6 @@
             jit.hint(self, force_virtualizable=True)
             return w_result
         except Return:
-            self.last_exception = None
             return self.popvalue()
 
     def jump_absolute(self, jumpto, ec):
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -107,8 +107,7 @@
         return space.newtuple([operror.w_type, operror.get_w_value(space),
                                space.wrap(operror.get_traceback())])
 
-def exc_info_without_tb(space, frame):
-    operror = frame.last_exception
+def exc_info_without_tb(space, operror):
     return space.newtuple([operror.w_type, operror.get_w_value(space),
                            space.w_None])
 
@@ -152,10 +151,11 @@
                         if space.int_w(w_constant) <= 2:
                             need_all_three_args = False
     #
-    if need_all_three_args or frame.last_exception is None or frame.hide():
+    operror = space.getexecutioncontext().sys_exc_info()
+    if need_all_three_args or operror is None or frame.hide():
         return exc_info_with_tb(space)
     else:
-        return exc_info_without_tb(space, frame)
+        return exc_info_without_tb(space, operror)
 
 def exc_clear(space):
     """Clear global information on the current exception.  Subsequent calls
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -210,26 +210,20 @@
                                space.newtuple([w_BuiltinAssertionError]),
                                w_dict)
 
-def _exc_info(space, err):
-    """Hack the fact that exc_info() isn't set until a app except
-    block catches it."""
-    err.normalize_exception(space)
-    frame = space.getexecutioncontext().gettopframe()
-    old = frame.last_exception
-    frame.last_exception = err
-    if not hasattr(space, '_w_ExceptionInfo'):
-        space._w_ExceptionInfo = space.appexec([], """():
-    class _ExceptionInfo(object):
-        def __init__(self):
-            import sys
-            self.type, self.value, self.traceback = sys.exc_info()
-
-    return _ExceptionInfo
-""")
-    try:
-        return space.call_function(space._w_ExceptionInfo)
-    finally:
-        frame.last_exception = old
+def _exc_info(space, operror):
+    """sys.exc_info() isn't set until a app except block catches it,
+    but we can directly copy the two lines of code from module/sys/vm.py."""
+    operror.normalize_exception(space)
+    return space.appexec([operror.w_type, operror.get_w_value(space),
+                          space.wrap(operror.get_traceback())], """(t, v, tb):
+        class _ExceptionInfo:
+            pass
+        e = _ExceptionInfo()
+        e.type = t
+        e.value = v
+        e.traceback = tb
+        return e
+    """)
 
 def pypyraises(space, w_ExpectedException, w_expr, __args__):
     """A built-in function providing the equivalent of py.test.raises()."""
diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py
--- a/pypy/tool/pytest/apptest.py
+++ b/pypy/tool/pytest/apptest.py
@@ -172,7 +172,8 @@
         f.write('\n'.join(defs))
         f.write('def %s():\n' % target_name)
         f.write('\n'.join(source))
-        f.write("\n%s()\n" % target_name)
+        f.write("\ntry:\n    %s()\n" % target_name)
+        f.write('finally:\n    print("===aefwuiheawiu===")')
     helper_dir = os.path.join(pypydir, 'tool', 'cpyext')
     env = os.environ.copy()
     env['PYTHONPATH'] = helper_dir
@@ -186,6 +187,8 @@
                      (python_, usemodules))
     elif res > 0:
         raise AssertionError("Subprocess failed:\n" + stderr)
+    elif "===aefwuiheawiu===" not in stdout:
+        raise AssertionError("%r crashed:\n%s" % (python_, stderr))
 
 
 def extract_docstring_if_empty_function(fn):
@@ -209,6 +212,7 @@
 
     def execute_appex(self, space, target, *args):
         self.space = space
+        space.getexecutioncontext().set_sys_exc_info(None)
         try:
             target(*args)
         except OperationError as e:


More information about the pypy-commit mailing list