slither.core.declarations.function

Function module

   1"""
   2    Function module
   3"""
   4import logging
   5from abc import abstractmethod, ABCMeta
   6from collections import namedtuple
   7from enum import Enum
   8from itertools import groupby
   9from typing import Any, Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
  10
  11from slither.core.cfg.scope import Scope
  12from slither.core.declarations.solidity_variables import (
  13    SolidityFunction,
  14    SolidityVariable,
  15    SolidityVariableComposed,
  16)
  17from slither.core.expressions import (
  18    Identifier,
  19    IndexAccess,
  20    MemberAccess,
  21    UnaryOperation,
  22)
  23from slither.core.solidity_types.type import Type
  24from slither.core.source_mapping.source_mapping import SourceMapping
  25from slither.core.variables.local_variable import LocalVariable
  26from slither.core.variables.state_variable import StateVariable
  27from slither.utils.type import convert_type_for_solidity_signature_to_string
  28from slither.utils.utils import unroll
  29
  30
  31# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
  32
  33if TYPE_CHECKING:
  34    from slither.utils.type_helpers import (
  35        InternalCallType,
  36        LowLevelCallType,
  37        HighLevelCallType,
  38        LibraryCallType,
  39    )
  40    from slither.core.declarations import Contract, FunctionContract
  41    from slither.core.cfg.node import Node, NodeType
  42    from slither.core.variables.variable import Variable
  43    from slither.slithir.variables.variable import SlithIRVariable
  44    from slither.slithir.variables import LocalIRVariable
  45    from slither.core.expressions.expression import Expression
  46    from slither.slithir.operations import Operation
  47    from slither.core.compilation_unit import SlitherCompilationUnit
  48    from slither.core.scope.scope import FileScope
  49
  50LOGGER = logging.getLogger("Function")
  51ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
  52
  53
  54class ModifierStatements:
  55    def __init__(
  56        self,
  57        modifier: Union["Contract", "Function"],
  58        entry_point: "Node",
  59        nodes: List["Node"],
  60    ) -> None:
  61        self._modifier = modifier
  62        self._entry_point = entry_point
  63        self._nodes = nodes
  64
  65    @property
  66    def modifier(self) -> Union["Contract", "Function"]:
  67        return self._modifier
  68
  69    @property
  70    def entry_point(self) -> "Node":
  71        return self._entry_point
  72
  73    @entry_point.setter
  74    def entry_point(self, entry_point: "Node"):
  75        self._entry_point = entry_point
  76
  77    @property
  78    def nodes(self) -> List["Node"]:
  79        return self._nodes
  80
  81    @nodes.setter
  82    def nodes(self, nodes: List["Node"]):
  83        self._nodes = nodes
  84
  85
  86class FunctionType(Enum):
  87    NORMAL = 0
  88    CONSTRUCTOR = 1
  89    FALLBACK = 2
  90    RECEIVE = 3
  91    CONSTRUCTOR_VARIABLES = 10  # Fake function to hold variable declaration statements
  92    CONSTRUCTOR_CONSTANT_VARIABLES = 11  # Fake function to hold variable declaration statements
  93
  94
  95def _filter_state_variables_written(expressions: List["Expression"]):
  96    ret = []
  97
  98    for expression in expressions:
  99        if isinstance(expression, (Identifier, UnaryOperation, MemberAccess)):
 100            ret.append(expression.expression)
 101        elif isinstance(expression, IndexAccess):
 102            ret.append(expression.expression_left)
 103    return ret
 104
 105
 106class FunctionLanguage(Enum):
 107    Solidity = 0
 108    Yul = 1
 109    Vyper = 2
 110
 111
 112class Function(SourceMapping, metaclass=ABCMeta):  # pylint: disable=too-many-public-methods
 113    """
 114    Function class
 115    """
 116
 117    def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
 118        super().__init__()
 119        self._internal_scope: List[str] = []
 120        self._name: Optional[str] = None
 121        self._view: bool = False
 122        self._pure: bool = False
 123        self._payable: bool = False
 124        self._visibility: Optional[str] = None
 125        self._virtual: bool = False
 126        self._overrides: List["FunctionContract"] = []
 127        self._overridden_by: List["FunctionContract"] = []
 128
 129        self._is_implemented: Optional[bool] = None
 130        self._is_empty: Optional[bool] = None
 131        self._entry_point: Optional["Node"] = None
 132        self._nodes: List["Node"] = []
 133        self._variables: Dict[str, "LocalVariable"] = {}
 134        # slithir Temporary and references variables (but not SSA)
 135        self._slithir_variables: Set["SlithIRVariable"] = set()
 136        self._parameters: List["LocalVariable"] = []
 137        self._parameters_ssa: List["LocalIRVariable"] = []
 138        self._parameters_src: SourceMapping = SourceMapping()
 139        # This is used for vyper calls with default arguments
 140        self._default_args_as_expressions: List["Expression"] = []
 141        self._returns: List["LocalVariable"] = []
 142        self._returns_ssa: List["LocalIRVariable"] = []
 143        self._returns_src: SourceMapping = SourceMapping()
 144        self._return_values: Optional[List["SlithIRVariable"]] = None
 145        self._return_values_ssa: Optional[List["SlithIRVariable"]] = None
 146        self._vars_read: List["Variable"] = []
 147        self._vars_written: List["Variable"] = []
 148        self._state_vars_read: List["StateVariable"] = []
 149        self._vars_read_or_written: List["Variable"] = []
 150        self._solidity_vars_read: List["SolidityVariable"] = []
 151        self._state_vars_written: List["StateVariable"] = []
 152        self._internal_calls: List["InternalCallType"] = []
 153        self._solidity_calls: List["SolidityFunction"] = []
 154        self._low_level_calls: List["LowLevelCallType"] = []
 155        self._high_level_calls: List["HighLevelCallType"] = []
 156        self._library_calls: List["LibraryCallType"] = []
 157        self._external_calls_as_expressions: List["Expression"] = []
 158        self._expression_vars_read: List["Expression"] = []
 159        self._expression_vars_written: List["Expression"] = []
 160        self._expression_calls: List["Expression"] = []
 161        # self._expression_modifiers: List["Expression"] = []
 162        self._modifiers: List[ModifierStatements] = []
 163        self._explicit_base_constructor_calls: List[ModifierStatements] = []
 164        self._contains_assembly: bool = False
 165
 166        self._expressions: Optional[List["Expression"]] = None
 167        self._slithir_operations: Optional[List["Operation"]] = None
 168        self._slithir_ssa_operations: Optional[List["Operation"]] = None
 169
 170        self._all_expressions: Optional[List["Expression"]] = None
 171        self._all_slithir_operations: Optional[List["Operation"]] = None
 172        self._all_internals_calls: Optional[List["InternalCallType"]] = None
 173        self._all_high_level_calls: Optional[List["HighLevelCallType"]] = None
 174        self._all_library_calls: Optional[List["LibraryCallType"]] = None
 175        self._all_low_level_calls: Optional[List["LowLevelCallType"]] = None
 176        self._all_solidity_calls: Optional[List["SolidityFunction"]] = None
 177        self._all_variables_read: Optional[List["Variable"]] = None
 178        self._all_variables_written: Optional[List["Variable"]] = None
 179        self._all_state_variables_read: Optional[List["StateVariable"]] = None
 180        self._all_solidity_variables_read: Optional[List["SolidityVariable"]] = None
 181        self._all_state_variables_written: Optional[List["StateVariable"]] = None
 182        self._all_slithir_variables: Optional[List["SlithIRVariable"]] = None
 183        self._all_nodes: Optional[List["Node"]] = None
 184        self._all_conditional_state_variables_read: Optional[List["StateVariable"]] = None
 185        self._all_conditional_state_variables_read_with_loop: Optional[List["StateVariable"]] = None
 186        self._all_conditional_solidity_variables_read: Optional[List["SolidityVariable"]] = None
 187        self._all_conditional_solidity_variables_read_with_loop: Optional[
 188            List["SolidityVariable"]
 189        ] = None
 190        self._all_solidity_variables_used_as_args: Optional[List["SolidityVariable"]] = None
 191
 192        self._is_shadowed: bool = False
 193        self._shadows: bool = False
 194
 195        # set(ReacheableNode)
 196        self._reachable_from_nodes: Set[ReacheableNode] = set()
 197        self._reachable_from_functions: Set[Function] = set()
 198        self._all_reachable_from_functions: Optional[Set[Function]] = None
 199
 200        # Constructor, fallback, State variable constructor
 201        self._function_type: Optional[FunctionType] = None
 202        self._is_constructor: Optional[bool] = None
 203
 204        # Computed on the fly, can be True of False
 205        self._can_reenter: Optional[bool] = None
 206        self._can_send_eth: Optional[bool] = None
 207
 208        self._nodes_ordered_dominators: Optional[List["Node"]] = None
 209
 210        self._counter_nodes = 0
 211
 212        # Memoize parameters:
 213        # TODO: identify all the memoize parameters and add a way to undo the memoization
 214        self._full_name: Optional[str] = None
 215        self._signature: Optional[Tuple[str, List[str], List[str]]] = None
 216        self._solidity_signature: Optional[str] = None
 217        self._signature_str: Optional[str] = None
 218        self._canonical_name: Optional[str] = None
 219        self._is_protected: Optional[bool] = None
 220
 221        self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
 222
 223        self.function_language: FunctionLanguage = (
 224            FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
 225        )
 226
 227        self._id: Optional[str] = None
 228
 229        # To be improved with a parsing of the documentation
 230        self.has_documentation: bool = False
 231
 232    ###################################################################################
 233    ###################################################################################
 234    # region General properties
 235    ###################################################################################
 236    ###################################################################################
 237
 238    @property
 239    def name(self) -> str:
 240        """
 241        str: function name
 242        """
 243        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
 244            return "constructor"
 245        if self._name == "" and self._function_type == FunctionType.FALLBACK:
 246            return "fallback"
 247        if self._function_type == FunctionType.RECEIVE:
 248            return "receive"
 249        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
 250            return "slitherConstructorVariables"
 251        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
 252            return "slitherConstructorConstantVariables"
 253        return self._name
 254
 255    @name.setter
 256    def name(self, new_name: str):
 257        self._name = new_name
 258
 259    @property
 260    def internal_scope(self) -> List[str]:
 261        """
 262        Return a list of name representing the scope of the function
 263        This is used to model nested functions declared in YUL
 264
 265        :return:
 266        """
 267        return self._internal_scope
 268
 269    @internal_scope.setter
 270    def internal_scope(self, new_scope: List[str]):
 271        self._internal_scope = new_scope
 272
 273    @property
 274    def full_name(self) -> str:
 275        """
 276        str: func_name(type1,type2)
 277        Return the function signature without the return values
 278        The difference between this function and solidity_function is that full_name does not translate the underlying
 279        type (ex: structure, contract to address, ...)
 280        """
 281        if self._full_name is None:
 282            name, parameters, _ = self.signature
 283            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
 284            self._full_name = full_name
 285        return self._full_name
 286
 287    @property
 288    @abstractmethod
 289    def canonical_name(self) -> str:
 290        """
 291        str: contract.func_name(type1,type2)
 292        Return the function signature without the return values
 293        """
 294        return ""
 295
 296    @property
 297    def contains_assembly(self) -> bool:
 298        return self._contains_assembly
 299
 300    @contains_assembly.setter
 301    def contains_assembly(self, c: bool):
 302        self._contains_assembly = c
 303
 304    def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool:
 305        """
 306        Check if the function can re-enter
 307        Follow internal calls.
 308        Do not consider CREATE as potential re-enter, but check if the
 309        destination's constructor can contain a call (recurs. follow nested CREATE)
 310        For Solidity > 0.5, filter access to public variables and constant/pure/view
 311        For call to this. check if the destination can re-enter
 312        Do not consider Send/Transfer as there is not enough gas
 313        :param callstack: used internally to check for recursion
 314        :return bool:
 315        """
 316        from slither.slithir.operations import Call
 317
 318        if self._can_reenter is None:
 319            self._can_reenter = False
 320            for ir in self.all_slithir_operations():
 321                if isinstance(ir, Call) and ir.can_reenter(callstack):
 322                    self._can_reenter = True
 323                    return True
 324        return self._can_reenter
 325
 326    def can_send_eth(self) -> bool:
 327        """
 328        Check if the function or any internal (not external) functions called by it can send eth
 329        :return bool:
 330        """
 331        from slither.slithir.operations import Call
 332
 333        if self._can_send_eth is None:
 334            self._can_send_eth = False
 335            for ir in self.all_slithir_operations():
 336                if isinstance(ir, Call) and ir.can_send_eth():
 337                    self._can_send_eth = True
 338                    return True
 339        return self._can_send_eth
 340
 341    @property
 342    def is_checked(self) -> bool:
 343        """
 344        Return true if the overflow are enabled by default
 345
 346
 347        :return:
 348        """
 349
 350        return self.compilation_unit.solc_version >= "0.8.0"
 351
 352    @property
 353    def id(self) -> Optional[str]:
 354        """
 355        Return the reference ID of the function, if available.
 356
 357        :return:
 358        :rtype:
 359        """
 360        return self._id
 361
 362    @id.setter
 363    def id(self, new_id: str):
 364        self._id = new_id
 365
 366    @property
 367    @abstractmethod
 368    def file_scope(self) -> "FileScope":
 369        pass
 370
 371    # endregion
 372    ###################################################################################
 373    ###################################################################################
 374    # region Type (FunctionType)
 375    ###################################################################################
 376    ###################################################################################
 377
 378    def set_function_type(self, t: FunctionType) -> None:
 379        assert isinstance(t, FunctionType)
 380        self._function_type = t
 381
 382    @property
 383    def function_type(self) -> Optional[FunctionType]:
 384        return self._function_type
 385
 386    @function_type.setter
 387    def function_type(self, t: FunctionType):
 388        self._function_type = t
 389
 390    @property
 391    def is_constructor(self) -> bool:
 392        """
 393        bool: True if the function is the constructor
 394        """
 395        return self._function_type == FunctionType.CONSTRUCTOR
 396
 397    @property
 398    def is_constructor_variables(self) -> bool:
 399        """
 400        bool: True if the function is the constructor of the variables
 401        Slither has inbuilt functions to hold the state variables initialization
 402        """
 403        return self._function_type in [
 404            FunctionType.CONSTRUCTOR_VARIABLES,
 405            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
 406        ]
 407
 408    @property
 409    def is_fallback(self) -> bool:
 410        """
 411            Determine if the function is the fallback function for the contract
 412        Returns
 413            (bool)
 414        """
 415        return self._function_type == FunctionType.FALLBACK
 416
 417    @property
 418    def is_receive(self) -> bool:
 419        """
 420            Determine if the function is the receive function for the contract
 421        Returns
 422            (bool)
 423        """
 424        return self._function_type == FunctionType.RECEIVE
 425
 426    # endregion
 427    ###################################################################################
 428    ###################################################################################
 429    # region Payable
 430    ###################################################################################
 431    ###################################################################################
 432
 433    @property
 434    def payable(self) -> bool:
 435        """
 436        bool: True if the function is payable
 437        """
 438        return self._payable
 439
 440    @payable.setter
 441    def payable(self, p: bool):
 442        self._payable = p
 443
 444    # endregion
 445    ###################################################################################
 446    ###################################################################################
 447    # region Virtual
 448    ###################################################################################
 449    ###################################################################################
 450
 451    @property
 452    def is_virtual(self) -> bool:
 453        """
 454        Note for Solidity < 0.6.0 it will always be false
 455        bool: True if the function is virtual
 456        """
 457        return self._virtual
 458
 459    @is_virtual.setter
 460    def is_virtual(self, v: bool):
 461        self._virtual = v
 462
 463    @property
 464    def is_override(self) -> bool:
 465        """
 466        Note for Solidity < 0.6.0 it will always be false
 467        bool: True if the function overrides a base function
 468        """
 469        return len(self._overrides) > 0
 470
 471    @property
 472    def overridden_by(self) -> List["FunctionContract"]:
 473        """
 474        List["FunctionContract"]: List of functions in child contracts that override this function
 475        This may include distinct instances of the same function due to inheritance
 476        """
 477        return self._overridden_by
 478
 479    @property
 480    def overrides(self) -> List["FunctionContract"]:
 481        """
 482        List["FunctionContract"]: List of functions in parent contracts that this function overrides
 483        This may include distinct instances of the same function due to inheritance
 484        """
 485        return self._overrides
 486
 487    # endregion
 488    ###################################################################################
 489    ###################################################################################
 490    # region Visibility
 491    ###################################################################################
 492    ###################################################################################
 493
 494    @property
 495    def visibility(self) -> str:
 496        """
 497        str: Function visibility
 498        """
 499        assert self._visibility is not None
 500        return self._visibility
 501
 502    @visibility.setter
 503    def visibility(self, v: str):
 504        self._visibility = v
 505
 506    def set_visibility(self, v: str) -> None:
 507        self._visibility = v
 508
 509    @property
 510    def view(self) -> bool:
 511        """
 512        bool: True if the function is declared as view
 513        """
 514        return self._view
 515
 516    @view.setter
 517    def view(self, v: bool):
 518        self._view = v
 519
 520    @property
 521    def pure(self) -> bool:
 522        """
 523        bool: True if the function is declared as pure
 524        """
 525        return self._pure
 526
 527    @pure.setter
 528    def pure(self, p: bool):
 529        self._pure = p
 530
 531    @property
 532    def is_shadowed(self) -> bool:
 533        return self._is_shadowed
 534
 535    @is_shadowed.setter
 536    def is_shadowed(self, is_shadowed):
 537        self._is_shadowed = is_shadowed
 538
 539    @property
 540    def shadows(self) -> bool:
 541        return self._shadows
 542
 543    @shadows.setter
 544    def shadows(self, _shadows: bool):
 545        self._shadows = _shadows
 546
 547    # endregion
 548    ###################################################################################
 549    ###################################################################################
 550    # region Function's body
 551    ###################################################################################
 552    ###################################################################################
 553
 554    @property
 555    def is_implemented(self) -> bool:
 556        """
 557        bool: True if the function is implemented
 558        """
 559        return self._is_implemented
 560
 561    @is_implemented.setter
 562    def is_implemented(self, is_impl: bool):
 563        self._is_implemented = is_impl
 564
 565    @property
 566    def is_empty(self) -> bool:
 567        """
 568        bool: True if the function is empty, None if the function is an interface
 569        """
 570        return self._is_empty
 571
 572    @is_empty.setter
 573    def is_empty(self, empty: bool):
 574        self._is_empty = empty
 575
 576    # endregion
 577    ###################################################################################
 578    ###################################################################################
 579    # region Nodes
 580    ###################################################################################
 581    ###################################################################################
 582
 583    @property
 584    def nodes(self) -> List["Node"]:
 585        """
 586        list(Node): List of the nodes
 587        """
 588        return list(self._nodes)
 589
 590    @nodes.setter
 591    def nodes(self, nodes: List["Node"]):
 592        self._nodes = nodes
 593
 594    @property
 595    def entry_point(self) -> Optional["Node"]:
 596        """
 597        Node: Entry point of the function
 598        """
 599        return self._entry_point
 600
 601    @entry_point.setter
 602    def entry_point(self, node: "Node"):
 603        self._entry_point = node
 604
 605    def add_node(self, node: "Node") -> None:
 606        if not self._entry_point:
 607            self._entry_point = node
 608        self._nodes.append(node)
 609
 610    @property
 611    def nodes_ordered_dominators(self) -> List["Node"]:
 612        # TODO: does not work properly; most likely due to modifier call
 613        # This will not work for modifier call that lead to multiple nodes
 614        # from slither.core.cfg.node import NodeType
 615        if self._nodes_ordered_dominators is None:
 616            self._nodes_ordered_dominators = []
 617            if self.entry_point:
 618                self._compute_nodes_ordered_dominators(self.entry_point)
 619
 620            for node in self.nodes:
 621                # if node.type == NodeType.OTHER_ENTRYPOINT:
 622                if not node in self._nodes_ordered_dominators:
 623                    self._compute_nodes_ordered_dominators(node)
 624
 625        return self._nodes_ordered_dominators
 626
 627    def _compute_nodes_ordered_dominators(self, node: "Node"):
 628        assert self._nodes_ordered_dominators is not None
 629        if node in self._nodes_ordered_dominators:
 630            return
 631        self._nodes_ordered_dominators.append(node)
 632        for dom in node.dominance_exploration_ordered:
 633            self._compute_nodes_ordered_dominators(dom)
 634
 635    # endregion
 636    ###################################################################################
 637    ###################################################################################
 638    # region Parameters
 639    ###################################################################################
 640    ###################################################################################
 641
 642    @property
 643    def parameters(self) -> List["LocalVariable"]:
 644        """
 645        list(LocalVariable): List of the parameters
 646        """
 647        return list(self._parameters)
 648
 649    def add_parameters(self, p: "LocalVariable") -> None:
 650        self._parameters.append(p)
 651
 652    @property
 653    def parameters_ssa(self) -> List["LocalIRVariable"]:
 654        """
 655        list(LocalIRVariable): List of the parameters (SSA form)
 656        """
 657        return list(self._parameters_ssa)
 658
 659    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
 660        self._parameters_ssa.append(var)
 661
 662    def parameters_src(self) -> SourceMapping:
 663        return self._parameters_src
 664
 665    # endregion
 666    ###################################################################################
 667    ###################################################################################
 668    # region Return values
 669    ###################################################################################
 670    ###################################################################################
 671
 672    @property
 673    def return_type(self) -> Optional[List[Type]]:
 674        """
 675        Return the list of return type
 676        If no return, return None
 677        """
 678        returns = self.returns
 679        if returns:
 680            return [r.type for r in returns]
 681        return None
 682
 683    def returns_src(self) -> SourceMapping:
 684        return self._returns_src
 685
 686    @property
 687    def type(self) -> Optional[List[Type]]:
 688        """
 689        Return the list of return type
 690        If no return, return None
 691        Alias of return_type
 692        """
 693        return self.return_type
 694
 695    @property
 696    def returns(self) -> List["LocalVariable"]:
 697        """
 698        list(LocalVariable): List of the return variables
 699        """
 700        return list(self._returns)
 701
 702    def add_return(self, r: "LocalVariable") -> None:
 703        self._returns.append(r)
 704
 705    @property
 706    def returns_ssa(self) -> List["LocalIRVariable"]:
 707        """
 708        list(LocalIRVariable): List of the return variables (SSA form)
 709        """
 710        return list(self._returns_ssa)
 711
 712    def add_return_ssa(self, var: "LocalIRVariable") -> None:
 713        self._returns_ssa.append(var)
 714
 715    # endregion
 716    ###################################################################################
 717    ###################################################################################
 718    # region Modifiers
 719    ###################################################################################
 720    ###################################################################################
 721
 722    @property
 723    def modifiers(self) -> List[Union["Contract", "Function"]]:
 724        """
 725        list(Modifier): List of the modifiers
 726        Can be contract for constructor's calls
 727
 728        """
 729        return [c.modifier for c in self._modifiers]
 730
 731    def add_modifier(self, modif: "ModifierStatements") -> None:
 732        self._modifiers.append(modif)
 733
 734    @property
 735    def modifiers_statements(self) -> List[ModifierStatements]:
 736        """
 737        list(ModifierCall): List of the modifiers call (include expression and irs)
 738        """
 739        return list(self._modifiers)
 740
 741    @property
 742    def explicit_base_constructor_calls(self) -> List["Function"]:
 743        """
 744        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
 745
 746                        Base constructors implicitly or explicitly called by the contract definition will not be
 747                        included.
 748        """
 749        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 750        return [
 751            c.modifier.constructors_declared
 752            for c in self._explicit_base_constructor_calls
 753            if c.modifier.constructors_declared
 754        ]
 755
 756    @property
 757    def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]:
 758        """
 759        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
 760
 761        """
 762        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 763        return list(self._explicit_base_constructor_calls)
 764
 765    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
 766        self._explicit_base_constructor_calls.append(modif)
 767
 768    # endregion
 769    ###################################################################################
 770    ###################################################################################
 771    # region Variables
 772    ###################################################################################
 773    ###################################################################################
 774
 775    @property
 776    def variables(self) -> List[LocalVariable]:
 777        """
 778        Return all local variables
 779        Include paramters and return values
 780        """
 781        return list(self._variables.values())
 782
 783    @property
 784    def local_variables(self) -> List[LocalVariable]:
 785        """
 786        Return all local variables (dont include paramters and return values)
 787        """
 788        return list(set(self.variables) - set(self.returns) - set(self.parameters))
 789
 790    @property
 791    def variables_as_dict(self) -> Dict[str, LocalVariable]:
 792        return self._variables
 793
 794    @property
 795    def variables_read(self) -> List["Variable"]:
 796        """
 797        list(Variable): Variables read (local/state/solidity)
 798        """
 799        return list(self._vars_read)
 800
 801    @property
 802    def variables_written(self) -> List["Variable"]:
 803        """
 804        list(Variable): Variables written (local/state/solidity)
 805        """
 806        return list(self._vars_written)
 807
 808    @property
 809    def state_variables_read(self) -> List["StateVariable"]:
 810        """
 811        list(StateVariable): State variables read
 812        """
 813        return list(self._state_vars_read)
 814
 815    @property
 816    def solidity_variables_read(self) -> List["SolidityVariable"]:
 817        """
 818        list(SolidityVariable): Solidity variables read
 819        """
 820        return list(self._solidity_vars_read)
 821
 822    @property
 823    def state_variables_written(self) -> List["StateVariable"]:
 824        """
 825        list(StateVariable): State variables written
 826        """
 827        return list(self._state_vars_written)
 828
 829    @property
 830    def variables_read_or_written(self) -> List["Variable"]:
 831        """
 832        list(Variable): Variables read or written (local/state/solidity)
 833        """
 834        return list(self._vars_read_or_written)
 835
 836    @property
 837    def variables_read_as_expression(self) -> List["Expression"]:
 838        return self._expression_vars_read
 839
 840    @property
 841    def variables_written_as_expression(self) -> List["Expression"]:
 842        return self._expression_vars_written
 843
 844    @property
 845    def slithir_variables(self) -> List["SlithIRVariable"]:
 846        """
 847        Temporary and Reference Variables (not SSA form)
 848        """
 849
 850        return list(self._slithir_variables)
 851
 852    # endregion
 853    ###################################################################################
 854    ###################################################################################
 855    # region Calls
 856    ###################################################################################
 857    ###################################################################################
 858
 859    @property
 860    def internal_calls(self) -> List["InternalCallType"]:
 861        """
 862        list(Function or SolidityFunction): List of function calls (that does not create a transaction)
 863        """
 864        return list(self._internal_calls)
 865
 866    @property
 867    def solidity_calls(self) -> List[SolidityFunction]:
 868        """
 869        list(SolidityFunction): List of Soldity calls
 870        """
 871        return list(self._solidity_calls)
 872
 873    @property
 874    def high_level_calls(self) -> List["HighLevelCallType"]:
 875        """
 876        list((Contract, Function|Variable)):
 877        List of high level calls (external calls).
 878        A variable is called in case of call to a public state variable
 879        Include library calls
 880        """
 881        return list(self._high_level_calls)
 882
 883    @property
 884    def library_calls(self) -> List["LibraryCallType"]:
 885        """
 886        list((Contract, Function)):
 887        """
 888        return list(self._library_calls)
 889
 890    @property
 891    def low_level_calls(self) -> List["LowLevelCallType"]:
 892        """
 893        list((Variable|SolidityVariable, str)): List of low_level call
 894        A low level call is defined by
 895        - the variable called
 896        - the name of the function (call/delegatecall/codecall)
 897        """
 898        return list(self._low_level_calls)
 899
 900    @property
 901    def external_calls_as_expressions(self) -> List["Expression"]:
 902        """
 903        list(ExpressionCall): List of message calls (that creates a transaction)
 904        """
 905        return list(self._external_calls_as_expressions)
 906
 907    # endregion
 908    ###################################################################################
 909    ###################################################################################
 910    # region Expressions
 911    ###################################################################################
 912    ###################################################################################
 913
 914    @property
 915    def calls_as_expressions(self) -> List["Expression"]:
 916        return self._expression_calls
 917
 918    @property
 919    def expressions(self) -> List["Expression"]:
 920        """
 921        list(Expression): List of the expressions
 922        """
 923        if self._expressions is None:
 924            expressionss = [n.expression for n in self.nodes]
 925            expressions = [e for e in expressionss if e]
 926            self._expressions = expressions
 927        return self._expressions
 928
 929    @property
 930    def return_values(self) -> List["SlithIRVariable"]:
 931        """
 932        list(Return Values): List of the return values
 933        """
 934        from slither.core.cfg.node import NodeType
 935        from slither.slithir.operations import Return
 936        from slither.slithir.variables import Constant
 937
 938        if self._return_values is None:
 939            return_values = []
 940            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 941            [  # pylint: disable=expression-not-assigned
 942                return_values.extend(ir.values)
 943                for node in returns
 944                for ir in node.irs
 945                if isinstance(ir, Return)
 946            ]
 947            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
 948        return self._return_values
 949
 950    @property
 951    def return_values_ssa(self) -> List["SlithIRVariable"]:
 952        """
 953        list(Return Values in SSA form): List of the return values in ssa form
 954        """
 955        from slither.core.cfg.node import NodeType
 956        from slither.slithir.operations import Return
 957        from slither.slithir.variables import Constant
 958
 959        if self._return_values_ssa is None:
 960            return_values_ssa = []
 961            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 962            [  # pylint: disable=expression-not-assigned
 963                return_values_ssa.extend(ir.values)
 964                for node in returns
 965                for ir in node.irs_ssa
 966                if isinstance(ir, Return)
 967            ]
 968            self._return_values_ssa = list(
 969                {x for x in return_values_ssa if not isinstance(x, Constant)}
 970            )
 971        return self._return_values_ssa
 972
 973    # endregion
 974    ###################################################################################
 975    ###################################################################################
 976    # region SlithIR
 977    ###################################################################################
 978    ###################################################################################
 979
 980    @property
 981    def slithir_operations(self) -> List["Operation"]:
 982        """
 983        list(Operation): List of the slithir operations
 984        """
 985        if self._slithir_operations is None:
 986            operationss = [n.irs for n in self.nodes]
 987            operations = [item for sublist in operationss for item in sublist if item]
 988            self._slithir_operations = operations
 989        return self._slithir_operations
 990
 991    @property
 992    def slithir_ssa_operations(self) -> List["Operation"]:
 993        """
 994        list(Operation): List of the slithir operations (SSA)
 995        """
 996        if self._slithir_ssa_operations is None:
 997            operationss = [n.irs_ssa for n in self.nodes]
 998            operations = [item for sublist in operationss for item in sublist if item]
 999            self._slithir_ssa_operations = operations
