Add support for try/catch statements

pull/415/head
Josselin 5 years ago
parent 97bd577c3f
commit 73ece898ea
  1. 35
      slither/core/cfg/node.py
  2. 33
      slither/solc_parsing/declarations/contract.py
  3. 95
      slither/solc_parsing/declarations/function.py

@ -62,6 +62,9 @@ class NodeType:
# Only modifier node # Only modifier node
PLACEHOLDER = 0x40 PLACEHOLDER = 0x40
TRY = 0x41
CATCH = 0x42
# Node not related to the CFG # Node not related to the CFG
# Use for state variable declaration # Use for state variable declaration
OTHER_ENTRYPOINT = 0x50 OTHER_ENTRYPOINT = 0x50
@ -69,33 +72,37 @@ class NodeType:
# @staticmethod # @staticmethod
def str(t): def str(t):
if t == 0x0: if t == NodeType.ENTRYPOINT:
return 'ENTRY_POINT' return 'ENTRY_POINT'
if t == 0x10: if t == NodeType.EXPRESSION:
return 'EXPRESSION' return 'EXPRESSION'
if t == 0x11: if t == NodeType.RETURN:
return 'RETURN' return 'RETURN'
if t == 0x12: if t == NodeType.IF:
return 'IF' return 'IF'
if t == 0x13: if t == NodeType.VARIABLE:
return 'NEW VARIABLE' return 'NEW VARIABLE'
if t == 0x14: if t == NodeType.ASSEMBLY:
return 'INLINE ASM' return 'INLINE ASM'
if t == 0x15: if t == NodeType.IFLOOP:
return 'IF_LOOP' return 'IF_LOOP'
if t == 0x20: if t == NodeType.THROW:
return 'THROW' return 'THROW'
if t == 0x31: if t == NodeType.BREAK:
return 'BREAK' return 'BREAK'
if t == 0x32: if t == NodeType.CONTINUE:
return 'CONTINUE' return 'CONTINUE'
if t == 0x40: if t == NodeType.PLACEHOLDER:
return '_' return '_'
if t == 0x50: if t == NodeType.TRY:
return 'TRY'
if t == NodeType.CATCH:
return 'CATCH'
if t == NodeType.ENDIF:
return 'END_IF' return 'END_IF'
if t == 0x51: if t == NodeType.STARTLOOP:
return 'BEGIN_LOOP' return 'BEGIN_LOOP'
if t == 0x52: if t == NodeType.ENDLOOP:
return 'END_LOOP' return 'END_LOOP'
return 'Unknown type {}'.format(hex(t)) return 'Unknown type {}'.format(hex(t))

@ -280,7 +280,8 @@ class ContractSolc04(Contract):
try: try:
for modifier in self.modifiers: for modifier in self.modifiers:
modifier.analyze_content() modifier.analyze_content()
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
return return
@ -288,7 +289,8 @@ class ContractSolc04(Contract):
try: try:
for function in self.functions: for function in self.functions:
function.analyze_content() function.analyze_content()
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
return return
@ -300,7 +302,8 @@ class ContractSolc04(Contract):
getter_available = lambda f: f.modifiers_declared getter_available = lambda f: f.modifiers_declared
Cls = ModifierSolc Cls = ModifierSolc
self._modifiers = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls) self._modifiers = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls)
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
self._modifiers_no_params = [] self._modifiers_no_params = []
@ -313,7 +316,8 @@ class ContractSolc04(Contract):
getter_available = lambda f: f.functions_declared getter_available = lambda f: f.functions_declared
Cls = FunctionSolc Cls = FunctionSolc
self._functions = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls) self._functions = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls)
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
self._functions_no_params = [] self._functions_no_params = []
return return
@ -364,7 +368,8 @@ class ContractSolc04(Contract):
if accessible_elements[element.full_name] != all_elements[element.canonical_name]: if accessible_elements[element.full_name] != all_elements[element.canonical_name]:
element.is_shadowed = True element.is_shadowed = True
accessible_elements[element.full_name].shadows = True accessible_elements[element.full_name].shadows = True
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
return all_elements return all_elements
@ -374,7 +379,8 @@ class ContractSolc04(Contract):
# cant parse constant expression based on function calls # cant parse constant expression based on function calls
try: try:
var.analyze(self) var.analyze(self)
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
pass pass
return return
@ -451,7 +457,8 @@ class ContractSolc04(Contract):
for var in self.variables: for var in self.variables:
var.analyze(self) var.analyze(self)
return return
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
def analyze_using_for(self): def analyze_using_for(self):
@ -483,7 +490,8 @@ class ContractSolc04(Contract):
self.using_for[old] = [] self.using_for[old] = []
self._using_for[old].append(new) self._using_for[old].append(new)
self._usingForNotParsed = [] self._usingForNotParsed = []
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
def analyze_enums(self): def analyze_enums(self):
@ -496,7 +504,8 @@ class ContractSolc04(Contract):
# at the same time # at the same time
self._analyze_enum(enum) self._analyze_enum(enum)
self._enumsNotParsed = None self._enumsNotParsed = None
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
def _analyze_enum(self, enum): def _analyze_enum(self, enum):
@ -530,7 +539,8 @@ class ContractSolc04(Contract):
try: try:
for struct in self.structures: for struct in self.structures:
self._analyze_struct(struct) self._analyze_struct(struct)
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
def analyze_events(self): def analyze_events(self):
@ -544,7 +554,8 @@ class ContractSolc04(Contract):
event.set_contract(self) event.set_contract(self)
event.set_offset(event_to_parse['src'], self.slither) event.set_offset(event_to_parse['src'], self.slither)
self._events[event.full_name] = event self._events[event.full_name] = event
except (VariableNotFound, KeyError): except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing() self.log_incorrect_parsing()
self._eventsNotParsed = None self._eventsNotParsed = None

