Commits

Armin Rigo committed a5e13eb Merge

Merge kill-flowobjspace (ronan):

This is a basically just a bunch of random cleanups. Despite the name,
it doesn't kill FlowObjSpace, but it does hide it from public view.

Highlights:

replaces FlowObjSpace().build_flow(func) with just build_flow(func)

cleans up the ArgumentsForTranslation mess in flowspace

moves ArgumentsForTranslation to rpython.annotator, since it's not
used in flowspace any more

  • Participants
  • Parent commits 4d7cd4d, 01aad6b

Comments (0)

Files changed (23)

File pypy/module/test_lib_pypy/test_collections.py

File contents unchanged.

File rpython/annotator/argument.py

+"""
+Arguments objects.
+"""
+from rpython.annotator.model import SomeTuple, SomeObject
+
+# for parsing call arguments
+class RPythonCallsSpace(object):
+    """Pseudo Object Space providing almost no real operation.
+    For the Arguments class: if it really needs other operations, it means
+    that the call pattern is too complex for R-Python.
+    """
+    def newtuple(self, items_s):
+        if len(items_s) == 1 and items_s[0] is Ellipsis:
+            res = SomeObject()   # hack to get a SomeObject as the *arg
+            res.from_ellipsis = True
+            return res
+        else:
+            return SomeTuple(items_s)
+
+    def unpackiterable(self, s_obj, expected_length=None):
+        if isinstance(s_obj, SomeTuple):
+            return list(s_obj.items)
+        if (s_obj.__class__ is SomeObject and
+            getattr(s_obj, 'from_ellipsis', False)):    # see newtuple()
+            return [Ellipsis]
+        raise CallPatternTooComplex("'*' argument must be SomeTuple")
+
+    def is_true(self, s_tup):
+        assert isinstance(s_tup, SomeTuple)
+        return bool(s_tup.items)
+
+
+class CallPatternTooComplex(Exception):
+    pass
+
+
+class ArgumentsForTranslation(object):
+    w_starstararg = None
+    def __init__(self, space, args_w, keywords=None, keywords_w=None,
+                 w_stararg=None, w_starstararg=None):
+        self.w_stararg = w_stararg
+        assert w_starstararg is None
+        self.space = space
+        assert isinstance(args_w, list)
+        self.arguments_w = args_w
+        self.keywords = keywords
+        self.keywords_w = keywords_w
+        self.keyword_names_w = None
+
+    def __repr__(self):
+        """ NOT_RPYTHON """
+        name = self.__class__.__name__
+        if not self.keywords:
+            return '%s(%s)' % (name, self.arguments_w,)
+        else:
+            return '%s(%s, %s, %s)' % (name, self.arguments_w,
+                                       self.keywords, self.keywords_w)
+
+    @property
+    def positional_args(self):
+        if self.w_stararg is not None:
+            args_w = self.space.unpackiterable(self.w_stararg)
+            return self.arguments_w + args_w
+        else:
+            return self.arguments_w
+
+    def fixedunpack(self, argcount):
+        """The simplest argument parsing: get the 'argcount' arguments,
+        or raise a real ValueError if the length is wrong."""
+        if self.keywords:
+            raise ValueError("no keyword arguments expected")
+        if len(self.arguments_w) > argcount:
+            raise ValueError("too many arguments (%d expected)" % argcount)
+        elif len(self.arguments_w) < argcount:
+            raise ValueError("not enough arguments (%d expected)" % argcount)
+        return self.arguments_w
+
+    def prepend(self, w_firstarg): # used often
+        "Return a new Arguments with a new argument inserted first."
+        return ArgumentsForTranslation(self.space, [w_firstarg] + self.arguments_w,
+                                       self.keywords, self.keywords_w, self.w_stararg,
+                                       self.w_starstararg)
+
+    def copy(self):
+        return ArgumentsForTranslation(self.space, self.arguments_w,
+                                       self.keywords, self.keywords_w, self.w_stararg,
+                                       self.w_starstararg)
+
+    def _match_signature(self, scope_w, signature, defaults_w=None):
+        """Parse args and kwargs according to the signature of a code object,
+        or raise an ArgErr in case of failure.
+        """
+        #   args_w = list of the normal actual parameters, wrapped
+        #   scope_w = resulting list of wrapped values
+        #
+        co_argcount = signature.num_argnames() # expected formal arguments, without */**
+
+        args_w = self.positional_args
+        num_args = len(args_w)
+        keywords = self.keywords or []
+        num_kwds = len(keywords)
+
+        # put as many positional input arguments into place as available
+        take = min(num_args, co_argcount)
+        scope_w[:take] = args_w[:take]
+        input_argcount = take
+
+        # collect extra positional arguments into the *vararg
+        if signature.has_vararg():
+            if num_args > co_argcount:
+                starargs_w = args_w[co_argcount:]
+            else:
+                starargs_w = []
+            scope_w[co_argcount] = self.space.newtuple(starargs_w)
+        elif num_args > co_argcount:
+            raise ArgErrCount(num_args, num_kwds, signature, defaults_w, 0)
+
+        # if a **kwargs argument is needed, explode
+        if signature.has_kwarg():
+            raise TypeError("Keyword arguments as **kwargs is not supported by RPython")
+
+        # handle keyword arguments
+        num_remainingkwds = 0
+        keywords_w = self.keywords_w
+        kwds_mapping = None
+        if num_kwds:
+            # kwds_mapping maps target indexes in the scope (minus input_argcount)
+            # to positions in the keywords_w list
+            kwds_mapping = [-1] * (co_argcount - input_argcount)
+            # match the keywords given at the call site to the argument names
+            # the called function takes
+            # this function must not take a scope_w, to make the scope not
+            # escape
+            num_remainingkwds = len(keywords)
+            for i, name in enumerate(keywords):
+                # If name was not encoded as a string, it could be None. In that
+                # case, it's definitely not going to be in the signature.
+                if name is None:
+                    continue
+                j = signature.find_argname(name)
+                # if j == -1 nothing happens
+                if j < input_argcount:
+                    # check that no keyword argument conflicts with these.
+                    if j >= 0:
+                        raise ArgErrMultipleValues(name)
+                else:
+                    kwds_mapping[j - input_argcount] = i # map to the right index
+                    num_remainingkwds -= 1
+
+            if num_remainingkwds:
+                if co_argcount == 0:
+                    raise ArgErrCount(num_args, num_kwds, signature, defaults_w, 0)
+                raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
+                                        kwds_mapping, self.keyword_names_w)
+
+        # check for missing arguments and fill them from the kwds,
+        # or with defaults, if available
+        missing = 0
+        if input_argcount < co_argcount:
+            def_first = co_argcount - (0 if defaults_w is None else len(defaults_w))
+            j = 0
+            kwds_index = -1
+            for i in range(input_argcount, co_argcount):
+                if kwds_mapping is not None:
+                    kwds_index = kwds_mapping[j]
+                    j += 1
+                    if kwds_index >= 0:
+                        scope_w[i] = keywords_w[kwds_index]
+                        continue
+                defnum = i - def_first
+                if defnum >= 0:
+                    scope_w[i] = defaults_w[defnum]
+                else:
+                    missing += 1
+            if missing:
+                raise ArgErrCount(num_args, num_kwds, signature, defaults_w, missing)
+
+    def unpack(self):
+        "Return a ([w1,w2...], {'kw':w3...}) pair."
+        kwds_w = dict(zip(self.keywords, self.keywords_w)) if self.keywords else {}
+        return self.positional_args, kwds_w
+
+    def match_signature(self, signature, defaults_w):
+        """Parse args and kwargs according to the signature of a code object,
+        or raise an ArgErr in case of failure.
+        """
+        scopelen = signature.scope_length()
+        scope_w = [None] * scopelen
+        self._match_signature(scope_w, signature, defaults_w)
+        return scope_w
+
+    def unmatch_signature(self, signature, data_w):
+        """kind of inverse of match_signature"""
+        need_cnt = len(self.positional_args)
+        need_kwds = self.keywords or []
+        space = self.space
+        argnames, varargname, kwargname = signature
+        assert kwargname is None
+        cnt = len(argnames)
+        data_args_w = data_w[:cnt]
+        if varargname:
+            data_w_stararg = data_w[cnt]
+            cnt += 1
+        else:
+            data_w_stararg = space.newtuple([])
+        assert len(data_w) == cnt
+
+        unfiltered_kwds_w = {}
+        if len(data_args_w) >= need_cnt:
+            args_w = data_args_w[:need_cnt]
+            for argname, w_arg in zip(argnames[need_cnt:], data_args_w[need_cnt:]):
+                unfiltered_kwds_w[argname] = w_arg
+            assert not space.is_true(data_w_stararg)
+        else:
+            stararg_w = space.unpackiterable(data_w_stararg)
+            args_w = data_args_w + stararg_w
+            assert len(args_w) == need_cnt
+
+        keywords = []
+        keywords_w = []
+        for key in need_kwds:
+            keywords.append(key)
+            keywords_w.append(unfiltered_kwds_w[key])
+
+        return ArgumentsForTranslation(self.space, args_w, keywords, keywords_w)
+
+    @staticmethod
+    def fromshape(space, (shape_cnt, shape_keys, shape_star, shape_stst), data_w):
+        args_w = data_w[:shape_cnt]
+        p = end_keys = shape_cnt + len(shape_keys)
+        if shape_star:
+            w_star = data_w[p]
+            p += 1
+        else:
+            w_star = None
+        if shape_stst:
+            w_starstar = data_w[p]
+            p += 1
+        else:
+            w_starstar = None
+        return ArgumentsForTranslation(space, args_w, list(shape_keys),
+                                       data_w[shape_cnt:end_keys], w_star,
+                                       w_starstar)
+
+    def flatten(self):
+        """ Argument <-> list of w_objects together with "shape" information """
+        shape_cnt, shape_keys, shape_star, shape_stst = self._rawshape()
+        data_w = self.arguments_w + [self.keywords_w[self.keywords.index(key)]
+                                         for key in shape_keys]
+        if shape_star:
+            data_w.append(self.w_stararg)
+        if shape_stst:
+            data_w.append(self.w_starstararg)
+        return (shape_cnt, shape_keys, shape_star, shape_stst), data_w
+
+    def _rawshape(self, nextra=0):
+        shape_cnt = len(self.arguments_w) + nextra       # Number of positional args
+        if self.keywords:
+            shape_keys = self.keywords[:]                # List of keywords (strings)
+            shape_keys.sort()
+        else:
+            shape_keys = []
+        shape_star = self.w_stararg is not None   # Flag: presence of *arg
+        shape_stst = self.w_starstararg is not None # Flag: presence of **kwds
+        return shape_cnt, tuple(shape_keys), shape_star, shape_stst # shape_keys are sorted
+
+
+def rawshape(args, nextra=0):
+    return args._rawshape(nextra)
+
+
+#
+# ArgErr family of exceptions raised in case of argument mismatch.
+# We try to give error messages following CPython's, which are very informative.
+#
+
+class ArgErr(Exception):
+    def getmsg(self):
+        raise NotImplementedError
+
+
+class ArgErrCount(ArgErr):
+    def __init__(self, got_nargs, nkwds, signature,
+                 defaults_w, missing_args):
+        self.signature = signature
+
+        self.num_defaults = 0 if defaults_w is None else len(defaults_w)
+        self.missing_args = missing_args
+        self.num_args = got_nargs
+        self.num_kwds = nkwds
+
+    def getmsg(self):
+        n = self.signature.num_argnames()
+        if n == 0:
+            msg = "takes no arguments (%d given)" % (
+                self.num_args + self.num_kwds)
+        else:
+            defcount = self.num_defaults
+            has_kwarg = self.signature.has_kwarg()
+            num_args = self.num_args
+            num_kwds = self.num_kwds
+            if defcount == 0 and not self.signature.has_vararg():
+                msg1 = "exactly"
+                if not has_kwarg:
+                    num_args += num_kwds
+                    num_kwds = 0
+            elif not self.missing_args:
+                msg1 = "at most"
+            else:
+                msg1 = "at least"
+                has_kwarg = False
+                n -= defcount
+            if n == 1:
+                plural = ""
+            else:
+                plural = "s"
+            if has_kwarg or num_kwds > 0:
+                msg2 = " non-keyword"
+            else:
+                msg2 = ""
+            msg = "takes %s %d%s argument%s (%d given)" % (
+                msg1,
+                n,
+                msg2,
+                plural,
+                num_args)
+        return msg
+
+
+class ArgErrMultipleValues(ArgErr):
+    def __init__(self, argname):
+        self.argname = argname
+
+    def getmsg(self):
+        msg = "got multiple values for keyword argument '%s'" % (
+            self.argname)
+        return msg
+
+
+class ArgErrUnknownKwds(ArgErr):
+    def __init__(self, space, num_remainingkwds, keywords, kwds_mapping,
+                 keyword_names_w):
+        name = ''
+        self.num_kwds = num_remainingkwds
+        if num_remainingkwds == 1:
+            for i in range(len(keywords)):
+                if i not in kwds_mapping:
+                    name = keywords[i]
+                    if name is None:
+                        # We'll assume it's unicode. Encode it.
+                        # Careful, I *think* it should not be possible to
+                        # get an IndexError here but you never know.
+                        try:
+                            if keyword_names_w is None:
+                                raise IndexError
+                            # note: negative-based indexing from the end
+                            w_name = keyword_names_w[i - len(keywords)]
+                        except IndexError:
+                            name = '?'
+                        else:
+                            w_enc = space.wrap(space.sys.defaultencoding)
+                            w_err = space.wrap("replace")
+                            w_name = space.call_method(w_name, "encode", w_enc,
+                                                       w_err)
+                            name = space.str_w(w_name)
+                    break
+        self.kwd_name = name
+
+    def getmsg(self):
+        if self.num_kwds == 1:
+            msg = "got an unexpected keyword argument '%s'" % (
+                self.kwd_name)
+        else:
+            msg = "got %d unexpected keyword arguments" % (
+                self.num_kwds)
+        return msg

