def buildflowgraph(self, func, mute_dot=False): """Get the flow graph for a function.""" if not isinstance(func, types.FunctionType): raise TypeError("buildflowgraph() expects a function, " "got %r" % (func,)) if func in self._prebuilt_graphs: graph = self._prebuilt_graphs.pop(func) else: if self.config.translation.verbose: log.start(nice_repr_for_func(func)) space = FlowObjSpace(self.flowconfig) if self.annotator: # ZZZ self.annotator.policy._adjust_space_config(space) elif hasattr(self, 'no_annotator_but_do_imports_immediately'): space.do_imports_immediately = ( self.no_annotator_but_do_imports_immediately) graph = space.build_flow(func) if self.config.translation.simplifying: simplify.simplify_graph(graph) if self.config.translation.list_comprehension_operations: simplify.detect_list_comprehension(graph) if self.config.translation.verbose: log.done(func.__name__) elif not mute_dot: log.dot() self.graphs.append(graph) # store the graph in our list return graph
def insert_resume_handling(self, graph): old_start_block = graph.startblock newinputargs = [unsimplify.copyvar(self.translator.annotator, v) for v in old_start_block.inputargs] new_start_block = model.Block(newinputargs) v_resume_substate = varoftype(lltype.Signed) new_start_block.operations.append( model.SpaceOperation("getfield", [self.ll_global_state, self.c_restart_substate_name], v_resume_substate)) not_resuming_link = model.Link(newinputargs, old_start_block, -1) not_resuming_link.llexitcase = -1 resuming_links = [] for resume_index, resume_block in enumerate(self.resume_blocks): resuming_links.append( model.Link([v_resume_substate], resume_block, resume_index)) resuming_links[-1].llexitcase = resume_index new_start_block.exitswitch = v_resume_substate new_start_block.closeblock(not_resuming_link, *resuming_links) old_start_block.isstartblock = False new_start_block.isstartblock = True graph.startblock = new_start_block for block in graph.iterblocks(): if len(block.exits) == 1 and block.exitswitch is not None: block.exitswitch = None block.exits[0].exitcase = block.exits[0].llexitcase = None simplify.simplify_graph(graph, [simplify.eliminate_empty_blocks, simplify.join_blocks, simplify.transform_dead_op_vars])
def test_implicitAttributeError(self): x = self.codetest(self.implicitAttributeError) simplify_graph(x) self.show(x) def cannot_reach_exceptblock(link): if isinstance(link, Link): assert link.target is not x.exceptblock traverse(cannot_reach_exceptblock, x)
def test_implicitException_os_stat(self): x = self.codetest(self.implicitException_os_stat) simplify_graph(x) self.show(x) assert len(x.startblock.exits) == 3 d = {} for link in x.startblock.exits: d[link.exitcase] = True assert d == {None: True, OSError: True, Exception: True}
def test_reraiseAnything(self): x = self.codetest(self.reraiseAnything) simplify_graph(x) self.show(x) found = {} for link in x.iterlinks(): if link.target is x.exceptblock: assert isinstance(link.args[0], Constant) found[link.args[0].value] = True assert found == {ValueError: True, ZeroDivisionError: True, OverflowError: True}
def test_multiple_catch_simple_call(self): graph = self.codetest(self.multiple_catch_simple_call) simplify_graph(graph) assert self.all_operations(graph) == {"simple_call": 1} entrymap = mkentrymap(graph) links = entrymap[graph.returnblock] assert len(links) == 3 assert dict.fromkeys([link.exitcase for link in links]) == dict.fromkeys([None, IndexError, OSError]) links = entrymap[graph.exceptblock] assert len(links) == 1 assert links[0].exitcase is Exception
def test_unicode(self): def myfunc(n): try: return unicode(chr(n)) except UnicodeDecodeError: return None graph = self.codetest(myfunc) simplify_graph(graph) assert graph.startblock.exitswitch == c_last_exception assert graph.startblock.exits[0].target is graph.returnblock assert graph.startblock.exits[1].target is graph.returnblock
def test_reraiseTypeError(self): x = self.codetest(self.reraiseTypeError) simplify_graph(x) self.show(x) found = [] def can_reach_exceptblock(link): if isinstance(link, Link): if link.target is x.exceptblock: found.append(link) traverse(can_reach_exceptblock, x) assert found
def test_reraiseAnythingDicCase(self): x = self.codetest(self.reraiseAnythingDicCase) simplify_graph(x) self.show(x) found = {} for link in x.iterlinks(): if link.target is x.exceptblock: if isinstance(link.args[0], Constant): found[link.args[0].value] = True else: found[link.exitcase] = None assert found == {IndexError: True, KeyError: True, Exception: None}
def test_raise1(self): x = self.codetest(self.raise1) simplify_graph(x) self.show(x) ops = x.startblock.operations assert len(ops) == 2 assert ops[0].opname == 'simple_call' assert ops[0].args == [Constant(IndexError)] assert ops[1].opname == 'type' assert ops[1].args == [ops[0].result] assert x.startblock.exits[0].args == [ops[1].result, ops[0].result] assert x.startblock.exits[0].target is x.exceptblock
def test_catch_importerror_1(self): def f(): try: import pypy.this_does_not_exist except ImportError: return 1 graph = self.codetest(f) simplify_graph(graph) self.show(graph) assert not graph.startblock.operations assert len(graph.startblock.exits) == 1 assert graph.startblock.exits[0].target is graph.returnblock
def test_multiple_catch_simple_call(self): graph = self.codetest(self.multiple_catch_simple_call) simplify_graph(graph) assert self.all_operations(graph) == {'simple_call': 1} entrymap = mkentrymap(graph) links = entrymap[graph.returnblock] assert len(links) == 3 assert (dict.fromkeys([link.exitcase for link in links]) == dict.fromkeys([None, IndexError, OSError])) links = entrymap[graph.exceptblock] assert len(links) == 1 assert links[0].exitcase is Exception
def test_reraiseAnything(self): x = self.codetest(self.reraiseAnything) simplify_graph(x) self.show(x) found = {} def find_exceptions(link): if isinstance(link, Link): if link.target is x.exceptblock: assert isinstance(link.args[0], Constant) found[link.args[0].value] = True traverse(find_exceptions, x) assert found == {ValueError: True, ZeroDivisionError: True, OverflowError: True}
def test_catch_importerror_2(self): def f(): try: from pypy import this_does_not_exist except ImportError: return 1 graph = self.codetest(f) simplify_graph(graph) self.show(graph) assert not graph.startblock.operations assert len(graph.startblock.exits) == 1 assert graph.startblock.exits[0].target is graph.returnblock
def test_reraiseAttributeError(self): x = self.codetest(self.reraiseAttributeError) simplify_graph(x) self.show(x) found_AttributeError = [] def only_raise_AttributeError(link): if isinstance(link, Link): if link.target is x.exceptblock: assert link.args[0] == Constant(AttributeError) found_AttributeError.append(link) traverse(only_raise_AttributeError, x) assert found_AttributeError
def test_reraiseAttributeError(self): x = self.codetest(self.reraiseAttributeError) simplify_graph(x) self.show(x) excfound = [] for link in x.iterlinks(): if link.target is x.exceptblock: excfound.append(link.exitcase) assert len(excfound) == 2 excfound.sort() expected = [Exception, AttributeError] expected.sort() assert excfound == expected
def test_reraiseAnything(self): x = self.codetest(self.reraiseAnything) simplify_graph(x) self.show(x) found = {} for link in x.iterlinks(): if link.target is x.exceptblock: assert isinstance(link.args[0], Constant) found[link.args[0].value] = True assert found == { ValueError: True, ZeroDivisionError: True, OverflowError: True }
def test_reraiseTypeError(self): x = self.codetest(self.reraiseTypeError) simplify_graph(x) self.show(x) excfound = [] def check(link): if isinstance(link, Link): if link.target is x.exceptblock: excfound.append(link.exitcase) traverse(check, x) assert len(excfound) == 2 excfound.sort() expected = [Exception, TypeError] expected.sort() assert excfound == expected
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) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable(); v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], 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): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, 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) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation('setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation('simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check 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 test_loop_in_bare_except_bug(self): x = self.codetest(self.loop_in_bare_except_bug) simplify_graph(x) self.show(x)
def test_implicitException(self): x = self.codetest(self.implicitException) simplify_graph(x) self.show(x) for link in x.iterlinks(): assert link.target is not x.exceptblock
def test_implicitAttributeError(self): x = self.codetest(self.implicitAttributeError) simplify_graph(x) self.show(x) for link in x.iterlinks(): assert link.target is not x.exceptblock
def test_implicitException_int_and_id(self): x = self.codetest(self.implicitException_int_and_id) simplify_graph(x) self.show(x) assert len(x.startblock.exits) == 1 assert x.startblock.exits[0].target is x.returnblock
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) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable() v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], 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): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, 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) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation( 'setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation( 'simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check 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 test_getitem(self): def f(c, x): try: return c[x] except Exception: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx_key': 1} g = lambda: None def f(c, x): try: return c[x] finally: g() graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == { 'getitem_idx_key': 1, 'simple_call': 2 } def f(c, x): try: return c[x] except IndexError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx': 1} def f(c, x): try: return c[x] except KeyError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_key': 1} def f(c, x): try: return c[x] except ValueError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem': 1} def f(c, x): try: return c[x] except Exception: return -1 graph = self.codetest(f) simplify_graph(graph) self.show(graph) assert self.all_operations(graph) == {'getitem_idx_key': 1} def f(c, x): try: return c[x] except IndexError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx': 1} def f(c, x): try: return c[x] except KeyError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_key': 1} def f(c, x): try: return c[x] except ValueError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem': 1}
def test_getitem(self): def f(c, x): try: return c[x] except Exception: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx_key': 1} g = lambda: None def f(c, x): try: return c[x] finally: g() graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx_key': 1, 'simple_call': 2} def f(c, x): try: return c[x] except IndexError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx': 1} def f(c, x): try: return c[x] except KeyError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_key': 1} def f(c, x): try: return c[x] except ValueError: raise graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem': 1} def f(c, x): try: return c[x] except Exception: return -1 graph = self.codetest(f) simplify_graph(graph) self.show(graph) assert self.all_operations(graph) == {'getitem_idx_key': 1} def f(c, x): try: return c[x] except IndexError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_idx': 1} def f(c, x): try: return c[x] except KeyError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem_key': 1} def f(c, x): try: return c[x] except ValueError: return -1 graph = self.codetest(f) simplify_graph(graph) assert self.all_operations(graph) == {'getitem': 1}
def build_cfunc(func, simplify=1, dot=1, inputargtypes=None): """ return a pyrex-generated cfunction from the given func. simplify is true -> perform simplifications on the flowgraph. dot is true -> generate a dot-configuration file and postscript. inputargtypes is a list (allowed to be empty) -> then annotation will be performed before generating dot/pyrex/c code. """ try: func = func.im_func except AttributeError: pass # build the flow graph from pypy.objspace.flow import Space from pypy.tool.udir import udir space = Space() name = func.func_name funcgraph = space.build_flow(func) if not inputargtypes: source = inspect.getsource(func) base = udir.join(name).new(ext='.py').write(source) if dot: from pypy.translator.tool.make_dot import FlowGraphDotGen dotgen = FlowGraphDotGen(name) dotgen.emit_subgraph(name, funcgraph) # apply transformations if simplify: from pypy.translator.simplify import simplify_graph simplify_graph(funcgraph) name += '_s' # get the pyrex generator from pypy.translator.pyrex.genpyrex import GenPyrex genpyrex = GenPyrex(funcgraph) # generate pyrex (without type inference) # apply type inference if inputargtypes is not None: genpyrex.annotate(inputargtypes) name += '_t' #a = Annotator(self.functiongraph) #a.build_types(input_arg_types) #a.simplify() pyxstring = genpyrex.emitcode() #funcgraph.source = inspect.getsource(func) else: pyxstring = genpyrex.emitcode() pyxheader = genpyrex.globaldeclarations() mod = make_module_from_pyxstring(name, udir, pyxheader + '\n' + pyxstring) if dot: if name != func.func_name: # if some transformations have been done dotgen.emit_subgraph(name, funcgraph) dotgen.generate() return getattr(mod, func.func_name)