Change the way slither handles top level enum/struct

- Split declaration.Enum/Structure to EnumContract/EnumToplevel (same
for structure)
- Update the parsing accordingly
pull/728/head
Josselin 4 years ago
parent e52a2aee0e
commit 0a7738d225
  1. 2
      slither/core/declarations/__init__.py
  2. 23
      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. 11
      slither/core/declarations/structure.py
  7. 12
      slither/core/declarations/structure_contract.py
  8. 6
      slither/core/declarations/structure_top_level.py
  9. 5
      slither/core/declarations/top_level.py
  10. 29
      slither/core/slither_core.py
  11. 6
      slither/slither.py
  12. 32
      slither/solc_parsing/declarations/contract.py
  13. 27
      slither/solc_parsing/declarations/structure_contract.py
  14. 56
      slither/solc_parsing/declarations/structure_top_level.py
  15. 4
      slither/solc_parsing/expressions/expression_parsing.py
  16. 109
      slither/solc_parsing/slitherSolc.py
  17. 6
      slither/solc_parsing/solidity_types/type_parsing.py
  18. 3
      tests/ast-parsing/expected/struct-0.6.0-compact.json
  19. 3
      tests/ast-parsing/expected/struct-0.6.1-compact.json
  20. 3
      tests/ast-parsing/expected/struct-0.6.10-compact.json
  21. 3
      tests/ast-parsing/expected/struct-0.6.11-compact.json
  22. 3
      tests/ast-parsing/expected/struct-0.6.12-compact.json
  23. 3
      tests/ast-parsing/expected/struct-0.6.2-compact.json
  24. 3
      tests/ast-parsing/expected/struct-0.6.3-compact.json
  25. 3
      tests/ast-parsing/expected/struct-0.6.4-compact.json
  26. 3
      tests/ast-parsing/expected/struct-0.6.5-compact.json
  27. 3
      tests/ast-parsing/expected/struct-0.6.6-compact.json
  28. 3
      tests/ast-parsing/expected/struct-0.6.7-compact.json
  29. 3
      tests/ast-parsing/expected/struct-0.6.8-compact.json
  30. 3
      tests/ast-parsing/expected/struct-0.6.9-compact.json
  31. 3
      tests/ast-parsing/expected/struct-0.7.0-compact.json
  32. 3
      tests/ast-parsing/expected/struct-0.7.1-compact.json
  33. 3
      tests/ast-parsing/expected/struct-0.7.2-compact.json
  34. 2
      tests/test_ast_parsing.py

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

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

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

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

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

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

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

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

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

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

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

