def loop_at(self, filename, lineno): if filename not in self.line_to_asts: self.line_to_asts[filename] = code_reader.make_line_to_asts(filename) for st in self.line_to_asts[filename][lineno]: if type(st) in (astroid.For, astroid.While): return st return None
def analyze_flow(stmt_sequence, loop_stats): """ Creates a data flow graph showing how information moves through the statement sequence. This should include an edge for every assignment (or impure function call) to where the variable is used. Conceptually there are two kinds of data flow: - Flow within an expression e.g. f(g(y)) ... y->g->f - Flow between statements (assign-use) The first often entails the other... e.g. g consists of statements. So there are multiple conceptual levels. For example in the above, the lowest level is the statements of g, which are "contained" in the expression g(y), which is contained in the expression f(g(y)). The flow of data in the above example is (def of y) -> (statements of g) -> (statements of f). This corresponds to the order statements are executed. In our graph we will want a node for each sub-expression of f(g(y)), and one for each sub-expression of each statement of f and g. In other words my nodes basically correspond to the "active" nodes of the AST, i.e. those that produce an intermediate value. loop_stats: a mapping from statement indices to LoopStats objects Function return: returns data to the node "containing" the function call """ mainvar('last_stmt_sequence', stmt_sequence) stmt_sequence = [ (os.path.abspath(f)[:-1] if f.endswith('pyc') else os.path.abspath(f), l, t) for (f, l, t) in stmt_sequence ] # Find all file names. Yup, this is a set comprehension. filenames = {fn for (fn, _, __) in stmt_sequence} line_to_asts = {fn: code_reader.make_line_to_asts(fn) for fn in filenames} dfg = DataFlowGraph(stmt_sequence, line_to_asts, filenames, loop_stats) stmt, nstmts = follow_statements_until_return(dfg, 0, None, {}) if isinstance(stmt, astroid.Return): last_node = sorted(dfg.terminal_nodes(), key=lambda n: n.line.stmt_idx)[-1] dfg.add_terminal_edge(len(stmt_sequence) - 1, last_node) #terminal_nodes = dfg.terminal_nodes() #for tn in terminal_nodes: #if not tn.ast_node.is_statement: #dfg.add_terminal_edge(len(stmt_sequence)-1, tn) # For tracking where data from return statements will flow to (file/line) return dfg
def analyze_flow(stmt_sequence, loop_stats): """ Creates a data flow graph showing how information moves through the statement sequence. This should include an edge for every assignment (or impure function call) to where the variable is used. Conceptually there are two kinds of data flow: - Flow within an expression e.g. f(g(y)) ... y->g->f - Flow between statements (assign-use) The first often entails the other... e.g. g consists of statements. So there are multiple conceptual levels. For example in the above, the lowest level is the statements of g, which are "contained" in the expression g(y), which is contained in the expression f(g(y)). The flow of data in the above example is (def of y) -> (statements of g) -> (statements of f). This corresponds to the order statements are executed. In our graph we will want a node for each sub-expression of f(g(y)), and one for each sub-expression of each statement of f and g. In other words my nodes basically correspond to the "active" nodes of the AST, i.e. those that produce an intermediate value. loop_stats: a mapping from statement indices to LoopStats objects Function return: returns data to the node "containing" the function call """ mainvar('last_stmt_sequence', stmt_sequence) stmt_sequence = [(os.path.abspath(f)[:-1] if f.endswith('pyc') else os.path.abspath(f), l, t) for (f,l,t) in stmt_sequence] # Find all file names. Yup, this is a set comprehension. filenames = {fn for (fn,_,__) in stmt_sequence} line_to_asts = {fn: code_reader.make_line_to_asts(fn) for fn in filenames} dfg = DataFlowGraph(stmt_sequence, line_to_asts, filenames, loop_stats) stmt, nstmts = follow_statements_until_return(dfg, 0, None, {}) if isinstance(stmt, astroid.Return): last_node = sorted(dfg.terminal_nodes(), key=lambda n: n.line.stmt_idx)[-1] dfg.add_terminal_edge(len(stmt_sequence)-1, last_node) #terminal_nodes = dfg.terminal_nodes() #for tn in terminal_nodes: #if not tn.ast_node.is_statement: #dfg.add_terminal_edge(len(stmt_sequence)-1, tn) # For tracking where data from return statements will flow to (file/line) return dfg