1000        return self._slithir_ssa_operations
1001
1002    # endregion
1003    ###################################################################################
1004    ###################################################################################
1005    # region Signature
1006    ###################################################################################
1007    ###################################################################################
1008
1009    @property
1010    def solidity_signature(self) -> str:
1011        """
1012        Return a signature following the Solidity Standard
1013        Contract and converted into address
1014
1015        It might still keep internal types (ex: structure name) for internal functions.
1016        The reason is that internal functions allows recursive structure definition, which
1017        can't be converted following the Solidity stand ard
1018
1019        :return: the solidity signature
1020        """
1021        if self._solidity_signature is None:
1022            parameters = [
1023                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1024            ]
1025            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1026        return self._solidity_signature
1027
1028    @property
1029    def signature(self) -> Tuple[str, List[str], List[str]]:
1030        """
1031        (str, list(str), list(str)): Function signature as
1032        (name, list parameters type, list return values type)
1033        """
1034        # FIXME memoizing this function is not working properly for vyper
1035        # if self._signature is None:
1036        return (
1037            self.name,
1038            [str(x.type) for x in self.parameters],
1039            [str(x.type) for x in self.returns],
1040        )
1041        #     self._signature = signature
1042        # return self._signature
1043
1044    @property
1045    def signature_str(self) -> str:
1046        """
1047        str: func_name(type1,type2) returns (type3)
1048        Return the function signature as a str (contains the return values)
1049        """
1050        if self._signature_str is None:
1051            name, parameters, returnVars = self.signature
1052            self._signature_str = (
1053                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1054            )
1055        return self._signature_str
1056
1057    # endregion
1058    ###################################################################################
1059    ###################################################################################
1060    # region Functions
1061    ###################################################################################
1062    ###################################################################################
1063
1064    @property
1065    @abstractmethod
1066    def functions_shadowed(self) -> List["Function"]:
1067        pass
1068
1069    # endregion
1070    ###################################################################################
1071    ###################################################################################
1072    # region Reachable
1073    ###################################################################################
1074    ###################################################################################
1075
1076    @property
1077    def reachable_from_nodes(self) -> Set[ReacheableNode]:
1078        """
1079        Return
1080            ReacheableNode
1081        """
1082        return self._reachable_from_nodes
1083
1084    @property
1085    def reachable_from_functions(self) -> Set["Function"]:
1086        return self._reachable_from_functions
1087
1088    @property
1089    def all_reachable_from_functions(self) -> Set["Function"]:
1090        """
1091        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1092        """
1093        if self._all_reachable_from_functions is None:
1094            functions: Set["Function"] = set()
1095
1096            new_functions = self.reachable_from_functions
1097            # iterate until we have are finding new functions
1098            while new_functions and not new_functions.issubset(functions):
1099                functions = functions.union(new_functions)
1100                # Use a temporary set, because we iterate over new_functions
1101                new_functionss: Set["Function"] = set()
1102                for f in new_functions:
1103                    new_functionss = new_functionss.union(f.reachable_from_functions)
1104                new_functions = new_functionss - functions
1105
1106            self._all_reachable_from_functions = functions
1107        return self._all_reachable_from_functions
1108
1109    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1110        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1111        self._reachable_from_functions.add(n.function)
1112
1113    # endregion
1114    ###################################################################################
1115    ###################################################################################
1116    # region Recursive getters
1117    ###################################################################################
1118    ###################################################################################
1119
1120    def _explore_functions(self, f_new_values: Callable[["Function"], List]) -> List[Any]:
1121        values = f_new_values(self)
1122        explored = [self]
1123        to_explore = [
1124            c for c in self.internal_calls if isinstance(c, Function) and c not in explored
1125        ]
1126        to_explore += [
1127            c for (_, c) in self.library_calls if isinstance(c, Function) and c not in explored
1128        ]
1129        to_explore += [m for m in self.modifiers if m not in explored]
1130
1131        while to_explore:
1132            f = to_explore[0]
1133            to_explore = to_explore[1:]
1134            if f in explored:
1135                continue
1136            explored.append(f)
1137
1138            values += f_new_values(f)
1139
1140            to_explore += [
1141                c
1142                for c in f.internal_calls
1143                if isinstance(c, Function) and c not in explored and c not in to_explore
1144            ]
1145            to_explore += [
1146                c
1147                for (_, c) in f.library_calls
1148                if isinstance(c, Function) and c not in explored and c not in to_explore
1149            ]
1150            to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
1151
1152        return list(set(values))
1153
1154    def all_variables_read(self) -> List["Variable"]:
1155        """recursive version of variables_read"""
1156        if self._all_variables_read is None:
1157            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1158        return self._all_variables_read
1159
1160    def all_variables_written(self) -> List["Variable"]:
1161        """recursive version of variables_written"""
1162        if self._all_variables_written is None:
1163            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1164        return self._all_variables_written
1165
1166    def all_state_variables_read(self) -> List["StateVariable"]:
1167        """recursive version of variables_read"""
1168        if self._all_state_variables_read is None:
1169            self._all_state_variables_read = self._explore_functions(
1170                lambda x: x.state_variables_read
1171            )
1172        return self._all_state_variables_read
1173
1174    def all_solidity_variables_read(self) -> List[SolidityVariable]:
1175        """recursive version of solidity_read"""
1176        if self._all_solidity_variables_read is None:
1177            self._all_solidity_variables_read = self._explore_functions(
1178                lambda x: x.solidity_variables_read
1179            )
1180        return self._all_solidity_variables_read
1181
1182    def all_slithir_variables(self) -> List["SlithIRVariable"]:
1183        """recursive version of slithir_variables"""
1184        if self._all_slithir_variables is None:
1185            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1186        return self._all_slithir_variables
1187
1188    def all_nodes(self) -> List["Node"]:
1189        """recursive version of nodes"""
1190        if self._all_nodes is None:
1191            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1192        return self._all_nodes
1193
1194    def all_expressions(self) -> List["Expression"]:
1195        """recursive version of variables_read"""
1196        if self._all_expressions is None:
1197            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1198        return self._all_expressions
1199
1200    def all_slithir_operations(self) -> List["Operation"]:
1201        if self._all_slithir_operations is None:
1202            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1203        return self._all_slithir_operations
1204
1205    def all_state_variables_written(self) -> List[StateVariable]:
1206        """recursive version of variables_written"""
1207        if self._all_state_variables_written is None:
1208            self._all_state_variables_written = self._explore_functions(
1209                lambda x: x.state_variables_written
1210            )
1211        return self._all_state_variables_written
1212
1213    def all_internal_calls(self) -> List["InternalCallType"]:
1214        """recursive version of internal_calls"""
1215        if self._all_internals_calls is None:
1216            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1217        return self._all_internals_calls
1218
1219    def all_low_level_calls(self) -> List["LowLevelCallType"]:
1220        """recursive version of low_level calls"""
1221        if self._all_low_level_calls is None:
1222            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1223        return self._all_low_level_calls
1224
1225    def all_high_level_calls(self) -> List["HighLevelCallType"]:
1226        """recursive version of high_level calls"""
1227        if self._all_high_level_calls is None:
1228            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1229        return self._all_high_level_calls
1230
1231    def all_library_calls(self) -> List["LibraryCallType"]:
1232        """recursive version of library calls"""
1233        if self._all_library_calls is None:
1234            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1235        return self._all_library_calls
1236
1237    def all_solidity_calls(self) -> List[SolidityFunction]:
1238        """recursive version of solidity calls"""
1239        if self._all_solidity_calls is None:
1240            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1241        return self._all_solidity_calls
1242
1243    @staticmethod
1244    def _explore_func_cond_read(func: "Function", include_loop: bool) -> List["StateVariable"]:
1245        ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]
1246        return [item for sublist in ret for item in sublist]
1247
1248    def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]:
1249        """
1250        Return the state variable used in a condition
1251
1252        Over approximate and also return index access
1253        It won't work if the variable is assigned to a temp variable
1254        """
1255        if include_loop:
1256            if self._all_conditional_state_variables_read_with_loop is None:
1257                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1258                    lambda x: self._explore_func_cond_read(x, include_loop)
1259                )
1260            return self._all_conditional_state_variables_read_with_loop
1261        if self._all_conditional_state_variables_read is None:
1262            self._all_conditional_state_variables_read = self._explore_functions(
1263                lambda x: self._explore_func_cond_read(x, include_loop)
1264            )
1265        return self._all_conditional_state_variables_read
1266
1267    @staticmethod
1268    def _solidity_variable_in_binary(node: "Node") -> List[SolidityVariable]:
1269        from slither.slithir.operations.binary import Binary
1270
1271        ret = []
1272        for ir in node.irs:
1273            if isinstance(ir, Binary):
1274                ret += ir.read
1275        return [var for var in ret if isinstance(var, SolidityVariable)]
1276
1277    @staticmethod
1278    def _explore_func_conditional(
1279        func: "Function",
1280        f: Callable[["Node"], List[SolidityVariable]],
1281        include_loop: bool,
1282    ) -> List[Any]:
1283        ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
1284        return [item for sublist in ret for item in sublist]
1285
1286    def all_conditional_solidity_variables_read(
1287        self, include_loop: bool = True
1288    ) -> List[SolidityVariable]:
1289        """
1290        Return the Soldiity variables directly used in a condtion
1291
1292        Use of the IR to filter index access
1293        Assumption: the solidity vars are used directly in the conditional node
1294        It won't work if the variable is assigned to a temp variable
1295        """
1296        if include_loop:
1297            if self._all_conditional_solidity_variables_read_with_loop is None:
1298                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1299                    lambda x: self._explore_func_conditional(
1300                        x, self._solidity_variable_in_binary, include_loop
1301                    )
1302                )
1303            return self._all_conditional_solidity_variables_read_with_loop
1304
1305        if self._all_conditional_solidity_variables_read is None:
1306            self._all_conditional_solidity_variables_read = self._explore_functions(
1307                lambda x: self._explore_func_conditional(
1308                    x, self._solidity_variable_in_binary, include_loop
1309                )
1310            )
1311        return self._all_conditional_solidity_variables_read
1312
1313    @staticmethod
1314    def _solidity_variable_in_internal_calls(node: "Node") -> List[SolidityVariable]:
1315        from slither.slithir.operations.internal_call import InternalCall
1316
1317        ret = []
1318        for ir in node.irs:
1319            if isinstance(ir, InternalCall):
1320                ret += ir.read
1321        return [var for var in ret if isinstance(var, SolidityVariable)]
1322
1323    @staticmethod
1324    def _explore_func_nodes(
1325        func: "Function", f: Callable[["Node"], List[SolidityVariable]]
1326    ) -> List[Union[Any, SolidityVariableComposed]]:
1327        ret = [f(n) for n in func.nodes]
1328        return [item for sublist in ret for item in sublist]
1329
1330    def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]:
1331        """
1332        Return the Soldiity variables directly used in a call
1333
1334        Use of the IR to filter index access
1335        Used to catch check(msg.sender)
1336        """
1337        if self._all_solidity_variables_used_as_args is None:
1338            self._all_solidity_variables_used_as_args = self._explore_functions(
1339                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1340            )
1341        return self._all_solidity_variables_used_as_args
1342
1343    # endregion
1344    ###################################################################################
1345    ###################################################################################
1346    # region Visitor
1347    ###################################################################################
1348    ###################################################################################
1349
1350    def apply_visitor(self, Visitor: Callable) -> List:
1351        """
1352            Apply a visitor to all the function expressions
1353        Args:
1354            Visitor: slither.visitors
1355        Returns
1356            list(): results of the visit
1357        """
1358        expressions = self.expressions
1359        v = [Visitor(e).result() for e in expressions]
1360        return [item for sublist in v for item in sublist]
1361
1362    # endregion
1363    ###################################################################################
1364    ###################################################################################
1365    # region Getters from/to object
1366    ###################################################################################
1367    ###################################################################################
1368
1369    def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]:
1370        """
1371            Return a local variable from a name
1372
1373        Args:
1374            variable_name (str): name of the variable
1375        Returns:
1376            LocalVariable
1377        """
1378        return next((v for v in self.variables if v.name == variable_name), None)
1379
1380    # endregion
1381    ###################################################################################
1382    ###################################################################################
1383    # region Export
1384    ###################################################################################
1385    ###################################################################################
1386
1387    def cfg_to_dot(self, filename: str):
1388        """
1389            Export the function to a dot file
1390        Args:
1391            filename (str)
1392        """
1393        with open(filename, "w", encoding="utf8") as f:
1394            f.write("digraph{\n")
1395            for node in self.nodes:
1396                f.write(f'{node.node_id}[label="{str(node)}"];\n')
1397                for son in node.sons:
1398                    f.write(f"{node.node_id}->{son.node_id};\n")
1399
1400            f.write("}\n")
1401
1402    def dominator_tree_to_dot(self, filename: str):
1403        """
1404            Export the dominator tree of the function to a dot file
1405        Args:
1406            filename (str)
1407        """
1408
1409        def description(node):
1410            desc = f"{node}\n"
1411            desc += f"id: {node.node_id}"
1412            if node.dominance_frontier:
1413                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1414            return desc
1415
1416        with open(filename, "w", encoding="utf8") as f:
1417            f.write("digraph{\n")
1418            for node in self.nodes:
1419                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1420                if node.immediate_dominator:
1421                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1422
1423            f.write("}\n")
1424
1425    def slithir_cfg_to_dot(self, filename: str):
1426        """
1427        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1428        :param filename:
1429        :return:
1430        """
1431        content = self.slithir_cfg_to_dot_str()
1432        with open(filename, "w", encoding="utf8") as f:
1433            f.write(content)
1434
1435    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1436        """
1437        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1438        :return: the DOT content
1439        :rtype: str
1440        """
1441        from slither.core.cfg.node import NodeType
1442
1443        content = ""
1444        content += "digraph{\n"
1445        for node in self.nodes:
1446            label = f"Node Type: {node.type.value} {node.node_id}\n"
1447            if node.expression and not skip_expressions:
1448                label += f"\nEXPRESSION:\n{node.expression}\n"
1449            if node.irs and not skip_expressions:
1450                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1451            content += f'{node.node_id}[label="{label}"];\n'
1452            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1453                true_node = node.son_true
1454                if true_node:
1455                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1456                false_node = node.son_false
1457                if false_node:
1458                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1459            else:
1460                for son in node.sons:
1461                    content += f"{node.node_id}->{son.node_id};\n"
1462
1463        content += "}\n"
1464        return content
1465
1466    # endregion
1467    ###################################################################################
1468    ###################################################################################
1469    # region Summary information
1470    ###################################################################################
1471    ###################################################################################
1472
1473    def is_reading(self, variable: "Variable") -> bool:
1474        """
1475            Check if the function reads the variable
1476        Args:
1477            variable (Variable):
1478        Returns:
1479            bool: True if the variable is read
1480        """
1481        return variable in self.variables_read
1482
1483    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1484        """
1485            Check if the function reads the variable in a IF node
1486        Args:
1487            variable (Variable):
1488        Returns:
1489            bool: True if the variable is read
1490        """
1491        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1492        variables_read = [item for sublist in variables_reads for item in sublist]
1493        return variable in variables_read
1494
1495    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1496        """
1497            Check if the function reads the variable in an require or assert
1498        Args:
1499            variable (Variable):
1500        Returns:
1501            bool: True if the variable is read
1502        """
1503        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1504        variables_read = [item for sublist in variables_reads for item in sublist]
1505        return variable in variables_read
1506
1507    def is_writing(self, variable: "Variable") -> bool:
1508        """
1509            Check if the function writes the variable
1510        Args:
1511            variable (Variable):
1512        Returns:
1513            bool: True if the variable is written
1514        """
1515        return variable in self.variables_written
1516
1517    @abstractmethod
1518    def get_summary(
1519        self,
1520    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1521        pass
1522
1523    def is_protected(self) -> bool:
1524        """
1525            Determine if the function is protected using a check on msg.sender
1526
1527            Consider onlyOwner as a safe modifier.
1528            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1529            classify the function as protected
1530
1531            Otherwise only detects if msg.sender is directly used in a condition
1532            For example, it wont work for:
1533                address a = msg.sender
1534                require(a == owner)
1535        Returns
1536            (bool)
1537        """
1538
1539        if self._is_protected is None:
1540            if self.is_constructor:
1541                self._is_protected = True
1542                return True
1543            if "onlyOwner" in [m.name for m in self.modifiers]:
1544                self._is_protected = True
1545                return True
1546            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1547            args_vars = self.all_solidity_variables_used_as_args()
1548            self._is_protected = (
1549                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1550            )
1551        return self._is_protected
1552
1553    @property
1554    def is_reentrant(self) -> bool:
1555        """
1556        Determine if the function can be re-entered
1557        """
1558        reentrancy_modifier = "nonReentrant"
1559
1560        if self.function_language == FunctionLanguage.Vyper:
1561            reentrancy_modifier = "nonreentrant(lock)"
1562
1563        # TODO: compare with hash of known nonReentrant modifier instead of the name
1564        if reentrancy_modifier in [m.name for m in self.modifiers]:
1565            return False
1566
1567        if self.visibility in ["public", "external"]:
1568            return True
1569
1570        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1571        all_entry_points = [
1572            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1573        ]
1574        if not all_entry_points:
1575            return True
1576        return not all(
1577            (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points)
1578        )
1579
1580    # endregion
1581    ###################################################################################
1582    ###################################################################################
1583    # region Analyses
1584    ###################################################################################
1585    ###################################################################################
1586
1587    def _analyze_read_write(self) -> None:
1588        """Compute variables read/written/..."""
1589        write_var = [x.variables_written_as_expression for x in self.nodes]
1590        write_var = [x for x in write_var if x]
1591        write_var = [item for sublist in write_var for item in sublist]
1592        write_var = list(set(write_var))
1593        # Remove duplicate if they share the same string representation
1594        write_var = [
1595            next(obj)
1596            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1597        ]
1598        self._expression_vars_written = write_var
1599
1600        write_var = [x.variables_written for x in self.nodes]
1601        write_var = [x for x in write_var if x]
1602        write_var = [item for sublist in write_var for item in sublist]
1603        write_var = list(set(write_var))
1604        # Remove duplicate if they share the same string representation
1605        write_var = [
1606            next(obj)
1607            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1608        ]
1609        self._vars_written = write_var
1610
1611        read_var = [x.variables_read_as_expression for x in self.nodes]
1612        read_var = [x for x in read_var if x]
1613        read_var = [item for sublist in read_var for item in sublist]
1614        # Remove duplicate if they share the same string representation
1615        read_var = [
1616            next(obj)
1617            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1618        ]
1619        self._expression_vars_read = read_var
1620
1621        read_var = [x.variables_read for x in self.nodes]
1622        read_var = [x for x in read_var if x]
1623        read_var = [item for sublist in read_var for item in sublist]
1624        # Remove duplicate if they share the same string representation
1625        read_var = [
1626            next(obj)
1627            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1628        ]
1629        self._vars_read = read_var
1630
1631        self._state_vars_written = [
1632            x for x in self.variables_written if isinstance(x, StateVariable)
1633        ]
1634        self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)]
1635        self._solidity_vars_read = [
1636            x for x in self.variables_read if isinstance(x, SolidityVariable)
1637        ]
1638
1639        self._vars_read_or_written = self._vars_written + self._vars_read
1640
1641        slithir_variables = [x.slithir_variables for x in self.nodes]
1642        slithir_variables = [x for x in slithir_variables if x]
1643        self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
1644
1645    def _analyze_calls(self) -> None:
1646        calls = [x.calls_as_expression for x in self.nodes]
1647        calls = [x for x in calls if x]
1648        calls = [item for sublist in calls for item in sublist]
1649        self._expression_calls = list(set(calls))
1650
1651        internal_calls = [x.internal_calls for x in self.nodes]
1652        internal_calls = [x for x in internal_calls if x]
1653        internal_calls = [item for sublist in internal_calls for item in sublist]
1654        self._internal_calls = list(set(internal_calls))
1655
1656        self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)]
1657
1658        low_level_calls = [x.low_level_calls for x in self.nodes]
1659        low_level_calls = [x for x in low_level_calls if x]
1660        low_level_calls = [item for sublist in low_level_calls for item in sublist]
1661        self._low_level_calls = list(set(low_level_calls))
1662
1663        high_level_calls = [x.high_level_calls for x in self.nodes]
1664        high_level_calls = [x for x in high_level_calls if x]
1665        high_level_calls = [item for sublist in high_level_calls for item in sublist]
1666        self._high_level_calls = list(set(high_level_calls))
1667
1668        library_calls = [x.library_calls for x in self.nodes]
1669        library_calls = [x for x in library_calls if x]
1670        library_calls = [item for sublist in library_calls for item in sublist]
1671        self._library_calls = list(set(library_calls))
1672
1673        external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
1674        external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
1675        external_calls_as_expressions = [
1676            item for sublist in external_calls_as_expressions for item in sublist
1677        ]
1678        self._external_calls_as_expressions = list(set(external_calls_as_expressions))
1679
1680    # endregion
1681    ###################################################################################
1682    ###################################################################################
1683    # region Nodes
1684    ###################################################################################
1685    ###################################################################################
1686
1687    def new_node(
1688        self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]
1689    ) -> "Node":
1690        from slither.core.cfg.node import Node
1691
1692        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1693        node.set_offset(src, self.compilation_unit)
1694        self._counter_nodes += 1
1695        node.set_function(self)
1696        self._nodes.append(node)
1697
1698        return node
1699
1700    # endregion
1701    ###################################################################################
1702    ###################################################################################
1703    # region SlithIr and SSA
1704    ###################################################################################
1705    ###################################################################################
1706
1707    def _get_last_ssa_variable_instances(
1708        self, target_state: bool, target_local: bool
1709    ) -> Dict[str, Set["SlithIRVariable"]]:
1710        # pylint: disable=too-many-locals,too-many-branches
1711        from slither.slithir.variables import ReferenceVariable
1712        from slither.slithir.operations import OperationWithLValue
1713        from slither.core.cfg.node import NodeType
1714
1715        if not self.is_implemented:
1716            return {}
1717
1718        if self._entry_point is None:
1719            return {}
1720        # node, values
1721        to_explore: List[Tuple["Node", Dict]] = [(self._entry_point, {})]
1722        # node -> values
1723        explored: Dict = {}
1724        # name -> instances
1725        ret: Dict = {}
1726
1727        while to_explore:
1728            node, values = to_explore[0]
1729            to_explore = to_explore[1::]
1730
1731            if node.type != NodeType.ENTRYPOINT:
1732                for ir_ssa in node.irs_ssa:
1733                    if isinstance(ir_ssa, OperationWithLValue):
1734                        lvalue = ir_ssa.lvalue
1735                        if isinstance(lvalue, ReferenceVariable):
1736                            lvalue = lvalue.points_to_origin
1737                        if isinstance(lvalue, StateVariable) and target_state:
1738                            values[lvalue.canonical_name] = {lvalue}
1739                        if isinstance(lvalue, LocalVariable) and target_local:
1740                            values[lvalue.canonical_name] = {lvalue}
1741
1742            # Check for fixpoint
1743            if node in explored:
1744                if values == explored[node]:
1745                    continue
1746                for k, instances in values.items():
1747                    if k not in explored[node]:
1748                        explored[node][k] = set()
1749                    explored[node][k] |= instances
1750                values = explored[node]
1751            else:
1752                explored[node] = values
1753
1754            # Return condition
1755            if node.will_return:
1756                for name, instances in values.items():
1757                    if name not in ret:
1758                        ret[name] = set()
1759                    ret[name] |= instances
1760
1761            for son in node.sons:
1762                to_explore.append((son, dict(values)))
1763
1764        return ret
1765
1766    def get_last_ssa_state_variables_instances(
1767        self,
1768    ) -> Dict[str, Set["SlithIRVariable"]]:
1769        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
1770
1771    def get_last_ssa_local_variables_instances(
1772        self,
1773    ) -> Dict[str, Set["SlithIRVariable"]]:
1774        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
1775
1776    @staticmethod
1777    def _unchange_phi(ir: "Operation") -> bool:
1778        from slither.slithir.operations import Phi, PhiCallback
1779
1780        if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
1781            return False
1782        if not ir.rvalues:
1783            return True
1784        return ir.rvalues[0] == ir.lvalue
1785
1786    def fix_phi(
1787        self,
1788        last_state_variables_instances: Dict[str, List["StateVariable"]],
1789        initial_state_variables_instances: Dict[str, "StateVariable"],
1790    ) -> None:
1791        from slither.slithir.operations import InternalCall, PhiCallback
1792        from slither.slithir.variables import Constant, StateIRVariable
1793
1794        for node in self.nodes:
1795            for ir in node.irs_ssa:
1796                if node == self.entry_point:
1797                    if isinstance(ir.lvalue, StateIRVariable):
1798                        additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1799                        additional += last_state_variables_instances[ir.lvalue.canonical_name]
1800                        ir.rvalues = list(set(additional + ir.rvalues))
1801                    # function parameter
1802                    else:
1803                        # find index of the parameter
1804                        idx = self.parameters.index(ir.lvalue.non_ssa_version)
1805                        # find non ssa version of that index
1806                        additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1807                        additional = unroll(additional)
1808                        additional = [a for a in additional if not isinstance(a, Constant)]
1809                        ir.rvalues = list(set(additional + ir.rvalues))
1810                if isinstance(ir, PhiCallback):
1811                    callee_ir = ir.callee_ir
1812                    if isinstance(callee_ir, InternalCall):
1813                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1814                        if ir.lvalue.canonical_name in last_ssa:
1815                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1816                        else:
1817                            ir.rvalues = [ir.lvalue]
1818                    else:
1819                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1820                        ir.rvalues = list(set(additional + ir.rvalues))
1821
1822            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
1823
1824    def generate_slithir_and_analyze(self) -> None:
1825
1826        for node in self.nodes:
1827            node.slithir_generation()
1828
1829        self._analyze_read_write()
1830        self._analyze_calls()
1831
1832    @abstractmethod
1833    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1834        pass
1835
1836    def update_read_write_using_ssa(self) -> None:
1837        for node in self.nodes:
1838            node.update_read_write_using_ssa()
1839        self._analyze_read_write()
1840
1841    ###################################################################################
1842    ###################################################################################
1843    # region Built in definitions
1844    ###################################################################################
1845    ###################################################################################
1846
1847    def __str__(self) -> str:
1848        return self.name
1849
1850    # endregion
LOGGER = <Logger Function (WARNING)>
class ReacheableNode(builtins.tuple):

