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 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 test_raise_prebuilt(self): error = ValueError('ouch') def g(x): return x def f(): raise g(error) x = self.codetest(f) simplify_graph(x) self.show(x) ops = x.startblock.operations assert ops[0].opname == 'simple_call' assert ops[0].args == [const(g), const(error)]
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 = op.getattr(w_manager, const("__exit__")).eval(self) self.settopvalue(w_exit) 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)
def replace_graph_with_bootstrap(GeneratorIterator, graph): Entry = GeneratorIterator.Entry newblock = Block(graph.startblock.inputargs) op_entry = op.simple_call(const(Entry)) v_entry = op_entry.result newblock.operations.append(op_entry) assert len(graph.startblock.inputargs) == len(Entry.varnames) for v, name in zip(graph.startblock.inputargs, Entry.varnames): newblock.operations.append(op.setattr(v_entry, Constant(name), v)) op_generator = op.simple_call(const(GeneratorIterator), v_entry) newblock.operations.append(op_generator) newblock.closeblock(Link([op_generator.result], graph.returnblock)) graph.startblock = newblock
def eval(self, ctx): w_iter, = self.args if isinstance(w_iter, Constant): it = w_iter.value if isinstance(it, _unroller): try: v, next_unroller = it.step() except IndexError: from rpython.flowspace.flowcontext import Raise raise Raise(const(StopIteration())) else: ctx.replace_in_stack(it, next_unroller) return const(v) return HLOperation.eval(self, ctx)
def eval(self, ctx): w_iter, = self.args if isinstance(w_iter, Constant): it = w_iter.value if isinstance(it, _unroller): try: v, next_unroller = it.step() except IndexError: from rpython.flowspace.flowcontext import Raise raise Raise(const(StopIteration())) else: ctx.replace_in_stack(it, next_unroller) return const(v) w_item = ctx.do_op(self) ctx.guessexception([StopIteration, RuntimeError], force=True) return w_item
def handle(self, ctx, unroller): w_exc = unroller.w_exc if ctx.exception_match(w_exc.w_type, const(StopIteration)): ctx.popvalue() return self.handlerposition else: return ctx.unroll(unroller)
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 _insert_reads(block, varnames): assert len(varnames) == len(block.inputargs) v_entry1 = Variable('entry') for i, name in enumerate(varnames): hlop = op.getattr(v_entry1, const(name)) hlop.result = block.inputargs[i] block.operations.insert(i, hlop) block.inputargs = [v_entry1]
def LOAD_DEREF(self, varindex): cell = self.closure[varindex] try: content = cell.cell_contents except ValueError: name = self.pycode.co_freevars[varindex] raise FlowingError("Undefined closure variable '%s'" % name) self.pushvalue(const(content))
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 nomoreblocks(self, ctx): w_exc = self.w_exc if w_exc.w_type == const(ImportError): msg = 'import statement always raises %s' % self raise ImportError(msg) link = Link([w_exc.w_type, w_exc.w_value], ctx.graph.exceptblock) ctx.recorder.crnt_block.closeblock(link) raise StopFlowing
def LIST_APPEND(self, oparg): w_value = self.popvalue() if sys.version_info < (2, 7): w_list = self.popvalue() else: 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 nomoreblocks(self, ctx): w_exc = self.w_exc if w_exc.w_type == const(ImportError): msg = 'ImportError is raised in RPython: %s' % ( getattr(w_exc.w_value, 'value', '<not a constant message>'),) raise ImportError(msg) link = Link([w_exc.w_type, w_exc.w_value], ctx.graph.exceptblock) ctx.recorder.crnt_block.closeblock(link) raise StopFlowing
def nomoreblocks(self, ctx): w_exc = self.w_exc if w_exc.w_type == const(ImportError): msg = 'ImportError is raised in RPython: %s' % (getattr( w_exc.w_value, 'value', '<not a constant message>'), ) raise ImportError(msg) link = Link([w_exc.w_type, w_exc.w_value], ctx.graph.exceptblock) ctx.recorder.crnt_block.closeblock(link) raise StopFlowing
def transform_varargs(annotator, v_func, v_shape, *data_v): callspec = CallSpec.fromshape(v_shape.value, list(data_v)) v_vararg = callspec.w_stararg if callspec.w_stararg: s_vararg = annotator.annotation(callspec.w_stararg) if not isinstance(s_vararg, SomeTuple): raise AnnotatorError( "Calls like f(..., *arg) require 'arg' to be a tuple") n_items = len(s_vararg.items) ops = [op.getitem(v_vararg, const(i)) for i in range(n_items)] new_args = callspec.arguments_w + [hlop.result for hlop in ops] if callspec.keywords: newspec = CallSpec(new_args, callspec.keywords) shape, data_v = newspec.flatten() call_op = op.call_args(v_func, const(shape), *data_v) else: call_op = op.simple_call(v_func, *new_args) ops.append(call_op) return ops
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 FOR_ITER(self, target): w_iterator = self.peekvalue() try: w_nextitem = op.next(w_iterator).eval(self) self.pushvalue(w_nextitem) except Raise as e: if self.exception_match(e.w_exc.w_type, const(StopIteration)): self.popvalue() return target else: raise
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 setattr_SomeInstance(annotator, v_obj, v_attr, v_value): s_attr = annotator.annotation(v_attr) if not s_attr.is_constant() or not isinstance(s_attr.const, str): return attr = s_attr.const setters = _find_property_meth(annotator.annotation(v_obj), attr, 'fset') if setters: if all(setters): get_setter = op.getattr(v_obj, const(attr + '__setter__')) return [get_setter, op.simple_call(get_setter.result, v_value)] elif not any(setters): raise AnnotatorError("Attribute %r is unwritable" % attr)
def test_translate_cast(): cdef = "typedef ssize_t Py_ssize_t;" cts = parse_source(cdef) def f(): return cts.cast('Py_ssize_t*', 0) graph = build_flow(f) simplify_graph(graph) assert len(graph.startblock.operations) == 1 op = graph.startblock.operations[0] assert op.args[0] == const(rffi.cast) assert op.args[1].value is cts.gettype('Py_ssize_t*')
def test_translate_gettype(): cdef = "typedef ssize_t Py_ssize_t;" cts = parse_source(cdef) def f(): return cts.gettype('Py_ssize_t*') graph = build_flow(f) simplify_graph(graph) # Check that the result is constant-folded assert graph.startblock.operations == [] [link] = graph.startblock.exits assert link.target is graph.returnblock assert link.args[0] == const(rffi.CArrayPtr(rffi.SSIZE_T))
def RAISE_VARARGS(self, nbargs): if nbargs == 0: if self.last_exception is not None: w_exc = self.last_exception else: w_exc = const(TypeError( "raise: no active exception to re-raise")) raise Raise(w_exc) if nbargs >= 3: self.popvalue() if nbargs >= 2: w_value = self.popvalue() w_type = self.popvalue() operror = self.exc_from_raise(w_type, w_value) else: w_type = self.popvalue() operror = self.exc_from_raise(w_type, w_None) raise Raise(operror)
def record_block(self, block): self.setstate(block.framestate) next_offset = block.framestate.next_offset self.recorder = block.make_recorder() try: while True: next_offset = self.handle_bytecode(next_offset) self.recorder.final_state = self.getstate(next_offset) except RaiseImplicit as e: w_exc = e.w_exc if isinstance(w_exc.w_type, Constant): exc_cls = w_exc.w_type.value else: exc_cls = Exception msg = "implicit %s shouldn't occur" % exc_cls.__name__ w_type = Constant(AssertionError) w_value = Constant(AssertionError(msg)) link = Link([w_type, w_value], self.graph.exceptblock) self.recorder.crnt_block.closeblock(link) except Raise as e: w_exc = e.w_exc 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 StopFlowing: pass except Return as exc: w_result = exc.w_value link = Link([w_result], self.graph.returnblock) self.recorder.crnt_block.closeblock(link) except FlowingError as exc: if exc.ctx is None: exc.ctx = self raise self.recorder = None
def record_block(self, block): self.setstate(block.framestate) next_pos = block.framestate.next_instr self.recorder = block.make_recorder() try: while True: next_pos = self.handle_bytecode(next_pos) self.recorder.final_state = self.getstate(next_pos) except RaiseImplicit as e: w_exc = e.w_exc if isinstance(w_exc.w_type, Constant): exc_cls = w_exc.w_type.value else: exc_cls = Exception msg = "implicit %s shouldn't occur" % exc_cls.__name__ w_type = Constant(AssertionError) w_value = Constant(AssertionError(msg)) link = Link([w_type, w_value], self.graph.exceptblock) self.recorder.crnt_block.closeblock(link) except Raise as e: w_exc = e.w_exc 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 StopFlowing: pass except Return as exc: w_result = exc.w_value link = Link([w_result], self.graph.returnblock) self.recorder.crnt_block.closeblock(link) except FlowingError as exc: if exc.ctx is None: exc.ctx = self raise self.recorder = None
def constfold(self): w_obj, w_name = self.args # handling special things like sys if (w_obj in NOT_REALLY_CONST and w_name not in NOT_REALLY_CONST[w_obj]): return if w_obj.foldable() and w_name.foldable(): obj, name = w_obj.value, w_name.value try: result = getattr(obj, name) except Exception as e: from rpython.flowspace.flowcontext import FlowingError etype = e.__class__ msg = "getattr(%s, %s) always raises %s: %s" % ( obj, name, etype, e) raise FlowingError(msg) try: return const(result) except WrapException: pass
def test_translate_enum(): cdef = """ typedef enum { mp_ass_subscript = 3, mp_length = 4, mp_subscript = 5, } Slot; """ cts = parse_source(cdef) def f(): return cts.gettype('Slot').mp_length graph = build_flow(f) simplify_graph(graph) # Check that the result is constant-folded assert graph.startblock.operations == [] [link] = graph.startblock.exits assert link.target is graph.returnblock assert link.args[0] == const(4)
def constfold(self): from rpython.flowspace.flowcontext import FlowingError if len(self.args) == 3: raise FlowingError( "getattr() with three arguments not supported: %s" % (self,)) w_obj, w_name = self.args # handling special things like sys if (w_obj in NOT_REALLY_CONST and w_name not in NOT_REALLY_CONST[w_obj]): return if w_obj.foldable() and w_name.foldable(): obj, name = w_obj.value, w_name.value try: result = getattr(obj, name) except Exception as e: etype = e.__class__ msg = "getattr(%s, %s) always raises %s: %s" % ( obj, name, etype, e) raise FlowingError(msg) try: return const(result) except WrapException: pass
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 tweak_generator_body_graph(Entry, graph): # First, always run simplify_graph in order to reduce the number of # variables passed around simplify_graph(graph) insert_empty_startblock(graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) op0 = op.simple_call(const(StopIteration)) op1 = op.type(op0.result) stopblock.operations = [op0, op1] stopblock.closeblock(Link([op1.result, op0.result], graph.exceptblock)) # for block in list(graph.iterblocks()): for exit in block.exits: if exit.target is graph.returnblock: exit.args = [] exit.target = stopblock assert block is not stopblock for index in range(len(block.operations) - 1, -1, -1): hlop = block.operations[index] if hlop.opname == 'yield_': [v_yielded_value] = hlop.args del block.operations[index] newlink = split_block(block, index) newblock = newlink.target # class Resume(AbstractPosition): _immutable_ = True block = newblock Resume.__name__ = 'Resume%d' % len(mappings) mappings.append(Resume) varnames = get_variable_names(newlink.args) # _insert_reads(newblock, varnames) # op_resume = op.simple_call(const(Resume)) block.operations.append(op_resume) v_resume = op_resume.result for i, name in enumerate(varnames): block.operations.append( op.setattr(v_resume, const(name), newlink.args[i])) op_pair = op.newtuple(v_resume, v_yielded_value) block.operations.append(op_pair) newlink.args = [op_pair.result] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: op_check = op.isinstance(block.inputargs[0], const(Resume)) block.operations.append(op_check) block.exitswitch = op_check.result link1 = Link([block.inputargs[0]], Resume.block) link1.exitcase = True nextblock = Block([Variable('entry')]) link2 = Link([block.inputargs[0]], nextblock) link2.exitcase = False block.closeblock(link1, link2) block = nextblock block.closeblock( Link([ Constant(AssertionError), Constant(AssertionError("bad generator class")) ], graph.exceptblock)) graph.startblock = regular_entry_block graph.signature = Signature(['entry']) graph.defaults = () checkgraph(graph) eliminate_empty_blocks(graph)
def contains_SomeInstance(annotator, v_ins, v_idx): get_contains = op.getattr(v_ins, const('__contains__')) return [get_contains, op.simple_call(get_contains.result, v_idx)]
def setitem_SomeInstance(annotator, v_ins, v_idx, v_value): get_setitem = op.getattr(v_ins, const('__setitem__')) return [get_setitem, op.simple_call(get_setitem.result, v_idx, v_value)]
import collections import types import __builtin__ from rpython.tool.error import source_lines 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) 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 w_None = const(None) class FlowingError(Exception): """ Signals invalid RPython in the function being analysed""" ctx = None def __str__(self): msg = ["\n"] msg += map(str, self.args) msg += [""] msg += source_lines(self.ctx.graph, None, offset=self.ctx.last_instr) return "\n".join(msg) class StopFlowing(Exception): pass
def sc_gettype(ctx, v_decl): if not isinstance(v_decl, Constant): raise FlowingError( "The argument of cts.gettype() must be a constant.") return const(self.gettype(v_decl.value))
def state_unpack_variables(self): return [const(self.jump_to)]
def getconstant_w(self, index): return const(self.pycode.consts[index])
def not_(self, w_obj): w_bool = op.bool(w_obj).eval(self) return const(not self.guessbool(w_bool))
def cmp_exc_match(self, w_1, w_2): return const(self.exception_match(w_1, w_2))
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)