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.core.declarations import Contract, FunctionContract
  35    from slither.core.cfg.node import Node, NodeType
  36    from slither.core.variables.variable import Variable
  37    from slither.slithir.variables.variable import SlithIRVariable
  38    from slither.slithir.variables import LocalIRVariable
  39    from slither.core.expressions.expression import Expression
  40    from slither.slithir.operations import (
  41        HighLevelCall,
  42        InternalCall,
  43        LibraryCall,
  44        LowLevelCall,
  45        SolidityCall,
  46        Operation,
  47    )
  48    from slither.core.compilation_unit import SlitherCompilationUnit
  49    from slither.core.scope.scope import FileScope
  50
  51LOGGER = logging.getLogger("Function")
  52ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
  53
  54
  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
  85
  86
  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
  94
  95
  96def _filter_state_variables_written(expressions: List["Expression"]):
  97    ret = []
  98
  99    for expression in expressions:
 100        if isinstance(expression, (Identifier, UnaryOperation, MemberAccess)):
 101            ret.append(expression.expression)
 102        elif isinstance(expression, IndexAccess):
 103            ret.append(expression.expression_left)
 104    return ret
 105
 106
 107class FunctionLanguage(Enum):
 108    Solidity = 0
 109    Yul = 1
 110    Vyper = 2
 111
 112
 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["InternalCall"] = []
 154        self._solidity_calls: List["SolidityCall"] = []
 155        self._low_level_calls: List["LowLevelCall"] = []
 156        self._high_level_calls: List[Tuple["Contract", "HighLevelCall"]] = []
 157        self._library_calls: List["LibraryCall"] = []
 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["InternalCall"]] = None
 174        self._all_high_level_calls: Optional[List[Tuple["Contract", "HighLevelCall"]]] = None
 175        self._all_library_calls: Optional[List["LibraryCall"]] = None
 176        self._all_low_level_calls: Optional[List["LowLevelCall"]] = None
 177        self._all_solidity_calls: Optional[List["SolidityCall"]] = 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 parameters 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 parameters 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["InternalCall"]:
 862        """
 863        list(InternalCall): List of IR operations for internal calls
 864        """
 865        return list(self._internal_calls)
 866
 867    @property
 868    def solidity_calls(self) -> List["SolidityCall"]:
 869        """
 870        list(SolidityCall): List of IR operations for Solidity calls
 871        """
 872        return list(self._solidity_calls)
 873
 874    @property
 875    def high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
 876        """
 877        list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
 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["LibraryCall"]:
 885        """
 886        list(LibraryCall): List of IR operations for library calls
 887        """
 888        return list(self._library_calls)
 889
 890    @property
 891    def low_level_calls(self) -> List["LowLevelCall"]:
 892        """
 893        list(LowLevelCall): List of IR operations for low level calls
 894        A low level call is defined by
 895        - the variable called
 896        - the name of the function (call/delegatecall/callcode)
 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            ir.function
