[pypy-svn] r7523 - in pypy/trunk/src/pypy: interpreter objspace/flow objspace/flow/test translator

arigo at codespeak.net arigo at codespeak.net
Sat Nov 20 18:30:03 CET 2004


Author: arigo
Date: Sat Nov 20 18:30:02 2004
New Revision: 7523

Modified:
   pypy/trunk/src/pypy/interpreter/pyframe.py
   pypy/trunk/src/pypy/objspace/flow/flowcontext.py
   pypy/trunk/src/pypy/objspace/flow/framestate.py
   pypy/trunk/src/pypy/objspace/flow/specialcase.py
   pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py
   pypy/trunk/src/pypy/translator/genc.h
Log:
Better support for exceptions: at least, the flow objspace can make sense out
of 'raise something' constructs.



Modified: pypy/trunk/src/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/pyframe.py	(original)
+++ pypy/trunk/src/pypy/interpreter/pyframe.py	Sat Nov 20 18:30:02 2004
@@ -266,6 +266,13 @@
         # could occur e.g. when a BREAK_LOOP is not actually within a loop
         raise BytecodeCorruption, "block stack exhausted"
 
+    # for the flow object space, a way to "pickle" and "unpickle" the
+    # ControlFlowException by enumerating the Variables it contains.
+    def state_unpack_variables(self, space):
+        return []     # by default, overridden below
+    def state_pack_variables(self, space, *values_w):
+        assert len(values_w) == 0
+
 class SApplicationException(ControlFlowException):
     """Unroll the stack because of an application-level exception
     (i.e. an OperationException)."""
@@ -286,6 +293,13 @@
             operationerr = self.args[0]
             raise operationerr
 
+    def state_unpack_variables(self, space):
+        e = self.args[0]
+        assert isinstance(e, OperationError)
+        return [e.w_type, e.w_value]
+    def state_pack_variables(self, space, w_type, w_value):
+        self.args = (OperationError(w_type, w_value),)
+
 class SBreakLoop(ControlFlowException):
     """Signals a 'break' statement."""
 
@@ -293,6 +307,12 @@
     """Signals a 'continue' statement.
     Argument is the bytecode position of the beginning of the loop."""
 
+    def state_unpack_variables(self, space):
+        jump_to = self.args[0]
+        return [space.wrap(jump_to)]
+    def state_pack_variables(self, space, w_jump_to):
+        self.args = (space.unwrap(w_jump_to),)
+
 class SReturnValue(ControlFlowException):
     """Signals a 'return' statement.
     Argument is the wrapped object to return."""
@@ -300,6 +320,12 @@
         w_returnvalue = self.args[0]
         raise ExitFrame(w_returnvalue)
 
+    def state_unpack_variables(self, space):
+        w_returnvalue = self.args[0]
+        return [w_returnvalue]
+    def state_pack_variables(self, space, w_returnvalue):
+        self.args = (w_returnvalue,)
+
 class ExitFrame(Exception):
     """Signals the end of the frame execution.
     The argument is the returned or yielded value, already wrapped."""

Modified: pypy/trunk/src/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/flowcontext.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/flowcontext.py	Sat Nov 20 18:30:02 2004
@@ -179,6 +179,7 @@
                               cases = [None] + list(classes))
 
     def build_flow(self):
+        from pypy.objspace.flow.objspace import UnwrapException
         while self.pendingblocks:
             block = self.pendingblocks.pop(0)
             frame = self.create_frame()
@@ -189,7 +190,10 @@
             try:
                 w_result = frame.eval(self)
             except OperationError, e:
-                exc_type = self.space.unwrap(e.w_type)
+                try:
+                    exc_type = self.space.unwrap(e.w_type)
+                except UnwrapException:
+                    exc_type = unknown_exception
                 link = Link([e.w_value], self.graph.getexceptblock(exc_type))
                 self.crnt_block.closeblock(link)
             else:
@@ -209,3 +213,7 @@
                     mapping[a] = Variable()
                 node.renamevariables(mapping)
         traverse(fixegg, self.graph)
+
+
+class unknown_exception(object):    # not meant to be raised!
+    pass

Modified: pypy/trunk/src/pypy/objspace/flow/framestate.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/framestate.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/framestate.py	Sat Nov 20 18:30:02 2004
@@ -6,7 +6,9 @@
 
     def __init__(self, state):
         if isinstance(state, PyFrame):
