Commits

Ronan Lamy  committed 1f657fe Merge

(ronan, Edd reviewing) hg merge less-stringly-ops

Use subclasses of SpaceOperation instead of SpaceOperator objects.
Random cleanups in flowspace.

  • Participants
  • Parent commits d8a0476, 04c8d02

Comments (0)

Files changed (40)

File rpython/annotator/argument.py

             return [Ellipsis]
         raise CallPatternTooComplex("'*' argument must be SomeTuple")
 
-    def is_true(self, s_tup):
+    def bool(self, s_tup):
         assert isinstance(s_tup, SomeTuple)
         return bool(s_tup.items)
 
             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)
+            assert not space.bool(data_w_stararg)
         else:
             stararg_w = space.unpackiterable(data_w_stararg)
             args_w = data_args_w + stararg_w

File rpython/annotator/bookkeeper.py

         fn, block, i = self.position_key
         op = block.operations[i]
         if opname is not None:
-            assert op.opname == opname or op.opname in opname
+            assert op.opname == opname
         if arity is not None:
             assert len(op.args) == arity
         if pos is not None:

File rpython/annotator/builtin.py

 
 
 def builtin_bool(s_obj):
-    return s_obj.is_true()
+    return s_obj.bool()
 
 def builtin_int(s_obj, s_base=None):
     if isinstance(s_obj, SomeInteger):

File rpython/annotator/test/test_annrpython.py

         s = a.build_types(snippet.prime, [int])
         assert s.knowntype == bool
 
-    def test_and_is_true_coalesce(self):
+    def test_and_bool_coalesce(self):
         def f(a,b,c,d,e):
             x = a and b
             if x:
         s = a.build_types(f, [int, str, a.bookkeeper.immutablevalue(1.0), a.bookkeeper.immutablevalue('d'), a.bookkeeper.immutablevalue('e')])
         assert s == annmodel.SomeTuple([annmodel.SomeChar(), a.bookkeeper.immutablevalue(1.0)])
 
-    def test_is_true_coalesce2(self):
+    def test_bool_coalesce2(self):
         def f(a,b,a1,b1,c,d,e):
             x = (a or  b) and (a1 or b1)
             if x:
         assert s == annmodel.SomeTuple([annmodel.SomeChar(),
                                         a.bookkeeper.immutablevalue(1.0)])
 
-    def test_is_true_coalesce_sanity(self):
+    def test_bool_coalesce_sanity(self):
         def f(a):
             while a:
                 pass

File rpython/annotator/test/test_argument.py

     def newtuple(self, items):
         return tuple(items)
 
-    def is_true(self, obj):
+    def bool(self, obj):
         return bool(obj)
 
     def unpackiterable(self, it):

File rpython/annotator/unaryop.py

 def immutablevalue(x):
     return getbookkeeper().immutablevalue(x)
 
-UNARY_OPERATIONS = set(['len', 'is_true', 'getattr', 'setattr', 'delattr',
+UNARY_OPERATIONS = set(['len', 'bool', 'getattr', 'setattr', 'delattr',
                         'simple_call', 'call_args', 'str', 'repr',
                         'iter', 'next', 'invert', 'type', 'issubtype',
-                        'pos', 'neg', 'nonzero', 'abs', 'hex', 'oct',
+                        'pos', 'neg', 'abs', 'hex', 'oct',
                         'ord', 'int', 'float', 'long',
                         'hash', 'id',    # <== not supported any more
                         'getslice', 'setslice', 'delslice',
     def len(obj):
         return SomeInteger(nonneg=True)
 
-    def is_true_behavior(obj, s):
+    def bool_behavior(obj, s):
         if obj.is_immutable_constant():
             s.const = bool(obj.const)
         else:
             if s_len.is_immutable_constant():
                 s.const = s_len.const > 0
 
-    def is_true(s_obj):
+    def bool(s_obj):
         r = SomeBool()
-        s_obj.is_true_behavior(r)
+        s_obj.bool_behavior(r)
 
         bk = getbookkeeper()
         knowntypedata = {}
-        op = bk._find_current_op(opname=("is_true", "nonzero"), arity=1)
+        op = bk._find_current_op(opname="bool", arity=1)
         arg = op.args[0]
         s_nonnone_obj = s_obj
         if s_obj.can_be_none():
         r.set_knowntypedata(knowntypedata)
         return r
 
-    def nonzero(obj):
-        return obj.is_true()
-
     def hash(obj):
         raise AnnotatorError("cannot use hash() in RPython")
 
 
     abs = neg
 
-    def is_true(self):
+    def bool(self):
         if self.is_immutable_constant():
             return getbookkeeper().immutablevalue(bool(self.const))
         return s_Bool
     abs_ovf = _clone(abs, [OverflowError])
 
 class __extend__(SomeBool):
-    def is_true(self):
+    def bool(self):
         return self
 
     def invert(self):
             # create or update the attribute in clsdef
             clsdef.generalize_attr(attr, s_value)
 
-    def is_true_behavior(ins, s):
+    def bool_behavior(ins, s):
         if not ins.can_be_None:
             s.const = True
 
         d = [desc.bind_under(classdef, name) for desc in pbc.descriptions]
         return SomePBC(d, can_be_None=pbc.can_be_None)
 
-    def is_true_behavior(pbc, s):
+    def bool_behavior(pbc, s):
         if pbc.isNone():
             s.const = False
         elif not pbc.can_be_None:
         v = p.ll_ptrtype._example()(*llargs)
         return ll_to_annotation(v)
 
-    def is_true(p):
+    def bool(p):
         return s_Bool
 
 class __extend__(SomeLLADTMeth):
             llmemory.supported_access_types[s_attr.const])
     getattr.can_only_throw = []
 
-    def is_true(s_addr):
+    def bool(s_addr):
         return s_Bool

File rpython/flowspace/bytecode.py

 """
 from rpython.tool.stdlib_opcode import host_bytecode_spec
 from opcode import EXTENDED_ARG, HAVE_ARGUMENT
+import opcode
 from rpython.flowspace.argument import Signature
 from rpython.flowspace.flowcontext import BytecodeCorruption
 
         Returns (next_instr, opname, oparg).
         """
         co_code = self.co_code
-        opcode = ord(co_code[pos])
+        opnum = ord(co_code[pos])
         next_instr = pos + 1
 
-        if opcode >= HAVE_ARGUMENT:
+        if opnum >= HAVE_ARGUMENT:
             lo = ord(co_code[next_instr])
             hi = ord(co_code[next_instr+1])
             next_instr += 2
         else:
             oparg = 0
 
-        while opcode == EXTENDED_ARG:
-            opcode = ord(co_code[next_instr])
-            if opcode < HAVE_ARGUMENT:
+        while opnum == EXTENDED_ARG:
+            opnum = ord(co_code[next_instr])
+            if opnum < HAVE_ARGUMENT:
                 raise BytecodeCorruption
             lo = ord(co_code[next_instr+1])
             hi = ord(co_code[next_instr+2])
             next_instr += 3
             oparg = (oparg * 65536) | (hi * 256) | lo
 
-        opname = self.opnames[opcode]
+        if opnum in opcode.hasjrel:
+            oparg += next_instr
+        opname = self.opnames[opnum]
         return next_instr, opname, oparg
 
     @property

File rpython/flowspace/flowcontext.py

 from rpython.tool.stdlib_opcode import host_bytecode_spec
 from rpython.flowspace.argument import CallSpec
 from rpython.flowspace.model import (Constant, Variable, Block, Link,
-    c_last_exception, SpaceOperation)
+    c_last_exception, SpaceOperation, const)
 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 op
 
 
 class FlowingError(Exception):
     """ Signals invalid RPython in the function being analysed"""
-    def __init__(self, frame, msg):
-        super(FlowingError, self).__init__(msg)
-        self.frame = frame
+    frame = None
 
     def __str__(self):
         msg = ["\n"]
     def append(self, operation):
         raise NotImplementedError
 
-    def guessbool(self, frame, w_condition, **kwds):
+    def guessbool(self, frame, w_condition):
         raise AssertionError("cannot guessbool(%s)" % (w_condition,))
 
 
                       [str(s) for s in self.listtoreplay[self.index:]]))
         self.index += 1
 
-    def guessbool(self, frame, w_condition, **kwds):
+    def guessbool(self, frame, w_condition):
         assert self.index == len(self.listtoreplay)
         frame.recorder = self.nextreplayer
         return self.booloutcome
 
 def unsupportedoperation(OPCODE, msg):
     def UNSUPPORTED(self, *ignored):