1125            for ir in self.internal_calls
1126            if isinstance(ir.function, Function) and ir.function not in explored
1127        ]
1128        to_explore += [
1129            ir.function
1130            for ir in self.library_calls
1131            if isinstance(ir.function, Function) and ir.function not in explored
1132        ]
1133        to_explore += [m for m in self.modifiers if m not in explored]
1134
1135        while to_explore:
1136            f = to_explore[0]
1137            to_explore = to_explore[1:]
1138            if f in explored:
1139                continue
1140            explored.append(f)
1141
1142            values += f_new_values(f)
1143
1144            to_explore += [
1145                ir.function
1146                for ir in f.internal_calls
1147                if isinstance(ir.function, Function)
1148                and ir.function not in explored
1149                and ir.function not in to_explore
1150            ]
1151            to_explore += [
1152                ir.function
1153                for ir in f.library_calls
1154                if isinstance(ir.function, Function)
1155                and ir.function not in explored
1156                and ir.function not in to_explore
1157            ]
1158            to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
1159
1160        return list(set(values))
1161
1162    def all_variables_read(self) -> List["Variable"]:
1163        """recursive version of variables_read"""
1164        if self._all_variables_read is None:
1165            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1166        return self._all_variables_read
1167
1168    def all_variables_written(self) -> List["Variable"]:
1169        """recursive version of variables_written"""
1170        if self._all_variables_written is None:
1171            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1172        return self._all_variables_written
1173
1174    def all_state_variables_read(self) -> List["StateVariable"]:
1175        """recursive version of variables_read"""
1176        if self._all_state_variables_read is None:
1177            self._all_state_variables_read = self._explore_functions(
1178                lambda x: x.state_variables_read
1179            )
1180        return self._all_state_variables_read
1181
1182    def all_solidity_variables_read(self) -> List[SolidityVariable]:
1183        """recursive version of solidity_read"""
1184        if self._all_solidity_variables_read is None:
1185            self._all_solidity_variables_read = self._explore_functions(
1186                lambda x: x.solidity_variables_read
1187            )
1188        return self._all_solidity_variables_read
1189
1190    def all_slithir_variables(self) -> List["SlithIRVariable"]:
1191        """recursive version of slithir_variables"""
1192        if self._all_slithir_variables is None:
1193            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1194        return self._all_slithir_variables
1195
1196    def all_nodes(self) -> List["Node"]:
1197        """recursive version of nodes"""
1198        if self._all_nodes is None:
1199            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1200        return self._all_nodes
1201
1202    def all_expressions(self) -> List["Expression"]:
1203        """recursive version of variables_read"""
1204        if self._all_expressions is None:
1205            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1206        return self._all_expressions
1207
1208    def all_slithir_operations(self) -> List["Operation"]:
1209        if self._all_slithir_operations is None:
1210            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1211        return self._all_slithir_operations
1212
1213    def all_state_variables_written(self) -> List[StateVariable]:
1214        """recursive version of variables_written"""
1215        if self._all_state_variables_written is None:
1216            self._all_state_variables_written = self._explore_functions(
1217                lambda x: x.state_variables_written
1218            )
1219        return self._all_state_variables_written
1220
1221    def all_internal_calls(self) -> List["InternalCall"]:
1222        """recursive version of internal_calls"""
1223        if self._all_internals_calls is None:
1224            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1225        return self._all_internals_calls
1226
1227    def all_low_level_calls(self) -> List["LowLevelCall"]:
1228        """recursive version of low_level calls"""
1229        if self._all_low_level_calls is None:
1230            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1231        return self._all_low_level_calls
1232
1233    def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
1234        """recursive version of high_level calls"""
1235        if self._all_high_level_calls is None:
1236            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1237        return self._all_high_level_calls
1238
1239    def all_library_calls(self) -> List["LibraryCall"]:
1240        """recursive version of library calls"""
1241        if self._all_library_calls is None:
1242            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1243        return self._all_library_calls
1244
1245    def all_solidity_calls(self) -> List["SolidityCall"]:
1246        """recursive version of solidity calls"""
1247        if self._all_solidity_calls is None:
1248            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1249        return self._all_solidity_calls
1250
1251    @staticmethod
1252    def _explore_func_cond_read(func: "Function", include_loop: bool) -> List["StateVariable"]:
1253        ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]
1254        return [item for sublist in ret for item in sublist]
1255
1256    def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]:
1257        """
1258        Return the state variable used in a condition
1259
1260        Over approximate and also return index access
1261        It won't work if the variable is assigned to a temp variable
1262        """
1263        if include_loop:
1264            if self._all_conditional_state_variables_read_with_loop is None:
1265                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1266                    lambda x: self._explore_func_cond_read(x, include_loop)
1267                )
1268            return self._all_conditional_state_variables_read_with_loop
1269        if self._all_conditional_state_variables_read is None:
1270            self._all_conditional_state_variables_read = self._explore_functions(
1271                lambda x: self._explore_func_cond_read(x, include_loop)
1272            )
1273        return self._all_conditional_state_variables_read
1274
1275    @staticmethod
1276    def _solidity_variable_in_binary(node: "Node") -> List[SolidityVariable]:
1277        from slither.slithir.operations.binary import Binary
1278
1279        ret = []
1280        for ir in node.irs:
1281            if isinstance(ir, Binary):
1282                ret += ir.read
1283        return [var for var in ret if isinstance(var, SolidityVariable)]
1284
1285    @staticmethod
1286    def _explore_func_conditional(
1287        func: "Function",
1288        f: Callable[["Node"], List[SolidityVariable]],
1289        include_loop: bool,
1290    ) -> List[Any]:
1291        ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
1292        return [item for sublist in ret for item in sublist]
1293
1294    def all_conditional_solidity_variables_read(
1295        self, include_loop: bool = True
1296    ) -> List[SolidityVariable]:
1297        """
1298        Return the Soldiity variables directly used in a condtion
1299
1300        Use of the IR to filter index access
1301        Assumption: the solidity vars are used directly in the conditional node
1302        It won't work if the variable is assigned to a temp variable
1303        """
1304        if include_loop:
1305            if self._all_conditional_solidity_variables_read_with_loop is None:
1306                self._all_conditional_solidity_variables_read_with_loop = 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_with_loop
1312
1313        if self._all_conditional_solidity_variables_read is None:
1314            self._all_conditional_solidity_variables_read = self._explore_functions(
1315                lambda x: self._explore_func_conditional(
1316                    x, self._solidity_variable_in_binary, include_loop
1317                )
1318            )
1319        return self._all_conditional_solidity_variables_read
1320
1321    @staticmethod
1322    def _solidity_variable_in_internal_calls(node: "Node") -> List[SolidityVariable]:
1323        from slither.slithir.operations.internal_call import InternalCall
1324
1325        ret = []
1326        for ir in node.irs:
1327            if isinstance(ir, InternalCall):
1328                ret += ir.read
1329        return [var for var in ret if isinstance(var, SolidityVariable)]
1330
1331    @staticmethod
1332    def _explore_func_nodes(
1333        func: "Function", f: Callable[["Node"], List[SolidityVariable]]
1334    ) -> List[Union[Any, SolidityVariableComposed]]:
1335        ret = [f(n) for n in func.nodes]
1336        return [item for sublist in ret for item in sublist]
1337
1338    def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]:
1339        """
1340        Return the Soldiity variables directly used in a call
1341
1342        Use of the IR to filter index access
1343        Used to catch check(msg.sender)
1344        """
1345        if self._all_solidity_variables_used_as_args is None:
1346            self._all_solidity_variables_used_as_args = self._explore_functions(
1347                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1348            )
1349        return self._all_solidity_variables_used_as_args
1350
1351    # endregion
1352    ###################################################################################
1353    ###################################################################################
1354    # region Visitor
1355    ###################################################################################
1356    ###################################################################################
1357
1358    def apply_visitor(self, Visitor: Callable) -> List:
1359        """
1360            Apply a visitor to all the function expressions
1361        Args:
1362            Visitor: slither.visitors
1363        Returns
1364            list(): results of the visit
1365        """
1366        expressions = self.expressions
1367        v = [Visitor(e).result() for e in expressions]
1368        return [item for sublist in v for item in sublist]
1369
1370    # endregion
1371    ###################################################################################
1372    ###################################################################################
1373    # region Getters from/to object
1374    ###################################################################################
1375    ###################################################################################
1376
1377    def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]:
1378        """
1379            Return a local variable from a name
1380
1381        Args:
1382            variable_name (str): name of the variable
1383        Returns:
1384            LocalVariable
1385        """
1386        return next((v for v in self.variables if v.name == variable_name), None)
1387
1388    # endregion
1389    ###################################################################################
1390    ###################################################################################
1391    # region Export
1392    ###################################################################################
1393    ###################################################################################
1394
1395    def cfg_to_dot(self, filename: str):
1396        """
1397            Export the function to a dot file
1398        Args:
1399            filename (str)
1400        """
1401        with open(filename, "w", encoding="utf8") as f:
1402            f.write("digraph{\n")
1403            for node in self.nodes:
1404                f.write(f'{node.node_id}[label="{str(node)}"];\n')
1405                for son in node.sons:
1406                    f.write(f"{node.node_id}->{son.node_id};\n")
1407
1408            f.write("}\n")
1409
1410    def dominator_tree_to_dot(self, filename: str):
1411        """
1412            Export the dominator tree of the function to a dot file
1413        Args:
1414            filename (str)
1415        """
1416
1417        def description(node):
1418            desc = f"{node}\n"
1419            desc += f"id: {node.node_id}"
1420            if node.dominance_frontier:
1421                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1422            return desc
1423
1424        with open(filename, "w", encoding="utf8") as f:
1425            f.write("digraph{\n")
1426            for node in self.nodes:
1427                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1428                if node.immediate_dominator:
1429                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1430
1431            f.write("}\n")
1432
1433    def slithir_cfg_to_dot(self, filename: str):
1434        """
1435        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1436        :param filename:
1437        :return:
1438        """
1439        content = self.slithir_cfg_to_dot_str()
1440        with open(filename, "w", encoding="utf8") as f:
1441            f.write(content)
1442
1443    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1444        """
1445        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1446        :return: the DOT content
1447        :rtype: str
1448        """
1449        from slither.core.cfg.node import NodeType
1450
1451        content = ""
1452        content += "digraph{\n"
1453        for node in self.nodes:
1454            label = f"Node Type: {node.type.value} {node.node_id}\n"
1455            if node.expression and not skip_expressions:
1456                label += f"\nEXPRESSION:\n{node.expression}\n"
1457            if node.irs and not skip_expressions:
1458                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1459            content += f'{node.node_id}[label="{label}"];\n'
1460            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1461                true_node = node.son_true
1462                if true_node:
1463                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1464                false_node = node.son_false
1465                if false_node:
1466                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1467            else:
1468                for son in node.sons:
1469                    content += f"{node.node_id}->{son.node_id};\n"
1470
1471        content += "}\n"
1472        return content
1473
1474    # endregion
1475    ###################################################################################
1476    ###################################################################################
1477    # region Summary information
1478    ###################################################################################
1479    ###################################################################################
1480
1481    def is_reading(self, variable: "Variable") -> bool:
1482        """
1483            Check if the function reads the variable
1484        Args:
1485            variable (Variable):
1486        Returns:
1487            bool: True if the variable is read
1488        """
1489        return variable in self.variables_read
1490
1491    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1492        """
1493            Check if the function reads the variable in a IF node
1494        Args:
1495            variable (Variable):
1496        Returns:
1497            bool: True if the variable is read
1498        """
1499        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1500        variables_read = [item for sublist in variables_reads for item in sublist]
1501        return variable in variables_read
1502
1503    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1504        """
1505            Check if the function reads the variable in an require or assert
1506        Args:
1507            variable (Variable):
1508        Returns:
1509            bool: True if the variable is read
1510        """
1511        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1512        variables_read = [item for sublist in variables_reads for item in sublist]
1513        return variable in variables_read
1514
1515    def is_writing(self, variable: "Variable") -> bool:
1516        """
1517            Check if the function writes the variable
1518        Args:
1519            variable (Variable):
1520        Returns:
1521            bool: True if the variable is written
1522        """
1523        return variable in self.variables_written
1524
1525    @abstractmethod
1526    def get_summary(
1527        self,
1528    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1529        pass
1530
1531    def is_protected(self) -> bool:
1532        """
1533            Determine if the function is protected using a check on msg.sender
1534
1535            Consider onlyOwner as a safe modifier.
1536            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1537            classify the function as protected
1538
1539            Otherwise only detects if msg.sender is directly used in a condition
1540            For example, it wont work for:
1541                address a = msg.sender
1542                require(a == owner)
1543        Returns
1544            (bool)
1545        """
1546
1547        if self._is_protected is None:
1548            if self.is_constructor:
1549                self._is_protected = True
1550                return True
1551            if "onlyOwner" in [m.name for m in self.modifiers]:
1552                self._is_protected = True
1553                return True
1554            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1555            args_vars = self.all_solidity_variables_used_as_args()
1556            self._is_protected = (
1557                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1558            )
1559        return self._is_protected
1560
1561    @property
1562    def is_reentrant(self) -> bool:
1563        """
1564        Determine if the function can be re-entered
1565        """
1566        reentrancy_modifier = "nonReentrant"
1567
1568        if self.function_language == FunctionLanguage.Vyper:
1569            reentrancy_modifier = "nonreentrant(lock)"
1570
1571        # TODO: compare with hash of known nonReentrant modifier instead of the name
1572        if reentrancy_modifier in [m.name for m in self.modifiers]:
1573            return False
1574
1575        if self.visibility in ["public", "external"]:
1576            return True
1577
1578        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1579        all_entry_points = [
1580            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1581        ]
1582        if not all_entry_points:
1583            return True
1584        return not all(
1585            (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points)
1586        )
1587
1588    # endregion
1589    ###################################################################################
1590    ###################################################################################
1591    # region Analyses
1592    ###################################################################################
1593    ###################################################################################
1594
1595    def _analyze_read_write(self) -> None:
1596        """Compute variables read/written/..."""
1597        write_var = [x.variables_written_as_expression for x in self.nodes]
1598        write_var = [x for x in write_var if x]
1599        write_var = [item for sublist in write_var for item in sublist]
1600        write_var = list(set(write_var))
1601        # Remove duplicate if they share the same string representation
1602        write_var = [
1603            next(obj)
1604            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1605        ]
1606        self._expression_vars_written = write_var
1607
1608        write_var = [x.variables_written for x in self.nodes]
1609        write_var = [x for x in write_var if x]
1610        write_var = [item for sublist in write_var for item in sublist]
1611        write_var = list(set(write_var))
1612        # Remove duplicate if they share the same string representation
1613        write_var = [
1614            next(obj)
1615            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1616        ]
1617        self._vars_written = write_var
1618
1619        read_var = [x.variables_read_as_expression for x in self.nodes]
1620        read_var = [x for x in read_var if x]
1621        read_var = [item for sublist in read_var for item in sublist]
1622        # Remove duplicate if they share the same string representation
1623        read_var = [
1624            next(obj)
1625            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1626        ]
1627        self._expression_vars_read = read_var
1628
1629        read_var = [x.variables_read for x in self.nodes]
1630        read_var = [x for x in read_var if x]
1631        read_var = [item for sublist in read_var for item in sublist]
1632        # Remove duplicate if they share the same string representation
1633        read_var = [
1634            next(obj)
1635            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1636        ]
1637        self._vars_read = read_var
1638
1639        self._state_vars_written = [
1640            x for x in self.variables_written if isinstance(x, StateVariable)
1641        ]
1642        self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)]
1643        self._solidity_vars_read = [
1644            x for x in self.variables_read if isinstance(x, SolidityVariable)
1645        ]
1646
1647        self._vars_read_or_written = self._vars_written + self._vars_read
1648
1649        slithir_variables = [x.slithir_variables for x in self.nodes]
1650        slithir_variables = [x for x in slithir_variables if x]
1651        self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
1652
1653    def _analyze_calls(self) -> None:
1654        calls = [x.calls_as_expression for x in self.nodes]
1655        calls = [x for x in calls if x]
1656        calls = [item for sublist in calls for item in sublist]
1657        self._expression_calls = list(set(calls))
1658
1659        internal_calls = [x.internal_calls for x in self.nodes]
1660        internal_calls = [x for x in internal_calls if x]
1661        internal_calls = [item for sublist in internal_calls for item in sublist]
1662        self._internal_calls = list(set(internal_calls))
1663
1664        self._solidity_calls = [
1665            ir for ir in internal_calls if isinstance(ir.function, SolidityFunction)
1666        ]
1667
1668        low_level_calls = [x.low_level_calls for x in self.nodes]
1669        low_level_calls = [x for x in low_level_calls if x]
1670        low_level_calls = [item for sublist in low_level_calls for item in sublist]
1671        self._low_level_calls = list(set(low_level_calls))
1672
1673        high_level_calls = [x.high_level_calls for x in self.nodes]
1674        high_level_calls = [x for x in high_level_calls if x]
1675        high_level_calls = [item for sublist in high_level_calls for item in sublist]
1676        self._high_level_calls = list(set(high_level_calls))
1677
1678        library_calls = [x.library_calls for x in self.nodes]
1679        library_calls = [x for x in library_calls if x]
1680        library_calls = [item for sublist in library_calls for item in sublist]
1681        self._library_calls = list(set(library_calls))
1682
1683        external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
1684        external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
1685        external_calls_as_expressions = [
1686            item for sublist in external_calls_as_expressions for item in sublist
1687        ]
1688        self._external_calls_as_expressions = list(set(external_calls_as_expressions))
1689
1690    # endregion
1691    ###################################################################################
1692    ###################################################################################
1693    # region Nodes
1694    ###################################################################################
1695    ###################################################################################
1696
1697    def new_node(
1698        self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]
1699    ) -> "Node":
1700        from slither.core.cfg.node import Node
1701
1702        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1703        node.set_offset(src, self.compilation_unit)
1704        self._counter_nodes += 1
1705        node.set_function(self)
1706        self._nodes.append(node)
1707
1708        return node
1709
1710    # endregion
1711    ###################################################################################
1712    ###################################################################################
1713    # region SlithIr and SSA
1714    ###################################################################################
1715    ###################################################################################
1716
1717    def _get_last_ssa_variable_instances(
1718        self, target_state: bool, target_local: bool
1719    ) -> Dict[str, Set["SlithIRVariable"]]:
1720        # pylint: disable=too-many-locals,too-many-branches
1721        from slither.slithir.variables import ReferenceVariable
1722        from slither.slithir.operations import OperationWithLValue
1723        from slither.core.cfg.node import NodeType
1724
1725        if not self.is_implemented:
1726            return {}
1727
1728        if self._entry_point is None:
1729            return {}
1730        # node, values
1731        to_explore: List[Tuple["Node", Dict]] = [(self._entry_point, {})]
1732        # node -> values
1733        explored: Dict = {}
1734        # name -> instances
1735        ret: Dict = {}
1736
1737        while to_explore:
1738            node, values = to_explore[0]
1739            to_explore = to_explore[1::]
1740
1741            if node.type != NodeType.ENTRYPOINT:
1742                for ir_ssa in node.irs_ssa:
1743                    if isinstance(ir_ssa, OperationWithLValue):
1744                        lvalue = ir_ssa.lvalue
1745                        if isinstance(lvalue, ReferenceVariable):
1746                            lvalue = lvalue.points_to_origin
1747                        if isinstance(lvalue, StateVariable) and target_state:
1748                            values[lvalue.canonical_name] = {lvalue}
1749                        if isinstance(lvalue, LocalVariable) and target_local:
1750                            values[lvalue.canonical_name] = {lvalue}
1751
1752            # Check for fixpoint
1753            if node in explored:
1754                if values == explored[node]:
1755                    continue
1756                for k, instances in values.items():
1757                    if k not in explored[node]:
1758                        explored[node][k] = set()
1759                    explored[node][k] |= instances
1760                values = explored[node]
1761            else:
1762                explored[node] = values
1763
1764            # Return condition
1765            if node.will_return:
1766                for name, instances in values.items():
1767                    if name not in ret:
1768                        ret[name] = set()
1769                    ret[name] |= instances
1770
1771            for son in node.sons:
1772                to_explore.append((son, dict(values)))
1773
1774        return ret
1775
1776    def get_last_ssa_state_variables_instances(
1777        self,
1778    ) -> Dict[str, Set["SlithIRVariable"]]:
1779        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
1780
1781    def get_last_ssa_local_variables_instances(
1782        self,
1783    ) -> Dict[str, Set["SlithIRVariable"]]:
1784        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
1785
1786    @staticmethod
1787    def _unchange_phi(ir: "Operation") -> bool:
1788        from slither.slithir.operations import Phi, PhiCallback
1789
1790        if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
1791            return False
1792        if not ir.rvalues:
1793            return True
1794        return ir.rvalues[0] == ir.lvalue
1795
1796    def fix_phi(
1797        self,
1798        last_state_variables_instances: Dict[str, List["StateVariable"]],
1799        initial_state_variables_instances: Dict[str, "StateVariable"],
1800    ) -> None:
1801        from slither.slithir.operations import InternalCall, PhiCallback
1802        from slither.slithir.variables import Constant, StateIRVariable
1803
1804        for node in self.nodes:
1805            for ir in node.irs_ssa:
1806                if node == self.entry_point:
1807                    if isinstance(ir.lvalue, StateIRVariable):
1808                        additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1809                        additional += last_state_variables_instances[ir.lvalue.canonical_name]
1810                        ir.rvalues = list(set(additional + ir.rvalues))
1811                    # function parameter
1812                    else:
1813                        # find index of the parameter
1814                        idx = self.parameters.index(ir.lvalue.non_ssa_version)
1815                        # find non ssa version of that index
1816                        additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1817                        additional = unroll(additional)
1818                        additional = [a for a in additional if not isinstance(a, Constant)]
1819                        ir.rvalues = list(set(additional + ir.rvalues))
1820                if isinstance(ir, PhiCallback):
1821                    callee_ir = ir.callee_ir
1822                    if isinstance(callee_ir, InternalCall):
1823                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1824                        if ir.lvalue.canonical_name in last_ssa:
1825                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1826                        else:
1827                            ir.rvalues = [ir.lvalue]
1828                    else:
1829                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1830                        ir.rvalues = list(set(additional + ir.rvalues))
1831
1832            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
1833
1834    def generate_slithir_and_analyze(self) -> None:
1835
1836        for node in self.nodes:
1837            node.slithir_generation()
1838
1839        self._analyze_read_write()
1840        self._analyze_calls()
1841
1842    @abstractmethod
1843    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1844        pass
1845
1846    def update_read_write_using_ssa(self) -> None:
1847        for node in self.nodes:
1848            node.update_read_write_using_ssa()
1849        self._analyze_read_write()
1850
1851    ###################################################################################
1852    ###################################################################################
1853    # region Built in definitions
1854    ###################################################################################
1855    ###################################################################################
1856
1857    def __str__(self) -> str:
1858        return self.name
1859
1860    # 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:
56class ModifierStatements:
57    def __init__(
58        self,
59        modifier: Union["Contract", "Function"],
60        entry_point: "Node",
61        nodes: List["Node"],
62    ) -> None:
63        self._modifier = modifier
64        self._entry_point = entry_point
65        self._nodes = nodes
66
67    @property
68    def modifier(self) -> Union["Contract", "Function"]:
69        return self._modifier
70
71    @property
72    def entry_point(self) -> "Node":
73        return self._entry_point
74
75    @entry_point.setter
76    def entry_point(self, entry_point: "Node"):
77        self._entry_point = entry_point
78
79    @property
80    def nodes(self) -> List["Node"]:
81        return self._nodes
82
83    @nodes.setter
84    def nodes(self, nodes: List["Node"]):
85        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])
57    def __init__(
58        self,
59        modifier: Union["Contract", "Function"],
60        entry_point: "Node",
61        nodes: List["Node"],
62    ) -> None:
63        self._modifier = modifier
64        self._entry_point = entry_point
65        self._nodes = nodes
67    @property
68    def modifier(self) -> Union["Contract", "Function"]:
69        return self._modifier
entry_point: slither.core.cfg.node.Node
71    @property
72    def entry_point(self) -> "Node":
73        return self._entry_point
nodes: list[slither.core.cfg.node.Node]
79    @property
80    def nodes(self) -> List["Node"]:
81        return self._nodes
class FunctionType(enum.Enum):
88class FunctionType(Enum):
89    NORMAL = 0
90    CONSTRUCTOR = 1
91    FALLBACK = 2
92    RECEIVE = 3
93    CONSTRUCTOR_VARIABLES = 10  # Fake function to hold variable declaration statements
94    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):
108class FunctionLanguage(Enum):
109    Solidity = 0
110    Yul = 1
111    Vyper = 2

