[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