added fallthrough support 🤮
This commit is contained in:
parent
801b60ce33
commit
2ca5091658
47
analyze.py
47
analyze.py
@ -14,6 +14,10 @@ if __name__ == "__main__":
|
|||||||
argparser.add_argument('filename',
|
argparser.add_argument('filename',
|
||||||
nargs='?',
|
nargs='?',
|
||||||
help='name of file to parse')
|
help='name of file to parse')
|
||||||
|
|
||||||
|
argparser.add_argument('-I', '--includedirs', nargs='+', default=[])
|
||||||
|
argparser.add_argument('-D', '--defines', nargs='+', default=[])
|
||||||
|
argparser.add_argument('-p', '--preprocess', help='output preprocessor file')
|
||||||
argparser.add_argument('--func', help='process function')
|
argparser.add_argument('--func', help='process function')
|
||||||
argparser.add_argument('--enum', help='state enum')
|
argparser.add_argument('--enum', help='state enum')
|
||||||
argparser.add_argument('--initial', help='initial state')
|
argparser.add_argument('--initial', help='initial state')
|
||||||
@ -29,18 +33,28 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
p = Preprocessor()
|
p = Preprocessor()
|
||||||
p.add_path('/usr/lib/gcc/x86_64-linux-gnu/12/include/')
|
p.add_path('/usr/lib/gcc/x86_64-linux-gnu/12/include/')
|
||||||
|
for inc in args.includedirs:
|
||||||
|
print(f"Include-Dir: {inc}")
|
||||||
|
p.add_path(inc)
|
||||||
|
|
||||||
|
for define in args.defines:
|
||||||
|
name,value = define.split('=')
|
||||||
|
print(f"Define: {name}={value}")
|
||||||
|
p.define(f"{name} {value}")
|
||||||
|
|
||||||
p.parse(source)
|
p.parse(source)
|
||||||
oh = io.StringIO()
|
oh = io.StringIO()
|
||||||
p.write(oh)
|
p.write(oh)
|
||||||
prep_source = oh.getvalue()
|
prep_source = oh.getvalue()#unicode(oh.getvalue(), errors='ignore')
|
||||||
|
|
||||||
#print(prep_source)
|
if args.preprocess is not None:
|
||||||
#exit()
|
with open(args.preprocess, "wt") as f:
|
||||||
|
n = f.write(prep_source)
|
||||||
|
|
||||||
parser = CParser()
|
parser = CParser()
|
||||||
ast = parser.parse(prep_source)
|
ast = parser.parse(prep_source)
|
||||||
#ast = parse_file(args.filename, use_cpp=False)
|
#ast = parse_file(args.filename, use_cpp=False)
|
||||||
|
#ast.show()
|
||||||
|
|
||||||
assign_table = []
|
assign_table = []
|
||||||
state_enums = []
|
state_enums = []
|
||||||
@ -57,21 +71,26 @@ if __name__ == "__main__":
|
|||||||
etv = EnumTypedefVisitor()
|
etv = EnumTypedefVisitor()
|
||||||
etv.visit(ast)
|
etv.visit(ast)
|
||||||
enum_table = etv.enums
|
enum_table = etv.enums
|
||||||
|
states = []
|
||||||
if not(args.func in func_table):
|
if not(args.func in func_table):
|
||||||
raise Exception(f"Function name '{args.func}' not found!")
|
raise Exception(f"Function name '{args.func}' not found!")
|
||||||
else:
|
else:
|
||||||
proc_func = func_table[args.func]
|
proc_func = func_table[args.func]
|
||||||
fsm_funcs.append(proc_func)
|
fsm_funcs.append(proc_func)
|
||||||
func_table[args.func].show()
|
|
||||||
fcv = FuncCallVisitor()
|
fcv = FuncCallVisitor()
|
||||||
fcv.visit(proc_func)
|
fcv.visit(proc_func)
|
||||||
fsm_funcs += [ func_table[x] for x in fcv.func_calls ]
|
for fc in fcv.func_calls:
|
||||||
|
if fc in func_table:
|
||||||
|
fsm_funcs.append(func_table[fc])
|
||||||
|
#fsm_funcs += [ func_table[x] for x in fcv.func_calls ]
|
||||||
|
|
||||||
|
print("Enum Table")
|
||||||
if args.enum in enum_table:
|
if args.enum in enum_table:
|
||||||
ev = EnumVisitor()
|
ev = EnumVisitor()
|
||||||
ev.visit(enum_table[args.enum])
|
ev.visit(enum_table[args.enum])
|
||||||
for ename in ev.enum_names:
|
for ename in ev.enum_names:
|
||||||
|
print(" - ",ename)
|
||||||
|
states.append(ename)
|
||||||
for f in fsm_funcs:
|
for f in fsm_funcs:
|
||||||
sav = StateAssignmentVisitor(ename)
|
sav = StateAssignmentVisitor(ename)
|
||||||
sav.visit(f)
|
sav.visit(f)
|
||||||
@ -89,7 +108,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
for sa in state_asmts:
|
for sa in state_asmts:
|
||||||
for f in fsm_funcs:
|
for f in fsm_funcs:
|
||||||
sctv = SwitchCaseTranVisitor(sa[0])
|
sctv = SwitchCaseTranVisitor(sa[0], states, sa[2])
|
||||||
sctv.visit(f)
|
sctv.visit(f)
|
||||||
tran_table += sctv.tran_table
|
tran_table += sctv.tran_table
|
||||||
|
|
||||||
@ -98,13 +117,15 @@ if __name__ == "__main__":
|
|||||||
for t in tran_table:
|
for t in tran_table:
|
||||||
print(f"{t[0]}->{t[1]}")
|
print(f"{t[0]}->{t[1]}")
|
||||||
if t[0] in comp_tt:
|
if t[0] in comp_tt:
|
||||||
|
if t[1] not in comp_tt[t[0]]:
|
||||||
comp_tt[t[0]].append(t[1])
|
comp_tt[t[0]].append(t[1])
|
||||||
else:
|
else:
|
||||||
comp_tt[t[0]] = [t[1]]
|
comp_tt[t[0]] = [t[1]]
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
pure_sa = [x[0] for x in state_asmts]
|
pure_sa = [x[0] for x in state_asmts]
|
||||||
states = comp_tt.keys()
|
#todo recomment once fixed
|
||||||
|
#states = comp_tt.keys()
|
||||||
print("States: ", ",".join(states))
|
print("States: ", ",".join(states))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
@ -156,13 +177,15 @@ if __name__ == "__main__":
|
|||||||
g = gv.Digraph('G')
|
g = gv.Digraph('G')
|
||||||
|
|
||||||
for state in states:
|
for state in states:
|
||||||
|
shape = 'circle'
|
||||||
|
if state == args.initial:
|
||||||
|
shape = 'doublecircle'
|
||||||
|
|
||||||
if state in props_by_state:
|
if state in props_by_state:
|
||||||
pstr = ",".join(props_by_state[state])
|
pstr = ",".join(props_by_state[state])
|
||||||
if state == args.initial:
|
g.node(state, label=state, xlabel=f"{{{pstr}}}", shape=shape)
|
||||||
g.attr('node', shape='doublecircle')
|
|
||||||
else:
|
else:
|
||||||
g.attr('node', shape='circle')
|
g.node(state, label=state, shape=shape)
|
||||||
g.node(state, label=state, xlabel=f"{{{pstr}}}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,43 @@
|
|||||||
from pycparser import c_ast
|
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_select_parent(child, p):
|
||||||
|
parent = None
|
||||||
|
for n in p:
|
||||||
|
if n == child:
|
||||||
|
return parent
|
||||||
|
parent = n
|
||||||
|
return None
|
||||||
|
|
||||||
|
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):
|
class FuncDefVisitor(c_ast.NodeVisitor):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -46,12 +85,38 @@ class StateAssignmentVisitor(c_ast.NodeVisitor):
|
|||||||
self.visit(c, path)
|
self.visit(c, path)
|
||||||
|
|
||||||
def visit_Assignment(self, n, path):
|
def visit_Assignment(self, n, path):
|
||||||
n.show()
|
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 = ''
|
rval_str = ''
|
||||||
if not isinstance(n.rvalue, c_ast.Constant):
|
|
||||||
|
if not(isinstance(n.rvalue, c_ast.Constant) or isinstance(n.rvalue, c_ast.BinaryOp)):
|
||||||
rval_str = n.rvalue.name
|
rval_str = n.rvalue.name
|
||||||
if rval_str == self.state:
|
if rval_str == self.state:
|
||||||
self.assignments.append((n,path))
|
self.assignments.append((n,path,fallthrough_case_names))
|
||||||
|
|
||||||
class EnumDefVisitor(c_ast.NodeVisitor):
|
class EnumDefVisitor(c_ast.NodeVisitor):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
@ -91,16 +156,21 @@ class SwitchCaseTermVisitor(c_ast.NodeVisitor):
|
|||||||
self.hit = True
|
self.hit = True
|
||||||
|
|
||||||
class SwitchCaseTranVisitor(c_ast.NodeVisitor):
|
class SwitchCaseTranVisitor(c_ast.NodeVisitor):
|
||||||
def __init__(self, asm_node):
|
def __init__(self, asm_node, states, fallthrough_states):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.states = states
|
||||||
|
self.fallthrough_states = fallthrough_states
|
||||||
self._asm_node = asm_node
|
self._asm_node = asm_node
|
||||||
self.tran_table = []
|
self.tran_table = []
|
||||||
|
|
||||||
def visit_Case(self, node):
|
def visit_Case(self, node):
|
||||||
|
state_from = node.children()[0][1].name
|
||||||
sctv = SwitchCaseTermVisitor(self._asm_node)
|
sctv = SwitchCaseTermVisitor(self._asm_node)
|
||||||
sctv.visit(node)
|
sctv.visit(node)
|
||||||
if sctv.hit:
|
if sctv.hit and (state_from in self.states):
|
||||||
self.tran_table.append((node.children()[0][1].name, self._asm_node.rvalue.name))
|
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):
|
class SwitchCasePropertyVisitor(c_ast.NodeVisitor):
|
||||||
def __init__(self, state_asmts):
|
def __init__(self, state_asmts):
|
||||||
@ -115,7 +185,7 @@ class SwitchCasePropertyVisitor(c_ast.NodeVisitor):
|
|||||||
if isinstance(node.lvalue, c_ast.StructRef):
|
if isinstance(node.lvalue, c_ast.StructRef):
|
||||||
lvalue = f"{node.lvalue.children()[0][1].name}->{node.lvalue.children()[1][1].name}";
|
lvalue = f"{node.lvalue.children()[0][1].name}->{node.lvalue.children()[1][1].name}";
|
||||||
else:
|
else:
|
||||||
lvalue = f"{node.lvalue.name}<={node.rvalue.name}"
|
lvalue = node.lvalue.name
|
||||||
|
|
||||||
if isinstance(node.rvalue, c_ast.Constant):
|
if isinstance(node.rvalue, c_ast.Constant):
|
||||||
rvalue = f"{node.rvalue.type }({node.rvalue.value})"
|
rvalue = f"{node.rvalue.type }({node.rvalue.value})"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user