ReacheableNode(node, ir)

ReacheableNode(node, ir)

Create new instance of ReacheableNode(node, ir)

node

Alias for field number 0

ir

Alias for field number 1

Inherited Members
builtins.tuple
index
count
class ModifierStatements:
55class ModifierStatements:
56    def __init__(
57        self,
58        modifier: Union["Contract", "Function"],
59        entry_point: "Node",
60        nodes: List["Node"],
61    ) -> None:
62        self._modifier = modifier
63        self._entry_point = entry_point
64        self._nodes = nodes
65
66    @property
67    def modifier(self) -> Union["Contract", "Function"]:
68        return self._modifier
69
70    @property
71    def entry_point(self) -> "Node":
72        return self._entry_point
73
74    @entry_point.setter
75    def entry_point(self, entry_point: "Node"):
76        self._entry_point = entry_point
77
78    @property
79    def nodes(self) -> List["Node"]:
80        return self._nodes
81
82    @nodes.setter
83    def nodes(self, nodes: List["Node"]):
84        self._nodes = nodes
ModifierStatements( modifier: Union[slither.core.declarations.contract.Contract, Function], entry_point: slither.core.cfg.node.Node, nodes: list[slither.core.cfg.node.Node])
56    def __init__(
57        self,
58        modifier: Union["Contract", "Function"],
59        entry_point: "Node",
60        nodes: List["Node"],
61    ) -> None:
62        self._modifier = modifier
63        self._entry_point = entry_point
64        self._nodes = nodes
66    @property
67    def modifier(self) -> Union["Contract", "Function"]:
68        return self._modifier
entry_point: slither.core.cfg.node.Node
70    @property
71    def entry_point(self) -> "Node":
72        return self._entry_point
nodes: list[slither.core.cfg.node.Node]
78    @property
79    def nodes(self) -> List["Node"]:
80        return self._nodes
class FunctionType(enum.Enum):
87class FunctionType(Enum):
88    NORMAL = 0
89    CONSTRUCTOR = 1
90    FALLBACK = 2
91    RECEIVE = 3
92    CONSTRUCTOR_VARIABLES = 10  # Fake function to hold variable declaration statements
93    CONSTRUCTOR_CONSTANT_VARIABLES = 11  # Fake function to hold variable declaration statements

