Merge pull request #728 from crytic/dev-top-level-elements

Improve top level objects support
pull/733/head
Feist Josselin 4 years ago committed by GitHub
commit 7d3bf292ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      slither/core/declarations/__init__.py
  2. 29
      slither/core/declarations/contract.py
  3. 16
      slither/core/declarations/enum.py
  4. 17
      slither/core/declarations/enum_contract.py
  5. 6
      slither/core/declarations/enum_top_level.py
  6. 80
      slither/core/declarations/function.py
  7. 106
      slither/core/declarations/function_contract.py
  8. 80
      slither/core/declarations/function_top_level.py
  9. 11
      slither/core/declarations/import_directive.py
  10. 4
      slither/core/declarations/modifier.py
  11. 37
      slither/core/declarations/solidity_variables.py
  12. 11
      slither/core/declarations/structure.py
  13. 12
      slither/core/declarations/structure_contract.py
  14. 6
      slither/core/declarations/structure_top_level.py
  15. 5
      slither/core/declarations/top_level.py
  16. 41
      slither/core/slither_core.py
  17. 36
      slither/core/variables/top_level_variable.py
  18. 43
      slither/printers/summary/slithir.py
  19. 6
      slither/slither.py
  20. 48
      slither/slithir/convert.py
  21. 5
      slither/slithir/operations/internal_call.py
  22. 115
      slither/solc_parsing/declarations/contract.py
  23. 21
      slither/solc_parsing/declarations/function.py
  24. 11
      slither/solc_parsing/declarations/modifier.py
  25. 29
      slither/solc_parsing/declarations/structure_contract.py
  26. 56
      slither/solc_parsing/declarations/structure_top_level.py
  27. 284
      slither/solc_parsing/expressions/expression_parsing.py
  28. 187
      slither/solc_parsing/slitherSolc.py
  29. 184
      slither/solc_parsing/solidity_types/type_parsing.py
  30. 15
      slither/solc_parsing/variables/top_level_variable.py
  31. 12
      slither/solc_parsing/yul/parse_yul.py
  32. 3
      tests/ast-parsing/expected/struct-0.6.0-compact.json
  33. 3
      tests/ast-parsing/expected/struct-0.6.1-compact.json
  34. 3
      tests/ast-parsing/expected/struct-0.6.10-compact.json
  35. 3
      tests/ast-parsing/expected/struct-0.6.11-compact.json
  36. 3
      tests/ast-parsing/expected/struct-0.6.12-compact.json
  37. 3
      tests/ast-parsing/expected/struct-0.6.2-compact.json
  38. 3
      tests/ast-parsing/expected/struct-0.6.3-compact.json
  39. 3
      tests/ast-parsing/expected/struct-0.6.4-compact.json
  40. 3
      tests/ast-parsing/expected/struct-0.6.5-compact.json
  41. 3
      tests/ast-parsing/expected/struct-0.6.6-compact.json
  42. 3
      tests/ast-parsing/expected/struct-0.6.7-compact.json
  43. 3
      tests/ast-parsing/expected/struct-0.6.8-compact.json
  44. 3
      tests/ast-parsing/expected/struct-0.6.9-compact.json
  45. 3
      tests/ast-parsing/expected/struct-0.7.0-compact.json
  46. 3
      tests/ast-parsing/expected/struct-0.7.1-compact.json
  47. 3
      tests/ast-parsing/expected/struct-0.7.2-compact.json
  48. 3
      tests/ast-parsing/expected/top-level-0.4.0-legacy.json
  49. 3
      tests/ast-parsing/expected/top-level-0.4.1-legacy.json
  50. 3
      tests/ast-parsing/expected/top-level-0.4.10-legacy.json
  51. 3
      tests/ast-parsing/expected/top-level-0.4.11-legacy.json
  52. 3
      tests/ast-parsing/expected/top-level-0.4.12-compact.json
  53. 3
      tests/ast-parsing/expected/top-level-0.4.12-legacy.json
  54. 3
      tests/ast-parsing/expected/top-level-0.4.13-compact.json
  55. 3
      tests/ast-parsing/expected/top-level-0.4.13-legacy.json
  56. 3
      tests/ast-parsing/expected/top-level-0.4.14-compact.json
  57. 3
      tests/ast-parsing/expected/top-level-0.4.14-legacy.json
  58. 3
      tests/ast-parsing/expected/top-level-0.4.15-compact.json
  59. 3
      tests/ast-parsing/expected/top-level-0.4.15-legacy.json
  60. 3
      tests/ast-parsing/expected/top-level-0.4.16-compact.json
  61. 3
      tests/ast-parsing/expected/top-level-0.4.16-legacy.json
  62. 3
      tests/ast-parsing/expected/top-level-0.4.17-compact.json
  63. 3
      tests/ast-parsing/expected/top-level-0.4.17-legacy.json
  64. 3
      tests/ast-parsing/expected/top-level-0.4.18-compact.json
  65. 3
      tests/ast-parsing/expected/top-level-0.4.18-legacy.json
  66. 3
      tests/ast-parsing/expected/top-level-0.4.19-compact.json
  67. 3
      tests/ast-parsing/expected/top-level-0.4.19-legacy.json
  68. 3
      tests/ast-parsing/expected/top-level-0.4.2-legacy.json
  69. 3
      tests/ast-parsing/expected/top-level-0.4.20-compact.json
  70. 3
      tests/ast-parsing/expected/top-level-0.4.20-legacy.json
  71. 3
      tests/ast-parsing/expected/top-level-0.4.21-compact.json
  72. 3
      tests/ast-parsing/expected/top-level-0.4.21-legacy.json
  73. 3
      tests/ast-parsing/expected/top-level-0.4.22-compact.json
  74. 3
      tests/ast-parsing/expected/top-level-0.4.22-legacy.json
  75. 3
      tests/ast-parsing/expected/top-level-0.4.23-compact.json
  76. 3
      tests/ast-parsing/expected/top-level-0.4.23-legacy.json
  77. 3
      tests/ast-parsing/expected/top-level-0.4.24-compact.json
  78. 3
      tests/ast-parsing/expected/top-level-0.4.24-legacy.json
  79. 3
      tests/ast-parsing/expected/top-level-0.4.25-compact.json
  80. 3
      tests/ast-parsing/expected/top-level-0.4.25-legacy.json
  81. 3
      tests/ast-parsing/expected/top-level-0.4.26-compact.json
  82. 3
      tests/ast-parsing/expected/top-level-0.4.26-legacy.json
  83. 3
      tests/ast-parsing/expected/top-level-0.4.3-legacy.json
  84. 3
      tests/ast-parsing/expected/top-level-0.4.4-legacy.json
  85. 3
      tests/ast-parsing/expected/top-level-0.4.5-legacy.json
  86. 3
      tests/ast-parsing/expected/top-level-0.4.6-legacy.json
  87. 3
      tests/ast-parsing/expected/top-level-0.4.7-legacy.json
  88. 3
      tests/ast-parsing/expected/top-level-0.4.8-legacy.json
  89. 3
      tests/ast-parsing/expected/top-level-0.4.9-legacy.json
  90. 3
      tests/ast-parsing/expected/top-level-0.5.0-compact.json
  91. 3
      tests/ast-parsing/expected/top-level-0.5.0-legacy.json
  92. 3
      tests/ast-parsing/expected/top-level-0.5.1-compact.json
  93. 3
      tests/ast-parsing/expected/top-level-0.5.1-legacy.json
  94. 3
      tests/ast-parsing/expected/top-level-0.5.10-compact.json
  95. 3
      tests/ast-parsing/expected/top-level-0.5.10-legacy.json
  96. 3
      tests/ast-parsing/expected/top-level-0.5.11-compact.json
  97. 3
      tests/ast-parsing/expected/top-level-0.5.11-legacy.json
  98. 3
      tests/ast-parsing/expected/top-level-0.5.12-compact.json
  99. 3
      tests/ast-parsing/expected/top-level-0.5.12-legacy.json
  100. 3
      tests/ast-parsing/expected/top-level-0.5.13-compact.json
  101. Some files were not shown because too many files have changed in this diff Show More

@ -11,3 +11,5 @@ from .solidity_variables import (
SolidityFunction, SolidityFunction,
) )
from .structure import Structure from .structure import Structure
from .enum_contract import EnumContract
from .structure_contract import StructureContract

@ -25,8 +25,7 @@ from slither.utils.tests_pattern import is_test_contract
# pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks # pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
from slither.core.declarations import Enum, Event, Modifier from slither.core.declarations import Enum, Event, Modifier, EnumContract, StructureContract
from slither.core.declarations import Structure
from slither.slithir.variables.variable import SlithIRVariable from slither.slithir.variables.variable import SlithIRVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
@ -51,8 +50,8 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
# contract B is A(1) { .. # contract B is A(1) { ..
self._explicit_base_constructor_calls: List["Contract"] = [] self._explicit_base_constructor_calls: List["Contract"] = []
self._enums: Dict[str, "Enum"] = {} self._enums: Dict[str, "EnumContract"] = {}
self._structures: Dict[str, "Structure"] = {} self._structures: Dict[str, "StructureContract"] = {}
self._events: Dict[str, "Event"] = {} self._events: Dict[str, "Event"] = {}
self._variables: Dict[str, "StateVariable"] = {} self._variables: Dict[str, "StateVariable"] = {}
self._variables_ordered: List["StateVariable"] = [] self._variables_ordered: List["StateVariable"] = []
@ -135,28 +134,28 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
################################################################################### ###################################################################################
@property @property
def structures(self) -> List["Structure"]: def structures(self) -> List["StructureContract"]:
""" """
list(Structure): List of the structures list(Structure): List of the structures
""" """
return list(self._structures.values()) return list(self._structures.values())
@property @property
def structures_inherited(self) -> List["Structure"]: def structures_inherited(self) -> List["StructureContract"]:
""" """
list(Structure): List of the inherited structures list(Structure): List of the inherited structures
""" """
return [s for s in self.structures if s.contract != self] return [s for s in self.structures if s.contract != self]
@property @property
def structures_declared(self) -> List["Structure"]: def structures_declared(self) -> List["StructureContract"]:
""" """
list(Structues): List of the structures declared within the contract (not inherited) list(Structues): List of the structures declared within the contract (not inherited)
""" """
return [s for s in self.structures if s.contract == self] return [s for s in self.structures if s.contract == self]
@property @property
def structures_as_dict(self) -> Dict[str, "Structure"]: def structures_as_dict(self) -> Dict[str, "StructureContract"]:
return self._structures return self._structures
# endregion # endregion
@ -167,25 +166,25 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
################################################################################### ###################################################################################
@property @property
def enums(self) -> List["Enum"]: def enums(self) -> List["EnumContract"]:
return list(self._enums.values()) return list(self._enums.values())
@property @property
def enums_inherited(self) -> List["Enum"]: def enums_inherited(self) -> List["EnumContract"]:
""" """
list(Enum): List of the inherited enums list(Enum): List of the inherited enums
""" """
return [e for e in self.enums if e.contract != self] return [e for e in self.enums if e.contract != self]
@property @property
def enums_declared(self) -> List["Enum"]: def enums_declared(self) -> List["EnumContract"]:
""" """
list(Enum): List of the enums declared within the contract (not inherited) list(Enum): List of the enums declared within the contract (not inherited)
""" """
return [e for e in self.enums if e.contract == self] return [e for e in self.enums if e.contract == self]
@property @property
def enums_as_dict(self) -> Dict[str, "Enum"]: def enums_as_dict(self) -> Dict[str, "EnumContract"]:
return self._enums return self._enums
# endregion # endregion
@ -1090,11 +1089,13 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._is_incorrectly_parsed = incorrect self._is_incorrectly_parsed = incorrect
def add_constructor_variables(self): def add_constructor_variables(self):
from slither.core.declarations.function_contract import FunctionContract
if self.state_variables: if self.state_variables:
for (idx, variable_candidate) in enumerate(self.state_variables): for (idx, variable_candidate) in enumerate(self.state_variables):
if variable_candidate.expression and not variable_candidate.is_constant: if variable_candidate.expression and not variable_candidate.is_constant:
constructor_variable = Function() constructor_variable = FunctionContract(self.slither)
constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
constructor_variable.set_contract(self) constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self) constructor_variable.set_contract_declarer(self)
@ -1120,7 +1121,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
for (idx, variable_candidate) in enumerate(self.state_variables): for (idx, variable_candidate) in enumerate(self.state_variables):
if variable_candidate.expression and variable_candidate.is_constant: if variable_candidate.expression and variable_candidate.is_constant:
constructor_variable = Function() constructor_variable = FunctionContract(self.slither)
constructor_variable.set_function_type( constructor_variable.set_function_type(
FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
) )

