[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