from pycparser import c_ast from itertools import pairwise def path_to_str(p): return "->".join([str(n.__class__.__name__) for n in p]) def path_select_last(classname, p): for n in reversed(p): if n.__class__.__name__ == classname: return n return None def path_contains(p, t): for n in p: if isinstance(n, t): return True return False def path_select_parent(child, p): parent = None for n in p: if n == child: return parent parent = n return None def path_slice_back(node, p): s = [] for n in reversed(p): if n == node: break s.append(n) return [x for x in reversed(s)] class ExprListSerializerVisitor(c_ast.NodeVisitor): def __init__(self): self.serial = [] def visit_Constant(self, node): expr = node.value self.serial.append(expr) def visit_ID(self, node): expr = node.name self.serial.append(expr) #todo: expand def expr_list_to_str(exprl): elsv = ExprListSerializerVisitor() elsv.visit(exprl) return ','.join(elsv.serial) class NodeVisitorWithParent(object): def __init__(self): self.current_parent = None def visit(self, node): """ Visit a node. """ method = 'visit_' + node.__class__.__name__ visitor = getattr(self, method, self.generic_visit) return visitor(node) def generic_visit(self, node): """ Called if no explicit visitor function exists for a node. Implements preorder visiting of the node. """ oldparent = self.current_parent self.current_parent = node for c in node.children(): self.visit(c) self.current_parent = oldparent class FuncDefVisitor(c_ast.NodeVisitor): def __init__(self): self.func_table = {} def visit_FuncDef(self, node): self.func_table[node.decl.name] = node class FuncCallVisitor(c_ast.NodeVisitor): def __init__(self): self.func_calls = [] def visit_FuncCall(self, node): self.func_calls.append(node.children()[0][1].name) class StateAssignmentVisitor(c_ast.NodeVisitor): def __init__(self, state): super().__init__() self.state = state self.assignments = [] def visit(self, node, path = None): """ Visit a node. """ if path is None: path = [] if self._method_cache is None: self._method_cache = {} visitor = self._method_cache.get(node.__class__.__name__, None) if visitor is None: method = 'visit_' + node.__class__.__name__ visitor = getattr(self, method, self.generic_visit) self._method_cache[node.__class__.__name__] = visitor return visitor(node, path) def generic_visit(self, node, path): """ Called if no explicit visitor function exists for a node. Implements preorder visiting of the node. """ path = path.copy() path.append(node) for c in node: self.visit(c, path) def visit_Assignment(self, n, path): # fallthrough detection case_node = path_select_last('Case', path) fallthrough_case_names = [] if case_node is not None: case_parent = path_select_parent(case_node, path) #path_select_last('Compound', path) siblings = [x for x in case_parent.block_items if not isinstance(x, c_ast.Default)] sibling_names = [x.expr.name for x in siblings] sibling_empty = [(len(x.stmts) == 0) for x in siblings] in_fallthrough = False slice_start = None slice_end = None for i,sibling in enumerate(zip(reversed(sibling_names),reversed(sibling_empty), reversed(siblings))): if sibling[0] == self.state: slice_start = i + 1 if (slice_start is not None) and sibling[1] and not(in_fallthrough) and (slice_start == i): in_fallthrough = True if in_fallthrough: if sibling[1]: slice_end = i else: in_fallthrough = False if (slice_start is not None) and (slice_end is not None): slice_start_temp = slice_start slice_start = len(siblings) - slice_end slice_end = len(siblings) - slice_start_temp fallthrough_case_names = sibling_names[slice_start-1:slice_end] rval_str = '' # conditional assignment detection asm_if = path_select_last('If', path) subpath_case = path_slice_back(case_node, path) is_exhaustive_conditional = True if asm_if is None else (asm_if.iftrue is not None) and (asm_if.iffalse is not None) is_conditional = path_contains(subpath_case, c_ast.If) and not(is_exhaustive_conditional) if not(isinstance(n.rvalue, c_ast.Constant) or isinstance(n.rvalue, c_ast.BinaryOp)): rval_str = n.rvalue.name if rval_str == self.state: self.assignments.append((n,path,fallthrough_case_names,is_conditional)) class EnumDefVisitor(c_ast.NodeVisitor): def __init__(self, name): super().__init__() self._name = name self.enums = {} def visit_Enum(self, node): self.enums[self._name] = node class EnumTypedefVisitor(c_ast.NodeVisitor): def __init__(self): self.enums = {} def visit_Typedef(self, node): ev = EnumDefVisitor(node.name) ev.visit(node) self.enums = {**self.enums, **ev.enums} class EnumVisitor(c_ast.NodeVisitor): def __init__(self): super().__init__() self.enum_names = [] def visit_Enumerator(self, node): self.enum_names.append(node.name) class SwitchCaseTermVisitor(c_ast.NodeVisitor): def __init__(self, asm_node): super().__init__() self._asm_node = asm_node self.hit = False def visit_Assignment(self, node): if node == self._asm_node: self.hit = True class SwitchCaseTranVisitor(c_ast.NodeVisitor): def __init__(self, asm_node, states, fallthrough_states, is_conditional): super().__init__() self.states = states self.fallthrough_states = fallthrough_states self.is_conditional = is_conditional self._asm_node = asm_node self.tran_table = [] def visit_Case(self, node): state_from = node.children()[0][1].name sctv = SwitchCaseTermVisitor(self._asm_node) sctv.visit(node) if sctv.hit and (state_from in self.states): #if conditional, state remains in state sometimes if self.is_conditional: self.tran_table.append((state_from,state_from)) # process state assignment self.tran_table.append((state_from, self._asm_node.rvalue.name)) for ft in self.fallthrough_states: self.tran_table.append((ft, self._asm_node.rvalue.name)) class SwitchCasePropertyVisitor(c_ast.NodeVisitor): def __init__(self, state_asmts): super().__init__() self._sas = state_asmts self.properties = [] def visit_FuncCall(self, node): name = node.name.name args = expr_list_to_str(node.args) if node.args is not None else "" fcall = f"{name}({args})" self.properties.append(fcall) def visit_Assignment(self, node): if not(node in self._sas): lvalue = None rvalue = None if isinstance(node.lvalue, c_ast.StructRef): lvalue = f"{node.lvalue.children()[0][1].name}->{node.lvalue.children()[1][1].name}"; else: lvalue = node.lvalue.name if isinstance(node.rvalue, c_ast.Constant): rvalue = f"{node.rvalue.type }({node.rvalue.value})" else: rvalue = node.rvalue.name prop = f"{lvalue}<={rvalue}" self.properties.append(prop) class SwitchCaseCodePropertyVisitor(c_ast.NodeVisitor): def __init__(self, case, state_asmts): super().__init__() self._case = case self._sas = state_asmts self.properties = [] def visit_Case(self, node): label = node.children()[0][1] block = node if label.name == self._case: scpv = SwitchCasePropertyVisitor(self._sas) scpv.visit(block) self.properties += scpv.properties