-        raise FlowingError(self, "%s is not RPython" % (msg,))
+        raise FlowingError("%s is not RPython" % (msg,))
     UNSUPPORTED.func_name = OPCODE
     return UNSUPPORTED
 
         if closure is None:
             self.closure = []
         else:
-            self.closure = [self.space.wrap(c.cell_contents) for c in closure]
+            self.closure = [const(c.cell_contents) for c in closure]
         assert len(self.closure) == len(self.pycode.co_freevars)
 
     def init_locals_stack(self, code):
         self.locals_stack_w[:len(items_w)] = items_w
         self.dropvaluesuntil(len(items_w))
 
-    def unrollstack(self, unroller_kind):
-        while self.blockstack:
-            block = self.blockstack.pop()
-            if (block.handling_mask & unroller_kind) != 0:
-                return block
-            block.cleanupstack(self)
-        return None
-
-    def unrollstack_and_jump(self, unroller):
-        block = self.unrollstack(unroller.kind)
-        if block is None:
-            raise BytecodeCorruption("misplaced bytecode - should not return")
-        return block.handle(self, unroller)
-
-    def getstate(self):
+    def getstate(self, next_pos):
         # getfastscope() can return real None, for undefined locals
         data = self.save_locals_stack()
         if self.last_exception is None:
         else:
             data.append(self.last_exception.w_type)
             data.append(self.last_exception.w_value)
-        recursively_flatten(self.space, data)
-        return FrameState(data, self.blockstack[:], self.last_instr)
+        recursively_flatten(data)
+        return FrameState(data, self.blockstack[:], next_pos)
 
     def setstate(self, state):
         """ Reset the frame to the given state. """
         data = state.mergeable[:]
-        recursively_unflatten(self.space, data)
+        recursively_unflatten(data)
         self.restore_locals_stack(data[:-2])  # Nones == undefined locals
         if data[-2] == Constant(None):
             assert data[-1] == Constant(None)
             self.last_exception = None
         else:
             self.last_exception = FSException(data[-2], data[-1])
-        self.last_instr = state.next_instr
         self.blockstack = state.blocklist[:]
 
-    def guessbool(self, w_condition, **kwds):
-        return self.recorder.guessbool(self, w_condition, **kwds)
+    def guessbool(self, w_condition):
+        if isinstance(w_condition, Constant):
+            return w_condition.value
+        return self.recorder.guessbool(self, w_condition)
 
     def do_operation(self, name, *args_w):
+        spaceop = SpaceOperation(name, args_w, Variable())
+        self.record(spaceop)
+        return spaceop.result
+
+    def record(self, spaceop):
         recorder = self.recorder
         if getattr(recorder, 'final_state', None) is not None:
             self.mergeblock(recorder.crnt_block, recorder.final_state)
             raise StopFlowing
-        spaceop = SpaceOperation(name, args_w, Variable())
         spaceop.offset = self.last_instr
         recorder.append(spaceop)
-        return spaceop.result
 
-    def do_operation_with_implicit_exceptions(self, name, *args_w):
-        w_result = self.do_operation(name, *args_w)
-        oper = getattr(op, name)
-        self.handle_implicit_exceptions(oper.canraise)
-        return w_result
+    def do_op(self, op):
+        self.record(op)
+        if op.canraise:
+            self.guessexception(op.canraise)
+        return op.result
 
-    def handle_implicit_exceptions(self, exceptions):
+    def guessexception(self, exceptions, force=False):
         """
         Catch possible exceptions implicitly.
 
         even if the interpreter re-raises the exception, it will not be the
         same ImplicitOperationError instance internally.
         """
-        if not exceptions:
+        if not force and not any(isinstance(block, (ExceptBlock, FinallyBlock))
+                for block in self.blockstack):
+            # The implicit exception wouldn't be caught and would later get
+            # removed, so don't bother creating it.
             return
-        return self.recorder.guessexception(self, *exceptions)
+        self.recorder.guessexception(self, *exceptions)
 
     def build_flow(self):
         graph = self.graph
 
     def record_block(self, block):
         self.setstate(block.framestate)
+        next_pos = block.framestate.next_instr
         self.recorder = block.make_recorder()
         try:
             while True:
-                self.last_instr = self.handle_bytecode(self.last_instr)
-                self.recorder.final_state = self.getstate()
+                next_pos = self.handle_bytecode(next_pos)
+                self.recorder.final_state = self.getstate(next_pos)
 
         except ImplicitOperationError, e:
             if isinstance(e.w_type, Constant):
             link = Link([w_result], self.graph.returnblock)
             self.recorder.crnt_block.closeblock(link)
 
+        except FlowingError as exc:
+            if exc.frame is None:
+                exc.frame = self
+            raise
+
         self.recorder = None
 
     def mergeblock(self, currentblock, currentstate):
                     break
 
     def handle_bytecode(self, next_instr):
+        self.last_instr = next_instr
         next_instr, methodname, oparg = self.pycode.read(next_instr)
         try:
-            res = getattr(self, methodname)(oparg, next_instr)
+            res = getattr(self, methodname)(oparg)
             return res if res is not None else next_instr
         except FSException, operr:
             return self.handle_operation_error(operr)
 
     def handle_operation_error(self, operr):
-        block = self.unrollstack(SApplicationException.kind)
-        if block is None:
-            raise operr
-        else:
-            unroller = SApplicationException(operr)
-            next_instr = block.handle(self, unroller)
-            return next_instr
+        unroller = SApplicationException(operr)
+        return unroller.unroll(self)
 
     def getlocalvarname(self, index):
         return self.pycode.co_varnames[index]
 
     def getconstant_w(self, index):
-        return self.space.wrap(self.pycode.consts[index])
+        return const(self.pycode.consts[index])
 
     def getname_u(self, index):
         return self.pycode.names[index]
     def getname_w(self, index):
         return Constant(self.pycode.names[index])
 
-    def BAD_OPCODE(self, _, next_instr):
-        raise FlowingError(self, "This operation is not RPython")
+    def BAD_OPCODE(self, _):
+        raise FlowingError("This operation is not RPython")
 
-    def BREAK_LOOP(self, oparg, next_instr):
-        return self.unrollstack_and_jump(SBreakLoop.singleton)
+    def BREAK_LOOP(self, oparg):
+        return SBreakLoop.singleton.unroll(self)
 
-    def CONTINUE_LOOP(self, startofloop, next_instr):
+    def CONTINUE_LOOP(self, startofloop):
         unroller = SContinueLoop(startofloop)
-        return self.unrollstack_and_jump(unroller)
+        return unroller.unroll(self)
 
     def cmp_lt(self, w_1, w_2):
         return self.space.lt(w_1, w_2)
     def cmp_exc_match(self, w_1, w_2):
         return self.space.newbool(self.space.exception_match(w_1, w_2))
 
-    def COMPARE_OP(self, testnum, next_instr):
+    def COMPARE_OP(self, testnum):
         w_2 = self.popvalue()
         w_1 = self.popvalue()
         w_result = getattr(self, compare_method[testnum])(w_1, w_2)
         self.pushvalue(w_result)
 
-    def RAISE_VARARGS(self, nbargs, next_instr):
+    def RAISE_VARARGS(self, nbargs):
         space = self.space
         if nbargs == 0:
             if self.last_exception is not None:
         operror = space.exc_from_raise(w_type, w_value)
         raise operror
 
-    def IMPORT_NAME(self, nameindex, next_instr):
+    def IMPORT_NAME(self, nameindex):
         space = self.space
         modulename = self.getname_u(nameindex)
-        glob = space.unwrap(self.w_globals)
-        fromlist = space.unwrap(self.popvalue())
+        glob = self.w_globals.value
+        fromlist = self.popvalue().value
         level = self.popvalue().value
         w_obj = space.import_name(modulename, glob, None, fromlist, level)
         self.pushvalue(w_obj)
 
-    def IMPORT_FROM(self, nameindex, next_instr):
+    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))
 
-    def RETURN_VALUE(self, oparg, next_instr):
+    def RETURN_VALUE(self, oparg):
         w_returnvalue = self.popvalue()
-        block = self.unrollstack(SReturnValue.kind)
-        if block is None:
-            raise Return(w_returnvalue)
-        else:
-            unroller = SReturnValue(w_returnvalue)
-            next_instr = block.handle(self, unroller)
-            return next_instr    # now inside a 'finally' block
+        unroller = SReturnValue(w_returnvalue)
+        return unroller.unroll(self)
 
