def draw(self, node, dot): if node is None: return '-1' if hasattr(node, 'drawn'): return utils.node_id(node) node.drawn = True node_id = self.draw_node(node, dot) if self.combined: dot.edge(node_id, utils.node_id(node.ast_node), style='solid', color=utils.AST_CFG_COLOR) return node_id
def visit_halt(self, node, dot, parent_id): _dot_id = utils.node_id(node) label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}"> {str(node)}</TD>' if not self.only_blocks: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">{node}</TD>' return label
def draw_array(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label=f'Array {node.name}', shape='note') if node.expressions: for e in node.expressions: dot.edge(_dot_id, self.draw(e, dot)) return _dot_id
def draw_assignment(self, node, dot): _dot_id = utils.node_id(node) label = f'{node.info}=' dot.node(_dot_id, label, shape='box') expr = self.draw(node.expr, dot) dot.edge(_dot_id, expr) return _dot_id
def draw_member_load(self, node: ir.MemberLoad, dot): _dot_id = utils.node_id(node) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' name_label = f'<TR><TD>LOAD MEMBER {node.base}</TD></TR>' index_label = f'<TD PORT="{node.member}">{node.member}</TD>' label = f'<{table_label}{name_label}<TR>{index_label}</TR></TABLE>>' dot.node(_dot_id, label=label, shape="none") return _dot_id
def draw_binop(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label='%s' % node.op, shape='circle') lhs = self.draw(node.lhs, dot) rhs = self.draw(node.rhs, dot) dot.edge(_dot_id, lhs) dot.edge(_dot_id, rhs) return _dot_id
def visit_return(self, node, dot, parent_id): _dot_id = utils.node_id(node) label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}"> {str(node)}</TD>' if not self.only_blocks: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Return</TD>' for ret in node.returns: returns_id = self.draw(ret, dot) dot.edge('%s:%s' % (parent_id, _dot_id), returns_id, label='returns', color=utils.CFG_STMT_COLOR) return label
def draw_mapping_load(self, node, dot): _dot_id = utils.node_id(node) index_id = self.draw(node.index, dot) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' name_label = f'<TR><TD>LOAD MAPPING {node.mapping}</TD></TR>' index_label = f'<TD PORT="{index_id}">Index</TD>' label = f'<{table_label}{name_label}<TR>{index_label}</TR></TABLE>>' dot.edge(f'{_dot_id}:{index_id}', index_id, label='index') dot.node(_dot_id, label=label, shape="none") return _dot_id
def draw_state_variable_store(self, node, dot): _dot_id = utils.node_id(node) if self.only_blocks: label = str(node) else: label = f'STORE {node.name}' expr_id = self.draw(node.expr, dot) dot.edge(_dot_id, expr_id, label='stores') dot.node(_dot_id, label=label, shape='hexagon') return _dot_id
def visit_goto(self, node, dot, parent_id): _dot_id = utils.node_id(node) if self.only_blocks: label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Goto</TD>' else: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Goto</TD>' block_id = self.draw(node.block, dot) dot.edge('%s:%s' % (parent_id, _dot_id), block_id, label=', '.join([f'{str(arg)}' for arg in node.args]), fontcolor=utils.GOTO_COLOR, color=utils.GOTO_COLOR) return label
def draw_member_store(self, node: ir.MemberStore, dot): _dot_id = utils.node_id(node) expr_id = self.draw(node.expr, dot) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' name_label = f'<TR><TD COLSPAN="2">STORE MEMBER {node.base}</TD></TR>' index_label = f'<TD PORT="{node.member}">{node.member}</TD>' expr_label = f'<TD PORT="{expr_id}">Expression</TD>' label = f'<{table_label}{name_label}<TR>{index_label}{expr_label}</TR></TABLE>>' dot.edge(f'{_dot_id}:{expr_id}', expr_id, label='expression') dot.node(_dot_id, label=label, shape="none") return _dot_id
def visit_jump(self, node, dot, parent_id): _dot_id = utils.node_id(node) if self.only_blocks: label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Jump</TD>' else: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Jump</TD>' dst_id = utils.node_id(node.dst) cont_id = self.draw(node.continuation, dot) edge_attributes = {'tail_name': f'{parent_id}:{_dot_id}', 'head_name': dst_id, 'label': ', '.join([f'{str(arg)}' for arg in node.args]), 'constraint': 'false', 'style': 'dashed', 'fontcolor': utils.JUMP_COLOR, 'color': utils.JUMP_COLOR} self.missing_links.append(edge_attributes) dot.edge('%s:%s' % (parent_id, _dot_id), cont_id, label=', '.join([f'{str(arg)}' for arg in node.continuation.args]), fontcolor=utils.GOTO_COLOR, color=utils.GOTO_COLOR) return label
def draw_block(self, node, dot): _dot_id = utils.node_id(node) args = ', '.join(str(arg) for arg in node.args) if args == '': args = ' ' if self.only_blocks: args_label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}">{args}</TD>' else: args_label = f'<TD BGCOLOR="{utils.ARGS_COLOR}">{args}</TD>' stmts = [] for stmt in node.stmts: stmt_id = utils.node_id(stmt) stmt_type = type(stmt.expr).__name__ if self.only_blocks: if self.combined: dot.edge(f'{_dot_id}:{stmt_id}', utils.node_id(stmt.ast_node), style='solid', color=utils.AST_CFG_COLOR) stmts.append(f'<TR><TD>{stmt_type}</TD><TD PORT="{stmt_id}">{str(stmt.expr)}</TD></TR>') else: if isinstance(stmt.expr, cfg_ir.Comment): stmts.append( f'<TR><TD PORT="{stmt_id}" BGCOLOR="{utils.COMMENT_COLOR}"> # {stmt.expr.comment} # </TD></TR>') else: stmts.append(f'<TR><TD PORT="{stmt_id}"> {stmt_type} {stmt.expr.info}</TD></TR>') expr_id = self.draw(stmt, dot) if expr_id is not None: dot.edge(f'{_dot_id}:{stmt_id}', expr_id, color=utils.CFG_STMT_COLOR) stmts_label = ''.join(stmts) transfer_label = self.visit_transfer(node.transfer, dot, _dot_id) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" bgcolor="WHITE">' if self.only_blocks: block_label = f'<TD COLSPAN="2" BGCOLOR="{utils.HEADER_COLOR}"><FONT COLOR="white">{node.info or node.id}</FONT></TD>' else: block_label = f'<TD BGCOLOR="{utils.HEADER_COLOR}"><FONT COLOR="white">{node.info or node.id}</FONT></TD>' label = f'<{table_label}<TR>{block_label}</TR><TR>{args_label}</TR>{stmts_label}<TR>{transfer_label}</TR></TABLE>>' dot.node(_dot_id, label=label, shape='none', style="") return _dot_id
def draw_array_load(self, node, dot): _dot_id = utils.node_id(node) base_id = self.draw(node.base, dot) index_id = self.draw(node.index, dot) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' store_label = f'<TR><TD COLSPAN="2" >LOAD ARRAY</TD></TR>' base_label = f'<TD PORT="{base_id}">Base</TD>' index_label = f'<TD PORT="{index_id}">Index</TD>' label = f'<{table_label}{store_label}<TR>{base_label}{index_label}</TR></TABLE>>' dot.edge(f'{_dot_id}:{base_id}', base_id, label='base') dot.edge(f'{_dot_id}:{index_id}', index_id, label='index') dot.node(_dot_id, label=label, shape="none") return _dot_id
def visit_call(self, node: ir.Call, dot, parent_id): _dot_id = utils.node_id(node) if self.only_blocks: label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Call</TD>' else: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Call</TD>' dst_id = utils.node_id(node.dst) + _dot_id dot.node(dst_id, label=f'Function {node.dst}', style='filled', fillcolor=utils.HEADER_COLOR, shape='doubleoctagon', fontcolor='white') cont_id = self.draw(node.continuation, dot) edge_attributes = {'tail_name': f'{parent_id}:{_dot_id}', 'head_name': dst_id, 'label': ', '.join([f'{str(arg)}' for arg in node.args]), 'constraint': 'true', 'style': 'dashed', 'fontcolor': utils.CALL_COLOR, 'color': utils.CALL_COLOR} self.missing_links.append(edge_attributes) dot.edge('%s:%s' % (parent_id, _dot_id), cont_id, label=', '.join([f'{str(arg)}' for arg in node.continuation.args]), fontcolor=utils.GOTO_COLOR, color=utils.GOTO_COLOR) return label
def draw_function(self, node: ir.Function, dot): _dot_id = utils.node_id(node) function_dot = Digraph(name=f'cluster_{id(node)}') function_dot.attr('graph', style='filled', bgcolor='#DDDDDD') label = "Constructor" if node.constructor else "Function" label = f"{label} {node.name} \n ({node.visibility})" dot.node(_dot_id, label=label, style='filled', fillcolor=utils.FUNCTION_COLOR, shape='folder', fontcolor='white') body_id = self.draw(node.cfg, function_dot) dot.edge(_dot_id, body_id) dot.subgraph(function_dot) return _dot_id
def draw_contract(self, node, dot): _dot_id = utils.node_id(node) contract_dot = Digraph(name=f'cluster_{id(node)}') dot.node(_dot_id, label=f'<<TABLE><TR><TD BGCOLOR="{utils.HEADER_COLOR}"><FONT COLOR="white">Contract {node.name}</FONT></TD></TR></TABLE>>', shape='none') for _, function_node in node.functions.items(): self.draw(function_node, contract_dot) # for _, variable in node.variables.items(): # self.draw(variable.value, contract_dot) dot.subgraph(contract_dot) return _dot_id
def draw_mapping_store(self, node, dot): _dot_id = utils.node_id(node) if self.only_blocks: label = f'STORE MAPPING{node.name}[{node.index}] = {node.expr}' else: index_id = self.draw(node.index, dot) expr_id = self.draw(node.expr, dot) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' name_label = f'<TR><TD COLSPAN="2">STORE MAPPING {node.mapping}</TD></TR>' index_label = f'<TD PORT="{index_id}">Index</TD>' expr_label = f'<TD PORT="{expr_id}">Expression</TD>' label = f'<{table_label}{name_label}<TR>{index_label}{expr_label}</TR></TABLE>>' dot.edge(f'{_dot_id}:{index_id}', index_id, label='index') dot.edge(f'{_dot_id}:{expr_id}', expr_id, label='expression') dot.node(_dot_id, label=label, shape="none") return _dot_id
def draw_node(self, node, dot): if not isinstance(node, ast.AstNode): return False dot_id = utils.node_id(node) children = [] children_ids = set() for child in dir(node): if child == 'original': continue if child.startswith("_"): continue try: if getattr(node.__class__, child, None) is not None: continue child_attr = getattr(node, child) except AttributeGrammarError: continue if isinstance(child_attr, list): for i, child_attr_member in enumerate(child_attr): self.handle_child(dot_id, dot, f'{child}{i}', child_attr_member, children, children_ids) else: self.handle_child(dot_id, dot, child, child_attr, children, children_ids) children_label = ''.join(children) bg_color = utils.AST_HEADER_COLOR font_color = 'white' if any([hl in type(node).__name__ for hl in self.highlight]): bg_color = utils.AST_HL_COLOR font_color = 'black' node_label = f'<TR><TD COLSPAN="2" BGCOLOR="{bg_color}"><FONT COLOR="{font_color}">{type(node).__name__} {node.id}</FONT></TD></TR>' label = f'<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">{node_label}{children_label}</TABLE>>' dot.node(dot_id, label=label, shape='none') with dot.subgraph() as s: s.attr(rank='same') for n in children_ids: s.node(n) return dot_id
def draw_array_store(self, node, dot): _dot_id = utils.node_id(node) if self.only_blocks: label = f'STORE ARRAY {node.base}[{node.index}] = {node.expr}' else: base_id = self.draw(node.base, dot) index_id = self.draw(node.index, dot) expr_id = self.draw(node.expr, dot) table_label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' store_label = f'<TR><TD COLSPAN="3" >STORE ARRAY</TD></TR>' base_label = f'<TD PORT="{base_id}">Base</TD>' index_label = f'<TD PORT="{index_id}">Index</TD>' expr_label = f'<TD PORT="{expr_id}">Expression</TD>' label = f'<{table_label}{store_label}<TR>{base_label}{index_label}{expr_label}</TR></TABLE>>' dot.edge(f'{_dot_id}:{base_id}', base_id, label='base') dot.edge(f'{_dot_id}:{index_id}', index_id, label='index') dot.edge(f'{_dot_id}:{expr_id}', expr_id, label='expression') dot.node(_dot_id, label=label, shape="none") return _dot_id
def visit_branch(self, node, dot, parent_id): _dot_id = utils.node_id(node) label = f'<TD COLSPAN="2" BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Branch {str(node.cond)}</TD>' true_id = self.draw(node.true_block, dot) false_id = self.draw(node.false_block, dot) if not self.only_blocks: label = f'<TD BGCOLOR="{utils.ARGS_COLOR}" PORT="{_dot_id}">Branch</TD>' cond_id = self.draw(node.cond, dot) dot.edge('%s:%s' % (parent_id, _dot_id), cond_id, label='condition', fontcolor=utils.CONDITION_COLOR, color=utils.CONDITION_COLOR) true_args_string = ', '.join([f'{str(arg)}' for arg in node.true_args]) false_args_string = ', '.join([f'{str(arg)}' for arg in node.false_args]) if true_args_string.strip() != "": true_args_string = f"[{true_args_string}]" if false_args_string.strip() != "": false_args_string = f"[{false_args_string}]" dot.edge('%s:%s' % (parent_id, _dot_id), true_id, label=f'True {true_args_string}', fontcolor=utils.TRUE_COLOR, color=utils.TRUE_COLOR) dot.edge('%s:%s' % (parent_id, _dot_id), false_id, label=f'False {false_args_string}', fontcolor=utils.FALSE_COLOR, color=utils.FALSE_COLOR) return label
def draw_parameter(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label='Parameter %s' % node.name) return _dot_id
def draw_argument(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label='Argument %s' % node.index) return _dot_id
def draw_unop(self, node: ir.UnaryOp, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label=node.op, shape='ellipse') sub = self.draw(node.sub, dot) dot.edge(_dot_id, sub) return _dot_id
def draw_const(self, node: ir.Const, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label='%s' % node.value, shape='ellipse', fillcolor=utils.CONST_COLOR) return _dot_id
def draw_destruct(self, node: ir.SelfDestruct, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label="Address", shape='ellipse') sub = self.draw(node.address, dot) dot.edge(_dot_id, sub) return _dot_id
def draw_placeholder(self, node, dot): _dot_id = utils.node_id(node) label = f'PlaceholderResultExpr' dot.node(_dot_id, label, shape='box') return _dot_id
def draw_state_variable_load(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label=str(node), shape='hexagon') return _dot_id
def draw_mapping(self, node, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label=f'Mapping {node.name}', shape='square') return _dot_id
def draw_magic_variable(self, node: ir.MagicVariable, dot): _dot_id = utils.node_id(node) dot.node(_dot_id, label='%s' % node.variable, shape='ellipse') return _dot_id