1. Pypy
  2. Untitled project
  3. pypy

Commits

Ronan Lamy  committed f5a0982 Merge

merge branch annotator

  • Participants
  • Parent commits cdeeb92, 7045f8e
  • Branches default

Comments (0)

Files changed (20)

File rpython/annotator/annrpython.py

View file
  • Ignore whitespace
 from rpython.flowspace.model import (Variable, Constant, FunctionGraph,
                                       c_last_exception, checkgraph)
 from rpython.translator import simplify, transform
-from rpython.annotator import model as annmodel, signature, unaryop, binaryop
+from rpython.annotator import model as annmodel, signature
 from rpython.annotator.bookkeeper import Bookkeeper
 
 import py
         # occour for this specific, typed operation.
         if block.exitswitch == c_last_exception:
             op = block.operations[-1]
-            if op.opname in binaryop.BINARY_OPERATIONS:
+            if op.dispatch == 2:
                 arg1 = self.binding(op.args[0])
                 arg2 = self.binding(op.args[1])
                 binop = getattr(pair(arg1, arg2), op.opname, None)
                 can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
-            elif op.opname in unaryop.UNARY_OPERATIONS:
+            elif op.dispatch == 1:
                 arg1 = self.binding(op.args[0])
                 opname = op.opname
                 if opname == 'contains': opname = 'op_contains'
     def noreturnvalue(self, op):
         return annmodel.s_ImpossibleValue  # no return value (hook method)
 
-    # XXX "contains" clash with SomeObject method
-    def consider_op_contains(self, seq, elem):
-        self.bookkeeper.count("contains", seq)
-        return seq.op_contains(elem)
-
-    def consider_op_newtuple(self, *args):
-        return annmodel.SomeTuple(items = args)
-
-    def consider_op_newlist(self, *args):
-        return self.bookkeeper.newlist(*args)
-
-    def consider_op_newdict(self):
-        return self.bookkeeper.newdict()
-
-
-    def _registeroperations(cls, unary_ops, binary_ops):
-        # All unary operations
-        d = {}
-        for opname in unary_ops:
-            fnname = 'consider_op_' + opname
-            exec py.code.Source("""
-def consider_op_%s(self, arg, *args):
-    return arg.%s(*args)
-""" % (opname, opname)).compile() in globals(), d
-            setattr(cls, fnname, d[fnname])
-        # All binary operations
-        for opname in binary_ops:
-            fnname = 'consider_op_' + opname
-            exec py.code.Source("""
-def consider_op_%s(self, arg1, arg2, *args):
-    return pair(arg1,arg2).%s(*args)
-""" % (opname, opname)).compile() in globals(), d
-            setattr(cls, fnname, d[fnname])
-    _registeroperations = classmethod(_registeroperations)
-
-# register simple operations handling
-RPythonAnnotator._registeroperations(unaryop.UNARY_OPERATIONS, binaryop.BINARY_OPERATIONS)
-
 
 class BlockedInference(Exception):
     """This exception signals the type inference engine that the situation

File rpython/annotator/binaryop.py

View file
  • Ignore whitespace
     SomeBuiltin, SomeIterator, SomePBC, SomeFloat, s_None, SomeByteArray,
     SomeWeakRef, SomeAddress, SomeTypedAddressAccess, SomeSingleFloat,
     SomeLongFloat, SomeType, SomeConstantType, unionof, UnionError,
-    missing_operation, read_can_only_throw, add_knowntypedata,
+    read_can_only_throw, add_knowntypedata,
     merge_knowntypedata,)
 from rpython.annotator.bookkeeper import getbookkeeper
 from rpython.flowspace.model import Variable, Constant
+from rpython.flowspace.operation import op
 from rpython.rlib import rarithmetic
 from rpython.annotator.model import AnnotatorError
 
 def immutablevalue(x):
     return getbookkeeper().immutablevalue(x)
 
-# XXX unify this with ObjSpace.MethodTable
-BINARY_OPERATIONS = set(['add', 'sub', 'mul', 'div', 'mod',
-                         'truediv', 'floordiv', 'divmod',
-                         'and_', 'or_', 'xor',
-                         'lshift', 'rshift',
-                         'getitem', 'setitem', 'delitem',
-                         'getitem_idx', 'getitem_key', 'getitem_idx_key',
-                         'inplace_add', 'inplace_sub', 'inplace_mul',
-                         'inplace_truediv', 'inplace_floordiv', 'inplace_div',
-                         'inplace_mod',
-                         'inplace_lshift', 'inplace_rshift',
-                         'inplace_and', 'inplace_or', 'inplace_xor',
-                         'lt', 'le', 'eq', 'ne', 'gt', 'ge', 'is_', 'cmp',
-                         'coerce',
-                         ]
-                        +[opname+'_ovf' for opname in
-                          """add sub mul floordiv div mod lshift
-                           """.split()
-                          ])
+BINARY_OPERATIONS = set([oper.opname for oper in op.__dict__.values()
+                        if oper.dispatch == 2])
 
-for opname in BINARY_OPERATIONS:
-    missing_operation(pairtype(SomeObject, SomeObject), opname)
 
 class __extend__(pairtype(SomeObject, SomeObject)):
 
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const < obj2.const)
         else:
-            getbookkeeper().count("non_int_comp", obj1, obj2)
             return s_Bool
 
     def le((obj1, obj2)):
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const <= obj2.const)
         else:
-            getbookkeeper().count("non_int_comp", obj1, obj2)
             return s_Bool
 
     def eq((obj1, obj2)):
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const == obj2.const)
         else:
-            getbookkeeper().count("non_int_eq", obj1, obj2)
             return s_Bool
 
     def ne((obj1, obj2)):
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const != obj2.const)
         else:
-            getbookkeeper().count("non_int_eq", obj1, obj2)
             return s_Bool
 
     def gt((obj1, obj2)):
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const > obj2.const)
         else:
-            getbookkeeper().count("non_int_comp", obj1, obj2)
             return s_Bool
 
     def ge((obj1, obj2)):
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(obj1.const >= obj2.const)
         else:
-            getbookkeeper().count("non_int_comp", obj1, obj2)
             return s_Bool
 
     def cmp((obj1, obj2)):
-        getbookkeeper().count("cmp", obj1, obj2)
         if obj1.is_immutable_constant() and obj2.is_immutable_constant():
             return immutablevalue(cmp(obj1.const, obj2.const))
         else:
         return r
 
     def divmod((obj1, obj2)):
-        getbookkeeper().count("divmod", obj1, obj2)
         return SomeTuple([pair(obj1, obj2).div(), pair(obj1, obj2).mod()])
 
     def coerce((obj1, obj2)):
-        getbookkeeper().count("coerce", obj1, obj2)
         return pair(obj1, obj2).union()   # reasonable enough
 
+    def getitem((obj1, obj2)):
+        return s_ImpossibleValue
+    add = sub = mul = truediv = floordiv = div = mod = getitem
+    lshift = rshift = and_ = or_ = xor = delitem = getitem
+
+    def setitem((obj1, obj2), _):
+        return s_ImpossibleValue
+
     # approximation of an annotation intersection, the result should be the annotation obj or
     # the intersection of obj and improvement
     def improve((obj, improvement)):
                                                   SomeUnicodeString))):
                 raise AnnotatorError(
                     "string formatting mixing strings and unicode not supported")
-        getbookkeeper().count('strformat', s_string, s_tuple)
         no_nul = s_string.no_nul
         for s_item in s_tuple.items:
             if isinstance(s_item, SomeFloat):
                  pairtype(SomeUnicodeString, SomeObject)):
 
     def mod((s_string, args)):
-        getbookkeeper().count('strformat', s_string, args)
         return s_string.__class__()
 
 class __extend__(pairtype(SomeFloat, SomeFloat)):
         return [KeyError]
 
     def getitem((dic1, obj2)):
-        getbookkeeper().count("dict_getitem", dic1)
         dic1.dictdef.generalize_key(obj2)
         return dic1.dictdef.read_value()
     getitem.can_only_throw = _can_only_throw
 
     def setitem((dic1, obj2), s_value):
-        getbookkeeper().count("dict_setitem", dic1)
         dic1.dictdef.generalize_key(obj2)
         dic1.dictdef.generalize_value(s_value)
     setitem.can_only_throw = _can_only_throw
 
     def delitem((dic1, obj2)):
-        getbookkeeper().count("dict_delitem", dic1)
         dic1.dictdef.generalize_key(obj2)
     delitem.can_only_throw = _can_only_throw
 
             except IndexError:
                 return s_ImpossibleValue
         else:
-            getbookkeeper().count("tuple_random_getitem", tup1)
             return unionof(*tup1.items)
     getitem.can_only_throw = [IndexError]
 
         return lst1.listdef.offspring()
 
     def getitem((lst1, int2)):
-        getbookkeeper().count("list_getitem", int2)
         return lst1.listdef.read_item()
     getitem.can_only_throw = []
 
     getitem_key = getitem
 
     def getitem_idx((lst1, int2)):
-        getbookkeeper().count("list_getitem", int2)
         return lst1.listdef.read_item()
     getitem_idx.can_only_throw = [IndexError]
 
     getitem_idx_key = getitem_idx
 
     def setitem((lst1, int2), s_value):
-        getbookkeeper().count("list_setitem", int2)
         lst1.listdef.mutate()
         lst1.listdef.generalize(s_value)
     setitem.can_only_throw = [IndexError]
 
     def delitem((lst1, int2)):
-        getbookkeeper().count("list_delitem", int2)
         lst1.listdef.resize()
     delitem.can_only_throw = [IndexError]
 
 class __extend__(pairtype(SomeString, SomeInteger)):
 
     def getitem((str1, int2)):
-        getbookkeeper().count("str_getitem", int2)
         return SomeChar(no_nul=str1.no_nul)
     getitem.can_only_throw = []
 
     getitem_key = getitem
 
     def getitem_idx((str1, int2)):
-        getbookkeeper().count("str_getitem", int2)
         return SomeChar(no_nul=str1.no_nul)
     getitem_idx.can_only_throw = [IndexError]
 
     getitem_idx_key = getitem_idx
 
     def mul((str1, int2)): # xxx do we want to support this
-        getbookkeeper().count("str_mul", str1, int2)
         return SomeString(no_nul=str1.no_nul)
 
 class __extend__(pairtype(SomeUnicodeString, SomeInteger)):
     def getitem((str1, int2)):
-        getbookkeeper().count("str_getitem", int2)
         return SomeUnicodeCodePoint()
     getitem.can_only_throw = []
 
     getitem_key = getitem
 
     def getitem_idx((str1, int2)):
-        getbookkeeper().count("str_getitem", int2)
         return SomeUnicodeCodePoint()
     getitem_idx.can_only_throw = [IndexError]
 
     getitem_idx_key = getitem_idx
 
     def mul((str1, int2)): # xxx do we want to support this
-        getbookkeeper().count("str_mul", str1, int2)
         return SomeUnicodeString()
 
 class __extend__(pairtype(SomeInteger, SomeString),
                  pairtype(SomeInteger, SomeUnicodeString)):
 
     def mul((int1, str2)): # xxx do we want to support this
-        getbookkeeper().count("str_mul", str2, int1)
         return str2.basestringclass()
 
 class __extend__(pairtype(SomeUnicodeCodePoint, SomeUnicodeString),

File rpython/annotator/bookkeeper.py

View file
  • Ignore whitespace
 from rpython.rtyper import extregistry
 
 
-class Stats(object):
-
-    def __init__(self, bookkeeper):
-        self.bookkeeper = bookkeeper
-        self.classify = {}
-
-    def count(self, category, *args):
-        for_category = self.classify.setdefault(category, {})
-        classifier = getattr(self, 'consider_%s' % category, self.consider_generic)
-        outcome = classifier(*args)
-        for_category[self.bookkeeper.position_key] = outcome
-
-    def indexrepr(self, idx):
-        if idx.is_constant():
-            if idx.const is None:
-                return ''
-            if isinstance(idx, SomeInteger):
-                if idx.const >=0:
-                    return 'pos-constant'
-                else:
-                    return 'Neg-constant'
-            return idx.const
-        else:
-            if isinstance(idx, SomeInteger):
-                if idx.nonneg:
-                    return "non-neg"
-                else:
-                    return "MAYBE-NEG"
-            else:
-                return self.typerepr(idx)
-
-    def steprepr(self, stp):
-        if stp.is_constant():
-            if stp.const in (1, None):
-                return 'step=1'
-            else:
-                return 'step=%s?' % stp.const
-        else:
-            return 'non-const-step %s' % self.typerepr(stp)
-
-    def consider_generic(self, *args):
-        return tuple([self.typerepr(x) for x in args])
-
-    def consider_list_list_eq(self, obj1, obj2):
-        return obj1, obj2
-
-    def consider_contains(self, seq):
-        return seq
-
-    def consider_non_int_eq(self, obj1, obj2):
-        if obj1.knowntype == obj2.knowntype == list:
-            self.count("list_list_eq", obj1, obj2)
-        return self.typerepr(obj1), self.typerepr(obj2)
-
-    def consider_non_int_comp(self, obj1, obj2):
-        return self.typerepr(obj1), self.typerepr(obj2)
-
-    def typerepr(self, obj):
-        if isinstance(obj, SomeInstance):
-            return obj.classdef.name
-        else:
-            return obj.knowntype.__name__
-
-    def consider_tuple_random_getitem(self, tup):
-        return tuple([self.typerepr(x) for x in tup.items])
-
-    def consider_list_index(self):
-        return '!'
-
-    def consider_list_getitem(self, idx):
-        return self.indexrepr(idx)
-
-    def consider_list_setitem(self, idx):
-        return self.indexrepr(idx)
-
-    def consider_list_delitem(self, idx):
-        return self.indexrepr(idx)
-
-    def consider_str_join(self, s):
-        if s.is_constant():
-            return repr(s.const)
-        else:
-            return "NON-CONSTANT"
-
-    def consider_str_getitem(self, idx):
-        return self.indexrepr(idx)
-
-    def consider_strformat(self, str, args):
-        if str.is_constant():
-            s = repr(str.const)
-        else:
-            s = "?!!!!!!"
-        if isinstance(args, SomeTuple):
-            return (s, tuple([self.typerepr(x) for x in args.items]))
-        else:
-            return (s, self.typerepr(args))
-
-    def consider_dict_getitem(self, dic):
-        return dic
-
-    def consider_dict_setitem(self, dic):
-        return dic
-
-    def consider_dict_delitem(self, dic):
-        return dic
-
 class Bookkeeper(object):
     """The log of choices that have been made while analysing the operations.
     It ensures that the same 'choice objects' will be returned if we ask
 
         self.needs_generic_instantiate = {}
 