An enumeration.

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

Function class

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

str: function name

internal_scope: List[str]
261    @property
262    def internal_scope(self) -> List[str]:
263        """
264        Return a list of name representing the scope of the function
265        This is used to model nested functions declared in YUL
266
267        :return:
268        """
269        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
275    @property
276    def full_name(self) -> str:
277        """
278        str: func_name(type1,type2)
279        Return the function signature without the return values
280        The difference between this function and solidity_function is that full_name does not translate the underlying
281        type (ex: structure, contract to address, ...)
282        """
283        if self._full_name is None:
284            name, parameters, _ = self.signature
285            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
286            self._full_name = full_name
287        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
289    @property
290    @abstractmethod
291    def canonical_name(self) -> str:
292        """
293        str: contract.func_name(type1,type2)
294        Return the function signature without the return values
295        """
296        return ""

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

contains_assembly: bool
298    @property
299    def contains_assembly(self) -> bool:
300        return self._contains_assembly
def can_reenter( self, callstack: Union[list[Union[Function, slither.core.variables.variable.Variable]], NoneType] = None) -> bool:
306    def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool:
307        """
308        Check if the function can re-enter
309        Follow internal calls.
310        Do not consider CREATE as potential re-enter, but check if the
311        destination's constructor can contain a call (recurs. follow nested CREATE)
312        For Solidity > 0.5, filter access to public variables and constant/pure/view
313        For call to this. check if the destination can re-enter
314        Do not consider Send/Transfer as there is not enough gas
315        :param callstack: used internally to check for recursion
316        :return bool:
317        """
318        from slither.slithir.operations import Call
319
320        if self._can_reenter is None:
321            self._can_reenter = False
322            for ir in self.all_slithir_operations():
323                if isinstance(ir, Call) and ir.can_reenter(callstack):
324                    self._can_reenter = True
325                    return True
326        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:
328    def can_send_eth(self) -> bool:
329        """
330        Check if the function or any internal (not external) functions called by it can send eth
331        :return bool:
332        """
333        from slither.slithir.operations import Call
334
335        if self._can_send_eth is None:
336            self._can_send_eth = False
337            for ir in self.all_slithir_operations():
338                if isinstance(ir, Call) and ir.can_send_eth():
339                    self._can_send_eth = True
340                    return True
341        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
343    @property
344    def is_checked(self) -> bool:
345        """
346        Return true if the overflow are enabled by default
347
348
349        :return:
350        """
351
352        return self.compilation_unit.solc_version >= "0.8.0"

