Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for 0.6 #415

Merged
merged 2 commits into from
Mar 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions slither/core/cfg/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,40 +62,47 @@ class NodeType:
# Only modifier node
PLACEHOLDER = 0x40

TRY = 0x41
CATCH = 0x42

# Node not related to the CFG
# Use for state variable declaration
OTHER_ENTRYPOINT = 0x50


# @staticmethod
def str(t):
if t == 0x0:
if t == NodeType.ENTRYPOINT:
return 'ENTRY_POINT'
if t == 0x10:
if t == NodeType.EXPRESSION:
return 'EXPRESSION'
if t == 0x11:
if t == NodeType.RETURN:
return 'RETURN'
if t == 0x12:
if t == NodeType.IF:
return 'IF'
if t == 0x13:
if t == NodeType.VARIABLE:
return 'NEW VARIABLE'
if t == 0x14:
if t == NodeType.ASSEMBLY:
return 'INLINE ASM'
if t == 0x15:
if t == NodeType.IFLOOP:
return 'IF_LOOP'
if t == 0x20:
if t == NodeType.THROW:
return 'THROW'
if t == 0x31:
if t == NodeType.BREAK:
return 'BREAK'
if t == 0x32:
if t == NodeType.CONTINUE:
return 'CONTINUE'
if t == 0x40:
if t == NodeType.PLACEHOLDER:
return '_'
if t == 0x50:
if t == NodeType.TRY:
return 'TRY'
if t == NodeType.CATCH:
return 'CATCH'
if t == NodeType.ENDIF:
return 'END_IF'
if t == 0x51:
if t == NodeType.STARTLOOP:
return 'BEGIN_LOOP'
if t == 0x52:
if t == NodeType.ENDLOOP:
return 'END_LOOP'
return 'Unknown type {}'.format(hex(t))

Expand Down
7 changes: 5 additions & 2 deletions slither/core/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ class FunctionType(Enum):
NORMAL = 0
CONSTRUCTOR = 1
FALLBACK = 2
CONSTRUCTOR_VARIABLES = 3 # Fake function to hold variable declaration statements
CONSTRUCTOR_CONSTANT_VARIABLES = 4 # Fake function to hold variable declaration statements
RECEIVE = 3
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):
"""
Expand Down Expand Up @@ -156,6 +157,8 @@ def name(self):
return 'constructor'
elif self._function_type == FunctionType.FALLBACK:
return 'fallback'
elif self._function_type == FunctionType.RECEIVE:
return 'receive'
elif self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
return 'slitherConstructorVariables'
elif self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
Expand Down
33 changes: 22 additions & 11 deletions slither/solc_parsing/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,17 @@ def analyze_content_modifiers(self):
try:
for modifier in self.modifiers:
modifier.analyze_content()
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()
return

def analyze_content_functions(self):
try:
for function in self.functions:
function.analyze_content()
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()
return

Expand All @@ -300,7 +302,8 @@ def analyze_params_modifiers(self):
getter_available = lambda f: f.modifiers_declared
Cls = ModifierSolc
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._modifiers_no_params = []

Expand All @@ -313,7 +316,8 @@ def analyze_params_functions(self):
getter_available = lambda f: f.functions_declared
Cls = FunctionSolc
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._functions_no_params = []
return
Expand Down Expand Up @@ -364,7 +368,8 @@ def _analyze_params_elements(self, elements_no_params, getter, getter_available,
if accessible_elements[element.full_name] != all_elements[element.canonical_name]:
element.is_shadowed = True
accessible_elements[element.full_name].shadows = True
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()
return all_elements

Expand All @@ -374,7 +379,8 @@ def analyze_constant_state_variables(self):
# cant parse constant expression based on function calls
try:
var.analyze(self)
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
pass
return

Expand Down Expand Up @@ -451,7 +457,8 @@ def analyze_state_variables(self):
for var in self.variables:
var.analyze(self)
return
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()

def analyze_using_for(self):
Expand Down Expand Up @@ -483,7 +490,8 @@ def analyze_using_for(self):
self.using_for[old] = []
self._using_for[old].append(new)
self._usingForNotParsed = []
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()

def analyze_enums(self):
Expand All @@ -496,7 +504,8 @@ def analyze_enums(self):
# at the same time
self._analyze_enum(enum)
self._enumsNotParsed = None
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()

def _analyze_enum(self, enum):
Expand Down Expand Up @@ -530,7 +539,8 @@ def analyze_structs(self):
try:
for struct in self.structures:
self._analyze_struct(struct)
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()

def analyze_events(self):
Expand All @@ -544,7 +554,8 @@ def analyze_events(self):
event.set_contract(self)
event.set_offset(event_to_parse['src'], self.slither)
self._events[event.full_name] = event
except (VariableNotFound, KeyError):
except (VariableNotFound, KeyError) as e:
logger.error(e)
self.log_incorrect_parsing()

self._eventsNotParsed = None
Expand Down
132 changes: 99 additions & 33 deletions slither/solc_parsing/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def __init__(self, function, contract, contract_declarer):
# which is only possible with solc > 0.5
self._variables_renamed = {}

self._analyze_type()

###################################################################################
###################################################################################
# region AST format
Expand Down Expand Up @@ -113,6 +115,31 @@ def _add_local_variable(self, local_var):
###################################################################################
###################################################################################

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):
if self.is_compact_ast:
attributes = self._functionNotParsed
Expand All @@ -133,14 +160,6 @@ def _analyze_attributes(self):
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

Expand Down Expand Up @@ -475,6 +494,39 @@ def _parse_dowhile(self, doWhilestatement, node):
link_nodes(node_condition, 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):
try:
local_var = LocalVariableSolc(statement)
Expand Down Expand Up @@ -734,8 +786,12 @@ def _parse_statement(self, statement, node):
new_node.add_unparsed_expression(expression)
link_nodes(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:
raise ParsingError('Statement not parsed %s'%name)
raise ParsingError('Statement not parsed %s' % name)

return node

Expand Down Expand Up @@ -846,6 +902,35 @@ def _fix_continue_node(self, node):
node.set_sons([start_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):
assert params[self.get_key()] == 'ParameterList'

Expand All @@ -859,20 +944,10 @@ def _parse_params(self, params):

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)
local_var = self._add_param(param)
self._parameters.append(local_var)


def _parse_returns(self, returns):

assert returns[self.get_key()] == 'ParameterList'
Expand All @@ -887,18 +962,7 @@ def _parse_returns(self, returns):

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)
local_var = self._add_param(ret)
self._returns.append(local_var)


Expand Down Expand Up @@ -954,6 +1018,8 @@ def _remove_incorrect_edges(self):
self._fix_break_node(node)
if node.type in [NodeType.CONTINUE]:
self._fix_continue_node(node)
if node.type in [NodeType.TRY]:
self._fix_try(node)

def _remove_alone_endif(self):
"""
Expand Down