-        self.stats = Stats(self)
-
         delayed_imports()
 
-    def count(self, category, *args):
-        self.stats.count(category, *args)
-
     def enter(self, position_key):
         """Start of an operation.
         The operation is uniquely identified by the given key."""

File rpython/annotator/builtin.py

View file
  • Ignore whitespace
 def test(*args):
     return s_Bool
 
-def import_func(*args):
-    return SomeObject()
-
 # collect all functions
 import __builtin__
 BUILTIN_ANALYZERS = {}
 else:
     BUILTIN_ANALYZERS[object.__init__] = object_init
 
-# import
-BUILTIN_ANALYZERS[__import__] = import_func
-
 # annotation of low-level types
 from rpython.annotator.model import SomePtr
 from rpython.rtyper.lltypesystem import lltype

File rpython/annotator/model.py

View file
  • Ignore whitespace
     assert 0, "couldn't get to commonbase of %r and %r" % (cls1, cls2)
 
 
-def missing_operation(cls, name):
-    def default_op(*args):
-        if args and isinstance(args[0], tuple):
-            flattened = tuple(args[0]) + args[1:]
-        else:
-            flattened = args
-        for arg in flattened:
-            if arg.__class__ is SomeObject and arg.knowntype is not type:
-                return SomeObject()
-        bookkeeper = rpython.annotator.bookkeeper.getbookkeeper()
-        bookkeeper.warning("no precise annotation supplied for %s%r" % (name, args))
-        return s_ImpossibleValue
-    setattr(cls, name, default_op)
-
-
 class HarmlesslyBlocked(Exception):
     """Raised by the unaryop/binaryop to signal a harmless kind of
     BlockedInference: the current block is blocked, but not in a way

File rpython/annotator/test/test_annrpython.py

View file
  • Ignore whitespace
 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 build_flow, FlowingError
+from rpython.flowspace.objspace import build_flow
+from rpython.flowspace.flowcontext import FlowingError
 from rpython.flowspace.operation import op
 
 from rpython.translator.test import snippet

File rpython/annotator/unaryop.py

View file
  • Ignore whitespace
 from __future__ import absolute_import
 
 from types import MethodType
+from rpython.flowspace.operation import op
 from rpython.annotator.model import (SomeObject, SomeInteger, SomeBool,
     SomeString, SomeChar, SomeList, SomeDict, SomeTuple, SomeImpossibleValue,
     SomeUnicodeCodePoint, SomeInstance, SomeBuiltin, SomeFloat, SomeIterator,
     SomePBC, SomeTypedAddressAccess, SomeAddress, SomeType, s_ImpossibleValue,
-    s_Bool, s_None, unionof, missing_operation, add_knowntypedata,
+    s_Bool, s_None, unionof, add_knowntypedata,
     HarmlesslyBlocked, SomeWeakRef, SomeUnicodeString, SomeByteArray)
 from rpython.annotator.bookkeeper import getbookkeeper
 from rpython.annotator import builtin
 def immutablevalue(x):
     return getbookkeeper().immutablevalue(x)
 
-UNARY_OPERATIONS = set(['len', 'bool', 'getattr', 'setattr', 'delattr',
-                        'simple_call', 'call_args', 'str', 'repr',
-                        'iter', 'next', 'invert', 'type', 'issubtype',
-                        'pos', 'neg', 'abs', 'hex', 'oct',
-                        'ord', 'int', 'float', 'long',
-                        'hash', 'id',    # <== not supported any more
-                        'getslice', 'setslice', 'delslice',
-                        'neg_ovf', 'abs_ovf', 'hint', 'unicode', 'unichr'])
-
-for opname in UNARY_OPERATIONS:
-    missing_operation(SomeObject, opname)
+UNARY_OPERATIONS = set([oper.opname for oper in op.__dict__.values()
+                        if oper.dispatch == 1])
 
 
 class __extend__(SomeObject):
         raise AnnotatorError("cannot use hash() in RPython")
 
     def str(self):
-        getbookkeeper().count('str', self)
         return SomeString()
 
     def unicode(self):
-        getbookkeeper().count('unicode', self)
         return SomeUnicodeString()
 
     def repr(self):
-        getbookkeeper().count('repr', self)
         return SomeString()
 
     def hex(self):
-        getbookkeeper().count('hex', self)
         return SomeString()
 
     def oct(self):
-        getbookkeeper().count('oct', self)
         return SomeString()
 
     def id(self):
         raise AnnotatorError("Cannot find attribute %r on %r" % (attr, self))
     getattr.can_only_throw = []
 
+    def setattr(self, *args):
+        return s_ImpossibleValue
+
     def bind_callables_under(self, classdef, name):
         return self   # default unbound __get__ implementation
 
     def hint(self, *args_s):
         return self
 
+    def getslice(self, *args):
+        return s_ImpossibleValue
+
+    def setslice(self, *args):
+        return s_ImpossibleValue
+
+    def delslice(self, *args):
+        return s_ImpossibleValue
+
+    def pos(self):
+        return s_ImpossibleValue
+    neg = abs = ord = invert = long = iter = next = pos
+
+
 class __extend__(SomeFloat):
 
     def pos(self):
         return immutablevalue(len(self.items))
 
     def iter(self):
-        getbookkeeper().count("tuple_iter", self)
         return SomeIterator(self)
     iter.can_only_throw = []
 
     method_pop.can_only_throw = [IndexError]
 
     def method_index(self, s_value):
-        getbookkeeper().count("list_index")
         self.listdef.generalize(s_value)
         return SomeInteger(nonneg=True)
 
     def method_join(self, s_list):
         if s_None.contains(s_list):
             return SomeImpossibleValue()
-        getbookkeeper().count("str_join", self)
         s_item = s_list.listdef.read_item()
         if s_None.contains(s_item):
             if isinstance(self, SomeUnicodeString):
         return self.basecharclass()
 
     def method_split(self, patt, max=-1):
-        getbookkeeper().count("str_split", self, patt)
         if max == -1 and patt.is_constant() and patt.const == "\0":
             no_nul = True
         else:
         return getbookkeeper().newlist(s_item)
 
     def method_rsplit(self, patt, max=-1):
-        getbookkeeper().count("str_rsplit", self, patt)
         s_item = self.basestringclass(no_nul=self.no_nul)
         return getbookkeeper().newlist(s_item)
 
         if self.s_self is not None:
             return self.analyser(self.s_self, *args)
         else:
-            if self.methodname:
-                getbookkeeper().count(self.methodname.replace('.', '_'), *args)
             return self.analyser(*args)
     simple_call.can_only_throw = _can_only_throw
 

File rpython/flowspace/flowcontext.py

View file
  • Ignore whitespace
-"""Implements the core parts of flow graph creation, in tandem
-with rpython.flowspace.objspace.
+"""Implements the core parts of flow graph creation.
 """
 
 import sys
 import collections