An enumeration.

NORMAL = <FunctionType.NORMAL: 0>
CONSTRUCTOR = <FunctionType.CONSTRUCTOR: 1>
FALLBACK = <FunctionType.FALLBACK: 2>
RECEIVE = <FunctionType.RECEIVE: 3>
CONSTRUCTOR_VARIABLES = <FunctionType.CONSTRUCTOR_VARIABLES: 10>
CONSTRUCTOR_CONSTANT_VARIABLES = <FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: 11>
Inherited Members
enum.Enum
name
value
class FunctionLanguage(enum.Enum):
107class FunctionLanguage(Enum):
108    Solidity = 0
109    Yul = 1
110    Vyper = 2

An enumeration.

Solidity = <FunctionLanguage.Solidity: 0>
Vyper = <FunctionLanguage.Vyper: 2>
Inherited Members
enum.Enum
name
value
 113class Function(SourceMapping, metaclass=ABCMeta):  # pylint: disable=too-many-public-methods
 114    """
 115    Function class
 116    """
 117
 118    def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
 119        super().__init__()
 120        self._internal_scope: List[str] = []
 121        self._name: Optional[str] = None
 122        self._view: bool = False
 123        self._pure: bool = False
 124        self._payable: bool = False
 125        self._visibility: Optional[str] = None
 126        self._virtual: bool = False
 127        self._overrides: List["FunctionContract"] = []
 128        self._overridden_by: List["FunctionContract"] = []
 129
 130        self._is_implemented: Optional[bool] = None
 131        self._is_empty: Optional[bool] = None
 132        self._entry_point: Optional["Node"] = None
 133        self._nodes: List["Node"] = []
 134        self._variables: Dict[str, "LocalVariable"] = {}
 135        # slithir Temporary and references variables (but not SSA)
 136        self._slithir_variables: Set["SlithIRVariable"] = set()
 137        self._parameters: List["LocalVariable"] = []
 138        self._parameters_ssa: List["LocalIRVariable"] = []
 139        self._parameters_src: SourceMapping = SourceMapping()
 140        # This is used for vyper calls with default arguments
 141        self._default_args_as_expressions: List["Expression"] = []
 142        self._returns: List["LocalVariable"] = []
 143        self._returns_ssa: List["LocalIRVariable"] = []
 144        self._returns_src: SourceMapping = SourceMapping()
 145        self._return_values: Optional[List["SlithIRVariable"]] = None
 146        self._return_values_ssa: Optional[List["SlithIRVariable"]] = None
 147        self._vars_read: List["Variable"] = []
 148        self._vars_written: List["Variable"] = []
 149        self._state_vars_read: List["StateVariable"] = []
 150        self._vars_read_or_written: List["Variable"] = []
 151        self._solidity_vars_read: List["SolidityVariable"] = []
 152        self._state_vars_written: List["StateVariable"] = []
 153        self._internal_calls: List["InternalCallType"] = []
 154        self._solidity_calls: List["SolidityFunction"] = []
 155        self._low_level_calls: List["LowLevelCallType"] = []
 156        self._high_level_calls: List["HighLevelCallType"] = []
 157        self._library_calls: List["LibraryCallType"] = []
 158        self._external_calls_as_expressions: List["Expression"] = []
 159        self._expression_vars_read: List["Expression"] = []
 160        self._expression_vars_written: List["Expression"] = []
 161        self._expression_calls: List["Expression"] = []
 162        # self._expression_modifiers: List["Expression"] = []
 163        self._modifiers: List[ModifierStatements] = []
 164        self._explicit_base_constructor_calls: List[ModifierStatements] = []
 165        self._contains_assembly: bool = False
 166
 167        self._expressions: Optional[List["Expression"]] = None
 168        self._slithir_operations: Optional[List["Operation"]] = None
 169        self._slithir_ssa_operations: Optional[List["Operation"]] = None
 170
 171        self._all_expressions: Optional[List["Expression"]] = None
 172        self._all_slithir_operations: Optional[List["Operation"]] = None
 173        self._all_internals_calls: Optional[List["InternalCallType"]] = None
 174        self._all_high_level_calls: Optional[List["HighLevelCallType"]] = None
 175        self._all_library_calls: Optional[List["LibraryCallType"]] = None
 176        self._all_low_level_calls: Optional[List["LowLevelCallType"]] = None
 177        self._all_solidity_calls: Optional[List["SolidityFunction"]] = None
 178        self._all_variables_read: Optional[List["Variable"]] = None
 179        self._all_variables_written: Optional[List["Variable"]] = None
 180        self._all_state_variables_read: Optional[List["StateVariable"]] = None
 181        self._all_solidity_variables_read: Optional[List["SolidityVariable"]] = None
 182        self._all_state_variables_written: Optional[List["StateVariable"]] = None
 183        self._all_slithir_variables: Optional[List["SlithIRVariable"]] = None
 184        self._all_nodes: Optional[List["Node"]] = None
 185        self._all_conditional_state_variables_read: Optional[List["StateVariable"]] = None
 186        self._all_conditional_state_variables_read_with_loop: Optional[List["StateVariable"]] = None
 187        self._all_conditional_solidity_variables_read: Optional[List["SolidityVariable"]] = None
 188        self._all_conditional_solidity_variables_read_with_loop: Optional[
 189            List["SolidityVariable"]
 190        ] = None
 191        self._all_solidity_variables_used_as_args: Optional[List["SolidityVariable"]] = None
 192
 193        self._is_shadowed: bool = False
 194        self._shadows: bool = False
 195
 196        # set(ReacheableNode)
 197        self._reachable_from_nodes: Set[ReacheableNode] = set()
 198        self._reachable_from_functions: Set[Function] = set()
 199        self._all_reachable_from_functions: Optional[Set[Function]] = None
 200
 201        # Constructor, fallback, State variable constructor
 202        self._function_type: Optional[FunctionType] = None
 203        self._is_constructor: Optional[bool] = None
 204
 205        # Computed on the fly, can be True of False
 206        self._can_reenter: Optional[bool] = None
 207        self._can_send_eth: Optional[bool] = None
 208
 209        self._nodes_ordered_dominators: Optional[List["Node"]] = None
 210
 211        self._counter_nodes = 0
 212
 213        # Memoize parameters:
 214        # TODO: identify all the memoize parameters and add a way to undo the memoization
 215        self._full_name: Optional[str] = None
 216        self._signature: Optional[Tuple[str, List[str], List[str]]] = None
 217        self._solidity_signature: Optional[str] = None
 218        self._signature_str: Optional[str] = None
 219        self._canonical_name: Optional[str] = None
 220        self._is_protected: Optional[bool] = None
 221
 222        self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
 223
 224        self.function_language: FunctionLanguage = (
 225            FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
 226        )
 227
 228        self._id: Optional[str] = None
 229
 230        # To be improved with a parsing of the documentation
 231        self.has_documentation: bool = False
 232
 233    ###################################################################################
 234    ###################################################################################
 235    # region General properties
 236    ###################################################################################
 237    ###################################################################################
 238
 239    @property
 240    def name(self) -> str:
 241        """
 242        str: function name
 243        """
 244        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
 245            return "constructor"
 246        if self._name == "" and self._function_type == FunctionType.FALLBACK:
 247            return "fallback"
 248        if self._function_type == FunctionType.RECEIVE:
 249            return "receive"
 250        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
 251            return "slitherConstructorVariables"
 252        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
 253            return "slitherConstructorConstantVariables"
 254        return self._name
 255
 256    @name.setter
 257    def name(self, new_name: str):
 258        self._name = new_name
 259
 260    @property
 261    def internal_scope(self) -> List[str]:
 262        """
 263        Return a list of name representing the scope of the function
 264        This is used to model nested functions declared in YUL
 265
 266        :return:
 267        """
 268        return self._internal_scope
 269
 270    @internal_scope.setter
 271    def internal_scope(self, new_scope: List[str]):
 272        self._internal_scope = new_scope
 273
 274    @property
 275    def full_name(self) -> str:
 276        """
 277        str: func_name(type1,type2)
 278        Return the function signature without the return values
 279        The difference between this function and solidity_function is that full_name does not translate the underlying
 280        type (ex: structure, contract to address, ...)
 281        """
 282        if self._full_name is None:
 283            name, parameters, _ = self.signature
 284            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
 285            self._full_name = full_name
 286        return self._full_name
 287
 288    @property
 289    @abstractmethod
 290    def canonical_name(self) -> str:
 291        """
 292        str: contract.func_name(type1,type2)
 293        Return the function signature without the return values
 294        """
 295        return ""
 296
 297    @property
 298    def contains_assembly(self) -> bool:
 299        return self._contains_assembly
 300
 301    @contains_assembly.setter
 302    def contains_assembly(self, c: bool):
 303        self._contains_assembly = c
 304
 305    def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool:
 306        """
 307        Check if the function can re-enter
 308        Follow internal calls.
 309        Do not consider CREATE as potential re-enter, but check if the
 310        destination's constructor can contain a call (recurs. follow nested CREATE)
 311        For Solidity > 0.5, filter access to public variables and constant/pure/view
 312        For call to this. check if the destination can re-enter
 313        Do not consider Send/Transfer as there is not enough gas
 314        :param callstack: used internally to check for recursion
 315        :return bool:
 316        """
 317        from slither.slithir.operations import Call
 318
 319        if self._can_reenter is None:
 320            self._can_reenter = False
 321            for ir in self.all_slithir_operations():
 322                if isinstance(ir, Call) and ir.can_reenter(callstack):
 323                    self._can_reenter = True
 324                    return True
 325        return self._can_reenter
 326
 327    def can_send_eth(self) -> bool:
 328        """
 329        Check if the function or any internal (not external) functions called by it can send eth
 330        :return bool:
 331        """
 332        from slither.slithir.operations import Call
 333
 334        if self._can_send_eth is None:
 335            self._can_send_eth = False
 336            for ir in self.all_slithir_operations():
 337                if isinstance(ir, Call) and ir.can_send_eth():
 338                    self._can_send_eth = True
 339                    return True
 340        return self._can_send_eth
 341
 342    @property
 343    def is_checked(self) -> bool:
 344        """
 345        Return true if the overflow are enabled by default
 346
 347
 348        :return:
 349        """
 350
 351        return self.compilation_unit.solc_version >= "0.8.0"
 352
 353    @property
 354    def id(self) -> Optional[str]:
 355        """
 356        Return the reference ID of the function, if available.
 357
 358        :return:
 359        :rtype:
 360        """
 361        return self._id
 362
 363    @id.setter
 364    def id(self, new_id: str):
 365        self._id = new_id
 366
 367    @property
 368    @abstractmethod
 369    def file_scope(self) -> "FileScope":
 370        pass
 371
 372    # endregion
 373    ###################################################################################
 374    ###################################################################################
 375    # region Type (FunctionType)
 376    ###################################################################################
 377    ###################################################################################
 378
 379    def set_function_type(self, t: FunctionType) -> None:
 380        assert isinstance(t, FunctionType)
 381        self._function_type = t
 382
 383    @property
 384    def function_type(self) -> Optional[FunctionType]:
 385        return self._function_type
 386
 387    @function_type.setter
 388    def function_type(self, t: FunctionType):
 389        self._function_type = t
 390
 391    @property
 392    def is_constructor(self) -> bool:
 393        """
 394        bool: True if the function is the constructor
 395        """
 396        return self._function_type == FunctionType.CONSTRUCTOR
 397
 398    @property
 399    def is_constructor_variables(self) -> bool:
 400        """
 401        bool: True if the function is the constructor of the variables
 402        Slither has inbuilt functions to hold the state variables initialization
 403        """
 404        return self._function_type in [
 405            FunctionType.CONSTRUCTOR_VARIABLES,
 406            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
 407        ]
 408
 409    @property
 410    def is_fallback(self) -> bool:
 411        """
 412            Determine if the function is the fallback function for the contract
 413        Returns
 414            (bool)
 415        """
 416        return self._function_type == FunctionType.FALLBACK
 417
 418    @property
 419    def is_receive(self) -> bool:
 420        """
 421            Determine if the function is the receive function for the contract
 422        Returns
 423            (bool)
 424        """
 425        return self._function_type == FunctionType.RECEIVE
 426
 427    # endregion
 428    ###################################################################################
 429    ###################################################################################
 430    # region Payable
 431    ###################################################################################
 432    ###################################################################################
 433
 434    @property
 435    def payable(self) -> bool:
 436        """
 437        bool: True if the function is payable
 438        """
 439        return self._payable
 440
 441    @payable.setter
 442    def payable(self, p: bool):
 443        self._payable = p
 444
 445    # endregion
 446    ###################################################################################
 447    ###################################################################################
 448    # region Virtual
 449    ###################################################################################
 450    ###################################################################################
 451
 452    @property
 453    def is_virtual(self) -> bool:
 454        """
 455        Note for Solidity < 0.6.0 it will always be false
 456        bool: True if the function is virtual
 457        """
 458        return self._virtual
 459
 460    @is_virtual.setter
 461    def is_virtual(self, v: bool):
 462        self._virtual = v
 463
 464    @property
 465    def is_override(self) -> bool:
 466        """
 467        Note for Solidity < 0.6.0 it will always be false
 468        bool: True if the function overrides a base function
 469        """
 470        return len(self._overrides) > 0
 471
 472    @property
 473    def overridden_by(self) -> List["FunctionContract"]:
 474        """
 475        List["FunctionContract"]: List of functions in child contracts that override this function
 476        This may include distinct instances of the same function due to inheritance
 477        """
 478        return self._overridden_by
 479
 480    @property
 481    def overrides(self) -> List["FunctionContract"]:
 482        """
 483        List["FunctionContract"]: List of functions in parent contracts that this function overrides
 484        This may include distinct instances of the same function due to inheritance
 485        """
 486        return self._overrides
 487
 488    # endregion
 489    ###################################################################################
 490    ###################################################################################
 491    # region Visibility
 492    ###################################################################################
 493    ###################################################################################
 494
 495    @property
 496    def visibility(self) -> str:
 497        """
 498        str: Function visibility
 499        """
 500        assert self._visibility is not None
 501        return self._visibility
 502
 503    @visibility.setter
 504    def visibility(self, v: str):
 505        self._visibility = v
 506
 507    def set_visibility(self, v: str) -> None:
 508        self._visibility = v
 509
 510    @property
 511    def view(self) -> bool:
 512        """
 513        bool: True if the function is declared as view
 514        """
 515        return self._view
 516
 517    @view.setter
 518    def view(self, v: bool):
 519        self._view = v
 520
 521    @property
 522    def pure(self) -> bool:
 523        """
 524        bool: True if the function is declared as pure
 525        """
 526        return self._pure
 527
 528    @pure.setter
 529    def pure(self, p: bool):
 530        self._pure = p
 531
 532    @property
 533    def is_shadowed(self) -> bool:
 534        return self._is_shadowed
 535
 536    @is_shadowed.setter
 537    def is_shadowed(self, is_shadowed):
 538        self._is_shadowed = is_shadowed
 539
 540    @property
 541    def shadows(self) -> bool:
 542        return self._shadows
 543
 544    @shadows.setter
 545    def shadows(self, _shadows: bool):
 546        self._shadows = _shadows
 547
 548    # endregion
 549    ###################################################################################
 550    ###################################################################################
 551    # region Function's body
 552    ###################################################################################
 553    ###################################################################################
 554
 555    @property
 556    def is_implemented(self) -> bool:
 557        """
 558        bool: True if the function is implemented
 559        """
 560        return self._is_implemented
 561
 562    @is_implemented.setter
 563    def is_implemented(self, is_impl: bool):
 564        self._is_implemented = is_impl
 565
 566    @property
 567    def is_empty(self) -> bool:
 568        """
 569        bool: True if the function is empty, None if the function is an interface
 570        """
 571        return self._is_empty
 572
 573    @is_empty.setter
 574    def is_empty(self, empty: bool):
 575        self._is_empty = empty
 576
 577    # endregion
 578    ###################################################################################
 579    ###################################################################################
 580    # region Nodes
 581    ###################################################################################
 582    ###################################################################################
 583
 584    @property
 585    def nodes(self) -> List["Node"]:
 586        """
 587        list(Node): List of the nodes
 588        """
 589        return list(self._nodes)
 590
 591    @nodes.setter
 592    def nodes(self, nodes: List["Node"]):
 593        self._nodes = nodes
 594
 595    @property
 596    def entry_point(self) -> Optional["Node"]:
 597        """
 598        Node: Entry point of the function
 599        """
 600        return self._entry_point
 601
 602    @entry_point.setter
 603    def entry_point(self, node: "Node"):
 604        self._entry_point = node
 605
 606    def add_node(self, node: "Node") -> None:
 607        if not self._entry_point:
 608            self._entry_point = node
 609        self._nodes.append(node)
 610
 611    @property
 612    def nodes_ordered_dominators(self) -> List["Node"]:
 613        # TODO: does not work properly; most likely due to modifier call
 614        # This will not work for modifier call that lead to multiple nodes
 615        # from slither.core.cfg.node import NodeType
 616        if self._nodes_ordered_dominators is None:
 617            self._nodes_ordered_dominators = []
 618            if self.entry_point:
 619                self._compute_nodes_ordered_dominators(self.entry_point)
 620
 621            for node in self.nodes:
 622                # if node.type == NodeType.OTHER_ENTRYPOINT:
 623                if not node in self._nodes_ordered_dominators:
 624                    self._compute_nodes_ordered_dominators(node)
 625
 626        return self._nodes_ordered_dominators
 627
 628    def _compute_nodes_ordered_dominators(self, node: "Node"):
 629        assert self._nodes_ordered_dominators is not None
 630        if node in self._nodes_ordered_dominators:
 631            return
 632        self._nodes_ordered_dominators.append(node)
 633        for dom in node.dominance_exploration_ordered:
 634            self._compute_nodes_ordered_dominators(dom)
 635
 636    # endregion
 637    ###################################################################################
 638    ###################################################################################
 639    # region Parameters
 640    ###################################################################################
 641    ###################################################################################
 642
 643    @property
 644    def parameters(self) -> List["LocalVariable"]:
 645        """
 646        list(LocalVariable): List of the parameters
 647        """
 648        return list(self._parameters)
 649
 650    def add_parameters(self, p: "LocalVariable") -> None:
 651        self._parameters.append(p)
 652
 653    @property
 654    def parameters_ssa(self) -> List["LocalIRVariable"]:
 655        """
 656        list(LocalIRVariable): List of the parameters (SSA form)
 657        """
 658        return list(self._parameters_ssa)
 659
 660    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
 661        self._parameters_ssa.append(var)
 662
 663    def parameters_src(self) -> SourceMapping:
 664        return self._parameters_src
 665
 666    # endregion
 667    ###################################################################################
 668    ###################################################################################
 669    # region Return values
 670    ###################################################################################
 671    ###################################################################################
 672
 673    @property
 674    def return_type(self) -> Optional[List[Type]]:
 675        """
 676        Return the list of return type
 677        If no return, return None
 678        """
 679        returns = self.returns
 680        if returns:
 681            return [r.type for r in returns]
 682        return None
 683
 684    def returns_src(self) -> SourceMapping:
 685        return self._returns_src
 686
 687    @property
 688    def type(self) -> Optional[List[Type]]:
 689        """
 690        Return the list of return type
 691        If no return, return None
 692        Alias of return_type
 693        """
 694        return self.return_type
 695
 696    @property
 697    def returns(self) -> List["LocalVariable"]:
 698        """
 699        list(LocalVariable): List of the return variables
 700        """
 701        return list(self._returns)
 702
 703    def add_return(self, r: "LocalVariable") -> None:
 704        self._returns.append(r)
 705
 706    @property
 707    def returns_ssa(self) -> List["LocalIRVariable"]:
 708        """
 709        list(LocalIRVariable): List of the return variables (SSA form)
 710        """
 711        return list(self._returns_ssa)
 712
 713    def add_return_ssa(self, var: "LocalIRVariable") -> None:
 714        self._returns_ssa.append(var)
 715
 716    # endregion
 717    ###################################################################################
 718    ###################################################################################
 719    # region Modifiers
 720    ###################################################################################
 721    ###################################################################################
 722
 723    @property
 724    def modifiers(self) -> List[Union["Contract", "Function"]]:
 725        """
 726        list(Modifier): List of the modifiers
 727        Can be contract for constructor's calls
 728
 729        """
 730        return [c.modifier for c in self._modifiers]
 731
 732    def add_modifier(self, modif: "ModifierStatements") -> None:
 733        self._modifiers.append(modif)
 734
 735    @property
 736    def modifiers_statements(self) -> List[ModifierStatements]:
 737        """
 738        list(ModifierCall): List of the modifiers call (include expression and irs)
 739        """
 740        return list(self._modifiers)
 741
 742    @property
 743    def explicit_base_constructor_calls(self) -> List["Function"]:
 744        """
 745        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
 746
 747                        Base constructors implicitly or explicitly called by the contract definition will not be
 748                        included.
 749        """
 750        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 751        return [
 752            c.modifier.constructors_declared
 753            for c in self._explicit_base_constructor_calls
 754            if c.modifier.constructors_declared
 755        ]
 756
 757    @property
 758    def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]:
 759        """
 760        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
 761
 762        """
 763        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 764        return list(self._explicit_base_constructor_calls)
 765
 766    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
 767        self._explicit_base_constructor_calls.append(modif)
 768
 769    # endregion
 770    ###################################################################################
 771    ###################################################################################
 772    # region Variables
 773    ###################################################################################
 774    ###################################################################################
 775
 776    @property
 777    def variables(self) -> List[LocalVariable]:
 778        """
 779        Return all local variables
 780        Include paramters and return values
 781        """
 782        return list(self._variables.values())
 783
 784    @property
 785    def local_variables(self) -> List[LocalVariable]:
 786        """
 787        Return all local variables (dont include paramters and return values)
 788        """
 789        return list(set(self.variables) - set(self.returns) - set(self.parameters))
 790
 791    @property
 792    def variables_as_dict(self) -> Dict[str, LocalVariable]:
 793        return self._variables
 794
 795    @property
 796    def variables_read(self) -> List["Variable"]:
 797        """
 798        list(Variable): Variables read (local/state/solidity)
 799        """
 800        return list(self._vars_read)
 801
 802    @property
 803    def variables_written(self) -> List["Variable"]:
 804        """
 805        list(Variable): Variables written (local/state/solidity)
 806        """
 807        return list(self._vars_written)
 808
 809    @property
 810    def state_variables_read(self) -> List["StateVariable"]:
 811        """
 812        list(StateVariable): State variables read
 813        """
 814        return list(self._state_vars_read)
 815
 816    @property
 817    def solidity_variables_read(self) -> List["SolidityVariable"]:
 818        """
 819        list(SolidityVariable): Solidity variables read
 820        """
 821        return list(self._solidity_vars_read)
 822
 823    @property
 824    def state_variables_written(self) -> List["StateVariable"]:
 825        """
 826        list(StateVariable): State variables written
 827        """
 828        return list(self._state_vars_written)
 829
 830    @property
 831    def variables_read_or_written(self) -> List["Variable"]:
 832        """
 833        list(Variable): Variables read or written (local/state/solidity)
 834        """
 835        return list(self._vars_read_or_written)
 836
 837    @property
 838    def variables_read_as_expression(self) -> List["Expression"]:
 839        return self._expression_vars_read
 840
 841    @property
 842    def variables_written_as_expression(self) -> List["Expression"]:
 843        return self._expression_vars_written
 844
 845    @property
 846    def slithir_variables(self) -> List["SlithIRVariable"]:
 847        """
 848        Temporary and Reference Variables (not SSA form)
 849        """
 850
 851        return list(self._slithir_variables)
 852
 853    # endregion
 854    ###################################################################################
 855    ###################################################################################
 856    # region Calls
 857    ###################################################################################
 858    ###################################################################################
 859
 860    @property
 861    def internal_calls(self) -> List["InternalCallType"]:
 862        """
 863        list(Function or SolidityFunction): List of function calls (that does not create a transaction)
 864        """
 865        return list(self._internal_calls)
 866
 867    @property
 868    def solidity_calls(self) -> List[SolidityFunction]:
 869        """
 870        list(SolidityFunction): List of Soldity calls
 871        """
 872        return list(self._solidity_calls)
 873
 874    @property
 875    def high_level_calls(self) -> List["HighLevelCallType"]:
 876        """
 877        list((Contract, Function|Variable)):
 878        List of high level calls (external calls).
 879        A variable is called in case of call to a public state variable
 880        Include library calls
 881        """
 882        return list(self._high_level_calls)
 883
 884    @property
 885    def library_calls(self) -> List["LibraryCallType"]:
 886        """
 887        list((Contract, Function)):
 888        """
 889        return list(self._library_calls)
 890
 891    @property
 892    def low_level_calls(self) -> List["LowLevelCallType"]:
 893        """
 894        list((Variable|SolidityVariable, str)): List of low_level call
 895        A low level call is defined by
 896        - the variable called
 897        - the name of the function (call/delegatecall/codecall)
 898        """
 899        return list(self._low_level_calls)
 900
 901    @property
 902    def external_calls_as_expressions(self) -> List["Expression"]:
 903        """
 904        list(ExpressionCall): List of message calls (that creates a transaction)
 905        """
 906        return list(self._external_calls_as_expressions)
 907
 908    # endregion
 909    ###################################################################################
 910    ###################################################################################
 911    # region Expressions
 912    ###################################################################################
 913    ###################################################################################
 914
 915    @property
 916    def calls_as_expressions(self) -> List["Expression"]:
 917        return self._expression_calls
 918
 919    @property
 920    def expressions(self) -> List["Expression"]:
 921        """
 922        list(Expression): List of the expressions
 923        """
 924        if self._expressions is None:
 925            expressionss = [n.expression for n in self.nodes]
 926            expressions = [e for e in expressionss if e]
 927            self._expressions = expressions
 928        return self._expressions
 929
 930    @property
 931    def return_values(self) -> List["SlithIRVariable"]:
 932        """
 933        list(Return Values): List of the return values
 934        """
 935        from slither.core.cfg.node import NodeType
 936        from slither.slithir.operations import Return
 937        from slither.slithir.variables import Constant
 938
 939        if self._return_values is None:
 940            return_values = []
 941            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 942            [  # pylint: disable=expression-not-assigned
 943                return_values.extend(ir.values)
 944                for node in returns
 945                for ir in node.irs
 946                if isinstance(ir, Return)
 947            ]
 948            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
 949        return self._return_values
 950
 951    @property
 952    def return_values_ssa(self) -> List["SlithIRVariable"]:
 953        """
 954        list(Return Values in SSA form): List of the return values in ssa form
 955        """
 956        from slither.core.cfg.node import NodeType
 957        from slither.slithir.operations import Return
 958        from slither.slithir.variables import Constant
 959
 960        if self._return_values_ssa is None:
 961            return_values_ssa = []
 962            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 963            [  # pylint: disable=expression-not-assigned
 964                return_values_ssa.extend(ir.values)
 965                for node in returns
 966                for ir in node.irs_ssa
 967                if isinstance(ir, Return)
 968            ]
 969            self._return_values_ssa = list(
 970                {x for x in return_values_ssa if not isinstance(x, Constant)}
 971            )
 972        return self._return_values_ssa
 973
 974    # endregion
 975    ###################################################################################
 976    ###################################################################################
 977    # region SlithIR
 978    ###################################################################################
 979    ###################################################################################
 980
 981    @property
 982    def slithir_operations(self) -> List["Operation"]:
 983        """
 984        list(Operation): List of the slithir operations
 985        """
 986        if self._slithir_operations is None:
 987            operationss = [n.irs for n in self.nodes]
 988            operations = [item for sublist in operationss for item in sublist if item]
 989            self._slithir_operations = operations
 990        return self._slithir_operations
 991
 992    @property
 993    def slithir_ssa_operations(self) -> List["Operation"]:
 994        """
 995        list(Operation): List of the slithir operations (SSA)
 996        """
 997        if self._slithir_ssa_operations is None:
 998            operationss = [n.irs_ssa for n in self.nodes]
 999            operations = [item for sublist in operationss for item in sublist if item]