-    def END_FINALLY(self, oparg, next_instr):
+    def END_FINALLY(self, oparg):
         # unlike CPython, there are two statically distinct cases: the
         # END_FINALLY might be closing an 'except' block or a 'finally'
         # block.  In the first case, the stack contains three items:
             return
         elif isinstance(w_top, SuspendedUnroller):
             # case of a finally: block
-            return self.unroll_finally(w_top)
+            return w_top.unroll(self)
         else:
             # case of an except: block.  We popped the exception type
             self.popvalue()        #     Now we pop the exception value
             unroller = self.popvalue()
-            return self.unroll_finally(unroller)
+            return unroller.unroll(self)
 
-    def unroll_finally(self, unroller):
-        # go on unrolling the stack
-        block = self.unrollstack(unroller.kind)
-        if block is None:
-            unroller.nomoreblocks()
-        else:
-            return block.handle(self, unroller)
-
-    def POP_BLOCK(self, oparg, next_instr):
+    def POP_BLOCK(self, oparg):
         block = self.blockstack.pop()
         block.cleanupstack(self)  # the block knows how to clean up the value stack
 
-    def JUMP_ABSOLUTE(self, jumpto, next_instr):
+    def JUMP_ABSOLUTE(self, jumpto):
         return jumpto
 
-    def YIELD_VALUE(self, _, next_instr):
+    def YIELD_VALUE(self, _):
         assert self.pycode.is_generator
         w_result = self.popvalue()
         self.do_operation('yield', w_result)
     PRINT_ITEM_TO = BAD_OPCODE
     PRINT_NEWLINE_TO = BAD_OPCODE
 
-    def PRINT_ITEM(self, oparg, next_instr):
+    def PRINT_ITEM(self, oparg):
         w_item = self.popvalue()
         w_s = self.do_operation('str', w_item)
         self.space.appcall(rpython_print_item, w_s)
 
-    def PRINT_NEWLINE(self, oparg, next_instr):
+    def PRINT_NEWLINE(self, oparg):
         self.space.appcall(rpython_print_newline)
 
-    def JUMP_FORWARD(self, jumpby, next_instr):
-        next_instr += jumpby
-        return next_instr
+    def JUMP_FORWARD(self, target):
+        return target
 
-    def JUMP_IF_FALSE(self, stepby, next_instr):
+    def JUMP_IF_FALSE(self, target):
         # Python <= 2.6 only
         w_cond = self.peekvalue()
-        if not self.space.is_true(w_cond):
-            next_instr += stepby
-        return next_instr
+        if not self.guessbool(self.space.bool(w_cond)):
+            return target
 
-    def JUMP_IF_TRUE(self, stepby, next_instr):
+    def JUMP_IF_TRUE(self, target):
         # Python <= 2.6 only
         w_cond = self.peekvalue()
-        if self.space.is_true(w_cond):
-            next_instr += stepby
-        return next_instr
+        if self.guessbool(self.space.bool(w_cond)):
+            return target
 
-    def POP_JUMP_IF_FALSE(self, target, next_instr):
+    def POP_JUMP_IF_FALSE(self, target):
         w_value = self.popvalue()
-        if not self.space.is_true(w_value):
+        if not self.guessbool(self.space.bool(w_value)):
             return target
-        return next_instr
 
-    def POP_JUMP_IF_TRUE(self, target, next_instr):
+    def POP_JUMP_IF_TRUE(self, target):
         w_value = self.popvalue()
-        if self.space.is_true(w_value):
+        if self.guessbool(self.space.bool(w_value)):
             return target
-        return next_instr
 
-    def JUMP_IF_FALSE_OR_POP(self, target, next_instr):
+    def JUMP_IF_FALSE_OR_POP(self, target):
         w_value = self.peekvalue()
-        if not self.space.is_true(w_value):
+        if not self.guessbool(self.space.bool(w_value)):
             return target
         self.popvalue()
-        return next_instr
 
-    def JUMP_IF_TRUE_OR_POP(self, target, next_instr):
+    def JUMP_IF_TRUE_OR_POP(self, target):
         w_value = self.peekvalue()
-        if self.space.is_true(w_value):
+        if self.guessbool(self.space.bool(w_value)):
             return target
         self.popvalue()
-        return next_instr
 
-    def JUMP_IF_NOT_DEBUG(self, target, next_instr):
-        return next_instr
+    def JUMP_IF_NOT_DEBUG(self, target):
+        pass
 
-    def GET_ITER(self, oparg, next_instr):
+    def GET_ITER(self, oparg):
         w_iterable = self.popvalue()
         w_iterator = self.space.iter(w_iterable)
         self.pushvalue(w_iterator)
 
-    def FOR_ITER(self, jumpby, next_instr):
+    def FOR_ITER(self, target):
         w_iterator = self.peekvalue()
         try:
             w_nextitem = self.space.next(w_iterator)
                 raise
             # iterator exhausted
             self.popvalue()
-            next_instr += jumpby
+            return target
         else:
             self.pushvalue(w_nextitem)
-        return next_instr
 
-    def SETUP_LOOP(self, offsettoend, next_instr):
-        block = LoopBlock(self, next_instr + offsettoend)
+    def SETUP_LOOP(self, target):
+        block = LoopBlock(self, target)
         self.blockstack.append(block)
 
-    def SETUP_EXCEPT(self, offsettoend, next_instr):
-        block = ExceptBlock(self, next_instr + offsettoend)
+    def SETUP_EXCEPT(self, target):
+        block = ExceptBlock(self, target)
         self.blockstack.append(block)
 
-    def SETUP_FINALLY(self, offsettoend, next_instr):
-        block = FinallyBlock(self, next_instr + offsettoend)
+    def SETUP_FINALLY(self, target):
+        block = FinallyBlock(self, target)
         self.blockstack.append(block)
 
-    def SETUP_WITH(self, offsettoend, next_instr):
+    def SETUP_WITH(self, target):
         # A simpler version than the 'real' 2.7 one:
         # 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, self.space.wrap("__exit__"))
+        w_exit = self.space.getattr(w_manager, const("__exit__"))
         self.settopvalue(w_exit)
         w_result = self.space.call_method(w_manager, "__enter__")
-        block = WithBlock(self, next_instr + offsettoend)
+        block = WithBlock(self, target)
         self.blockstack.append(block)
         self.pushvalue(w_result)
 
-    def WITH_CLEANUP(self, oparg, next_instr):
+    def WITH_CLEANUP(self, oparg):
         # Note: RPython context managers receive None in lieu of tracebacks
         # and cannot suppress the exception.
         # This opcode changed a lot between CPython versions
         else:
             self.space.call_function(w_exitfunc, w_None, w_None, w_None)
 
-    def LOAD_FAST(self, varindex, next_instr):
+    def LOAD_FAST(self, varindex):
         w_value = self.locals_stack_w[varindex]
         if w_value is None:
-            raise FlowingError(self, "Local variable referenced before assignment")
+            raise FlowingError("Local variable referenced before assignment")
         self.pushvalue(w_value)
 
-    def LOAD_CONST(self, constindex, next_instr):
+    def LOAD_CONST(self, constindex):
         w_const = self.getconstant_w(constindex)
         self.pushvalue(w_const)
 
-    def LOAD_GLOBAL(self, nameindex, next_instr):
+    def LOAD_GLOBAL(self, nameindex):
         w_result = self.space.find_global(self.w_globals, self.getname_u(nameindex))
         self.pushvalue(w_result)
     LOAD_NAME = LOAD_GLOBAL
 
-    def LOAD_ATTR(self, nameindex, next_instr):
+    def LOAD_ATTR(self, nameindex):
         "obj.attributename"
         w_obj = self.popvalue()
         w_attributename = self.getname_w(nameindex)
         self.pushvalue(w_value)
     LOOKUP_METHOD = LOAD_ATTR
 
-    def LOAD_DEREF(self, varindex, next_instr):
+    def LOAD_DEREF(self, varindex):
         self.pushvalue(self.closure[varindex])
 
-    def STORE_FAST(self, varindex, next_instr):
+    def STORE_FAST(self, varindex):
         w_newvalue = self.popvalue()
         assert w_newvalue is not None
         self.locals_stack_w[varindex] = w_newvalue
 