+import types
+import __builtin__
 
 from rpython.tool.error import source_lines
 from rpython.tool.stdlib_opcode import host_bytecode_spec
+from rpython.rlib import rstackovf
 from rpython.flowspace.argument import CallSpec
 from rpython.flowspace.model import (Constant, Variable, Block, Link,
     c_last_exception, const, FSException)
     recursively_flatten)
 from rpython.flowspace.specialcase import (rpython_print_item,
     rpython_print_newline)
+from rpython.flowspace.operation import op
 
+w_None = const(None)
 
 class FlowingError(Exception):
     """ Signals invalid RPython in the function being analysed"""
-    frame = None
+    ctx = None
 
     def __str__(self):
         msg = ["\n"]
         msg += map(str, self.args)
         msg += [""]
-        msg += source_lines(self.frame.graph, None, offset=self.frame.last_instr)
+        msg += source_lines(self.ctx.graph, None, offset=self.ctx.last_instr)
         return "\n".join(msg)
 
 class StopFlowing(Exception):
     def append(self, operation):
         raise NotImplementedError
 
-    def guessbool(self, frame, w_condition):
+    def guessbool(self, ctx, w_condition):
         raise AssertionError("cannot guessbool(%s)" % (w_condition,))
 
 
     def append(self, operation):
         self.crnt_block.operations.append(operation)
 
-    def guessbool(self, frame, w_condition):
+    def guessbool(self, ctx, w_condition):
         block = self.crnt_block
         vars = block.getvariables()
         links = []
         for case in [False, True]:
             egg = EggBlock(vars, block, case)
-            frame.pendingblocks.append(egg)
+            ctx.pendingblocks.append(egg)
             link = Link(vars, egg, case)
             links.append(link)
 
         # block.exits[True] = ifLink.
         raise StopFlowing
 
-    def guessexception(self, frame, *cases):
+    def guessexception(self, ctx, *cases):
         block = self.crnt_block
         bvars = vars = vars2 = block.getvariables()
         links = []
                 vars.extend([last_exc, last_exc_value])
                 vars2.extend([Variable(), Variable()])
             egg = EggBlock(vars2, block, case)
-            frame.pendingblocks.append(egg)
+            ctx.pendingblocks.append(egg)
             link = Link(vars, egg, case)
             if case is not None:
                 link.extravars(last_exception=last_exc, last_exc_value=last_exc_value)
                       [str(s) for s in self.listtoreplay[self.index:]]))
         self.index += 1
 
-    def guessbool(self, frame, w_condition):
+    def guessbool(self, ctx, w_condition):
         assert self.index == len(self.listtoreplay)
-        frame.recorder = self.nextreplayer
+        ctx.recorder = self.nextreplayer
         return self.booloutcome
 
-    def guessexception(self, frame, *classes):
+    def guessexception(self, ctx, *classes):
         assert self.index == len(self.listtoreplay)
-        frame.recorder = self.nextreplayer
+        ctx.recorder = self.nextreplayer
         outcome = self.booloutcome
         if outcome is not None:
             egg = self.nextreplayer.crnt_block
 # ____________________________________________________________
 
 _unary_ops = [
-    ('UNARY_POSITIVE', "pos"),
-    ('UNARY_NEGATIVE', "neg"),
-    ('UNARY_NOT', "not_"),
-    ('UNARY_CONVERT', "repr"),
-    ('UNARY_INVERT', "invert"),
+    ('UNARY_POSITIVE', op.pos),
+    ('UNARY_NEGATIVE', op.neg),
+    ('UNARY_CONVERT', op.repr),
+    ('UNARY_INVERT', op.invert),
 ]
 
-def unaryoperation(OPCODE, op):
+def unaryoperation(OPCODE, operation):
     def UNARY_OP(self, *ignored):
-        operation = getattr(self.space, op)
         w_1 = self.popvalue()
-        w_result = operation(w_1)
+        w_result = operation(w_1).eval(self)
         self.pushvalue(w_result)
-    UNARY_OP.unaryop = op
     UNARY_OP.func_name = OPCODE
     return UNARY_OP
 
 _binary_ops = [
-    ('BINARY_MULTIPLY', "mul"),
-    ('BINARY_TRUE_DIVIDE', "truediv"),
-    ('BINARY_FLOOR_DIVIDE', "floordiv"),
-    ('BINARY_DIVIDE', "div"),
-    ('BINARY_MODULO', "mod"),
-    ('BINARY_ADD', "add"),
-    ('BINARY_SUBTRACT', "sub"),
-    ('BINARY_SUBSCR', "getitem"),
-    ('BINARY_LSHIFT', "lshift"),
-    ('BINARY_RSHIFT', "rshift"),
-    ('BINARY_AND', "and_"),
-    ('BINARY_XOR', "xor"),
-    ('BINARY_OR', "or_"),
-    ('INPLACE_MULTIPLY', "inplace_mul"),
-    ('INPLACE_TRUE_DIVIDE', "inplace_truediv"),
-    ('INPLACE_FLOOR_DIVIDE', "inplace_floordiv"),
-    ('INPLACE_DIVIDE', "inplace_div"),
-    ('INPLACE_MODULO', "inplace_mod"),
-    ('INPLACE_ADD', "inplace_add"),
-    ('INPLACE_SUBTRACT', "inplace_sub"),
-    ('INPLACE_LSHIFT', "inplace_lshift"),
-    ('INPLACE_RSHIFT', "inplace_rshift"),
-    ('INPLACE_AND', "inplace_and"),
-    ('INPLACE_XOR', "inplace_xor"),
-    ('INPLACE_OR', "inplace_or"),
+    ('BINARY_MULTIPLY', op.mul),
+    ('BINARY_TRUE_DIVIDE', op.truediv),
+    ('BINARY_FLOOR_DIVIDE', op.floordiv),
+    ('BINARY_DIVIDE', op.div),
+    ('BINARY_MODULO', op.mod),
+    ('BINARY_ADD', op.add),
+    ('BINARY_SUBTRACT', op.sub),
+    ('BINARY_SUBSCR', op.getitem),
+    ('BINARY_LSHIFT', op.lshift),
+    ('BINARY_RSHIFT', op.rshift),
+    ('BINARY_AND', op.and_),
+    ('BINARY_XOR', op.xor),
+    ('BINARY_OR', op.or_),
+    ('INPLACE_MULTIPLY', op.inplace_mul),
+    ('INPLACE_TRUE_DIVIDE', op.inplace_truediv),
+    ('INPLACE_FLOOR_DIVIDE', op.inplace_floordiv),
+    ('INPLACE_DIVIDE', op.inplace_div),
+    ('INPLACE_MODULO', op.inplace_mod),
+    ('INPLACE_ADD', op.inplace_add),
+    ('INPLACE_SUBTRACT', op.inplace_sub),
+    ('INPLACE_LSHIFT', op.inplace_lshift),
+    ('INPLACE_RSHIFT', op.inplace_rshift),
+    ('INPLACE_AND', op.inplace_and),
+    ('INPLACE_XOR', op.inplace_xor),
+    ('INPLACE_OR', op.inplace_or),
 ]
 