@ -494,6 +494,39 @@ class FunctionSolc(Function):
link_nodes(node_condition, node_endDoWhile) link_nodes(node_condition, node_endDoWhile)
return node_endDoWhile return node_endDoWhile
def _parse_try_catch(self, statement, node):
externalCall = statement.get('externalCall', None)
if externalCall is None:
raise ParsingError('Try/Catch not correctly parsed by Slither %s' % statement)
new_node = self._new_node(NodeType.TRY, statement['src'])
new_node.add_unparsed_expression(externalCall)
link_nodes(node, new_node)
node = new_node
for clause in statement.get('clauses', []):
self._parse_catch(clause, node)
return node
def _parse_catch(self, statement, node):
block = statement.get('block', None)
if block is None:
raise ParsingError('Catch not correctly parsed by Slither %s' % statement)
try_node = self._new_node(NodeType.CATCH, statement['src'])
link_nodes(node, try_node)
if self.is_compact_ast:
params = statement['parameters']
else:
params = statement[self.get_children('children')]
for param in params.get('parameters', []):
assert param[self.get_key()] == 'VariableDeclaration'
self._add_param(param)
return self._parse_statement(block, try_node)
def _parse_variable_definition(self, statement, node): def _parse_variable_definition(self, statement, node):
try: try:
local_var = LocalVariableSolc(statement) local_var = LocalVariableSolc(statement)
@ -753,8 +786,12 @@ class FunctionSolc(Function):
new_node.add_unparsed_expression(expression) new_node.add_unparsed_expression(expression)
link_nodes(node, new_node) link_nodes(node, new_node)
node = new_node node = new_node
elif name == 'TryStatement':
node = self._parse_try_catch(statement, node)
# elif name == 'TryCatchClause':
# self._parse_catch(statement, node)
else: else:
raise ParsingError('Statement not parsed %s'%name) raise ParsingError('Statement not parsed %s' % name)
return node return node
@ -865,20 +902,21 @@ class FunctionSolc(Function):
node.set_sons([start_node]) node.set_sons([start_node])
start_node.add_father(node) start_node.add_father(node)
def _parse_params(self, params): def _fix_try(self, node):
assert params[self.get_key()] == 'ParameterList' end_node = next((son for son in node.sons if son.type != NodeType.CATCH), None)
if end_node:
self.parameters_src = SourceMapping() for son in node.sons:
self.parameters_src.set_offset(params['src'], self.contract.slither) if son.type == NodeType.CATCH:
self._fix_catch(son, end_node)
if self.is_compact_ast: def _fix_catch(self, node, end_node):
params = params['parameters'] if not node.sons:
link_nodes(node, end_node)
else: else:
params = params[self.get_children('children')] for son in node.sons:
self._fix_catch(son, end_node)
for param in params:
assert param[self.get_key()] == 'VariableDeclaration'
def _add_param(self, param):
local_var = LocalVariableSolc(param) local_var = LocalVariableSolc(param)
local_var.set_function(self) local_var.set_function(self)
@ -890,8 +928,26 @@ class FunctionSolc(Function):
local_var.set_location('memory') local_var.set_location('memory')
self._add_local_variable(local_var) self._add_local_variable(local_var)
return local_var
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 = self._add_param(param)
self._parameters.append(local_var) self._parameters.append(local_var)
def _parse_returns(self, returns): def _parse_returns(self, returns):
assert returns[self.get_key()] == 'ParameterList' assert returns[self.get_key()] == 'ParameterList'
@ -906,18 +962,7 @@ class FunctionSolc(Function):
for ret in returns: for ret in returns:
assert ret[self.get_key()] == 'VariableDeclaration' assert ret[self.get_key()] == 'VariableDeclaration'
local_var = self._add_param(ret)
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) self._returns.append(local_var)
@ -973,6 +1018,8 @@ class FunctionSolc(Function):
self._fix_break_node(node) self._fix_break_node(node)
if node.type in [NodeType.CONTINUE]: if node.type in [NodeType.CONTINUE]:
self._fix_continue_node(node) self._fix_continue_node(node)
if node.type in [NodeType.TRY]:
self._fix_try(node)
def _remove_alone_endif(self): def _remove_alone_endif(self):
""" """

Loading…
Cancel
Save