-    def STORE_GLOBAL(self, nameindex, next_instr):
+    def STORE_GLOBAL(self, nameindex):
         varname = self.getname_u(nameindex)
-        raise FlowingError(self,
-                "Attempting to modify global variable  %r." % (varname))
+        raise FlowingError(
+            "Attempting to modify global variable  %r." % (varname))
 
-    def POP_TOP(self, oparg, next_instr):
+    def POP_TOP(self, oparg):
         self.popvalue()
 
-    def ROT_TWO(self, oparg, next_instr):
+    def ROT_TWO(self, oparg):
         w_1 = self.popvalue()
         w_2 = self.popvalue()
         self.pushvalue(w_1)
         self.pushvalue(w_2)
 
-    def ROT_THREE(self, oparg, next_instr):
+    def ROT_THREE(self, oparg):
         w_1 = self.popvalue()
         w_2 = self.popvalue()
         w_3 = self.popvalue()
         self.pushvalue(w_3)
         self.pushvalue(w_2)
 
-    def ROT_FOUR(self, oparg, next_instr):
+    def ROT_FOUR(self, oparg):
         w_1 = self.popvalue()
         w_2 = self.popvalue()
         w_3 = self.popvalue()
         self.pushvalue(w_3)
         self.pushvalue(w_2)
 
-    def DUP_TOP(self, oparg, next_instr):
+    def DUP_TOP(self, oparg):
         w_1 = self.peekvalue()
         self.pushvalue(w_1)
 
-    def DUP_TOPX(self, itemcount, next_instr):
+    def DUP_TOPX(self, itemcount):
         delta = itemcount - 1
         while True:
             itemcount -= 1
     for OPCODE, op in _unsupported_ops:
         locals()[OPCODE] = unsupportedoperation(OPCODE, op)
 
-    def BUILD_LIST_FROM_ARG(self, _, next_instr):
+    def BUILD_LIST_FROM_ARG(self, _):
         # This opcode was added with pypy-1.8.  Here is a simpler
         # version, enough for annotation.
         last_val = self.popvalue()
 
     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")
+            raise FlowingError("Dict-unpacking is not RPython")
         n_arguments = oparg & 0xff
         n_keywords = (oparg >> 8) & 0xff
         keywords = {}
         for _ in range(n_keywords):
             w_value = self.popvalue()
             w_key = self.popvalue()
-            key = self.space.str_w(w_key)
+            key = w_key.value
             keywords[key] = w_value
         arguments = self.popvalues(n_arguments)
         args = CallSpec(arguments, keywords, w_star, w_starstar)
         w_result = self.space.call_args(w_function, args)
         self.pushvalue(w_result)
 
-    def CALL_FUNCTION(self, oparg, next_instr):
+    def CALL_FUNCTION(self, oparg):
         self.call_function(oparg)
     CALL_METHOD = CALL_FUNCTION
 
-    def CALL_FUNCTION_VAR(self, oparg, next_instr):
+    def CALL_FUNCTION_VAR(self, oparg):
         w_varargs = self.popvalue()
         self.call_function(oparg, w_varargs)
 
-    def CALL_FUNCTION_KW(self, oparg, next_instr):
+    def CALL_FUNCTION_KW(self, oparg):
         w_varkw = self.popvalue()
         self.call_function(oparg, None, w_varkw)
 
-    def CALL_FUNCTION_VAR_KW(self, oparg, next_instr):
+    def CALL_FUNCTION_VAR_KW(self, oparg):
         w_varkw = self.popvalue()
         w_varargs = self.popvalue()
         self.call_function(oparg, w_varargs, w_varkw)
 
-    def MAKE_FUNCTION(self, numdefaults, next_instr):
+    def MAKE_FUNCTION(self, numdefaults):
         w_codeobj = self.popvalue()
         defaults = self.popvalues(numdefaults)
         fn = self.space.newfunction(w_codeobj, self.w_globals, defaults)
         self.pushvalue(fn)
 
-    def STORE_ATTR(self, nameindex, next_instr):
+    def STORE_ATTR(self, nameindex):
         "obj.attributename = newvalue"
         w_attributename = self.getname_w(nameindex)
         w_obj = self.popvalue()
         w_newvalue = self.popvalue()
         self.space.setattr(w_obj, w_attributename, w_newvalue)
 
-    def UNPACK_SEQUENCE(self, itemcount, next_instr):
+    def UNPACK_SEQUENCE(self, itemcount):
         w_iterable = self.popvalue()
         items = self.space.unpack_sequence(w_iterable, itemcount)
         for w_item in reversed(items):
         w_result = self.space.getslice(w_obj, w_start, w_end)
         self.pushvalue(w_result)
 
-    def SLICE_0(self, oparg, next_instr):
+    def SLICE_0(self, oparg):
         self.slice(self.space.w_None, self.space.w_None)
 
-    def SLICE_1(self, oparg, next_instr):
+    def SLICE_1(self, oparg):
         w_start = self.popvalue()
         self.slice(w_start, self.space.w_None)
 
-    def SLICE_2(self, oparg, next_instr):
+    def SLICE_2(self, oparg):
         w_end = self.popvalue()
         self.slice(self.space.w_None, w_end)
 
-    def SLICE_3(self, oparg, next_instr):
+    def SLICE_3(self, oparg):
         w_end = self.popvalue()
         w_start = self.popvalue()
         self.slice(w_start, w_end)
         w_newvalue = self.popvalue()
         self.space.setslice(w_obj, w_start, w_end, w_newvalue)
 
-    def STORE_SLICE_0(self, oparg, next_instr):
+    def STORE_SLICE_0(self, oparg):
         self.storeslice(self.space.w_None, self.space.w_None)
 
-    def STORE_SLICE_1(self, oparg, next_instr):
+    def STORE_SLICE_1(self, oparg):
         w_start = self.popvalue()
         self.storeslice(w_start, self.space.w_None)
 
-    def STORE_SLICE_2(self, oparg, next_instr):
+    def STORE_SLICE_2(self, oparg):
         w_end = self.popvalue()
         self.storeslice(self.space.w_None, w_end)
 
-    def STORE_SLICE_3(self, oparg, next_instr):
+    def STORE_SLICE_3(self, oparg):
         w_end = self.popvalue()
         w_start = self.popvalue()
         self.storeslice(w_start, w_end)
         w_obj = self.popvalue()
         self.space.delslice(w_obj, w_start, w_end)
 
-    def DELETE_SLICE_0(self, oparg, next_instr):
+    def DELETE_SLICE_0(self, oparg):
         self.deleteslice(self.space.w_None, self.space.w_None)
 
-    def DELETE_SLICE_1(self, oparg, next_instr):
+    def DELETE_SLICE_1(self, oparg):
         w_start = self.popvalue()
         self.deleteslice(w_start, self.space.w_None)
 
-    def DELETE_SLICE_2(self, oparg, next_instr):
+    def DELETE_SLICE_2(self, oparg):
         w_end = self.popvalue()
         self.deleteslice(self.space.w_None, w_end)
 
-    def DELETE_SLICE_3(self, oparg, next_instr):
+    def DELETE_SLICE_3(self, oparg):
         w_end = self.popvalue()
         w_start = self.popvalue()
         self.deleteslice(w_start, w_end)
 
-    def LIST_APPEND(self, oparg, next_instr):
+    def LIST_APPEND(self, oparg):
         w = self.popvalue()
         if sys.version_info < (2, 7):
             v = self.popvalue()
             v = self.peekvalue(oparg - 1)
         self.space.call_method(v, 'append', w)
 
-    def DELETE_FAST(self, varindex, next_instr):
+    def DELETE_FAST(self, varindex):
         if self.locals_stack_w[varindex] is None:
             varname = self.getlocalvarname(varindex)
             message = "local variable '%s' referenced before assignment"
             raise UnboundLocalError(message, varname)
         self.locals_stack_w[varindex] = None
 
-    def STORE_MAP(self, oparg, next_instr):
+    def STORE_MAP(self, oparg):
         w_key = self.popvalue()
         w_value = self.popvalue()
         w_dict = self.peekvalue()
         self.space.setitem(w_dict, w_key, w_value)
 
-    def STORE_SUBSCR(self, oparg, next_instr):
+    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)
 
-    def BUILD_SLICE(self, numargs, next_instr):
+    def BUILD_SLICE(self, numargs):
         if numargs == 3:
             w_step = self.popvalue()
         elif numargs == 2:
         w_slice = self.space.newslice(w_start, w_end, w_step)
         self.pushvalue(w_slice)
 