-def binaryoperation(OPCODE, op):
+def binaryoperation(OPCODE, operation):
     """NOT_RPYTHON"""
-    def BINARY_OP(self, *ignored):
-        operation = getattr(self.space, op)
+    def BINARY_OP(self, _):
         w_2 = self.popvalue()
         w_1 = self.popvalue()
-        w_result = operation(w_1, w_2)
+        w_result = operation(w_1, w_2).eval(self)
         self.pushvalue(w_result)
-    BINARY_OP.binop = op
     BINARY_OP.func_name = OPCODE
     return BINARY_OP
 
     "cmp_exc_match",
     ]
 
-class FlowSpaceFrame(object):
+class FlowContext(object):
     opcode_method_names = host_bytecode_spec.method_names
 
-    def __init__(self, space, graph, code):
+    def __init__(self, graph, code):
         self.graph = graph
         func = graph.func
         self.pycode = code
-        self.space = space
         self.w_globals = Constant(func.func_globals)
         self.blockstack = []
 
         self.last_instr = 0
 
         self.init_locals_stack(code)
-        self.w_locals = None # XXX: only for compatibility with PyFrame
 
         self.joinpoints = {}
 
         return FrameState(data, self.blockstack[:], next_pos)
 
     def setstate(self, state):
-        """ Reset the frame to the given state. """
+        """ Reset the context to the given frame state. """
         data = state.mergeable[:]
         recursively_unflatten(data)
         self.restore_locals_stack(data[:-2])  # Nones == undefined locals
 
         except Raise as e:
             w_exc = e.w_exc
-            if w_exc.w_type == self.space.w_ImportError:
+            if w_exc.w_type == const(ImportError):
                 msg = 'import statement always raises %s' % e
                 raise ImportError(msg)
             link = Link([w_exc.w_type, w_exc.w_value], self.graph.exceptblock)
             self.recorder.crnt_block.closeblock(link)
 
         except FlowingError as exc:
-            if exc.frame is None:
-                exc.frame = self
+            if exc.ctx is None:
+                exc.ctx = self
             raise
 
         self.recorder = None
     def getname_w(self, index):
         return Constant(self.pycode.names[index])
 
+    def appcall(self, func, *args_w):
+        """Call an app-level RPython function directly"""
+        w_func = const(func)
+        return self.do_op(op.simple_call(w_func, *args_w))
+
     def BAD_OPCODE(self, _):
         raise FlowingError("This operation is not RPython")
 
     def CONTINUE_LOOP(self, startofloop):
         raise Continue(startofloop)
 
+    def not_(self, w_obj):
+        w_bool = op.bool(w_obj).eval(self)
+        return const(not self.guessbool(w_bool))
+
+    def UNARY_NOT(self, _):
+        w_obj = self.popvalue()
+        self.pushvalue(self.not_(w_obj))
+
     def cmp_lt(self, w_1, w_2):
-        return self.space.lt(w_1, w_2)
+        return op.lt(w_1, w_2).eval(self)
 
     def cmp_le(self, w_1, w_2):
-        return self.space.le(w_1, w_2)
+        return op.le(w_1, w_2).eval(self)
 
     def cmp_eq(self, w_1, w_2):
-        return self.space.eq(w_1, w_2)
+        return op.eq(w_1, w_2).eval(self)
 
     def cmp_ne(self, w_1, w_2):
-        return self.space.ne(w_1, w_2)
+        return op.ne(w_1, w_2).eval(self)
 
     def cmp_gt(self, w_1, w_2):
-        return self.space.gt(w_1, w_2)
+        return op.gt(w_1, w_2).eval(self)
 
     def cmp_ge(self, w_1, w_2):
-        return self.space.ge(w_1, w_2)
+        return op.ge(w_1, w_2).eval(self)
 
     def cmp_in(self, w_1, w_2):
-        return self.space.contains(w_2, w_1)
+        return op.contains(w_2, w_1).eval(self)
 
     def cmp_not_in(self, w_1, w_2):
-        return self.space.not_(self.space.contains(w_2, w_1))
+        return self.not_(self.cmp_in(w_1, w_2))
 
     def cmp_is(self, w_1, w_2):
-        return self.space.is_(w_1, w_2)
+        return op.is_(w_1, w_2).eval(self)
 
     def cmp_is_not(self, w_1, w_2):
-        return self.space.not_(self.space.is_(w_1, w_2))
+        return self.not_(op.is_(w_1, w_2).eval(self))
+
+    def exception_match(self, w_exc_type, w_check_class):
+        """Checks if the given exception type matches 'w_check_class'."""
+        if not isinstance(w_check_class, Constant):
+            raise FlowingError("Non-constant except guard.")
+        check_class = w_check_class.value
+        if check_class in (NotImplementedError, AssertionError):
+            raise FlowingError(
+                "Catching %s is not valid in RPython" % check_class.__name__)
+        if not isinstance(check_class, tuple):
+            # the simple case
+            return self.guessbool(op.issubtype(w_exc_type, w_check_class).eval(self))
+        # special case for StackOverflow (see rlib/rstackovf.py)
+        if check_class == rstackovf.StackOverflow:
+            w_real_class = const(rstackovf._StackOverflow)
+            return self.guessbool(op.issubtype(w_exc_type, w_real_class).eval(self))
+        # checking a tuple of classes
+        for klass in w_check_class.value:
+            if self.exception_match(w_exc_type, const(klass)):
+                return True
+        return False
 
     def cmp_exc_match(self, w_1, w_2):
-        return self.space.newbool(self.space.exception_match(w_1, w_2))
+        return const(self.exception_match(w_1, w_2))
 
     def COMPARE_OP(self, testnum):
         w_2 = self.popvalue()
         w_result = getattr(self, compare_method[testnum])(w_1, w_2)
         self.pushvalue(w_result)
 
+    def exc_from_raise(self, w_arg1, w_arg2):
+        """
+        Create a wrapped exception from the arguments of a raise statement.
+
+        Returns an FSException object whose w_value is an instance of w_type.
+        """
+        w_is_type = op.simple_call(const(isinstance), w_arg1, const(type)).eval(self)
+        if self.guessbool(w_is_type):
+            # this is for all cases of the form (Class, something)
+            if self.guessbool(op.is_(w_arg2, w_None).eval(self)):
+                # raise Type: we assume we have to instantiate Type
+                w_value = op.simple_call(w_arg1).eval(self)
+            else:
+                w_valuetype = op.type(w_arg2).eval(self)
+                if self.guessbool(op.issubtype(w_valuetype, w_arg1).eval(self)):
+                    # raise Type, Instance: let etype be the exact type of value
+                    w_value = w_arg2
+                else:
+                    # raise Type, X: assume X is the constructor argument
+                    w_value = op.simple_call(w_arg1, w_arg2).eval(self)
+        else:
+            # the only case left here is (inst, None), from a 'raise inst'.
+            if not self.guessbool(op.is_(w_arg2, const(None)).eval(self)):
+                exc = TypeError("instance exception may not have a "
+                                "separate value")
+                raise Raise(const(exc))
+            w_value = w_arg1
+        w_type = op.type(w_value).eval(self)
+        return FSException(w_type, w_value)
+
     def RAISE_VARARGS(self, nbargs):
-        space = self.space
         if nbargs == 0:
             if self.last_exception is not None:
                 w_exc = self.last_exception
         if nbargs >= 2:
             w_value = self.popvalue()
             w_type = self.popvalue()
-            operror = space.exc_from_raise(w_type, w_value)
+            operror = self.exc_from_raise(w_type, w_value)
         else:
             w_type = self.popvalue()
-            if isinstance(w_type, FSException):
-                operror = w_type
-            else:
-                operror = space.exc_from_raise(w_type, space.w_None)
+            operror = self.exc_from_raise(w_type, w_None)
         raise Raise(operror)
 
+    def import_name(self, name, glob=None, loc=None, frm=None, level=-1):
+        try:
+            mod = __import__(name, glob, loc, frm, level)
+        except ImportError as e:
+            raise Raise(const(e))
+        return const(mod)
+
     def IMPORT_NAME(self, nameindex):
