[pypy-commit] pypy default: Merged in rlamy/pypy/translation-cleanup (pull request #88)

fijal noreply at buildbot.pypy.org
Thu Sep 20 19:39:24 CEST 2012


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: 
Changeset: r57438:44180f5b2058
Date: 2012-09-20 19:38 +0200
http://bitbucket.org/pypy/pypy/changeset/44180f5b2058/

Log:	Merged in rlamy/pypy/translation-cleanup (pull request #88)

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -22,8 +22,7 @@
 
     def __init__(self, w_type, w_value, tb=None):
         if not we_are_translated() and w_type is None:
-            from pypy.tool.error import FlowingError
-            raise FlowingError(w_value)
+            raise ValueError
         self.setup(w_type)
         self._w_value = w_value
         self._application_traceback = tb
@@ -328,8 +327,7 @@
                 for i, attr in entries:
                     setattr(self, attr, args[i])
                 if not we_are_translated() and w_type is None:
-                    from pypy.tool.error import FlowingError
-                    raise FlowingError(self._compute_value())
+                    raise ValueError
             def _compute_value(self):
                 lst = [None] * (len(formats) + len(formats) + 1)
                 for i, attr in entries:
@@ -393,7 +391,7 @@
         return OperationError(exc, w_error)
 
 def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
-                  w_exception_class=None): 
+                  w_exception_class=None):
     assert isinstance(e, OSError)
 
     if _WINDOWS and isinstance(e, WindowsError):
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -153,17 +153,13 @@
             self._trace(frame, 'exception', None, operationerr)
         #operationerr.print_detailed_traceback(self.space)
 
-    def _convert_exc(self, operr):
-        # Only for the flow object space
-        return operr
-
     def sys_exc_info(self): # attn: the result is not the wrapped sys.exc_info() !!!
         """Implements sys.exc_info().
         Return an OperationError instance or None."""
         frame = self.gettopframe_nohidden()
         while frame:
             if frame.last_exception is not None:
-                return self._convert_exc(frame.last_exception)
+                return frame.last_exception
             frame = self.getnextframe_nohidden(frame)
         return None
 
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -90,10 +90,6 @@
             next_instr = self.dispatch_bytecode(co_code, next_instr, ec)
         except OperationError, operr:
             next_instr = self.handle_operation_error(ec, operr)
-        except Reraise:
-            operr = self.last_exception
-            next_instr = self.handle_operation_error(ec, operr,
-                                                     attach_tb=False)
         except RaiseWithExplicitTraceback, e:
             next_instr = self.handle_operation_error(ec, e.operr,
                                                      attach_tb=False)
@@ -542,7 +538,7 @@
             ec = self.space.getexecutioncontext()
             while frame:
                 if frame.last_exception is not None:
-                    operror = ec._convert_exc(frame.last_exception)
+                    operror = frame.last_exception
                     break
                 frame = frame.f_backref()
             else:
@@ -550,7 +546,7 @@
                     space.wrap("raise: no active exception to re-raise"))
             # re-raise, no new traceback obj will be attached
             self.last_exception = operror
-            raise Reraise
+            raise RaiseWithExplicitTraceback(operror)
 
         w_value = w_traceback = space.w_None
         if nbargs >= 3:
@@ -1168,10 +1164,8 @@
 class Yield(ExitFrame):
     """Raised when exiting a frame via a 'yield' statement."""
 
-class Reraise(Exception):
-    """Raised at interp-level by a bare 'raise' statement."""
 class RaiseWithExplicitTraceback(Exception):
-    """Raised at interp-level by a 3-arguments 'raise' statement."""
+    """Raised at interp-level by a 0- or 3-arguments 'raise' statement."""
     def __init__(self, operr):
         self.operr = operr
 
@@ -1228,12 +1222,6 @@
     def nomoreblocks(self):
         raise RaiseWithExplicitTraceback(self.operr)
 
-    def state_unpack_variables(self, space):
-        return [self.operr.w_type, self.operr.get_w_value(space)]
-    def state_pack_variables(space, w_type, w_value):
-        return SApplicationException(OperationError(w_type, w_value))
-    state_pack_variables = staticmethod(state_pack_variables)
-
 class SBreakLoop(SuspendedUnroller):
     """Signals a 'break' statement."""
     _immutable_ = True
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -1,7 +1,6 @@
 import collections
 import sys
-from pypy.tool.error import FlowingError
-from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.tool.error import source_lines
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.pytraceback import PyTraceback
 from pypy.interpreter import pyframe
@@ -9,22 +8,83 @@
 from pypy.interpreter.pycode import CO_OPTIMIZED, CO_NEWLOCALS
 from pypy.interpreter.argument import ArgumentsForTranslation
 from pypy.interpreter.pyopcode import (Return, Yield, SuspendedUnroller,
-        SReturnValue, SApplicationException, BytecodeCorruption, Reraise,
+        SReturnValue, SApplicationException, BytecodeCorruption,
         RaiseWithExplicitTraceback)
-from pypy.objspace.flow.operation import (ImplicitOperationError,
-        OperationThatShouldNotBePropagatedError)
 from pypy.objspace.flow.model import *
 from pypy.objspace.flow.framestate import (FrameState, recursively_unflatten,
         recursively_flatten)
 from pypy.objspace.flow.bytecode import HostCode
 
+class FlowingError(Exception):
+    """ Signals invalid RPython in the function being analysed"""
+    def __init__(self, frame, msg):
+        super(FlowingError, self).__init__(msg)
+        self.frame = frame
+
+    def __str__(self):
+        msg = ['-+' * 30]
+        msg += map(str, self.args)
+        msg += source_lines(self.frame.graph, None, offset=self.frame.last_instr)
+        return "\n".join(msg)
+
+
 class StopFlowing(Exception):
     pass
 
