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,
)
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
if TYPE_CHECKING:
from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
from slither.core.declarations import Enum, Event, Modifier
from slither.core.declarations import Structure
from slither.core.declarations import Enum, Event, Modifier, EnumContract, StructureContract
from slither.slithir.variables.variable import SlithIRVariable
from slither.core.variables.variable import Variable
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) { ..
self._explicit_base_constructor_calls: List["Contract"] = []
self._enums: Dict[str, "Enum"] = {}
self._structures: Dict[str, "Structure"] = {}
self._enums: Dict[str, "EnumContract"] = {}
self._structures: Dict[str, "StructureContract"] = {}
self._events: Dict[str, "Event"] = {}
self._variables: Dict[str, "StateVariable"] = {}
self._variables_ordered: List["StateVariable"] = []
@ -135,28 +134,28 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
###################################################################################
@property
def structures(self) -> List["Structure"]:
def structures(self) -> List["StructureContract"]:
"""
list(Structure): List of the structures
"""
return list(self._structures.values())
@property
def structures_inherited(self) -> List["Structure"]:
def structures_inherited(self) -> List["StructureContract"]:
"""
list(Structure): List of the inherited structures
"""
return [s for s in self.structures if s.contract != self]
@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)
"""
return [s for s in self.structures if s.contract == self]
@property
def structures_as_dict(self) -> Dict[str, "Structure"]:
def structures_as_dict(self) -> Dict[str, "StructureContract"]:
return self._structures
# endregion
@ -167,25 +166,25 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
###################################################################################
@property
def enums(self) -> List["Enum"]:
def enums(self) -> List["EnumContract"]:
return list(self._enums.values())
@property
def enums_inherited(self) -> List["Enum"]:
def enums_inherited(self) -> List["EnumContract"]:
"""
list(Enum): List of the inherited enums
"""
return [e for e in self.enums if e.contract != self]
@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)
"""
return [e for e in self.enums if e.contract == self]
@property
def enums_as_dict(self) -> Dict[str, "Enum"]:
def enums_as_dict(self) -> Dict[str, "EnumContract"]:
return self._enums
# endregion
@ -1090,11 +1089,13 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._is_incorrectly_parsed = incorrect
def add_constructor_variables(self):
from slither.core.declarations.function_contract import FunctionContract
if self.state_variables:
for (idx, variable_candidate) in enumerate(self.state_variables):
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_contract(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):
if variable_candidate.expression and variable_candidate.is_constant:
constructor_variable = Function()
constructor_variable = FunctionContract(self.slither)
constructor_variable.set_function_type(
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.children.child_contract import ChildContract
if TYPE_CHECKING:
from slither.core.declarations import Contract
class Enum(ChildContract, SourceMapping):
class Enum(SourceMapping):
def __init__(self, name: str, canonical_name: str, values: List[str]):
super().__init__()
self._name = name
@ -26,13 +22,5 @@ class Enum(ChildContract, SourceMapping):
def values(self) -> List[str]:
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):
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
"""
import logging
from abc import ABCMeta, abstractmethod
from collections import namedtuple
from enum import Enum
from itertools import groupby
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 (
SolidityFunction,
SolidityVariable,
@ -24,7 +23,6 @@ from slither.core.solidity_types import UserDefinedType
from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.utils.utils import unroll
@ -46,6 +44,7 @@ if TYPE_CHECKING:
from slither.slithir.operations import Operation
from slither.slither import Slither
from slither.core.cfg.node import NodeType
from slither.core.slither_core import SlitherCore
LOGGER = logging.getLogger("Function")
ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@ -103,14 +102,12 @@ def _filter_state_variables_written(expressions: List["Expression"]):
return ret
class Function(
ChildContract, ChildInheritance, SourceMapping
): # pylint: disable=too-many-public-methods
class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
"""
Function class
"""
def __init__(self):
def __init__(self, slither: "SlitherCore"):
super().__init__()
self._scope: List[str] = []
self._name: Optional[str] = None
@ -206,6 +203,8 @@ class Function(
self._canonical_name: Optional[str] = None
self._is_protected: Optional[bool] = None
self._slither: "SlitherCore" = slither
###################################################################################
###################################################################################
# region General properties
@ -260,20 +259,13 @@ class Function(
return self._full_name
@property
@abstractmethod
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
return ""
@property
def contains_assembly(self) -> bool:
@ -320,16 +312,12 @@ class Function(
return self._can_reenter
@property
def slither(self) -> "Slither":
return self.contract.slither
def slither(self) -> "SlitherCore":
return self._slither
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
@slither.setter
def slither(self, sl: "SlitherCore"):
self._slither = sl
# endregion
###################################################################################
@ -978,16 +966,9 @@ class Function(
###################################################################################
@property
@abstractmethod
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]
pass
# endregion
###################################################################################
@ -1400,25 +1381,11 @@ class Function(
"""
return variable in self.variables_written
@abstractmethod
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],
)
pass
def is_protected(self) -> bool:
"""
@ -1684,18 +1651,9 @@ class Function(
self._analyze_read_write()
self._analyze_calls()
@abstractmethod
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)
pass
def update_read_write_using_ssa(self):
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
@ -5,10 +7,19 @@ class Import(SourceMapping):
def __init__(self, filename: str):
super().__init__()
self._filename = filename
self._alias: Optional[str] = None
@property
def filename(self) -> str:
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):
return self.filename

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

@ -1,10 +1,13 @@
# 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.solidity_types import ElementaryType, TypeInformation
from slither.exceptions import SlitherException
if TYPE_CHECKING:
from slither.core.declarations import Import
SOLIDITY_VARIABLES = {
"now": "uint256",
"this": "address",
@ -31,7 +34,6 @@ SOLIDITY_VARIABLES_COMPOSED = {
"tx.origin": "address",
}
SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"gasleft()": ["uint256"],
"assert(bool)": [],
@ -181,3 +183,34 @@ class SolidityFunction:
def __hash__(self):
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 slither.core.children.child_contract import ChildContract
from slither.core.source_mapping.source_mapping import SourceMapping
if TYPE_CHECKING:
from slither.core.variables.structure_variable import StructureVariable
class Structure(ChildContract, SourceMapping):
class Structure(SourceMapping):
def __init__(self):
super().__init__()
self._name = None
@ -39,14 +38,6 @@ class Structure(ChildContract, SourceMapping):
def add_elem_in_order(self, s: str):
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
def elems_ordered(self) -> List["StructureVariable"]:
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
"""
import os
import logging
import json
import re
import logging
import math
import os
import re
from collections import defaultdict
from typing import Optional, Dict, List, Set, Union, Tuple
@ -18,10 +18,12 @@ from slither.core.declarations import (
Import,
Function,
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.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall
from slither.slithir.variables import Constant
from slither.utils.colors import red
@ -44,12 +46,19 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
def __init__(self):
super().__init__()
# Top level object
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._source_units: Dict[int, str] = {}
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._all_functions: Set[Function] = set()
self._all_modifiers: Set[Modifier] = set()
@ -234,14 +243,20 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
###################################################################################
@property
def top_level_structures(self) -> List[Structure]:
top_level_structures = [c.structures for c in self.contracts if c.is_top_level]
return [st for sublist in top_level_structures for st in sublist]
def structures_top_level(self) -> List[StructureTopLevel]:
return self._structures_top_level
@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
def top_level_enums(self) -> List[Enum]:
top_level_enums = [c.enums for c in self.contracts if c.is_top_level]
return [st for sublist in top_level_enums for st in sublist]
def functions_top_level(self) -> List[FunctionTopLevel]:
return self._functions_top_level
# 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
"""
from slither.core.declarations import Function
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):
ARGUMENT = "slithir"
HELP = "Print the slithIR representation of the functions"
@ -24,26 +39,16 @@ class PrinterSlithIR(AbstractPrinter):
continue
txt += "Contract {}\n".format(contract.name)
for function in contract.functions:
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
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)
txt = f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
txt += _print_function(function)
for modifier in contract.modifiers:
txt += "\tModifier {}\n".format(modifier.canonical_name)
for node in modifier.nodes:
txt += str(node)
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)
txt += _print_function(modifier)
if self.slither.functions_top_level:
txt += "Top level functions"
for function in self.slither.functions_top_level:
txt += f"\tFunction {function.canonical_name}\n"
txt += _print_function(function)
self.info(txt)
res = self.generate_output(txt)
return res

@ -78,7 +78,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
# pylint: disable=raise-missing-from
raise SlitherError(f"Invalid compilation: \n{str(e)}")
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)
if kwargs.get("generate_patches", False):
@ -120,7 +120,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self._parser = SlitherSolc(filename, self)
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):
self._parser = SlitherSolc("", self)
@ -129,7 +129,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
path = c["absolutePath"]
else:
path = c["attributes"]["absolutePath"]
self._parser.parse_contracts_from_loaded_json(c, path)
self._parser.parse_top_level_from_loaded_json(c, path)
@property
def detectors(self):

@ -1,5 +1,5 @@
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
from slither.core.declarations import (
@ -12,6 +12,7 @@ from slither.core.declarations import (
SolidityVariableComposed,
Structure,
)
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import (
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.type import Type
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.slithir.operations.codesize import CodeSize
from slither.slithir.variables import TupleVariable
from slither.core.variables.variable import Variable
from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations import (
Assignment,
Balance,
@ -61,6 +61,7 @@ from slither.slithir.operations import (
Unpack,
Nop,
)
from slither.slithir.operations.codesize import CodeSize
from slither.slithir.tmp_operations.argument import Argument, ArgumentType
from slither.slithir.tmp_operations.tmp_call import TmpCall
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_structure import TmpNewStructure
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.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")
@ -428,15 +432,15 @@ def _convert_type_contract(ir, slither):
assignment.lvalue.set_type(ElementaryType("string"))
return assignment
if isinstance(contract, ElementaryType):
print(contract.type)
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
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):
# Force assignment in case of missing previous correct type
if not ir.lvalue.type:
@ -478,9 +482,11 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
# Convert HighLevelCall to LowLevelCall
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":
return convert_type_of_high_and_internal_level_call(
ir, node.function.contract
ir, node_function.contract
)
if can_be_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)
left = ir.variable_left
t = None
ir_func = ir.function
# Handling of this.function_name usage
if (
left == SolidityVariable("this")
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
# And the contract does not have two functions starting with function_name
# Otherwise solc raises:
# Error: Member "f" not unique after argument-dependent lookup in contract
targeted_function = next(
(
x
for x in ir.function.contract.functions
if x.name == str(ir.variable_right)
)
(x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
)
t = _make_function_type(targeted_function)
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
assert isinstance(ins, TmpCall)
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
# 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);
# }
# }
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 = (
[] + 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:
lib_func = candidates[0]
# Library must be from a contract
assert isinstance(lib_func, FunctionContract)
lib_call = LibraryCall(
lib_func.contract,
Constant(lib_func.name),

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

@ -1,15 +1,14 @@
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.enum import Enum
from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable
from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.function import FunctionSolc
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.solidity_types.type_parsing import parse_type
from slither.solc_parsing.variables.state_variable import StateVariableSolc
@ -44,7 +43,7 @@ class ContractSolc:
self._functions_parser: List[FunctionSolc] = []
self._modifiers_parser: List[ModifierSolc] = []
self._structures_parser: List[StructureSolc] = []
self._structures_parser: List[StructureContractSolc] = []
self._is_analyzed: bool = False
@ -252,28 +251,13 @@ class ContractSolc:
return
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_offset(struct["src"], self._contract.slither)
st_parser = StructureSolc(st, name, canonicalName, children, self)
self._contract.structures_as_dict[name] = st
st_parser = StructureContractSolc(st, struct, self)
self._contract.structures_as_dict[st.name] = st
self._structures_parser.append(st_parser)
def parse_structs(self):
@ -307,17 +291,17 @@ class ContractSolc:
self._contract.add_variables_ordered([var])
def _parse_modifier(self, modifier_data: Dict):
modif = Modifier()
modif = Modifier(self.slither)
modif.set_offset(modifier_data["src"], self._contract.slither)
modif.set_contract(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._modifiers_no_params.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):
for modifier in self._modifiersNotParsed:
@ -325,17 +309,17 @@ class ContractSolc:
self._modifiersNotParsed = None
def _parse_function(self, function_data: Dict):
func = Function()
func = FunctionContract(self.slither)
func.set_offset(function_data["src"], self._contract.slither)
func.set_contract(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._functions_no_params.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):
@ -396,7 +380,7 @@ class ContractSolc:
elements_no_params = self._functions_no_params
getter = lambda c: c.functions_parser
getter_available = lambda c: c.functions_declared
Cls = Function
Cls = FunctionContract
Cls_parser = FunctionSolc
functions = self._analyze_params_elements(
elements_no_params,
@ -411,15 +395,56 @@ class ContractSolc:
self.log_incorrect_parsing(f"Missing params {e}")
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
self,
elements_no_params: List[FunctionSolc],
getter: Callable[["ContractSolc"], List[FunctionSolc]],
getter_available: Callable[[Contract], List[Function]],
getter_available: Callable[[Contract], List[FunctionContract]],
Cls: Callable,
Cls_parser: Callable,
parser: List[FunctionSolc],
) -> Dict[str, Union[Function, Modifier]]:
) -> Dict[str, Union[FunctionContract, 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)
@ -433,29 +458,15 @@ class ContractSolc:
"""
all_elements = {}
explored_reference_id = set()
try:
for father in self._contract.inheritance:
father_parser = self._slither_parser.underlying_contract_to_parser[father]
for element_parser in getter(father_parser):
elem = Cls()
elem.set_contract(self._contract)
elem.set_contract_declarer(element_parser.underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"], self._contract.slither,
self._analyze_params_element(
Cls, Cls_parser, element_parser, explored_reference_id, parser, all_elements
)
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(
all_elements, getter_available
)
@ -573,12 +584,12 @@ class ContractSolc:
else:
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_offset(enum["src"], self._contract.slither)
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()
def analyze_structs(self):

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

@ -11,11 +11,18 @@ from slither.solc_parsing.declarations.function import FunctionSolc
if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slitherSolc import SlitherSolc
class ModifierSolc(FunctionSolc):
def __init__(self, modifier: Modifier, function_data: Dict, contract_parser: "ContractSolc"):
super().__init__(modifier, function_data, contract_parser)
def __init__(
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
# confusion for mypy in underlying_function
self._modifier = modifier

@ -1,17 +1,17 @@
"""
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.solc_parsing.variables.structure_variable import StructureVariableSolc
from slither.core.declarations.structure import Structure
if TYPE_CHECKING:
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
"""
@ -19,19 +19,28 @@ class StructureSolc: # pylint: disable=too-few-public-methods
# elems = [(type, name)]
def __init__( # pylint: disable=too-many-arguments
self,
st: Structure,
name: str,
canonicalName: str,
elems: List[str],
contract_parser: "ContractSolc",
self, st: Structure, struct: Dict, 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
st.name = name
st.canonical_name = canonicalName
self._contract_parser = contract_parser
self._elemsNotParsed = elems
self._elemsNotParsed = children
def analyze(self):
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 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.contract import Contract
from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_variables import (
SOLIDITY_FUNCTIONS,
SOLIDITY_VARIABLES,
@ -12,6 +13,7 @@ from slither.core.declarations.solidity_variables import (
SolidityFunction,
SolidityVariable,
SolidityVariableComposed,
SolidityImportPlaceHolder,
)
from slither.core.expressions.assignment_operation import (
AssignmentOperation,
@ -43,6 +45,7 @@ from slither.core.solidity_types import (
MappingType,
)
from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
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.solc_parsing.declarations.function import FunctionSolc
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")
@ -78,62 +83,85 @@ def get_pointer_name(variable: Variable):
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,
caller_context: CallerContext,
function_parser: Optional["FunctionSolc"],
referenced_declaration: Optional[int] = None,
is_super=False,
) -> Union[
Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
]:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
) -> Optional[Variable]:
if function_parser is None:
return None
# We look for variable declared with the referencedDeclaration attr
func_variables_renamed = function_parser.variables_renamed
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
# 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
return None
if isinstance(caller_context, ContractSolc):
function: Optional[FunctionSolc] = None
contract = caller_context.underlying_contract
contract_declarer = caller_context.underlying_contract
elif isinstance(caller_context, FunctionSolc):
function = caller_context
contract = function.underlying_function.contract
contract_declarer = function.underlying_function.contract_declarer
else:
raise ParsingError("Incorrect caller context")
if function:
# We look for variable declared with the referencedDeclaration attr
func_variables_renamed = function.variables_renamed
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.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.underlying_function.variables
}
if var_name and var_name in func_variables_ptr:
return func_variables_ptr[var_name]
def _find_top_level(
var_name: str, sl: "SlitherCore"
) -> Optional[Union[Enum, Structure, SolidityVariable]]:
structures_top_level = sl.structures_top_level
for st in structures_top_level:
if st.name == var_name:
return st
enums_top_level = sl.enums_top_level
for enum in enums_top_level:
if enum.name == var_name:
return enum
for import_directive in sl.import_directives:
if import_directive.alias == var_name:
return SolidityImportPlaceHolder(import_directive)
return None
def _find_in_contract(
var_name: str,
contract: Optional[Contract],
contract_declarer: Optional[Contract],
is_super: bool,
) -> Optional[Union[Variable, Function, Contract, Event, Enum, Structure,]]:
if contract is None or contract_declarer is None:
return None
# variable are looked from the contract declarer
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:
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
if var_name in events:
return events[var_name]
@ -191,42 +214,147 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if var_name in enums:
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
enums = {e.name: e for e in contract.enums}
if var_name in enums:
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
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()}
if var_name in all_enums:
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:
return SolidityVariable(var_name)
if var_name in SOLIDITY_FUNCTIONS:
return SolidityFunction(var_name)
contracts = contract.slither.contracts_as_dict
if var_name in contracts:
return contracts[var_name]
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
# Top level must be at the end, if nothing else was found
ret = _find_top_level(var_name, sl)
if ret:
return ret
raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context))