File rpython/annotator/bookkeeper.py

 from rpython.annotator.dictdef import DictDef
 from rpython.annotator import description
 from rpython.annotator.signature import annotationoftype
-from rpython.flowspace.argument import ArgumentsForTranslation
+from rpython.annotator.argument import ArgumentsForTranslation, RPythonCallsSpace
 from rpython.rlib.objectmodel import r_dict, Symbolic
 from rpython.tool.algo.unionfind import UnionFind
 from rpython.rtyper.lltypesystem import lltype, llmemory
         return False
     else:
         return True
-
-# for parsing call arguments
-class RPythonCallsSpace(object):
-    """Pseudo Object Space providing almost no real operation.
-    For the Arguments class: if it really needs other operations, it means
-    that the call pattern is too complex for R-Python.
-    """
-    w_tuple = SomeTuple
-    def newtuple(self, items_s):
-        if len(items_s) == 1 and items_s[0] is Ellipsis:
-            res = SomeObject()   # hack to get a SomeObject as the *arg
-            res.from_ellipsis = True
-            return res
-        else:
-            return SomeTuple(items_s)
-
-    def newdict(self):
-        raise CallPatternTooComplex, "'**' argument"
-
-    def unpackiterable(self, s_obj, expected_length=None):
-        if isinstance(s_obj, SomeTuple):
-            if (expected_length is not None and
-                expected_length != len(s_obj.items)):
-                raise ValueError
-            return list(s_obj.items)
-        if (s_obj.__class__ is SomeObject and
-            getattr(s_obj, 'from_ellipsis', False)):    # see newtuple()
-            return [Ellipsis]
-        raise CallPatternTooComplex, "'*' argument must be SomeTuple"
-    fixedview = unpackiterable
-    listview  = unpackiterable
-
-    def is_w(self, one, other):
-        return one is other
-
-    def type(self, item):
-        return type(item)
-
-    def is_true(self, s_tup):
-        assert isinstance(s_tup, SomeTuple)
-        return bool(s_tup.items)
-
-class CallPatternTooComplex(Exception):
-    pass
-
 # get current bookkeeper
 
 def getbookkeeper():

File rpython/annotator/description.py

 from rpython.annotator.signature import enforce_signature_args, enforce_signature_return
 from rpython.flowspace.model import Constant, FunctionGraph
 from rpython.flowspace.bytecode import cpython_code_signature
-from rpython.flowspace.argument import rawshape, ArgErr
+from rpython.annotator.argument import rawshape, ArgErr
 from rpython.tool.sourcetools import valid_identifier, func_with_new_name
 from rpython.tool.pairtype import extendabletype
 

File rpython/annotator/test/test_annrpython.py

 from rpython.rlib.rarithmetic import r_uint, base_int, r_longlong, r_ulonglong
 from rpython.rlib.rarithmetic import r_singlefloat
 from rpython.rlib import objectmodel
-from rpython.flowspace.objspace import FlowObjSpace, FlowingError
+from rpython.flowspace.objspace import build_flow, FlowingError
 
 from rpython.translator.test import snippet
 
 
 
 class TestAnnotateTestCase:
-    def setup_class(cls):
-        cls.space = FlowObjSpace()
-
     def teardown_method(self, meth):
         assert annmodel.s_Bool == annmodel.SomeBool()
 
         except AttributeError:
             pass
         name = func.func_name