-class MergeBlock(Exception):
-    def __init__(self, block, currentstate):
-        self.block = block
-        self.currentstate = currentstate
+class FSException(OperationError):
+    def __init__(self, w_type, w_value, tb=None):
+        assert w_type is not None
+        self.w_type = w_type
+        self.w_value = w_value
+        self._application_traceback = tb
+
+    def get_w_value(self, _):
+        return self.w_value
+
+    def __str__(self):
+        return '[%s: %s]' % (self.w_type, self.w_value)
+
+    def normalize_exception(self, space):
+        """Normalize the OperationError.  In other words, fix w_type and/or
+        w_value to make sure that the __class__ of w_value is exactly w_type.
+        """
+        w_type  = self.w_type
+        w_value = self.w_value
+        if space.exception_is_valid_obj_as_class_w(w_type):
+            # this is for all cases of the form (Class, something)
+            if space.is_w(w_value, space.w_None):
+                # raise Type: we assume we have to instantiate Type
+                w_value = space.call_function(w_type)
+                w_type = self._exception_getclass(space, w_value)
+            else:
+                w_valuetype = space.exception_getclass(w_value)
+                if space.exception_issubclass_w(w_valuetype, w_type):
+                    # raise Type, Instance: let etype be the exact type of value
+                    w_type = w_valuetype
+                else:
+                    # raise Type, X: assume X is the constructor argument
+                    w_value = space.call_function(w_type, w_value)
+                    w_type = self._exception_getclass(space, w_value)
+
+        else:
+            # the only case left here is (inst, None), from a 'raise inst'.
+            w_inst = w_type
+            w_instclass = self._exception_getclass(space, w_inst)
+            if not space.is_w(w_value, space.w_None):
+                raise FSException(space.w_TypeError,
+                                     space.wrap("instance exception may not "
+                                                "have a separate value"))
+            w_value = w_inst
+            w_type = w_instclass
+
+        self.w_type = w_type
+        self.w_value = w_value
+
+class OperationThatShouldNotBePropagatedError(FSException):
+    pass
+
+class ImplicitOperationError(FSException):
+    pass
 
 class SpamBlock(Block):
     # make slots optional, for debugging
@@ -49,6 +109,31 @@
     def extravars(self, last_exception=None, last_exc_value=None):
         self.last_exception = last_exception
 
+def fixeggblocks(graph):
+    # EggBlocks reuse the variables of their previous block,
+    # which is deemed not acceptable for simplicity of the operations
+    # that will be performed later on the flow graph.
+    for link in list(graph.iterlinks()):
+            block = link.target
+            if isinstance(block, EggBlock):
+                if (not block.operations and len(block.exits) == 1 and
+                    link.args == block.inputargs):   # not renamed
+                    # if the variables are not renamed across this link
+                    # (common case for EggBlocks) then it's easy enough to
+                    # get rid of the empty EggBlock.
+                    link2 = block.exits[0]
+                    link.args = list(link2.args)
+                    link.target = link2.target
+                    assert link2.exitcase is None
+                else:
+                    mapping = {}
+                    for a in block.inputargs:
+                        mapping[a] = Variable(a)
+                    block.renamevariables(mapping)
+    for block in graph.iterblocks():
+        if isinstance(link, SpamBlock):
+            del link.framestate     # memory saver
+
 # ____________________________________________________________
 
 class Recorder:
@@ -59,7 +144,7 @@
     def bytecode_trace(self, frame):
         pass
 
-    def guessbool(self, ec, w_condition, **kwds):
+    def guessbool(self, frame, w_condition, **kwds):
         raise AssertionError, "cannot guessbool(%s)" % (w_condition,)
 
 
@@ -73,9 +158,6 @@
         self.enterspamblock = isinstance(block, SpamBlock)
 
     def append(self, operation):
-        if self.last_join_point is not None:
-            # only add operations corresponding to the first bytecode
-            raise MergeBlock(self.crnt_block, self.last_join_point)
         self.crnt_block.operations.append(operation)
 
     def bytecode_trace(self, frame):
@@ -96,30 +178,14 @@
             # before.
             self.last_join_point = frame.getstate()
 
-    def guessbool(self, ec, w_condition, cases=[False,True],
-                  replace_last_variable_except_in_first_case = None):
+    def guessbool(self, frame, w_condition):
         block = self.crnt_block
-        bvars = vars = vars2 = block.getvariables()
+        vars = block.getvariables()
         links = []
-        first = True
-        attach = {}
-        for case in cases:
-            if first:
-                first = False
-            elif replace_last_variable_except_in_first_case is not None:
-                assert block.operations[-1].result is bvars[-1]
-                vars = bvars[:-1]
-                vars2 = bvars[:-1]
-                for name, newvar in replace_last_variable_except_in_first_case(case):
-                    attach[name] = newvar
-                    vars.append(newvar)
-                    vars2.append(Variable())
-            egg = EggBlock(vars2, block, case)
-            ec.pendingblocks.append(egg)
-            link = ec.make_link(vars, egg, case)
-            if attach:
-                link.extravars(**attach)
-                egg.extravars(**attach) # xxx
+        for case in [False, True]:
+            egg = EggBlock(vars, block, case)
+            frame.pendingblocks.append(egg)
+            link = Link(vars, egg, case)
             links.append(link)
 
         block.exitswitch = w_condition
