kripkomat/astvisitors.py

265 lines
8.5 KiB
Python

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