Return true if the overflow are enabled by default

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

Return the reference ID of the function, if available.

Returns

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

bool: True if the function is the constructor

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

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

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

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

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

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

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

bool: True if the function is payable

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

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

is_override: bool
465    @property
466    def is_override(self) -> bool:
467        """
468        Note for Solidity < 0.6.0 it will always be false
469        bool: True if the function overrides a base function
470        """
471        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

473    @property
474    def overridden_by(self) -> List["FunctionContract"]:
475        """
476        List["FunctionContract"]: List of functions in child contracts that override this function
477        This may include distinct instances of the same function due to inheritance
478        """
479        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

481    @property
482    def overrides(self) -> List["FunctionContract"]:
483        """
484        List["FunctionContract"]: List of functions in parent contracts that this function overrides
485        This may include distinct instances of the same function due to inheritance
486        """
487        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
496    @property
497    def visibility(self) -> str:
498        """
499        str: Function visibility
500        """
501        assert self._visibility is not None
502        return self._visibility

str: Function visibility

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

bool: True if the function is declared as view

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

bool: True if the function is declared as pure

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

bool: True if the function is implemented

is_empty: bool
567    @property
568    def is_empty(self) -> bool:
569        """
570        bool: True if the function is empty, None if the function is an interface
571        """
572        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]
585    @property
586    def nodes(self) -> List["Node"]:
587        """
588        list(Node): List of the nodes
589        """
590        return list(self._nodes)