@@ -130,6 +196,34 @@
         # block.exits[True] = ifLink.
         raise StopFlowing
 
+    def guessexception(self, frame, *cases):
+        block = self.crnt_block
+        bvars = vars = vars2 = block.getvariables()
+        links = []
+        for case in [None] + list(cases):
+            if case is not None:
+                assert block.operations[-1].result is bvars[-1]
+                vars = bvars[:-1]
+                vars2 = bvars[:-1]
+                if case is Exception:
+                    last_exc = Variable('last_exception')
+                else:
+                    last_exc = Constant(case)
+                last_exc_value = Variable('last_exc_value')
+                vars.extend([last_exc, last_exc_value])
+                vars2.extend([Variable(), Variable()])
+            egg = EggBlock(vars2, block, case)
+            frame.pendingblocks.append(egg)
+            link = Link(vars, egg, case)
+            if case is not None:
+                link.extravars(last_exception=last_exc, last_exc_value=last_exc_value)
+                egg.extravars(last_exception=last_exc)
+            links.append(link)
+
+        block.exitswitch = c_last_exception
+        block.closeblock(*links)
+        raise StopFlowing
+
 
 class Replayer(Recorder):
 
@@ -150,179 +244,23 @@
                       [str(s) for s in self.listtoreplay[self.index:]]))
         self.index += 1
 
-    def guessbool(self, ec, w_condition, **kwds):
+    def guessbool(self, frame, w_condition, **kwds):
         assert self.index == len(self.listtoreplay)
-        ec.recorder = self.nextreplayer
+        frame.recorder = self.nextreplayer
         return self.booloutcome
 
-# ____________________________________________________________
-
-
-class FlowExecutionContext(ExecutionContext):
-
-    make_link = Link # overridable for transition tracking
-
-    # disable superclass method
-    bytecode_trace = None
-
-    def guessbool(self, w_condition, **kwds):
-        return self.recorder.guessbool(self, w_condition, **kwds)
-
-    def guessexception(self, *classes):
-        def replace_exc_values(case):
-            if case is not Exception:
-                yield 'last_exception', Constant(case)
-                yield 'last_exc_value', Variable('last_exc_value')
-            else:
-                yield 'last_exception', Variable('last_exception')
-                yield 'last_exc_value', Variable('last_exc_value')
-        outcome = self.guessbool(c_last_exception,
-                                 cases = [None] + list(classes),
-                                 replace_last_variable_except_in_first_case = replace_exc_values)
-        if outcome is None:
-            w_exc_cls, w_exc_value = None, None
-        else:
-            egg = self.recorder.crnt_block
+    def guessexception(self, frame, *classes):
+        assert self.index == len(self.listtoreplay)
+        frame.recorder = self.nextreplayer
+        outcome = self.booloutcome
+        if outcome is not None:
+            egg = self.nextreplayer.crnt_block
             w_exc_cls, w_exc_value = egg.inputargs[-2:]
             if isinstance(egg.last_exception, Constant):
                 w_exc_cls = egg.last_exception
-        return outcome, w_exc_cls, w_exc_value
+            raise ImplicitOperationError(w_exc_cls, w_exc_value)
 