-    def DELETE_SUBSCR(self, oparg, next_instr):
+    def DELETE_SUBSCR(self, oparg):
         "del obj[subscr]"
         w_subscr = self.popvalue()
         w_obj = self.popvalue()
         self.space.delitem(w_obj, w_subscr)
 
-    def BUILD_TUPLE(self, itemcount, next_instr):
+    def BUILD_TUPLE(self, itemcount):
         items = self.popvalues(itemcount)
         w_tuple = self.space.newtuple(items)
         self.pushvalue(w_tuple)
 
-    def BUILD_LIST(self, itemcount, next_instr):
+    def BUILD_LIST(self, itemcount):
         items = self.popvalues(itemcount)
         w_list = self.space.newlist(items)
         self.pushvalue(w_list)
 
-    def BUILD_MAP(self, itemcount, next_instr):
+    def BUILD_MAP(self, itemcount):
         w_dict = self.space.newdict()
         self.pushvalue(w_dict)
 
 
     # Set literals, set comprehensions
 
-    def BUILD_SET(self, oparg, next_instr):
+    def BUILD_SET(self, oparg):
         raise NotImplementedError("BUILD_SET")
 
-    def SET_ADD(self, oparg, next_instr):
+    def SET_ADD(self, oparg):
         raise NotImplementedError("SET_ADD")
 
     # Dict comprehensions
 
-    def MAP_ADD(self, oparg, next_instr):
+    def MAP_ADD(self, oparg):
         raise NotImplementedError("MAP_ADD")
 
     # Closures
                 WHY_CONTINUE,   SContinueLoop
                 WHY_YIELD       not needed
     """
+    def unroll(self, frame):
+        while frame.blockstack:
+            block = frame.blockstack.pop()
+            if isinstance(self, block.handles):
+                return block.handle(frame, self)
+            block.cleanupstack(frame)
+        return self.nomoreblocks()
+
     def nomoreblocks(self):
         raise BytecodeCorruption("misplaced bytecode - should not return")
 
 class SReturnValue(SuspendedUnroller):
     """Signals a 'return' statement.
     Argument is the wrapped object to return."""
-    kind = 0x01
 
     def __init__(self, w_returnvalue):
         self.w_returnvalue = w_returnvalue
     def nomoreblocks(self):
         raise Return(self.w_returnvalue)
 
-    def state_unpack_variables(self, space):
+    def state_unpack_variables(self):
         return [self.w_returnvalue]
 
     @staticmethod
-    def state_pack_variables(space, w_returnvalue):
+    def state_pack_variables(w_returnvalue):
         return SReturnValue(w_returnvalue)
 
 class SApplicationException(SuspendedUnroller):
     """Signals an application-level exception
     (i.e. an OperationException)."""
-    kind = 0x02
 
     def __init__(self, operr):
         self.operr = operr
     def nomoreblocks(self):
         raise self.operr
 
-    def state_unpack_variables(self, space):
+    def state_unpack_variables(self):
         return [self.operr.w_type, self.operr.w_value]
 
     @staticmethod
-    def state_pack_variables(space, w_type, w_value):
+    def state_pack_variables(w_type, w_value):
         return SApplicationException(FSException(w_type, w_value))
 
 class SBreakLoop(SuspendedUnroller):
     """Signals a 'break' statement."""
-    kind = 0x04
 
-    def state_unpack_variables(self, space):
+    def state_unpack_variables(self):
         return []
 
     @staticmethod
-    def state_pack_variables(space):
+    def state_pack_variables():
         return SBreakLoop.singleton
 
 SBreakLoop.singleton = SBreakLoop()
 class SContinueLoop(SuspendedUnroller):
     """Signals a 'continue' statement.
     Argument is the bytecode position of the beginning of the loop."""
-    kind = 0x08
 
     def __init__(self, jump_to):
         self.jump_to = jump_to
 
-    def state_unpack_variables(self, space):
-        return [space.wrap(self.jump_to)]
+    def state_unpack_variables(self):
+        return [const(self.jump_to)]
 
     @staticmethod
-    def state_pack_variables(space, w_jump_to):
-        return SContinueLoop(space.int_w(w_jump_to))
+    def state_pack_variables(w_jump_to):
+        return SContinueLoop(w_jump_to.value)
 
 
 class FrameBlock(object):
 class LoopBlock(FrameBlock):
     """A loop block.  Stores the end-of-loop pointer in case of 'break'."""
 
-    _opname = 'SETUP_LOOP'
-    handling_mask = SBreakLoop.kind | SContinueLoop.kind
+    handles = (SBreakLoop, SContinueLoop)
 
     def handle(self, frame, unroller):
         if isinstance(unroller, SContinueLoop):
 class ExceptBlock(FrameBlock):
     """An try:except: block.  Stores the position of the exception handler."""
 
-    _opname = 'SETUP_EXCEPT'
-    handling_mask = SApplicationException.kind
+    handles = SApplicationException
 
     def handle(self, frame, unroller):
         # push the exception to the value stack for inspection by the
 class FinallyBlock(FrameBlock):
     """A try:finally: block.  Stores the position of the exception handler."""
 
-    _opname = 'SETUP_FINALLY'
-    handling_mask = -1     # handles every kind of SuspendedUnroller
+    handles = SuspendedUnroller
 
     def handle(self, frame, unroller):
         # any abnormal reason for unrolling a finally: triggers the end of

File rpython/flowspace/framestate.py

 UNPICKLE_TAGS = {}
 
 
-def recursively_flatten(space, lst):
+def recursively_flatten(lst):
     from rpython.flowspace.flowcontext import SuspendedUnroller
     i = 0
     while i < len(lst):
         if not isinstance(unroller, SuspendedUnroller):
             i += 1
         else:
-            vars = unroller.state_unpack_variables(space)
+            vars = unroller.state_unpack_variables()
             key = unroller.__class__, len(vars)
             try:
                 tag = PICKLE_TAGS[key]
             lst[i:i + 1] = [tag] + vars
 
 
-def recursively_unflatten(space, lst):
+def recursively_unflatten(lst):
     for i in xrange(len(lst) - 1, -1, -1):
         item = lst[i]
         if item in UNPICKLE_TAGS:
             unrollerclass, argcount = UNPICKLE_TAGS[item]
             arguments = lst[i + 1:i + 1 + argcount]
             del lst[i + 1:i + 1 + argcount]
-            unroller = unrollerclass.state_pack_variables(space, *arguments)
+            unroller = unrollerclass.state_pack_variables(*arguments)
             lst[i] = unroller

File rpython/flowspace/model.py

 
 from rpython.tool.uid import uid, Hashable
 from rpython.tool.sourcetools import PY_IDENTIFIER, nice_repr_for_func
-from rpython.rlib.rarithmetic import is_valid_int, r_longlong, r_ulonglong, r_uint
 
 
 """
     during its construction"""
 
 
+# method-wrappers have not enough introspection in CPython
+if hasattr(complex.real.__get__, 'im_self'):
+    type_with_bad_introspection = None     # on top of PyPy
+else:
+    type_with_bad_introspection = type(complex.real.__get__)
+
+def const(obj):
+    if isinstance(obj, (Variable, Constant)):
+        raise TypeError("already wrapped: " + repr(obj))
+    # method-wrapper have ill-defined comparison and introspection
+    # to appear in a flow graph
+    if type(obj) is type_with_bad_introspection:
+        raise WrapException
+    return Constant(obj)
+
 class SpaceOperation(object):
     __slots__ = "opname args result offset".split()
 
     if not __debug__:
         return
     try:
-
+        from rpython.rlib.rarithmetic import (is_valid_int, r_longlong,
+            r_ulonglong, r_uint)
         vars_previous_blocks = {}
 
         exitblocks = {graph.returnblock: 1,   # retval

File rpython/flowspace/objspace.py

 
 from rpython.flowspace.argument import CallSpec
 from rpython.flowspace.model import (Constant, Variable, WrapException,
-    UnwrapException, checkgraph)
+    UnwrapException, checkgraph, const)
 from rpython.flowspace.bytecode import HostCode
-from rpython.flowspace import operation
+from rpython.flowspace.operation import op
 from rpython.flowspace.flowcontext import (FlowSpaceFrame, fixeggblocks,
     FSException, FlowingError)
 from rpython.flowspace.generator import (tweak_generator_graph,
 from rpython.flowspace.specialcase import SPECIAL_CASES
 from rpython.rlib.unroll import unrolling_iterable, _unroller
 from rpython.rlib import rstackovf
-from rpython.rlib.rarithmetic import is_valid_int
 
 
-# method-wrappers have not enough introspection in CPython
-if hasattr(complex.real.__get__, 'im_self'):
-    type_with_bad_introspection = None     # on top of PyPy
-else:
-    type_with_bad_introspection = type(complex.real.__get__)
-
 # the following gives us easy access to declare more for applications:
 NOT_REALLY_CONST = {
     Constant(sys): {
     (the bytecode of) some function.
     """
     w_None = Constant(None)