@ -1,15 +1,14 @@
import logging import logging
from typing import List, Dict, Callable, TYPE_CHECKING, Union from typing import List, Dict, Callable, TYPE_CHECKING, Union
from slither.core.declarations import Modifier, Structure, Event from slither.core.declarations import Modifier, Event, EnumContract, StructureContract
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.enum import Enum
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.solc_parsing.declarations.event import EventSolc from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.modifier import ModifierSolc from slither.solc_parsing.declarations.modifier import ModifierSolc
from slither.solc_parsing.declarations.structure import StructureSolc from slither.solc_parsing.declarations.structure_contract import StructureContractSolc
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
from slither.solc_parsing.solidity_types.type_parsing import parse_type from slither.solc_parsing.solidity_types.type_parsing import parse_type
from slither.solc_parsing.variables.state_variable import StateVariableSolc from slither.solc_parsing.variables.state_variable import StateVariableSolc
@ -44,7 +43,7 @@ class ContractSolc:
self._functions_parser: List[FunctionSolc] = [] self._functions_parser: List[FunctionSolc] = []
self._modifiers_parser: List[ModifierSolc] = [] self._modifiers_parser: List[ModifierSolc] = []
self._structures_parser: List[StructureSolc] = [] self._structures_parser: List[StructureContractSolc] = []
self._is_analyzed: bool = False self._is_analyzed: bool = False
@ -252,28 +251,13 @@ class ContractSolc:
return return
def _parse_struct(self, struct: Dict): def _parse_struct(self, struct: Dict):
if self.is_compact_ast:
name = struct["name"]
attributes = struct
else:
name = struct["attributes"][self.get_key()]
attributes = struct["attributes"]
if "canonicalName" in attributes:
canonicalName = attributes["canonicalName"]
else:
canonicalName = self._contract.name + "." + name
if self.get_children("members") in struct:
children = struct[self.get_children("members")]
else:
children = [] # empty struct
st = Structure() st = StructureContract()
st.set_contract(self._contract) st.set_contract(self._contract)
st.set_offset(struct["src"], self._contract.slither) st.set_offset(struct["src"], self._contract.slither)
st_parser = StructureSolc(st, name, canonicalName, children, self) st_parser = StructureContractSolc(st, struct, self)
self._contract.structures_as_dict[name] = st self._contract.structures_as_dict[st.name] = st
self._structures_parser.append(st_parser) self._structures_parser.append(st_parser)
def parse_structs(self): def parse_structs(self):
@ -573,12 +557,12 @@ class ContractSolc:
else: else:
values.append(child["attributes"][self.get_key()]) values.append(child["attributes"][self.get_key()])
new_enum = Enum(name, canonicalName, values) new_enum = EnumContract(name, canonicalName, values)
new_enum.set_contract(self._contract) new_enum.set_contract(self._contract)
new_enum.set_offset(enum["src"], self._contract.slither) new_enum.set_offset(enum["src"], self._contract.slither)
self._contract.enums_as_dict[canonicalName] = new_enum self._contract.enums_as_dict[canonicalName] = new_enum
def _analyze_struct(self, struct: StructureSolc): # pylint: disable=no-self-use def _analyze_struct(self, struct: StructureContractSolc): # pylint: disable=no-self-use
struct.analyze() struct.analyze()
def analyze_structs(self): def analyze_structs(self):

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

@ -0,0 +1,56 @@
"""
Structure module
"""
from typing import List, TYPE_CHECKING, Dict
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.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._structure.contract.slither)
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 = []

@ -178,7 +178,7 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if var_name in structures: if var_name in structures:
return structures[var_name] return structures[var_name]
structures_top_level = contract.slither.top_level_structures structures_top_level = contract.slither.structures_top_level
for st in structures_top_level: for st in structures_top_level:
if st.name == var_name: if st.name == var_name:
return st return st
@ -191,7 +191,7 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if var_name in enums: if var_name in enums:
return enums[var_name] return enums[var_name]
enums_top_level = contract.slither.top_level_enums enums_top_level = contract.slither.enums_top_level
for enum in enums_top_level: for enum in enums_top_level:
if enum.name == var_name: if enum.name == var_name:
return enum return enum