-    def build_flow(self, func, constargs={}):
-        space = self.space
-        self.frame = frame = FlowSpaceFrame(self.space, func, constargs)
-        self.joinpoints = {}
-        self.graph = frame._init_graph(func)
-        self.pendingblocks = collections.deque([self.graph.startblock])
-
-        while self.pendingblocks:
-            block = self.pendingblocks.popleft()
-            try:
-                self.recorder = frame.recording(block)
-                frame.frame_finished_execution = False
-                next_instr = frame.last_instr
-                while True:
-                    next_instr = frame.handle_bytecode(next_instr)
-
-            except ImplicitOperationError, e:
-                if isinstance(e.w_type, Constant):
-                    exc_cls = e.w_type.value
-                else:
-                    exc_cls = Exception
-                msg = "implicit %s shouldn't occur" % exc_cls.__name__
-                w_type = Constant(AssertionError)
-                w_value = Constant(AssertionError(msg))
-                link = self.make_link([w_type, w_value], self.graph.exceptblock)
-                self.recorder.crnt_block.closeblock(link)
-
-            except OperationError, e:
-                if e.w_type is self.space.w_ImportError:
-                    msg = 'import statement always raises %s' % e
-                    raise ImportError(msg)
-                w_value = e.get_w_value(self.space)
-                link = self.make_link([e.w_type, w_value], self.graph.exceptblock)
-                self.recorder.crnt_block.closeblock(link)
-
-            except StopFlowing:
-                pass
-
-            except MergeBlock, e:
-                self.mergeblock(e.block, e.currentstate)
-
-            except Return:
-                w_result = frame.popvalue()
-                assert w_result is not None
-                link = self.make_link([w_result], self.graph.returnblock)
-                self.recorder.crnt_block.closeblock(link)
-
-        del self.recorder
-        self.fixeggblocks()
-
-
-    def fixeggblocks(self):
-        # EggBlocks reuse the variables of their previous block,
-        # which is deemed not acceptable for simplicity of the operations
-        # that will be performed later on the flow graph.
-        for link in list(self.graph.iterlinks()):
-                block = link.target
-                if isinstance(block, EggBlock):
-                    if (not block.operations and len(block.exits) == 1 and
-                        link.args == block.inputargs):   # not renamed
-                        # if the variables are not renamed across this link
-                        # (common case for EggBlocks) then it's easy enough to
-                        # get rid of the empty EggBlock.
-                        link2 = block.exits[0]
-                        link.args = list(link2.args)
-                        link.target = link2.target
-                        assert link2.exitcase is None
-                    else:
-                        mapping = {}
-                        for a in block.inputargs:
-                            mapping[a] = Variable(a)
-                        block.renamevariables(mapping)
-        for block in self.graph.iterblocks():
-            if isinstance(link, SpamBlock):
-                del link.framestate     # memory saver
-
-    def mergeblock(self, currentblock, currentstate):
-        next_instr = currentstate.next_instr
-        # can 'currentstate' be merged with one of the blocks that
-        # already exist for this bytecode position?
-        candidates = self.joinpoints.setdefault(next_instr, [])
-        for block in candidates:
-            newstate = block.framestate.union(currentstate)
-            if newstate is not None:
-                # yes
-                finished = newstate == block.framestate
-                break
-        else:
-            # no
-            newstate = currentstate.copy()
-            finished = False
-            block = None
-
-        if finished:
-            newblock = block
-        else:
-            newblock = SpamBlock(newstate)
-        # unconditionally link the current block to the newblock
-        outputargs = currentstate.getoutputargs(newstate)
-        link = self.make_link(outputargs, newblock)
-        currentblock.closeblock(link)
-        # phew
-        if not finished:
-            if block is not None:
-                # to simplify the graph, we patch the old block to point
-                # directly at the new block which is its generalization
-                block.dead = True
-                block.operations = ()
-                block.exitswitch = None
-                outputargs = block.framestate.getoutputargs(newstate)
-                block.recloseblock(self.make_link(outputargs, newblock))
-                candidates.remove(block)
-            candidates.insert(0, newblock)
-            self.pendingblocks.append(newblock)
-
-    def _convert_exc(self, operr):
-        if isinstance(operr, ImplicitOperationError):
-            # re-raising an implicit operation makes it an explicit one
-            w_value = operr.get_w_value(self.space)
-            operr = OperationError(operr.w_type, w_value)
-        return operr
-
-    # hack for unrolling iterables, don't use this
-    def replace_in_stack(self, oldvalue, newvalue):
-        w_new = Constant(newvalue)
-        f = self.frame
-        stack_items_w = f.locals_stack_w
-        for i in range(f.valuestackdepth-1, f.pycode.co_nlocals-1, -1):
-            w_v = stack_items_w[i]
-            if isinstance(w_v, Constant):
-                if w_v.value is oldvalue:
-                    # replace the topmost item of the stack that is equal
-                    # to 'oldvalue' with 'newvalue'.
-                    stack_items_w[i] = w_new
-                    break
+# ____________________________________________________________
 
 class FlowSpaceFrame(pyframe.CPythonFrame):
 
@@ -354,6 +292,10 @@
 
         self.w_locals = None # XXX: only for compatibility with PyFrame
 