-    builtin = Constant(__builtin__)
     sys = Constant(sys)
     w_False = Constant(False)
     w_True = Constant(True)
     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))
-
     is_ = None     # real version added by add_operations()
     id  = None     # real version added by add_operations()
 
             return self.w_False
 
     def newfunction(self, w_code, w_globals, defaults_w):
-        try:
-            code = self.unwrap(w_code)
-            globals = self.unwrap(w_globals)
-            defaults = tuple([self.unwrap(value) for value in defaults_w])
-        except UnwrapException:
-            raise FlowingError(self.frame, "Dynamically created function must"
+        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 wrap(self, obj):
-        if isinstance(obj, (Variable, Constant)):
-            raise TypeError("already wrapped: " + repr(obj))
-        # method-wrapper have ill-defined comparison and introspection
-        # to appear in a flow graph
-        if type(obj) is type_with_bad_introspection:
-            raise WrapException
-        return Constant(obj)
-
     def exc_wrap(self, exc):
-        w_value = self.wrap(exc)
-        w_type = self.wrap(type(exc))
+        w_value = const(exc)
+        w_type = const(type(exc))
         return FSException(w_type, w_value)
 
-    def int_w(self, w_obj):
-        if isinstance(w_obj, Constant):
-            val = w_obj.value
-            if not is_valid_int(val):
-                raise TypeError("expected integer: " + repr(w_obj))
-            return val
-        return self.unwrap(w_obj)
-
-    def str_w(self, w_obj):
-        if isinstance(w_obj, Constant):
-            val = w_obj.value
-            if type(val) is not str:
-                raise TypeError("expected string: " + repr(w_obj))
-            return val
-        return self.unwrap(w_obj)
-
-    def unwrap(self, w_obj):
-        if isinstance(w_obj, Variable):
-            raise UnwrapException
-        elif isinstance(w_obj, Constant):
-            return w_obj.value
-        else:
-            raise TypeError("not wrapped: " + repr(w_obj))
-
-    def exception_issubclass_w(self, w_cls1, w_cls2):
-        return self.is_true(self.issubtype(w_cls1, w_cls2))
-
     def exception_match(self, w_exc_type, w_check_class):
         """Checks if the given exception type matches 'w_check_class'."""
-        try:
-            check_class = self.unwrap(w_check_class)
-        except UnwrapException:
-            raise FlowingError(self.frame, "Non-constant except guard.")
+        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(self.frame,
+            raise FlowingError(
                 "Catching %s is not valid in RPython" % check_class.__name__)
         if not isinstance(check_class, tuple):
             # the simple case
-            return self.exception_issubclass_w(w_exc_type, w_check_class)
+            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 = self.wrap(rstackovf._StackOverflow)
-            return self.exception_issubclass_w(w_exc_type, w_real_class)
+            w_real_class = const(rstackovf._StackOverflow)
+            return frame.guessbool(self.issubtype(w_exc_type, w_real_class))
         # checking a tuple of classes
         for w_klass in self.unpackiterable(w_check_class):
             if self.exception_match(w_exc_type, w_klass):
 
         Returns an FSException object whose w_value is an instance of w_type.
         """
-        if self.isinstance_w(w_arg1, self.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 self.is_w(w_arg2, self.w_None):
+            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 self.exception_issubclass_w(w_valuetype, w_arg1):
+                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:
                     w_value = self.call_function(w_arg1, w_arg2)
         else:
             # the only case left here is (inst, None), from a 'raise inst'.
-            if not self.is_w(w_arg2, self.w_None):
+            if not frame.guessbool(self.is_(w_arg2, self.w_None)):
                 raise self.exc_wrap(TypeError(
                     "instance exception may not have a separate value"))
             w_value = w_arg1
     def unpackiterable(self, w_iterable):
         if isinstance(w_iterable, Constant):
             l = w_iterable.value
-            return [self.wrap(x) for x in l]
+            return [const(x) for x in l]
         else:
             raise UnwrapException("cannot unpack a Variable iterable ")
 
     def unpack_sequence(self, w_iterable, expected_length):
         if isinstance(w_iterable, Constant):
-            l = list(self.unwrap(w_iterable))
+            l = list(w_iterable.value)
             if len(l) != expected_length:
                 raise ValueError
-            return [self.wrap(x) for x in l]
+            return [const(x) for x in l]
         else:
             w_len = self.len(w_iterable)
-            w_correct = self.eq(w_len, self.wrap(expected_length))
-            if not self.is_true(w_correct):
+            w_correct = self.eq(w_len, const(expected_length))
+            if not self.frame.guessbool(self.bool(w_correct)):
                 e = self.exc_from_raise(self.w_ValueError, self.w_None)
                 raise e
-            return [self.frame.do_operation('getitem', w_iterable, self.wrap(i))
+            return [self.frame.do_operation('getitem', w_iterable, const(i))
                         for i in range(expected_length)]
 
     # ____________________________________________________________
     def not_(self, w_obj):
-        return self.wrap(not self.is_true(w_obj))
-
-    def is_true(self, w_obj):
-        if w_obj.foldable():
-            return bool(w_obj.value)
-        w_truthvalue = self.frame.do_operation('is_true', w_obj)
-        return self.frame.guessbool(w_truthvalue)
+        return const(not self.frame.guessbool(self.bool(w_obj)))
 
     def iter(self, w_iterable):
         if isinstance(w_iterable, Constant):
             iterable = w_iterable.value
             if isinstance(iterable, unrolling_iterable):
-                return self.wrap(iterable.get_unroller())
+                return const(iterable.get_unroller())
         w_iter = self.frame.do_operation("iter", w_iterable)
         return w_iter
 
                     raise self.exc_wrap(StopIteration())
                 else:
                     frame.replace_in_stack(it, next_unroller)
-                    return self.wrap(v)
+                    return const(v)
         w_item = frame.do_operation("next", w_iter)
-        frame.handle_implicit_exceptions([StopIteration, RuntimeError])
+        frame.guessexception([StopIteration, RuntimeError], force=True)
         return w_item
 
 
         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.frame.do_operation_with_implicit_exceptions('getattr',
-                                                                w_obj, w_name)
+                return self.frame.do_op(op.getattr(w_obj, w_name))
         if w_obj.foldable() and w_name.foldable():
             obj, name = w_obj.value, w_name.value
             try:
                 etype = e.__class__
                 msg = "getattr(%s, %s) always raises %s: %s" % (
                     obj, name, etype, e)
-                raise FlowingError(self.frame, msg)
+                raise FlowingError(msg)
             try:
-                return self.wrap(result)
+                return const(result)
             except WrapException:
                 pass
-        return self.frame.do_operation_with_implicit_exceptions('getattr',
-                w_obj, w_name)
-
-    def isinstance_w(self, w_obj, w_type):
-        return self.is_true(self.isinstance(w_obj, w_type))
+        return self.frame.do_op(op.getattr(w_obj, w_name))
 
     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 self.exc_wrap(e)
-        return self.wrap(mod)
+        return const(mod)
 
     def import_from(self, w_module, w_name):
         assert isinstance(w_module, Constant)
         if w_module in self.not_really_const:
             const_w = self.not_really_const[w_module]
             if w_name not in const_w:
-                return self.frame.do_operation_with_implicit_exceptions('getattr',
-                                                                w_module, w_name)
+                return self.frame.do_op(op.getattr(w_module, w_name))
         try:
-            return self.wrap(getattr(w_module.value, w_name.value))
+            return const(getattr(w_module.value, w_name.value))
         except AttributeError:
             raise self.exc_wrap(ImportError(
                 "cannot import name '%s'" % w_name.value))
 
     def call_method(self, w_obj, methname, *arg_w):
-        w_meth = self.getattr(w_obj, self.wrap(methname))
+        w_meth = self.getattr(w_obj, const(methname))
         return self.call_function(w_meth, *arg_w)
 
     def call_function(self, w_func, *args_w):
 
     def appcall(self, func, *args_w):
         """Call an app-level RPython function directly"""
-        w_func = self.wrap(func)
+        w_func = const(func)
         return self.frame.do_operation('simple_call', w_func, *args_w)
 
     def call_args(self, w_callable, args):
             fn = w_callable.value
             if hasattr(fn, "_flowspace_rewrite_directly_as_"):
                 fn = fn._flowspace_rewrite_directly_as_
-                w_callable = self.wrap(fn)
+                w_callable = const(fn)
             try:
                 sc = self.specialcases[fn]   # TypeError if 'fn' not hashable
             except (KeyError, TypeError):
                     args_w = args.arguments_w + self.unpackiterable(args.w_stararg)
                 else:
                     args_w = args.arguments_w
-                return sc(self, args_w)
+                return sc(self, *args_w)
 
         if args.keywords or isinstance(args.w_stararg, Variable):
             shape, args_w = args.flatten()
                                types.TypeType)) and
                   c.__module__ in ['__builtin__', 'exceptions']):
                 if c in builtins_exceptions:
-                    self.frame.handle_implicit_exceptions(builtins_exceptions[c])
+                    self.frame.guessexception(builtins_exceptions[c])
                 return w_res
         # *any* exception for non-builtins
-        self.frame.handle_implicit_exceptions([Exception])
+        self.frame.guessexception([Exception])
         return w_res
 
     def find_global(self, w_globals, varname):
         try:
-            value = self.unwrap(w_globals)[varname]
+            value = w_globals.value[varname]
         except KeyError:
             # not in the globals, now look in the built-ins
             try:
-                value = getattr(self.unwrap(self.builtin), varname)
+                value = getattr(__builtin__, varname)
             except AttributeError:
-                message = "global name '%s' is not defined" % varname
-                raise FlowingError(self.frame, self.wrap(message))
-        return self.wrap(value)
+                raise FlowingError("global name '%s' is not defined" % varname)
+        return const(value)
 
-def make_impure_op(oper):
-    def generic_operator(self, *args_w):
-        if len(args_w) != oper.arity:
-            raise TypeError(oper.name + " got the wrong number of arguments")
-        w_result = self.frame.do_operation_with_implicit_exceptions(oper.name, *args_w)
-        return w_result
+def make_op(cls):
+    def generic_operator(self, *args):
+        return cls(*args).eval(self.frame)
     return generic_operator
 
-def make_op(oper):
-    """Add function operation to the flow space."""
-    name = oper.name
-    func = oper.pyfunc
-
-    def generic_operator(self, *args_w):
-        assert len(args_w) == oper.arity, name + " got the wrong number of arguments"
-        args = []
-        if all(w_arg.foldable() for w_arg in args_w):
-            args = [w_arg.value for w_arg in args_w]
-            # All arguments are constants: call the operator now
-            try:
-                result = func(*args)
-            except Exception, e:
-                etype = e.__class__
-                msg = "%s%r always raises %s: %s" % (
-                    name, tuple(args), etype, e)
-                raise FlowingError(self.frame, msg)
-            else:
-                # don't try to constant-fold operations giving a 'long'
-                # result.  The result is probably meant to be sent to
-                # an intmask(), but the 'long' constant confuses the
-                # annotator a lot.
-                if oper.can_overflow and type(result) is long:
-                    pass
-                # don't constant-fold getslice on lists, either
-                elif name == 'getslice' and type(result) is list:
-                    pass
-                # otherwise, fine
-                else:
-                    try:
-                        return self.wrap(result)
-                    except WrapException:
-                        # type cannot sanely appear in flow graph,
-                        # store operation with variable result instead
-                        pass
-        w_result = self.frame.do_operation_with_implicit_exceptions(name, *args_w)
-        return w_result
-    return generic_operator
-
-for oper in operation.op.__dict__.values():
-    if getattr(FlowObjSpace, oper.name, None) is None:
-        if oper.pure:
-            op_method = make_op(oper)
-        else:
-            op_method = make_impure_op(oper)
-        setattr(FlowObjSpace, oper.name, op_method)
+for cls in op.__dict__.values():
+    if getattr(FlowObjSpace, cls.opname, None) is None:
+        setattr(FlowObjSpace, cls.opname, make_op(cls))
 
 
 def build_flow(func, space=FlowObjSpace()):

File rpython/flowspace/operation.py

 import __future__
 import operator
 from rpython.tool.sourcetools import compile2
-from rpython.rlib.rarithmetic import ovfcheck
-from rpython.flowspace.model import Constant
+from rpython.flowspace.model import (Constant, WrapException, const, Variable,
+                                     SpaceOperation)
+from rpython.flowspace.specialcase import register_flow_sc
 
 class _OpHolder(object): pass
 op = _OpHolder()
 
 func2op = {}
 
-class SpaceOperator(object):
-    def __init__(self, name, arity, symbol, pyfunc, pure=False,
-            can_overflow=False):
-        self.name = name
-        self.arity = arity
-        self.symbol = symbol
-        self.pyfunc = pyfunc
-        self.pure = pure
-        self.can_overflow = can_overflow
-        self.canraise = []
+class HLOperation(SpaceOperation):
+    pure = False
+    def __init__(self, *args):
+        if len(args) != self.arity:
+            raise TypeError(self.opname + " got the wrong number of arguments")
+        self.args = list(args)
+        self.result = Variable()
+        self.offset = -1
 
-    def make_sc(self):
-        def sc_operator(space, args_w):
-            if len(args_w) != self.arity:
-                if self is op.pow and len(args_w) == 2:
-                    args_w = args_w + [Constant(None)]
-                elif self is op.getattr and len(args_w) == 3:
+    @classmethod
+    def make_sc(cls):
+        def sc_operator(space, *args_w):
+            if len(args_w) != cls.arity:
+                if cls is op.pow and len(args_w) == 2:
+                    args_w = list(args_w) + [Constant(None)]
+                elif cls is op.getattr and len(args_w) == 3:
                     return space.frame.do_operation('simple_call', Constant(getattr), *args_w)
                 else:
                     raise Exception("should call %r with exactly %d arguments" % (
-                        self.name, self.arity))
+                        cls.opname, cls.arity))
             # completely replace the call with the underlying
             # operation and its limited implicit exceptions semantic
-            return getattr(space, self.name)(*args_w)
+            return getattr(space, cls.opname)(*args_w)
         return sc_operator
 
+    def eval(self, frame):
+        result = self.constfold()
+        if result is not None:
+            return result
+        return frame.do_op(self)
+
+    def constfold(self):
+        return None
+
+class PureOperation(HLOperation):
+    pure = True
+
+    def constfold(self):
+        args = []
+        if all(w_arg.foldable() for w_arg in self.args):
+            args = [w_arg.value for w_arg in self.args]
+            # All arguments are constants: call the operator now
+            try:
+                result = self.pyfunc(*args)
+            except Exception as e:
+                from rpython.flowspace.flowcontext import FlowingError
+                msg = "%s%r always raises %s: %s" % (
+                    self.opname, tuple(args), type(e), e)
+                raise FlowingError(msg)
+            else:
+                # don't try to constant-fold operations giving a 'long'
+                # result.  The result is probably meant to be sent to
+                # an intmask(), but the 'long' constant confuses the
+                # annotator a lot.
+                if self.can_overflow and type(result) is long:
+                    pass
+                # don't constant-fold getslice on lists, either
+                elif self.opname == 'getslice' and type(result) is list:
+                    pass
+                # otherwise, fine
+                else:
+                    try:
+                        return const(result)
+                    except WrapException:
+                        # type cannot sanely appear in flow graph,
+                        # store operation with variable result instead
+                        pass
+
 
 def add_operator(name, arity, symbol, pyfunc=None, pure=False, ovf=False):
     operator_func = getattr(operator, name, None)
-    oper = SpaceOperator(name, arity, symbol, pyfunc, pure, can_overflow=ovf)
-    setattr(op, name, oper)
+    base_cls = PureOperation if pure else HLOperation
+    cls = type(name, (base_cls,), {'opname': name, 'arity': arity,
+                                   'can_overflow': ovf, 'canraise': []})
+    setattr(op, name, cls)
     if pyfunc is not None:
-        func2op[pyfunc] = oper
+        func2op[pyfunc] = cls
     if operator_func:
-        func2op[operator_func] = oper
-        if pyfunc is None:
-            oper.pyfunc = operator_func
+        func2op[operator_func] = cls
+    if pyfunc is not None:
+        cls.pyfunc = staticmethod(pyfunc)
+    elif operator_func is not None:
+        cls.pyfunc = staticmethod(operator_func)
     if ovf:
-        ovf_func = lambda *args: ovfcheck(oper.pyfunc(*args))
+        from rpython.rlib.rarithmetic import ovfcheck
+        ovf_func = lambda *args: ovfcheck(cls.pyfunc(*args))
         add_operator(name + '_ovf', arity, symbol, pyfunc=ovf_func)
 
 # ____________________________________________________________
 add_operator('is_', 2, 'is', pure=True)
 add_operator('id', 1, 'id', pyfunc=id)
 add_operator('type', 1, 'type', pyfunc=new_style_type, pure=True)
-add_operator('isinstance', 2, 'isinstance', pyfunc=isinstance, pure=True)
 add_operator('issubtype', 2, 'issubtype', pyfunc=issubclass, pure=True)  # not for old-style classes
 add_operator('repr', 1, 'repr', pyfunc=repr, pure=True)
 add_operator('str', 1, 'str', pyfunc=str, pure=True)
 add_operator('trunc', 1, 'trunc', pyfunc=unsupported)
 add_operator('pos', 1, 'pos', pure=True)
 add_operator('neg', 1, 'neg', pure=True, ovf=True)
-add_operator('nonzero', 1, 'truth', pyfunc=bool, pure=True)
-op.is_true = op.nonzero
-add_operator('abs' , 1, 'abs', pyfunc=abs, pure=True, ovf=True)
+add_operator('bool', 1, 'truth', pyfunc=bool, pure=True)
+op.is_true = op.nonzero = op.bool  # for llinterp
+add_operator('abs', 1, 'abs', pyfunc=abs, pure=True, ovf=True)
 add_operator('hex', 1, 'hex', pyfunc=hex, pure=True)
 add_operator('oct', 1, 'oct', pyfunc=oct, pure=True)
 add_operator('ord', 1, 'ord', pyfunc=ord, pure=True)
 
 # Other functions that get directly translated to SpaceOperators
 func2op[type] = op.type
-func2op[operator.truth] = op.nonzero
+func2op[operator.truth] = op.bool
 if hasattr(__builtin__, 'next'):
     func2op[__builtin__.next] = op.next
 
+for fn, oper in func2op.items():
+    register_flow_sc(fn)(oper.make_sc())
+
 
 op_appendices = {
     OverflowError: 'ovf',
         oper = getattr(op, name)
         lis = oper.canraise
         if exc in lis:
-            raise ValueError, "your list is causing duplication!"
+            raise ValueError("your list is causing duplication!")
         lis.append(exc)
         assert exc in op_appendices
 

File rpython/flowspace/specialcase.py

-from rpython.flowspace.model import Constant
-from rpython.flowspace.operation import func2op, op
-from rpython.rlib.rarithmetic import r_uint
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.flowspace.model import Constant, const
 
-def sc_import(space, args_w):
+SPECIAL_CASES = {}
+
+def register_flow_sc(func):
+    """Decorator triggering special-case handling of ``func``.
+
+    When the flow graph builder sees ``func``, it calls the decorated function
+    with ``decorated_func(space, *args_w)``, where ``args_w`` is a sequence of
+    flow objects (Constants or Variables).
+    """
+    def decorate(sc_func):
+        SPECIAL_CASES[func] = sc_func
+    return decorate
+
+@register_flow_sc(__import__)
+def sc_import(space, *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]
+    assert all(isinstance(arg, Constant) for arg in args_w)
+    args = [arg.value for arg in args_w]
     return space.import_name(*args)
 
+@register_flow_sc(locals)
+def sc_locals(_, *args):
+    raise Exception(
+        "A function calling locals() is not RPython.  "
+        "Note that if you're translating code outside the PyPy "
+        "repository, a likely cause is that py.test's --assert=rewrite "
+        "mode is getting in the way.  You should copy the file "
+        "pytest.ini from the root of the PyPy repository into your "
+        "own project.")
+
+@register_flow_sc(isinstance)
+def sc_isinstance(space, w_instance, w_type):
+    if w_instance.foldable() and w_type.foldable():
+        return const(isinstance(w_instance.value, w_type.value))
+    return space.frame.do_operation('simple_call', const(isinstance),
+            w_instance, w_type)
+
 # _________________________________________________________________________
 # a simplified version of the basic printing routines, for RPython programs
 class StdOutBuffer:
         s = '\n'
     import os
     os.write(1, s)
-
-# _________________________________________________________________________
-
-def sc_r_uint(space, 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)
-    [w_value] = args_w
-    if isinstance(w_value, Constant):
-        return Constant(r_uint(w_value.value))
-    return space.frame.do_operation('simple_call', space.wrap(r_uint), w_value)
-
-def sc_we_are_translated(space, args_w):
-    return Constant(True)
-
-def sc_locals(space, args):
-    raise Exception(
-        "A function calling locals() is not RPython.  "
-        "Note that if you're translating code outside the PyPy "
-        "repository, a likely cause is that py.test's --assert=rewrite "
-        "mode is getting in the way.  You should copy the file "
-        "pytest.ini from the root of the PyPy repository into your "
-        "own project.")
-
-SPECIAL_CASES = {__import__: sc_import, r_uint: sc_r_uint,
-        we_are_translated: sc_we_are_translated,
-        locals: sc_locals}
-for fn, oper in func2op.items():
-    SPECIAL_CASES[fn] = oper.make_sc()

File rpython/flowspace/test/test_framestate.py

 
     def test_eq_framestate(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
-        fs2 = frame.getstate()
+        fs1 = frame.getstate(0)
+        fs2 = frame.getstate(0)
         assert fs1 == fs2
 
     def test_neq_hacked_framestate(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Variable()
-        fs2 = frame.getstate()
+        fs2 = frame.getstate(0)
         assert fs1 != fs2
 
     def test_union_on_equal_framestates(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
-        fs2 = frame.getstate()
+        fs1 = frame.getstate(0)
+        fs2 = frame.getstate(0)
         assert fs1.union(fs2) == fs1
 
     def test_union_on_hacked_framestates(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Variable()
-        fs2 = frame.getstate()
+        fs2 = frame.getstate(0)
         assert fs1.union(fs2) == fs2  # fs2 is more general
         assert fs2.union(fs1) == fs2  # fs2 is more general
 
     def test_restore_frame(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Variable()
         frame.setstate(fs1)
-        assert fs1 == frame.getstate()
+        assert fs1 == frame.getstate(0)
 
     def test_copy(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         fs2 = fs1.copy()
         assert fs1 == fs2
 
     def test_getvariables(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         vars = fs1.getvariables()
         assert len(vars) == 1
 
     def test_getoutputargs(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Variable()
-        fs2 = frame.getstate()
+        fs2 = frame.getstate(0)
         outputargs = fs1.getoutputargs(fs2)
         # 'x' -> 'x' is a Variable
         # locals_w[n-1] -> locals_w[n-1] is Constant(None)
 
     def test_union_different_constants(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Constant(42)
-        fs2 = frame.getstate()
+        fs2 = frame.getstate(0)
         fs3 = fs1.union(fs2)
         frame.setstate(fs3)
         assert isinstance(frame.locals_stack_w[frame.pycode.co_nlocals-1],
 
     def test_union_spectag(self):
         frame = self.getframe(self.func_simple)
-        fs1 = frame.getstate()
+        fs1 = frame.getstate(0)
         frame.locals_stack_w[frame.pycode.co_nlocals-1] = Constant(SpecTag())
-        fs2 = frame.getstate()
+        fs2 = frame.getstate(0)
         assert fs1.union(fs2) is None   # UnionError

File rpython/flowspace/test/test_objspace.py

     def test_loop(self):
         graph = self.codetest(self.loop)
         assert self.all_operations(graph) == {'abs': 1,
-                                              'is_true': 1,
+                                              'bool': 1,
                                               'sub': 1}
 
     #__________________________________________________________
         def f(x):
             return not ~-x
         graph = self.codetest(f)
-        assert self.all_operations(graph) == {'is_true': 1, 'invert': 1, 'neg': 1}
+        assert self.all_operations(graph) == {'bool': 1, 'invert': 1, 'neg': 1}