-        space = self.space
         modulename = self.getname_u(nameindex)
         glob = self.w_globals.value
         fromlist = self.popvalue().value
         level = self.popvalue().value
-        w_obj = space.import_name(modulename, glob, None, fromlist, level)
+        w_obj = self.import_name(modulename, glob, None, fromlist, level)
         self.pushvalue(w_obj)
 
+    def import_from(self, w_module, w_name):
+        assert isinstance(w_module, Constant)
+        assert isinstance(w_name, Constant)
+        try:
+            return op.getattr(w_module, w_name).eval(self)
+        except FlowingError:
+            exc = ImportError("cannot import name '%s'" % w_name.value)
+            raise Raise(const(exc))
+
     def IMPORT_FROM(self, nameindex):
         w_name = self.getname_w(nameindex)
         w_module = self.peekvalue()
-        self.pushvalue(self.space.import_from(w_module, w_name))
+        self.pushvalue(self.import_from(w_module, w_name))
 
     def RETURN_VALUE(self, oparg):
         w_returnvalue = self.popvalue()
         # item (unlike CPython which can have 1, 2 or 3 items):
         #   [subclass of FlowSignal]
         w_top = self.popvalue()
-        if w_top == self.space.w_None:
+        if w_top == w_None:
             # finally: block with no unroller active
             return
         elif isinstance(w_top, FlowSignal):
     def YIELD_VALUE(self, _):
         assert self.pycode.is_generator
         w_result = self.popvalue()
-        self.space.yield_(w_result)
+        op.yield_(w_result).eval(self)
         # XXX yield expressions not supported. This will blow up if the value
         # isn't popped straightaway.
         self.pushvalue(None)
 
     def PRINT_ITEM(self, oparg):
         w_item = self.popvalue()
-        w_s = self.space.str(w_item)
-        self.space.appcall(rpython_print_item, w_s)
+        w_s = op.str(w_item).eval(self)
+        self.appcall(rpython_print_item, w_s)
 
     def PRINT_NEWLINE(self, oparg):
-        self.space.appcall(rpython_print_newline)
+        self.appcall(rpython_print_newline)
 
     def JUMP_FORWARD(self, target):
         return target
     def JUMP_IF_FALSE(self, target):
         # Python <= 2.6 only
         w_cond = self.peekvalue()
-        if not self.guessbool(self.space.bool(w_cond)):
+        if not self.guessbool(op.bool(w_cond).eval(self)):
             return target
 
     def JUMP_IF_TRUE(self, target):
         # Python <= 2.6 only
         w_cond = self.peekvalue()
-        if self.guessbool(self.space.bool(w_cond)):
+        if self.guessbool(op.bool(w_cond).eval(self)):
             return target
 
     def POP_JUMP_IF_FALSE(self, target):
         w_value = self.popvalue()
-        if not self.guessbool(self.space.bool(w_value)):
+        if not self.guessbool(op.bool(w_value).eval(self)):
             return target
 
     def POP_JUMP_IF_TRUE(self, target):
         w_value = self.popvalue()
-        if self.guessbool(self.space.bool(w_value)):
+        if self.guessbool(op.bool(w_value).eval(self)):
             return target
 
     def JUMP_IF_FALSE_OR_POP(self, target):
         w_value = self.peekvalue()
-        if not self.guessbool(self.space.bool(w_value)):
+        if not self.guessbool(op.bool(w_value).eval(self)):
             return target
         self.popvalue()
 
     def JUMP_IF_TRUE_OR_POP(self, target):
         w_value = self.peekvalue()
-        if self.guessbool(self.space.bool(w_value)):
+        if self.guessbool(op.bool(w_value).eval(self)):
+            return target
             return target
         self.popvalue()
 
 
     def GET_ITER(self, oparg):
         w_iterable = self.popvalue()
-        w_iterator = self.space.iter(w_iterable)
+        w_iterator = op.iter(w_iterable).eval(self)
         self.pushvalue(w_iterator)
 
     def FOR_ITER(self, target):
         w_iterator = self.peekvalue()
         try:
-            w_nextitem = self.space.next(w_iterator)
+            w_nextitem = op.next(w_iterator).eval(self)
+            self.pushvalue(w_nextitem)
         except Raise as e:
-            w_exc = e.w_exc
-            if not self.space.exception_match(w_exc.w_type,
-                                              self.space.w_StopIteration):
+            if self.exception_match(e.w_exc.w_type, const(StopIteration)):
+                self.popvalue()
+                return target
+            else:
                 raise
-            # iterator exhausted
-            self.popvalue()
-            return target
-        else:
-            self.pushvalue(w_nextitem)
 
     def SETUP_LOOP(self, target):
         block = LoopBlock(self, target)
         # directly call manager.__enter__(), don't use special lookup functions
         # which don't make sense on the RPython type system.
         w_manager = self.peekvalue()
-        w_exit = self.space.getattr(w_manager, const("__exit__"))
+        w_exit = op.getattr(w_manager, const("__exit__")).eval(self)
         self.settopvalue(w_exit)
-        w_result = self.space.call_method(w_manager, "__enter__")
+        w_enter = op.getattr(w_manager, const('__enter__')).eval(self)
+        w_result = op.simple_call(w_enter).eval(self)
         block = WithBlock(self, target)
         self.blockstack.append(block)
         self.pushvalue(w_result)
             w_exitfunc = self.popvalue()
             unroller = self.peekvalue(0)
 
-        w_None = self.space.w_None
         if isinstance(unroller, Raise):
             w_exc = unroller.w_exc
             # The annotator won't allow to merge exception types with None.
             # Replace it with the exception value...
-            self.space.call_function(w_exitfunc,
-                    w_exc.w_value, w_exc.w_value, w_None)
+            op.simple_call(w_exitfunc, w_exc.w_value, w_exc.w_value, w_None
+                           ).eval(self)
         else:
-            self.space.call_function(w_exitfunc, w_None, w_None, w_None)
+            op.simple_call(w_exitfunc, w_None, w_None, w_None).eval(self)
 
     def LOAD_FAST(self, varindex):
         w_value = self.locals_stack_w[varindex]
         w_const = self.getconstant_w(constindex)
         self.pushvalue(w_const)
 
+    def find_global(self, w_globals, varname):
+        try:
+            value = w_globals.value[varname]
+        except KeyError:
+            # not in the globals, now look in the built-ins
+            try:
+                value = getattr(__builtin__, varname)
+            except AttributeError:
+                raise FlowingError("global name '%s' is not defined" % varname)
+        return const(value)
+
     def LOAD_GLOBAL(self, nameindex):
-        w_result = self.space.find_global(self.w_globals, self.getname_u(nameindex))
+        w_result = self.find_global(self.w_globals, self.getname_u(nameindex))
         self.pushvalue(w_result)
     LOAD_NAME = LOAD_GLOBAL
 
         "obj.attributename"
         w_obj = self.popvalue()
         w_attributename = self.getname_w(nameindex)
-        w_value = self.space.getattr(w_obj, w_attributename)
+        w_value = op.getattr(w_obj, w_attributename).eval(self)
         self.pushvalue(w_value)
     LOOKUP_METHOD = LOAD_ATTR
 
         # This opcode was added with pypy-1.8.  Here is a simpler
         # version, enough for annotation.
         last_val = self.popvalue()
-        self.pushvalue(self.space.newlist())
+        self.pushvalue(op.newlist().eval(self))
         self.pushvalue(last_val)
 
     def call_function(self, oparg, w_star=None, w_starstar=None):
         arguments = self.popvalues(n_arguments)
         args = CallSpec(arguments, keywords, w_star)
         w_function = self.popvalue()
-        w_result = self.space.call(w_function, args)
-        self.pushvalue(w_result)
+        if args.keywords or isinstance(args.w_stararg, Variable):
+            shape, args_w = args.flatten()
+            hlop = op.call_args(w_function, Constant(shape), *args_w)
+        else:
+            hlop = op.simple_call(w_function, *args.as_list())
+        self.pushvalue(hlop.eval(self))
 
     def CALL_FUNCTION(self, oparg):
         self.call_function(oparg)
         w_varargs = self.popvalue()
         self.call_function(oparg, w_varargs, w_varkw)
 
+    def newfunction(self, w_code, defaults_w):
+        if not all(isinstance(value, Constant) for value in defaults_w):
+            raise FlowingError("Dynamically created function must"
+                    " have constant default values.")
+        code = w_code.value
+        globals = self.w_globals.value
+        defaults = tuple([default.value for default in defaults_w])
+        fn = types.FunctionType(code, globals, code.co_name, defaults)
+        return Constant(fn)
+
     def MAKE_FUNCTION(self, numdefaults):
         w_codeobj = self.popvalue()
         defaults = self.popvalues(numdefaults)
-        fn = self.space.newfunction(w_codeobj, self.w_globals, defaults)
+        fn = self.newfunction(w_codeobj, defaults)
         self.pushvalue(fn)
 
     def STORE_ATTR(self, nameindex):
         w_attributename = self.getname_w(nameindex)
         w_obj = self.popvalue()
         w_newvalue = self.popvalue()