@ -5,6 +5,10 @@ import re
from typing import List, Dict
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.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.import_directive import Import
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()
logger = logging.getLogger("SlitherSolcParsing")
@ -29,11 +36,14 @@ class SlitherSolc:
self._analyzed = False
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._core: SlitherCore = core
self._all_functions_parser: List[FunctionSolc] = []
self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0
@ -42,11 +52,11 @@ class SlitherSolc:
return self._core
@property
def all_functions_parser(self) -> List[FunctionSolc]:
return self._all_functions_parser
def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
return self._all_functions_and_modifier_parser
def add_functions_parser(self, f: FunctionSolc):
self._all_functions_parser.append(f)
def add_function_or_modifier_parser(self, f: FunctionSolc):
self._all_functions_and_modifier_parser.append(f)
@property
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:
data_loaded = json.loads(json_data)
# Truffle AST
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
# solc AST, where the non-json text was removed
if "attributes" in data_loaded:
filename = data_loaded["attributes"]["absolutePath"]
else:
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
except ValueError:
@ -102,13 +112,40 @@ class SlitherSolc:
json_data = json_data[first:last]
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 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
): # pylint: disable=too-many-branches
): # pylint: disable=too-many-branches,too-many-statements
if "nodeType" in data_loaded:
self._is_compact_ast = True
@ -128,68 +165,62 @@ class SlitherSolc:
logger.error("solc version is not supported")
return
for contract_data in data_loaded[self.get_children()]:
assert contract_data[self.get_key()] in [
"ContractDefinition",
"PragmaDirective",
"ImportDirective",
"StructDefinition",
"EnumDefinition",
]
if contract_data[self.get_key()] == "ContractDefinition":
for top_level_data in data_loaded[self.get_children()]:
if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract()
contract_parser = ContractSolc(self, contract, contract_data)
if "src" in contract_data:
contract.set_offset(contract_data["src"], self._core)
contract_parser = ContractSolc(self, contract, top_level_data)
if "src" in top_level_data:
contract.set_offset(top_level_data["src"], self._core)
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:
pragma = Pragma(contract_data["literals"])
pragma = Pragma(top_level_data["literals"])
else:
pragma = Pragma(contract_data["attributes"]["literals"])
pragma.set_offset(contract_data["src"], self._core)
pragma = Pragma(top_level_data["attributes"]["literals"])
pragma.set_offset(top_level_data["src"], self._core)
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:
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:
import_directive = Import(contract_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(contract_data["src"], self._core)
import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(top_level_data["src"], self._core)
self._core.import_directives.append(import_directive)
elif contract_data[self.get_key()] in [
"StructDefinition",
"EnumDefinition",
]:
# This can only happen for top-level structure and enum
# They were introduced with 0.6.5
assert self._is_compact_ast # Do not support top level definition for legacy AST
fake_contract_data = {
"name": f"SlitherInternalTopLevelContract{self._top_level_contracts_counter}",
"id": -1000
+ self._top_level_contracts_counter, # TODO: determine if collission possible
"linearizedBaseContracts": [],
"fullyImplemented": True,
"contractKind": "SLitherInternal",
}
self._top_level_contracts_counter += 1
contract = Contract()
top_level_contract = ContractSolc(self, contract, fake_contract_data)
contract.is_top_level = True
contract.set_offset(contract_data["src"], self._core)
if contract_data[self.get_key()] == "StructDefinition":
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
elif top_level_data[self.get_key()] == "StructDefinition":
st = StructureTopLevel()
st.set_offset(top_level_data["src"], self._core)
st_parser = StructureTopLevelSolc(st, top_level_data, self)
self._core.structures_top_level.append(st)
self._structures_top_level_parser.append(st_parser)
elif top_level_data[self.get_key()] == "EnumDefinition":
# Note enum don't need a complex parser, so everything is directly done
self._parse_enum(top_level_data)
elif top_level_data[self.get_key()] == "VariableDeclaration":
var = TopLevelVariable()
var_parser = TopLevelVariableSolc(var, top_level_data)
var.set_offset(top_level_data["src"], self._core)
self._core.variables_top_level.append(var)
self._variables_top_level_parser.append(var_parser)
elif top_level_data[self.get_key()] == "FunctionDefinition":
func = FunctionTopLevel(self.core)
func_parser = FunctionSolc(func, top_level_data, None, self)
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):
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:
self._analyze_struct_events(lib)
self._analyze_top_level_structures()
# Start with the contracts without inheritance
# Analyze a contract only if all its fathers
# Were analyzed
@ -478,16 +511,44 @@ Please rename it, this name is reserved for Slither's internals"""
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):
# State variables, modifiers and functions can refer to anything
contract.analyze_params_modifiers()
contract.analyze_params_functions()
self._analyze_params_top_level_function()
contract.analyze_state_variables()
contract.analyze_content_modifiers()
contract.analyze_content_functions()
self._analyze_content_top_level_function()
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()
for func in self.core.functions_top_level:
func.generate_slithir_and_analyze()
func.generate_slithir_ssa(dict())
self._core.propagate_function_calls()
for contract in self._core.contracts:
contract.fix_phi()

