[pypy-commit] pypy py3k: properly implement POP_EXCEPT as a block, instead of popping the last_exception from the stack directly in the implementation of the opcode. This fixes test_pop_exception_value. Took two days to hunt&fix :-(

antocuni noreply at buildbot.pypy.org
Thu May 10 23:30:41 CEST 2012


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: py3k
Changeset: r55015:6c1827662c80
Date: 2012-05-10 23:30 +0200
http://bitbucket.org/pypy/pypy/changeset/6c1827662c80/

Log:	properly implement POP_EXCEPT as a block, instead of popping the
	last_exception from the stack directly in the implementation of the
	opcode. This fixes test_pop_exception_value. Took two days to
	hunt&fix :-(

diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -529,14 +529,9 @@
 
     def POP_EXCEPT(self, oparg, next_instr):
         assert self.space.py3k
-        # on CPython, POP_EXCEPT also pops the block. Here, the block is
-        # automatically popped by unrollstack()
-        w_last_exception = self.popvalue()
-        if not isinstance(w_last_exception, W_OperationError):
-            msg = "expected an OperationError, got %s" % (
-                self.space.str_w(w_last_exception))
-            raise BytecodeCorruption(msg)
-        self.last_exception = w_last_exception.operr
+        block = self.pop_block()
+        block.cleanup(self)
+        return
 
     def POP_BLOCK(self, oparg, next_instr):
         block = self.pop_block()
@@ -1303,6 +1298,30 @@
             return r_uint(self.handlerposition)
 
 
+class ExceptHandlerBlock(FrameBlock):
+    """
+    This is a special, implicit block type which is created when entering an
+    except handler. It does not belong to any opcode
+    """
+
+    _immutable_ = True
+    _opname = 'EXCEPT_HANDLER_BLOCK' # it's not associated to any opcode
+    handling_mask = 0 # this block is never handled, only popped by POP_EXCEPT
+
+    def handle(self, frame, unroller):
+        assert False # never called
+
+    def cleanup(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.cleanup(self, frame)
+
+
 class ExceptBlock(FrameBlock):
     """An try:except: block.  Stores the position of the exception handler."""
 
@@ -1326,6 +1345,8 @@
             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, 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)
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
@@ -15,6 +15,13 @@
         else:
             raise AssertionError("exception executing else clause!")
 
+    def test_store_exception(self):
+        try:
+            raise ValueError
+        except Exception as e:
+            assert e
+
+
     def test_args(self):
         try:
             raise SystemError(1, 2)


More information about the pypy-commit mailing list