def add_other_to_json(self, name, source_mapping, d, additional_fields={}): # If this a tuple with (filename, start, end), convert it to a source mapping. if isinstance(source_mapping, tuple): # Parse the source id (filename, start, end) = source_mapping source_id = next((source_unit_id for ( source_unit_id, source_unit_filename) in self.slither.source_units.items() if source_unit_filename == filename), -1) # Convert to a source mapping string source_mapping = f"{start}:{end}:{source_id}" # If this is a source mapping string, parse it. if isinstance(source_mapping, str): source_mapping_str = source_mapping source_mapping = SourceMapping() source_mapping.set_offset(source_mapping_str, self.slither) # If this is a source mapping object, get the underlying source mapping dictionary if isinstance(source_mapping, SourceMapping): source_mapping = source_mapping.source_mapping # Create the underlying element and add it to our resulting json element = self._create_base_element('other', name, source_mapping, {}, additional_fields) d['elements'].append(element)
def add_other( self, name: str, source_mapping, compilation_unit: "SlitherCompilationUnit", additional_fields: Optional[Dict] = None, ): # If this a tuple with (filename, start, end), convert it to a source mapping. if additional_fields is None: additional_fields = {} if isinstance(source_mapping, tuple): # Parse the source id (filename, start, end) = source_mapping source_id = next( (source_unit_id for ( source_unit_id, source_unit_filename, ) in compilation_unit.source_units.items() if source_unit_filename == filename), -1, ) # Convert to a source mapping string source_mapping = f"{start}:{end}:{source_id}" # If this is a source mapping string, parse it. if isinstance(source_mapping, str): source_mapping_str = source_mapping source_mapping = SourceMapping() source_mapping.set_offset(source_mapping_str, compilation_unit) # If this is a source mapping object, get the underlying source mapping dictionary if isinstance(source_mapping, SourceMapping): source_mapping = source_mapping.source_mapping # Create the underlying element and add it to our resulting json element = _create_base_element("other", name, source_mapping, {}, additional_fields) self._data["elements"].append(element)
class FunctionSolc(Function): """ """ # elems = [(type, name)] def __init__(self, function, contract, contract_declarer): super(FunctionSolc, self).__init__() self._contract = contract self._contract_declarer = contract_declarer # Only present if compact AST self._referenced_declaration = None if self.is_compact_ast: self._name = function['name'] if 'id' in function: self._referenced_declaration = function['id'] else: self._name = function['attributes'][self.get_key()] self._functionNotParsed = function self._params_was_analyzed = False self._content_was_analyzed = False self._counter_nodes = 0 self._counter_scope_local_variables = 0 # variable renamed will map the solc id # to the variable. It only works for compact format # Later if an expression provides the referencedDeclaration attr # we can retrieve the variable # It only matters if two variables have the same name in the function # which is only possible with solc > 0.5 self._variables_renamed = {} ################################################################################### ################################################################################### # region AST format ################################################################################### ################################################################################### def get_key(self): return self.slither.get_key() def get_children(self, key): if self.is_compact_ast: return key return 'children' @property def is_compact_ast(self): return self.slither.is_compact_ast @property def referenced_declaration(self): ''' Return the compact AST referenced declaration id (None for legacy AST) ''' return self._referenced_declaration # endregion ################################################################################### ################################################################################### # region Variables ################################################################################### ################################################################################### @property def variables_renamed(self): return self._variables_renamed def _add_local_variable(self, local_var): # If two local variables have the same name # We add a suffix to the new variable # This is done to prevent collision during SSA translation # Use of while in case of collision # In the worst case, the name will be really long if local_var.name: while local_var.name in self._variables: local_var.name += "_scope_{}".format( self._counter_scope_local_variables) self._counter_scope_local_variables += 1 if not local_var.reference_id is None: self._variables_renamed[local_var.reference_id] = local_var self._variables[local_var.name] = local_var # endregion ################################################################################### ################################################################################### # region Analyses ################################################################################### ################################################################################### def _analyze_attributes(self): if self.is_compact_ast: attributes = self._functionNotParsed else: attributes = self._functionNotParsed['attributes'] if 'payable' in attributes: self._payable = attributes['payable'] if 'stateMutability' in attributes: if attributes['stateMutability'] == 'payable': self._payable = True elif attributes['stateMutability'] == 'pure': self._pure = True self._view = True elif attributes['stateMutability'] == 'view': self._view = True if 'constant' in attributes: self._view = attributes['constant'] if self._name == '': self._function_type = FunctionType.FALLBACK else: self._function_type = FunctionType.NORMAL if self._name == self.contract_declarer.name: self._function_type = FunctionType.CONSTRUCTOR if 'isConstructor' in attributes and attributes['isConstructor']: self._function_type = FunctionType.CONSTRUCTOR if 'kind' in attributes: if attributes['kind'] == 'constructor': self._function_type = FunctionType.CONSTRUCTOR if 'visibility' in attributes: self._visibility = attributes['visibility'] # old solc elif 'public' in attributes: if attributes['public']: self._visibility = 'public' else: self._visibility = 'private' else: self._visibility = 'public' if 'payable' in attributes: self._payable = attributes['payable'] def analyze_params(self): # Can be re-analyzed due to inheritance if self._params_was_analyzed: return self._params_was_analyzed = True self._analyze_attributes() if self.is_compact_ast: params = self._functionNotParsed['parameters'] returns = self._functionNotParsed['returnParameters'] else: children = self._functionNotParsed[self.get_children('children')] params = children[0] returns = children[1] if params: self._parse_params(params) if returns: self._parse_returns(returns) def analyze_content(self): if self._content_was_analyzed: return self._content_was_analyzed = True if self.is_compact_ast: body = self._functionNotParsed['body'] if body and body[self.get_key()] == 'Block': self._is_implemented = True self._parse_cfg(body) for modifier in self._functionNotParsed['modifiers']: self._parse_modifier(modifier) else: children = self._functionNotParsed[self.get_children('children')] self._is_implemented = False for child in children[2:]: if child[self.get_key()] == 'Block': self._is_implemented = True self._parse_cfg(child) # Parse modifier after parsing all the block # In the case a local variable is used in the modifier for child in children[2:]: if child[self.get_key()] == 'ModifierInvocation': self._parse_modifier(child) for local_vars in self.variables: local_vars.analyze(self) for node in self.nodes: node.analyze_expressions(self) if self._filter_ternary(): for modifier_statement in self.modifiers_statements: modifier_statement.nodes = recheable( modifier_statement.entry_point) for modifier_statement in self.explicit_base_constructor_calls_statements: modifier_statement.nodes = recheable( modifier_statement.entry_point) self._remove_alone_endif() # endregion ################################################################################### ################################################################################### # region Nodes ################################################################################### ################################################################################### def _new_node(self, node_type, src): node = NodeSolc(node_type, self._counter_nodes) node.set_offset(src, self.slither) self._counter_nodes += 1 node.set_function(self) self._nodes.append(node) return node # endregion ################################################################################### ################################################################################### # region Parsing function ################################################################################### ################################################################################### def _parse_if(self, ifStatement, node): # IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? falseStatement = None if self.is_compact_ast: condition = ifStatement['condition'] # Note: check if the expression could be directly # parsed here condition_node = self._new_node(NodeType.IF, condition['src']) condition_node.add_unparsed_expression(condition) link_nodes(node, condition_node) trueStatement = self._parse_statement(ifStatement['trueBody'], condition_node) if ifStatement['falseBody']: falseStatement = self._parse_statement( ifStatement['falseBody'], condition_node) else: children = ifStatement[self.get_children('children')] condition = children[0] # Note: check if the expression could be directly # parsed here condition_node = self._new_node(NodeType.IF, condition['src']) condition_node.add_unparsed_expression(condition) link_nodes(node, condition_node) trueStatement = self._parse_statement(children[1], condition_node) if len(children) == 3: falseStatement = self._parse_statement(children[2], condition_node) endIf_node = self._new_node(NodeType.ENDIF, ifStatement['src']) link_nodes(trueStatement, endIf_node) if falseStatement: link_nodes(falseStatement, endIf_node) else: link_nodes(condition_node, endIf_node) return endIf_node def _parse_while(self, whileStatement, node): # WhileStatement = 'while' '(' Expression ')' Statement node_startWhile = self._new_node(NodeType.STARTLOOP, whileStatement['src']) if self.is_compact_ast: node_condition = self._new_node(NodeType.IFLOOP, whileStatement['condition']['src']) node_condition.add_unparsed_expression(whileStatement['condition']) statement = self._parse_statement(whileStatement['body'], node_condition) else: children = whileStatement[self.get_children('children')] expression = children[0] node_condition = self._new_node(NodeType.IFLOOP, expression['src']) node_condition.add_unparsed_expression(expression) statement = self._parse_statement(children[1], node_condition) node_endWhile = self._new_node(NodeType.ENDLOOP, whileStatement['src']) link_nodes(node, node_startWhile) link_nodes(node_startWhile, node_condition) link_nodes(statement, node_condition) link_nodes(node_condition, node_endWhile) return node_endWhile def _parse_for_compact_ast(self, statement, node): body = statement['body'] init_expression = statement['initializationExpression'] condition = statement['condition'] loop_expression = statement['loopExpression'] node_startLoop = self._new_node(NodeType.STARTLOOP, statement['src']) node_endLoop = self._new_node(NodeType.ENDLOOP, statement['src']) if init_expression: node_init_expression = self._parse_statement(init_expression, node) link_nodes(node_init_expression, node_startLoop) else: link_nodes(node, node_startLoop) if condition: node_condition = self._new_node(NodeType.IFLOOP, condition['src']) node_condition.add_unparsed_expression(condition) link_nodes(node_startLoop, node_condition) link_nodes(node_condition, node_endLoop) else: node_condition = node_startLoop node_body = self._parse_statement(body, node_condition) if loop_expression: node_LoopExpression = self._parse_statement( loop_expression, node_body) link_nodes(node_LoopExpression, node_condition) else: link_nodes(node_body, node_condition) if not condition: if not loop_expression: # TODO: fix case where loop has no expression link_nodes(node_startLoop, node_endLoop) else: link_nodes(node_LoopExpression, node_endLoop) return node_endLoop def _parse_for(self, statement, node): # ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement # the handling of loop in the legacy ast is too complex # to integrate the comapct ast # its cleaner to do it separately if self.is_compact_ast: return self._parse_for_compact_ast(statement, node) hasInitExession = True hasCondition = True hasLoopExpression = True # Old solc version do not prevent in the attributes # if the loop has a init value /condition or expression # There is no way to determine that for(a;;) and for(;a;) are different with old solc if 'attributes' in statement: attributes = statement['attributes'] if 'initializationExpression' in statement: if not statement['initializationExpression']: hasInitExession = False elif 'initializationExpression' in attributes: if not attributes['initializationExpression']: hasInitExession = False if 'condition' in statement: if not statement['condition']: hasCondition = False elif 'condition' in attributes: if not attributes['condition']: hasCondition = False if 'loopExpression' in statement: if not statement['loopExpression']: hasLoopExpression = False elif 'loopExpression' in attributes: if not attributes['loopExpression']: hasLoopExpression = False node_startLoop = self._new_node(NodeType.STARTLOOP, statement['src']) node_endLoop = self._new_node(NodeType.ENDLOOP, statement['src']) children = statement[self.get_children('children')] if hasInitExession: if len(children) >= 2: if children[0][self.get_key()] in [ 'VariableDefinitionStatement', 'VariableDeclarationStatement', 'ExpressionStatement' ]: node_initExpression = self._parse_statement( children[0], node) link_nodes(node_initExpression, node_startLoop) else: hasInitExession = False else: hasInitExession = False if not hasInitExession: link_nodes(node, node_startLoop) node_condition = node_startLoop if hasCondition: if hasInitExession and len(children) >= 2: candidate = children[1] else: candidate = children[0] if candidate[self.get_key()] not in [ 'VariableDefinitionStatement', 'VariableDeclarationStatement', 'ExpressionStatement' ]: expression = candidate node_condition = self._new_node(NodeType.IFLOOP, expression['src']) #expression = parse_expression(candidate, self) node_condition.add_unparsed_expression(expression) link_nodes(node_startLoop, node_condition) link_nodes(node_condition, node_endLoop) hasCondition = True else: hasCondition = False node_statement = self._parse_statement(children[-1], node_condition) node_LoopExpression = node_statement if hasLoopExpression: if len(children) > 2: if children[-2][self.get_key()] == 'ExpressionStatement': node_LoopExpression = self._parse_statement( children[-2], node_statement) if not hasCondition: link_nodes(node_LoopExpression, node_endLoop) if not hasCondition and not hasLoopExpression: link_nodes(node, node_endLoop) link_nodes(node_LoopExpression, node_condition) return node_endLoop def _parse_dowhile(self, doWhilestatement, node): node_startDoWhile = self._new_node(NodeType.STARTLOOP, doWhilestatement['src']) if self.is_compact_ast: node_condition = self._new_node( NodeType.IFLOOP, doWhilestatement['condition']['src']) node_condition.add_unparsed_expression( doWhilestatement['condition']) statement = self._parse_statement(doWhilestatement['body'], node_condition) else: children = doWhilestatement[self.get_children('children')] # same order in the AST as while expression = children[0] node_condition = self._new_node(NodeType.IFLOOP, expression['src']) node_condition.add_unparsed_expression(expression) statement = self._parse_statement(children[1], node_condition) node_endDoWhile = self._new_node(NodeType.ENDLOOP, doWhilestatement['src']) link_nodes(node, node_startDoWhile) # empty block, loop from the start to the condition if not node_condition.sons: link_nodes(node_startDoWhile, node_condition) else: link_nodes(node_startDoWhile, node_condition.sons[0]) link_nodes(statement, node_condition) link_nodes(node_condition, node_endDoWhile) return node_endDoWhile def _parse_variable_definition(self, statement, node): try: local_var = LocalVariableSolc(statement) local_var.set_function(self) local_var.set_offset(statement['src'], self.contract.slither) self._add_local_variable(local_var) #local_var.analyze(self) new_node = self._new_node(NodeType.VARIABLE, statement['src']) new_node.add_variable_declaration(local_var) link_nodes(node, new_node) return new_node except MultipleVariablesDeclaration: # Custom handling of var (a,b) = .. style declaration if self.is_compact_ast: variables = statement['declarations'] count = len(variables) if statement['initialValue']['nodeType'] == 'TupleExpression': inits = statement['initialValue']['components'] i = 0 new_node = node for variable in variables: init = inits[i] src = variable['src'] i = i + 1 new_statement = { 'nodeType': 'VariableDefinitionStatement', 'src': src, 'declarations': [variable], 'initialValue': init } new_node = self._parse_variable_definition( new_statement, new_node) else: # If we have # var (a, b) = f() # we can split in multiple declarations, without init # Then we craft one expression that does the assignment variables = [] i = 0 new_node = node for variable in statement['declarations']: i = i + 1 if variable: src = variable['src'] # Create a fake statement to be consistent new_statement = { 'nodeType': 'VariableDefinitionStatement', 'src': src, 'declarations': [variable] } variables.append(variable) new_node = self._parse_variable_definition_init_tuple( new_statement, i, new_node) var_identifiers = [] # craft of the expression doing the assignement for v in variables: identifier = { 'nodeType': 'Identifier', 'src': v['src'], 'name': v['name'], 'typeDescriptions': { 'typeString': v['typeDescriptions']['typeString'] } } var_identifiers.append(identifier) tuple_expression = { 'nodeType': 'TupleExpression', 'src': statement['src'], 'components': var_identifiers } expression = { 'nodeType': 'Assignment', 'src': statement['src'], 'operator': '=', 'type': 'tuple()', 'leftHandSide': tuple_expression, 'rightHandSide': statement['initialValue'], 'typeDescriptions': { 'typeString': 'tuple()' } } node = new_node new_node = self._new_node(NodeType.EXPRESSION, statement['src']) new_node.add_unparsed_expression(expression) link_nodes(node, new_node) else: count = 0 children = statement[self.get_children('children')] child = children[0] while child[self.get_key()] == 'VariableDeclaration': count = count + 1 child = children[count] assert len(children) == (count + 1) tuple_vars = children[count] variables_declaration = children[0:count] i = 0 new_node = node if tuple_vars[self.get_key()] == 'TupleExpression': assert len( tuple_vars[self.get_children('children')]) == count for variable in variables_declaration: init = tuple_vars[self.get_children('children')][i] src = variable['src'] i = i + 1 # Create a fake statement to be consistent new_statement = { self.get_key(): 'VariableDefinitionStatement', 'src': src, self.get_children('children'): [variable, init] } new_node = self._parse_variable_definition( new_statement, new_node) else: # If we have # var (a, b) = f() # we can split in multiple declarations, without init # Then we craft one expression that does the assignment assert tuple_vars[self.get_key()] in [ 'FunctionCall', 'Conditional' ] variables = [] for variable in variables_declaration: src = variable['src'] i = i + 1 # Create a fake statement to be consistent new_statement = { self.get_key(): 'VariableDefinitionStatement', 'src': src, self.get_children('children'): [variable] } variables.append(variable) new_node = self._parse_variable_definition_init_tuple( new_statement, i, new_node) var_identifiers = [] # craft of the expression doing the assignement for v in variables: identifier = { self.get_key(): 'Identifier', 'src': v['src'], 'attributes': { 'value': v['attributes'][self.get_key()], 'type': v['attributes']['type'] } } var_identifiers.append(identifier) expression = { self.get_key(): 'Assignment', 'src': statement['src'], 'attributes': { 'operator': '=', 'type': 'tuple()' }, self.get_children('children'): [{ self.get_key(): 'TupleExpression', 'src': statement['src'], self.get_children('children'): var_identifiers }, tuple_vars] } node = new_node new_node = self._new_node(NodeType.EXPRESSION, statement['src']) new_node.add_unparsed_expression(expression) link_nodes(node, new_node) return new_node def _parse_variable_definition_init_tuple(self, statement, index, node): local_var = LocalVariableInitFromTupleSolc(statement, index) #local_var = LocalVariableSolc(statement[self.get_children('children')][0], statement[self.get_children('children')][1::]) local_var.set_function(self) local_var.set_offset(statement['src'], self.contract.slither) self._add_local_variable(local_var) # local_var.analyze(self) new_node = self._new_node(NodeType.VARIABLE, statement['src']) new_node.add_variable_declaration(local_var) link_nodes(node, new_node) return new_node def _parse_statement(self, statement, node): """ Return: node """ # Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | # ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | # Throw | EmitStatement | SimpleStatement ) ';' # SimpleStatement = VariableDefinition | ExpressionStatement name = statement[self.get_key()] # SimpleStatement = VariableDefinition | ExpressionStatement if name == 'IfStatement': node = self._parse_if(statement, node) elif name == 'WhileStatement': node = self._parse_while(statement, node) elif name == 'ForStatement': node = self._parse_for(statement, node) elif name == 'Block': node = self._parse_block(statement, node) elif name == 'InlineAssembly': break_node = self._new_node(NodeType.ASSEMBLY, statement['src']) self._contains_assembly = True link_nodes(node, break_node) node = break_node elif name == 'DoWhileStatement': node = self._parse_dowhile(statement, node) # For Continue / Break / Return / Throw # The is fixed later elif name == 'Continue': continue_node = self._new_node(NodeType.CONTINUE, statement['src']) link_nodes(node, continue_node) node = continue_node elif name == 'Break': break_node = self._new_node(NodeType.BREAK, statement['src']) link_nodes(node, break_node) node = break_node elif name == 'Return': return_node = self._new_node(NodeType.RETURN, statement['src']) link_nodes(node, return_node) if self.is_compact_ast: if statement['expression']: return_node.add_unparsed_expression( statement['expression']) else: if self.get_children('children') in statement and statement[ self.get_children('children')]: assert len(statement[self.get_children('children')]) == 1 expression = statement[self.get_children('children')][0] return_node.add_unparsed_expression(expression) node = return_node elif name == 'Throw': throw_node = self._new_node(NodeType.THROW, statement['src']) link_nodes(node, throw_node) node = throw_node elif name == 'EmitStatement': #expression = parse_expression(statement[self.get_children('children')][0], self) if self.is_compact_ast: expression = statement['eventCall'] else: expression = statement[self.get_children('children')][0] new_node = self._new_node(NodeType.EXPRESSION, statement['src']) new_node.add_unparsed_expression(expression) link_nodes(node, new_node) node = new_node elif name in [ 'VariableDefinitionStatement', 'VariableDeclarationStatement' ]: node = self._parse_variable_definition(statement, node) elif name == 'ExpressionStatement': #assert len(statement[self.get_children('expression')]) == 1 #assert not 'attributes' in statement #expression = parse_expression(statement[self.get_children('children')][0], self) if self.is_compact_ast: expression = statement[self.get_children('expression')] else: expression = statement[self.get_children('expression')][0] new_node = self._new_node(NodeType.EXPRESSION, statement['src']) new_node.add_unparsed_expression(expression) link_nodes(node, new_node) node = new_node else: raise ParsingError('Statement not parsed %s' % name) return node def _parse_block(self, block, node): ''' Return: Node ''' assert block[self.get_key()] == 'Block' if self.is_compact_ast: statements = block['statements'] else: statements = block[self.get_children('children')] for statement in statements: node = self._parse_statement(statement, node) return node def _parse_cfg(self, cfg): assert cfg[self.get_key()] == 'Block' node = self._new_node(NodeType.ENTRYPOINT, cfg['src']) self._entry_point = node if self.is_compact_ast: statements = cfg['statements'] else: statements = cfg[self.get_children('children')] if not statements: self._is_empty = True else: self._is_empty = False self._parse_block(cfg, node) self._remove_incorrect_edges() self._remove_alone_endif() # endregion ################################################################################### ################################################################################### # region Loops ################################################################################### ################################################################################### def _find_end_loop(self, node, visited, counter): # counter allows to explore nested loop if node in visited: return None if node.type == NodeType.ENDLOOP: if counter == 0: return node counter -= 1 # nested loop if node.type == NodeType.STARTLOOP: counter += 1 visited = visited + [node] for son in node.sons: ret = self._find_end_loop(son, visited, counter) if ret: return ret return None def _find_start_loop(self, node, visited): if node in visited: return None if node.type == NodeType.STARTLOOP: return node visited = visited + [node] for father in node.fathers: ret = self._find_start_loop(father, visited) if ret: return ret return None def _fix_break_node(self, node): end_node = self._find_end_loop(node, [], 0) if not end_node: raise ParsingError('Break in no-loop context {}'.format(node)) for son in node.sons: son.remove_father(node) node.set_sons([end_node]) end_node.add_father(node) def _fix_continue_node(self, node): start_node = self._find_start_loop(node, []) if not start_node: raise ParsingError('Continue in no-loop context {}'.format( node.nodeId())) for son in node.sons: son.remove_father(node) node.set_sons([start_node]) start_node.add_father(node) def _parse_params(self, params): assert params[self.get_key()] == 'ParameterList' self.parameters_src = SourceMapping() self.parameters_src.set_offset(params['src'], self.contract.slither) if self.is_compact_ast: params = params['parameters'] else: params = params[self.get_children('children')] for param in params: assert param[self.get_key()] == 'VariableDeclaration' local_var = LocalVariableSolc(param) local_var.set_function(self) local_var.set_offset(param['src'], self.contract.slither) local_var.analyze(self) # see https://solidity.readthedocs.io/en/v0.4.24/types.html?highlight=storage%20location#data-location if local_var.location == 'default': local_var.set_location('memory') self._add_local_variable(local_var) self._parameters.append(local_var) def _parse_returns(self, returns): assert returns[self.get_key()] == 'ParameterList' self.returns_src = SourceMapping() self.returns_src.set_offset(returns['src'], self.contract.slither) if self.is_compact_ast: returns = returns['parameters'] else: returns = returns[self.get_children('children')] for ret in returns: assert ret[self.get_key()] == 'VariableDeclaration' local_var = LocalVariableSolc(ret) local_var.set_function(self) local_var.set_offset(ret['src'], self.contract.slither) local_var.analyze(self) # see https://solidity.readthedocs.io/en/v0.4.24/types.html?highlight=storage%20location#data-location if local_var.location == 'default': local_var.set_location('memory') self._add_local_variable(local_var) self._returns.append(local_var) def _parse_modifier(self, modifier): m = parse_expression(modifier, self) self._expression_modifiers.append(m) for m in ExportValues(m).result(): if isinstance(m, Function): entry_point = self._new_node(NodeType.OTHER_ENTRYPOINT, modifier['src']) node = self._new_node(NodeType.EXPRESSION, modifier['src']) node.add_unparsed_expression(modifier) link_nodes(entry_point, node) self._modifiers.append( ModifierStatements(modifier=m, entry_point=entry_point, nodes=[entry_point, node])) elif isinstance(m, Contract): entry_point = self._new_node(NodeType.OTHER_ENTRYPOINT, modifier['src']) node = self._new_node(NodeType.EXPRESSION, modifier['src']) node.add_unparsed_expression(modifier) link_nodes(entry_point, node) self._explicit_base_constructor_calls.append( ModifierStatements(modifier=m, entry_point=entry_point, nodes=[entry_point, node])) # endregion ################################################################################### ################################################################################### # region Edges ################################################################################### ################################################################################### def _remove_incorrect_edges(self): for node in self._nodes: if node.type in [NodeType.RETURN, NodeType.THROW]: for son in node.sons: son.remove_father(node) node.set_sons([]) if node.type in [NodeType.BREAK]: self._fix_break_node(node) if node.type in [NodeType.CONTINUE]: self._fix_continue_node(node) def _remove_alone_endif(self): """ Can occur on: if(..){ return } else{ return } Iterate until a fix point to remove the ENDIF node creates on the following pattern if(){ return } else if(){ return } """ prev_nodes = [] while set(prev_nodes) != set(self.nodes): prev_nodes = self.nodes to_remove = [] for node in self.nodes: if node.type == NodeType.ENDIF and not node.fathers: for son in node.sons: son.remove_father(node) node.set_sons([]) to_remove.append(node) self._nodes = [n for n in self.nodes if not n in to_remove] # endregion ################################################################################### ################################################################################### # region Ternary ################################################################################### ################################################################################### def _filter_ternary(self): ternary_found = True updated = False while ternary_found: ternary_found = False for node in self._nodes: has_cond = HasConditional(node.expression) if has_cond.result(): st = SplitTernaryExpression(node.expression) condition = st.condition if not condition: raise ParsingError( f'Incorrect ternary conversion {node.expression} {node.source_mapping_str}' ) true_expr = st.true_expression false_expr = st.false_expression self._split_ternary_node(node, condition, true_expr, false_expr) ternary_found = True updated = True break return updated def _split_ternary_node(self, node, condition, true_expr, false_expr): condition_node = self._new_node(NodeType.IF, node.source_mapping) condition_node.add_expression(condition) condition_node.analyze_expressions(self) if node.type == NodeType.VARIABLE: condition_node.add_variable_declaration(node.variable_declaration) true_node = self._new_node(NodeType.EXPRESSION, node.source_mapping) if node.type == NodeType.VARIABLE: assert isinstance(true_expr, AssignmentOperation) #true_expr = true_expr.expression_right elif node.type == NodeType.RETURN: true_node.type = NodeType.RETURN true_node.add_expression(true_expr) true_node.analyze_expressions(self) false_node = self._new_node(NodeType.EXPRESSION, node.source_mapping) if node.type == NodeType.VARIABLE: assert isinstance(false_expr, AssignmentOperation) elif node.type == NodeType.RETURN: false_node.type = NodeType.RETURN #false_expr = false_expr.expression_right false_node.add_expression(false_expr) false_node.analyze_expressions(self) endif_node = self._new_node(NodeType.ENDIF, node.source_mapping) for father in node.fathers: father.remove_son(node) father.add_son(condition_node) condition_node.add_father(father) for son in node.sons: son.remove_father(node) son.add_father(endif_node) endif_node.add_son(son) link_nodes(condition_node, true_node) link_nodes(condition_node, false_node) if not true_node.type in [NodeType.THROW, NodeType.RETURN]: link_nodes(true_node, endif_node) if not false_node.type in [NodeType.THROW, NodeType.RETURN]: link_nodes(false_node, endif_node) self._nodes = [n for n in self._nodes if n.node_id != node.node_id]