1000            self._slithir_ssa_operations = operations
1001        return self._slithir_ssa_operations
1002
1003    # endregion
1004    ###################################################################################
1005    ###################################################################################
1006    # region Signature
1007    ###################################################################################
1008    ###################################################################################
1009
1010    @property
1011    def solidity_signature(self) -> str:
1012        """
1013        Return a signature following the Solidity Standard
1014        Contract and converted into address
1015
1016        It might still keep internal types (ex: structure name) for internal functions.
1017        The reason is that internal functions allows recursive structure definition, which
1018        can't be converted following the Solidity stand ard
1019
1020        :return: the solidity signature
1021        """
1022        if self._solidity_signature is None:
1023            parameters = [
1024                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1025            ]
1026            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1027        return self._solidity_signature
1028
1029    @property
1030    def signature(self) -> Tuple[str, List[str], List[str]]:
1031        """
1032        (str, list(str), list(str)): Function signature as
1033        (name, list parameters type, list return values type)
1034        """
1035        # FIXME memoizing this function is not working properly for vyper
1036        # if self._signature is None:
1037        return (
1038            self.name,
1039            [str(x.type) for x in self.parameters],
1040            [str(x.type) for x in self.returns],
1041        )
1042        #     self._signature = signature
1043        # return self._signature
1044
1045    @property
1046    def signature_str(self) -> str:
1047        """
1048        str: func_name(type1,type2) returns (type3)
1049        Return the function signature as a str (contains the return values)
1050        """
1051        if self._signature_str is None:
1052            name, parameters, returnVars = self.signature
1053            self._signature_str = (
1054                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1055            )
1056        return self._signature_str
1057
1058    # endregion
1059    ###################################################################################
1060    ###################################################################################
1061    # region Functions
1062    ###################################################################################
1063    ###################################################################################
1064
1065    @property
1066    @abstractmethod
1067    def functions_shadowed(self) -> List["Function"]:
1068        pass
1069
1070    # endregion
1071    ###################################################################################
1072    ###################################################################################
1073    # region Reachable
1074    ###################################################################################
1075    ###################################################################################
1076
1077    @property
1078    def reachable_from_nodes(self) -> Set[ReacheableNode]:
1079        """
1080        Return
1081            ReacheableNode
1082        """
1083        return self._reachable_from_nodes
1084
1085    @property
1086    def reachable_from_functions(self) -> Set["Function"]:
1087        return self._reachable_from_functions
1088
1089    @property
1090    def all_reachable_from_functions(self) -> Set["Function"]:
1091        """
1092        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1093        """
1094        if self._all_reachable_from_functions is None:
1095            functions: Set["Function"] = set()
1096
1097            new_functions = self.reachable_from_functions
1098            # iterate until we have are finding new functions
1099            while new_functions and not new_functions.issubset(functions):
1100                functions = functions.union(new_functions)
1101                # Use a temporary set, because we iterate over new_functions
1102                new_functionss: Set["Function"] = set()
1103                for f in new_functions:
1104                    new_functionss = new_functionss.union(f.reachable_from_functions)
1105                new_functions = new_functionss - functions
1106
1107            self._all_reachable_from_functions = functions
1108        return self._all_reachable_from_functions
1109
1110    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1111        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1112        self._reachable_from_functions.add(n.function)
1113
1114    # endregion
1115    ###################################################################################
1116    ###################################################################################
1117    # region Recursive getters
1118    ###################################################################################
1119    ###################################################################################
1120
1121    def _explore_functions(self, f_new_values: Callable[["Function"], List]) -> List[Any]:
1122        values = f_new_values(self)
1123        explored = [self]
1124        to_explore = [
1125            c for c in self.internal_calls if isinstance(c, Function) and c not in explored
1126        ]
1127        to_explore += [
1128            c for (_, c) in self.library_calls if isinstance(c, Function) and c not in explored
1129        ]
1130        to_explore += [m for m in self.modifiers if m not in explored]
1131
1132        while to_explore:
1133            f = to_explore[0]
1134            to_explore = to_explore[1:]
1135            if f in explored:
1136                continue
1137            explored.append(f)
1138
1139            values += f_new_values(f)
1140
1141            to_explore += [
1142                c
1143                for c in f.internal_calls
1144                if isinstance(c, Function) and c not in explored and c not in to_explore
1145            ]
1146            to_explore += [
1147                c
1148                for (_, c) in f.library_calls
1149                if isinstance(c, Function) and c not in explored and c not in to_explore
1150            ]
1151            to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
1152
1153        return list(set(values))
1154
1155    def all_variables_read(self) -> List["Variable"]:
1156        """recursive version of variables_read"""
1157        if self._all_variables_read is None:
1158            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1159        return self._all_variables_read
1160
1161    def all_variables_written(self) -> List["Variable"]:
1162        """recursive version of variables_written"""
1163        if self._all_variables_written is None:
1164            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1165        return self._all_variables_written
1166
1167    def all_state_variables_read(self) -> List["StateVariable"]:
1168        """recursive version of variables_read"""
1169        if self._all_state_variables_read is None:
1170            self._all_state_variables_read = self._explore_functions(
1171                lambda x: x.state_variables_read
1172            )
1173        return self._all_state_variables_read
1174
1175    def all_solidity_variables_read(self) -> List[SolidityVariable]:
1176        """recursive version of solidity_read"""
1177        if self._all_solidity_variables_read is None:
1178            self._all_solidity_variables_read = self._explore_functions(
1179                lambda x: x.solidity_variables_read
1180            )
1181        return self._all_solidity_variables_read
1182
1183    def all_slithir_variables(self) -> List["SlithIRVariable"]:
1184        """recursive version of slithir_variables"""
1185        if self._all_slithir_variables is None:
1186            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1187        return self._all_slithir_variables
1188
1189    def all_nodes(self) -> List["Node"]:
1190        """recursive version of nodes"""
1191        if self._all_nodes is None:
1192            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1193        return self._all_nodes
1194
1195    def all_expressions(self) -> List["Expression"]:
1196        """recursive version of variables_read"""
1197        if self._all_expressions is None:
1198            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1199        return self._all_expressions
1200
1201    def all_slithir_operations(self) -> List["Operation"]:
1202        if self._all_slithir_operations is None:
1203            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1204        return self._all_slithir_operations
1205
1206    def all_state_variables_written(self) -> List[StateVariable]:
1207        """recursive version of variables_written"""
1208        if self._all_state_variables_written is None:
1209            self._all_state_variables_written = self._explore_functions(
1210                lambda x: x.state_variables_written
1211            )
1212        return self._all_state_variables_written
1213
1214    def all_internal_calls(self) -> List["InternalCallType"]:
1215        """recursive version of internal_calls"""
1216        if self._all_internals_calls is None:
1217            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1218        return self._all_internals_calls
1219
1220    def all_low_level_calls(self) -> List["LowLevelCallType"]:
1221        """recursive version of low_level calls"""
1222        if self._all_low_level_calls is None:
1223            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1224        return self._all_low_level_calls
1225
1226    def all_high_level_calls(self) -> List["HighLevelCallType"]:
1227        """recursive version of high_level calls"""
1228        if self._all_high_level_calls is None:
1229            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1230        return self._all_high_level_calls
1231
1232    def all_library_calls(self) -> List["LibraryCallType"]:
1233        """recursive version of library calls"""
1234        if self._all_library_calls is None:
1235            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1236        return self._all_library_calls
1237
1238    def all_solidity_calls(self) -> List[SolidityFunction]:
1239        """recursive version of solidity calls"""
1240        if self._all_solidity_calls is None:
1241            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1242        return self._all_solidity_calls
1243
1244    @staticmethod
1245    def _explore_func_cond_read(func: "Function", include_loop: bool) -> List["StateVariable"]:
1246        ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]
1247        return [item for sublist in ret for item in sublist]
1248
1249    def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]:
1250        """
1251        Return the state variable used in a condition
1252
1253        Over approximate and also return index access
1254        It won't work if the variable is assigned to a temp variable
1255        """
1256        if include_loop:
1257            if self._all_conditional_state_variables_read_with_loop is None:
1258                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1259                    lambda x: self._explore_func_cond_read(x, include_loop)
1260                )
1261            return self._all_conditional_state_variables_read_with_loop
1262        if self._all_conditional_state_variables_read is None:
1263            self._all_conditional_state_variables_read = self._explore_functions(
1264                lambda x: self._explore_func_cond_read(x, include_loop)
1265            )
1266        return self._all_conditional_state_variables_read
1267
1268    @staticmethod
1269    def _solidity_variable_in_binary(node: "Node") -> List[SolidityVariable]:
1270        from slither.slithir.operations.binary import Binary
1271
1272        ret = []
1273        for ir in node.irs:
1274            if isinstance(ir, Binary):
1275                ret += ir.read
1276        return [var for var in ret if isinstance(var, SolidityVariable)]
1277
1278    @staticmethod
1279    def _explore_func_conditional(
1280        func: "Function",
1281        f: Callable[["Node"], List[SolidityVariable]],
1282        include_loop: bool,
1283    ) -> List[Any]:
1284        ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
1285        return [item for sublist in ret for item in sublist]
1286
1287    def all_conditional_solidity_variables_read(
1288        self, include_loop: bool = True
1289    ) -> List[SolidityVariable]:
1290        """
1291        Return the Soldiity variables directly used in a condtion
1292
1293        Use of the IR to filter index access
1294        Assumption: the solidity vars are used directly in the conditional node
1295        It won't work if the variable is assigned to a temp variable
1296        """
1297        if include_loop:
1298            if self._all_conditional_solidity_variables_read_with_loop is None:
1299                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1300                    lambda x: self._explore_func_conditional(
1301                        x, self._solidity_variable_in_binary, include_loop
1302                    )
1303                )
1304            return self._all_conditional_solidity_variables_read_with_loop
1305
1306        if self._all_conditional_solidity_variables_read is None:
1307            self._all_conditional_solidity_variables_read = self._explore_functions(
1308                lambda x: self._explore_func_conditional(
1309                    x, self._solidity_variable_in_binary, include_loop
1310                )
1311            )
1312        return self._all_conditional_solidity_variables_read
1313
1314    @staticmethod
1315    def _solidity_variable_in_internal_calls(node: "Node") -> List[SolidityVariable]:
1316        from slither.slithir.operations.internal_call import InternalCall
1317
1318        ret = []
1319        for ir in node.irs:
1320            if isinstance(ir, InternalCall):
1321                ret += ir.read
1322        return [var for var in ret if isinstance(var, SolidityVariable)]
1323
1324    @staticmethod
1325    def _explore_func_nodes(
1326        func: "Function", f: Callable[["Node"], List[SolidityVariable]]
1327    ) -> List[Union[Any, SolidityVariableComposed]]:
1328        ret = [f(n) for n in func.nodes]
1329        return [item for sublist in ret for item in sublist]
1330
1331    def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]:
1332        """
1333        Return the Soldiity variables directly used in a call
1334
1335        Use of the IR to filter index access
1336        Used to catch check(msg.sender)
1337        """
1338        if self._all_solidity_variables_used_as_args is None:
1339            self._all_solidity_variables_used_as_args = self._explore_functions(
1340                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1341            )
1342        return self._all_solidity_variables_used_as_args
1343
1344    # endregion
1345    ###################################################################################
1346    ###################################################################################
1347    # region Visitor
1348    ###################################################################################
1349    ###################################################################################
1350
1351    def apply_visitor(self, Visitor: Callable) -> List:
1352        """
1353            Apply a visitor to all the function expressions
1354        Args:
1355            Visitor: slither.visitors
1356        Returns
1357            list(): results of the visit
1358        """
1359        expressions = self.expressions
1360        v = [Visitor(e).result() for e in expressions]
1361        return [item for sublist in v for item in sublist]
1362
1363    # endregion
1364    ###################################################################################
1365    ###################################################################################
1366    # region Getters from/to object
1367    ###################################################################################
1368    ###################################################################################
1369
1370    def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]:
1371        """
1372            Return a local variable from a name
1373
1374        Args:
1375            variable_name (str): name of the variable
1376        Returns:
1377            LocalVariable
1378        """
1379        return next((v for v in self.variables if v.name == variable_name), None)
1380
1381    # endregion
1382    ###################################################################################
1383    ###################################################################################
1384    # region Export
1385    ###################################################################################
1386    ###################################################################################
1387
1388    def cfg_to_dot(self, filename: str):
1389        """
1390            Export the function to a dot file
1391        Args:
1392            filename (str)
1393        """
1394        with open(filename, "w", encoding="utf8") as f:
1395            f.write("digraph{\n")
1396            for node in self.nodes:
1397                f.write(f'{node.node_id}[label="{str(node)}"];\n')
1398                for son in node.sons:
1399                    f.write(f"{node.node_id}->{son.node_id};\n")
1400
1401            f.write("}\n")
1402
1403    def dominator_tree_to_dot(self, filename: str):
1404        """
1405            Export the dominator tree of the function to a dot file
1406        Args:
1407            filename (str)
1408        """
1409
1410        def description(node):
1411            desc = f"{node}\n"
1412            desc += f"id: {node.node_id}"
1413            if node.dominance_frontier:
1414                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1415            return desc
1416
1417        with open(filename, "w", encoding="utf8") as f:
1418            f.write("digraph{\n")
1419            for node in self.nodes:
1420                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1421                if node.immediate_dominator:
1422                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1423
1424            f.write("}\n")
1425
1426    def slithir_cfg_to_dot(self, filename: str):
1427        """
1428        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1429        :param filename:
1430        :return:
1431        """
1432        content = self.slithir_cfg_to_dot_str()
1433        with open(filename, "w", encoding="utf8") as f:
1434            f.write(content)
1435
1436    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1437        """
1438        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1439        :return: the DOT content
1440        :rtype: str
1441        """
1442        from slither.core.cfg.node import NodeType
1443
1444        content = ""
1445        content += "digraph{\n"
1446        for node in self.nodes:
1447            label = f"Node Type: {node.type.value} {node.node_id}\n"
1448            if node.expression and not skip_expressions:
1449                label += f"\nEXPRESSION:\n{node.expression}\n"
1450            if node.irs and not skip_expressions:
1451                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1452            content += f'{node.node_id}[label="{label}"];\n'
1453            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1454                true_node = node.son_true
1455                if true_node:
1456                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1457                false_node = node.son_false
1458                if false_node:
1459                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1460            else:
1461                for son in node.sons:
1462                    content += f"{node.node_id}->{son.node_id};\n"
1463
1464        content += "}\n"
1465        return content
1466
1467    # endregion
1468    ###################################################################################
1469    ###################################################################################
1470    # region Summary information
1471    ###################################################################################
1472    ###################################################################################
1473
1474    def is_reading(self, variable: "Variable") -> bool:
1475        """
1476            Check if the function reads the variable
1477        Args:
1478            variable (Variable):
1479        Returns:
1480            bool: True if the variable is read
1481        """
1482        return variable in self.variables_read
1483
1484    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1485        """
1486            Check if the function reads the variable in a IF node
1487        Args:
1488            variable (Variable):
1489        Returns:
1490            bool: True if the variable is read
1491        """
1492        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1493        variables_read = [item for sublist in variables_reads for item in sublist]
1494        return variable in variables_read
1495
1496    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1497        """
1498            Check if the function reads the variable in an require or assert
1499        Args:
1500            variable (Variable):
1501        Returns:
1502            bool: True if the variable is read
1503        """
1504        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1505        variables_read = [item for sublist in variables_reads for item in sublist]
1506        return variable in variables_read
1507
1508    def is_writing(self, variable: "Variable") -> bool:
1509        """
1510            Check if the function writes the variable
1511        Args:
1512            variable (Variable):
1513        Returns:
1514            bool: True if the variable is written
1515        """
1516        return variable in self.variables_written
1517
1518    @abstractmethod
1519    def get_summary(
1520        self,
1521    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1522        pass
1523
1524    def is_protected(self) -> bool:
1525        """
1526            Determine if the function is protected using a check on msg.sender
1527
1528            Consider onlyOwner as a safe modifier.
1529            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1530            classify the function as protected
1531
1532            Otherwise only detects if msg.sender is directly used in a condition
1533            For example, it wont work for:
1534                address a = msg.sender
1535                require(a == owner)
1536        Returns
1537            (bool)
1538        """
1539
1540        if self._is_protected is None:
1541            if self.is_constructor:
1542                self._is_protected = True
1543                return True
1544            if "onlyOwner" in [m.name for m in self.modifiers]:
1545                self._is_protected = True
1546                return True
1547            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1548            args_vars = self.all_solidity_variables_used_as_args()
1549            self._is_protected = (
1550                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1551            )
1552        return self._is_protected
1553
1554    @property
1555    def is_reentrant(self) -> bool:
1556        """
1557        Determine if the function can be re-entered
1558        """
1559        reentrancy_modifier = "nonReentrant"
1560
1561        if self.function_language == FunctionLanguage.Vyper:
1562            reentrancy_modifier = "nonreentrant(lock)"
1563
1564        # TODO: compare with hash of known nonReentrant modifier instead of the name
1565        if reentrancy_modifier in [m.name for m in self.modifiers]:
1566            return False
1567
1568        if self.visibility in ["public", "external"]:
1569            return True
1570
1571        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1572        all_entry_points = [
1573            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1574        ]
1575        if not all_entry_points:
1576            return True
1577        return not all(
1578            (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points)
1579        )
1580
1581    # endregion
1582    ###################################################################################
1583    ###################################################################################
1584    # region Analyses
1585    ###################################################################################
1586    ###################################################################################
1587
1588    def _analyze_read_write(self) -> None:
1589        """Compute variables read/written/..."""
1590        write_var = [x.variables_written_as_expression for x in self.nodes]
1591        write_var = [x for x in write_var if x]
1592        write_var = [item for sublist in write_var for item in sublist]
1593        write_var = list(set(write_var))
1594        # Remove duplicate if they share the same string representation
1595        write_var = [
1596            next(obj)
1597            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1598        ]
1599        self._expression_vars_written = write_var
1600
1601        write_var = [x.variables_written for x in self.nodes]
1602        write_var = [x for x in write_var if x]
1603        write_var = [item for sublist in write_var for item in sublist]
1604        write_var = list(set(write_var))
1605        # Remove duplicate if they share the same string representation
1606        write_var = [
1607            next(obj)
1608            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1609        ]
1610        self._vars_written = write_var
1611
1612        read_var = [x.variables_read_as_expression for x in self.nodes]
1613        read_var = [x for x in read_var if x]
1614        read_var = [item for sublist in read_var for item in sublist]
1615        # Remove duplicate if they share the same string representation
1616        read_var = [
1617            next(obj)
1618            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1619        ]
1620        self._expression_vars_read = read_var
1621
1622        read_var = [x.variables_read for x in self.nodes]
1623        read_var = [x for x in read_var if x]
1624        read_var = [item for sublist in read_var for item in sublist]
1625        # Remove duplicate if they share the same string representation
1626        read_var = [
1627            next(obj)
1628            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1629        ]
1630        self._vars_read = read_var
1631
1632        self._state_vars_written = [
1633            x for x in self.variables_written if isinstance(x, StateVariable)
1634        ]
1635        self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)]
1636        self._solidity_vars_read = [
1637            x for x in self.variables_read if isinstance(x, SolidityVariable)
1638        ]
1639
1640        self._vars_read_or_written = self._vars_written + self._vars_read
1641
1642        slithir_variables = [x.slithir_variables for x in self.nodes]
1643        slithir_variables = [x for x in slithir_variables if x]
1644        self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
1645
1646    def _analyze_calls(self) -> None:
1647        calls = [x.calls_as_expression for x in self.nodes]
1648        calls = [x for x in calls if x]
1649        calls = [item for sublist in calls for item in sublist]
1650        self._expression_calls = list(set(calls))
1651
1652        internal_calls = [x.internal_calls for x in self.nodes]
1653        internal_calls = [x for x in internal_calls if x]
1654        internal_calls = [item for sublist in internal_calls for item in sublist]
1655        self._internal_calls = list(set(internal_calls))
1656
1657        self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)]
1658
1659        low_level_calls = [x.low_level_calls for x in self.nodes]
1660        low_level_calls = [x for x in low_level_calls if x]
1661        low_level_calls = [item for sublist in low_level_calls for item in sublist]
1662        self._low_level_calls = list(set(low_level_calls))
1663
1664        high_level_calls = [x.high_level_calls for x in self.nodes]
1665        high_level_calls = [x for x in high_level_calls if x]
1666        high_level_calls = [item for sublist in high_level_calls for item in sublist]
1667        self._high_level_calls = list(set(high_level_calls))
1668
1669        library_calls = [x.library_calls for x in self.nodes]
1670        library_calls = [x for x in library_calls if x]
1671        library_calls = [item for sublist in library_calls for item in sublist]
1672        self._library_calls = list(set(library_calls))
1673
1674        external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
1675        external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
1676        external_calls_as_expressions = [
1677            item for sublist in external_calls_as_expressions for item in sublist
1678        ]
1679        self._external_calls_as_expressions = list(set(external_calls_as_expressions))
1680
1681    # endregion
1682    ###################################################################################
1683    ###################################################################################
1684    # region Nodes
1685    ###################################################################################
1686    ###################################################################################
1687
1688    def new_node(
1689        self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]
1690    ) -> "Node":
1691        from slither.core.cfg.node import Node
1692
1693        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1694        node.set_offset(src, self.compilation_unit)
1695        self._counter_nodes += 1
1696        node.set_function(self)
1697        self._nodes.append(node)
1698
1699        return node
1700
1701    # endregion
1702    ###################################################################################
1703    ###################################################################################
1704    # region SlithIr and SSA
1705    ###################################################################################
1706    ###################################################################################
1707
1708    def _get_last_ssa_variable_instances(
1709        self, target_state: bool, target_local: bool
1710    ) -> Dict[str, Set["SlithIRVariable"]]:
1711        # pylint: disable=too-many-locals,too-many-branches
1712        from slither.slithir.variables import ReferenceVariable
1713        from slither.slithir.operations import OperationWithLValue
1714        from slither.core.cfg.node import NodeType
1715
1716        if not self.is_implemented:
1717            return {}
1718
1719        if self._entry_point is None:
1720            return {}
1721        # node, values
1722        to_explore: List[Tuple["Node", Dict]] = [(self._entry_point, {})]
1723        # node -> values
1724        explored: Dict = {}
1725        # name -> instances
1726        ret: Dict = {}
1727
1728        while to_explore:
1729            node, values = to_explore[0]
1730            to_explore = to_explore[1::]
1731
1732            if node.type != NodeType.ENTRYPOINT:
1733                for ir_ssa in node.irs_ssa:
1734                    if isinstance(ir_ssa, OperationWithLValue):
1735                        lvalue = ir_ssa.lvalue
1736                        if isinstance(lvalue, ReferenceVariable):
1737                            lvalue = lvalue.points_to_origin
1738                        if isinstance(lvalue, StateVariable) and target_state:
1739                            values[lvalue.canonical_name] = {lvalue}
1740                        if isinstance(lvalue, LocalVariable) and target_local:
1741                            values[lvalue.canonical_name] = {lvalue}
1742
1743            # Check for fixpoint
1744            if node in explored:
1745                if values == explored[node]:
1746                    continue
1747                for k, instances in values.items():
1748                    if k not in explored[node]:
1749                        explored[node][k] = set()
1750                    explored[node][k] |= instances
1751                values = explored[node]
1752            else:
1753                explored[node] = values
1754
1755            # Return condition
1756            if node.will_return:
1757                for name, instances in values.items():
1758                    if name not in ret:
1759                        ret[name] = set()
1760                    ret[name] |= instances
1761
1762            for son in node.sons:
1763                to_explore.append((son, dict(values)))
1764
1765        return ret
1766
1767    def get_last_ssa_state_variables_instances(
1768        self,
1769    ) -> Dict[str, Set["SlithIRVariable"]]:
1770        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
1771
1772    def get_last_ssa_local_variables_instances(
1773        self,
1774    ) -> Dict[str, Set["SlithIRVariable"]]:
1775        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
1776
1777    @staticmethod
1778    def _unchange_phi(ir: "Operation") -> bool:
1779        from slither.slithir.operations import Phi, PhiCallback
1780
1781        if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
1782            return False
1783        if not ir.rvalues:
1784            return True
1785        return ir.rvalues[0] == ir.lvalue
1786
1787    def fix_phi(
1788        self,
1789        last_state_variables_instances: Dict[str, List["StateVariable"]],
1790        initial_state_variables_instances: Dict[str, "StateVariable"],
1791    ) -> None:
1792        from slither.slithir.operations import InternalCall, PhiCallback
1793        from slither.slithir.variables import Constant, StateIRVariable
1794
1795        for node in self.nodes:
1796            for ir in node.irs_ssa:
1797                if node == self.entry_point:
1798                    if isinstance(ir.lvalue, StateIRVariable):
1799                        additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1800                        additional += last_state_variables_instances[ir.lvalue.canonical_name]
1801                        ir.rvalues = list(set(additional + ir.rvalues))
1802                    # function parameter
1803                    else:
1804                        # find index of the parameter
1805                        idx = self.parameters.index(ir.lvalue.non_ssa_version)
1806                        # find non ssa version of that index
1807                        additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1808                        additional = unroll(additional)
1809                        additional = [a for a in additional if not isinstance(a, Constant)]
1810                        ir.rvalues = list(set(additional + ir.rvalues))
1811                if isinstance(ir, PhiCallback):
1812                    callee_ir = ir.callee_ir
1813                    if isinstance(callee_ir, InternalCall):
1814                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1815                        if ir.lvalue.canonical_name in last_ssa:
1816                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1817                        else:
1818                            ir.rvalues = [ir.lvalue]
1819                    else:
1820                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1821                        ir.rvalues = list(set(additional + ir.rvalues))
1822
1823            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
1824
1825    def generate_slithir_and_analyze(self) -> None:
1826
1827        for node in self.nodes:
1828            node.slithir_generation()
1829
1830        self._analyze_read_write()
1831        self._analyze_calls()
1832
1833    @abstractmethod
1834    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1835        pass
1836
1837    def update_read_write_using_ssa(self) -> None:
1838        for node in self.nodes:
1839            node.update_read_write_using_ssa()
1840        self._analyze_read_write()
1841
1842    ###################################################################################
1843    ###################################################################################
1844    # region Built in definitions
1845    ###################################################################################
1846    ###################################################################################
1847
1848    def __str__(self) -> str:
1849        return self.name
1850
1851    # endregion