+        self.joinpoints = {}
+        self._init_graph(func)
+        self.pendingblocks = collections.deque([self.graph.startblock])
+
     def initialize_frame_scopes(self, closure, code):
         if not (code.co_flags & CO_NEWLOCALS):
             raise ValueError("The code object for a function should have "
@@ -384,7 +326,7 @@
         graph.signature = self.pycode.signature()
         graph.defaults = func.func_defaults or ()
         graph.is_generator = self.pycode.is_generator
-        return graph
+        self.graph = graph
 
     def getstate(self):
         # getfastscope() can return real None, for undefined locals
@@ -394,7 +336,7 @@
             data.append(Constant(None))
         else:
             data.append(self.last_exception.w_type)
-            data.append(self.last_exception.get_w_value(self.space))
+            data.append(self.last_exception.w_value)
         recursively_flatten(self.space, data)
         nonmergeable = (self.get_blocklist(),
             self.last_instr)   # == next_instr when between bytecodes
@@ -409,7 +351,7 @@
             assert data[-1] == Constant(None)
             self.last_exception = None
         else:
-            self.last_exception = OperationError(data[-2], data[-1])
+            self.last_exception = FSException(data[-2], data[-1])
         blocklist, self.last_instr = state.nonmergeable
         self.set_blocklist(blocklist)
 
@@ -431,20 +373,138 @@
             prevblock = parent
         return recorder
 
+    def record(self, spaceop):
+        """Record an operation into the active block"""
+        recorder = self.recorder
+        if getattr(recorder, 'last_join_point', None) is not None:
+            self.mergeblock(recorder.crnt_block, recorder.last_join_point)
+            raise StopFlowing
+        recorder.append(spaceop)
+
+    def guessbool(self, w_condition, **kwds):
+        return self.recorder.guessbool(self, w_condition, **kwds)
+
+    def handle_implicit_exceptions(self, exceptions):
+        """
+        Catch possible exceptions implicitly.
+
+        If the FSException is not caught in the same function, it will
+        produce an exception-raising return block in the flow graph. Note that
+        even if the interpreter re-raises the exception, it will not be the
+        same ImplicitOperationError instance internally.
+        """
+        if not exceptions:
+            return
+        return self.recorder.guessexception(self, *exceptions)
+
+    def build_flow(self):
+        while self.pendingblocks:
+            block = self.pendingblocks.popleft()
+            try:
+                self.recorder = self.recording(block)
+                self.frame_finished_execution = False
+                next_instr = self.last_instr
+                while True:
+                    next_instr = self.handle_bytecode(next_instr)
+
+            except ImplicitOperationError, e:
+                if isinstance(e.w_type, Constant):
+                    exc_cls = e.w_type.value
+                else:
+                    exc_cls = Exception
+                msg = "implicit %s shouldn't occur" % exc_cls.__name__
+                w_type = Constant(AssertionError)
+                w_value = Constant(AssertionError(msg))
+                link = Link([w_type, w_value], self.graph.exceptblock)
+                self.recorder.crnt_block.closeblock(link)
+
+            except FSException, e:
+                if e.w_type is self.space.w_ImportError:
+                    msg = 'import statement always raises %s' % e
+                    raise ImportError(msg)
+                link = Link([e.w_type, e.w_value], self.graph.exceptblock)
+                self.recorder.crnt_block.closeblock(link)
+
+            except StopFlowing:
+                pass
+
+            except Return:
+                w_result = self.popvalue()
+                assert w_result is not None
+                link = Link([w_result], self.graph.returnblock)
+                self.recorder.crnt_block.closeblock(link)
+
+        del self.recorder
+
+    def mergeblock(self, currentblock, currentstate):
+        next_instr = currentstate.next_instr
+        # can 'currentstate' be merged with one of the blocks that
+        # already exist for this bytecode position?
+        candidates = self.joinpoints.setdefault(next_instr, [])
+        for block in candidates:
+            newstate = block.framestate.union(currentstate)
+            if newstate is not None:
+                # yes
+                finished = newstate == block.framestate
+                break
+        else:
+            # no
+            newstate = currentstate.copy()
+            finished = False
+            block = None
+
+        if finished:
+            newblock = block
+        else:
+            newblock = SpamBlock(newstate)
+        # unconditionally link the current block to the newblock
+        outputargs = currentstate.getoutputargs(newstate)
+        link = Link(outputargs, newblock)
+        currentblock.closeblock(link)
+        # phew
+        if not finished:
+            if block is not None:
+                # to simplify the graph, we patch the old block to point
+                # directly at the new block which is its generalization
+                block.dead = True
+                block.operations = ()
+                block.exitswitch = None
+                outputargs = block.framestate.getoutputargs(newstate)
+                block.recloseblock(Link(outputargs, newblock))
+                candidates.remove(block)
+            candidates.insert(0, newblock)
+            self.pendingblocks.append(newblock)
+
+    # hack for unrolling iterables, don't use this
+    def replace_in_stack(self, oldvalue, newvalue):
+        w_new = Constant(newvalue)
+        stack_items_w = self.locals_stack_w
+        for i in range(self.valuestackdepth-1, self.pycode.co_nlocals-1, -1):
+            w_v = stack_items_w[i]
+            if isinstance(w_v, Constant):
+                if w_v.value is oldvalue:
+                    # replace the topmost item of the stack that is equal
+                    # to 'oldvalue' with 'newvalue'.
+                    stack_items_w[i] = w_new
+                    break
+
     def handle_bytecode(self, next_instr):
         try:
-            next_instr = self.dispatch_bytecode(next_instr)
+            while True:
+                self.last_instr = next_instr
+                self.recorder.bytecode_trace(self)
+                next_instr, methodname, oparg = self.pycode.read(next_instr)
+                res = getattr(self, methodname)(oparg, next_instr)
+                if res is not None:
+                    next_instr = res
         except OperationThatShouldNotBePropagatedError, e:
             raise Exception(
                 'found an operation that always raises %s: %s' % (
                     self.space.unwrap(e.w_type).__name__,
-                    self.space.unwrap(e.get_w_value(self.space))))
-        except OperationError, operr:
+                    self.space.unwrap(e.w_value)))
+        except FSException, operr:
             self.attach_traceback(operr)
             next_instr = self.handle_operation_error(operr)
-        except Reraise:
-            operr = self.last_exception
-            next_instr = self.handle_operation_error(operr)
         except RaiseWithExplicitTraceback, e:
             next_instr = self.handle_operation_error(e.operr)
         return next_instr
@@ -457,29 +517,42 @@
         operr.set_traceback(tb)
 
     def handle_operation_error(self, operr):
-        block = self.unrollstack(SApplicationException.kind)
+        block = self.unrollstack(SFlowException.kind)
         if block is None:
-            # no handler found for the OperationError
+            # no handler found for the exception
             # try to preserve the CPython-level traceback
             import sys
             tb = sys.exc_info()[2]
-            raise OperationError, operr, tb
+            raise operr, None, tb
         else:
-            unroller = SApplicationException(operr)
+            unroller = SFlowException(operr)
             next_instr = block.handle(self, unroller)
             return next_instr
 
-    def enter_bytecode(self, next_instr):
-        self.last_instr = next_instr
-        self.space.executioncontext.recorder.bytecode_trace(self)
+    def RAISE_VARARGS(self, nbargs, next_instr):
+        space = self.space
+        if nbargs == 0:
+            if self.last_exception is not None:
+                operr = self.last_exception
+                if isinstance(operr, ImplicitOperationError):
+                    # re-raising an implicit operation makes it an explicit one
+                    operr = FSException(operr.w_type, operr.w_value)
+                self.last_exception = operr
+                raise RaiseWithExplicitTraceback(operr)
+            else:
+                raise FSException(space.w_TypeError,
+                    space.wrap("raise: no active exception to re-raise"))
 
-    def dispatch_bytecode(self, next_instr):
-        while True:
-            self.enter_bytecode(next_instr)
-            next_instr, methodname, oparg = self.pycode.read(next_instr)
-            res = getattr(self, methodname)(oparg, next_instr)
-            if res is not None:
-                next_instr = res
+        w_value = w_traceback = space.w_None
+        if nbargs >= 3:
+            w_traceback = self.popvalue()
+        if nbargs >= 2:
+            w_value = self.popvalue()
+        if 1:
+            w_type = self.popvalue()
+        operror = FSException(w_type, w_value)
+        operror.normalize_exception(space)
+        raise operror
 
     def IMPORT_NAME(self, nameindex, next_instr):
         space = self.space