@ -2,26 +2,23 @@ import logging
import re
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 (
ElementaryType,
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.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.declarations.contract import Contract
from slither.core.expressions.literal import Literal
from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING:
from slither.core.declarations import Structure, Enum
from slither.core.declarations.contract import Contract
logger = logging.getLogger("TypeParsing")
@ -37,12 +34,14 @@ class UnknownType: # pylint: disable=too-few-public-methods
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,
contract: Contract,
contracts: List[Contract],
structures: List["Structure"],
enums: List["Enum"],
functions_direct_access: List["Function"],
contracts_direct_access: List["Contract"],
structures_direct_access: List["Structure"],
all_structures: List["Structure"],
enums_direct_access: List["Enum"],
all_enums: List["Enum"],
) -> Type:
name_elementary = name.split(" ")[0]
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 ") :]
if name_contract.startswith("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:
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:
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:
# any contract can refer to another contract's enum
enum_name = name
if enum_name.startswith("enum "):
enum_name = enum_name[len("enum ") :]
all_enums = [c.enums for c in contracts]
all_enums = [item for sublist in all_enums for item in sublist]
# all_enums = [c.enums for c in contracts]
# 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)
if not var_type:
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 "):
name_struct = name_struct[len("struct ") :]
name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end
all_structures = [c.structures for c in contracts]
all_structures = [item for sublist in all_structures for item in sublist]
# all_structures = [c.structures for c in contracts]
# 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)
if not var_type:
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"))
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 name.startswith("function "):
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 []
)
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 = [
_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
]
params_vars = []
@ -140,8 +158,24 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
from_ = found[0][0]
to_ = found[0][1]
from_type = _find_from_type_name(from_, contract, contracts, structures, enums)
to_type = _find_from_type_name(to_, contract, contracts, structures, enums)
from_type = _find_from_type_name(
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)
@ -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.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
if isinstance(caller_context, ContractSolc):
contract = caller_context.underlying_contract
contract_parser = caller_context
is_compact_ast = caller_context.is_compact_ast
elif isinstance(caller_context, FunctionSolc):
contract = caller_context.underlying_function.contract
contract_parser = caller_context.contract_parser
is_compact_ast = caller_context.is_compact_ast
from slither.solc_parsing.slitherSolc import SlitherSolc
# Note: for convenicence top level functions use the same parser than function in contract
# but contract_parser is set to None
if isinstance(caller_context, SlitherSolc) or (
isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
):
if isinstance(caller_context, SlitherSolc):
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:
raise ParsingError(f"Incorrect caller context: {type(caller_context)}")
is_compact_ast = caller_context.is_compact_ast
if is_compact_ast:
key = "nodeType"
else:
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):
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 is_compact_ast:
@ -190,13 +266,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if t[key] == "UserDefinedTypeName":
if is_compact_ast:
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').
type_name_key = "type" if "type" in t["attributes"] else key
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":
@ -204,25 +292,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if is_compact_ast:
if t.get("length", None):
length = parse_expression(t["length"], caller_context)
array_type = parse_type(t["baseType"], contract_parser)
array_type = parse_type(t["baseType"], next_context)
else:
if len(t["children"]) == 2:
length = parse_expression(t["children"][1], caller_context)
else:
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)
if t[key] == "Mapping":
if is_compact_ast:
mappingFrom = parse_type(t["keyType"], contract_parser)
mappingTo = parse_type(t["valueType"], contract_parser)
mappingFrom = parse_type(t["keyType"], next_context)
mappingTo = parse_type(t["valueType"], next_context)
else:
assert len(t["children"]) == 2
mappingFrom = parse_type(t["children"][0], contract_parser)
mappingTo = parse_type(t["children"][1], contract_parser)
mappingFrom = parse_type(t["children"][0], next_context)
mappingTo = parse_type(t["children"][1], next_context)
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,
Contract,
)
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import (
Literal,
AssignmentOperation,
@ -113,7 +114,9 @@ class YulScope(metaclass=abc.ABCMeta):
"_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._id: List[str] = yul_id
self._yul_local_variables: List[YulLocalVariable] = []
@ -125,7 +128,7 @@ class YulScope(metaclass=abc.ABCMeta):
return self._id
@property
def contract(self) -> Contract:
def contract(self) -> Optional[Contract]:
return self._contract
@property
@ -205,6 +208,7 @@ class YulFunction(YulScope):
func.set_visibility("private")
func.set_offset(ast["src"], root.slither)
func.set_contract(root.contract)
func.slither = root.slither
func.set_contract_declarer(root.contract)
func.scope = root.id
func.is_implemented = True
@ -267,7 +271,7 @@ class YulBlock(YulScope):
__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)
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:
func = Function()
func = FunctionContract(root.slither)
yul_function = YulFunction(func, root, ast)
root.contract.add_function(func)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save