[pypy-svn] r34333 - in pypy/branch/resume-point-hack/pypy/rlib: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Nov 7 15:57:54 CET 2006


Author: cfbolz
Date: Tue Nov  7 15:57:52 2006
New Revision: 34333

Added:
   pypy/branch/resume-point-hack/pypy/rlib/test/test_rstack.py
Modified:
   pypy/branch/resume-point-hack/pypy/rlib/rstack.py
Log:
hacks to support resume_point, resume_state_create and resume_state_invoke on
top of CPython.


Modified: pypy/branch/resume-point-hack/pypy/rlib/rstack.py
==============================================================================
--- pypy/branch/resume-point-hack/pypy/rlib/rstack.py	(original)
+++ pypy/branch/resume-point-hack/pypy/rlib/rstack.py	Tue Nov  7 15:57:52 2006
@@ -2,8 +2,8 @@
 This file defines utilities for manipulating the stack in an
 RPython-compliant way, intended mostly for use by the Stackless PyPy.
 """
-
-import inspect
+import py
+import inspect, random, sys, dis
 
 def stack_unwind():
     raise RuntimeError("cannot unwind stack in non-translated versions")
@@ -36,8 +36,46 @@
 
 from pypy.rpython.extregistry import ExtRegistryEntry
 
-def resume_point(label, *args, **kwds):
-    pass
+def resume_point(name, *args, **kwargs):
+    if not isinstance(name, str):
+        raise TypeError("resume point name has to be a string")
+    assert len(kwargs) <= 1
+    #print "    resume_point", name
+    resume_data = ResumeData.current_resume_data
+    if resume_data is None or not resume_data.is_resuming(name):
+        #print "     not resuming"
+        return
+    frame = sys._getframe(1)
+    print "    ", frame, frame.f_locals
+    setvals = {}
+    assert len(resume_data.args) == len(args), (
+            "resume state for %s created with wrong number of arguments" %
+            (name, ))
+    for name, val in frame.f_locals.iteritems():
+        for i, arg in enumerate(args):
+            if val is arg:
+                if name in setvals:
+                    raise ValueError(
+                        "Could not reliably determine value of %s" % name)
+                setvals[name] = val
+                resume_data.set_local(name, resume_data.args[i], 1)
+        if kwargs:
+            if val is kwargs["returns"]:
+                if resume_data.returning is NOTHING:
+                    assert resume_data.raising is not NOTHING
+                    continue
+                if name in setvals:
+                    raise ValueError(
+                        "Could not reliably determine value of %s" % name)
+                setvals[name] = val
+                resume_data.set_local(name, resume_data.returning, 1)
+    if resume_data.raising is not NOTHING:
+        print "raising..."
+        e = resume_data.raising
+        resume_data.resuming_finished()
+        raise e
+    resume_data.resuming_finished()
+
 
 class ResumePointFnEntry(ExtRegistryEntry):
     _about_ = resume_point
@@ -70,8 +108,8 @@
         return hop.genop('resume_point', [c_label, v_return] + args_v,
                          hop.r_result)
 
-def resume_state_create(prevstate, label, *args):
-    raise RuntimeError("cannot resume states in non-translated versions")
+def resume_state_create(prevstate, label, func, *args):
+    return ResumeData(prevstate, label, func, *args)
 
 def concretify_argument(hop, index):
     from pypy.objspace.flow import model
@@ -86,7 +124,7 @@
 class ResumeStateCreateFnEntry(ExtRegistryEntry):
     _about_ = resume_state_create
 
-    def compute_result_annotation(self, s_prevstate, s_label, *args_s):
+    def compute_result_annotation(self, s_prevstate, s_label, s_ignore, *args_s):
         from pypy.annotation import model as annmodel
         return annmodel.SomeExternalObject(frame_stack_top)
 
@@ -97,6 +135,7 @@
 
         assert hop.args_s[1].is_constant()
         c_label = hop.inputconst(lltype.Void, hop.args_s[1].const)
+        c_func = hop.inputconst(lltype.Void, hop.args_s[2].const)
 
         v_state = hop.inputarg(hop.r_result, arg=0)
 
@@ -109,7 +148,7 @@
                          hop.r_result)
 
 def resume_state_invoke(type, state, **kwds):
-    raise NotImplementedError("only works in translated versions")
+    return state.resume(**kwds)
 
 class ResumeStateInvokeFnEntry(ExtRegistryEntry):
     _about_ = resume_state_invoke
@@ -143,3 +182,351 @@
                          hop.r_result)
         
         
+# __________________________________________________________________
+# the following code is used for the (quite hackish) implementation
+# of resume states on top of CPython
+
+
+
+operations = [
+    ('operator.pos',         'pos',       1, ['__pos__']),
+    ('operator.neg',         'neg',       1, ['__neg__']),
+    ('abs',                  'abs',       1, ['__abs__']),
+    ('operator.invert',      '~',         1, ['__invert__']),
+    ('getattr',              'getattr',   2, ['__getattr__']),
+    ('operator.getitem',     'getitem',   2, ['__getitem__']),
+    ('operator.add',         '+',         2, ['__add__', '__radd__']),
+    ('operator.sub',         '-',         2, ['__sub__', '__rsub__']),
+    ('operator.mul',         '*',         2, ['__mul__', '__rmul__']),
+    ('operator.truediv',     '/',         2, ['__truediv__', '__rtruediv__']),
+    ('operator.floordiv',    '//',        2, ['__floordiv__', '__rfloordiv__']),
+    ('operator.div',         'div',       2, ['__div__', '__rdiv__']),
+    ('operator.mod',         '%',         2, ['__mod__', '__rmod__']),
+    ('operator.divmod',      'divmod',    2, ['__divmod__', '__rdivmod__']),
+    ('operator.lshift',      '<<',        2, ['__lshift__', '__rlshift__']),
+    ('operator.rshift',      '>>',        2, ['__rshift__', '__rrshift__']),
+    ('operator.and_',        '&',         2, ['__and__', '__rand__']),
+    ('operator.or_',         '|',         2, ['__or__', '__ror__']),
+    ('operator.xor',         '^',         2, ['__xor__', '__rxor__']),
+    ('operator.lt',          '<',         2, ['__lt__', '__gt__']),
+    ('operator.le',          '<=',        2, ['__le__', '__ge__']),
+    ('operator.eq',          '==',        2, ['__eq__', '__eq__']),
+    ('operator.ne',          '!=',        2, ['__ne__', '__ne__']),
+    ('operator.gt',          '>',         2, ['__gt__', '__lt__']),
+    ('operator.ge',          '>=',        2, ['__ge__', '__le__']),
+    ('cmp',                  'cmp',       2, ['__cmp__']),
+]
+
+class Blackhole(object):
+    for impl, _, arity, names in operations:
+        args = ", ".join(["v%s" % i for i in range(arity)])
+        for name in names:
+            exec py.code.Source("""
+    def %s(%s):
+        return v0.__class__()
+    """ % (name, args)).compile()
+
+    def __pow__(self, *args):
+        return self.__class__()
+
+    def __call__(self, *args):
+        return self.__class__()
+
+
+class HomingBlackhole(Blackhole):
+    def __nonzero__(self):
+        print "taking decision",
+        decision = ResumeData.current_resume_data.pop_next_decision("bool")[1]
+        print decision
+        return decision
+
+    def next(self):
+        print "taking decision"
+        decision = ResumeData.current_resume_data.pop_next_decision("next")[1]
+        print decision
+        if decision == "stop":
+            raise StopIteration
+        return self.__class__()
+
+    def __iter__(self):
+        return self.__class__()
+
+    def _freeze_(self):
+        assert 0 # a HomingBlackhole was left!!!
+
+
+
+NOTHING = object()
+
+class ResumeData(object):
+    current_resume_data = None
+
+    def __init__(self, goto_after, name, func, *args):
+        self.local_changes = []
+        self.goto_after = goto_after
+        self.name = name
+        self.func = func
+        self.args = args
+        self.decision_stack = None
+        self.likely_frame = None
+        self.previous_globals = None
+        self.returning = NOTHING
+        self.raising = NOTHING
+
+    def register_local_change(self, frame, name, value):
+        assert frame is self.likely_frame
+        self.local_changes.append((name, value))
+
+    def pop_local_changes(self, frame):
+        assert frame is self.likely_frame
+        res = self.local_changes
+        self.local_changes = []
+        return res
+
+    def is_resuming(self, name):
+        return self.name == name
+
+    def resume(self, returning=NOTHING, raising=NOTHING):
+        assert returning is NOTHING or raising is NOTHING
+        try:
+            ResumeData.current_resume_data = self
+            self.returning = returning
+            self.raising = raising
+            dummy_args = [HomingBlackhole()] * self.func.func_code.co_argcount
+            paths, other_globals = find_path_to_resume_point(self.func)
+            self.fix_globals = [g for g, val in other_globals.items() if val]
+            decisions = paths[self.name][:]
+            decisions.reverse()
+            self.decision_stack = decisions
+            sys.settrace(self.resume_tracer)
+            try:
+                result = self.func(*dummy_args)
+            except Exception, e:
+                if self.goto_after is not None:
+                    print "caught something, handing it on..."
+                    return self.goto_after.resume(raising=e)
+                raise
+        finally:
+            sys.settrace(None)
+        if self.goto_after is not None:
+            return self.goto_after.resume(returning=result)
+        return result
+
+    def pop_next_decision(self, what):
+        assert self.decision_stack[-1][0] == what
+        return self.decision_stack.pop()
+
+    def resuming_finished(self):
+        self.decisions = None
+        ResumeData.current_resume_data = None
+        self.returning = NOTHING
+        self.raising = NOTHING
+
+    def possible_frame(self, frame):
+        if self.likely_frame is None:
+            self.likely_frame = frame
+            return True
+        return frame is self.likely_frame
+
+    def set_local(self, local, val, levels=0):
+        frame = sys._getframe(levels + 1)
+        self.register_local_change(frame, local, val)
+
+    def resume_tracer(self, frame, what, x):
+        is_resume_frame = self.possible_frame(frame)
+#        print frame, what, x, is_resume_frame
+        if not is_resume_frame:
+            return None
+        # still waiting for resume_point?
+        if ResumeData.current_resume_data is self:
+            newlocals = {}
+            for name, val in frame.f_locals.iteritems():
+                if not isinstance(val, HomingBlackhole):
+                    val = HomingBlackhole()
+                    newlocals[name] = val
+            if newlocals:
+#                print "fixing locals", newlocals
+                frame.f_locals.update(newlocals)
+            if self.fix_globals:
+                self.previous_globals = {}
+                print "fixing globals", self.fix_globals
+                for gl in self.fix_globals:
+                    try:
+                        oldvalue = frame.f_globals[gl]
+                    except KeyError:
+                        oldvalue = NOTHING
+                    frame.f_globals[gl] = HomingBlackhole()
+                    self.previous_globals[gl] = oldvalue
+                    assert gl in frame.f_globals
+                self.fix_globals = None
+        else:
+            print "restore correct globals"
+            if self.previous_globals is not None:
+                for gl, oldvalue in self.previous_globals.iteritems():
+                    if oldvalue is NOTHING:
+                        if gl in frame.f_globals:
+                            del frame.f_globals[gl]
+                    else:
+                        frame.f_globals[gl] = oldvalue
+        changes = self.pop_local_changes(frame)
+        if changes:
+            print 'changes', dict(changes), changes
+            frame.f_locals.update(dict(changes))
+        return self.resume_tracer
+
+
+# _________________________________________________________________________
+# Functions to analyze bytecode to find a way to reach resume_points
+
+def find_basic_blocks(code):
+    labels = []
+    n = len(code)
+    i = 0
+    while i < n:
+        c = code[i]
+        op = ord(c)
+        i = i + 1
+        if op >= dis.HAVE_ARGUMENT:
+            oparg = ord(code[i]) + ord(code[i+1])*256
+            i = i+2
+            label = -1
+            if op in dis.hasjrel:
+                label = i+oparg
+            elif op in dis.hasjabs:
+                label = oparg
+            if label >= 0:
+                if label not in labels:
+                    labels.append(label)
+                if i - 3 not in labels:
+                    labels.append(i - 3)
+    return labels
+
+def split_opcode_args(code):
+    ops_args = []
+    n = len(code)
+    i = 0
+    while i < n:
+        c = code[i]
+        op = ord(c)
+        i = i + 1
+        if op >= dis.HAVE_ARGUMENT:
+            oparg = ord(code[i]) + ord(code[i+1])*256
+            ops_args.append((i - 1, dis.opname[op], oparg))
+            i = i+2
+        else:
+            ops_args.append((i - 1, dis.opname[op], None))
+    return ops_args
+
+def find_resume_points_and_other_globals(func):
+    code_obj = func.func_code
+    code = code_obj.co_code
+    ops_args = split_opcode_args(code)
+    globals = func.func_globals
+    loaded_global = None
+    loaded_global_name = None
+    positions = []
+    other_globals = {}
+    for i, (pos, opname, arg) in enumerate(ops_args):
+        resume_pos = -1
+        if opname == "LOAD_GLOBAL":
+            loaded_global_name = code_obj.co_names[arg]
+            try:
+                loaded_global = globals[loaded_global_name]
+            except KeyError:
+                loaded_global = None
+            if loaded_global is resume_point:
+                resume_pos = pos
+                loaded_global = None
+                other_globals[loaded_global_name] = False
+            else:
+                if (loaded_global_name not in other_globals and 
+                    loaded_global_name is not None):
+                    other_globals[loaded_global_name] = True
+        elif opname == "LOAD_ATTR":
+            if getattr(loaded_global,
+                       code_obj.co_names[arg], None) is resume_point:
+                resume_pos = pos
+                loaded_global = None
+                other_globals[loaded_global_name] = False
+            else:
+                if (loaded_global_name not in other_globals and 
+                    loaded_global_name is not None):
+                    other_globals[loaded_global_name] = True
+        else:
+            loaded_global = None
+        if resume_pos >= 0:
+            if i + 1 >= len(ops_args) or ops_args[i + 1][1] != "LOAD_CONST":
+                raise TypeError(
+                    "could not statically determine resume point names")
+            else:
+                positions.append((pos, code_obj.co_consts[ops_args[i + 1][2]]))
+    if len(dict.fromkeys(positions)) != len(positions):
+        raise TypeError("duplicate resume point name")
+    return positions, other_globals
+
+def _get_targets_with_condition(pos, name, arg):
+    op = dis.opname.index(name)
+    if op in dis.hasjrel:
+        target = pos + arg + 3
+        if name == "JUMP_IF_FALSE":
+            return [(target, "bool", False), (pos + 3, "bool", True)]
+        elif name == "JUMP_IF_TRUE":
+            return [(target, "bool", True), (pos + 3, "bool", False)]
+        elif name == "JUMP_FORWARD":
+            return [(target, None, None)]
+        elif name == "SETUP_LOOP" or name == "SETUP_EXCEPT":
+            return [(pos + 3, None, None)] #XXX not sure
+        elif name == "FOR_ITER":
+            return [(target, "next", "stop")]
+        assert 0, "not implemented"
+    elif op in dis.hasjabs:
+        if name == "JUMP_ABSOLUTE":
+            return [(arg, None, None)]
+        assert 0, "not implemented"
+    else:
+        if op > dis.HAVE_ARGUMENT:
+            return [(pos + 3, None, None)]
+        else:
+            return [(pos + 1, None, None)]
+        assert 0, "not implemented"
+
+def find_path_to_resume_point(func):
+    resume_points, other_globals = find_resume_points_and_other_globals(func)
+    ops_args = split_opcode_args(func.func_code.co_code)
+    paths = {ops_args[0][0]: []}
+    last_unknown = None
+    while 1:
+        flip_boolean = False
+        num_nots = 0
+        unknown = {}
+        for i, (pos, name, arg) in enumerate(ops_args):
+            path = paths.get(pos, None)
+            if path is None:
+                unknown[path] = True
+                continue
+            targets = _get_targets_with_condition(pos, name, arg)
+            if name == "UNARY_NOT":
+                flip_boolean = not flip_boolean
+                num_nots += 1
+            elif num_nots:
+                num_nots = 0
+                for i in range(len(targets)):
+                    target, what, value = targets[i]
+                    if what is None:
+                        flip_boolean = False
+                        targets[i] = target, "bool", False
+            for target, what, value in targets:
+                oldpath = paths.get(target, None)
+                if oldpath is None or len(oldpath) > len(path) + 1:
+                    if what == "bool" and flip_boolean:
+                        value = not value
+                    paths[target] = path + [(what, value)]
+        if not unknown or unknown == last_unknown:
+            break
+        last_unknown = unknown
+    result = {}
+    for pos, name in resume_points:
+        if pos in paths:
+            result[name] = [(how, what) for how, what in paths[pos]
+                                            if how is not None]
+    return result, other_globals

Added: pypy/branch/resume-point-hack/pypy/rlib/test/test_rstack.py
==============================================================================
--- (empty file)
+++ pypy/branch/resume-point-hack/pypy/rlib/test/test_rstack.py	Tue Nov  7 15:57:52 2006
@@ -0,0 +1,427 @@
+import py
+import sys, dis
+from pypy.rlib.rstack import Blackhole, resume_point
+from pypy.rlib.rstack import find_basic_blocks, split_opcode_args
+from pypy.rlib.rstack import find_resume_points_and_other_globals
+from pypy.rlib.rstack import find_path_to_resume_point, ResumeData
+from pypy.rlib.rstack import resume_state_create, resume_state_invoke
+from pypy.rlib import rstack
+
+def test_blackhole_simple():
+    b = Blackhole()
+    assert isinstance(b + b, Blackhole)
+    assert isinstance(b.whatever, Blackhole)
+    assert isinstance(b(b, b, 1), Blackhole)
+    assert isinstance(b ** b, Blackhole)
+
+def test_set_local_direct():
+    try:
+        def f(x):
+            resume_data.set_local('x', 42)
+            a = 1
+            return x
+        resume_data = ResumeData(None, "...", f)
+        sys.settrace(resume_data.resume_tracer)
+        res = f(1)
+    finally:
+        sys.settrace(None)
+    assert res == 42
+
+def test_set_local_direct_several():
+    try:
+        def f(x, y):
+            resume_data.set_local('x', 42)
+            resume_data.set_local('y', 42)
+            return x + y
+        resume_data = ResumeData(None, "...", f)
+        sys.settrace(resume_data.resume_tracer)
+        res = f(1, 1)
+    finally:
+        sys.settrace(None)
+    assert res == 84
+
+def test_set_local_nested():
+    try:
+        def f(x):
+            g()
+            return x
+        def g():
+            resume_data.set_local('x', 42, 1)
+        resume_data = ResumeData(None, "...", f)
+        sys.settrace(resume_data.resume_tracer)
+        res = f(1)
+    finally:
+        sys.settrace(None)
+    assert res == 42
+
+def test_set_local_nested_several():
+    try:
+        def f(x, y):
+            g()
+            return x + y
+        def g():
+            resume_data.set_local('y', 22, 1)
+            resume_data.set_local('x', 20, 1)
+        resume_data = ResumeData(None, "...", f)
+        sys.settrace(resume_data.resume_tracer)
+        res = f(1, 1)
+    finally:
+        sys.settrace(None)
+    assert res == 42
+
+
+def test_resume_simple():
+    def f(x):
+        x += 1
+        print 1, x
+        resume_point("r1", x)
+        print 2, x
+        return x
+    assert f(1) == 2
+    s1 = resume_state_create(None, "r1", f, 1)
+    r = resume_state_invoke(int, s1)
+    assert r == 1
+
+def test_resume_simple_module_attribute():
+    def f(x):
+        x += 1
+        print 1, x
+        rstack.resume_point("r1", x)
+        print 2, x
+        return x
+    assert f(1) == 2
+    s1 = resume_state_create(None, "r1", f, 1)
+    r = resume_state_invoke(int, s1)
+    assert r == 1
+
+def test_normal_gettatr():
+    def f(out):
+        out.append(3)
+        rstack.resume_point("r1", out)
+        return out[-1]
+    s1 = resume_state_create(None, "r1", f, [4])
+    r = resume_state_invoke(int, s1)
+    assert r == 4
+
+
+def test_resume_many_args():
+    def f(x, y, z):
+        x += 1
+        y += 2
+        z += 3
+        resume_point("r1", x, y, z)
+        return x + y + z
+    assert f(1, 2, 3) == 12
+    s1 = resume_state_create(None, "r1", f, 1, 2, 3)
+    res = resume_state_invoke(int, s1)
+    assert res == 6
+
+def test_several_resume_points():
+    def f(x, y, z):
+        x += 1
+        y += 2
+        resume_point("r1", x, y, returns=z)
+        z += 3
+        resume_point("r2", x, y, returns=z)
+        return x + y + z
+    assert f(1, 2, 3) == 12
+    s1 = resume_state_create(None, "r1", f, 1, 2)
+    res = resume_state_invoke(int, s1, returning=3)
+    assert res == 9
+    s2 = resume_state_create(None, "r2", f, 1, 2)
+    res = resume_state_invoke(int, s2, returning=3)
+    assert res == 6
+
+def test_resume_in_branch():
+    def f(cond, x):
+        x += 1
+        if cond:
+            resume_point("r1", x)
+            return x
+        else:
+            resume_point("r2", x)
+            x += 1
+            return x
+    assert f(True, 1) == 2
+    assert f(False, 2) == 4
+    resume_data = resume_state_create(None, "r2", f, 2)
+    res = resume_state_invoke(int, resume_data)
+    assert res == 3
+    resume_data = resume_state_create(None, "r1", f, 1)
+    res = resume_state_invoke(int, resume_data)
+    assert res == 1
+
+def test_resume_branches_nested():
+    def f(c1, c2, x):
+        if c1:
+            if c2:
+                resume_point("r1", x)
+                return x + 1
+            else:
+                resume_point("r2", x)
+                return x + 2
+        else:
+            if c2:
+                resume_point("r3", x)
+                return x + 3
+            else:
+                resume_point("r4", x)
+                return x + 4
+    for i, name in enumerate("r1 r2 r3 r4".split()):
+        s1 = resume_state_create(None, name, f, 2)
+        res = resume_state_invoke(int, s1)
+        assert res == 3 + i
+
+def test_resume_while_simple():
+    def f(x):
+        a = 1
+        result = 0
+        while a <= x:
+            resume_point("r", a, x, result)
+            print result, a, x
+            result += a
+            a += 1
+        return result
+    s1 = resume_state_create(None, "r", f, 4, 6, 10)
+    res = resume_state_invoke(int, s1)
+    assert res == 25
+
+def test_resume_while_non_unique():
+    def f(x):
+        a = 0
+        result = 0
+        while a <= x:
+            resume_point("r", a, x, result)
+            print result, a, x
+            result += a
+            a += 1
+        return result
+    s1 = resume_state_create(None, "r", f, 4, 6, 10)
+    res = resume_state_invoke(int, s1)
+    assert res == 25
+
+def test_resume_function_call():
+    def g(x):
+        while 1:
+            x += 1
+        return x
+    def f(x):
+        x = g(x)
+        resume_point("r", x)
+        return x
+    s1 = resume_state_create(None, "r", f, 1)
+    res = resume_state_invoke(int, s1)
+    assert res == 1
+
+def forever(x):
+    if x:
+        while 1:
+            print "bad idea!"
+    return 41
+
+def test_resume_global_function_call():
+    old_forever = forever
+    def f(x):
+        forever(1)
+        resume_point("r", x)
+        x += forever(0)
+        return x
+    s1 = resume_state_create(None, "r", f, 1)
+    res = resume_state_invoke(int, s1)
+    assert res == 42
+    assert forever is old_forever # make sure the globals were restored
+
+def test_chained_states():
+    def g(x, y):
+        x += 1
+        resume_point("rp1", x, y)
+        return x + y
+    def f(x, y, z):
+        y += 1
+        r = g(x, y)
+        resume_point("rp2", z, returns=r)
+        return r + z
+    def example():
+        v1 = f(1, 2, 3)
+        s2 = resume_state_create(None, "rp2", f, 2)
+        s1 = resume_state_create(s2, "rp1", g, 4, 5)
+        return 100*v1 + resume_state_invoke(int, s1)
+    res = example()
+    assert res == 811
+
+def test_resume_and_raise_and_catch():
+    def g(x):
+        x += 1
+        resume_point("rp0", x)
+        if x == 0:
+            raise KeyError
+        return x + 1
+    def f(x):
+        x = x - 1
+        try:
+            r = g(x)
+            resume_point("rp1", returns=r)
+        except KeyError:
+            r = 42
+        return r - 1
+    def example():
+        v1 = f(2)
+        s1 = resume_state_create(None, "rp1", f)
+        s0 = resume_state_create(s1, "rp0", g, 0)
+        v2 = resume_state_invoke(int, s0)
+        return v1*100 + v2
+    res = example()
+    assert res == 241
+
+def DONOTtest_resume_in_except_block(): # probably not necessary 
+    def g(x):
+        if x:
+            raise KeyError
+    def f(x):
+        x += 1
+        try:
+            g(x)
+        except KeyError:
+            resume_point("r1", x)
+        return x
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+
+def test_jump_over_for():
+    def f(x):
+        result = 0
+        for i in range(x):
+            print "bad idea"
+            result += i
+        resume_point("r1", result)
+        return result
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+
+def test_resume_point_guarded_by_complex_condition():
+    def f(x):
+        x += 5
+        if (x >> 8) & 0xff == 0:
+            resume_point("r1", x)
+        else:
+            x += 8
+        return x
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+    
+def test_function_with_bool():
+    def f(x):
+        x = bool(x)
+        if x:
+            resume_point("r1", x)
+        else:
+            x += 8
+        return x
+    dis.dis(f)
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+
+def test_function_with_and():
+    def f(x, y):
+        if x and not y:
+            resume_point("r1", x)
+        else:
+            x += 8
+        return x
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+
+def test_function_with_not():
+    def f(x, y):
+        x = not x
+        if x:
+            resume_point("r1", x)
+        else:
+            x += 8
+        return x
+    dis.dis(f)
+    s = resume_state_create(None, "r1", f, 42)
+    res = resume_state_invoke(int, s)
+    assert res == 42
+
+# _________________________________________________________________________
+# test for bytecode analyzing functions
+
+def test_find_basic_blocks():
+    def f(cond, x):
+        x += 1
+        if cond:
+            return x
+        else:
+            x += 1
+            return x
+    code = f.func_code.co_code
+    res = find_basic_blocks(code)
+    assert len(res) == 4
+
+def test_split_opcode_args():
+    def f(x):
+        return x + 1
+    dis.dis(f)
+    res = split_opcode_args(f.func_code.co_code)
+    assert len(res) == 4
+    assert [op for pos, op, const in res] == [
+        "LOAD_FAST", "LOAD_CONST", "BINARY_ADD", "RETURN_VALUE"]
+    assert [pos for pos, op, const in res] == [0, 3, 6, 7]
+    assert res[0][2] == 0 # LOAD_FAST
+    assert res[1][2] == 1 # LOAD_CONST
+    assert res[2][2] is None # BINARY_ADD
+    assert res[3][2] is None # RETURN_VALUE
+
+def test_find_resume_points():
+    def f(cond, x):
+        forever(x)
+        x += 1
+        if cond:
+            resume_point("r1", x)
+            return x
+        else:
+            resume_point("r2", x)
+            x += 1
+            return x
+    rps, other_globals = find_resume_points_and_other_globals(f)
+    assert len(rps) == 2
+    assert rps[0][1] == "r1"
+    assert rps[1][1] == "r2"
+    assert not other_globals['resume_point']
+    assert other_globals['forever']
+
+def test_find_path_to_resume_point():
+    def f(cond, x):
+        x += 1
+        if cond:
+            resume_point("r1", x)
+            return x
+        else:
+            resume_point("r2", x)
+            x += 1
+            return x
+    paths, other_globals = find_path_to_resume_point(f)
+    assert paths["r1"] == [("bool", True)]
+    assert paths["r2"] == [("bool", False)]
+
+def test_find_path_with_not():
+    def f(x, y):
+        y = not y
+        resume_point("r2", x)
+        if not not x:
+            resume_point("r1", x)
+        else:
+            x += 8
+        return x
+    dis.dis(f)
+    paths, other_globals = find_path_to_resume_point(f)
+    assert paths["r1"][0][0] == "bool"
+    assert paths["r1"][1] == ("bool", True)
+    assert paths["r2"][0][0] == "bool"
+



More information about the Pypy-commit mailing list