-        funcgraph = self.space.build_flow(func)
+        funcgraph = build_flow(func)
         funcgraph.source = inspect.getsource(func)
         return funcgraph
 
         assert isinstance(s, annmodel.SomePBC)
         assert s.const == myobj
 
-    def test_cleanup_protocol(self): 
+    def test_cleanup_protocol(self):
         class Stuff:
             def __init__(self):
                 self.called = False
 
     def test_join_none_and_nonnull(self):
         from rpython.rlib.rstring import assert_str0
-        
+
         def f(i):
             a = str(i)
             a = assert_str0(a)
         class A(object):
             def __iter__(self):
                 return self
-        
+
         def fn():
             return iter(A())
 
                 return True
 
         x = X()
-        
+
         def f(i):
             if i:
                 x1 = x

File rpython/annotator/test/test_argument.py

+# -*- coding: utf-8 -*-
+import py
+from rpython.annotator.argument import ArgumentsForTranslation, rawshape
+from rpython.flowspace.argument import Signature
+
+class DummySpace(object):
+    def newtuple(self, items):
+        return tuple(items)
+
+    def is_true(self, obj):
+        return bool(obj)
+
+    def unpackiterable(self, it):
+        return list(it)
+
+
+def make_arguments_for_translation(space, args_w, keywords_w={},
+                                   w_stararg=None, w_starstararg=None):
+    return ArgumentsForTranslation(space, args_w, keywords_w.keys(),
+                                   keywords_w.values(), w_stararg,
+                                   w_starstararg)
+
+class TestArgumentsForTranslation(object):
+
+    def test_prepend(self):
+        space = DummySpace()
+        args = ArgumentsForTranslation(space, ["0"])
+        args1 = args.prepend("thingy")
+        assert args1 is not args
+        assert args1.arguments_w == ["thingy", "0"]
+        assert args1.keywords is args.keywords
+        assert args1.keywords_w is args.keywords_w
+
+    def test_fixedunpacked(self):
+        space = DummySpace()
+
+        args = ArgumentsForTranslation(space, [], ["k"], [1])
+        py.test.raises(ValueError, args.fixedunpack, 1)
+
+        args = ArgumentsForTranslation(space, ["a", "b"])
+        py.test.raises(ValueError, args.fixedunpack, 0)
+        py.test.raises(ValueError, args.fixedunpack, 1)
+        py.test.raises(ValueError, args.fixedunpack, 3)
+        py.test.raises(ValueError, args.fixedunpack, 4)
+
+        assert args.fixedunpack(2) == ['a', 'b']
+
+    def test_unmatch_signature(self):
+        space = DummySpace()
+        args = make_arguments_for_translation(space, [1,2,3])
+        sig = Signature(['a', 'b', 'c'], None, None)
+        data = args.match_signature(sig, [])
+        new_args = args.unmatch_signature(sig, data)
+        assert args.unpack() == new_args.unpack()
+
+        args = make_arguments_for_translation(space, [1])
+        sig = Signature(['a', 'b', 'c'], None, None)
+        data = args.match_signature(sig, [2, 3])
+        new_args = args.unmatch_signature(sig, data)
+        assert args.unpack() == new_args.unpack()
+
+        args = make_arguments_for_translation(space, [1,2,3,4,5])
+        sig = Signature(['a', 'b', 'c'], 'r', None)
+        data = args.match_signature(sig, [])
+        new_args = args.unmatch_signature(sig, data)
+        assert args.unpack() == new_args.unpack()
+
+        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
+        sig = Signature(['a', 'b', 'c'], None, None)
+        data = args.match_signature(sig, [])
+        new_args = args.unmatch_signature(sig, data)
+        assert args.unpack() == new_args.unpack()
+
+        args = make_arguments_for_translation(space, [1], {'c': 5})
+        sig = Signature(['a', 'b', 'c'], None, None)
+        data = args.match_signature(sig, [2, 3])
+        new_args = args.unmatch_signature(sig, data)
+        assert args.unpack() == new_args.unpack()
+
+        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
+        sig = Signature(['a', 'b', 'c'], None, 'kw')
+        py.test.raises(TypeError, args.match_signature, sig, [2, 3])
+
+    def test_rawshape(self):
+        space = DummySpace()
+        args = make_arguments_for_translation(space, [1,2,3])
+        assert rawshape(args) == (3, (), False, False)
+
+        args = make_arguments_for_translation(space, [1])
+        assert rawshape(args, 2) == (3, (), False, False)
+
+        args = make_arguments_for_translation(space, [1,2,3,4,5])
+        assert rawshape(args) == (5, (), False, False)
+
+        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
+        assert rawshape(args) == (1, ('b', 'c'), False, False)
+
+        args = make_arguments_for_translation(space, [1], {'c': 5})
+        assert rawshape(args) == (1, ('c', ), False, False)
+
+        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
+        assert rawshape(args) == (1, ('c', 'd'), False, False)
+
+        args = make_arguments_for_translation(space, [1,2,3,4,5], {'e': 5, 'd': 7})
+        assert rawshape(args) == (5, ('d', 'e'), False, False)
+
+
+    def test_flatten(self):
+        space = DummySpace()
+        args = make_arguments_for_translation(space, [1,2,3])
+        assert args.flatten() == ((3, (), False, False), [1, 2, 3])
+
+        args = make_arguments_for_translation(space, [1])
+        assert args.flatten() == ((1, (), False, False), [1])
+
+        args = make_arguments_for_translation(space, [1,2,3,4,5])
+        assert args.flatten() == ((5, (), False, False), [1,2,3,4,5])
+
+        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
+        assert args.flatten() == ((1, ('b', 'c'), False, False), [1, 2, 3])
+
+        args = make_arguments_for_translation(space, [1], {'c': 5})
+        assert args.flatten() == ((1, ('c', ), False, False), [1, 5])
+
+        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
+        assert args.flatten() == ((1, ('c', 'd'), False, False), [1, 5, 7])
+
+        args = make_arguments_for_translation(space, [1,2,3,4,5], {'e': 5, 'd': 7})
+        assert args.flatten() == ((5, ('d', 'e'), False, False), [1, 2, 3, 4, 5, 7, 5])
+
+    def test_stararg_flowspace_variable(self):
+        space = DummySpace()
+        var = object()
+        shape = ((2, ('g', ), True, False), [1, 2, 9, var])
+        args = make_arguments_for_translation(space, [1,2], {'g': 9},
+                                       w_stararg=var)
+        assert args.flatten() == shape
+
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+
+    def test_fromshape(self):
+        space = DummySpace()
+        shape = ((3, (), False, False), [1, 2, 3])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((1, (), False, False), [1])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((5, (), False, False), [1,2,3,4,5])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((1, ('b', 'c'), False, False), [1, 2, 3])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((1, ('c', ), False, False), [1, 5])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((1, ('c', 'd'), False, False), [1, 5, 7])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape
+
+        shape = ((5, ('d', 'e'), False, False), [1, 2, 3, 4, 5, 7, 5])
+        args = ArgumentsForTranslation.fromshape(space, *shape)
+        assert args.flatten() == shape

File rpython/flowspace/argument.py

         raise IndexError
 
 
-class ArgumentsForTranslation(object):
-    def __init__(self, space, args_w, keywords=None, keywords_w=None,
-                 w_stararg=None, w_starstararg=None):
+class CallSpec(object):
+    """Represents the arguments passed into a function call, i.e. the
+    `a, b, *c, **d` part in `return func(a, b, *c, **d)`.
+    """
+    def __init__(self, args_w, keywords=None, w_stararg=None,
+            w_starstararg=None):
         self.w_stararg = w_stararg
-        self.w_starstararg = w_starstararg
-        self.combine_has_happened = False
-        self.space = space
+        assert w_starstararg is None, "No **-unpacking in RPython"
         assert isinstance(args_w, list)
         self.arguments_w = args_w