@ -5,6 +5,8 @@ import re
from typing import List, Dict from typing import List, Dict
from slither.core.declarations import Contract from slither.core.declarations import Contract
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
@ -13,6 +15,7 @@ from slither.core.slither_core import SlitherCore
from slither.core.declarations.pragma_directive import Pragma from slither.core.declarations.pragma_directive import Pragma
from slither.core.declarations.import_directive import Import from slither.core.declarations.import_directive import Import
from slither.analyses.data_dependency.data_dependency import compute_dependency from slither.analyses.data_dependency.data_dependency import compute_dependency
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("SlitherSolcParsing") logger = logging.getLogger("SlitherSolcParsing")
@ -29,6 +32,7 @@ class SlitherSolc:
self._analyzed = False self._analyzed = False
self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict() self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
self._structures_top_level_parser: List[StructureTopLevelSolc] = []
self._is_compact_ast = False self._is_compact_ast = False
self._core: SlitherCore = core self._core: SlitherCore = core
@ -79,19 +83,19 @@ class SlitherSolc:
################################################################################### ###################################################################################
################################################################################### ###################################################################################
def parse_contracts_from_json(self, json_data: str) -> bool: def parse_top_level_from_json(self, json_data: str) -> bool:
try: try:
data_loaded = json.loads(json_data) data_loaded = json.loads(json_data)
# Truffle AST # Truffle AST
if "ast" in data_loaded: if "ast" in data_loaded:
self.parse_contracts_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"]) self.parse_top_level_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"])
return True return True
# solc AST, where the non-json text was removed # solc AST, where the non-json text was removed
if "attributes" in data_loaded: if "attributes" in data_loaded:
filename = data_loaded["attributes"]["absolutePath"] filename = data_loaded["attributes"]["absolutePath"]
else: else:
filename = data_loaded["absolutePath"] filename = data_loaded["absolutePath"]
self.parse_contracts_from_loaded_json(data_loaded, filename) self.parse_top_level_from_loaded_json(data_loaded, filename)
return True return True
except ValueError: except ValueError:
@ -102,11 +106,38 @@ class SlitherSolc:
json_data = json_data[first:last] json_data = json_data[first:last]
data_loaded = json.loads(json_data) data_loaded = json.loads(json_data)
self.parse_contracts_from_loaded_json(data_loaded, filename) self.parse_top_level_from_loaded_json(data_loaded, filename)
return True return True
return False return False
def parse_contracts_from_loaded_json( def _parse_enum(self, top_level_data:Dict):
if self.is_compact_ast:
name = top_level_data["name"]
canonicalName = top_level_data["canonicalName"]
else:
name = top_level_data["attributes"][self.get_key()]
if "canonicalName" in top_level_data["attributes"]:
canonicalName = top_level_data["attributes"]["canonicalName"]
else:
canonicalName = name
values = []
children = (
top_level_data["members"]
if "members" in top_level_data
else top_level_data.get("children", [])
)
for child in children:
assert child[self.get_key()] == "EnumValue"
if self.is_compact_ast:
values.append(child["name"])
else:
values.append(child["attributes"][self.get_key()])
enum = EnumTopLevel(name, canonicalName, values)
enum.set_offset(top_level_data["src"], self._core)
self.core.enums_top_level.append(enum)
def parse_top_level_from_loaded_json(
self, data_loaded: Dict, filename: str self, data_loaded: Dict, filename: str
): # pylint: disable=too-many-branches ): # pylint: disable=too-many-branches
if "nodeType" in data_loaded: if "nodeType" in data_loaded:
@ -128,68 +159,48 @@ class SlitherSolc:
logger.error("solc version is not supported") logger.error("solc version is not supported")
return return
for contract_data in data_loaded[self.get_children()]: for top_level_data in data_loaded[self.get_children()]:
assert contract_data[self.get_key()] in [ assert top_level_data[self.get_key()] in [
"ContractDefinition", "ContractDefinition",
"PragmaDirective", "PragmaDirective",
"ImportDirective", "ImportDirective",
"StructDefinition", "StructDefinition",
"EnumDefinition", "EnumDefinition",
] ]
if contract_data[self.get_key()] == "ContractDefinition": if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract() contract = Contract()
contract_parser = ContractSolc(self, contract, contract_data) contract_parser = ContractSolc(self, contract, top_level_data)
if "src" in contract_data: if "src" in top_level_data:
contract.set_offset(contract_data["src"], self._core) contract.set_offset(top_level_data["src"], self._core)
self._underlying_contract_to_parser[contract] = contract_parser self._underlying_contract_to_parser[contract] = contract_parser
elif contract_data[self.get_key()] == "PragmaDirective": elif top_level_data[self.get_key()] == "PragmaDirective":
if self._is_compact_ast: if self._is_compact_ast:
pragma = Pragma(contract_data["literals"]) pragma = Pragma(top_level_data["literals"])
else: else:
pragma = Pragma(contract_data["attributes"]["literals"]) pragma = Pragma(top_level_data["attributes"]["literals"])
pragma.set_offset(contract_data["src"], self._core) pragma.set_offset(top_level_data["src"], self._core)
self._core.pragma_directives.append(pragma) self._core.pragma_directives.append(pragma)
elif contract_data[self.get_key()] == "ImportDirective": elif top_level_data[self.get_key()] == "ImportDirective":
if self.is_compact_ast: if self.is_compact_ast:
import_directive = Import(contract_data["absolutePath"]) import_directive = Import(top_level_data["absolutePath"])
else: else:
import_directive = Import(contract_data["attributes"].get("absolutePath", "")) import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(contract_data["src"], self._core) import_directive.set_offset(top_level_data["src"], self._core)
self._core.import_directives.append(import_directive) self._core.import_directives.append(import_directive)
elif contract_data[self.get_key()] in [ elif top_level_data[self.get_key()] == "StructDefinition":
"StructDefinition", st = StructureTopLevel()
"EnumDefinition", st.set_offset(top_level_data["src"], self._core)
]: st_parser = StructureTopLevelSolc(st, top_level_data, self)
# This can only happen for top-level structure and enum
# They were introduced with 0.6.5 self._core.structures_top_level.append(st)
assert self._is_compact_ast # Do not support top level definition for legacy AST self._structures_top_level_parser.append(st_parser)
fake_contract_data = {
"name": f"SlitherInternalTopLevelContract{self._top_level_contracts_counter}",
"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
self._underlying_contract_to_parser[contract] = top_level_contract 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)
def _parse_source_unit(self, data: Dict, filename: str): def _parse_source_unit(self, data: Dict, filename: str):
if data[self.get_key()] != "SourceUnit": if data[self.get_key()] != "SourceUnit":

@ -73,6 +73,7 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
enum_name = enum_name[len("enum ") :] enum_name = enum_name[len("enum ") :]
all_enums = [c.enums for c in contracts] all_enums = [c.enums for c in contracts]
all_enums = [item for sublist in all_enums for item in sublist] all_enums = [item for sublist in all_enums for item in sublist]
all_enums += contract.slither.enums_top_level
var_type = next((e for e in all_enums if e.name == enum_name), None) var_type = next((e for e in all_enums if e.name == enum_name), None)
if not var_type: if not var_type:
var_type = next((e for e in all_enums if e.canonical_name == enum_name), None) var_type = next((e for e in all_enums if e.canonical_name == enum_name), None)
@ -84,6 +85,7 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end
all_structures = [c.structures for c in contracts] all_structures = [c.structures for c in contracts]
all_structures = [item for sublist in all_structures for item in sublist] all_structures = [item for sublist in all_structures for item in sublist]
all_structures += contract.slither.structures_top_level
var_type = next((st for st in all_structures if st.name == name_struct), None) var_type = next((st for st in all_structures if st.name == name_struct), None)
if not var_type: if not var_type:
var_type = next((st for st in all_structures if st.canonical_name == name_struct), None) var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
@ -175,8 +177,8 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
else: else:
key = "name" key = "name"
structures = contract.structures + contract.slither.top_level_structures structures = contract.structures + contract.slither.structures_top_level
enums = contract.enums + contract.slither.top_level_enums enums = contract.enums + contract.slither.enums_top_level
contracts = contract.slither.contracts contracts = contract.slither.contracts
if isinstance(t, UnknownType): if isinstance(t, UnknownType):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -558,7 +558,7 @@ def test_parsing(test_item: Item):
diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree") diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree")
if diff: if diff:
for change in diff["values_changed"]: for change in diff.get("values_changed", []):
path_list = re.findall(r"\['(.*?)'\]", change.path()) path_list = re.findall(r"\['(.*?)'\]", change.path())
path = "_".join(path_list) path = "_".join(path_list)
with open(f"test_artifacts/{id_test(test_item)}_{path}_expected.dot", "w") as f: with open(f"test_artifacts/{id_test(test_item)}_{path}_expected.dot", "w") as f:

Loading…
Cancel
Save