-            self.mergeable = state.getfastscope() + state.valuestack.items
+            data = state.getfastscope() + state.valuestack.items
+            recursively_flatten(state.space, data)
+            self.mergeable = data
             self.nonmergeable = (
                 state.blockstack.items[:],
                 state.last_exception,
@@ -25,8 +27,10 @@
     def restoreframe(self, frame):
         if isinstance(frame, PyFrame):
             fastlocals = len(frame.fastlocals_w)
-            frame.setfastscope(self.mergeable[:fastlocals])
-            frame.valuestack.items[:] = self.mergeable[fastlocals:]
+            data = self.mergeable[:]
+            recursively_unflatten(frame.space, data)
+            frame.setfastscope(data[:fastlocals])
+            frame.valuestack.items[:] = data[fastlocals:]
             (
                 frame.blockstack.items[:],
                 frame.last_exception,
@@ -103,11 +107,56 @@
         # This is needed for try:except: and try:finally:, though
         # it makes the control flow a bit larger by duplicating the
         # handlers.
-        dont_merge_w1 = isinstance(w1.value, ControlFlowException)
-        dont_merge_w2 = isinstance(w2.value, ControlFlowException)
+        dont_merge_w1 = w1 in UNPICKLE_TAGS
+        dont_merge_w2 = w2 in UNPICKLE_TAGS
         if dont_merge_w1 or dont_merge_w2:
             raise UnionError
         else:
             return Variable()  # generalize different constants
     raise TypeError('union of %r and %r' % (w1.__class__.__name__,
                                             w2.__class__.__name__))
+
+# ____________________________________________________________
+#
+# We have to flatten out the state of the frame into a list of
+# Variables and Constants.  This is done above by collecting the
+# locals and the items on the value stack, but the latter may contain
+# ControlFlowExceptions.  We have to handle these specially, because
+# some of them hide references to more Variables and Constants.
+# The trick is to flatten ("pickle") them into the list so that the
+# extra Variables show up directly in the list too.
+
+class PickleTag:
+    pass
+
+PICKLE_TAGS = {}
+UNPICKLE_TAGS = {}
+
+def recursively_flatten(space, lst):
+    i = 0
+    while i < len(lst):
+        item = lst[i]
+        if not (isinstance(item, Constant) and
+                isinstance(item.value, ControlFlowException)):
+            i += 1
+        else:
+            unroller = item.value
+            vars = unroller.state_unpack_variables(space)
+            key = unroller.__class__, len(vars)
+            try:
+                tag = PICKLE_TAGS[key]
+            except:
+                tag = PICKLE_TAGS[key] = Constant(PickleTag())
+                UNPICKLE_TAGS[tag] = key
+            lst[i:i+1] = [tag] + vars
+
+def recursively_unflatten(space, lst):
+    for i in range(len(lst)-1, -1, -1):
+        item = lst[i]
+        if item in UNPICKLE_TAGS:
+            unrollerclass, argcount = UNPICKLE_TAGS[item]
+            arguments = lst[i+1: i+1+argcount]
+            del lst[i+1: i+1+argcount]
+            unroller = unrollerclass()
+            unroller.state_pack_variables(space, *arguments)
+            lst[i] = Constant(unroller)

Modified: pypy/trunk/src/pypy/objspace/flow/specialcase.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/specialcase.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/specialcase.py	Sat Nov 20 18:30:02 2004
@@ -17,29 +17,46 @@
 
 
 def sc_normalize_exception(space, fn, args):
-    """Special-case for 'raise' statements.
+    """Special-case for 'raise' statements.  Case-by-case analysis:
 
-    Only accept the following syntaxes:
     * raise Class
-    * raise Class, Arg
+       - with a constant Class, it is easy to recognize.
+         The associated value is Class().
+
     * raise Class(...)
+       - when the class is instantiated in-place, we can figure that out
+
+    * raise Instance
+       - assumes that it's not a class, and raises an exception whose class
+         is variable and whose value is Instance.
+
+    * raise Class, Arg
+       - assumes that Arg is the value you want for the exception, and
+         that Class is exactly the exception class.  No check or normalization.
     """
     assert len(args.args_w) == 2 and args.kwds_w == {}
     w_arg1, w_arg2 = args.args_w
+    if w_arg2 != space.w_None:
+        # raise Class, Arg: no normalization
+        return (w_arg1, w_arg2)
     etype = getconstclass(space, w_arg1)
     if etype is not None:
-        # raise Class or raise Class, Arg: no normalization
+        # raise Class
+        w_arg2 = space.do_operation('simple_call', w_arg1)
         return (w_arg1, w_arg2)
-    else:
-        # raise Instance: we need a hack to figure out of which class it is.
-        # Normally, Instance should have been created by the previous operation
-        # which should be a simple_call(<Class>, ...).
-        # Fetch the <Class> out of there.  (This doesn't work while replaying)
+    # raise Class(..)?  We need a hack to figure out of which class it is.
+    # Normally, Instance should have been created by the previous operation
+    # which should be a simple_call(<Class>, ...).
+    # Fetch the <Class> out of there.  (This doesn't work while replaying)
+    if space.executioncontext.crnt_ops:
         spaceop = space.executioncontext.crnt_ops[-1]
-        assert spaceop.opname == 'simple_call'
-        assert spaceop.result is w_arg1
-        w_type = spaceop.args[0]
-        return (w_type, w_arg2)
+        if (spaceop.opname == 'simple_call' and
+            spaceop.result is w_arg1):
+            w_type = spaceop.args[0]
+            return (w_type, w_arg1)
+    # raise Instance.  Fall-back.
+    w_type = space.do_operation('type', w_arg1)
+    return (w_type, w_arg1)
     # this function returns a real tuple that can be handled
     # by FlowObjSpace.unpacktuple()
 

Modified: pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py	Sat Nov 20 18:30:02 2004
@@ -1,5 +1,6 @@
 import autopath
 from pypy.tool import testit
+from pypy.objspace.flow.model import Constant
 
 
 class TestFlowObjSpace(testit.TestCase):
@@ -18,10 +19,11 @@
         return graph
 
     def reallyshow(self, x):
-        import os
-        from pypy.translator.tool.make_dot import make_dot
-        dest = make_dot(x.name, x)
-        os.system('gv %s' % str(dest))
+        x.show()
+        #import os
+        #from pypy.translator.tool.make_dot import make_dot
+        #dest = make_dot(x.name, x)
+        #os.system('gv %s' % str(dest))
 
     def show(self, x):
         pass   # or   self.reallyshow(x)
@@ -228,6 +230,11 @@
     
     def test_raise1(self):
         x = self.codetest(self.raise1)
+        assert len(x.startblock.operations) == 1
+        assert x.startblock.operations[0].opname == 'simple_call'
+        assert list(x.startblock.operations[0].args) == [Constant(IndexError)]
+        assert x.startblock.operations[0].result is x.startblock.exits[0].args[0]
+        assert x.startblock.exits[0].target is x.exceptblocks[IndexError]
         self.show(x)
 
     #__________________________________________________________
@@ -236,6 +243,8 @@
     
     def test_raise2(self):
         x = self.codetest(self.raise2)
+        assert len(x.startblock.operations) == 0
+        assert x.startblock.exits[0].target is x.exceptblocks[IndexError]
         self.show(x)
 
     #__________________________________________________________
@@ -244,6 +253,32 @@
     
     def test_raise3(self):
         x = self.codetest(self.raise3)
+        assert len(x.startblock.operations) == 1
+        assert x.startblock.operations[0].opname == 'simple_call'
+        assert list(x.startblock.operations[0].args) == [
+            Constant(IndexError), x.startblock.inputargs[0]]
+        assert x.startblock.operations[0].result is x.startblock.exits[0].args[0]
+        assert x.startblock.exits[0].target is x.exceptblocks[IndexError]
+        self.show(x)
+
+    #__________________________________________________________
+    def raise4(stuff):
+        raise stuff
+    
+    def test_raise4(self):
+        x = self.codetest(self.raise4)
+        self.show(x)
+
+    #__________________________________________________________
+    def raise_and_catch_1(exception_instance):
+        try:
+            raise exception_instance
+        except IndexError:
+            return -1
+        return 0
+    
+    def test_raise_and_catch_1(self):
+        x = self.codetest(self.raise_and_catch_1)
         self.show(x)
 
     #__________________________________________________________

Modified: pypy/trunk/src/pypy/translator/genc.h
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.h	(original)
+++ pypy/trunk/src/pypy/translator/genc.h	Sat Nov 20 18:30:02 2004
@@ -113,6 +113,11 @@
 #define OP_SIMPLE_CALL(args,r,err) if (!(r=PyObject_CallFunctionObjArgs args)) \
 					FAIL(err)
 
+#define OP_TYPE()   /* to whoever needs to implement this: if 'x' is an
+                       old-style exception instance, then OP_TYPE(x)
+                       should really return its (old-style) class */
+#define OP_ISSUBTYPE()  /* same comments */
+
 /*** tests ***/
 
 #define EQ_False(o)     (o == Py_False)



More information about the Pypy-commit mailing list