-        self.keywords = keywords
-        self.keywords_w = keywords_w
-        self.keyword_names_w = None
-
-    def __repr__(self):
-        """ NOT_RPYTHON """
-        name = self.__class__.__name__
-        if not self.keywords:
-            return '%s(%s)' % (name, self.arguments_w,)
-        else:
-            return '%s(%s, %s, %s)' % (name, self.arguments_w,
-                                       self.keywords, self.keywords_w)
-
-    def _combine_wrapped(self, w_stararg, w_starstararg):
-        "unpack the *arg and **kwd into arguments_w and keywords_w"
-        if w_stararg is not None:
-            self._combine_starargs_wrapped(w_stararg)
-        if w_starstararg is not None:
-            self._combine_starstarargs_wrapped(w_starstararg)
-
-    def _combine_starargs_wrapped(self, w_stararg):
-        # unpack the * arguments
-        space = self.space
-        args_w = space.unpackiterable(w_stararg)
-        self.arguments_w = self.arguments_w + args_w
-
-    def _combine_starstarargs_wrapped(self, w_starstararg):
-        # unpack the ** arguments
-        space = self.space
-        keywords, values_w = space.view_as_kwargs(w_starstararg)
-        if keywords is not None: # this path also taken for empty dicts
-            if self.keywords is None:
-                self.keywords = keywords
-                self.keywords_w = values_w
-            else:
-                if set(keywords) & set(self.keywords):
-                    raise TypeError("got multiple values for keyword arguments '%s'", set(keywords) & set(self.keywords))
-                self.keywords = self.keywords + keywords
-                self.keywords_w = self.keywords_w + values_w
-            return
-        if space.isinstance_w(w_starstararg, space.w_dict):
-            keys_w = space.unpackiterable(w_starstararg)
-        else:
-            w_keys = space.call_method(w_starstararg, "keys")
-            keys_w = space.unpackiterable(w_keys)
-        keywords_w = [None] * len(keys_w)
-        keywords = [None] * len(keys_w)
-        for i, w_key in enumerate(keys_w):
-            key = space.str_w(w_key)
-            if key in self.keywords:
-                raise TypeError("got multiple values for keyword argument '%s'" % key)
-            keywords[i] = key
-            keywords_w[i] = space.getitem(w_starstararg, w_key)
-        self.keyword_names_w = keys_w
-        if self.keywords is None:
-            self.keywords = keywords
-            self.keywords_w = keywords_w
-        else:
-            self.keywords = self.keywords + keywords
-            self.keywords_w = self.keywords_w + keywords_w
-
-    def fixedunpack(self, argcount):
-        """The simplest argument parsing: get the 'argcount' arguments,
-        or raise a real ValueError if the length is wrong."""
-        if self.keywords:
-            raise ValueError("no keyword arguments expected")
-        if len(self.arguments_w) > argcount:
-            raise ValueError("too many arguments (%d expected)" % argcount)
-        elif len(self.arguments_w) < argcount:
-            raise ValueError("not enough arguments (%d expected)" % argcount)
-        return self.arguments_w
-
-    def combine_if_necessary(self):
-        if self.combine_has_happened:
-            return
-        self._combine_wrapped(self.w_stararg, self.w_starstararg)
-        self.combine_has_happened = True
-
-    def prepend(self, w_firstarg): # used often
-        "Return a new Arguments with a new argument inserted first."
-        return ArgumentsForTranslation(self.space, [w_firstarg] + self.arguments_w,
-                                       self.keywords, self.keywords_w, self.w_stararg,
-                                       self.w_starstararg)
-
-    def copy(self):
-        return ArgumentsForTranslation(self.space, self.arguments_w,
-                                       self.keywords, self.keywords_w, self.w_stararg,
-                                       self.w_starstararg)
-
-    def _match_signature(self, scope_w, signature, defaults_w=None):
-        """Parse args and kwargs according to the signature of a code object,
-        or raise an ArgErr in case of failure.
-        """
-        #   args_w = list of the normal actual parameters, wrapped
-        #   scope_w = resulting list of wrapped values
-        #
-        self.combine_if_necessary()
-        co_argcount = signature.num_argnames() # expected formal arguments, without */**
-
-        args_w = self.arguments_w
-        num_args = len(args_w)
-        keywords = self.keywords or []
-        num_kwds = len(keywords)
-
-        # put as many positional input arguments into place as available
-        take = min(num_args, co_argcount)
-        scope_w[:take] = args_w[:take]
-        input_argcount = take
-
-        # collect extra positional arguments into the *vararg
-        if signature.has_vararg():
-            if num_args > co_argcount:
-                starargs_w = args_w[co_argcount:]
-            else:
-                starargs_w = []
-            scope_w[co_argcount] = self.space.newtuple(starargs_w)
-        elif num_args > co_argcount:
-            raise ArgErrCount(num_args, num_kwds, signature, defaults_w, 0)
-
-        # if a **kwargs argument is needed, explode
-        if signature.has_kwarg():
-            raise TypeError("Keyword arguments as **kwargs is not supported by RPython")
-
-        # handle keyword arguments
-        num_remainingkwds = 0
-        keywords_w = self.keywords_w
-        kwds_mapping = None
-        if num_kwds:
-            # kwds_mapping maps target indexes in the scope (minus input_argcount)
-            # to positions in the keywords_w list
-            kwds_mapping = [-1] * (co_argcount - input_argcount)
-            # match the keywords given at the call site to the argument names
-            # the called function takes
-            # this function must not take a scope_w, to make the scope not
-            # escape
-            num_remainingkwds = len(keywords)
-            for i, name in enumerate(keywords):
-                # If name was not encoded as a string, it could be None. In that
-                # case, it's definitely not going to be in the signature.
-                if name is None:
-                    continue
-                j = signature.find_argname(name)
-                # if j == -1 nothing happens
-                if j < input_argcount:
-                    # check that no keyword argument conflicts with these.
-                    if j >= 0:
-                        raise ArgErrMultipleValues(name)
-                else:
-                    kwds_mapping[j - input_argcount] = i # map to the right index
-                    num_remainingkwds -= 1
-
-            if num_remainingkwds:
-                if co_argcount == 0:
-                    raise ArgErrCount(num_args, num_kwds, signature, defaults_w, 0)
-                raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
-                                        kwds_mapping, self.keyword_names_w)
-
-        # check for missing arguments and fill them from the kwds,
-        # or with defaults, if available
-        missing = 0
-        if input_argcount < co_argcount:
-            def_first = co_argcount - (0 if defaults_w is None else len(defaults_w))
-            j = 0
-            kwds_index = -1
-            for i in range(input_argcount, co_argcount):
-                if kwds_mapping is not None:
-                    kwds_index = kwds_mapping[j]
-                    j += 1
-                    if kwds_index >= 0:
-                        scope_w[i] = keywords_w[kwds_index]
-                        continue
-                defnum = i - def_first
-                if defnum >= 0:
-                    scope_w[i] = defaults_w[defnum]
-                else:
-                    missing += 1
-            if missing:
-                raise ArgErrCount(num_args, num_kwds, signature, defaults_w, missing)
-
-    def unpack(self):
-        "Return a ([w1,w2...], {'kw':w3...}) pair."
-        self.combine_if_necessary()
-        kwds_w = {}
-        if self.keywords:
-            for i in range(len(self.keywords)):
-                kwds_w[self.keywords[i]] = self.keywords_w[i]
-        return self.arguments_w, kwds_w
-
-    def match_signature(self, signature, defaults_w):
-        """Parse args and kwargs according to the signature of a code object,
-        or raise an ArgErr in case of failure.
-        """
-        scopelen = signature.scope_length()
-        scope_w = [None] * scopelen
-        self._match_signature(scope_w, signature, defaults_w)
-        return scope_w
-
-    def unmatch_signature(self, signature, data_w):
-        """kind of inverse of match_signature"""
-        args_w, kwds_w = self.unpack()
-        need_cnt = len(args_w)
-        need_kwds = kwds_w.keys()
-        space = self.space
-        argnames, varargname, kwargname = signature
-        cnt = len(argnames)
-        data_args_w = data_w[:cnt]
-        if varargname:
-            data_w_stararg = data_w[cnt]
-            cnt += 1
-        else:
-            data_w_stararg = space.newtuple([])
-
-        unfiltered_kwds_w = {}
-        if kwargname:
-            data_w_starargarg = data_w[cnt]
-            for w_key in space.unpackiterable(data_w_starargarg):
-                key = space.str_w(w_key)
-                w_value = space.getitem(data_w_starargarg, w_key)
-                unfiltered_kwds_w[key] = w_value
-            cnt += 1
-        assert len(data_w) == cnt
-
-        ndata_args_w = len(data_args_w)
-        if ndata_args_w >= need_cnt:
-            args_w = data_args_w[:need_cnt]
-            for argname, w_arg in zip(argnames[need_cnt:], data_args_w[need_cnt:]):
-                unfiltered_kwds_w[argname] = w_arg
-            assert not space.is_true(data_w_stararg)
-        else:
-            stararg_w = space.unpackiterable(data_w_stararg)
-            datalen = len(data_args_w)
-            args_w = [None] * (datalen + len(stararg_w))
-            for i in range(0, datalen):
-                args_w[i] = data_args_w[i]
-            for i in range(0, len(stararg_w)):
-                args_w[i + datalen] = stararg_w[i]
-            assert len(args_w) == need_cnt
-
-        keywords = []
-        keywords_w = []
-        for key in need_kwds:
-            keywords.append(key)
-            keywords_w.append(unfiltered_kwds_w[key])
-
-        return ArgumentsForTranslation(self.space, args_w, keywords, keywords_w)
-
-    @staticmethod
-    def fromshape(space, (shape_cnt, shape_keys, shape_star, shape_stst), data_w):
-        args_w = data_w[:shape_cnt]
-        p = end_keys = shape_cnt + len(shape_keys)
-        if shape_star:
-            w_star = data_w[p]
-            p += 1
-        else:
-            w_star = None
-        if shape_stst:
-            w_starstar = data_w[p]
-            p += 1
-        else:
-            w_starstar = None
-        return ArgumentsForTranslation(space, args_w, list(shape_keys),
-                                       data_w[shape_cnt:end_keys], w_star,
-                                       w_starstar)
+        self.keywords = keywords or {}
 
     def flatten(self):
         """ Argument <-> list of w_objects together with "shape" information """