-        self.space.setattr(w_obj, w_attributename, w_newvalue)
+        op.setattr(w_obj, w_attributename, w_newvalue).eval(self)
+
+    def unpack_sequence(self, w_iterable, expected_length):
+        w_len = op.len(w_iterable).eval(self)
+        w_correct = op.eq(w_len, const(expected_length)).eval(self)
+        if not self.guessbool(op.bool(w_correct).eval(self)):
+            w_exc = self.exc_from_raise(const(ValueError), const(None))
+            raise Raise(w_exc)
+        return [op.getitem(w_iterable, const(i)).eval(self)
+                    for i in range(expected_length)]
 
     def UNPACK_SEQUENCE(self, itemcount):
         w_iterable = self.popvalue()
-        items = self.space.unpack_sequence(w_iterable, itemcount)
+        items = self.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()
-        w_result = self.space.getslice(w_obj, w_start, w_end)
+        w_result = op.getslice(w_obj, w_start, w_end).eval(self)
         self.pushvalue(w_result)
 
     def SLICE_0(self, oparg):
-        self.slice(self.space.w_None, self.space.w_None)
+        self.slice(w_None, w_None)
 
     def SLICE_1(self, oparg):
         w_start = self.popvalue()
-        self.slice(w_start, self.space.w_None)
+        self.slice(w_start, w_None)
 
     def SLICE_2(self, oparg):
         w_end = self.popvalue()
-        self.slice(self.space.w_None, w_end)
+        self.slice(w_None, w_end)
 
     def SLICE_3(self, oparg):
         w_end = self.popvalue()
     def storeslice(self, w_start, w_end):
         w_obj = self.popvalue()
         w_newvalue = self.popvalue()
-        self.space.setslice(w_obj, w_start, w_end, w_newvalue)
+        op.setslice(w_obj, w_start, w_end, w_newvalue).eval(self)
 
     def STORE_SLICE_0(self, oparg):
-        self.storeslice(self.space.w_None, self.space.w_None)
+        self.storeslice(w_None, w_None)
 
     def STORE_SLICE_1(self, oparg):
         w_start = self.popvalue()
-        self.storeslice(w_start, self.space.w_None)
+        self.storeslice(w_start, w_None)
 
     def STORE_SLICE_2(self, oparg):
         w_end = self.popvalue()
-        self.storeslice(self.space.w_None, w_end)
+        self.storeslice(w_None, w_end)
 
     def STORE_SLICE_3(self, oparg):
         w_end = self.popvalue()
 
     def deleteslice(self, w_start, w_end):
         w_obj = self.popvalue()
-        self.space.delslice(w_obj, w_start, w_end)
+        op.delslice(w_obj, w_start, w_end).eval(self)
 
     def DELETE_SLICE_0(self, oparg):
-        self.deleteslice(self.space.w_None, self.space.w_None)
+        self.deleteslice(w_None, w_None)
 
     def DELETE_SLICE_1(self, oparg):
         w_start = self.popvalue()
-        self.deleteslice(w_start, self.space.w_None)
+        self.deleteslice(w_start, w_None)
 
     def DELETE_SLICE_2(self, oparg):
         w_end = self.popvalue()
-        self.deleteslice(self.space.w_None, w_end)
+        self.deleteslice(w_None, w_end)
 
     def DELETE_SLICE_3(self, oparg):
         w_end = self.popvalue()
         self.deleteslice(w_start, w_end)
 
     def LIST_APPEND(self, oparg):
-        w = self.popvalue()
+        w_value = self.popvalue()
         if sys.version_info < (2, 7):
-            v = self.popvalue()
+            w_list = self.popvalue()
         else:
-            v = self.peekvalue(oparg - 1)
-        self.space.call_method(v, 'append', w)
+            w_list = self.peekvalue(oparg - 1)
+        w_append_meth = op.getattr(w_list, const('append')).eval(self)
+        op.simple_call(w_append_meth, w_value).eval(self)
 
     def DELETE_FAST(self, varindex):
         if self.locals_stack_w[varindex] is None:
         w_key = self.popvalue()
         w_value = self.popvalue()
         w_dict = self.peekvalue()
-        self.space.setitem(w_dict, w_key, w_value)
+        op.setitem(w_dict, w_key, w_value).eval(self)
 
     def STORE_SUBSCR(self, oparg):
         "obj[subscr] = newvalue"
         w_subscr = self.popvalue()
         w_obj = self.popvalue()
         w_newvalue = self.popvalue()
-        self.space.setitem(w_obj, w_subscr, w_newvalue)
+        op.setitem(w_obj, w_subscr, w_newvalue).eval(self)
 
     def BUILD_SLICE(self, numargs):
         if numargs == 3:
             w_step = self.popvalue()
         elif numargs == 2:
-            w_step = self.space.w_None
+            w_step = w_None
         else:
             raise BytecodeCorruption
         w_end = self.popvalue()
         w_start = self.popvalue()
-        w_slice = self.space.newslice(w_start, w_end, w_step)
+        w_slice = op.newslice(w_start, w_end, w_step).eval(self)
         self.pushvalue(w_slice)
 
     def DELETE_SUBSCR(self, oparg):
         "del obj[subscr]"
         w_subscr = self.popvalue()
         w_obj = self.popvalue()
-        self.space.delitem(w_obj, w_subscr)
+        op.delitem(w_obj, w_subscr).eval(self)
 
     def BUILD_TUPLE(self, itemcount):
         items = self.popvalues(itemcount)
-        w_tuple = self.space.newtuple(*items)
+        w_tuple = op.newtuple(*items).eval(self)
         self.pushvalue(w_tuple)
 
     def BUILD_LIST(self, itemcount):
         items = self.popvalues(itemcount)
-        w_list = self.space.newlist(*items)
+        w_list = op.newlist(*items).eval(self)
         self.pushvalue(w_list)
 
     def BUILD_MAP(self, itemcount):
-        w_dict = self.space.newdict()
+        w_dict = op.newdict().eval(self)
         self.pushvalue(w_dict)
 
     def NOP(self, *args):
     """Abstract base class for frame blocks from the blockstack,
     used by the SETUP_XXX and POP_BLOCK opcodes."""
 
-    def __init__(self, frame, handlerposition):
+    def __init__(self, ctx, handlerposition):
         self.handlerposition = handlerposition