Function class

function_language: FunctionLanguage
has_documentation: bool
name: str
239    @property
240    def name(self) -> str:
241        """
242        str: function name
243        """
244        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
245            return "constructor"
246        if self._name == "" and self._function_type == FunctionType.FALLBACK:
247            return "fallback"
248        if self._function_type == FunctionType.RECEIVE:
249            return "receive"
250        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
251            return "slitherConstructorVariables"
252        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
253            return "slitherConstructorConstantVariables"
254        return self._name

str: function name

internal_scope: List[str]
260    @property
261    def internal_scope(self) -> List[str]:
262        """
263        Return a list of name representing the scope of the function
264        This is used to model nested functions declared in YUL
265
266        :return:
267        """
268        return self._internal_scope

Return a list of name representing the scope of the function This is used to model nested functions declared in YUL

Returns
full_name: str
274    @property
275    def full_name(self) -> str:
276        """
277        str: func_name(type1,type2)
278        Return the function signature without the return values
279        The difference between this function and solidity_function is that full_name does not translate the underlying
280        type (ex: structure, contract to address, ...)
281        """
282        if self._full_name is None:
283            name, parameters, _ = self.signature
284            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
285            self._full_name = full_name
286        return self._full_name

str: func_name(type1,type2) Return the function signature without the return values The difference between this function and solidity_function is that full_name does not translate the underlying type (ex: structure, contract to address, ...)

canonical_name: str
288    @property
289    @abstractmethod
290    def canonical_name(self) -> str:
291        """
292        str: contract.func_name(type1,type2)
293        Return the function signature without the return values
294        """
295        return ""

str: contract.func_name(type1,type2) Return the function signature without the return values

contains_assembly: bool
297    @property
298    def contains_assembly(self) -> bool:
299        return self._contains_assembly
def can_reenter( self, callstack: Union[list[Union[Function, slither.core.variables.variable.Variable]], NoneType] = None) -> bool:
305    def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool:
306        """
307        Check if the function can re-enter
308        Follow internal calls.
309        Do not consider CREATE as potential re-enter, but check if the
310        destination's constructor can contain a call (recurs. follow nested CREATE)
311        For Solidity > 0.5, filter access to public variables and constant/pure/view
312        For call to this. check if the destination can re-enter
313        Do not consider Send/Transfer as there is not enough gas
314        :param callstack: used internally to check for recursion
315        :return bool:
316        """
317        from slither.slithir.operations import Call
318
319        if self._can_reenter is None:
320            self._can_reenter = False
321            for ir in self.all_slithir_operations():
322                if isinstance(ir, Call) and ir.can_reenter(callstack):
323                    self._can_reenter = True
324                    return True
325        return self._can_reenter

Check if the function can re-enter Follow internal calls. Do not consider CREATE as potential re-enter, but check if the destination's constructor can contain a call (recurs. follow nested CREATE) For Solidity > 0.5, filter access to public variables and constant/pure/view For call to this. check if the destination can re-enter Do not consider Send/Transfer as there is not enough gas

Parameters
  • callstack: used internally to check for recursion
Returns
def can_send_eth(self) -> bool:
327    def can_send_eth(self) -> bool:
328        """
329        Check if the function or any internal (not external) functions called by it can send eth
330        :return bool:
331        """
332        from slither.slithir.operations import Call
333
334        if self._can_send_eth is None:
335            self._can_send_eth = False
336            for ir in self.all_slithir_operations():
337                if isinstance(ir, Call) and ir.can_send_eth():
338                    self._can_send_eth = True
339                    return True
340        return self._can_send_eth

Check if the function or any internal (not external) functions called by it can send eth

Returns
is_checked: bool
342    @property
343    def is_checked(self) -> bool:
344        """
345        Return true if the overflow are enabled by default
346
347
348        :return:
349        """
350
351        return self.compilation_unit.solc_version >= "0.8.0"

Return true if the overflow are enabled by default

Returns
id: Union[str, NoneType]
353    @property
354    def id(self) -> Optional[str]:
355        """
356        Return the reference ID of the function, if available.
357
358        :return:
359        :rtype:
360        """
361        return self._id

Return the reference ID of the function, if available.

Returns

file_scope: slither.core.scope.scope.FileScope
367    @property
368    @abstractmethod
369    def file_scope(self) -> "FileScope":
370        pass
def set_function_type(self, t: FunctionType) -> None:
379    def set_function_type(self, t: FunctionType) -> None:
380        assert isinstance(t, FunctionType)
381        self._function_type = t
function_type: Union[FunctionType, NoneType]
383    @property
384    def function_type(self) -> Optional[FunctionType]:
385        return self._function_type
is_constructor: bool
391    @property
392    def is_constructor(self) -> bool:
393        """
394        bool: True if the function is the constructor
395        """
396        return self._function_type == FunctionType.CONSTRUCTOR