-        shape_cnt, shape_keys, shape_star, shape_stst = self._rawshape()
-        data_w = self.arguments_w + [self.keywords_w[self.keywords.index(key)]
-                                         for key in shape_keys]
+        shape_cnt  = len(self.arguments_w)    # Number of positional args
+        shape_keys = tuple(sorted(self.keywords))
+        shape_star = self.w_stararg is not None   # Flag: presence of *arg
+        shape_stst = False # Flag: presence of **kwds
+        data_w = self.arguments_w + [self.keywords[key] for key in shape_keys]
         if shape_star:
             data_w.append(self.w_stararg)
-        if shape_stst:
-            data_w.append(self.w_starstararg)
         return (shape_cnt, shape_keys, shape_star, shape_stst), data_w
-
-    def _rawshape(self, nextra=0):
-        assert not self.combine_has_happened
-        shape_cnt = len(self.arguments_w) + nextra        # Number of positional args
-        if self.keywords:
-            shape_keys = self.keywords[:]                # List of keywords (strings)
-            shape_keys.sort()
-        else:
-            shape_keys = []
-        shape_star = self.w_stararg is not None   # Flag: presence of *arg
-        shape_stst = self.w_starstararg is not None # Flag: presence of **kwds
-        return shape_cnt, tuple(shape_keys), shape_star, shape_stst # shape_keys are sorted
-
-
-def rawshape(args, nextra=0):
-    return args._rawshape(nextra)
-
-
-#
-# ArgErr family of exceptions raised in case of argument mismatch.
-# We try to give error messages following CPython's, which are very informative.
-#
-
-class ArgErr(Exception):
-    def getmsg(self):
-        raise NotImplementedError
-
-
-class ArgErrCount(ArgErr):
-    def __init__(self, got_nargs, nkwds, signature,
-                 defaults_w, missing_args):
-        self.signature = signature
-
-        self.num_defaults = 0 if defaults_w is None else len(defaults_w)
-        self.missing_args = missing_args
-        self.num_args = got_nargs
-        self.num_kwds = nkwds
-
-    def getmsg(self):
-        n = self.signature.num_argnames()
-        if n == 0:
-            msg = "takes no arguments (%d given)" % (
-                self.num_args + self.num_kwds)
-        else:
-            defcount = self.num_defaults
-            has_kwarg = self.signature.has_kwarg()
-            num_args = self.num_args
-            num_kwds = self.num_kwds
-            if defcount == 0 and not self.signature.has_vararg():
-                msg1 = "exactly"
-                if not has_kwarg:
-                    num_args += num_kwds
-                    num_kwds = 0
-            elif not self.missing_args:
-                msg1 = "at most"
-            else:
-                msg1 = "at least"
-                has_kwarg = False
-                n -= defcount
-            if n == 1:
-                plural = ""
-            else:
-                plural = "s"
-            if has_kwarg or num_kwds > 0:
-                msg2 = " non-keyword"
-            else:
-                msg2 = ""
-            msg = "takes %s %d%s argument%s (%d given)" % (
-                msg1,
-                n,
-                msg2,
-                plural,
-                num_args)
-        return msg
-
-
-class ArgErrMultipleValues(ArgErr):
-    def __init__(self, argname):
-        self.argname = argname
-
-    def getmsg(self):
-        msg = "got multiple values for keyword argument '%s'" % (
-            self.argname)
-        return msg
-
-
-class ArgErrUnknownKwds(ArgErr):
-    def __init__(self, space, num_remainingkwds, keywords, kwds_mapping,
-                 keyword_names_w):
-        name = ''
-        self.num_kwds = num_remainingkwds
-        if num_remainingkwds == 1:
-            for i in range(len(keywords)):
-                if i not in kwds_mapping:
-                    name = keywords[i]
-                    if name is None:
-                        # We'll assume it's unicode. Encode it.
-                        # Careful, I *think* it should not be possible to
-                        # get an IndexError here but you never know.
-                        try:
-                            if keyword_names_w is None:
-                                raise IndexError
-                            # note: negative-based indexing from the end
-                            w_name = keyword_names_w[i - len(keywords)]
-                        except IndexError:
-                            name = '?'
-                        else:
-                            w_enc = space.wrap(space.sys.defaultencoding)
-                            w_err = space.wrap("replace")
-                            w_name = space.call_method(w_name, "encode", w_enc,
-                                                       w_err)
-                            name = space.str_w(w_name)
-                    break
-        self.kwd_name = name
-
-    def getmsg(self):
-        if self.num_kwds == 1:
-            msg = "got an unexpected keyword argument '%s'" % (
-                self.kwd_name)
-        else:
-            msg = "got %d unexpected keyword arguments" % (
-                self.num_kwds)
-        return msg

File rpython/flowspace/bytecode.py

                      code, consts, names, varnames, filename,
                      name, firstlineno, lnotab, freevars):
         """Initialize a new code object"""
-        self.co_name = name
         assert nlocals >= 0
         self.co_argcount = argcount
         self.co_nlocals = nlocals

File rpython/flowspace/flowcontext.py

 
 from rpython.tool.error import source_lines
 from rpython.tool.stdlib_opcode import host_bytecode_spec
-from rpython.flowspace.argument import ArgumentsForTranslation
+from rpython.flowspace.argument import CallSpec
 from rpython.flowspace.model import (Constant, Variable, Block, Link,
-    c_last_exception)
+    c_last_exception, SpaceOperation)
 from rpython.flowspace.framestate import (FrameState, recursively_unflatten,
     recursively_flatten)
 from rpython.flowspace.specialcase import (rpython_print_item,
     rpython_print_newline)
+from rpython.flowspace.operation import implicit_exceptions
 
 
 class FlowingError(Exception):
             "peek past the bottom of the stack")
         return self.locals_stack_w[index]
 