list(Node): List of the nodes

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

Node: Entry point of the function

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

list(LocalVariable): List of the parameters

def add_parameters(self, p: slither.core.variables.local_variable.LocalVariable) -> None:
651    def add_parameters(self, p: "LocalVariable") -> None:
652        self._parameters.append(p)
654    @property
655    def parameters_ssa(self) -> List["LocalIRVariable"]:
656        """
657        list(LocalIRVariable): List of the parameters (SSA form)
658        """
659        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:
661    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
662        self._parameters_ssa.append(var)
def parameters_src(self) -> slither.core.source_mapping.source_mapping.SourceMapping:
664    def parameters_src(self) -> SourceMapping:
665        return self._parameters_src
return_type: Union[List[slither.core.solidity_types.type.Type], NoneType]
674    @property
675    def return_type(self) -> Optional[List[Type]]:
676        """
677        Return the list of return type
678        If no return, return None
679        """
680        returns = self.returns
681        if returns:
682            return [r.type for r in returns]
683        return None

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

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

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

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

list(LocalVariable): List of the return variables

def add_return(self, r: slither.core.variables.local_variable.LocalVariable) -> None:
704    def add_return(self, r: "LocalVariable") -> None:
705        self._returns.append(r)
707    @property
708    def returns_ssa(self) -> List["LocalIRVariable"]:
709        """
710        list(LocalIRVariable): List of the return variables (SSA form)
711        """
712        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:
714    def add_return_ssa(self, var: "LocalIRVariable") -> None:
715        self._returns_ssa.append(var)
modifiers: list[typing.Union[slither.core.declarations.contract.Contract, Function]]
724    @property
725    def modifiers(self) -> List[Union["Contract", "Function"]]:
726        """
727        list(Modifier): List of the modifiers
728        Can be contract for constructor's calls
729
730        """
731        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:
733    def add_modifier(self, modif: "ModifierStatements") -> None:
734        self._modifiers.append(modif)
modifiers_statements: List[ModifierStatements]
736    @property
737    def modifiers_statements(self) -> List[ModifierStatements]:
738        """
739        list(ModifierCall): List of the modifiers call (include expression and irs)
740        """
741        return list(self._modifiers)

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

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

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]
758    @property
759    def explicit_base_constructor_calls_statements(self) -> List[ModifierStatements]:
760        """
761        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
762
763        """
764        # This is a list of contracts internally, so we convert it to a list of constructor functions.
765        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:
767    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
768        self._explicit_base_constructor_calls.append(modif)
777    @property
778    def variables(self) -> List[LocalVariable]:
779        """
780        Return all local variables
781        Include parameters and return values
782        """
783        return list(self._variables.values())