@@ -584,3 +657,13 @@
         # swallow the exception
         return self.space.w_None
 
+### Frame blocks ###
+
+class SFlowException(SApplicationException):
+    """Flowspace override for SApplicationException"""
+    def state_unpack_variables(self, space):
+        return [self.operr.w_type, self.operr.w_value]
+
+    @staticmethod
+    def state_pack_variables(space, w_type, w_value):
+        return SFlowException(FSException(w_type, w_value))
diff --git a/pypy/objspace/flow/objspace.py b/pypy/objspace/flow/objspace.py
--- a/pypy/objspace/flow/objspace.py
+++ b/pypy/objspace/flow/objspace.py
@@ -3,12 +3,12 @@
 import sys
 import operator
 import types
-from pypy.tool import error
 from pypy.interpreter.baseobjspace import ObjSpace, Wrappable
-from pypy.interpreter.error import OperationError
 from pypy.interpreter import pyframe, argument
 from pypy.objspace.flow.model import *
-from pypy.objspace.flow import flowcontext, operation
+from pypy.objspace.flow import operation
+from pypy.objspace.flow.flowcontext import (FlowSpaceFrame, fixeggblocks,
+    OperationThatShouldNotBePropagatedError, FSException, FlowingError)
 from pypy.objspace.flow.specialcase import SPECIAL_CASES
 from pypy.rlib.unroll import unrolling_iterable, _unroller
 from pypy.rlib import rstackovf, rarithmetic
@@ -46,7 +46,7 @@
     """
 
     full_exceptions = False
-    FrameClass = flowcontext.FlowSpaceFrame
+    FrameClass = FlowSpaceFrame
 
     def initialize(self):
         self.w_None     = Constant(None)
@@ -78,6 +78,7 @@
     # disable superclass methods
     enter_cache_building_mode = None
     leave_cache_building_mode = None
+    createcompiler = None
 
     def is_w(self, w_one, w_two):
         return self.is_true(self.is_(w_one, w_two))
@@ -181,12 +182,7 @@
                                         isinstance(w_obj.value, RequiredClass))
 
     def getexecutioncontext(self):
-        return getattr(self, 'executioncontext', None)
-
-    def createcompiler(self):
-        # no parser/compiler needed - don't build one, it takes too much time
-        # because it is done each time a FlowExecutionContext is built
-        return None
+        return self.frame
 
     def exception_match(self, w_exc_type, w_check_class):
         try:
@@ -194,8 +190,8 @@
         except UnwrapException:
             raise Exception, "non-constant except guard"
         if check_class in (NotImplementedError, AssertionError):
-            raise error.FlowingError("Catching %s is not valid in RPython" %
-                                     check_class.__name__)
+            raise FlowingError(self.frame,
+                "Catching %s is not valid in RPython" % check_class.__name__)
         if not isinstance(check_class, tuple):
             # the simple case
             return ObjSpace.exception_match(self, w_exc_type, w_check_class)
@@ -224,20 +220,10 @@
         """
         if func.func_doc and func.func_doc.lstrip().startswith('NOT_RPYTHON'):
             raise Exception, "%r is tagged as NOT_RPYTHON" % (func,)
-        ec = flowcontext.FlowExecutionContext(self)
-        self.executioncontext = ec
-
-        try:
-            ec.build_flow(func, constargs)
-        except error.FlowingError, a:
-            # attach additional source info to AnnotatorError
-            _, _, tb = sys.exc_info()
-            formated = error.format_global_error(ec.graph, ec.frame.last_instr,
-                                                 str(a))
-            e = error.FlowingError(formated)
-            raise error.FlowingError, e, tb
-
-        graph = ec.graph
+        frame = self.frame = FlowSpaceFrame(self, func, constargs)
+        frame.build_flow()
+        graph = frame.graph
+        fixeggblocks(graph)
         checkgraph(graph)
         if graph.is_generator and tweak_for_generator:
             from pypy.translator.generator import tweak_generator_graph
@@ -261,7 +247,7 @@
             w_len = self.len(w_iterable)
             w_correct = self.eq(w_len, self.wrap(expected_length))
             if not self.is_true(w_correct):