-    def pushrevvalues(self, n, values_w): # n should be len(values_w)
-        assert len(values_w) == n
-        for i in range(n - 1, -1, -1):
-            self.pushvalue(values_w[i])
-
     def settopvalue(self, w_object, index_from_top=0):
         index = self.valuestackdepth + ~index_from_top
         assert index >= self.pycode.co_nlocals, (
         values_w.reverse()
         return values_w
 
-    def peekvalues(self, n):
-        values_w = [None] * n
-        base = self.valuestackdepth - n
-        while True:
-            n -= 1
-            if n < 0:
-                break
-            values_w[n] = self.locals_stack_w[base + n]
-        return values_w
-
     def dropvalues(self, n):
         finaldepth = self.valuestackdepth - n
         for n in range(finaldepth, self.valuestackdepth):
     def guessbool(self, w_condition, **kwds):
         return self.recorder.guessbool(self, w_condition, **kwds)
 
+    def do_operation(self, name, *args_w):
+        spaceop = SpaceOperation(name, args_w, Variable())
+        spaceop.offset = self.last_instr
+        self.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(implicit_exceptions.get(name))
+        return w_result
+
     def handle_implicit_exceptions(self, exceptions):
         """
         Catch possible exceptions implicitly.
     def YIELD_VALUE(self, _, next_instr):
         assert self.pycode.is_generator
         w_result = self.popvalue()
-        self.space.do_operation('yield', w_result)
+        self.do_operation('yield', w_result)
         # XXX yield expressions not supported. This will blow up if the value
         # isn't popped straightaway.
         self.pushvalue(None)
 
     def PRINT_ITEM(self, oparg, next_instr):
         w_item = self.popvalue()
-        w_s = self.space.do_operation('str', w_item)
+        w_s = self.do_operation('str', w_item)
         self.space.appcall(rpython_print_item, w_s)
 
     def PRINT_NEWLINE(self, oparg, next_instr):
         self.pushvalue(last_val)
 
     def call_function(self, oparg, w_star=None, w_starstar=None):
+        if w_starstar is not None:
+            raise FlowingError(self, "Dict-unpacking is not RPython")
         n_arguments = oparg & 0xff
         n_keywords = (oparg >> 8) & 0xff
-        if n_keywords:
-            keywords = [None] * n_keywords
-            keywords_w = [None] * n_keywords
-            while True:
-                n_keywords -= 1
-                if n_keywords < 0:
-                    break
-                w_value = self.popvalue()
-                w_key = self.popvalue()
-                key = self.space.str_w(w_key)
-                keywords[n_keywords] = key
-                keywords_w[n_keywords] = w_value
-        else:
-            keywords = None
-            keywords_w = None
+        keywords = {}
+        for _ in range(n_keywords):
+            w_value = self.popvalue()
+            w_key = self.popvalue()
+            key = self.space.str_w(w_key)
+            keywords[key] = w_value
         arguments = self.popvalues(n_arguments)
-        args = ArgumentsForTranslation(self.space, arguments, keywords,
-                keywords_w, w_star, w_starstar)
+        args = CallSpec(arguments, keywords, w_star, w_starstar)
         w_function = self.popvalue()
         w_result = self.space.call_args(w_function, args)
         self.pushvalue(w_result)
 
     def UNPACK_SEQUENCE(self, itemcount, next_instr):
         w_iterable = self.popvalue()
-        items = self.space.unpackiterable(w_iterable, itemcount)
-        self.pushrevvalues(itemcount, items)
+        items = self.space.unpack_sequence(w_iterable, itemcount)
+        for w_item in reversed(items):
+            self.pushvalue(w_item)
 
     def slice(self, w_start, w_end):
         w_obj = self.popvalue()

File rpython/flowspace/objspace.py

 import types
 from inspect import CO_NEWLOCALS
 
-from rpython.flowspace.argument import ArgumentsForTranslation
+from rpython.flowspace.argument import CallSpec
 from rpython.flowspace.model import (Constant, Variable, WrapException,
-    UnwrapException, checkgraph, SpaceOperation)
+    UnwrapException, checkgraph)
 from rpython.flowspace.bytecode import HostCode
 from rpython.flowspace import operation
 from rpython.flowspace.flowcontext import (FlowSpaceFrame, fixeggblocks,
     # objects which should keep their SomeObjectness
     not_really_const = NOT_REALLY_CONST
 
+    def build_flow(self, func):
+        return build_flow(func, self)
+
     def is_w(self, w_one, w_two):
         return self.is_true(self.is_(w_one, w_two))
 
     id  = None     # real version added by add_operations()
 
     def newdict(self, module="ignored"):
-        return self.do_operation('newdict')
+        return self.frame.do_operation('newdict')
 
     def newtuple(self, args_w):
-        try:
-            content = [self.unwrap(w_arg) for w_arg in args_w]
-        except UnwrapException:
-            return self.do_operation('newtuple', *args_w)
+        if all(isinstance(w_arg, Constant) for w_arg in args_w):
+            content = [w_arg.value for w_arg in args_w]
+            return Constant(tuple(content))
         else:
-            return Constant(tuple(content))
+            return self.frame.do_operation('newtuple', *args_w)
 
     def newlist(self, args_w, sizehint=None):
-        return self.do_operation('newlist', *args_w)
+        return self.frame.do_operation('newlist', *args_w)
 
     def newslice(self, w_start, w_stop, w_step):
-        return self.do_operation('newslice', w_start, w_stop, w_step)
+        return self.frame.do_operation('newslice', w_start, w_stop, w_step)
 
     def newbool(self, b):
         if b:
             w_type = w_instclass
         return FSException(w_type, w_value)
 
-    def build_flow(self, func):
-        """
-        """
-        _assert_rpythonic(func)
-        code = HostCode._from_code(func.func_code)
-        if (code.is_generator and
-                not hasattr(func, '_generator_next_method_of_')):
-            graph = PyGraph(func, code)
-            block = graph.startblock
-            for name, w_value in zip(code.co_varnames, block.framestate.mergeable):
-                if isinstance(w_value, Variable):
-                    w_value.rename(name)
-            return bootstrap_generator(graph)
-        graph = PyGraph(func, code)
-        frame = self.frame = FlowSpaceFrame(self, graph, code)
-        frame.build_flow()
-        fixeggblocks(graph)
-        checkgraph(graph)
-        if code.is_generator:
-            tweak_generator_graph(graph)
-        return graph
+    def unpackiterable(self, w_iterable):
+        if isinstance(w_iterable, Constant):
+            l = w_iterable.value
+            return [self.wrap(x) for x in l]
+        else:
+            raise UnwrapException("cannot unpack a Variable iterable ")
 
-    def unpackiterable(self, w_iterable, expected_length=None):
-        if not isinstance(w_iterable, Variable):
+    def unpack_sequence(self, w_iterable, expected_length):
+        if isinstance(w_iterable, Constant):
             l = list(self.unwrap(w_iterable))
-            if expected_length is not None and len(l) != expected_length:
+            if len(l) != expected_length:
                 raise ValueError
             return [self.wrap(x) for x in l]
-        elif expected_length is None:
-            raise UnwrapException("cannot unpack a Variable iterable "
-                                    "without knowing its length")
         else:
             w_len = self.len(w_iterable)
             w_correct = self.eq(w_len, self.wrap(expected_length))
             if not self.is_true(w_correct):
                 e = self.exc_from_raise(self.w_ValueError, self.w_None)
                 raise e
-            return [self.do_operation('getitem', w_iterable, self.wrap(i))
+            return [self.frame.do_operation('getitem', w_iterable, self.wrap(i))
                         for i in range(expected_length)]
 
     # ____________________________________________________________
-    def do_operation(self, name, *args_w):
-        spaceop = SpaceOperation(name, args_w, Variable())
-        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.frame.handle_implicit_exceptions(
-                operation.implicit_exceptions.get(name))
-        return w_result
-
     def not_(self, w_obj):
         return self.wrap(not self.is_true(w_obj))
 
             pass
         else:
             return bool(obj)
-        w_truthvalue = self.do_operation('is_true', w_obj)
+        w_truthvalue = self.frame.do_operation('is_true', w_obj)
         return self.frame.guessbool(w_truthvalue)
 
     def iter(self, w_iterable):
-        try:
-            iterable = self.unwrap(w_iterable)
-        except UnwrapException:
-            pass
-        else:
+        if isinstance(w_iterable, Constant):
+            iterable = w_iterable.value
             if isinstance(iterable, unrolling_iterable):
                 return self.wrap(iterable.get_unroller())
-        w_iter = self.do_operation("iter", w_iterable)
+        w_iter = self.frame.do_operation("iter", w_iterable)
         return w_iter
 
     def next(self, w_iter):
         frame = self.frame
-        try:
-            it = self.unwrap(w_iter)
-        except UnwrapException:
-            pass
-        else:
+        if isinstance(w_iter, Constant):
+            it = w_iter.value
             if isinstance(it, _unroller):
                 try:
                     v, next_unroller = it.step()
                 else:
                     frame.replace_in_stack(it, next_unroller)
                     return self.wrap(v)
-        w_item = self.do_operation("next", w_iter)
+        w_item = frame.do_operation("next", w_iter)
         frame.handle_implicit_exceptions([StopIteration, RuntimeError])
         return w_item
 
         if w_obj is self.frame.w_globals:
             raise FlowingError(self.frame,
                     "Attempting to modify global variable  %r." % (w_key))
-        return self.do_operation_with_implicit_exceptions('setitem', w_obj,
-                                                          w_key, w_val)
+        return self.frame.do_operation_with_implicit_exceptions('setitem',
+                w_obj, w_key, w_val)
 
     def setitem_str(self, w_obj, key, w_value):
         return self.setitem(w_obj, self.wrap(key), w_value)
         if w_obj in self.not_really_const:
             const_w = self.not_really_const[w_obj]
             if w_name not in const_w:
-                return self.do_operation_with_implicit_exceptions('getattr',
+                return self.frame.do_operation_with_implicit_exceptions('getattr',
                                                                 w_obj, w_name)
         try:
             obj = self.unwrap_for_computation(w_obj)
                 return self.wrap(result)
             except WrapException:
                 pass
-        return self.do_operation_with_implicit_exceptions('getattr',
+        return self.frame.do_operation_with_implicit_exceptions('getattr',
                 w_obj, w_name)
 
     def isinstance_w(self, w_obj, w_type):
         if w_module in self.not_really_const:
             const_w = self.not_really_const[w_obj]
             if w_name not in const_w:
-                return self.do_operation_with_implicit_exceptions('getattr',
+                return self.frame.do_operation_with_implicit_exceptions('getattr',
                                                                 w_obj, w_name)
         try:
             return self.wrap(getattr(w_module.value, w_name.value))
         return self.call_function(w_meth, *arg_w)
 
     def call_function(self, w_func, *args_w):
-        args = ArgumentsForTranslation(self, list(args_w))
+        args = CallSpec(list(args_w))
         return self.call_args(w_func, args)
 
     def appcall(self, func, *args_w):
         """Call an app-level RPython function directly"""
         w_func = self.wrap(func)
-        return self.do_operation('simple_call', w_func, *args_w)
+        return self.frame.do_operation('simple_call', w_func, *args_w)
 
     def call_args(self, w_callable, args):
-        try:
-            fn = self.unwrap(w_callable)
+        if isinstance(w_callable, Constant):
+            fn = w_callable.value
             if hasattr(fn, "_flowspace_rewrite_directly_as_"):
                 fn = fn._flowspace_rewrite_directly_as_
                 w_callable = self.wrap(fn)
-            sc = self.specialcases[fn]   # TypeError if 'fn' not hashable
-        except (UnwrapException, KeyError, TypeError):
-            pass
+            try:
+                sc = self.specialcases[fn]   # TypeError if 'fn' not hashable
+            except (KeyError, TypeError):
+                pass
+            else:
+                assert args.keywords == {}, "should not call %r with keyword arguments" % (fn,)
+                if args.w_stararg is not None:
+                    args_w = args.arguments_w + self.unpackiterable(args.w_stararg)
+                else:
+                    args_w = args.arguments_w
+                return sc(self, fn, args_w)
+
+        if args.keywords or isinstance(args.w_stararg, Variable):
+            shape, args_w = args.flatten()
+            w_res = self.frame.do_operation('call_args', w_callable,
+                    Constant(shape), *args_w)
         else:
-            return sc(self, fn, args)
-
-        try:
-            args_w, kwds_w = args.copy().unpack()
-        except UnwrapException:
-            args_w, kwds_w = '?', '?'
-        # NOTE: annrpython needs to know about the following two operations!
-        if not kwds_w:
-            # simple case
-            w_res = self.do_operation('simple_call', w_callable, *args_w)
-        else:
-            # general case
-            shape, args_w = args.flatten()
-            w_res = self.do_operation('call_args', w_callable, Constant(shape),
-                                      *args_w)
+            if args.w_stararg is not None:
+                args_w = args.arguments_w + self.unpackiterable(args.w_stararg)
+            else:
+                args_w = args.arguments_w
+            w_res = self.frame.do_operation('simple_call', w_callable, *args_w)
 
         # maybe the call has generated an exception (any one)
         # but, let's say, not if we are calling a built-in class or function
                             # type cannot sanely appear in flow graph,
                             # store operation with variable result instead
                             pass
-        w_result = self.do_operation_with_implicit_exceptions(name, *args_w)
+        w_result = self.frame.do_operation_with_implicit_exceptions(name, *args_w)
         return w_result
 
     setattr(FlowObjSpace, name, generic_operator)
 
-
 for (name, symbol, arity, specialnames) in operation.MethodTable:
     make_op(name, arity)
+
+
+def build_flow(func, space=FlowObjSpace()):
+    """
+    Create the flow graph for the function.
+    """
+    _assert_rpythonic(func)
+    code = HostCode._from_code(func.func_code)
+    if (code.is_generator and
+            not hasattr(func, '_generator_next_method_of_')):
+        graph = PyGraph(func, code)
+        block = graph.startblock
+        for name, w_value in zip(code.co_varnames, block.framestate.mergeable):
+            if isinstance(w_value, Variable):
+                w_value.rename(name)
+        return bootstrap_generator(graph)
+    graph = PyGraph(func, code)
+    frame = space.frame = FlowSpaceFrame(space, graph, code)
+    frame.build_flow()
+    fixeggblocks(graph)
+    checkgraph(graph)
+    if code.is_generator:
+        tweak_generator_graph(graph)
+    return graph

File rpython/flowspace/specialcase.py

 from rpython.rlib.rarithmetic import r_uint
 from rpython.rlib.objectmodel import we_are_translated
 
-def sc_import(space, fn, args):
-    args_w, kwds_w = args.unpack()
-    assert kwds_w == {}, "should not call %r with keyword arguments" % (fn,)
+def sc_import(space, fn, args_w):
     assert len(args_w) > 0 and len(args_w) <= 5, 'import needs 1 to 5 arguments'
     args = [space.unwrap(arg) for arg in args_w]
     return space.import_name(*args)
 
-def sc_operator(space, fn, args):
-    args_w, kwds_w = args.unpack()
-    assert kwds_w == {}, "should not call %r with keyword arguments" % (fn,)
+def sc_operator(space, fn, args_w):
     opname = OperationName[fn]
     if len(args_w) != Arity[opname]:
         if opname == 'pow' and len(args_w) == 2:
             args_w = args_w + [Constant(None)]
         elif opname == 'getattr' and len(args_w) == 3:
-            return space.do_operation('simple_call', Constant(getattr), *args_w)
+            return space.frame.do_operation('simple_call', Constant(getattr), *args_w)
         else:
             raise Exception("should call %r with exactly %d arguments" % (
                 fn, Arity[opname]))
 
 # _________________________________________________________________________
 
-def sc_r_uint(space, r_uint, args):
+def sc_r_uint(space, r_uint, args_w):
     # special case to constant-fold r_uint(32-bit-constant)
     # (normally, the 32-bit constant is a long, and is not allowed to
     # show up in the flow graphs at all)
-    args_w, kwds_w = args.unpack()
-    assert not kwds_w
     [w_value] = args_w
     if isinstance(w_value, Constant):
         return Constant(r_uint(w_value.value))
-    return space.do_operation('simple_call', space.wrap(r_uint), w_value)
+    return space.frame.do_operation('simple_call', space.wrap(r_uint), w_value)
 
-def sc_we_are_translated(space, we_are_translated, args):
+def sc_we_are_translated(space, we_are_translated, args_w):
     return Constant(True)
 
 def sc_locals(space, locals, args):

File rpython/flowspace/test/test_argument.py

 # -*- coding: utf-8 -*-
 import py
-from rpython.flowspace.argument import (ArgumentsForTranslation, rawshape,
-    Signature)
+from rpython.flowspace.argument import Signature
 
 
 class TestSignature(object):
         assert x == ["a", "b", "c"]
         assert y == "d"
         assert z == "e"
-
-class dummy_wrapped_dict(dict):
-    def __nonzero__(self):
-        raise NotImplementedError
-
-class kwargsdict(dict):
-    pass
-
-class DummySpace(object):
-    def newtuple(self, items):
-        return tuple(items)
-
-    def is_true(self, obj):
-        if isinstance(obj, dummy_wrapped_dict):
-            return bool(dict(obj))
-        return bool(obj)
-
-    def fixedview(self, it):
-        return list(it)
-
-    def listview(self, it):
-        return list(it)
-
-    def unpackiterable(self, it):
-        return list(it)
-
-    def view_as_kwargs(self, x):
-        if len(x) == 0:
-            return [], []
-        return None, None
-
-    def newdict(self):
-        return {}
-
-    def newlist(self, l=[]):
-        return l
-
-    def setitem(self, obj, key, value):
-        obj[key] = value
-
-    def getitem(self, obj, key):
-        return obj[key]
-
-    def wrap(self, obj):
-        return obj
-
-    def str_w(self, s):
-        return str(s)
-
-    def len(self, x):
-        return len(x)
-
-    def int_w(self, x):
-        return x
-
-    def eq_w(self, x, y):
-        return x == y
-
-    def isinstance(self, obj, cls):
-        return isinstance(obj, cls)
-    isinstance_w = isinstance
-
-    def exception_match(self, w_type1, w_type2):
-        return issubclass(w_type1, w_type2)
-
-    def call_method(self, obj, name, *args):
-        method = getattr(obj, name)
-        return method(*args)
-
-    def type(self, obj):
-        class Type:
-            def getname(self, space, default='?'):
-                return type(obj).__name__
-        return Type()
-
-
-    w_TypeError = TypeError
-    w_AttributeError = AttributeError
-    w_UnicodeEncodeError = UnicodeEncodeError
-    w_dict = dict
-    w_str = str
-
-def make_arguments_for_translation(space, args_w, keywords_w={},
-                                   w_stararg=None, w_starstararg=None):
-    return ArgumentsForTranslation(space, args_w, keywords_w.keys(),
-                                   keywords_w.values(), w_stararg,
-                                   w_starstararg)
-
-class TestArgumentsForTranslation(object):
-
-    def test_prepend(self):
-        space = DummySpace()
-        args = ArgumentsForTranslation(space, ["0"])
-        args1 = args.prepend("thingy")
-        assert args1 is not args
-        assert args1.arguments_w == ["thingy", "0"]
-        assert args1.keywords is args.keywords
-        assert args1.keywords_w is args.keywords_w
-
-    def test_fixedunpacked(self):
-        space = DummySpace()
-
-        args = ArgumentsForTranslation(space, [], ["k"], [1])
-        py.test.raises(ValueError, args.fixedunpack, 1)
-
-        args = ArgumentsForTranslation(space, ["a", "b"])
-        py.test.raises(ValueError, args.fixedunpack, 0)
-        py.test.raises(ValueError, args.fixedunpack, 1)
-        py.test.raises(ValueError, args.fixedunpack, 3)
-        py.test.raises(ValueError, args.fixedunpack, 4)
-
-        assert args.fixedunpack(2) == ['a', 'b']
-
-    def test_unmatch_signature(self):
-        space = DummySpace()
-        args = make_arguments_for_translation(space, [1,2,3])
-        sig = Signature(['a', 'b', 'c'], None, None)
-        data = args.match_signature(sig, [])
-        new_args = args.unmatch_signature(sig, data)
-        assert args.unpack() == new_args.unpack()
-
-        args = make_arguments_for_translation(space, [1])
-        sig = Signature(['a', 'b', 'c'], None, None)
-        data = args.match_signature(sig, [2, 3])
-        new_args = args.unmatch_signature(sig, data)
-        assert args.unpack() == new_args.unpack()
-
-        args = make_arguments_for_translation(space, [1,2,3,4,5])
-        sig = Signature(['a', 'b', 'c'], 'r', None)
-        data = args.match_signature(sig, [])
-        new_args = args.unmatch_signature(sig, data)
-        assert args.unpack() == new_args.unpack()
-
-        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
-        sig = Signature(['a', 'b', 'c'], None, None)
-        data = args.match_signature(sig, [])
-        new_args = args.unmatch_signature(sig, data)
-        assert args.unpack() == new_args.unpack()
-
-        args = make_arguments_for_translation(space, [1], {'c': 5})
-        sig = Signature(['a', 'b', 'c'], None, None)
-        data = args.match_signature(sig, [2, 3])
-        new_args = args.unmatch_signature(sig, data)
-        assert args.unpack() == new_args.unpack()
-
-        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
-        sig = Signature(['a', 'b', 'c'], None, 'kw')
-        py.test.raises(TypeError, args.match_signature, sig, [2, 3])
-
-    def test_rawshape(self):
-        space = DummySpace()
-        args = make_arguments_for_translation(space, [1,2,3])
-        assert rawshape(args) == (3, (), False, False)
-
-        args = make_arguments_for_translation(space, [1])
-        assert rawshape(args, 2) == (3, (), False, False)
-
-        args = make_arguments_for_translation(space, [1,2,3,4,5])
-        assert rawshape(args) == (5, (), False, False)
-
-        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
-        assert rawshape(args) == (1, ('b', 'c'), False, False)
-
-        args = make_arguments_for_translation(space, [1], {'c': 5})
-        assert rawshape(args) == (1, ('c', ), False, False)
-
-        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
-        assert rawshape(args) == (1, ('c', 'd'), False, False)
-
-        args = make_arguments_for_translation(space, [1,2,3,4,5], {'e': 5, 'd': 7})
-        assert rawshape(args) == (5, ('d', 'e'), False, False)
-
-        args = make_arguments_for_translation(space, [], {},
-                                       w_stararg=[1],
-                                       w_starstararg={'c': 5, 'd': 7})
-        assert rawshape(args) == (0, (), True, True)
-
-        args = make_arguments_for_translation(space, [1,2], {'g': 9},
-                                       w_stararg=[3,4,5],
-                                       w_starstararg={'e': 5, 'd': 7})
-        assert rawshape(args) == (2, ('g', ), True, True)
-
-    def test_copy_and_shape(self):
-        space = DummySpace()
-        args = ArgumentsForTranslation(space, ['a'], ['x'], [1],
-                                       ['w1'], {'y': 'w2'})
-        args1 = args.copy()
-        args.combine_if_necessary()
-        assert rawshape(args1) == (1, ('x',), True, True)
-
-
-    def test_flatten(self):
-        space = DummySpace()
-        args = make_arguments_for_translation(space, [1,2,3])
-        assert args.flatten() == ((3, (), False, False), [1, 2, 3])
-
-        args = make_arguments_for_translation(space, [1])
-        assert args.flatten() == ((1, (), False, False), [1])
-
-        args = make_arguments_for_translation(space, [1,2,3,4,5])
-        assert args.flatten() == ((5, (), False, False), [1,2,3,4,5])
-
-        args = make_arguments_for_translation(space, [1], {'c': 3, 'b': 2})
-        assert args.flatten() == ((1, ('b', 'c'), False, False), [1, 2, 3])
-
-        args = make_arguments_for_translation(space, [1], {'c': 5})
-        assert args.flatten() == ((1, ('c', ), False, False), [1, 5])
-
-        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
-        assert args.flatten() == ((1, ('c', 'd'), False, False), [1, 5, 7])
-
-        args = make_arguments_for_translation(space, [1,2,3,4,5], {'e': 5, 'd': 7})
-        assert args.flatten() == ((5, ('d', 'e'), False, False), [1, 2, 3, 4, 5, 7, 5])
-
-        args = make_arguments_for_translation(space, [], {},
-                                       w_stararg=[1],
-                                       w_starstararg={'c': 5, 'd': 7})
-        assert args.flatten() == ((0, (), True, True), [[1], {'c': 5, 'd': 7}])
-
-        args = make_arguments_for_translation(space, [1,2], {'g': 9},
-                                       w_stararg=[3,4,5],
-                                       w_starstararg={'e': 5, 'd': 7})
-        assert args.flatten() == ((2, ('g', ), True, True), [1, 2, 9, [3, 4, 5], {'e': 5, 'd': 7}])
-
-    def test_stararg_flowspace_variable(self):
-        space = DummySpace()
-        var = object()
-        shape = ((2, ('g', ), True, False), [1, 2, 9, var])
-        args = make_arguments_for_translation(space, [1,2], {'g': 9},
-                                       w_stararg=var)
-        assert args.flatten() == shape
-
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-
-    def test_fromshape(self):
-        space = DummySpace()
-        shape = ((3, (), False, False), [1, 2, 3])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((1, (), False, False), [1])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((5, (), False, False), [1,2,3,4,5])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((1, ('b', 'c'), False, False), [1, 2, 3])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((1, ('c', ), False, False), [1, 5])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((1, ('c', 'd'), False, False), [1, 5, 7])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((5, ('d', 'e'), False, False), [1, 2, 3, 4, 5, 7, 5])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((0, (), True, True), [[1], {'c': 5, 'd': 7}])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-
-        shape = ((2, ('g', ), True, True), [1, 2, 9, [3, 4, 5], {'e': 5, 'd': 7}])
-        args = ArgumentsForTranslation.fromshape(space, *shape)
-        assert args.flatten() == shape
-

File rpython/flowspace/test/test_framestate.py

 from rpython.flowspace.pygraph import PyGraph
 
 class TestFrameState:
-    def setup_class(cls):
-        cls.space = FlowObjSpace()
-
     def getframe(self, func):
         try:
             func = func.im_func
             pass
         code = HostCode._from_code(func.func_code)
         graph = PyGraph(func, code)
-        frame = FlowSpaceFrame(self.space, graph, code)
+        frame = FlowSpaceFrame(FlowObjSpace(), graph, code)
         # hack the frame
         frame.setstate(graph.startblock.framestate)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Constant(None)

File rpython/flowspace/test/test_generator.py

 from rpython.conftest import option
-from rpython.flowspace.objspace import FlowObjSpace
+from rpython.flowspace.objspace import build_flow
 from rpython.flowspace.model import Variable
 from rpython.flowspace.generator import (make_generatoriterator_class,
         replace_graph_with_bootstrap, get_variable_names, attach_next_method)
             yield n
             yield n
         #
-        graph = FlowObjSpace().build_flow(func)
+        graph = build_flow(func)
         if option.view:
             graph.show()
         block = graph.startblock
             yield n + 1
             z -= 10
         #
-        space = FlowObjSpace()
-        graph = space.build_flow(f)
+        graph = build_flow(f)
         GeneratorIterator = make_generatoriterator_class(graph)
         replace_graph_with_bootstrap(GeneratorIterator, graph)
         func1 = attach_next_method(GeneratorIterator, graph)
         assert func1._generator_next_method_of_ is GeneratorIterator
         assert hasattr(GeneratorIterator, 'next')
         #
-        graph_next = space.build_flow(GeneratorIterator.next.im_func)
+        graph_next = build_flow(GeneratorIterator.next.im_func)
         join_blocks(graph_next)
         if option.view:
             graph_next.show()
         #
-        graph1 = space.build_flow(func1)
+        graph1 = build_flow(func1)
         if option.view:
             graph1.show()
 
             yield n + 1
             z -= 10
         #
-        graph = FlowObjSpace().build_flow(f)
+        graph = build_flow(f)
         if option.view:
             graph.show()
         block = graph.startblock

File rpython/flowspace/test/test_objspace.py

 
 from rpython.flowspace.model import Constant, mkentrymap, c_last_exception