@ -6,7 +6,7 @@ from abc import abstractmethod, ABCMeta
from collections import namedtuple
from collections import namedtuple
from enum import Enum
from enum import Enum
from itertools import groupby
from itertools import groupby
from typing import Dict , TYPE_CHECKING , List , Optional , Set , Union , Callable , Tuple
from typing import Any , Dict , TYPE_CHECKING , List , Optional , Set , Union , Callable , Tuple
from slither . core . cfg . scope import Scope
from slither . core . cfg . scope import Scope
from slither . core . declarations . solidity_variables import (
from slither . core . declarations . solidity_variables import (
@ -27,6 +27,7 @@ from slither.core.variables.state_variable import StateVariable
from slither . utils . type import convert_type_for_solidity_signature_to_string
from slither . utils . type import convert_type_for_solidity_signature_to_string
from slither . utils . utils import unroll
from slither . utils . utils import unroll
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
if TYPE_CHECKING :
if TYPE_CHECKING :
@ -45,6 +46,8 @@ if TYPE_CHECKING:
from slither . slithir . operations import Operation
from slither . slithir . operations import Operation
from slither . core . compilation_unit import SlitherCompilationUnit
from slither . core . compilation_unit import SlitherCompilationUnit
from slither . core . scope . scope import FileScope
from slither . core . scope . scope import FileScope
from slither . slithir . variables . state_variable import StateIRVariable
from slither . core . declarations . function_contract import FunctionContract
LOGGER = logging . getLogger ( " Function " )
LOGGER = logging . getLogger ( " Function " )
ReacheableNode = namedtuple ( " ReacheableNode " , [ " node " , " ir " ] )
ReacheableNode = namedtuple ( " ReacheableNode " , [ " node " , " ir " ] )
@ -56,7 +59,7 @@ class ModifierStatements:
modifier : Union [ " Contract " , " Function " ] ,
modifier : Union [ " Contract " , " Function " ] ,
entry_point : " Node " ,
entry_point : " Node " ,
nodes : List [ " Node " ] ,
nodes : List [ " Node " ] ,
) :
) - > None :
self . _modifier = modifier
self . _modifier = modifier
self . _entry_point = entry_point
self . _entry_point = entry_point
self . _nodes = nodes
self . _nodes = nodes
@ -116,7 +119,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
Function class
Function class
"""
"""
def __init__ ( self , compilation_unit : " SlitherCompilationUnit " ) :
def __init__ ( self , compilation_unit : " SlitherCompilationUnit " ) - > None :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . _internal_scope : List [ str ] = [ ]
self . _internal_scope : List [ str ] = [ ]
self . _name : Optional [ str ] = None
self . _name : Optional [ str ] = None
@ -295,7 +298,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def contains_assembly ( self , c : bool ) :
def contains_assembly ( self , c : bool ) :
self . _contains_assembly = c
self . _contains_assembly = c
def can_reenter ( self , callstack = None ) - > bool :
def can_reenter ( self , callstack : Optional [ List [ " FunctionContract " ] ] = None ) - > bool :
"""
"""
Check if the function can re - enter
Check if the function can re - enter
Follow internal calls .
Follow internal calls .
@ -370,7 +373,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
###################################################################################
###################################################################################
def set_function_type ( self , t : FunctionType ) :
def set_function_type ( self , t : FunctionType ) - > None :
assert isinstance ( t , FunctionType )
assert isinstance ( t , FunctionType )
self . _function_type = t
self . _function_type = t
@ -455,7 +458,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def visibility ( self , v : str ) :
def visibility ( self , v : str ) :
self . _visibility = v
self . _visibility = v
def set_visibility ( self , v : str ) :
def set_visibility ( self , v : str ) - > None :
self . _visibility = v
self . _visibility = v
@property
@property
@ -554,7 +557,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def entry_point ( self , node : " Node " ) :
def entry_point ( self , node : " Node " ) :
self . _entry_point = node
self . _entry_point = node
def add_node ( self , node : " Node " ) :
def add_node ( self , node : " Node " ) - > None :
if not self . _entry_point :
if not self . _entry_point :
self . _entry_point = node
self . _entry_point = node
self . _nodes . append ( node )
self . _nodes . append ( node )
@ -598,7 +601,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
"""
return list ( self . _parameters )
return list ( self . _parameters )
def add_parameters ( self , p : " LocalVariable " ) :
def add_parameters ( self , p : " LocalVariable " ) - > None :
self . _parameters . append ( p )
self . _parameters . append ( p )
@property
@property
@ -608,7 +611,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
"""
return list ( self . _parameters_ssa )
return list ( self . _parameters_ssa )
def add_parameter_ssa ( self , var : " LocalIRVariable " ) :
def add_parameter_ssa ( self , var : " LocalIRVariable " ) - > None :
self . _parameters_ssa . append ( var )
self . _parameters_ssa . append ( var )
def parameters_src ( self ) - > SourceMapping :
def parameters_src ( self ) - > SourceMapping :
@ -651,7 +654,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
"""
return list ( self . _returns )
return list ( self . _returns )
def add_return ( self , r : " LocalVariable " ) :
def add_return ( self , r : " LocalVariable " ) - > None :
self . _returns . append ( r )
self . _returns . append ( r )
@property
@property
@ -661,7 +664,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
"""
return list ( self . _returns_ssa )
return list ( self . _returns_ssa )
def add_return_ssa ( self , var : " LocalIRVariable " ) :
def add_return_ssa ( self , var : " LocalIRVariable " ) - > None :
self . _returns_ssa . append ( var )
self . _returns_ssa . append ( var )
# endregion
# endregion
@ -680,7 +683,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
"""
return [ c . modifier for c in self . _modifiers ]
return [ c . modifier for c in self . _modifiers ]
def add_modifier ( self , modif : " ModifierStatements " ) :
def add_modifier ( self , modif : " ModifierStatements " ) - > None :
self . _modifiers . append ( modif )
self . _modifiers . append ( modif )
@property
@property
@ -714,7 +717,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
# This is a list of contracts internally, so we convert it to a list of constructor functions.
# This is a list of contracts internally, so we convert it to a list of constructor functions.
return list ( self . _explicit_base_constructor_calls )
return list ( self . _explicit_base_constructor_calls )
def add_explicit_base_constructor_calls_statements ( self , modif : ModifierStatements ) :
def add_explicit_base_constructor_calls_statements ( self , modif : ModifierStatements ) - > None :
self . _explicit_base_constructor_calls . append ( modif )
self . _explicit_base_constructor_calls . append ( modif )
# endregion
# endregion
@ -1057,7 +1060,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self . _all_reachable_from_functions = functions
self . _all_reachable_from_functions = functions
return self . _all_reachable_from_functions
return self . _all_reachable_from_functions
def add_reachable_from_node ( self , n : " Node " , ir : " Operation " ) :
def add_reachable_from_node ( self , n : " Node " , ir : " Operation " ) - > None :
self . _reachable_from_nodes . add ( ReacheableNode ( n , ir ) )
self . _reachable_from_nodes . add ( ReacheableNode ( n , ir ) )
self . _reachable_from_functions . add ( n . function )
self . _reachable_from_functions . add ( n . function )
@ -1068,7 +1071,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
###################################################################################
###################################################################################
def _explore_functions ( self , f_new_values : Callable [ [ " Function " ] , List ] ) :
def _explore_functions ( self , f_new_values : Callable [ [ " Function " ] , List ] ) - > List [ Any ] :
values = f_new_values ( self )
values = f_new_values ( self )
explored = [ self ]
explored = [ self ]
to_explore = [
to_explore = [
@ -1218,11 +1221,13 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
func : " Function " ,
func : " Function " ,
f : Callable [ [ " Node " ] , List [ SolidityVariable ] ] ,
f : Callable [ [ " Node " ] , List [ SolidityVariable ] ] ,
include_loop : bool ,
include_loop : bool ,
) :
) - > List [ Any ] :
ret = [ f ( n ) for n in func . nodes if n . is_conditional ( include_loop ) ]
ret = [ f ( n ) for n in func . nodes if n . is_conditional ( include_loop ) ]
return [ item for sublist in ret for item in sublist ]
return [ item for sublist in ret for item in sublist ]
def all_conditional_solidity_variables_read ( self , include_loop = True ) - > List [ SolidityVariable ] :
def all_conditional_solidity_variables_read (
self , include_loop : bool = True
) - > List [ SolidityVariable ] :
"""
"""
Return the Soldiity variables directly used in a condtion
Return the Soldiity variables directly used in a condtion
@ -1258,7 +1263,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return [ var for var in ret if isinstance ( var , SolidityVariable ) ]
return [ var for var in ret if isinstance ( var , SolidityVariable ) ]
@staticmethod
@staticmethod
def _explore_func_nodes ( func : " Function " , f : Callable [ [ " Node " ] , List [ SolidityVariable ] ] ) :
def _explore_func_nodes (
func : " Function " , f : Callable [ [ " Node " ] , List [ SolidityVariable ] ]
) - > List [ Union [ Any , SolidityVariableComposed ] ] :
ret = [ f ( n ) for n in func . nodes ]
ret = [ f ( n ) for n in func . nodes ]
return [ item for sublist in ret for item in sublist ]
return [ item for sublist in ret for item in sublist ]
@ -1367,7 +1374,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
with open ( filename , " w " , encoding = " utf8 " ) as f :
with open ( filename , " w " , encoding = " utf8 " ) as f :
f . write ( content )
f . write ( content )
def slithir_cfg_to_dot_str ( self , skip_expressions = False ) - > str :
def slithir_cfg_to_dot_str ( self , skip_expressions : bool = False ) - > str :
"""
"""
Export the CFG to a DOT format . The nodes includes the Solidity expressions and the IRs
Export the CFG to a DOT format . The nodes includes the Solidity expressions and the IRs
: return : the DOT content
: return : the DOT content
@ -1512,7 +1519,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
###################################################################################
###################################################################################
def _analyze_read_write ( self ) :
def _analyze_read_write ( self ) - > None :
""" Compute variables read/written/... """
""" Compute variables read/written/... """
write_var = [ x . variables_written_as_expression for x in self . nodes ]
write_var = [ x . variables_written_as_expression for x in self . nodes ]
write_var = [ x for x in write_var if x ]
write_var = [ x for x in write_var if x ]
@ -1570,7 +1577,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
slithir_variables = [ x for x in slithir_variables if x ]
slithir_variables = [ x for x in slithir_variables if x ]
self . _slithir_variables = [ item for sublist in slithir_variables for item in sublist ]
self . _slithir_variables = [ item for sublist in slithir_variables for item in sublist ]
def _analyze_calls ( self ) :
def _analyze_calls ( self ) - > None :
calls = [ x . calls_as_expression for x in self . nodes ]
calls = [ x . calls_as_expression for x in self . nodes ]
calls = [ x for x in calls if x ]
calls = [ x for x in calls if x ]
calls = [ item for sublist in calls for item in sublist ]
calls = [ item for sublist in calls for item in sublist ]
@ -1702,7 +1709,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return self . _get_last_ssa_variable_instances ( target_state = False , target_local = True )
return self . _get_last_ssa_variable_instances ( target_state = False , target_local = True )
@staticmethod
@staticmethod
def _unchange_phi ( ir : " Operation " ) :
def _unchange_phi ( ir : " Operation " ) - > bool :
from slither . slithir . operations import Phi , PhiCallback
from slither . slithir . operations import Phi , PhiCallback
if not isinstance ( ir , ( Phi , PhiCallback ) ) or len ( ir . rvalues ) > 1 :
if not isinstance ( ir , ( Phi , PhiCallback ) ) or len ( ir . rvalues ) > 1 :
@ -1711,7 +1718,13 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return True
return True
return ir . rvalues [ 0 ] == ir . lvalue
return ir . rvalues [ 0 ] == ir . lvalue
def fix_phi ( self , last_state_variables_instances , initial_state_variables_instances ) :
def fix_phi (
self ,
last_state_variables_instances : Dict [
str , Union [ List [ Any ] , List [ Union [ Any , " StateIRVariable " ] ] , List [ " StateIRVariable " ] ]
] ,
initial_state_variables_instances : Dict [ str , " StateIRVariable " ] ,
) - > None :
from slither . slithir . operations import InternalCall , PhiCallback
from slither . slithir . operations import InternalCall , PhiCallback
from slither . slithir . variables import Constant , StateIRVariable
from slither . slithir . variables import Constant , StateIRVariable
@ -1745,7 +1758,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
node . irs_ssa = [ ir for ir in node . irs_ssa if not self . _unchange_phi ( ir ) ]
node . irs_ssa = [ ir for ir in node . irs_ssa if not self . _unchange_phi ( ir ) ]
def generate_slithir_and_analyze ( self ) :
def generate_slithir_and_analyze ( self ) - > None :
for node in self . nodes :
for node in self . nodes :
node . slithir_generation ( )
node . slithir_generation ( )
@ -1756,7 +1769,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def generate_slithir_ssa ( self , all_ssa_state_variables_instances ) :
def generate_slithir_ssa ( self , all_ssa_state_variables_instances ) :
pass
pass
def update_read_write_using_ssa ( self ) :
def update_read_write_using_ssa ( self ) - > None :
for node in self . nodes :
for node in self . nodes :
node . update_read_write_using_ssa ( )
node . update_read_write_using_ssa ( )
self . _analyze_read_write ( )
self . _analyze_read_write ( )
@ -1767,7 +1780,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
###################################################################################
###################################################################################
def __str__ ( self ) :
def __str__ ( self ) - > str :
return self . name
return self . name
# endregion
# endregion