Add support for top level constant variables

Improve parse_type
pull/728/head
Josselin 4 years ago
parent c0406c38e8
commit 9c06483954
  1. 6
      slither/core/slither_core.py
  2. 39
      slither/core/variables/top_level_variable.py
  3. 2
      slither/solc_parsing/declarations/structure_top_level.py
  4. 38
      slither/solc_parsing/slitherSolc.py
  5. 141
      slither/solc_parsing/solidity_types/type_parsing.py
  6. 15
      slither/solc_parsing/variables/top_level_variable.py

@ -22,6 +22,7 @@ from slither.core.declarations import (
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.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall
from slither.slithir.variables import Constant
from slither.utils.colors import red
@ -49,6 +50,7 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
self._contracts: Dict[str, Contract] = {}
self._structures_top_level: List[StructureTopLevel] = []
self._enums_top_level: List[EnumTopLevel] = []
self._variables_top_level: List[TopLevelVariable] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
@ -246,6 +248,10 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
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
# endregion
###################################################################################
###################################################################################

@ -0,0 +1,39 @@
from typing import Optional, TYPE_CHECKING, Tuple, List
from slither.core.declarations.top_level import TopLevel
from slither.core.variables.variable import Variable
from slither.core.children.child_contract import ChildContract
from slither.utils.type import export_nested_types_from_variable
if TYPE_CHECKING:
from slither.core.cfg.node import Node
from slither.core.declarations import Contract
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
###################################################################################
###################################################################################

@ -46,7 +46,7 @@ class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
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.set_offset(elem_to_parse["src"], self._slither_parser.core)
elem_parser = StructureVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self._slither_parser)

@ -7,6 +7,7 @@ from typing import List, Dict
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.core.variables.top_level_variable import TopLevelVariable
from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc
@ -16,6 +17,8 @@ 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")
@ -33,6 +36,7 @@ class SlitherSolc:
self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
self._structures_top_level_parser: List[StructureTopLevelSolc] = []
self._variables_top_level_parser: List[TopLevelVariableSolc] = []
self._is_compact_ast = False
self._core: SlitherCore = core
@ -160,13 +164,6 @@ class SlitherSolc:
return
for top_level_data in data_loaded[self.get_children()]:
assert top_level_data[self.get_key()] in [
"ContractDefinition",
"PragmaDirective",
"ImportDirective",
"StructDefinition",
"EnumDefinition",
]
if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract()
contract_parser = ContractSolc(self, contract, top_level_data)
@ -202,6 +199,17 @@ class SlitherSolc:
# 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)
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":
return # handle solc prior 0.3.6
@ -420,6 +428,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
@ -489,6 +499,20 @@ 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")
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")
def _analyze_variables_modifiers_functions(self, contract: ContractSolc):
# State variables, modifiers and functions can refer to anything

@ -39,10 +39,12 @@ class UnknownType: # pylint: disable=too-few-public-methods
def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
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,20 +62,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 += contract.slither.enums_top_level
# 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)
@ -83,9 +85,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 += contract.slither.structures_top_level
# 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)
@ -100,7 +102,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(
@ -112,10 +114,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 = []
@ -142,8 +161,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)
@ -160,29 +195,59 @@ 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
from slither.solc_parsing.slitherSolc import SlitherSolc
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
if isinstance(caller_context, (ContractSolc, FunctionSolc)):
if isinstance(caller_context, FunctionSolc):
contract = caller_context.underlying_function.contract
contract_parser = caller_context.contract_parser
else:
contract = caller_context.underlying_contract
contract_parser = 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
elif isinstance(caller_context, SlitherSolc):
structures_direct_access = caller_context.core.structures_top_level
all_structuress = [c.structures for c in caller_context.core.contracts]
all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += structures_direct_access
enums_direct_access = caller_context.core.enums_top_level
all_enumss = [c.enums for c in caller_context.core.contracts]
all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += enums_direct_access
contracts = caller_context.core.contracts
functions = []
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.structures_top_level
enums = contract.enums + contract.slither.enums_top_level
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:
@ -192,13 +257,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":

@ -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
Loading…
Cancel
Save