@ -1,13 +1,9 @@
from typing import List, TYPE_CHECKING from typing import List
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.children.child_contract import ChildContract
if TYPE_CHECKING:
from slither.core.declarations import Contract
class Enum(SourceMapping):
class Enum(ChildContract, SourceMapping):
def __init__(self, name: str, canonical_name: str, values: List[str]): def __init__(self, name: str, canonical_name: str, values: List[str]):
super().__init__() super().__init__()
self._name = name self._name = name
@ -26,13 +22,5 @@ class Enum(ChildContract, SourceMapping):
def values(self) -> List[str]: def values(self) -> List[str]:
return self._values return self._values
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
def __str__(self): def __str__(self):
return self.name return self.name

@ -0,0 +1,17 @@
from typing import TYPE_CHECKING
from slither.core.children.child_contract import ChildContract
from slither.core.declarations import Enum
if TYPE_CHECKING:
from slither.core.declarations import Contract
class EnumContract(Enum, ChildContract):
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract

@ -0,0 +1,6 @@
from slither.core.declarations import Enum
from slither.core.declarations.top_level import TopLevel
class EnumTopLevel(Enum, TopLevel):
pass

@ -2,13 +2,12 @@
Function module Function module
""" """
import logging import logging
from abc import ABCMeta, abstractmethod
from collections import namedtuple from collections import namedtuple
from enum import Enum from enum import Enum
from itertools import groupby from itertools import groupby
from typing import Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple from typing import Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_inheritance import ChildInheritance
from slither.core.declarations.solidity_variables import ( from slither.core.declarations.solidity_variables import (
SolidityFunction, SolidityFunction,
SolidityVariable, SolidityVariable,
@ -24,7 +23,6 @@ from slither.core.solidity_types import UserDefinedType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.utils.utils import unroll from slither.utils.utils import unroll
@ -46,6 +44,7 @@ if TYPE_CHECKING:
from slither.slithir.operations import Operation from slither.slithir.operations import Operation
from slither.slither import Slither from slither.slither import Slither
from slither.core.cfg.node import NodeType from slither.core.cfg.node import NodeType
from slither.core.slither_core import SlitherCore
LOGGER = logging.getLogger("Function") LOGGER = logging.getLogger("Function")
ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"]) ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@ -103,14 +102,12 @@ def _filter_state_variables_written(expressions: List["Expression"]):
return ret return ret
class Function( class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
ChildContract, ChildInheritance, SourceMapping
): # pylint: disable=too-many-public-methods
""" """
Function class Function class
""" """
def __init__(self): def __init__(self, slither: "SlitherCore"):
super().__init__() super().__init__()
self._scope: List[str] = [] self._scope: List[str] = []
self._name: Optional[str] = None self._name: Optional[str] = None
@ -206,6 +203,8 @@ class Function(
self._canonical_name: Optional[str] = None self._canonical_name: Optional[str] = None
self._is_protected: Optional[bool] = None self._is_protected: Optional[bool] = None
self._slither: "SlitherCore" = slither
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region General properties # region General properties
@ -260,20 +259,13 @@ class Function(
return self._full_name return self._full_name
@property @property
@abstractmethod
def canonical_name(self) -> str: def canonical_name(self) -> str:
""" """
str: contract.func_name(type1,type2) str: contract.func_name(type1,type2)
Return the function signature without the return values Return the function signature without the return values
""" """
if self._canonical_name is None: return ""
name, parameters, _ = self.signature
self._canonical_name = (
".".join([self.contract_declarer.name] + self._scope + [name])
+ "("
+ ",".join(parameters)
+ ")"
)
return self._canonical_name
@property @property
def contains_assembly(self) -> bool: def contains_assembly(self) -> bool:
@ -320,16 +312,12 @@ class Function(
return self._can_reenter return self._can_reenter
@property @property
def slither(self) -> "Slither": def slither(self) -> "SlitherCore":
return self.contract.slither return self._slither
def is_declared_by(self, contract: "Contract") -> bool: @slither.setter
""" def slither(self, sl: "SlitherCore"):
Check if the element is declared by the contract self._slither = sl
:param contract:
:return:
"""
return self.contract_declarer == contract
# endregion # endregion
################################################################################### ###################################################################################
@ -978,16 +966,9 @@ class Function(
################################################################################### ###################################################################################
@property @property
@abstractmethod
def functions_shadowed(self) -> List["Function"]: def functions_shadowed(self) -> List["Function"]:
""" pass
Return the list of functions shadowed
Returns:
list(core.Function)
"""
candidates = [c.functions_declared for c in self.contract.inheritance]
candidates = [candidate for sublist in candidates for candidate in sublist]
return [f for f in candidates if f.full_name == self.full_name]
# endregion # endregion
################################################################################### ###################################################################################
@ -1400,25 +1381,11 @@ class Function(
""" """
return variable in self.variables_written return variable in self.variables_written
@abstractmethod
def get_summary( def get_summary(
self, self,
) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]: ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
""" pass
Return the function summary
Returns:
(str, str, str, list(str), list(str), listr(str), list(str), list(str);
contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
"""
return (
self.contract_declarer.name,
self.full_name,
self.visibility,
[str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls_as_expressions],
)
def is_protected(self) -> bool: def is_protected(self) -> bool:
""" """
@ -1684,18 +1651,9 @@ class Function(
self._analyze_read_write() self._analyze_read_write()
self._analyze_calls() self._analyze_calls()
@abstractmethod
def generate_slithir_ssa(self, all_ssa_state_variables_instances): def generate_slithir_ssa(self, all_ssa_state_variables_instances):
from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa pass
from slither.core.dominators.utils import (
compute_dominance_frontier,
compute_dominators,
)
compute_dominators(self.nodes)
compute_dominance_frontier(self.nodes)
transform_slithir_vars_to_ssa(self)
if not self.contract.is_incorrectly_constructed:
add_ssa_ir(self, all_ssa_state_variables_instances)
def update_read_write_using_ssa(self): def update_read_write_using_ssa(self):
for node in self.nodes: for node in self.nodes:

@ -0,0 +1,106 @@
"""
Function module
"""
from typing import TYPE_CHECKING, List, Tuple
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_inheritance import ChildInheritance
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.declarations import Function
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
if TYPE_CHECKING:
from slither.core.declarations import Contract
class FunctionContract(Function, ChildContract, ChildInheritance, SourceMapping):
@property
def canonical_name(self) -> str:
"""
str: contract.func_name(type1,type2)
Return the function signature without the return values
"""
if self._canonical_name is None:
name, parameters, _ = self.signature
self._canonical_name = (
".".join([self.contract_declarer.name] + self._scope + [name])
+ "("
+ ",".join(parameters)
+ ")"
)
return self._canonical_name
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract_declarer == contract
# endregion
###################################################################################
###################################################################################
# region Functions
###################################################################################
###################################################################################
@property
def functions_shadowed(self) -> List["Function"]:
"""
Return the list of functions shadowed
Returns:
list(core.Function)
"""
candidates = [c.functions_declared for c in self.contract.inheritance]
candidates = [candidate for sublist in candidates for candidate in sublist]
return [f for f in candidates if f.full_name == self.full_name]
# endregion
###################################################################################
###################################################################################
# region Summary information
###################################################################################
###################################################################################
def get_summary(
self,
) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
"""
Return the function summary
Returns:
(str, str, str, list(str), list(str), listr(str), list(str), list(str);
contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
"""
return (
self.contract_declarer.name,
self.full_name,
self.visibility,
[str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls_as_expressions],
)
# endregion
###################################################################################
###################################################################################
# region SlithIr and SSA
###################################################################################
###################################################################################
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
from slither.core.dominators.utils import (
compute_dominance_frontier,
compute_dominators,
)
compute_dominators(self.nodes)
compute_dominance_frontier(self.nodes)
transform_slithir_vars_to_ssa(self)
if not self.contract.is_incorrectly_constructed:
add_ssa_ir(self, all_ssa_state_variables_instances)

@ -0,0 +1,80 @@
"""
Function module
"""
from typing import List, Tuple
from slither.core.declarations import Function
from slither.core.declarations.top_level import TopLevel
from slither.core.source_mapping.source_mapping import SourceMapping
class FunctionTopLevel(Function, TopLevel, SourceMapping):
@property
def canonical_name(self) -> str:
"""
str: contract.func_name(type1,type2)
Return the function signature without the return values
"""
if self._canonical_name is None:
name, parameters, _ = self.signature
self._canonical_name = ".".join(self._scope + [name]) + "(" + ",".join(parameters) + ")"
return self._canonical_name
# endregion
###################################################################################
###################################################################################
# region Functions
###################################################################################
###################################################################################
@property
def functions_shadowed(self) -> List["Function"]:
return []
# endregion
###################################################################################
###################################################################################
# region Summary information
###################################################################################
###################################################################################
def get_summary(
self,
) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
"""
Return the function summary
Returns:
(str, str, str, list(str), list(str), listr(str), list(str), list(str);
contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
"""
return (
"",
self.full_name,
self.visibility,
[str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls_as_expressions],
)
# endregion
###################################################################################
###################################################################################
# region SlithIr and SSA
###################################################################################
###################################################################################
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
# pylint: disable=import-outside-toplevel
from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
from slither.core.dominators.utils import (
compute_dominance_frontier,
compute_dominators,
)
compute_dominators(self.nodes)
compute_dominance_frontier(self.nodes)
transform_slithir_vars_to_ssa(self)
add_ssa_ir(self, all_ssa_state_variables_instances)

@ -1,3 +1,5 @@
from typing import Optional
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
@ -5,10 +7,19 @@ class Import(SourceMapping):
def __init__(self, filename: str): def __init__(self, filename: str):
super().__init__() super().__init__()
self._filename = filename self._filename = filename
self._alias: Optional[str] = None
@property @property
def filename(self) -> str: def filename(self) -> str:
return self._filename return self._filename
@property
def alias(self) -> Optional[str]:
return self._alias
@alias.setter
def alias(self, a: str):
self._alias = a
def __str__(self): def __str__(self):
return self.filename return self.filename

@ -1,8 +1,8 @@
""" """
Modifier module Modifier module
""" """
from .function import Function from .function_contract import FunctionContract
class Modifier(Function): class Modifier(FunctionContract):
pass pass

@ -1,10 +1,13 @@
# https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html # https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
from typing import List, Dict, Union from typing import List, Dict, Union, TYPE_CHECKING
from slither.core.context.context import Context from slither.core.context.context import Context
from slither.core.solidity_types import ElementaryType, TypeInformation from slither.core.solidity_types import ElementaryType, TypeInformation
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
if TYPE_CHECKING:
from slither.core.declarations import Import
SOLIDITY_VARIABLES = { SOLIDITY_VARIABLES = {
"now": "uint256", "now": "uint256",
"this": "address", "this": "address",
@ -31,7 +34,6 @@ SOLIDITY_VARIABLES_COMPOSED = {
"tx.origin": "address", "tx.origin": "address",
} }
SOLIDITY_FUNCTIONS: Dict[str, List[str]] = { SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"gasleft()": ["uint256"], "gasleft()": ["uint256"],
"assert(bool)": [], "assert(bool)": [],
@ -181,3 +183,34 @@ class SolidityFunction:
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
class SolidityImportPlaceHolder(SolidityVariable):
"""
Placeholder for import on top level objects
See the example at https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/
In the long term we should remove this and better integrate import aliases
"""
def __init__(self, import_directive: "Import"):
assert import_directive.alias is not None
super().__init__(import_directive.alias)
self._import_directive = import_directive
def _check_name(self, name: str):
return True
@property
def type(self) -> ElementaryType:
return ElementaryType("string")
def __eq__(self, other):
return (
self.__class__ == other.__class__
and self.name == other.name
and self._import_directive.filename == self._import_directive.filename
)
@property
def import_directive(self) -> "Import":
return self._import_directive

@ -1,13 +1,12 @@
from typing import List, TYPE_CHECKING, Dict from typing import List, TYPE_CHECKING, Dict
from slither.core.children.child_contract import ChildContract
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.variables.structure_variable import StructureVariable from slither.core.variables.structure_variable import StructureVariable
class Structure(ChildContract, SourceMapping): class Structure(SourceMapping):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._name = None self._name = None
@ -39,14 +38,6 @@ class Structure(ChildContract, SourceMapping):
def add_elem_in_order(self, s: str): def add_elem_in_order(self, s: str):
self._elems_ordered.append(s) self._elems_ordered.append(s)
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
@property @property
def elems_ordered(self) -> List["StructureVariable"]: def elems_ordered(self) -> List["StructureVariable"]:
ret = [] ret = []

@ -0,0 +1,12 @@
from slither.core.children.child_contract import ChildContract
from slither.core.declarations import Structure
class StructureContract(Structure, ChildContract):
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract

@ -0,0 +1,6 @@
from slither.core.declarations import Structure
from slither.core.declarations.top_level import TopLevel
class StructureTopLevel(Structure, TopLevel):
pass

@ -0,0 +1,5 @@
from slither.core.source_mapping.source_mapping import SourceMapping
class TopLevel(SourceMapping):
pass

@ -1,11 +1,11 @@
""" """
Main module Main module
""" """
import os
import logging
import json import json
import re import logging
import math import math
import os
import re
from collections import defaultdict from collections import defaultdict
from typing import Optional, Dict, List, Set, Union, Tuple from typing import Optional, Dict, List, Set, Union, Tuple
@ -18,10 +18,12 @@ from slither.core.declarations import (
Import, Import,
Function, Function,
Modifier, Modifier,
Structure,
Enum,
) )
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall from slither.slithir.operations import InternalCall
from slither.slithir.variables import Constant from slither.slithir.variables import Constant
from slither.utils.colors import red from slither.utils.colors import red
@ -44,12 +46,19 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# Top level object
self._contracts: Dict[str, Contract] = {} self._contracts: Dict[str, Contract] = {}
self._structures_top_level: List[StructureTopLevel] = []
self._enums_top_level: List[EnumTopLevel] = []
self._variables_top_level: List[TopLevelVariable] = []
self._functions_top_level: List[FunctionTopLevel] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._filename: Optional[str] = None self._filename: Optional[str] = None
self._source_units: Dict[int, str] = {} self._source_units: Dict[int, str] = {}
self._solc_version: Optional[str] = None # '0.3' or '0.4':! self._solc_version: Optional[str] = None # '0.3' or '0.4':!
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._raw_source_code: Dict[str, str] = {} self._raw_source_code: Dict[str, str] = {}
self._all_functions: Set[Function] = set() self._all_functions: Set[Function] = set()
self._all_modifiers: Set[Modifier] = set() self._all_modifiers: Set[Modifier] = set()
@ -234,14 +243,20 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
################################################################################### ###################################################################################
@property @property
def top_level_structures(self) -> List[Structure]: def structures_top_level(self) -> List[StructureTopLevel]:
top_level_structures = [c.structures for c in self.contracts if c.is_top_level] return self._structures_top_level
return [st for sublist in top_level_structures for st in sublist]
@property
def enums_top_level(self) -> List[EnumTopLevel]:
return self._enums_top_level
@property
def variables_top_level(self) -> List[TopLevelVariable]:
return self._variables_top_level
@property @property
def top_level_enums(self) -> List[Enum]: def functions_top_level(self) -> List[FunctionTopLevel]:
top_level_enums = [c.enums for c in self.contracts if c.is_top_level] return self._functions_top_level
return [st for sublist in top_level_enums for st in sublist]
# endregion # endregion
################################################################################### ###################################################################################

@ -0,0 +1,36 @@
from typing import Optional, TYPE_CHECKING
from slither.core.declarations.top_level import TopLevel
from slither.core.variables.variable import Variable
if TYPE_CHECKING:
from slither.core.cfg.node import Node
class TopLevelVariable(TopLevel, Variable):
def __init__(self):
super().__init__()
self._node_initialization: Optional["Node"] = None
# endregion
###################################################################################
###################################################################################
# region IRs (initialization)
###################################################################################
###################################################################################
@property
def node_initialization(self) -> Optional["Node"]:
"""
Node for the state variable initalization
:return:
"""
return self._node_initialization
@node_initialization.setter
def node_initialization(self, node_initialization):
self._node_initialization = node_initialization
# endregion
###################################################################################
###################################################################################

@ -1,10 +1,25 @@
""" """
Module printing summary of the contract Module printing summary of the contract
""" """
from slither.core.declarations import Function
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
def _print_function(function: Function) -> str:
txt = ""
for node in function.nodes:
if node.expression:
txt += "\t\tExpression: {}\n".format(node.expression)
txt += "\t\tIRs:\n"
for ir in node.irs:
txt += "\t\t\t{}\n".format(ir)
elif node.irs:
txt += "\t\tIRs:\n"
for ir in node.irs:
txt += "\t\t\t{}\n".format(ir)
return txt
class PrinterSlithIR(AbstractPrinter): class PrinterSlithIR(AbstractPrinter):
ARGUMENT = "slithir" ARGUMENT = "slithir"
HELP = "Print the slithIR representation of the functions" HELP = "Print the slithIR representation of the functions"
@ -24,26 +39,16 @@ class PrinterSlithIR(AbstractPrinter):
continue continue
txt += "Contract {}\n".format(contract.name) txt += "Contract {}\n".format(contract.name)
for function in contract.functions: for function in contract.functions:
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n' txt = f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
for node in function.nodes: txt += _print_function(function)
if node.expression:
txt += "\t\tExpression: {}\n".format(node.expression)
txt += "\t\tIRs:\n"
for ir in node.irs:
txt += "\t\t\t{}\n".format(ir)
elif node.irs:
txt += "\t\tIRs:\n"
for ir in node.irs:
txt += "\t\t\t{}\n".format(ir)
for modifier in contract.modifiers: for modifier in contract.modifiers:
txt += "\tModifier {}\n".format(modifier.canonical_name) txt += "\tModifier {}\n".format(modifier.canonical_name)
for node in modifier.nodes: txt += _print_function(modifier)
txt += str(node) if self.slither.functions_top_level:
if node.expression: txt += "Top level functions"
txt += "\t\tExpression: {}\n".format(node.expression) for function in self.slither.functions_top_level:
txt += "\t\tIRs:\n" txt += f"\tFunction {function.canonical_name}\n"
for ir in node.irs: txt += _print_function(function)
txt += "\t\t\t{}\n".format(ir)
self.info(txt) self.info(txt)
res = self.generate_output(txt) res = self.generate_output(txt)
return res return res

@ -78,7 +78,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
# pylint: disable=raise-missing-from # pylint: disable=raise-missing-from
raise SlitherError(f"Invalid compilation: \n{str(e)}") raise SlitherError(f"Invalid compilation: \n{str(e)}")
for path, ast in crytic_compile.asts.items(): for path, ast in crytic_compile.asts.items():
self._parser.parse_contracts_from_loaded_json(ast, path) self._parser.parse_top_level_from_loaded_json(ast, path)
self.add_source_code(path) self.add_source_code(path)
if kwargs.get("generate_patches", False): if kwargs.get("generate_patches", False):
@ -120,7 +120,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self._parser = SlitherSolc(filename, self) self._parser = SlitherSolc(filename, self)
for c in contracts_json: for c in contracts_json:
self._parser.parse_contracts_from_json(c) self._parser.parse_top_level_from_json(c)
def _init_from_list(self, contract): def _init_from_list(self, contract):
self._parser = SlitherSolc("", self) self._parser = SlitherSolc("", self)
@ -129,7 +129,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
path = c["absolutePath"] path = c["absolutePath"]
else: else:
path = c["attributes"]["absolutePath"] path = c["attributes"]["absolutePath"]
self._parser.parse_contracts_from_loaded_json(c, path) self._parser.parse_top_level_from_loaded_json(c, path)
@property @property
def detectors(self): def detectors(self):

@ -1,5 +1,5 @@
import logging import logging
from typing import List from typing import List, TYPE_CHECKING
# pylint: disable= too-many-lines,import-outside-toplevel,too-many-branches,too-many-statements,too-many-nested-blocks # pylint: disable= too-many-lines,import-outside-toplevel,too-many-branches,too-many-statements,too-many-nested-blocks
from slither.core.declarations import ( from slither.core.declarations import (
@ -12,6 +12,7 @@ from slither.core.declarations import (
SolidityVariableComposed, SolidityVariableComposed,
Structure, Structure,
) )
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import Identifier, Literal from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import ( from slither.core.solidity_types import (
ArrayType, ArrayType,
@ -24,10 +25,9 @@ from slither.core.solidity_types import (
from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.variables.function_type_variable import FunctionTypeVariable from slither.core.variables.function_type_variable import FunctionTypeVariable
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations.codesize import CodeSize from slither.core.variables.variable import Variable
from slither.slithir.variables import TupleVariable from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations import ( from slither.slithir.operations import (
Assignment, Assignment,
Balance, Balance,
@ -61,6 +61,7 @@ from slither.slithir.operations import (
Unpack, Unpack,
Nop, Nop,
) )
from slither.slithir.operations.codesize import CodeSize
from slither.slithir.tmp_operations.argument import Argument, ArgumentType from slither.slithir.tmp_operations.argument import Argument, ArgumentType
from slither.slithir.tmp_operations.tmp_call import TmpCall from slither.slithir.tmp_operations.tmp_call import TmpCall
from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
@ -68,10 +69,13 @@ from slither.slithir.tmp_operations.tmp_new_contract import TmpNewContract
from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType
from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure
from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable
from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR from slither.slithir.variables import TupleVariable
from slither.utils.function import get_function_id from slither.utils.function import get_function_id
from slither.utils.type import export_nested_types_from_variable from slither.utils.type import export_nested_types_from_variable
from slither.slithir.exceptions import SlithIRError from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
if TYPE_CHECKING:
from slither.core.cfg.node import Node
logger = logging.getLogger("ConvertToIR") logger = logging.getLogger("ConvertToIR")
@ -428,15 +432,15 @@ def _convert_type_contract(ir, slither):
assignment.lvalue.set_type(ElementaryType("string")) assignment.lvalue.set_type(ElementaryType("string"))
return assignment return assignment
if isinstance(contract, ElementaryType):
print(contract.type)
raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown") raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown")
def propagate_types(ir, node): # pylint: disable=too-many-locals def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
# propagate the type # propagate the type
using_for = node.function.contract.using_for node_function = node.function
using_for = (
node_function.contract.using_for if isinstance(node_function, FunctionContract) else dict()
)
if isinstance(ir, OperationWithLValue): if isinstance(ir, OperationWithLValue):
# Force assignment in case of missing previous correct type # Force assignment in case of missing previous correct type
if not ir.lvalue.type: if not ir.lvalue.type:
@ -478,9 +482,11 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
# Convert HighLevelCall to LowLevelCall # Convert HighLevelCall to LowLevelCall
if isinstance(t, ElementaryType) and t.name == "address": if isinstance(t, ElementaryType) and t.name == "address":
# Cannot be a top level function with this.
assert isinstance(node_function, FunctionContract)
if ir.destination.name == "this": if ir.destination.name == "this":
return convert_type_of_high_and_internal_level_call( return convert_type_of_high_and_internal_level_call(
ir, node.function.contract ir, node_function.contract
) )
if can_be_low_level(ir): if can_be_low_level(ir):
return convert_to_low_level(ir) return convert_to_low_level(ir)
@ -578,22 +584,20 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
return _convert_type_contract(ir, node.function.slither) return _convert_type_contract(ir, node.function.slither)
left = ir.variable_left left = ir.variable_left
t = None t = None
ir_func = ir.function
# Handling of this.function_name usage # Handling of this.function_name usage
if ( if (
left == SolidityVariable("this") left == SolidityVariable("this")
and isinstance(ir.variable_right, Constant) and isinstance(ir.variable_right, Constant)
and str(ir.variable_right) in [x.name for x in ir.function.contract.functions] and isinstance(ir_func, FunctionContract)
and str(ir.variable_right) in [x.name for x in ir_func.contract.functions]
): ):
# Assumption that this.function_name can only compile if # Assumption that this.function_name can only compile if
# And the contract does not have two functions starting with function_name # And the contract does not have two functions starting with function_name
# Otherwise solc raises: # Otherwise solc raises:
# Error: Member "f" not unique after argument-dependent lookup in contract # Error: Member "f" not unique after argument-dependent lookup in contract
targeted_function = next( targeted_function = next(
( (x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
x
for x in ir.function.contract.functions
if x.name == str(ir.variable_right)
)
) )
t = _make_function_type(targeted_function) t = _make_function_type(targeted_function)
ir.lvalue.set_type(t) ir.lvalue.set_type(t)
@ -689,7 +693,6 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals
assert isinstance(ins, TmpCall) assert isinstance(ins, TmpCall)
if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType): if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType):
# If the call is made to a variable member, where the member is this # If the call is made to a variable member, where the member is this
# We need to convert it to a HighLelelCall and not an internal dynamic call # We need to convert it to a HighLelelCall and not an internal dynamic call
@ -753,7 +756,10 @@ def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals
# f.h(1); # f.h(1);
# } # }
# } # }
using_for = ins.node.function.contract.using_for node_func = ins.node.function
using_for = (
node_func.contract.using_for if isinstance(node_func, FunctionContract) else dict()
)
targeted_libraries = ( targeted_libraries = (
[] + using_for.get("*", []) + using_for.get(FunctionType([], []), []) [] + using_for.get("*", []) + using_for.get(FunctionType([], []), [])
@ -772,6 +778,8 @@ def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals
if len(candidates) == 1: if len(candidates) == 1:
lib_func = candidates[0] lib_func = candidates[0]
# Library must be from a contract
assert isinstance(lib_func, FunctionContract)
lib_call = LibraryCall( lib_call = LibraryCall(
lib_func.contract, lib_func.contract,
Constant(lib_func.name), Constant(lib_func.name),

@ -1,5 +1,6 @@
from slither.core.declarations import Modifier from slither.core.declarations import Modifier
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.slithir.operations.call import Call from slither.slithir.operations.call import Call
from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.operations.lvalue import OperationWithLValue
@ -7,10 +8,12 @@ from slither.slithir.operations.lvalue import OperationWithLValue
class InternalCall(Call, OperationWithLValue): # pylint: disable=too-many-instance-attributes class InternalCall(Call, OperationWithLValue): # pylint: disable=too-many-instance-attributes
def __init__(self, function, nbr_arguments, result, type_call): def __init__(self, function, nbr_arguments, result, type_call):
super().__init__() super().__init__()
self._contract_name = ""
if isinstance(function, Function): if isinstance(function, Function):
self._function = function self._function = function
self._function_name = function.name self._function_name = function.name
self._contract_name = function.contract_declarer.name if isinstance(function, FunctionContract):
self._contract_name = function.contract_declarer.name
else: else:
self._function = None self._function = None
self._function_name, self._contract_name = function self._function_name, self._contract_name = function

@ -1,15 +1,14 @@
import logging import logging
from typing import List, Dict, Callable, TYPE_CHECKING, Union from typing import List, Dict, Callable, TYPE_CHECKING, Union, Set
from slither.core.declarations import Modifier, Structure, Event from slither.core.declarations import Modifier, Event, EnumContract, StructureContract, Function
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.enum import Enum from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function import Function
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.solc_parsing.declarations.event import EventSolc from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.modifier import ModifierSolc from slither.solc_parsing.declarations.modifier import ModifierSolc
from slither.solc_parsing.declarations.structure import StructureSolc from slither.solc_parsing.declarations.structure_contract import StructureContractSolc
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
from slither.solc_parsing.solidity_types.type_parsing import parse_type from slither.solc_parsing.solidity_types.type_parsing import parse_type
from slither.solc_parsing.variables.state_variable import StateVariableSolc from slither.solc_parsing.variables.state_variable import StateVariableSolc
@ -44,7 +43,7 @@ class ContractSolc:
self._functions_parser: List[FunctionSolc] = [] self._functions_parser: List[FunctionSolc] = []
self._modifiers_parser: List[ModifierSolc] = [] self._modifiers_parser: List[ModifierSolc] = []
self._structures_parser: List[StructureSolc] = [] self._structures_parser: List[StructureContractSolc] = []
self._is_analyzed: bool = False self._is_analyzed: bool = False
@ -252,28 +251,13 @@ class ContractSolc:
return return
def _parse_struct(self, struct: Dict): def _parse_struct(self, struct: Dict):
if self.is_compact_ast:
name = struct["name"]
attributes = struct
else:
name = struct["attributes"][self.get_key()]
attributes = struct["attributes"]
if "canonicalName" in attributes:
canonicalName = attributes["canonicalName"]
else:
canonicalName = self._contract.name + "." + name
if self.get_children("members") in struct:
children = struct[self.get_children("members")]
else:
children = [] # empty struct
st = Structure() st = StructureContract()
st.set_contract(self._contract) st.set_contract(self._contract)
st.set_offset(struct["src"], self._contract.slither) st.set_offset(struct["src"], self._contract.slither)
st_parser = StructureSolc(st, name, canonicalName, children, self) st_parser = StructureContractSolc(st, struct, self)
self._contract.structures_as_dict[name] = st self._contract.structures_as_dict[st.name] = st
self._structures_parser.append(st_parser) self._structures_parser.append(st_parser)
def parse_structs(self): def parse_structs(self):
@ -307,17 +291,17 @@ class ContractSolc:
self._contract.add_variables_ordered([var]) self._contract.add_variables_ordered([var])
def _parse_modifier(self, modifier_data: Dict): def _parse_modifier(self, modifier_data: Dict):
modif = Modifier() modif = Modifier(self.slither)
modif.set_offset(modifier_data["src"], self._contract.slither) modif.set_offset(modifier_data["src"], self._contract.slither)
modif.set_contract(self._contract) modif.set_contract(self._contract)
modif.set_contract_declarer(self._contract) modif.set_contract_declarer(self._contract)
modif_parser = ModifierSolc(modif, modifier_data, self) modif_parser = ModifierSolc(modif, modifier_data, self, self.slither_parser)
self._contract.slither.add_modifier(modif) self._contract.slither.add_modifier(modif)
self._modifiers_no_params.append(modif_parser) self._modifiers_no_params.append(modif_parser)
self._modifiers_parser.append(modif_parser) self._modifiers_parser.append(modif_parser)
self._slither_parser.add_functions_parser(modif_parser) self._slither_parser.add_function_or_modifier_parser(modif_parser)
def parse_modifiers(self): def parse_modifiers(self):
for modifier in self._modifiersNotParsed: for modifier in self._modifiersNotParsed:
@ -325,17 +309,17 @@ class ContractSolc:
self._modifiersNotParsed = None self._modifiersNotParsed = None
def _parse_function(self, function_data: Dict): def _parse_function(self, function_data: Dict):
func = Function() func = FunctionContract(self.slither)
func.set_offset(function_data["src"], self._contract.slither) func.set_offset(function_data["src"], self._contract.slither)
func.set_contract(self._contract) func.set_contract(self._contract)
func.set_contract_declarer(self._contract) func.set_contract_declarer(self._contract)
func_parser = FunctionSolc(func, function_data, self) func_parser = FunctionSolc(func, function_data, self, self._slither_parser)
self._contract.slither.add_function(func) self._contract.slither.add_function(func)
self._functions_no_params.append(func_parser) self._functions_no_params.append(func_parser)
self._functions_parser.append(func_parser) self._functions_parser.append(func_parser)
self._slither_parser.add_functions_parser(func_parser) self._slither_parser.add_function_or_modifier_parser(func_parser)
def parse_functions(self): def parse_functions(self):
@ -396,7 +380,7 @@ class ContractSolc:
elements_no_params = self._functions_no_params elements_no_params = self._functions_no_params
getter = lambda c: c.functions_parser getter = lambda c: c.functions_parser
getter_available = lambda c: c.functions_declared getter_available = lambda c: c.functions_declared
Cls = Function Cls = FunctionContract
Cls_parser = FunctionSolc Cls_parser = FunctionSolc
functions = self._analyze_params_elements( functions = self._analyze_params_elements(
elements_no_params, elements_no_params,
@ -411,15 +395,56 @@ class ContractSolc:
self.log_incorrect_parsing(f"Missing params {e}") self.log_incorrect_parsing(f"Missing params {e}")
self._functions_no_params = [] self._functions_no_params = []
def _analyze_params_element( # pylint: disable=too-many-arguments
self,
Cls: Callable,
Cls_parser: Callable,
element_parser: FunctionSolc,
explored_reference_id: Set[int],
parser: List[FunctionSolc],
all_elements: Dict[str, Function],
):
elem = Cls(self.slither)
elem.set_contract(self._contract)
underlying_function = element_parser.underlying_function
# TopLevel function are not analyzed here
assert isinstance(underlying_function, FunctionContract)
elem.set_contract_declarer(underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"], self._contract.slither,
)
elem_parser = Cls_parser(
elem, element_parser.function_not_parsed, self, self.slither_parser
)
if (
element_parser.referenced_declaration
and element_parser.referenced_declaration in explored_reference_id
):
# Already added from other fathers
return
if element_parser.referenced_declaration:
explored_reference_id.add(element_parser.referenced_declaration)
elem_parser.analyze_params()
if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem)
else:
self._contract.slither.add_function(elem)
self._slither_parser.add_function_or_modifier_parser(elem_parser)
all_elements[elem.canonical_name] = elem
parser.append(elem_parser)
def _analyze_params_elements( # pylint: disable=too-many-arguments,too-many-locals def _analyze_params_elements( # pylint: disable=too-many-arguments,too-many-locals
self, self,
elements_no_params: List[FunctionSolc], elements_no_params: List[FunctionSolc],
getter: Callable[["ContractSolc"], List[FunctionSolc]], getter: Callable[["ContractSolc"], List[FunctionSolc]],
getter_available: Callable[[Contract], List[Function]], getter_available: Callable[[Contract], List[FunctionContract]],
Cls: Callable, Cls: Callable,
Cls_parser: Callable, Cls_parser: Callable,
parser: List[FunctionSolc], parser: List[FunctionSolc],
) -> Dict[str, Union[Function, Modifier]]: ) -> Dict[str, Union[FunctionContract, Modifier]]:
""" """
Analyze the parameters of the given elements (Function or Modifier). Analyze the parameters of the given elements (Function or Modifier).
The function iterates over the inheritance to create an instance or inherited elements (Function or Modifier) The function iterates over the inheritance to create an instance or inherited elements (Function or Modifier)
@ -433,29 +458,15 @@ class ContractSolc:
""" """
all_elements = {} all_elements = {}
explored_reference_id = set()
try: try:
for father in self._contract.inheritance: for father in self._contract.inheritance:
father_parser = self._slither_parser.underlying_contract_to_parser[father] father_parser = self._slither_parser.underlying_contract_to_parser[father]
for element_parser in getter(father_parser): for element_parser in getter(father_parser):
elem = Cls() self._analyze_params_element(
elem.set_contract(self._contract) Cls, Cls_parser, element_parser, explored_reference_id, parser, all_elements
elem.set_contract_declarer(element_parser.underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"], self._contract.slither,
) )
elem_parser = Cls_parser(elem, element_parser.function_not_parsed, self,)
elem_parser.analyze_params()
if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem)
else:
self._contract.slither.add_function(elem)
self._slither_parser.add_functions_parser(elem_parser)
all_elements[elem.canonical_name] = elem
parser.append(elem_parser)
accessible_elements = self._contract.available_elements_from_inheritances( accessible_elements = self._contract.available_elements_from_inheritances(
all_elements, getter_available all_elements, getter_available
) )
@ -573,12 +584,12 @@ class ContractSolc:
else: else:
values.append(child["attributes"][self.get_key()]) values.append(child["attributes"][self.get_key()])
new_enum = Enum(name, canonicalName, values) new_enum = EnumContract(name, canonicalName, values)
new_enum.set_contract(self._contract) new_enum.set_contract(self._contract)
new_enum.set_offset(enum["src"], self._contract.slither) new_enum.set_offset(enum["src"], self._contract.slither)
self._contract.enums_as_dict[canonicalName] = new_enum self._contract.enums_as_dict[canonicalName] = new_enum
def _analyze_struct(self, struct: StructureSolc): # pylint: disable=no-self-use def _analyze_struct(self, struct: StructureContractSolc): # pylint: disable=no-self-use
struct.analyze() struct.analyze()
def analyze_structs(self): def analyze_structs(self):

@ -8,6 +8,7 @@ from slither.core.declarations.function import (
ModifierStatements, ModifierStatements,
FunctionType, FunctionType,
) )
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import AssignmentOperation from slither.core.expressions import AssignmentOperation
from slither.core.variables.local_variable import LocalVariable from slither.core.variables.local_variable import LocalVariable
@ -48,9 +49,13 @@ class FunctionSolc:
# elems = [(type, name)] # elems = [(type, name)]
def __init__( def __init__(
self, function: Function, function_data: Dict, contract_parser: "ContractSolc", self,
function: Function,
function_data: Dict,
contract_parser: Optional["ContractSolc"],
slither_parser: "SlitherSolc",
): ):
self._slither_parser: "SlitherSolc" = contract_parser.slither_parser self._slither_parser: "SlitherSolc" = slither_parser
self._contract_parser = contract_parser self._contract_parser = contract_parser
self._function = function self._function = function
@ -95,7 +100,7 @@ class FunctionSolc:
return self._function return self._function
@property @property
def contract_parser(self) -> "ContractSolc": def contract_parser(self) -> Optional["ContractSolc"]:
return self._contract_parser return self._contract_parser
@property @property
@ -200,8 +205,9 @@ class FunctionSolc:
else: else:
self._function.function_type = FunctionType.NORMAL self._function.function_type = FunctionType.NORMAL
if self._function.name == self._function.contract_declarer.name: if isinstance(self._function, FunctionContract):
self._function.function_type = FunctionType.CONSTRUCTOR if self._function.name == self._function.contract_declarer.name:
self._function.function_type = FunctionType.CONSTRUCTOR
def _analyze_attributes(self): def _analyze_attributes(self):
if self.is_compact_ast: if self.is_compact_ast:
@ -332,8 +338,11 @@ class FunctionSolc:
def _new_yul_block(self, src: Union[str, Dict]) -> YulBlock: def _new_yul_block(self, src: Union[str, Dict]) -> YulBlock:
node = self._function.new_node(NodeType.ASSEMBLY, src) node = self._function.new_node(NodeType.ASSEMBLY, src)
contract = None
if isinstance(self._function, FunctionContract):
contract = self._function.contract
yul_object = YulBlock( yul_object = YulBlock(
self._function.contract, contract,
node, node,
[self._function.name, f"asm_{len(self._node_to_yulobject)}"], [self._function.name, f"asm_{len(self._node_to_yulobject)}"],
parent_func=self._function, parent_func=self._function,

@ -11,11 +11,18 @@ from slither.solc_parsing.declarations.function import FunctionSolc
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slitherSolc import SlitherSolc
class ModifierSolc(FunctionSolc): class ModifierSolc(FunctionSolc):
def __init__(self, modifier: Modifier, function_data: Dict, contract_parser: "ContractSolc"): def __init__(
super().__init__(modifier, function_data, contract_parser) self,
modifier: Modifier,
function_data: Dict,
contract_parser: "ContractSolc",
slither_parser: "SlitherSolc",
):
super().__init__(modifier, function_data, contract_parser, slither_parser)
# _modifier is equal to _function, but keep it here to prevent # _modifier is equal to _function, but keep it here to prevent
# confusion for mypy in underlying_function # confusion for mypy in underlying_function
self._modifier = modifier self._modifier = modifier

@ -1,17 +1,17 @@
""" """
Structure module Structure module
""" """
from typing import List, TYPE_CHECKING from typing import TYPE_CHECKING, Dict
from slither.core.declarations.structure import Structure
from slither.core.variables.structure_variable import StructureVariable from slither.core.variables.structure_variable import StructureVariable
from slither.solc_parsing.variables.structure_variable import StructureVariableSolc from slither.solc_parsing.variables.structure_variable import StructureVariableSolc
from slither.core.declarations.structure import Structure
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
class StructureSolc: # pylint: disable=too-few-public-methods class StructureContractSolc: # pylint: disable=too-few-public-methods
""" """
Structure class Structure class
""" """
@ -19,19 +19,28 @@ class StructureSolc: # pylint: disable=too-few-public-methods
# elems = [(type, name)] # elems = [(type, name)]
def __init__( # pylint: disable=too-many-arguments def __init__( # pylint: disable=too-many-arguments
self, self, st: Structure, struct: Dict, contract_parser: "ContractSolc",
st: Structure,
name: str,
canonicalName: str,
elems: List[str],
contract_parser: "ContractSolc",
): ):
if contract_parser.is_compact_ast:
name = struct["name"]
attributes = struct
else:
name = struct["attributes"][contract_parser.get_key()]
attributes = struct["attributes"]
if "canonicalName" in attributes:
canonicalName = attributes["canonicalName"]
else:
canonicalName = contract_parser.underlying_contract.name + "." + name
children = struct["members"] if "members" in struct else struct.get("children", [])
self._structure = st self._structure = st
st.name = name st.name = name
st.canonical_name = canonicalName st.canonical_name = canonicalName
self._contract_parser = contract_parser self._contract_parser = contract_parser
self._elemsNotParsed = elems self._elemsNotParsed = children
def analyze(self): def analyze(self):
for elem_to_parse in self._elemsNotParsed: for elem_to_parse in self._elemsNotParsed:

@ -0,0 +1,56 @@
"""
Structure module
"""
from typing import TYPE_CHECKING, Dict
from slither.core.declarations.structure import Structure
from slither.core.variables.structure_variable import StructureVariable
from slither.solc_parsing.variables.structure_variable import StructureVariableSolc
if TYPE_CHECKING:
from slither.solc_parsing.slitherSolc import SlitherSolc
class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
"""
Structure class
"""
# elems = [(type, name)]
def __init__( # pylint: disable=too-many-arguments
self, st: Structure, struct: Dict, slither_parser: "SlitherSolc",
):
if slither_parser.is_compact_ast:
name = struct["name"]
attributes = struct
else:
name = struct["attributes"][slither_parser.get_key()]
attributes = struct["attributes"]
if "canonicalName" in attributes:
canonicalName = attributes["canonicalName"]
else:
canonicalName = name
children = struct["members"] if "members" in struct else struct.get("children", [])
self._structure = st
st.name = name
st.canonical_name = canonicalName
self._slither_parser = slither_parser
self._elemsNotParsed = children
def analyze(self):
for elem_to_parse in self._elemsNotParsed:
elem = StructureVariable()
elem.set_structure(self._structure)
elem.set_offset(elem_to_parse["src"], self._slither_parser.core)
elem_parser = StructureVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self._slither_parser)
self._structure.elems[elem.name] = elem
self._structure.add_elem_in_order(elem.name)
self._elemsNotParsed = []

@ -1,10 +1,11 @@
import logging import logging
import re import re
from typing import Dict, TYPE_CHECKING, Optional, Union from typing import Dict, TYPE_CHECKING, Optional, Union, List, Tuple
from slither.core.declarations import Event, Enum, Structure from slither.core.declarations import Event, Enum, Structure
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_variables import ( from slither.core.declarations.solidity_variables import (
SOLIDITY_FUNCTIONS, SOLIDITY_FUNCTIONS,
SOLIDITY_VARIABLES, SOLIDITY_VARIABLES,
@ -12,6 +13,7 @@ from slither.core.declarations.solidity_variables import (
SolidityFunction, SolidityFunction,
SolidityVariable, SolidityVariable,
SolidityVariableComposed, SolidityVariableComposed,
SolidityImportPlaceHolder,
) )
from slither.core.expressions.assignment_operation import ( from slither.core.expressions.assignment_operation import (
AssignmentOperation, AssignmentOperation,
@ -43,6 +45,7 @@ from slither.core.solidity_types import (
MappingType, MappingType,
) )
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
from slither.solc_parsing.solidity_types.type_parsing import UnknownType, parse_type from slither.solc_parsing.solidity_types.type_parsing import UnknownType, parse_type
@ -50,6 +53,8 @@ if TYPE_CHECKING:
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.core.slither_core import SlitherCore
from slither.solc_parsing.slitherSolc import SlitherSolc
logger = logging.getLogger("ExpressionParsing") logger = logging.getLogger("ExpressionParsing")
@ -78,62 +83,85 @@ def get_pointer_name(variable: Variable):
return None return None
def find_variable( # pylint: disable=too-many-locals,too-many-statements def _find_variable_from_ref_declaration(
referenced_declaration: Optional[int],
all_contracts: List["Contract"],
all_functions_parser: List["FunctionSolc"],
) -> Optional[Union[Contract, Function]]:
if referenced_declaration is None:
return None
# id of the contracts is the referenced declaration
# This is not true for the functions, as we dont always have the referenced_declaration
# But maybe we could? (TODO)
for contract_candidate in all_contracts:
if contract_candidate.id == referenced_declaration:
return contract_candidate
for function_candidate in all_functions_parser:
if (
function_candidate.referenced_declaration == referenced_declaration
and not function_candidate.underlying_function.is_shadowed
):
return function_candidate.underlying_function
return None
def _find_variable_in_function_parser(
var_name: str, var_name: str,
caller_context: CallerContext, function_parser: Optional["FunctionSolc"],
referenced_declaration: Optional[int] = None, referenced_declaration: Optional[int] = None,
is_super=False, ) -> Optional[Variable]:
) -> Union[ if function_parser is None:
Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure, return None
]: # We look for variable declared with the referencedDeclaration attr
from slither.solc_parsing.declarations.contract import ContractSolc func_variables_renamed = function_parser.variables_renamed
from slither.solc_parsing.declarations.function import FunctionSolc if referenced_declaration and referenced_declaration in func_variables_renamed:
return func_variables_renamed[referenced_declaration].underlying_variable
# If not found, check for name
func_variables = function_parser.underlying_function.variables_as_dict
if var_name in func_variables:
return func_variables[var_name]
# A local variable can be a pointer
# for example
# function test(function(uint) internal returns(bool) t) interna{
# Will have a local variable t which will match the signature
# t(uint256)
func_variables_ptr = {
get_pointer_name(f): f for f in function_parser.underlying_function.variables
}
if var_name and var_name in func_variables_ptr:
return func_variables_ptr[var_name]
# variable are looked from the contract declarer return None
# functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
# the difference between function and variable come from the fact that an internal call, or an variable access
# in a function does not behave similariy, for example in:
# contract C{
# function f(){
# state_var = 1
# f2()
# }
# state_var will refer to C.state_var, no mater if C is inherited
# while f2() will refer to the function definition of the inherited contract (C.f2() in the context of C, or
# the contract inheriting from C)
# for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
# structure/enums cannot be shadowed
if isinstance(caller_context, ContractSolc):
function: Optional[FunctionSolc] = None def _find_top_level(
contract = caller_context.underlying_contract var_name: str, sl: "SlitherCore"
contract_declarer = caller_context.underlying_contract ) -> Optional[Union[Enum, Structure, SolidityVariable]]:
elif isinstance(caller_context, FunctionSolc): structures_top_level = sl.structures_top_level
function = caller_context for st in structures_top_level:
contract = function.underlying_function.contract if st.name == var_name:
contract_declarer = function.underlying_function.contract_declarer return st
else:
raise ParsingError("Incorrect caller context") enums_top_level = sl.enums_top_level
for enum in enums_top_level:
if function: if enum.name == var_name:
# We look for variable declared with the referencedDeclaration attr return enum
func_variables_renamed = function.variables_renamed
if referenced_declaration and referenced_declaration in func_variables_renamed: for import_directive in sl.import_directives:
return func_variables_renamed[referenced_declaration].underlying_variable if import_directive.alias == var_name:
# If not found, check for name return SolidityImportPlaceHolder(import_directive)
func_variables = function.underlying_function.variables_as_dict
if var_name in func_variables: return None
return func_variables[var_name]
# A local variable can be a pointer
# for example def _find_in_contract(
# function test(function(uint) internal returns(bool) t) interna{ var_name: str,
# Will have a local variable t which will match the signature contract: Optional[Contract],
# t(uint256) contract_declarer: Optional[Contract],
func_variables_ptr = { is_super: bool,
get_pointer_name(f): f for f in function.underlying_function.variables ) -> Optional[Union[Variable, Function, Contract, Event, Enum, Structure,]]:
} if contract is None or contract_declarer is None:
if var_name and var_name in func_variables_ptr: return None
return func_variables_ptr[var_name]
# variable are looked from the contract declarer # variable are looked from the contract declarer
contract_variables = contract_declarer.variables_as_dict contract_variables = contract_declarer.variables_as_dict
@ -178,11 +206,6 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if var_name in structures: if var_name in structures:
return structures[var_name] return structures[var_name]
structures_top_level = contract.slither.top_level_structures
for st in structures_top_level:
if st.name == var_name:
return st
events = contract.events_as_dict events = contract.events_as_dict
if var_name in events: if var_name in events:
return events[var_name] return events[var_name]
@ -191,42 +214,147 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if var_name in enums: if var_name in enums:
return enums[var_name] return enums[var_name]
enums_top_level = contract.slither.top_level_enums
for enum in enums_top_level:
if enum.name == var_name:
return enum
# If the enum is refered as its name rather than its canonicalName # If the enum is refered as its name rather than its canonicalName
enums = {e.name: e for e in contract.enums} enums = {e.name: e for e in contract.enums}
if var_name in enums: if var_name in enums:
return enums[var_name] return enums[var_name]
return None
def _find_variable_init(
caller_context: CallerContext,
) -> Tuple[List[Contract], Union[List["FunctionSolc"]], "SlitherCore", "SlitherSolc"]:
from slither.solc_parsing.slitherSolc import SlitherSolc
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
direct_contracts: List[Contract]
direct_functions_parser: List[FunctionSolc]
if isinstance(caller_context, SlitherSolc):
direct_contracts = []
direct_functions_parser = []
sl = caller_context.core
sl_parser = caller_context
elif isinstance(caller_context, ContractSolc):
direct_contracts = [caller_context.underlying_contract]
direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser
sl = caller_context.slither
sl_parser = caller_context.slither_parser
elif isinstance(caller_context, FunctionSolc):
if caller_context.contract_parser:
direct_contracts = [caller_context.contract_parser.underlying_contract]
direct_functions_parser = (
caller_context.contract_parser.functions_parser
+ caller_context.contract_parser.modifiers_parser
)
else:
# Top level functions
direct_contracts = []
direct_functions_parser = []
sl = caller_context.slither
sl_parser = caller_context.slither_parser
else:
raise SlitherError(
f"{type(caller_context)} ({caller_context} is not valid for find_variable"
)
return direct_contracts, direct_functions_parser, sl, sl_parser
def find_variable(
var_name: str,
caller_context: CallerContext,
referenced_declaration: Optional[int] = None,
is_super=False,
) -> Union[
Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
]:
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.contract import ContractSolc
# variable are looked from the contract declarer
# functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
# the difference between function and variable come from the fact that an internal call, or an variable access
# in a function does not behave similariy, for example in:
# contract C{
# function f(){
# state_var = 1
# f2()
# }
# state_var will refer to C.state_var, no mater if C is inherited
# while f2() will refer to the function definition of the inherited contract (C.f2() in the context of C, or
# the contract inheriting from C)
# for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
# structure/enums cannot be shadowed
direct_contracts, direct_functions_parser, sl, sl_parser = _find_variable_init(caller_context)
all_contracts = sl.contracts
all_functions_parser = sl_parser.all_functions_and_modifiers_parser
# Look for all references delcaration
# First look only in the context of function/contract
# Then look everywhere
# Because functions are copied between contracts, two functions can have the same ref
# So we need to first look with respect to the direct context
ret = _find_variable_from_ref_declaration(
referenced_declaration, direct_contracts, direct_functions_parser
)
if ret:
return ret
ret = _find_variable_from_ref_declaration(
referenced_declaration, all_contracts, all_functions_parser
)
if ret:
return ret
function_parser: Optional[FunctionSolc] = caller_context if isinstance(
caller_context, FunctionSolc
) else None
ret = _find_variable_in_function_parser(var_name, function_parser, referenced_declaration)
if ret:
return ret
contract: Optional[Contract] = None
contract_declarer: Optional[Contract] = None
if isinstance(caller_context, ContractSolc):
contract = caller_context.underlying_contract
contract_declarer = caller_context.underlying_contract
elif isinstance(caller_context, FunctionSolc):
underlying_func = caller_context.underlying_function
# If contract_parser is set to None, then underlying_function is a functionContract
assert isinstance(underlying_func, FunctionContract)
contract = underlying_func.contract
contract_declarer = underlying_func.contract_declarer
ret = _find_in_contract(var_name, contract, contract_declarer, is_super)
if ret:
return ret
# Could refer to any enum # Could refer to any enum
all_enumss = [c.enums_as_dict for c in contract.slither.contracts] all_enumss = [c.enums_as_dict for c in sl.contracts]
all_enums = {k: v for d in all_enumss for k, v in d.items()} all_enums = {k: v for d in all_enumss for k, v in d.items()}
if var_name in all_enums: if var_name in all_enums:
return all_enums[var_name] return all_enums[var_name]
contracts = sl.contracts_as_dict
if var_name in contracts:
return contracts[var_name]
if var_name in SOLIDITY_VARIABLES: if var_name in SOLIDITY_VARIABLES:
return SolidityVariable(var_name) return SolidityVariable(var_name)
if var_name in SOLIDITY_FUNCTIONS: if var_name in SOLIDITY_FUNCTIONS:
return SolidityFunction(var_name) return SolidityFunction(var_name)
contracts = contract.slither.contracts_as_dict # Top level must be at the end, if nothing else was found
if var_name in contracts: ret = _find_top_level(var_name, sl)
return contracts[var_name] if ret:
return ret
if referenced_declaration:
# id of the contracts is the referenced declaration
# This is not true for the functions, as we dont always have the referenced_declaration
# But maybe we could? (TODO)
for contract_candidate in contract.slither.contracts:
if contract_candidate.id == referenced_declaration:
return contract_candidate
for function_candidate in caller_context.slither_parser.all_functions_parser:
if function_candidate.referenced_declaration == referenced_declaration:
return function_candidate.underlying_function
raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context)) raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context))

@ -5,6 +5,10 @@ import re
from typing import List, Dict from typing import List, Dict
from slither.core.declarations import Contract from slither.core.declarations import Contract
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
@ -13,6 +17,9 @@ from slither.core.slither_core import SlitherCore
from slither.core.declarations.pragma_directive import Pragma from slither.core.declarations.pragma_directive import Pragma
from slither.core.declarations.import_directive import Import from slither.core.declarations.import_directive import Import
from slither.analyses.data_dependency.data_dependency import compute_dependency from slither.analyses.data_dependency.data_dependency import compute_dependency
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.exceptions import VariableNotFound
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("SlitherSolcParsing") logger = logging.getLogger("SlitherSolcParsing")
@ -29,11 +36,14 @@ class SlitherSolc:
self._analyzed = False self._analyzed = False
self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict() self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
self._structures_top_level_parser: List[StructureTopLevelSolc] = []
self._variables_top_level_parser: List[TopLevelVariableSolc] = []
self._functions_top_level_parser: List[FunctionSolc] = []
self._is_compact_ast = False self._is_compact_ast = False
self._core: SlitherCore = core self._core: SlitherCore = core
self._all_functions_parser: List[FunctionSolc] = [] self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0 self._top_level_contracts_counter = 0
@ -42,11 +52,11 @@ class SlitherSolc:
return self._core return self._core
@property @property
def all_functions_parser(self) -> List[FunctionSolc]: def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
return self._all_functions_parser return self._all_functions_and_modifier_parser
def add_functions_parser(self, f: FunctionSolc): def add_function_or_modifier_parser(self, f: FunctionSolc):
self._all_functions_parser.append(f) self._all_functions_and_modifier_parser.append(f)
@property @property
def underlying_contract_to_parser(self) -> Dict[Contract, ContractSolc]: def underlying_contract_to_parser(self) -> Dict[Contract, ContractSolc]:
@ -79,19 +89,19 @@ class SlitherSolc:
################################################################################### ###################################################################################
################################################################################### ###################################################################################
def parse_contracts_from_json(self, json_data: str) -> bool: def parse_top_level_from_json(self, json_data: str) -> bool:
try: try:
data_loaded = json.loads(json_data) data_loaded = json.loads(json_data)
# Truffle AST # Truffle AST
if "ast" in data_loaded: if "ast" in data_loaded:
self.parse_contracts_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"]) self.parse_top_level_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"])
return True return True
# solc AST, where the non-json text was removed # solc AST, where the non-json text was removed
if "attributes" in data_loaded: if "attributes" in data_loaded:
filename = data_loaded["attributes"]["absolutePath"] filename = data_loaded["attributes"]["absolutePath"]
else: else:
filename = data_loaded["absolutePath"] filename = data_loaded["absolutePath"]
self.parse_contracts_from_loaded_json(data_loaded, filename) self.parse_top_level_from_loaded_json(data_loaded, filename)
return True return True
except ValueError: except ValueError:
@ -102,13 +112,40 @@ class SlitherSolc:
json_data = json_data[first:last] json_data = json_data[first:last]
data_loaded = json.loads(json_data) data_loaded = json.loads(json_data)
self.parse_contracts_from_loaded_json(data_loaded, filename) self.parse_top_level_from_loaded_json(data_loaded, filename)
return True return True
return False return False
def parse_contracts_from_loaded_json( def _parse_enum(self, top_level_data: Dict):
if self.is_compact_ast:
name = top_level_data["name"]
canonicalName = top_level_data["canonicalName"]
else:
name = top_level_data["attributes"][self.get_key()]
if "canonicalName" in top_level_data["attributes"]:
canonicalName = top_level_data["attributes"]["canonicalName"]
else:
canonicalName = name
values = []
children = (
top_level_data["members"]
if "members" in top_level_data
else top_level_data.get("children", [])
)
for child in children:
assert child[self.get_key()] == "EnumValue"
if self.is_compact_ast:
values.append(child["name"])
else:
values.append(child["attributes"][self.get_key()])
enum = EnumTopLevel(name, canonicalName, values)
enum.set_offset(top_level_data["src"], self._core)
self.core.enums_top_level.append(enum)
def parse_top_level_from_loaded_json(
self, data_loaded: Dict, filename: str self, data_loaded: Dict, filename: str
): # pylint: disable=too-many-branches ): # pylint: disable=too-many-branches,too-many-statements
if "nodeType" in data_loaded: if "nodeType" in data_loaded:
self._is_compact_ast = True self._is_compact_ast = True
@ -128,68 +165,62 @@ class SlitherSolc:
logger.error("solc version is not supported") logger.error("solc version is not supported")
return return
for contract_data in data_loaded[self.get_children()]: for top_level_data in data_loaded[self.get_children()]:
assert contract_data[self.get_key()] in [ if top_level_data[self.get_key()] == "ContractDefinition":
"ContractDefinition",
"PragmaDirective",
"ImportDirective",
"StructDefinition",
"EnumDefinition",
]
if contract_data[self.get_key()] == "ContractDefinition":
contract = Contract() contract = Contract()
contract_parser = ContractSolc(self, contract, contract_data) contract_parser = ContractSolc(self, contract, top_level_data)
if "src" in contract_data: if "src" in top_level_data:
contract.set_offset(contract_data["src"], self._core) contract.set_offset(top_level_data["src"], self._core)
self._underlying_contract_to_parser[contract] = contract_parser self._underlying_contract_to_parser[contract] = contract_parser
elif contract_data[self.get_key()] == "PragmaDirective": elif top_level_data[self.get_key()] == "PragmaDirective":
if self._is_compact_ast: if self._is_compact_ast:
pragma = Pragma(contract_data["literals"]) pragma = Pragma(top_level_data["literals"])
else: else:
pragma = Pragma(contract_data["attributes"]["literals"]) pragma = Pragma(top_level_data["attributes"]["literals"])
pragma.set_offset(contract_data["src"], self._core) pragma.set_offset(top_level_data["src"], self._core)
self._core.pragma_directives.append(pragma) self._core.pragma_directives.append(pragma)
elif contract_data[self.get_key()] == "ImportDirective": elif top_level_data[self.get_key()] == "ImportDirective":
if self.is_compact_ast: if self.is_compact_ast:
import_directive = Import(contract_data["absolutePath"]) import_directive = Import(top_level_data["absolutePath"])
# TODO investigate unitAlias in version < 0.7 and legacy ast
if "unitAlias" in top_level_data:
import_directive.alias = top_level_data["unitAlias"]
else: else:
import_directive = Import(contract_data["attributes"].get("absolutePath", "")) import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(contract_data["src"], self._core) import_directive.set_offset(top_level_data["src"], self._core)
self._core.import_directives.append(import_directive) self._core.import_directives.append(import_directive)
elif contract_data[self.get_key()] in [ elif top_level_data[self.get_key()] == "StructDefinition":
"StructDefinition", st = StructureTopLevel()
"EnumDefinition", st.set_offset(top_level_data["src"], self._core)
]: st_parser = StructureTopLevelSolc(st, top_level_data, self)
# This can only happen for top-level structure and enum
# They were introduced with 0.6.5 self._core.structures_top_level.append(st)
assert self._is_compact_ast # Do not support top level definition for legacy AST self._structures_top_level_parser.append(st_parser)
fake_contract_data = {
"name": f"SlitherInternalTopLevelContract{self._top_level_contracts_counter}", elif top_level_data[self.get_key()] == "EnumDefinition":
"id": -1000 # Note enum don't need a complex parser, so everything is directly done
+ self._top_level_contracts_counter, # TODO: determine if collission possible self._parse_enum(top_level_data)
"linearizedBaseContracts": [],
"fullyImplemented": True, elif top_level_data[self.get_key()] == "VariableDeclaration":
"contractKind": "SLitherInternal", var = TopLevelVariable()
} var_parser = TopLevelVariableSolc(var, top_level_data)
self._top_level_contracts_counter += 1 var.set_offset(top_level_data["src"], self._core)
contract = Contract()
top_level_contract = ContractSolc(self, contract, fake_contract_data) self._core.variables_top_level.append(var)
contract.is_top_level = True self._variables_top_level_parser.append(var_parser)
contract.set_offset(contract_data["src"], self._core) elif top_level_data[self.get_key()] == "FunctionDefinition":
func = FunctionTopLevel(self.core)
if contract_data[self.get_key()] == "StructDefinition": func_parser = FunctionSolc(func, top_level_data, None, self)
top_level_contract.structures_not_parsed.append(
contract_data
) # Todo add proper setters
else:
top_level_contract.enums_not_parsed.append(
contract_data
) # Todo add proper setters
self._underlying_contract_to_parser[contract] = top_level_contract self._core.functions_top_level.append(func)
self._functions_top_level_parser.append(func_parser)
self.add_function_or_modifier_parser(func_parser)
else:
raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")
def _parse_source_unit(self, data: Dict, filename: str): def _parse_source_unit(self, data: Dict, filename: str):
if data[self.get_key()] != "SourceUnit": if data[self.get_key()] != "SourceUnit":
@ -409,6 +440,8 @@ Please rename it, this name is reserved for Slither's internals"""
for lib in libraries: for lib in libraries:
self._analyze_struct_events(lib) self._analyze_struct_events(lib)
self._analyze_top_level_structures()
# Start with the contracts without inheritance # Start with the contracts without inheritance
# Analyze a contract only if all its fathers # Analyze a contract only if all its fathers
# Were analyzed # Were analyzed
@ -478,16 +511,44 @@ Please rename it, this name is reserved for Slither's internals"""
contract.set_is_analyzed(True) contract.set_is_analyzed(True)
def _analyze_top_level_structures(self):
try:
for struct in self._structures_top_level_parser:
struct.analyze()
except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing struct {e} during top level structure analyze") from e
def _analyze_top_level_variables(self):
try:
for var in self._variables_top_level_parser:
var.analyze(self)
except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing struct {e} during top level structure analyze") from e
def _analyze_params_top_level_function(self):
for func_parser in self._functions_top_level_parser:
func_parser.analyze_params()
self.core.add_function(func_parser.underlying_function)
def _analyze_content_top_level_function(self):
try:
for func_parser in self._functions_top_level_parser:
func_parser.analyze_content()
except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing {e} during top level function analyze") from e
def _analyze_variables_modifiers_functions(self, contract: ContractSolc): def _analyze_variables_modifiers_functions(self, contract: ContractSolc):
# State variables, modifiers and functions can refer to anything # State variables, modifiers and functions can refer to anything
contract.analyze_params_modifiers() contract.analyze_params_modifiers()
contract.analyze_params_functions() contract.analyze_params_functions()
self._analyze_params_top_level_function()
contract.analyze_state_variables() contract.analyze_state_variables()
contract.analyze_content_modifiers() contract.analyze_content_modifiers()
contract.analyze_content_functions() contract.analyze_content_functions()
self._analyze_content_top_level_function()
contract.set_is_analyzed(True) contract.set_is_analyzed(True)
@ -508,6 +569,10 @@ Please rename it, this name is reserved for Slither's internals"""
) )
contract.convert_expression_to_slithir_ssa() contract.convert_expression_to_slithir_ssa()
for func in self.core.functions_top_level:
func.generate_slithir_and_analyze()
func.generate_slithir_ssa(dict())
self._core.propagate_function_calls() self._core.propagate_function_calls()
for contract in self._core.contracts: for contract in self._core.contracts:
contract.fix_phi() contract.fix_phi()

@ -2,26 +2,23 @@ import logging
import re import re
from typing import List, TYPE_CHECKING, Union, Dict from typing import List, TYPE_CHECKING, Union, Dict
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions.literal import Literal
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.elementary_type import ( from slither.core.solidity_types.elementary_type import (
ElementaryType, ElementaryType,
ElementaryTypeName, ElementaryTypeName,
) )
from slither.core.solidity_types.function_type import FunctionType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.solidity_types.user_defined_type import UserDefinedType from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.function_type import FunctionType
from slither.core.variables.function_type_variable import FunctionTypeVariable from slither.core.variables.function_type_variable import FunctionTypeVariable
from slither.core.declarations.contract import Contract
from slither.core.expressions.literal import Literal
from slither.solc_parsing.exceptions import ParsingError from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.declarations import Structure, Enum from slither.core.declarations import Structure, Enum
from slither.core.declarations.contract import Contract
logger = logging.getLogger("TypeParsing") logger = logging.getLogger("TypeParsing")
@ -37,12 +34,14 @@ class UnknownType: # pylint: disable=too-few-public-methods
return self._name return self._name
def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,too-many-statements def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,too-many-statements,too-many-arguments
name: str, name: str,
contract: Contract, functions_direct_access: List["Function"],
contracts: List[Contract], contracts_direct_access: List["Contract"],
structures: List["Structure"], structures_direct_access: List["Structure"],
enums: List["Enum"], all_structures: List["Structure"],
enums_direct_access: List["Enum"],
all_enums: List["Enum"],
) -> Type: ) -> Type:
name_elementary = name.split(" ")[0] name_elementary = name.split(" ")[0]
if "[" in name_elementary: if "[" in name_elementary:
@ -60,19 +59,20 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
name_contract = name_contract[len("contract ") :] name_contract = name_contract[len("contract ") :]
if name_contract.startswith("library "): if name_contract.startswith("library "):
name_contract = name_contract[len("library ") :] name_contract = name_contract[len("library ") :]
var_type = next((c for c in contracts if c.name == name_contract), None) var_type = next((c for c in contracts_direct_access if c.name == name_contract), None)
if not var_type: if not var_type:
var_type = next((st for st in structures if st.name == name), None) var_type = next((st for st in structures_direct_access if st.name == name), None)
if not var_type: if not var_type:
var_type = next((e for e in enums if e.name == name), None) var_type = next((e for e in enums_direct_access if e.name == name), None)
if not var_type: if not var_type:
# any contract can refer to another contract's enum # any contract can refer to another contract's enum
enum_name = name enum_name = name
if enum_name.startswith("enum "): if enum_name.startswith("enum "):
enum_name = enum_name[len("enum ") :] enum_name = enum_name[len("enum ") :]
all_enums = [c.enums for c in contracts] # all_enums = [c.enums for c in contracts]
all_enums = [item for sublist in all_enums for item in sublist] # all_enums = [item for sublist in all_enums for item in sublist]
# all_enums += contract.slither.enums_top_level
var_type = next((e for e in all_enums if e.name == enum_name), None) var_type = next((e for e in all_enums if e.name == enum_name), None)
if not var_type: if not var_type:
var_type = next((e for e in all_enums if e.canonical_name == enum_name), None) var_type = next((e for e in all_enums if e.canonical_name == enum_name), None)
@ -82,8 +82,9 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
if name_struct.startswith("struct "): if name_struct.startswith("struct "):
name_struct = name_struct[len("struct ") :] name_struct = name_struct[len("struct ") :]
name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end
all_structures = [c.structures for c in contracts] # all_structures = [c.structures for c in contracts]
all_structures = [item for sublist in all_structures for item in sublist] # all_structures = [item for sublist in all_structures for item in sublist]
# all_structures += contract.slither.structures_top_level
var_type = next((st for st in all_structures if st.name == name_struct), None) var_type = next((st for st in all_structures if st.name == name_struct), None)
if not var_type: if not var_type:
var_type = next((st for st in all_structures if st.canonical_name == name_struct), None) var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
@ -98,7 +99,7 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
return ArrayType(UserDefinedType(var_type), Literal(depth, "uint256")) return ArrayType(UserDefinedType(var_type), Literal(depth, "uint256"))
if not var_type: if not var_type:
var_type = next((f for f in contract.functions if f.name == name), None) var_type = next((f for f in functions_direct_access if f.name == name), None)
if not var_type: if not var_type:
if name.startswith("function "): if name.startswith("function "):
found = re.findall( found = re.findall(
@ -110,10 +111,27 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
[v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else [] [v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else []
) )
params = [ params = [
_find_from_type_name(p, contract, contracts, structures, enums) for p in params _find_from_type_name(
p,
functions_direct_access,
contracts_direct_access,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
)
for p in params
] ]
return_values = [ return_values = [
_find_from_type_name(r, contract, contracts, structures, enums) _find_from_type_name(
r,
functions_direct_access,
contracts_direct_access,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
)
for r in return_values for r in return_values
] ]
params_vars = [] params_vars = []
@ -140,8 +158,24 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
from_ = found[0][0] from_ = found[0][0]
to_ = found[0][1] to_ = found[0][1]
from_type = _find_from_type_name(from_, contract, contracts, structures, enums) from_type = _find_from_type_name(
to_type = _find_from_type_name(to_, contract, contracts, structures, enums) from_,
functions_direct_access,
contracts_direct_access,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
)
to_type = _find_from_type_name(
to_,
functions_direct_access,
contracts_direct_access,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
)
return MappingType(from_type, to_type) return MappingType(from_type, to_type)
@ -158,29 +192,71 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.slitherSolc import SlitherSolc
if isinstance(caller_context, ContractSolc):
contract = caller_context.underlying_contract # Note: for convenicence top level functions use the same parser than function in contract
contract_parser = caller_context # but contract_parser is set to None
is_compact_ast = caller_context.is_compact_ast if isinstance(caller_context, SlitherSolc) or (
elif isinstance(caller_context, FunctionSolc): isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
contract = caller_context.underlying_function.contract ):
contract_parser = caller_context.contract_parser if isinstance(caller_context, SlitherSolc):
is_compact_ast = caller_context.is_compact_ast sl = caller_context.core
next_context = caller_context
else:
assert isinstance(caller_context, FunctionSolc)
sl = caller_context.underlying_function.slither
next_context = caller_context.slither_parser
structures_direct_access = sl.structures_top_level
all_structuress = [c.structures for c in sl.contracts]
all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += structures_direct_access
enums_direct_access = sl.enums_top_level
all_enumss = [c.enums for c in sl.contracts]
all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += enums_direct_access
contracts = sl.contracts
functions = []
elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
if isinstance(caller_context, FunctionSolc):
underlying_func = caller_context.underlying_function
# If contract_parser is set to None, then underlying_function is a functionContract
# See note above
assert isinstance(underlying_func, FunctionContract)
contract = underlying_func.contract
next_context = caller_context.contract_parser
else:
contract = caller_context.underlying_contract
next_context = caller_context
structures_direct_access = contract.structures + contract.slither.structures_top_level
all_structuress = [c.structures for c in contract.slither.contracts]
all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += contract.slither.structures_top_level
enums_direct_access = contract.enums + contract.slither.enums_top_level
all_enumss = [c.enums for c in contract.slither.contracts]
all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += contract.slither.enums_top_level
contracts = contract.slither.contracts
functions = contract.functions + contract.modifiers
else: else:
raise ParsingError(f"Incorrect caller context: {type(caller_context)}") raise ParsingError(f"Incorrect caller context: {type(caller_context)}")
is_compact_ast = caller_context.is_compact_ast
if is_compact_ast: if is_compact_ast:
key = "nodeType" key = "nodeType"
else: else:
key = "name" key = "name"
structures = contract.structures + contract.slither.top_level_structures
enums = contract.enums + contract.slither.top_level_enums
contracts = contract.slither.contracts
if isinstance(t, UnknownType): if isinstance(t, UnknownType):
return _find_from_type_name(t.name, contract, contracts, structures, enums) return _find_from_type_name(
t.name,
functions,
contracts,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
)
if t[key] == "ElementaryTypeName": if t[key] == "ElementaryTypeName":
if is_compact_ast: if is_compact_ast:
@ -190,13 +266,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if t[key] == "UserDefinedTypeName": if t[key] == "UserDefinedTypeName":
if is_compact_ast: if is_compact_ast:
return _find_from_type_name( return _find_from_type_name(
t["typeDescriptions"]["typeString"], contract, contracts, structures, enums, t["typeDescriptions"]["typeString"],
functions,
contracts,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
) )
# Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type'). # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
type_name_key = "type" if "type" in t["attributes"] else key type_name_key = "type" if "type" in t["attributes"] else key
return _find_from_type_name( return _find_from_type_name(
t["attributes"][type_name_key], contract, contracts, structures, enums t["attributes"][type_name_key],
functions,
contracts,
structures_direct_access,
all_structures,
enums_direct_access,
all_enums,
) )
if t[key] == "ArrayTypeName": if t[key] == "ArrayTypeName":
@ -204,25 +292,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if is_compact_ast: if is_compact_ast:
if t.get("length", None): if t.get("length", None):
length = parse_expression(t["length"], caller_context) length = parse_expression(t["length"], caller_context)
array_type = parse_type(t["baseType"], contract_parser) array_type = parse_type(t["baseType"], next_context)
else: else:
if len(t["children"]) == 2: if len(t["children"]) == 2:
length = parse_expression(t["children"][1], caller_context) length = parse_expression(t["children"][1], caller_context)
else: else:
assert len(t["children"]) == 1 assert len(t["children"]) == 1
array_type = parse_type(t["children"][0], contract_parser) array_type = parse_type(t["children"][0], next_context)
return ArrayType(array_type, length) return ArrayType(array_type, length)
if t[key] == "Mapping": if t[key] == "Mapping":
if is_compact_ast: if is_compact_ast:
mappingFrom = parse_type(t["keyType"], contract_parser) mappingFrom = parse_type(t["keyType"], next_context)
mappingTo = parse_type(t["valueType"], contract_parser) mappingTo = parse_type(t["valueType"], next_context)
else: else:
assert len(t["children"]) == 2 assert len(t["children"]) == 2
mappingFrom = parse_type(t["children"][0], contract_parser) mappingFrom = parse_type(t["children"][0], next_context)
mappingTo = parse_type(t["children"][1], contract_parser) mappingTo = parse_type(t["children"][1], next_context)
return MappingType(mappingFrom, mappingTo) return MappingType(mappingFrom, mappingTo)

@ -0,0 +1,15 @@
from typing import Dict
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.solc_parsing.variables.variable_declaration import VariableDeclarationSolc
class TopLevelVariableSolc(VariableDeclarationSolc):
def __init__(self, variable: TopLevelVariable, variable_data: Dict):
super().__init__(variable, variable_data)
@property
def underlying_variable(self) -> TopLevelVariable:
# Todo: Not sure how to overcome this with mypy
assert isinstance(self._variable, TopLevelVariable)
return self._variable

@ -8,6 +8,7 @@ from slither.core.declarations import (
SolidityFunction, SolidityFunction,
Contract, Contract,
) )
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import ( from slither.core.expressions import (
Literal, Literal,
AssignmentOperation, AssignmentOperation,
@ -113,7 +114,9 @@ class YulScope(metaclass=abc.ABCMeta):
"_parent_func", "_parent_func",
] ]
def __init__(self, contract: Contract, yul_id: List[str], parent_func: Function = None): def __init__(
self, contract: Optional[Contract], yul_id: List[str], parent_func: Function = None
):
self._contract = contract self._contract = contract
self._id: List[str] = yul_id self._id: List[str] = yul_id
self._yul_local_variables: List[YulLocalVariable] = [] self._yul_local_variables: List[YulLocalVariable] = []
@ -125,7 +128,7 @@ class YulScope(metaclass=abc.ABCMeta):
return self._id return self._id
@property @property
def contract(self) -> Contract: def contract(self) -> Optional[Contract]:
return self._contract return self._contract
@property @property
@ -205,6 +208,7 @@ class YulFunction(YulScope):
func.set_visibility("private") func.set_visibility("private")
func.set_offset(ast["src"], root.slither) func.set_offset(ast["src"], root.slither)
func.set_contract(root.contract) func.set_contract(root.contract)
func.slither = root.slither
func.set_contract_declarer(root.contract) func.set_contract_declarer(root.contract)
func.scope = root.id func.scope = root.id
func.is_implemented = True func.is_implemented = True
@ -267,7 +271,7 @@ class YulBlock(YulScope):
__slots__ = ["_entrypoint", "_parent_func", "_nodes"] __slots__ = ["_entrypoint", "_parent_func", "_nodes"]
def __init__(self, contract: Contract, entrypoint: Node, yul_id: List[str], **kwargs): def __init__(self, contract: Optional[Contract], entrypoint: Node, yul_id: List[str], **kwargs):
super().__init__(contract, yul_id, **kwargs) super().__init__(contract, yul_id, **kwargs)
self._entrypoint: YulNode = YulNode(entrypoint, self) self._entrypoint: YulNode = YulNode(entrypoint, self)
@ -329,7 +333,7 @@ def convert_yul_block(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode: def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
func = Function() func = FunctionContract(root.slither)
yul_function = YulFunction(func, root, ast) yul_function = YulFunction(func, root, ast)
root.contract.add_function(func) root.contract.add_function(func)

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

@ -1,4 +1,3 @@
{ {
"C": {}, "C": {}
"SlitherInternalTopLevelContract0": {}
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save