Return all local variables Include parameters and return values

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

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

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

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

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

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

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

list(StateVariable): State variables read

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

list(SolidityVariable): Solidity variables read

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

list(StateVariable): State variables written

variables_read_or_written: list[slither.core.variables.variable.Variable]
831    @property
832    def variables_read_or_written(self) -> List["Variable"]:
833        """
834        list(Variable): Variables read or written (local/state/solidity)
835        """
836        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]
838    @property
839    def variables_read_as_expression(self) -> List["Expression"]:
840        return self._expression_vars_read
variables_written_as_expression: list[slither.core.expressions.expression.Expression]
842    @property
843    def variables_written_as_expression(self) -> List["Expression"]:
844        return self._expression_vars_written
slithir_variables: list[slither.slithir.variables.variable.SlithIRVariable]
846    @property
847    def slithir_variables(self) -> List["SlithIRVariable"]:
848        """
849        Temporary and Reference Variables (not SSA form)
850        """
851
852        return list(self._slithir_variables)

Temporary and Reference Variables (not SSA form)

internal_calls: list[slither.slithir.operations.internal_call.InternalCall]
861    @property
862    def internal_calls(self) -> List["InternalCall"]:
863        """
864        list(InternalCall): List of IR operations for internal calls
865        """
866        return list(self._internal_calls)

list(InternalCall): List of IR operations for internal calls

solidity_calls: list[slither.slithir.operations.solidity_call.SolidityCall]
868    @property
869    def solidity_calls(self) -> List["SolidityCall"]:
870        """
871        list(SolidityCall): List of IR operations for Solidity calls
872        """
873        return list(self._solidity_calls)

list(SolidityCall): List of IR operations for Solidity calls