bool: True if the function is the constructor

is_constructor_variables: bool
398    @property
399    def is_constructor_variables(self) -> bool:
400        """
401        bool: True if the function is the constructor of the variables
402        Slither has inbuilt functions to hold the state variables initialization
403        """
404        return self._function_type in [
405            FunctionType.CONSTRUCTOR_VARIABLES,
406            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
407        ]

bool: True if the function is the constructor of the variables Slither has inbuilt functions to hold the state variables initialization

is_fallback: bool
409    @property
410    def is_fallback(self) -> bool:
411        """
412            Determine if the function is the fallback function for the contract
413        Returns
414            (bool)
415        """
416        return self._function_type == FunctionType.FALLBACK

Determine if the function is the fallback function for the contract Returns (bool)

is_receive: bool
418    @property
419    def is_receive(self) -> bool:
420        """
421            Determine if the function is the receive function for the contract
422        Returns
423            (bool)
424        """
425        return self._function_type == FunctionType.RECEIVE

Determine if the function is the receive function for the contract Returns (bool)

payable: bool
434    @property
435    def payable(self) -> bool:
436        """
437        bool: True if the function is payable
438        """
439        return self._payable

bool: True if the function is payable

is_virtual: bool
452    @property
453    def is_virtual(self) -> bool:
454        """
455        Note for Solidity < 0.6.0 it will always be false
456        bool: True if the function is virtual
457        """
458        return self._virtual

Note for Solidity < 0.6.0 it will always be false bool: True if the function is virtual

is_override: bool
464    @property
465    def is_override(self) -> bool:
466        """
467        Note for Solidity < 0.6.0 it will always be false
468        bool: True if the function overrides a base function
469        """
470        return len(self._overrides) > 0

Note for Solidity < 0.6.0 it will always be false bool: True if the function overrides a base function

472    @property
473    def overridden_by(self) -> List["FunctionContract"]:
474        """
475        List["FunctionContract"]: List of functions in child contracts that override this function
476        This may include distinct instances of the same function due to inheritance
477        """
478        return self._overridden_by

List["FunctionContract"]: List of functions in child contracts that override this function This may include distinct instances of the same function due to inheritance

480    @property
481    def overrides(self) -> List["FunctionContract"]:
482        """
483        List["FunctionContract"]: List of functions in parent contracts that this function overrides
484        This may include distinct instances of the same function due to inheritance
485        """
486        return self._overrides

List["FunctionContract"]: List of functions in parent contracts that this function overrides This may include distinct instances of the same function due to inheritance

visibility: str
495    @property
496    def visibility(self) -> str:
497        """
498        str: Function visibility
499        """
500        assert self._visibility is not None
501        return self._visibility

str: Function visibility

def set_visibility(self, v: str) -> None:
507    def set_visibility(self, v: str) -> None:
508        self._visibility = v
view: bool
510    @property
511    def view(self) -> bool:
512        """
513        bool: True if the function is declared as view
514        """
515        return self._view

bool: True if the function is declared as view

pure: bool
521    @property
522    def pure(self) -> bool:
523        """
524        bool: True if the function is declared as pure
525        """
526        return self._pure

bool: True if the function is declared as pure

is_shadowed: bool
532    @property
533    def is_shadowed(self) -> bool:
534        return self._is_shadowed
shadows: bool
540    @property
541    def shadows(self) -> bool:
542        return self._shadows
is_implemented: bool
555    @property
556    def is_implemented(self) -> bool:
557        """
558        bool: True if the function is implemented
559        """
560        return self._is_implemented

bool: True if the function is implemented

is_empty: bool
566    @property
567    def is_empty(self) -> bool:
568        """
569        bool: True if the function is empty, None if the function is an interface
570        """
571        return self._is_empty

bool: True if the function is empty, None if the function is an interface

nodes: list[slither.core.cfg.node.Node]
584    @property
585    def nodes(self) -> List["Node"]:
586        """
587        list(Node): List of the nodes
588        """
589        return list(self._nodes)

list(Node): List of the nodes

entry_point: Union[slither.core.cfg.node.Node, NoneType]
595    @property
596    def entry_point(self) -> Optional["Node"]:
597        """
598        Node: Entry point of the function
599        """
600        return self._entry_point

Node: Entry point of the function

def add_node(self, node: slither.core.cfg.node.Node) -> None:
606    def add_node(self, node: "Node") -> None:
607        if not self._entry_point:
608            self._entry_point = node
609        self._nodes.append(node)
nodes_ordered_dominators: list[slither.core.cfg.node.Node]
611    @property
612    def nodes_ordered_dominators(self) -> List["Node"]:
613        # TODO: does not work properly; most likely due to modifier call
614        # This will not work for modifier call that lead to multiple nodes
615        # from slither.core.cfg.node import NodeType
616        if self._nodes_ordered_dominators is None:
617            self._nodes_ordered_dominators = []
618            if self.entry_point:
619                self._compute_nodes_ordered_dominators(self.entry_point)
620
621            for node in self.nodes:
622                # if node.type == NodeType.OTHER_ENTRYPOINT:
623                if not node in self._nodes_ordered_dominators:
624                    self._compute_nodes_ordered_dominators(node)
625
626        return self._nodes_ordered_dominators
643    @property
644    def parameters(self) -> List["LocalVariable"]:
645        """
646        list(LocalVariable): List of the parameters
647        """
648        return list(self._parameters)

list(LocalVariable): List of the parameters

def add_parameters(self, p: slither.core.variables.local_variable.LocalVariable) -> None:
650    def add_parameters(self, p: "LocalVariable") -> None:
651        self._parameters.append(p)
653    @property
654    def parameters_ssa(self) -> List["LocalIRVariable"]:
655        """
656        list(LocalIRVariable): List of the parameters (SSA form)
657        """
658        return list(self._parameters_ssa)

list(LocalIRVariable): List of the parameters (SSA form)

def add_parameter_ssa( self, var: slither.slithir.variables.local_variable.LocalIRVariable) -> None:
660    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
661        self._parameters_ssa.append(var)
def parameters_src(self) -> slither.core.source_mapping.source_mapping.SourceMapping:
663    def parameters_src(self) -> SourceMapping:
664        return self._parameters_src
return_type: Union[List[slither.core.solidity_types.type.Type], NoneType]
673    @property
674    def return_type(self) -> Optional[List[Type]]:
675        """
676        Return the list of return type
677        If no return, return None
678        """
679        returns = self.returns
680        if returns:
681            return [r.type for r in returns]
682        return None

Return the list of return type If no return, return None

def returns_src(self) -> slither.core.source_mapping.source_mapping.SourceMapping:
684    def returns_src(self) -> SourceMapping:
685        return self._returns_src
type: Union[List[slither.core.solidity_types.type.Type], NoneType]
687    @property
688    def type(self) -> Optional[List[Type]]:
689        """
690        Return the list of return type
691        If no return, return None
692        Alias of return_type
693        """
694        return self.return_type

Return the list of return type If no return, return None Alias of return_type

696    @property
697    def returns(self) -> List["LocalVariable"]:
698        """
699        list(LocalVariable): List of the return variables
700        """
701        return list(self._returns)

list(LocalVariable): List of the return variables

def add_return(self, r: slither.core.variables.local_variable.LocalVariable) -> None:
703    def add_return(self, r: "LocalVariable") -> None:
704        self._returns.append(r)
706    @property
707    def returns_ssa(self) -> List["LocalIRVariable"]:
708        """
709        list(LocalIRVariable): List of the return variables (SSA form)
710        """
711        return list(self._returns_ssa)

list(LocalIRVariable): List of the return variables (SSA form)

def add_return_ssa( self, var: slither.slithir.variables.local_variable.LocalIRVariable) -> None:
713    def add_return_ssa(self, var: "LocalIRVariable") -> None:
714        self._returns_ssa.append(var)
modifiers: list[typing.Union[slither.core.declarations.contract.Contract, Function]]
723    @property
724    def modifiers(self) -> List[Union["Contract", "Function"]]:
725        """
726        list(Modifier): List of the modifiers
727        Can be contract for constructor's calls
728
729        """
730        return [c.modifier for c in self._modifiers]

list(Modifier): List of the modifiers Can be contract for constructor's calls

def add_modifier( self, modif: ModifierStatements) -> None:
732    def add_modifier(self, modif: "ModifierStatements") -> None:
733        self._modifiers.append(modif)
modifiers_statements: List[ModifierStatements]
735    @property
736    def modifiers_statements(self) -> List[ModifierStatements]:
737        """
738        list(ModifierCall): List of the modifiers call (include expression and irs)
739        """
740        return list(self._modifiers)

list(ModifierCall): List of the modifiers call (include expression and irs)

explicit_base_constructor_calls: list[Function]
742    @property
743    def explicit_base_constructor_calls(self) -> List["Function"]:
744        """
745        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
746
747                        Base constructors implicitly or explicitly called by the contract definition will not be
748                        included.
749        """
750        # This is a list of contracts internally, so we convert it to a list of constructor functions.
751        return [
752            c.modifier.constructors_declared
753            for c in self._explicit_base_constructor_calls
754            if c.modifier.constructors_declared
755        ]

list(Function): List of the base constructors called explicitly by this presumed constructor definition.

Base constructors implicitly or explicitly called by the contract definition will not be included.

explicit_base_constructor_calls_statements: List[ModifierStatements]
757    @property
758    def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]:
759        """
760        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
761
762        """
763        # This is a list of contracts internally, so we convert it to a list of constructor functions.
764        return list(self._explicit_base_constructor_calls)

list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.

def add_explicit_base_constructor_calls_statements( self, modif: ModifierStatements) -> None:
766    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
767        self._explicit_base_constructor_calls.append(modif)
776    @property
777    def variables(self) -> List[LocalVariable]:
778        """
779        Return all local variables
780        Include paramters and return values
781        """
782        return list(self._variables.values())

Return all local variables Include paramters and return values

local_variables: List[slither.core.variables.local_variable.LocalVariable]
784    @property
785    def local_variables(self) -> List[LocalVariable]:
786        """
787        Return all local variables (dont include paramters and return values)
788        """
789        return list(set(self.variables) - set(self.returns) - set(self.parameters))

Return all local variables (dont include paramters and return values)

variables_as_dict: Dict[str, slither.core.variables.local_variable.LocalVariable]
791    @property
792    def variables_as_dict(self) -> Dict[str, LocalVariable]:
793        return self._variables
variables_read: list[slither.core.variables.variable.Variable]
795    @property
796    def variables_read(self) -> List["Variable"]:
797        """
798        list(Variable): Variables read (local/state/solidity)
799        """
800        return list(self._vars_read)

list(Variable): Variables read (local/state/solidity)

variables_written: list[slither.core.variables.variable.Variable]
802    @property
803    def variables_written(self) -> List["Variable"]:
804        """
805        list(Variable): Variables written (local/state/solidity)
806        """
807        return list(self._vars_written)

list(Variable): Variables written (local/state/solidity)

state_variables_read: list[slither.core.variables.state_variable.StateVariable]
809    @property
810    def state_variables_read(self) -> List["StateVariable"]:
811        """
812        list(StateVariable): State variables read
813        """
814        return list(self._state_vars_read)

list(StateVariable): State variables read

solidity_variables_read: list[slither.core.declarations.solidity_variables.SolidityVariable]
816    @property
817    def solidity_variables_read(self) -> List["SolidityVariable"]:
818        """
819        list(SolidityVariable): Solidity variables read
820        """
821        return list(self._solidity_vars_read)

list(SolidityVariable): Solidity variables read

state_variables_written: list[slither.core.variables.state_variable.StateVariable]
823    @property
824    def state_variables_written(self) -> List["StateVariable"]:
825        """
826        list(StateVariable): State variables written
827        """
828        return list(self._state_vars_written)

list(StateVariable): State variables written

variables_read_or_written: list[slither.core.variables.variable.Variable]
830    @property
831    def variables_read_or_written(self) -> List["Variable"]:
832        """
833        list(Variable): Variables read or written (local/state/solidity)
834        """
835        return list(self._vars_read_or_written)

list(Variable): Variables read or written (local/state/solidity)

variables_read_as_expression: list[slither.core.expressions.expression.Expression]
837    @property
838    def variables_read_as_expression(self) -> List["Expression"]:
839        return self._expression_vars_read
variables_written_as_expression: list[slither.core.expressions.expression.Expression]
841    @property
842    def variables_written_as_expression(self) -> List["Expression"]:
843        return self._expression_vars_written
slithir_variables: list[slither.slithir.variables.variable.SlithIRVariable]
845    @property
846    def slithir_variables(self) -> List["SlithIRVariable"]:
847        """
848        Temporary and Reference Variables (not SSA form)
849        """
850
851        return list(self._slithir_variables)

Temporary and Reference Variables (not SSA form)

internal_calls: list[typing.Union[Function, slither.core.declarations.solidity_variables.SolidityFunction]]
860    @property
861    def internal_calls(self) -> List["InternalCallType"]:
862        """
863        list(Function or SolidityFunction): List of function calls (that does not create a transaction)
864        """
865        return list(self._internal_calls)

list(Function or SolidityFunction): List of function calls (that does not create a transaction)

867    @property
868    def solidity_calls(self) -> List[SolidityFunction]:
869        """
870        list(SolidityFunction): List of Soldity calls
871        """
872        return list(self._solidity_calls)

list(SolidityFunction): List of Soldity calls

high_level_calls: list[tuple[slither.core.declarations.contract.Contract, typing.Union[Function, slither.core.variables.variable.Variable]]]
874    @property
875    def high_level_calls(self) -> List["HighLevelCallType"]:
876        """
877        list((Contract, Function|Variable)):
878        List of high level calls (external calls).
879        A variable is called in case of call to a public state variable
880        Include library calls
881        """
882        return list(self._high_level_calls)

list((Contract, Function|Variable)): List of high level calls (external calls). A variable is called in case of call to a public state variable Include library calls

library_calls: list[tuple[slither.core.declarations.contract.Contract, Function]]
884    @property
885    def library_calls(self) -> List["LibraryCallType"]:
886        """
887        list((Contract, Function)):
888        """
889        return list(self._library_calls)

list((Contract, Function)):

891    @property
892    def low_level_calls(self) -> List["LowLevelCallType"]:
893        """
894        list((Variable|SolidityVariable, str)): List of low_level call
895        A low level call is defined by
896        - the variable called
897        - the name of the function (call/delegatecall/codecall)
898        """
899        return list(self._low_level_calls)

list((Variable|SolidityVariable, str)): List of low_level call A low level call is defined by

  • the variable called
  • the name of the function (call/delegatecall/codecall)
external_calls_as_expressions: list[slither.core.expressions.expression.Expression]
901    @property
902    def external_calls_as_expressions(self) -> List["Expression"]:
903        """
904        list(ExpressionCall): List of message calls (that creates a transaction)
905        """
906        return list(self._external_calls_as_expressions)

list(ExpressionCall): List of message calls (that creates a transaction)

calls_as_expressions: list[slither.core.expressions.expression.Expression]
915    @property
916    def calls_as_expressions(self) -> List["Expression"]:
917        return self._expression_calls
expressions: list[slither.core.expressions.expression.Expression]
919    @property
920    def expressions(self) -> List["Expression"]:
921        """
922        list(Expression): List of the expressions
923        """
924        if self._expressions is None:
925            expressionss = [n.expression for n in self.nodes]
926            expressions = [e for e in expressionss if e]
927            self._expressions = expressions
928        return self._expressions

list(Expression): List of the expressions

return_values: list[slither.slithir.variables.variable.SlithIRVariable]
930    @property
931    def return_values(self) -> List["SlithIRVariable"]:
932        """
933        list(Return Values): List of the return values
934        """
935        from slither.core.cfg.node import NodeType
936        from slither.slithir.operations import Return
937        from slither.slithir.variables import Constant
938
939        if self._return_values is None:
940            return_values = []
941            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
942            [  # pylint: disable=expression-not-assigned
943                return_values.extend(ir.values)
944                for node in returns
945                for ir in node.irs
946                if isinstance(ir, Return)
947            ]
948            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
949        return self._return_values

list(Return Values): List of the return values

return_values_ssa: list[slither.slithir.variables.variable.SlithIRVariable]
951    @property
952    def return_values_ssa(self) -> List["SlithIRVariable"]:
953        """
954        list(Return Values in SSA form): List of the return values in ssa form
955        """
956        from slither.core.cfg.node import NodeType
957        from slither.slithir.operations import Return
958        from slither.slithir.variables import Constant
959
960        if self._return_values_ssa is None:
961            return_values_ssa = []
962            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
963            [  # pylint: disable=expression-not-assigned
964                return_values_ssa.extend(ir.values)
965                for node in returns
966                for ir in node.irs_ssa
967                if isinstance(ir, Return)
968            ]
969            self._return_values_ssa = list(
970                {x for x in return_values_ssa if not isinstance(x, Constant)}
971            )
972        return self._return_values_ssa

list(Return Values in SSA form): List of the return values in ssa form

slithir_operations: list[slither.slithir.operations.operation.Operation]
981    @property
982    def slithir_operations(self) -> List["Operation"]:
983        """
984        list(Operation): List of the slithir operations
985        """
986        if self._slithir_operations is None:
987            operationss = [n.irs for n in self.nodes]
988            operations = [item for sublist in operationss for item in sublist if item]
989            self._slithir_operations = operations
990        return self._slithir_operations

list(Operation): List of the slithir operations

slithir_ssa_operations: list[slither.slithir.operations.operation.Operation]
 992    @property
 993    def slithir_ssa_operations(self) -> List["Operation"]:
 994        """
 995        list(Operation): List of the slithir operations (SSA)
 996        """
 997        if self._slithir_ssa_operations is None:
 998            operationss = [n.irs_ssa for n in self.nodes]
 999            operations = [item for sublist in operationss for item in sublist if item]
1000            self._slithir_ssa_operations = operations
1001        return self._slithir_ssa_operations

list(Operation): List of the slithir operations (SSA)

solidity_signature: str
1010    @property
1011    def solidity_signature(self) -> str:
1012        """
1013        Return a signature following the Solidity Standard
1014        Contract and converted into address
1015
1016        It might still keep internal types (ex: structure name) for internal functions.
1017        The reason is that internal functions allows recursive structure definition, which
1018        can't be converted following the Solidity stand ard
1019
1020        :return: the solidity signature
1021        """
1022        if self._solidity_signature is None:
1023            parameters = [
1024                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1025            ]
1026            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1027        return self._solidity_signature

Return a signature following the Solidity Standard Contract and converted into address

It might still keep internal types (ex: structure name) for internal functions. The reason is that internal functions allows recursive structure definition, which can't be converted following the Solidity stand ard

Returns

the solidity signature

signature: Tuple[str, List[str], List[str]]
1029    @property
1030    def signature(self) -> Tuple[str, List[str], List[str]]:
1031        """
1032        (str, list(str), list(str)): Function signature as
1033        (name, list parameters type, list return values type)
1034        """
1035        # FIXME memoizing this function is not working properly for vyper
1036        # if self._signature is None:
1037        return (
1038            self.name,
1039            [str(x.type) for x in self.parameters],
1040            [str(x.type) for x in self.returns],
1041        )
1042        #     self._signature = signature
1043        # return self._signature

(str, list(str), list(str)): Function signature as (name, list parameters type, list return values type)

signature_str: str
1045    @property
1046    def signature_str(self) -> str:
1047        """
1048        str: func_name(type1,type2) returns (type3)
1049        Return the function signature as a str (contains the return values)
1050        """
1051        if self._signature_str is None:
1052            name, parameters, returnVars = self.signature
1053            self._signature_str = (
1054                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1055            )
1056        return self._signature_str