-                e = OperationError(self.w_ValueError, self.w_None)
+                e = FSException(self.w_ValueError, self.w_None)
                 e.normalize_exception(self)
                 raise e
             return [self.do_operation('getitem', w_iterable, self.wrap(i))
@@ -271,13 +257,14 @@
     # ____________________________________________________________
     def do_operation(self, name, *args_w):
         spaceop = SpaceOperation(name, args_w, Variable())
-        spaceop.offset = self.executioncontext.frame.last_instr
-        self.executioncontext.recorder.append(spaceop)
+        spaceop.offset = self.frame.last_instr
+        self.frame.record(spaceop)
         return spaceop.result
 
     def do_operation_with_implicit_exceptions(self, name, *args_w):
         w_result = self.do_operation(name, *args_w)
-        self.handle_implicit_exceptions(operation.implicit_exceptions.get(name))
+        self.frame.handle_implicit_exceptions(
+                operation.implicit_exceptions.get(name))
         return w_result
 
     def is_true(self, w_obj):
@@ -288,8 +275,7 @@
         else:
             return bool(obj)
         w_truthvalue = self.do_operation('is_true', w_obj)
-        context = self.getexecutioncontext()
-        return context.guessbool(w_truthvalue)
+        return self.frame.guessbool(w_truthvalue)
 
     def iter(self, w_iterable):
         try:
@@ -303,7 +289,7 @@
         return w_iter
 
     def next(self, w_iter):
-        context = self.getexecutioncontext()
+        frame = self.frame
         try:
             it = self.unwrap(w_iter)
         except UnwrapException:
@@ -313,25 +299,17 @@
                 try:
                     v, next_unroller = it.step()
                 except IndexError:
-                    raise OperationError(self.w_StopIteration, self.w_None)
+                    raise FSException(self.w_StopIteration, self.w_None)
                 else:
-                    context.replace_in_stack(it, next_unroller)
+                    frame.replace_in_stack(it, next_unroller)
                     return self.wrap(v)
         w_item = self.do_operation("next", w_iter)
-        outcome, w_exc_cls, w_exc_value = context.guessexception(StopIteration,
-                                                                 RuntimeError)
-        if outcome is StopIteration:
-            raise OperationError(self.w_StopIteration, w_exc_value)
-        elif outcome is RuntimeError:
-            raise operation.ImplicitOperationError(Constant(RuntimeError),
-                                                    w_exc_value)
-        else:
-            return w_item
+        frame.handle_implicit_exceptions([StopIteration, RuntimeError])
+        return w_item
 
     def setitem(self, w_obj, w_key, w_val):
         # protect us from globals write access
-        ec = self.getexecutioncontext()
-        if ec and w_obj is ec.frame.w_globals:
+        if w_obj is self.frame.w_globals:
             raise SyntaxError("attempt to modify global attribute %r in %r"
                             % (w_key, ec.graph.func))
         return self.do_operation_with_implicit_exceptions('setitem', w_obj,
@@ -357,7 +335,7 @@
                 etype = e.__class__
                 msg = "generated by a constant operation:\n\t%s%r" % (
                     'getattr', (obj, name))
-                raise operation.OperationThatShouldNotBePropagatedError(
+                raise OperationThatShouldNotBePropagatedError(
                     self.wrap(etype), self.wrap(msg))
             try:
                 return self.wrap(result)
@@ -370,15 +348,15 @@
         try:
             mod = __import__(name, glob, loc, frm, level)
         except ImportError, e:
-            raise OperationError(self.w_ImportError, self.wrap(str(e)))
+            raise FSException(self.w_ImportError, self.wrap(str(e)))
         return self.wrap(mod)
 
     def import_from(self, w_module, w_name):
         try:
             return self.getattr(w_module, w_name)
-        except OperationError, e:
+        except FSException, e:
             if e.match(self, self.w_AttributeError):
-                raise OperationError(self.w_ImportError,
+                raise FSException(self.w_ImportError,
                     self.wrap("cannot import name '%s'" % w_name.value))
             else:
                 raise
@@ -431,28 +409,9 @@
                                types.TypeType)) and
                   c.__module__ in ['__builtin__', 'exceptions']):
                 exceptions = operation.implicit_exceptions.get(c)
-        self.handle_implicit_exceptions(exceptions)
+        self.frame.handle_implicit_exceptions(exceptions)
         return w_res
 
-    def handle_implicit_exceptions(self, exceptions):
-        if not exceptions:
-            return
-        # catch possible exceptions implicitly.  If the OperationError
-        # below is not caught in the same function, it will produce an
-        # exception-raising return block in the flow graph.  Note that
-        # even if the interpreter re-raises the exception, it will not
-        # be the same ImplicitOperationError instance internally.
-        context = self.getexecutioncontext()
-        outcome, w_exc_cls, w_exc_value = context.guessexception(*exceptions)
-        if outcome is not None:
-            # we assume that the caught exc_cls will be exactly the
-            # one specified by 'outcome', and not a subclass of it,
-            # unless 'outcome' is Exception.
-            #if outcome is not Exception:
-                #w_exc_cls = Constant(outcome) Now done by guessexception itself
-                #pass
-             raise operation.ImplicitOperationError(w_exc_cls, w_exc_value)
-
     def find_global(self, w_globals, varname):
         try:
             value = self.unwrap(w_globals)[varname]
@@ -462,7 +421,7 @@
                 value = getattr(self.unwrap(self.builtin), varname)
             except AttributeError:
                 message = "global name '%s' is not defined" % varname
-                raise OperationError(self.w_NameError, self.wrap(message))
+                raise FlowingError(self.frame, self.wrap(message))
         return self.wrap(value)
 
     def w_KeyboardInterrupt(self):
@@ -529,7 +488,7 @@
                     etype = e.__class__
                     msg = "generated by a constant operation:\n\t%s%r" % (
                         name, tuple(args))
-                    raise operation.OperationThatShouldNotBePropagatedError(
+                    raise OperationThatShouldNotBePropagatedError(
                         self.wrap(etype), self.wrap(msg))
                 else:
                     # don't try to constant-fold operations giving a 'long'
diff --git a/pypy/objspace/flow/operation.py b/pypy/objspace/flow/operation.py
--- a/pypy/objspace/flow/operation.py
+++ b/pypy/objspace/flow/operation.py
@@ -15,13 +15,6 @@
 from pypy.objspace.flow import model
 
 
-class OperationThatShouldNotBePropagatedError(OperationError):
-    pass
-
-class ImplicitOperationError(OperationError):
-    pass
-
-
 FunctionByName = {}   # dict {"operation_name": <built-in function>}
 OperationName  = {}   # dict {<built-in function>: "operation_name"}
 Arity          = {}   # dict {"operation name": number of arguments}
diff --git a/pypy/objspace/flow/test/test_objspace.py b/pypy/objspace/flow/test/test_objspace.py
--- a/pypy/objspace/flow/test/test_objspace.py
+++ b/pypy/objspace/flow/test/test_objspace.py
@@ -5,8 +5,8 @@
 from pypy.objspace.flow.model import mkentrymap, c_last_exception
 from pypy.interpreter.argument import Arguments
 from pypy.translator.simplify import simplify_graph
-from pypy.objspace.flow.objspace import FlowObjSpace, error
-from pypy.objspace.flow import objspace, flowcontext
+from pypy.objspace.flow.objspace import FlowObjSpace
+from pypy.objspace.flow.flowcontext import FlowingError, FlowSpaceFrame
 from pypy import conftest
 from pypy.tool.stdlib_opcode import bytecode_spec
 from pypy.interpreter.pyframe import PyFrame
@@ -857,7 +857,7 @@
                         c.co_lnotab)
 
     def patch_opcodes(self, *opcodes):
-        flow_meth_names = flowcontext.FlowSpaceFrame.opcode_method_names
+        flow_meth_names = FlowSpaceFrame.opcode_method_names
         pyframe_meth_names = PyFrame.opcode_method_names
         for name in opcodes:
             num = bytecode_spec.opmap[name]
@@ -865,7 +865,7 @@
             flow_meth_names[num] = pyframe_meth_names[num]
 
     def unpatch_opcodes(self, *opcodes):
-        flow_meth_names = flowcontext.FlowSpaceFrame.opcode_method_names
+        flow_meth_names = FlowSpaceFrame.opcode_method_names
         for name in opcodes:
             num = bytecode_spec.opmap[name]
             flow_meth_names[num] = getattr(self, 'old_' + name)
@@ -985,14 +985,14 @@
                 f()
             except NotImplementedError:
                 pass
-        py.test.raises(error.FlowingError, "self.codetest(f)")
+        py.test.raises(FlowingError, "self.codetest(f)")
         #
         def f():
             try:
                 f()
             except AssertionError:
                 pass
-        py.test.raises(error.FlowingError, "self.codetest(f)")
+        py.test.raises(FlowingError, "self.codetest(f)")
 
     def test_locals_dict(self):
         def f():
@@ -1004,6 +1004,19 @@
         assert graph.startblock.exits[0].target == graph.returnblock
 
 
+    def test_global_variable(self):
+        def global_var_missing():
+            return a
+
+        with py.test.raises(FlowingError) as rex:
+            self.codetest(global_var_missing)
+        assert str(rex.exconly()).find("global variable 'a' undeclared")
+
+    def test_eval(self):
+        exec("def f(): return a")
+        with py.test.raises(FlowingError):
+            self.codetest(f)
+
 DATA = {'x': 5,
         'y': 6}
 
diff --git a/pypy/tool/error.py b/pypy/tool/error.py
--- a/pypy/tool/error.py
+++ b/pypy/tool/error.py
@@ -65,9 +65,6 @@
     lines = source_lines1(graph, *args, **kwds)
     return ['In %r:' % (graph,)] + lines
 
-class FlowingError(Exception):
-    pass
-
 class AnnotatorError(Exception):
     pass
 
@@ -177,13 +174,6 @@
     msg.append("  " + str(binding))
     return "\n".join(msg)
 
-def format_global_error(graph, offset, message):
-    msg = []
-    msg.append('-+' * 30)
-    msg.append(message)
-    msg += source_lines(graph, None, offset=offset)
-    return "\n".join(msg)
-
 def debug(drv, use_pdb=True):
     # XXX unify some code with pypy.translator.goal.translate
     from pypy.translator.tool.pdbplus import PdbPlusShow
diff --git a/pypy/tool/test/test_error.py b/pypy/tool/test/test_error.py
--- a/pypy/tool/test/test_error.py
+++ b/pypy/tool/test/test_error.py
@@ -3,7 +3,7 @@
 """
 
 from pypy.translator.translator import TranslationContext
-from pypy.tool.error import FlowingError, AnnotatorError, NoSuchAttrError
+from pypy.tool.error import AnnotatorError, NoSuchAttrError
 from pypy.annotation.policy import BasicAnnotatorPolicy
 
 import py
@@ -15,20 +15,13 @@
     t = TranslationContext()
     t.buildannotator(policy=Policy()).build_types(function, annotation)
 
-def test_global_variable():
-    def global_var_missing():
-        return a
-    
-    rex = py.test.raises(FlowingError, compile_function, global_var_missing)
-    assert str(rex.exconly()).find("global variable 'a' undeclared")
-
 class AAA(object):
     pass
 
 def test_blocked_inference1():
     def blocked_inference():
         return AAA().m()
-    
+
     py.test.raises(AnnotatorError, compile_function, blocked_inference)
 
 def test_blocked_inference2():
@@ -36,7 +29,7 @@
         a = AAA()
         b = a.x
         return b
-    
+
     py.test.raises(AnnotatorError, compile_function, blocked_inference)
 
 def test_someobject():
@@ -59,14 +52,9 @@
 
     py.test.raises(AnnotatorError, compile_function, someobject_deg, [int])
 
-def test_eval():
-    exec("def f(): return a")
-    
-    py.test.raises(FlowingError, compile_function, f)
-
 def test_eval_someobject():
     exec("def f(n):\n if n == 2:\n  return 'a'\n else:\n  return 3")
-    
+
     py.test.raises(AnnotatorError, compile_function, f, [int])
 
 def test_someobject_from_call():


More information about the pypy-commit mailing list