[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