-        self.valuestackdepth = frame.valuestackdepth
+        self.valuestackdepth = ctx.valuestackdepth
 
     def __eq__(self, other):
         return (self.__class__ is other.__class__ and
     def __hash__(self):
         return hash((self.handlerposition, self.valuestackdepth))
 
-    def cleanupstack(self, frame):
-        frame.dropvaluesuntil(self.valuestackdepth)
+    def cleanupstack(self, ctx):
+        ctx.dropvaluesuntil(self.valuestackdepth)
 
-    def handle(self, frame, unroller):
+    def handle(self, ctx, unroller):
         raise NotImplementedError
 
 class LoopBlock(FrameBlock):
 
     handles = (Break, Continue)
 
-    def handle(self, frame, unroller):
+    def handle(self, ctx, unroller):
         if isinstance(unroller, Continue):
             # re-push the loop block without cleaning up the value stack,
             # and jump to the beginning of the loop, stored in the
             # exception's argument
-            frame.blockstack.append(self)
+            ctx.blockstack.append(self)
             return unroller.jump_to
         else:
             # jump to the end of the loop
-            self.cleanupstack(frame)
+            self.cleanupstack(ctx)
             return self.handlerposition
 
 class ExceptBlock(FrameBlock):
 
     handles = Raise
 
-    def handle(self, frame, unroller):
+    def handle(self, ctx, unroller):
         # push the exception to the value stack for inspection by the
         # exception handler (the code after the except:)
-        self.cleanupstack(frame)
+        self.cleanupstack(ctx)
         assert isinstance(unroller, Raise)
         w_exc = unroller.w_exc
         # the stack setup is slightly different than in CPython:
         # instead of the traceback, we store the unroller object,
         # wrapped.
-        frame.pushvalue(unroller)
-        frame.pushvalue(w_exc.w_value)
-        frame.pushvalue(w_exc.w_type)
-        frame.last_exception = w_exc
+        ctx.pushvalue(unroller)
+        ctx.pushvalue(w_exc.w_value)
+        ctx.pushvalue(w_exc.w_type)
+        ctx.last_exception = w_exc
         return self.handlerposition   # jump to the handler
 
 class FinallyBlock(FrameBlock):
 
     handles = FlowSignal
 
-    def handle(self, frame, unroller):
+    def handle(self, ctx, unroller):
         # any abnormal reason for unrolling a finally: triggers the end of
         # the block unrolling and the entering the finally: handler.
-        self.cleanupstack(frame)
-        frame.pushvalue(unroller)
+        self.cleanupstack(ctx)
+        ctx.pushvalue(unroller)
         return self.handlerposition   # jump to the handler
 
 
 class WithBlock(FinallyBlock):
 
-    def handle(self, frame, unroller):
-        return FinallyBlock.handle(self, frame, unroller)
+    def handle(self, ctx, unroller):
+        return FinallyBlock.handle(self, ctx, unroller)

File rpython/flowspace/model.py

View file
  • Ignore whitespace
     type_with_bad_introspection = type(complex.real.__get__)
 
 def const(obj):
+    if hasattr(obj, "_flowspace_rewrite_directly_as_"):
+        obj = obj._flowspace_rewrite_directly_as_
     if isinstance(obj, (Variable, Constant)):
         raise TypeError("already wrapped: " + repr(obj))
     # method-wrapper have ill-defined comparison and introspection

File rpython/flowspace/objspace.py

View file
  • Ignore whitespace
-"""Implements the core parts of flow graph creation, in tandem
-with rpython.flowspace.flowcontext.
+"""Implements the main interface for flow graph creation: build_flow().
 """
 
-import __builtin__
-import sys
-import types
 from inspect import CO_NEWLOCALS
 
-from rpython.flowspace.argument import CallSpec
-from rpython.flowspace.model import (Constant, Variable, checkgraph, const,
-    FSException)
+from rpython.flowspace.model import Variable, checkgraph
 from rpython.flowspace.bytecode import HostCode
-from rpython.flowspace.operation import op, NOT_REALLY_CONST
-from rpython.flowspace.flowcontext import (FlowSpaceFrame, fixeggblocks,
-    FlowingError, Raise)
+from rpython.flowspace.flowcontext import (FlowContext, fixeggblocks)
 from rpython.flowspace.generator import (tweak_generator_graph,
         bootstrap_generator)
 from rpython.flowspace.pygraph import PyGraph
-from rpython.flowspace.specialcase import SPECIAL_CASES
-from rpython.rlib import rstackovf
-
 
 
 def _assert_rpythonic(func):
                 "the flag CO_NEWLOCALS set.")
 
 
-# ______________________________________________________________________
-class FlowObjSpace(object):
-    """NOT_RPYTHON.
-    The flow objspace space is used to produce a flow graph by recording
-    the space operations that the interpreter generates when it interprets
-    (the bytecode of) some function.
-    """
-    w_None = Constant(None)
-    sys = Constant(sys)
-    w_False = Constant(False)
-    w_True = Constant(True)
-    w_type = Constant(type)
-    w_tuple = Constant(tuple)
-    for exc in [KeyError, ValueError, IndexError, StopIteration,
-                AssertionError, TypeError, AttributeError, ImportError]:
-        clsname = exc.__name__
-        locals()['w_' + clsname] = Constant(exc)
-
-    # the following exceptions should not show up
-    # during flow graph construction
-    w_NameError = 'NameError'
-    w_UnboundLocalError = 'UnboundLocalError'
-    specialcases = SPECIAL_CASES
-
-    def build_flow(self, func):
-        return build_flow(func, self)
-
-    def newbool(self, b):
-        if b:
-            return self.w_True
-        else:
-            return self.w_False
-
-    def newfunction(self, w_code, w_globals, defaults_w):
-        if not all(isinstance(value, Constant) for value in defaults_w):
-            raise FlowingError("Dynamically created function must"
-                    " have constant default values.")
-        code = w_code.value
-        globals = w_globals.value
-        defaults = tuple([default.value for default in defaults_w])
-        fn = types.FunctionType(code, globals, code.co_name, defaults)
-        return Constant(fn)
-
-    def exception_match(self, w_exc_type, w_check_class):
-        """Checks if the given exception type matches 'w_check_class'."""
-        frame = self.frame
-        if not isinstance(w_check_class, Constant):
-            raise FlowingError("Non-constant except guard.")
-        check_class = w_check_class.value
-        if check_class in (NotImplementedError, AssertionError):
-            raise FlowingError(
-                "Catching %s is not valid in RPython" % check_class.__name__)
-        if not isinstance(check_class, tuple):
-            # the simple case
-            return frame.guessbool(self.issubtype(w_exc_type, w_check_class))
-        # special case for StackOverflow (see rlib/rstackovf.py)
-        if check_class == rstackovf.StackOverflow:
-            w_real_class = const(rstackovf._StackOverflow)
-            return frame.guessbool(self.issubtype(w_exc_type, w_real_class))
-        # checking a tuple of classes
-        for klass in w_check_class.value:
-            if self.exception_match(w_exc_type, const(klass)):
-                return True
-        return False
-
-    def exc_from_raise(self, w_arg1, w_arg2):
-        """
-        Create a wrapped exception from the arguments of a raise statement.
-
-        Returns an FSException object whose w_value is an instance of w_type.
-        """
-        frame = self.frame
-        if frame.guessbool(self.call_function(const(isinstance), w_arg1,
-                self.w_type)):
-            # this is for all cases of the form (Class, something)
-            if frame.guessbool(self.is_(w_arg2, self.w_None)):
-                # raise Type: we assume we have to instantiate Type
-                w_value = self.call_function(w_arg1)
-            else:
-                w_valuetype = self.type(w_arg2)
-                if frame.guessbool(self.issubtype(w_valuetype, w_arg1)):
-                    # raise Type, Instance: let etype be the exact type of value
-                    w_value = w_arg2
-                else:
-                    # raise Type, X: assume X is the constructor argument
-                    w_value = self.call_function(w_arg1, w_arg2)
-        else:
-            # the only case left here is (inst, None), from a 'raise inst'.
-            if not frame.guessbool(self.is_(w_arg2, self.w_None)):
-                exc = TypeError("instance exception may not have a "
-                                "separate value")
-                raise Raise(const(exc))
-            w_value = w_arg1
-        w_type = self.type(w_value)
-        return FSException(w_type, w_value)
-
-    def unpack_sequence(self, w_iterable, expected_length):
-        if isinstance(w_iterable, Constant):
-            l = list(w_iterable.value)
-            if len(l) != expected_length:
-                raise ValueError
-            return [const(x) for x in l]
-        else:
-            w_len = self.len(w_iterable)
-            w_correct = self.eq(w_len, const(expected_length))
-            if not self.frame.guessbool(self.bool(w_correct)):
-                w_exc = self.exc_from_raise(self.w_ValueError, self.w_None)
-                raise Raise(w_exc)
-            return [self.getitem(w_iterable, const(i))
-                        for i in range(expected_length)]
-
-    # ____________________________________________________________
-    def not_(self, w_obj):
-        return const(not self.frame.guessbool(self.bool(w_obj)))
-
-    def import_name(self, name, glob=None, loc=None, frm=None, level=-1):
-        try:
-            mod = __import__(name, glob, loc, frm, level)
-        except ImportError as e:
-            raise Raise(const(e))
-        return const(mod)
-
-    def import_from(self, w_module, w_name):
-        assert isinstance(w_module, Constant)
-        assert isinstance(w_name, Constant)
-        try:
-            return self.getattr(w_module, w_name)
-        except FlowingError:
-            exc = ImportError("cannot import name '%s'" % w_name.value)
-            raise Raise(const(exc))
-
-    def call_method(self, w_obj, methname, *arg_w):
-        w_meth = self.getattr(w_obj, const(methname))
-        return self.call_function(w_meth, *arg_w)
-
-    def call_function(self, w_func, *args_w):
-        args = CallSpec(list(args_w))
-        return self.call(w_func, args)
-
-    def appcall(self, func, *args_w):
-        """Call an app-level RPython function directly"""
-        w_func = const(func)
-        return op.simple_call(w_func, *args_w).eval(self.frame)
-
-    def call(self, w_callable, args):
-        if isinstance(w_callable, Constant):
-            fn = w_callable.value
-            if hasattr(fn, "_flowspace_rewrite_directly_as_"):
-                fn = fn._flowspace_rewrite_directly_as_
-                w_callable = const(fn)
-            try:
-                sc = self.specialcases[fn]   # TypeError if 'fn' not hashable
-            except (KeyError, TypeError):
-                pass
-            else:
-                if args.keywords:
-                    raise FlowingError(
-                        "should not call %r with keyword arguments" % (fn,))
-                return sc(self, *args.as_list())
-
-        if args.keywords or isinstance(args.w_stararg, Variable):
-            shape, args_w = args.flatten()
-            hlop = op.call_args(w_callable, Constant(shape), *args_w)
-        else:
-            hlop = op.simple_call(w_callable, *args.as_list())
-        return self.frame.do_op(hlop)
-
-    def find_global(self, w_globals, varname):
-        try:
-            value = w_globals.value[varname]
-        except KeyError:
-            # not in the globals, now look in the built-ins
-            try:
-                value = getattr(__builtin__, varname)
-            except AttributeError:
-                raise FlowingError("global name '%s' is not defined" % varname)
-        return const(value)
-
-for cls in op.__dict__.values():
-    if getattr(FlowObjSpace, cls.opname, None) is None:
-        setattr(FlowObjSpace, cls.opname, cls.make_sc())
-
-
-def build_flow(func, space=FlowObjSpace()):
+def build_flow(func):
     """
     Create the flow graph for the function.
     """
                 w_value.rename(name)
         return bootstrap_generator(graph)
     graph = PyGraph(func, code)
-    frame = space.frame = FlowSpaceFrame(space, graph, code)
-    frame.build_flow()
+    ctx = FlowContext(graph, code)
+    ctx.build_flow()
     fixeggblocks(graph)
     checkgraph(graph)
     if code.is_generator:

File rpython/flowspace/operation.py

View file
  • Ignore whitespace
 """
-This module defines mappings between operation names and Python's
-built-in functions (or type constructors) implementing them.
+This module defines all the SpaceOeprations used in rpython.flowspace.
 """
 
 import __builtin__
 import operator
 import sys
 import types
+from rpython.tool.pairtype import pair
 from rpython.rlib.unroll import unrolling_iterable, _unroller
 from rpython.tool.sourcetools import compile2
 from rpython.flowspace.model import (Constant, WrapException, const, Variable,
                                      SpaceOperation)
 from rpython.flowspace.specialcase import register_flow_sc
+from rpython.annotator.model import SomeTuple
+from rpython.flowspace.specialcase import SPECIAL_CASES
+
 
 NOT_REALLY_CONST = {
     Constant(sys): {
 }
 
 
-class _OpHolder(object): pass
+class _OpHolder(object):
+    pass
 op = _OpHolder()
 
 func2op = {}
     __metaclass__ = HLOperationMeta
     pure = False
     can_overflow = False
+    dispatch = None  # number of arguments to dispatch on
+                     # (None means special handling)
 
     def __init__(self, *args):
         self.args = list(args)
 
     @classmethod
     def make_sc(cls):
-        def sc_operator(space, *args_w):
-            return cls(*args_w).eval(space.frame)
+        def sc_operator(ctx, *args_w):
+            return cls(*args_w).eval(ctx)
         return sc_operator
 
-    def eval(self, frame):
+    def eval(self, ctx):
         result = self.constfold()
         if result is not None:
             return result
-        return frame.do_op(self)
+        return ctx.do_op(self)
 
     def constfold(self):
         return None
 
 class OverflowingOperation(PureOperation):
     can_overflow = True
+
     def ovfchecked(self):
         ovf = self.ovf_variant(*self.args)
         ovf.offset = self.offset
         return ovf
 
+class SingleDispatchMixin(object):
+    dispatch = 1
 
-def add_operator(name, arity, pyfunc=None, pure=False, ovf=False):
+    def consider(self, annotator, arg, *other_args):
+        impl = getattr(arg, self.opname)
+        return impl(*other_args)
+
+class DoubleDispatchMixin(object):
+    dispatch = 2
+
+    def consider(self, annotator, arg1, arg2, *other_args):
+        impl = getattr(pair(arg1, arg2), self.opname)
+        return impl(*other_args)
+
+
+def add_operator(name, arity, dispatch=None, pyfunc=None, pure=False, ovf=False):
     operator_func = getattr(operator, name, None)
+    if dispatch == 1:
+        bases = [SingleDispatchMixin]
+    elif dispatch == 2:
+        bases = [DoubleDispatchMixin]
+    else:
+        bases = []
     if ovf:
         assert pure
         base_cls = OverflowingOperation
         base_cls = PureOperation
     else:
         base_cls = HLOperation
-    cls = HLOperationMeta(name, (base_cls,), {'opname': name, 'arity': arity,
-                                              'canraise': []})
+    bases.append(base_cls)
+    cls = HLOperationMeta(name, tuple(bases), {'opname': name, 'arity': arity,
+                                              'canraise': [],
+                                              'dispatch': dispatch})
     if pyfunc is not None:
         func2op[pyfunc] = cls
     if operator_func:
     if ovf:
         from rpython.rlib.rarithmetic import ovfcheck
         ovf_func = lambda *args: ovfcheck(cls.pyfunc(*args))
-        add_operator(name + '_ovf', arity, pyfunc=ovf_func)
+        add_operator(name + '_ovf', arity, dispatch, pyfunc=ovf_func)
         cls.ovf_variant = getattr(op, name + '_ovf')
 
 # ____________________________________________________________
     raise ValueError("this is not supported")
 
 
-add_operator('is_', 2, pure=True)
-add_operator('id', 1, pyfunc=id)
-add_operator('type', 1, pyfunc=new_style_type, pure=True)
-add_operator('issubtype', 2, pyfunc=issubclass, pure=True)  # not for old-style classes
-add_operator('repr', 1, pyfunc=repr, pure=True)
-add_operator('str', 1, pyfunc=str, pure=True)
+add_operator('is_', 2, dispatch=2, pure=True)
+add_operator('id', 1, dispatch=1, pyfunc=id)
+add_operator('type', 1, dispatch=1, pyfunc=new_style_type, pure=True)
+add_operator('issubtype', 2, dispatch=1, pyfunc=issubclass, pure=True)  # not for old-style classes
+add_operator('repr', 1, dispatch=1, pyfunc=repr, pure=True)
+add_operator('str', 1, dispatch=1, pyfunc=str, pure=True)
 add_operator('format', 2, pyfunc=unsupported)
-add_operator('len', 1, pyfunc=len, pure=True)
-add_operator('hash', 1, pyfunc=hash)
-add_operator('setattr', 3, pyfunc=setattr)
-add_operator('delattr', 2, pyfunc=delattr)
-add_operator('getitem', 2, pure=True)
-add_operator('getitem_idx', 2, pure=True)
-add_operator('getitem_key', 2, pure=True)
-add_operator('getitem_idx_key', 2, pure=True)
-add_operator('setitem', 3)
-add_operator('delitem', 2)
-add_operator('getslice', 3, pyfunc=do_getslice, pure=True)
-add_operator('setslice', 4, pyfunc=do_setslice)
-add_operator('delslice', 3, pyfunc=do_delslice)
+add_operator('len', 1, dispatch=1, pyfunc=len, pure=True)
+add_operator('hash', 1, dispatch=1, pyfunc=hash)
+add_operator('setattr', 3, dispatch=1, pyfunc=setattr)
+add_operator('delattr', 2, dispatch=1, pyfunc=delattr)
+add_operator('getitem', 2, dispatch=2, pure=True)
+add_operator('getitem_idx', 2, dispatch=2, pure=True)
+add_operator('getitem_key', 2, dispatch=2, pure=True)
+add_operator('getitem_idx_key', 2, dispatch=2, pure=True)
+add_operator('setitem', 3, dispatch=2)
+add_operator('delitem', 2, dispatch=2)
+add_operator('getslice', 3, dispatch=1, pyfunc=do_getslice, pure=True)
+add_operator('setslice', 4, dispatch=1, pyfunc=do_setslice)
+add_operator('delslice', 3, dispatch=1, pyfunc=do_delslice)
 add_operator('trunc', 1, pyfunc=unsupported)
-add_operator('pos', 1, pure=True)
-add_operator('neg', 1, pure=True, ovf=True)
-add_operator('bool', 1, pyfunc=bool, pure=True)
+add_operator('pos', 1, dispatch=1, pure=True)
+add_operator('neg', 1, dispatch=1, pure=True, ovf=True)
+add_operator('bool', 1, dispatch=1, pyfunc=bool, pure=True)
 op.is_true = op.nonzero = op.bool  # for llinterp
-add_operator('abs', 1, pyfunc=abs, pure=True, ovf=True)
-add_operator('hex', 1, pyfunc=hex, pure=True)
-add_operator('oct', 1, pyfunc=oct, pure=True)
-add_operator('ord', 1, pyfunc=ord, pure=True)
-add_operator('invert', 1, pure=True)
-add_operator('add', 2, pure=True, ovf=True)
-add_operator('sub', 2, pure=True, ovf=True)
-add_operator('mul', 2, pure=True, ovf=True)
-add_operator('truediv', 2, pure=True)
-add_operator('floordiv', 2, pure=True, ovf=True)
-add_operator('div', 2, pure=True, ovf=True)
-add_operator('mod', 2, pure=True, ovf=True)
+add_operator('abs', 1, dispatch=1, pyfunc=abs, pure=True, ovf=True)
+add_operator('hex', 1, dispatch=1, pyfunc=hex, pure=True)
+add_operator('oct', 1, dispatch=1, pyfunc=oct, pure=True)
+add_operator('ord', 1, dispatch=1, pyfunc=ord, pure=True)
+add_operator('invert', 1, dispatch=1, pure=True)
+add_operator('add', 2, dispatch=2, pure=True, ovf=True)
+add_operator('sub', 2, dispatch=2, pure=True, ovf=True)
+add_operator('mul', 2, dispatch=2, pure=True, ovf=True)
+add_operator('truediv', 2, dispatch=2, pure=True)
+add_operator('floordiv', 2, dispatch=2, pure=True, ovf=True)
+add_operator('div', 2, dispatch=2, pure=True, ovf=True)
+add_operator('mod', 2, dispatch=2, pure=True, ovf=True)
 add_operator('divmod', 2, pyfunc=divmod, pure=True)
-add_operator('lshift', 2, pure=True, ovf=True)
-add_operator('rshift', 2, pure=True)
-add_operator('and_', 2, pure=True)
-add_operator('or_', 2, pure=True)
-add_operator('xor', 2, pure=True)
-add_operator('int', 1, pyfunc=do_int, pure=True)
+add_operator('lshift', 2, dispatch=2, pure=True, ovf=True)
+add_operator('rshift', 2, dispatch=2, pure=True)
+add_operator('and_', 2, dispatch=2, pure=True)
+add_operator('or_', 2, dispatch=2, pure=True)