str: func_name(type1,type2) returns (type3) Return the function signature as a str (contains the return values)

functions_shadowed: list[Function]
1065    @property
1066    @abstractmethod
1067    def functions_shadowed(self) -> List["Function"]:
1068        pass
reachable_from_nodes: Set[ReacheableNode]
1077    @property
1078    def reachable_from_nodes(self) -> Set[ReacheableNode]:
1079        """
1080        Return
1081            ReacheableNode
1082        """
1083        return self._reachable_from_nodes

Return ReacheableNode

reachable_from_functions: set[Function]
1085    @property
1086    def reachable_from_functions(self) -> Set["Function"]:
1087        return self._reachable_from_functions
all_reachable_from_functions: set[Function]
1089    @property
1090    def all_reachable_from_functions(self) -> Set["Function"]:
1091        """
1092        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1093        """
1094        if self._all_reachable_from_functions is None:
1095            functions: Set["Function"] = set()
1096
1097            new_functions = self.reachable_from_functions
1098            # iterate until we have are finding new functions
1099            while new_functions and not new_functions.issubset(functions):
1100                functions = functions.union(new_functions)
1101                # Use a temporary set, because we iterate over new_functions
1102                new_functionss: Set["Function"] = set()
1103                for f in new_functions:
1104                    new_functionss = new_functionss.union(f.reachable_from_functions)
1105                new_functions = new_functionss - functions
1106
1107            self._all_reachable_from_functions = functions
1108        return self._all_reachable_from_functions

Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)

def add_reachable_from_node( self, n: slither.core.cfg.node.Node, ir: slither.slithir.operations.operation.Operation) -> None:
1110    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1111        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1112        self._reachable_from_functions.add(n.function)
def all_variables_read(self) -> list[slither.core.variables.variable.Variable]:
1155    def all_variables_read(self) -> List["Variable"]:
1156        """recursive version of variables_read"""
1157        if self._all_variables_read is None:
1158            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1159        return self._all_variables_read

recursive version of variables_read

def all_variables_written(self) -> list[slither.core.variables.variable.Variable]:
1161    def all_variables_written(self) -> List["Variable"]:
1162        """recursive version of variables_written"""
1163        if self._all_variables_written is None:
1164            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1165        return self._all_variables_written

recursive version of variables_written

def all_state_variables_read(self) -> list[slither.core.variables.state_variable.StateVariable]:
1167    def all_state_variables_read(self) -> List["StateVariable"]:
1168        """recursive version of variables_read"""
1169        if self._all_state_variables_read is None:
1170            self._all_state_variables_read = self._explore_functions(
1171                lambda x: x.state_variables_read
1172            )
1173        return self._all_state_variables_read

recursive version of variables_read

def all_solidity_variables_read( self) -> List[slither.core.declarations.solidity_variables.SolidityVariable]:
1175    def all_solidity_variables_read(self) -> List[SolidityVariable]:
1176        """recursive version of solidity_read"""
1177        if self._all_solidity_variables_read is None:
1178            self._all_solidity_variables_read = self._explore_functions(
1179                lambda x: x.solidity_variables_read
1180            )
1181        return self._all_solidity_variables_read

recursive version of solidity_read

def all_slithir_variables(self) -> list[slither.slithir.variables.variable.SlithIRVariable]:
1183    def all_slithir_variables(self) -> List["SlithIRVariable"]:
1184        """recursive version of slithir_variables"""
1185        if self._all_slithir_variables is None:
1186            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1187        return self._all_slithir_variables

recursive version of slithir_variables

def all_nodes(self) -> list[slither.core.cfg.node.Node]:
1189    def all_nodes(self) -> List["Node"]:
1190        """recursive version of nodes"""
1191        if self._all_nodes is None:
1192            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1193        return self._all_nodes

recursive version of nodes

def all_expressions(self) -> list[slither.core.expressions.expression.Expression]:
1195    def all_expressions(self) -> List["Expression"]:
1196        """recursive version of variables_read"""
1197        if self._all_expressions is None:
1198            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1199        return self._all_expressions

recursive version of variables_read

def all_slithir_operations(self) -> list[slither.slithir.operations.operation.Operation]:
1201    def all_slithir_operations(self) -> List["Operation"]:
1202        if self._all_slithir_operations is None:
1203            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1204        return self._all_slithir_operations
def all_state_variables_written(self) -> List[slither.core.variables.state_variable.StateVariable]:
1206    def all_state_variables_written(self) -> List[StateVariable]:
1207        """recursive version of variables_written"""
1208        if self._all_state_variables_written is None:
1209            self._all_state_variables_written = self._explore_functions(
1210                lambda x: x.state_variables_written
1211            )
1212        return self._all_state_variables_written

recursive version of variables_written

def all_internal_calls( self) -> list[typing.Union[Function, slither.core.declarations.solidity_variables.SolidityFunction]]:
1214    def all_internal_calls(self) -> List["InternalCallType"]:
1215        """recursive version of internal_calls"""
1216        if self._all_internals_calls is None:
1217            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1218        return self._all_internals_calls

recursive version of internal_calls

def all_low_level_calls( self) -> list[tuple[typing.Union[slither.core.variables.variable.Variable, slither.core.declarations.solidity_variables.SolidityVariable], str]]:
1220    def all_low_level_calls(self) -> List["LowLevelCallType"]:
1221        """recursive version of low_level calls"""
1222        if self._all_low_level_calls is None:
1223            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1224        return self._all_low_level_calls

recursive version of low_level calls

def all_high_level_calls( self) -> list[tuple[slither.core.declarations.contract.Contract, typing.Union[Function, slither.core.variables.variable.Variable]]]:
1226    def all_high_level_calls(self) -> List["HighLevelCallType"]:
1227        """recursive version of high_level calls"""
1228        if self._all_high_level_calls is None:
1229            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1230        return self._all_high_level_calls

recursive version of high_level calls

def all_library_calls( self) -> list[tuple[slither.core.declarations.contract.Contract, Function]]:
1232    def all_library_calls(self) -> List["LibraryCallType"]:
1233        """recursive version of library calls"""
1234        if self._all_library_calls is None:
1235            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1236        return self._all_library_calls

recursive version of library calls

def all_solidity_calls( self) -> List[slither.core.declarations.solidity_variables.SolidityFunction]:
1238    def all_solidity_calls(self) -> List[SolidityFunction]:
1239        """recursive version of solidity calls"""
1240        if self._all_solidity_calls is None:
1241            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1242        return self._all_solidity_calls

recursive version of solidity calls

def all_conditional_state_variables_read( self, include_loop=True) -> list[slither.core.variables.state_variable.StateVariable]:
1249    def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]:
1250        """
1251        Return the state variable used in a condition
1252
1253        Over approximate and also return index access
1254        It won't work if the variable is assigned to a temp variable
1255        """
1256        if include_loop:
1257            if self._all_conditional_state_variables_read_with_loop is None:
1258                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1259                    lambda x: self._explore_func_cond_read(x, include_loop)
1260                )
1261            return self._all_conditional_state_variables_read_with_loop
1262        if self._all_conditional_state_variables_read is None:
1263            self._all_conditional_state_variables_read = self._explore_functions(
1264                lambda x: self._explore_func_cond_read(x, include_loop)
1265            )
1266        return self._all_conditional_state_variables_read

Return the state variable used in a condition

Over approximate and also return index access It won't work if the variable is assigned to a temp variable

def all_conditional_solidity_variables_read( self, include_loop: bool = True) -> List[slither.core.declarations.solidity_variables.SolidityVariable]:
1287    def all_conditional_solidity_variables_read(
1288        self, include_loop: bool = True
1289    ) -> List[SolidityVariable]:
1290        """
1291        Return the Soldiity variables directly used in a condtion
1292
1293        Use of the IR to filter index access
1294        Assumption: the solidity vars are used directly in the conditional node
1295        It won't work if the variable is assigned to a temp variable
1296        """
1297        if include_loop:
1298            if self._all_conditional_solidity_variables_read_with_loop is None:
1299                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1300                    lambda x: self._explore_func_conditional(
1301                        x, self._solidity_variable_in_binary, include_loop
1302                    )
1303                )
1304            return self._all_conditional_solidity_variables_read_with_loop
1305
1306        if self._all_conditional_solidity_variables_read is None:
1307            self._all_conditional_solidity_variables_read = self._explore_functions(
1308                lambda x: self._explore_func_conditional(
1309                    x, self._solidity_variable_in_binary, include_loop
1310                )
1311            )
1312        return self._all_conditional_solidity_variables_read

Return the Soldiity variables directly used in a condtion

Use of the IR to filter index access Assumption: the solidity vars are used directly in the conditional node It won't work if the variable is assigned to a temp variable

def all_solidity_variables_used_as_args( self) -> List[slither.core.declarations.solidity_variables.SolidityVariable]:
1331    def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]:
1332        """
1333        Return the Soldiity variables directly used in a call
1334
1335        Use of the IR to filter index access
1336        Used to catch check(msg.sender)
1337        """
1338        if self._all_solidity_variables_used_as_args is None:
1339            self._all_solidity_variables_used_as_args = self._explore_functions(
1340                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1341            )
1342        return self._all_solidity_variables_used_as_args

Return the Soldiity variables directly used in a call

Use of the IR to filter index access Used to catch check(msg.sender)

def apply_visitor(self, Visitor: Callable) -> List:
1351    def apply_visitor(self, Visitor: Callable) -> List:
1352        """
1353            Apply a visitor to all the function expressions
1354        Args:
1355            Visitor: slither.visitors
1356        Returns
1357            list(): results of the visit
1358        """
1359        expressions = self.expressions
1360        v = [Visitor(e).result() for e in expressions]
1361        return [item for sublist in v for item in sublist]

Apply a visitor to all the function expressions Args: Visitor: slither.visitors Returns list(): results of the visit

def get_local_variable_from_name( self, variable_name: str) -> Union[slither.core.variables.local_variable.LocalVariable, NoneType]:
1370    def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]:
1371        """
1372            Return a local variable from a name
1373
1374        Args:
1375            variable_name (str): name of the variable
1376        Returns:
1377            LocalVariable
1378        """
1379        return next((v for v in self.variables if v.name == variable_name), None)

Return a local variable from a name

Args: variable_name (str): name of the variable Returns: LocalVariable

def cfg_to_dot(self, filename: str):
1388    def cfg_to_dot(self, filename: str):
1389        """
1390            Export the function to a dot file
1391        Args:
1392            filename (str)
1393        """
1394        with open(filename, "w", encoding="utf8") as f:
1395            f.write("digraph{\n")
1396            for node in self.nodes:
1397                f.write(f'{node.node_id}[label="{str(node)}"];\n')
1398                for son in node.sons:
1399                    f.write(f"{node.node_id}->{son.node_id};\n")
1400
1401            f.write("}\n")

Export the function to a dot file Args: filename (str)

def dominator_tree_to_dot(self, filename: str):
1403    def dominator_tree_to_dot(self, filename: str):
1404        """
1405            Export the dominator tree of the function to a dot file
1406        Args:
1407            filename (str)
1408        """
1409
1410        def description(node):
1411            desc = f"{node}\n"
1412            desc += f"id: {node.node_id}"
1413            if node.dominance_frontier:
1414                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1415            return desc
1416
1417        with open(filename, "w", encoding="utf8") as f:
1418            f.write("digraph{\n")
1419            for node in self.nodes:
1420                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1421                if node.immediate_dominator:
1422                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1423
1424            f.write("}\n")

Export the dominator tree of the function to a dot file Args: filename (str)

def slithir_cfg_to_dot(self, filename: str):
1426    def slithir_cfg_to_dot(self, filename: str):
1427        """
1428        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1429        :param filename:
1430        :return:
1431        """
1432        content = self.slithir_cfg_to_dot_str()
1433        with open(filename, "w", encoding="utf8") as f:
1434            f.write(content)

Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs

Parameters
  • filename:
Returns
def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1436    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1437        """
1438        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1439        :return: the DOT content
1440        :rtype: str
1441        """
1442        from slither.core.cfg.node import NodeType
1443
1444        content = ""
1445        content += "digraph{\n"
1446        for node in self.nodes:
1447            label = f"Node Type: {node.type.value} {node.node_id}\n"
1448            if node.expression and not skip_expressions:
1449                label += f"\nEXPRESSION:\n{node.expression}\n"
1450            if node.irs and not skip_expressions:
1451                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1452            content += f'{node.node_id}[label="{label}"];\n'
1453            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1454                true_node = node.son_true
1455                if true_node:
1456                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1457                false_node = node.son_false
1458                if false_node:
1459                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1460            else:
1461                for son in node.sons:
1462                    content += f"{node.node_id}->{son.node_id};\n"
1463
1464        content += "}\n"
1465        return content

Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs

Returns

the DOT content

def is_reading(self, variable: slither.core.variables.variable.Variable) -> bool:
1474    def is_reading(self, variable: "Variable") -> bool:
1475        """
1476            Check if the function reads the variable
1477        Args:
1478            variable (Variable):
1479        Returns:
1480            bool: True if the variable is read
1481        """
1482        return variable in self.variables_read

Check if the function reads the variable Args: variable (Variable): Returns: bool: True if the variable is read

def is_reading_in_conditional_node(self, variable: slither.core.variables.variable.Variable) -> bool:
1484    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1485        """
1486            Check if the function reads the variable in a IF node
1487        Args:
1488            variable (Variable):
1489        Returns:
1490            bool: True if the variable is read
1491        """
1492        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1493        variables_read = [item for sublist in variables_reads for item in sublist]
1494        return variable in variables_read

Check if the function reads the variable in a IF node Args: variable (Variable): Returns: bool: True if the variable is read

def is_reading_in_require_or_assert(self, variable: slither.core.variables.variable.Variable) -> bool:
1496    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1497        """
1498            Check if the function reads the variable in an require or assert
1499        Args:
1500            variable (Variable):
1501        Returns:
1502            bool: True if the variable is read
1503        """
1504        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1505        variables_read = [item for sublist in variables_reads for item in sublist]
1506        return variable in variables_read

Check if the function reads the variable in an require or assert Args: variable (Variable): Returns: bool: True if the variable is read

def is_writing(self, variable: slither.core.variables.variable.Variable) -> bool:
1508    def is_writing(self, variable: "Variable") -> bool:
1509        """
1510            Check if the function writes the variable
1511        Args:
1512            variable (Variable):
1513        Returns:
1514            bool: True if the variable is written
1515        """
1516        return variable in self.variables_written

Check if the function writes the variable Args: variable (Variable): Returns: bool: True if the variable is written

@abstractmethod
def get_summary( self) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1518    @abstractmethod
1519    def get_summary(
1520        self,
1521    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1522        pass
def is_protected(self) -> bool:
1524    def is_protected(self) -> bool:
1525        """
1526            Determine if the function is protected using a check on msg.sender
1527
1528            Consider onlyOwner as a safe modifier.
1529            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1530            classify the function as protected
1531
1532            Otherwise only detects if msg.sender is directly used in a condition
1533            For example, it wont work for:
1534                address a = msg.sender
1535                require(a == owner)
1536        Returns
1537            (bool)
1538        """
1539
1540        if self._is_protected is None:
1541            if self.is_constructor:
1542                self._is_protected = True
1543                return True
1544            if "onlyOwner" in [m.name for m in self.modifiers]:
1545                self._is_protected = True
1546                return True
1547            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1548            args_vars = self.all_solidity_variables_used_as_args()
1549            self._is_protected = (
1550                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1551            )
1552        return self._is_protected

Determine if the function is protected using a check on msg.sender

Consider onlyOwner as a safe modifier.
If the owner functionality is incorrectly implemented, this will lead to incorrectly
classify the function as protected

Otherwise only detects if msg.sender is directly used in a condition
For example, it wont work for:
    address a = msg.sender
    require(a == owner)

Returns (bool)

is_reentrant: bool
1554    @property
1555    def is_reentrant(self) -> bool:
1556        """
1557        Determine if the function can be re-entered
1558        """
1559        reentrancy_modifier = "nonReentrant"
1560
1561        if self.function_language == FunctionLanguage.Vyper:
1562            reentrancy_modifier = "nonreentrant(lock)"
1563
1564        # TODO: compare with hash of known nonReentrant modifier instead of the name
1565        if reentrancy_modifier in [m.name for m in self.modifiers]:
1566            return False
1567
1568        if self.visibility in ["public", "external"]:
1569            return True
1570
1571        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1572        all_entry_points = [
1573            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1574        ]
1575        if not all_entry_points:
1576            return True
1577        return not all(
1578            (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points)
1579        )

Determine if the function can be re-entered

def new_node( self, node_type: slither.core.cfg.node.NodeType, src: Union[str, Dict], scope: Union[slither.core.cfg.scope.Scope, Function]) -> slither.core.cfg.node.Node:
1688    def new_node(
1689        self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]
1690    ) -> "Node":
1691        from slither.core.cfg.node import Node
1692
1693        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1694        node.set_offset(src, self.compilation_unit)
1695        self._counter_nodes += 1
1696        node.set_function(self)
1697        self._nodes.append(node)
1698
1699        return node
def get_last_ssa_state_variables_instances( self) -> dict[str, set[slither.slithir.variables.variable.SlithIRVariable]]:
1767    def get_last_ssa_state_variables_instances(
1768        self,
1769    ) -> Dict[str, Set["SlithIRVariable"]]:
1770        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
def get_last_ssa_local_variables_instances( self) -> dict[str, set[slither.slithir.variables.variable.SlithIRVariable]]:
1772    def get_last_ssa_local_variables_instances(
1773        self,
1774    ) -> Dict[str, Set["SlithIRVariable"]]:
1775        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
def fix_phi( self, last_state_variables_instances: dict[str, list[slither.core.variables.state_variable.StateVariable]], initial_state_variables_instances: dict[str, slither.core.variables.state_variable.StateVariable]) -> None:
1787    def fix_phi(
1788        self,
1789        last_state_variables_instances: Dict[str, List["StateVariable"]],
1790        initial_state_variables_instances: Dict[str, "StateVariable"],
1791    ) -> None:
1792        from slither.slithir.operations import InternalCall, PhiCallback
1793        from slither.slithir.variables import Constant, StateIRVariable
1794
1795        for node in self.nodes:
1796            for ir in node.irs_ssa:
1797                if node == self.entry_point:
1798                    if isinstance(ir.lvalue, StateIRVariable):
1799                        additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1800                        additional += last_state_variables_instances[ir.lvalue.canonical_name]
1801                        ir.rvalues = list(set(additional + ir.rvalues))
1802                    # function parameter
1803                    else:
1804                        # find index of the parameter
1805                        idx = self.parameters.index(ir.lvalue.non_ssa_version)
1806                        # find non ssa version of that index
1807                        additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1808                        additional = unroll(additional)
1809                        additional = [a for a in additional if not isinstance(a, Constant)]
1810                        ir.rvalues = list(set(additional + ir.rvalues))
1811                if isinstance(ir, PhiCallback):
1812                    callee_ir = ir.callee_ir
1813                    if isinstance(callee_ir, InternalCall):
1814                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1815                        if ir.lvalue.canonical_name in last_ssa:
1816                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1817                        else:
1818                            ir.rvalues = [ir.lvalue]
1819                    else:
1820                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1821                        ir.rvalues = list(set(additional + ir.rvalues))
1822
1823            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
def generate_slithir_and_analyze(self) -> None:
1825    def generate_slithir_and_analyze(self) -> None:
1826
1827        for node in self.nodes:
1828            node.slithir_generation()
1829
1830        self._analyze_read_write()
1831        self._analyze_calls()
@abstractmethod
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1833    @abstractmethod
1834    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1835        pass
def update_read_write_using_ssa(self) -> None:
1837    def update_read_write_using_ssa(self) -> None:
1838        for node in self.nodes:
1839            node.update_read_write_using_ssa()
1840        self._analyze_read_write()