Merge branch 'dev' into dev-fix-constructor

pull/416/head
Josselin 5 years ago
commit ce878aff8b
  1. 35
      slither/core/cfg/node.py
  2. 7
      slither/core/declarations/function.py
  3. 33
      slither/solc_parsing/declarations/contract.py
  4. 132
      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))

@ -54,8 +54,9 @@ class FunctionType(Enum):
NORMAL = 0 NORMAL = 0
CONSTRUCTOR = 1 CONSTRUCTOR = 1
FALLBACK = 2 FALLBACK = 2
CONSTRUCTOR_VARIABLES = 3 # Fake function to hold variable declaration statements RECEIVE = 3
CONSTRUCTOR_CONSTANT_VARIABLES = 4 # Fake function to hold variable declaration statements CONSTRUCTOR_VARIABLES = 10 # Fake function to hold variable declaration statements
CONSTRUCTOR_CONSTANT_VARIABLES = 11 # Fake function to hold variable declaration statements
class Function(ChildContract, ChildInheritance, SourceMapping): class Function(ChildContract, ChildInheritance, SourceMapping):
""" """
@ -156,6 +157,8 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
return 'constructor' return 'constructor'
elif self._function_type == FunctionType.FALLBACK: elif self._function_type == FunctionType.FALLBACK:
return 'fallback' return 'fallback'
elif self._function_type == FunctionType.RECEIVE:
return 'receive'
elif self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: elif self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
return 'slitherConstructorVariables' return 'slitherConstructorVariables'
elif self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: elif self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:

@ -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

@ -56,6 +56,8 @@ class FunctionSolc(Function):
# which is only possible with solc > 0.5 # which is only possible with solc > 0.5
self._variables_renamed = {} self._variables_renamed = {}
self._analyze_type()
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region AST format # region AST format
@ -113,6 +115,31 @@ class FunctionSolc(Function):
################################################################################### ###################################################################################
################################################################################### ###################################################################################
def _analyze_type(self):
"""
Analyz the type of the function
Myst be called in the constructor as the name might change according to the function's type
For example both the fallback and the receiver will have an empty name
:return:
"""
if self.is_compact_ast:
attributes = self._functionNotParsed
else:
attributes = self._functionNotParsed['attributes']
if self._name == '':
self._function_type = FunctionType.FALLBACK
# 0.6.x introduced the receiver function
# It has also an empty name, so we need to check the kind attribute
if 'kind' in attributes:
if attributes['kind'] == 'receive':
self._function_type = FunctionType.RECEIVE
else:
self._function_type = FunctionType.NORMAL
if self._name == self.contract_declarer.name:
self._function_type = FunctionType.CONSTRUCTOR
def _analyze_attributes(self): def _analyze_attributes(self):
if self.is_compact_ast: if self.is_compact_ast:
attributes = self._functionNotParsed attributes = self._functionNotParsed
@ -133,14 +160,6 @@ class FunctionSolc(Function):
if 'constant' in attributes: if 'constant' in attributes:
self._view = attributes['constant'] 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']: if 'isConstructor' in attributes and attributes['isConstructor']:
self._function_type = FunctionType.CONSTRUCTOR self._function_type = FunctionType.CONSTRUCTOR
@ -475,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)
@ -734,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
@ -846,6 +902,35 @@ class FunctionSolc(Function):
node.set_sons([start_node]) node.set_sons([start_node])
start_node.add_father(node) start_node.add_father(node)
def _fix_try(self, node):
end_node = next((son for son in node.sons if son.type != NodeType.CATCH), None)
if end_node:
for son in node.sons:
if son.type == NodeType.CATCH:
self._fix_catch(son, end_node)
def _fix_catch(self, node, end_node):
if not node.sons:
link_nodes(node, end_node)
else:
for son in node.sons:
self._fix_catch(son, end_node)
def _add_param(self, param):
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)
return local_var
def _parse_params(self, params): def _parse_params(self, params):
assert params[self.get_key()] == 'ParameterList' assert params[self.get_key()] == 'ParameterList'
@ -859,20 +944,10 @@ class FunctionSolc(Function):
for param in params: for param in params:
assert param[self.get_key()] == 'VariableDeclaration' assert param[self.get_key()] == 'VariableDeclaration'
local_var = self._add_param(param)
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) 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'
@ -887,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)
@ -954,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