875    @property
876    def high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
877        """
878        list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
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(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call A variable is called in case of call to a public state variable Include library calls

library_calls: list[slither.slithir.operations.library_call.LibraryCall]
884    @property
885    def library_calls(self) -> List["LibraryCall"]:
886        """
887        list(LibraryCall): List of IR operations for library calls
888        """
889        return list(self._library_calls)

list(LibraryCall): List of IR operations for library calls

low_level_calls: list[slither.slithir.operations.low_level_call.LowLevelCall]
891    @property
892    def low_level_calls(self) -> List["LowLevelCall"]:
893        """
894        list(LowLevelCall): List of IR operations for low level calls
895        A low level call is defined by
896        - the variable called
897        - the name of the function (call/delegatecall/callcode)
898        """
899        return list(self._low_level_calls)

list(LowLevelCall): List of IR operations for low level calls A low level call is defined by

  • the variable called
  • the name of the function (call/delegatecall/callcode)
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]:
1163    def all_variables_read(self) -> List["Variable"]:
1164        """recursive version of variables_read"""
1165        if self._all_variables_read is None:
1166            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1167        return self._all_variables_read

recursive version of variables_read

def all_variables_written(self) -> list[slither.core.variables.variable.Variable]:
1169    def all_variables_written(self) -> List["Variable"]:
1170        """recursive version of variables_written"""
1171        if self._all_variables_written is None:
1172            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1173        return self._all_variables_written

recursive version of variables_written

def all_state_variables_read(self) -> list[slither.core.variables.state_variable.StateVariable]:
1175    def all_state_variables_read(self) -> List["StateVariable"]:
1176        """recursive version of variables_read"""
1177        if self._all_state_variables_read is None:
1178            self._all_state_variables_read = self._explore_functions(
1179                lambda x: x.state_variables_read
1180            )
1181        return self._all_state_variables_read

recursive version of variables_read

def all_solidity_variables_read( self) -> List[slither.core.declarations.solidity_variables.SolidityVariable]:
1183    def all_solidity_variables_read(self) -> List[SolidityVariable]:
1184        """recursive version of solidity_read"""
1185        if self._all_solidity_variables_read is None:
1186            self._all_solidity_variables_read = self._explore_functions(
1187                lambda x: x.solidity_variables_read
1188            )
1189        return self._all_solidity_variables_read

recursive version of solidity_read

def all_slithir_variables(self) -> list[slither.slithir.variables.variable.SlithIRVariable]:
1191    def all_slithir_variables(self) -> List["SlithIRVariable"]:
1192        """recursive version of slithir_variables"""
1193        if self._all_slithir_variables is None:
1194            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1195        return self._all_slithir_variables

recursive version of slithir_variables

def all_nodes(self) -> list[slither.core.cfg.node.Node]:
1197    def all_nodes(self) -> List["Node"]:
1198        """recursive version of nodes"""
1199        if self._all_nodes is None:
1200            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1201        return self._all_nodes

recursive version of nodes

def all_expressions(self) -> list[slither.core.expressions.expression.Expression]:
1203    def all_expressions(self) -> List["Expression"]:
1204        """recursive version of variables_read"""
1205        if self._all_expressions is None:
1206            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1207        return self._all_expressions

recursive version of variables_read

def all_slithir_operations(self) -> list[slither.slithir.operations.operation.Operation]:
1209    def all_slithir_operations(self) -> List["Operation"]:
1210        if self._all_slithir_operations is None:
1211            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1212        return self._all_slithir_operations
def all_state_variables_written(self) -> List[slither.core.variables.state_variable.StateVariable]:
1214    def all_state_variables_written(self) -> List[StateVariable]:
1215        """recursive version of variables_written"""
1216        if self._all_state_variables_written is None:
1217            self._all_state_variables_written = self._explore_functions(
1218                lambda x: x.state_variables_written
1219            )
1220        return self._all_state_variables_written

recursive version of variables_written

def all_internal_calls(self) -> list[slither.slithir.operations.internal_call.InternalCall]:
1222    def all_internal_calls(self) -> List["InternalCall"]:
1223        """recursive version of internal_calls"""
1224        if self._all_internals_calls is None:
1225            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1226        return self._all_internals_calls

recursive version of internal_calls

def all_low_level_calls(self) -> list[slither.slithir.operations.low_level_call.LowLevelCall]:
1228    def all_low_level_calls(self) -> List["LowLevelCall"]:
1229        """recursive version of low_level calls"""
1230        if self._all_low_level_calls is None:
1231            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1232        return self._all_low_level_calls

recursive version of low_level calls

1234    def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
1235        """recursive version of high_level calls"""
1236        if self._all_high_level_calls is None:
1237            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1238        return self._all_high_level_calls

recursive version of high_level calls

def all_library_calls(self) -> list[slither.slithir.operations.library_call.LibraryCall]:
1240    def all_library_calls(self) -> List["LibraryCall"]:
1241        """recursive version of library calls"""
1242        if self._all_library_calls is None:
1243            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1244        return self._all_library_calls

recursive version of library calls

def all_solidity_calls(self) -> list[slither.slithir.operations.solidity_call.SolidityCall]:
1246    def all_solidity_calls(self) -> List["SolidityCall"]:
1247        """recursive version of solidity calls"""
1248        if self._all_solidity_calls is None:
1249            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1250        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]:
1257    def all_conditional_state_variables_read(self, include_loop=True) -> List["StateVariable"]:
1258        """
1259        Return the state variable used in a condition
1260
1261        Over approximate and also return index access
1262        It won't work if the variable is assigned to a temp variable
1263        """
1264        if include_loop:
1265            if self._all_conditional_state_variables_read_with_loop is None:
1266                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1267                    lambda x: self._explore_func_cond_read(x, include_loop)
1268                )
1269            return self._all_conditional_state_variables_read_with_loop
1270        if self._all_conditional_state_variables_read is None:
1271            self._all_conditional_state_variables_read = self._explore_functions(
1272                lambda x: self._explore_func_cond_read(x, include_loop)
1273            )
1274        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]:
1295    def all_conditional_solidity_variables_read(
1296        self, include_loop: bool = True
1297    ) -> List[SolidityVariable]:
1298        """
1299        Return the Soldiity variables directly used in a condtion
1300
1301        Use of the IR to filter index access
1302        Assumption: the solidity vars are used directly in the conditional node
1303        It won't work if the variable is assigned to a temp variable
1304        """
1305        if include_loop:
1306            if self._all_conditional_solidity_variables_read_with_loop is None:
1307                self._all_conditional_solidity_variables_read_with_loop = 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_with_loop
1313
1314        if self._all_conditional_solidity_variables_read is None:
1315            self._all_conditional_solidity_variables_read = self._explore_functions(
1316                lambda x: self._explore_func_conditional(
1317                    x, self._solidity_variable_in_binary, include_loop
1318                )
1319            )
1320        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]:
1339    def all_solidity_variables_used_as_args(self) -> List[SolidityVariable]:
1340        """
1341        Return the Soldiity variables directly used in a call
1342
1343        Use of the IR to filter index access
1344        Used to catch check(msg.sender)
1345        """
1346        if self._all_solidity_variables_used_as_args is None:
1347            self._all_solidity_variables_used_as_args = self._explore_functions(
1348                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1349            )
1350        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:
1359    def apply_visitor(self, Visitor: Callable) -> List:
1360        """
1361            Apply a visitor to all the function expressions
1362        Args:
1363            Visitor: slither.visitors
1364        Returns
1365            list(): results of the visit
1366        """
1367        expressions = self.expressions
1368        v = [Visitor(e).result() for e in expressions]
1369        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]:
1378    def get_local_variable_from_name(self, variable_name: str) -> Optional[LocalVariable]:
1379        """
1380            Return a local variable from a name
1381
1382        Args:
1383            variable_name (str): name of the variable
1384        Returns:
1385            LocalVariable
1386        """
1387        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):
1396    def cfg_to_dot(self, filename: str):
1397        """
1398            Export the function to a dot file
1399        Args:
1400            filename (str)
1401        """
1402        with open(filename, "w", encoding="utf8") as f:
1403            f.write("digraph{\n")
1404            for node in self.nodes:
1405                f.write(f'{node.node_id}[label="{str(node)}"];\n')
1406                for son in node.sons:
1407                    f.write(f"{node.node_id}->{son.node_id};\n")
1408
1409            f.write("}\n")

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

def dominator_tree_to_dot(self, filename: str):
1411    def dominator_tree_to_dot(self, filename: str):
1412        """
1413            Export the dominator tree of the function to a dot file
1414        Args:
1415            filename (str)
1416        """
1417
1418        def description(node):
1419            desc = f"{node}\n"
1420            desc += f"id: {node.node_id}"
1421            if node.dominance_frontier:
1422                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1423            return desc
1424
1425        with open(filename, "w", encoding="utf8") as f:
1426            f.write("digraph{\n")
1427            for node in self.nodes:
1428                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1429                if node.immediate_dominator:
1430                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1431
1432            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):
1434    def slithir_cfg_to_dot(self, filename: str):
1435        """
1436        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1437        :param filename:
1438        :return:
1439        """
1440        content = self.slithir_cfg_to_dot_str()
1441        with open(filename, "w", encoding="utf8") as f:
1442            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:
1444    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1445        """
1446        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1447        :return: the DOT content
1448        :rtype: str
1449        """
1450        from slither.core.cfg.node import NodeType
1451
1452        content = ""
1453        content += "digraph{\n"
1454        for node in self.nodes:
1455            label = f"Node Type: {node.type.value} {node.node_id}\n"
1456            if node.expression and not skip_expressions:
1457                label += f"\nEXPRESSION:\n{node.expression}\n"
1458            if node.irs and not skip_expressions:
1459                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1460            content += f'{node.node_id}[label="{label}"];\n'
1461            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1462                true_node = node.son_true
1463                if true_node:
1464                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1465                false_node = node.son_false
1466                if false_node:
1467                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1468            else:
1469                for son in node.sons:
1470                    content += f"{node.node_id}->{son.node_id};\n"
1471
1472        content += "}\n"
1473        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:
1482    def is_reading(self, variable: "Variable") -> bool:
1483        """
1484            Check if the function reads the variable
1485        Args:
1486            variable (Variable):
1487        Returns:
1488            bool: True if the variable is read
1489        """
1490        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:
1492    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1493        """
1494            Check if the function reads the variable in a IF node
1495        Args:
1496            variable (Variable):
1497        Returns:
1498            bool: True if the variable is read
1499        """
1500        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1501        variables_read = [item for sublist in variables_reads for item in sublist]
1502        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:
1504    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1505        """
1506            Check if the function reads the variable in an require or assert
1507        Args:
1508            variable (Variable):
1509        Returns:
1510            bool: True if the variable is read
1511        """
1512        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1513        variables_read = [item for sublist in variables_reads for item in sublist]
1514        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:
1516    def is_writing(self, variable: "Variable") -> bool:
1517        """
1518            Check if the function writes the variable
1519        Args:
1520            variable (Variable):
1521        Returns:
1522            bool: True if the variable is written
1523        """
1524        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]]:
1526    @abstractmethod
1527    def get_summary(
1528        self,
1529    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
1530        pass
def is_protected(self) -> bool:
1532    def is_protected(self) -> bool:
1533        """
1534            Determine if the function is protected using a check on msg.sender
1535
1536            Consider onlyOwner as a safe modifier.
1537            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1538            classify the function as protected
1539
1540            Otherwise only detects if msg.sender is directly used in a condition
1541            For example, it wont work for:
1542                address a = msg.sender
1543                require(a == owner)
1544        Returns
1545            (bool)
1546        """
1547
1548        if self._is_protected is None:
1549            if self.is_constructor:
1550                self._is_protected = True
1551                return True
1552            if "onlyOwner" in [m.name for m in self.modifiers]:
1553                self._is_protected = True
1554                return True
1555            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1556            args_vars = self.all_solidity_variables_used_as_args()
1557            self._is_protected = (
1558                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1559            )
1560        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
1562    @property
1563    def is_reentrant(self) -> bool:
1564        """
1565        Determine if the function can be re-entered
1566        """
1567        reentrancy_modifier = "nonReentrant"
1568
1569        if self.function_language == FunctionLanguage.Vyper:
1570            reentrancy_modifier = "nonreentrant(lock)"
1571
1572        # TODO: compare with hash of known nonReentrant modifier instead of the name
1573        if reentrancy_modifier in [m.name for m in self.modifiers]:
1574            return False
1575
1576        if self.visibility in ["public", "external"]:
1577            return True
1578
1579        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1580        all_entry_points = [
1581            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1582        ]
1583        if not all_entry_points:
1584            return True
1585        return not all(
1586            (reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points)
1587        )

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:
1698    def new_node(
1699        self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]
1700    ) -> "Node":
1701        from slither.core.cfg.node import Node
1702
1703        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1704        node.set_offset(src, self.compilation_unit)
1705        self._counter_nodes += 1
1706        node.set_function(self)
1707        self._nodes.append(node)
1708
1709        return node
def get_last_ssa_state_variables_instances( self) -> dict[str, set[slither.slithir.variables.variable.SlithIRVariable]]:
1777    def get_last_ssa_state_variables_instances(
1778        self,
1779    ) -> Dict[str, Set["SlithIRVariable"]]:
1780        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]]:
1782    def get_last_ssa_local_variables_instances(
1783        self,
1784    ) -> Dict[str, Set["SlithIRVariable"]]:
1785        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:
1797    def fix_phi(
1798        self,
1799        last_state_variables_instances: Dict[str, List["StateVariable"]],
1800        initial_state_variables_instances: Dict[str, "StateVariable"],
1801    ) -> None:
1802        from slither.slithir.operations import InternalCall, PhiCallback
1803        from slither.slithir.variables import Constant, StateIRVariable
1804
1805        for node in self.nodes:
1806            for ir in node.irs_ssa:
1807                if node == self.entry_point:
1808                    if isinstance(ir.lvalue, StateIRVariable):
1809                        additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1810                        additional += last_state_variables_instances[ir.lvalue.canonical_name]
1811                        ir.rvalues = list(set(additional + ir.rvalues))
1812                    # function parameter
1813                    else:
1814                        # find index of the parameter
1815                        idx = self.parameters.index(ir.lvalue.non_ssa_version)
1816                        # find non ssa version of that index
1817                        additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1818                        additional = unroll(additional)
1819                        additional = [a for a in additional if not isinstance(a, Constant)]
1820                        ir.rvalues = list(set(additional + ir.rvalues))
1821                if isinstance(ir, PhiCallback):
1822                    callee_ir = ir.callee_ir
1823                    if isinstance(callee_ir, InternalCall):
1824                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1825                        if ir.lvalue.canonical_name in last_ssa:
1826                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1827                        else:
1828                            ir.rvalues = [ir.lvalue]
1829                    else:
1830                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1831                        ir.rvalues = list(set(additional + ir.rvalues))
1832
1833            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
def generate_slithir_and_analyze(self) -> None:
1835    def generate_slithir_and_analyze(self) -> None:
1836
1837        for node in self.nodes:
1838            node.slithir_generation()
1839
1840        self._analyze_read_write()
1841        self._analyze_calls()
@abstractmethod
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1843    @abstractmethod
1844    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1845        pass
def update_read_write_using_ssa(self) -> None:
1847    def update_read_write_using_ssa(self) -> None:
1848        for node in self.nodes:
1849            node.update_read_write_using_ssa()
1850        self._analyze_read_write()