class CTraverser(c_ast.NodeVisitor): top_level = '__file__' # The fake name for containing-function of # top level function calls def __init__(self): self._data = ParserData(self.top_level) def visit_FuncDef(self, node): # For now, we assume that function name is unique (i.e. no overloading), # so ignore param types and return type self._data.parse_func(node.decl.name, self.generic_visit, node) def visit_FuncCall(self, node): name = node.name if isinstance(name, c_ast.ID): self._data.func_called(node.name.name) else: # The next most common case is struct reference, as e.g. for a stored callback. # So isinstance(name, c_ast.StructRef) == True, and `name` has two c_ast.ID # children (like a->b). To try and handle more general cases than just a # StructRef, we find the textually-latest c_ast.ID node -- i.e. the one # furthest down the AST. This should be the text directly left of the # opening paren of the function call. Delegate to the helper function for clarity. # For example: if our call looks like: # # val = structptr1->child1.callback(args); # # Then 'structptr1', 'child1', and 'callback' will all appear as c_ast.ID nodes # somewhere in `name.children()`. We want the textually latest, i.e. rightmost # name -- 'callback' -- and use that as the "name" of the function. retval = self._find_furthest_node(name, c_ast.ID) if retval is None: raise ValueError('Got a function call with no ID node') self._data.func_called(retval.name) self.generic_visit(node) def _find_furthest_node(self, startnode, cls): for attr, value in reversed(startnode.children()): if isinstance(value, cls): return value else: self._find_furthest_node(value, cls) return None def result(self): '''After parsing is complete, call this to get the function->subcalls dictionary and nested functions set (as a tuple).''' return self._data.result()
class PythonTraverser(ast.NodeVisitor): top_level = '__module__' # The fake name for containing-function of # top level function calls def __init__(self): self._data = ParserData(self.top_level) def _generic_visit(self, thing): # Like super's visit, except also accepts the list "nodes" as well # Alternately, super().generic_visit is equivalent to: # def generic_visit(self, node): # for field, value in iter_fields(node): # self._generic_visit(value) if isinstance(thing, list): for thng in thing: if isinstance(thng, ast.AST): self.visit(thng) elif isinstance(thing, ast.AST): self.visit(thing) def visit_FunctionDef(self, node): self._data.parse_func(node.name, self._generic_visit, node.body) def visit_Call(self, node): # A function's "name" need not be an identifier, e.g. # for i in mylist: # mylist[i](myargs, mykwargs) # For now, if this is the case, then ignore this node #print('visiting call node inside function', self.current_func) if isinstance(node.func, ast.Name): # simple call by identifier self._data.func_called(node.func.id) #print(self.current_func, node.func.id) elif isinstance(node.func, ast.Attribute): # call by attribute self._data.func_called(node.func.attr) # For now, we ignore whatever object(s) whose attr is the func # ignore(node.func.value) self.generic_visit(node) def result(self): '''After parsing is complete, call this to get the function->subcalls dictionary and nested functions set (as a tuple).''' return self._data.result()
def __init__(self): self._data = ParserData(self.top_level)