diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 8eca260fa..727c2e44c 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -35,6 +35,14 @@ def _relative_path_format(path: str) -> str: return path.split("..")[-1].strip(".").strip("/") +def empty_tuple_list(): + return [(-1, -1)] + + +def tuple_dict(): + return defaultdict(empty_tuple_list) + + # pylint: disable=too-many-instance-attributes,too-many-public-methods class SlitherCore(Context): """ @@ -80,7 +88,7 @@ def __init__(self) -> None: # Maps from file to detector name to the start/end ranges for that detector. # Infinity is used to signal a detector has no end range. self._ignore_ranges: Dict[str, Dict[str, List[Tuple[int, ...]]]] = defaultdict( - lambda: defaultdict(lambda: [(-1, -1)]) + tuple_dict ) self._compilation_units: List[SlitherCompilationUnit] = [] diff --git a/slither/core/solidity_types/array_type.py b/slither/core/solidity_types/array_type.py index 04a458d68..254125891 100644 --- a/slither/core/solidity_types/array_type.py +++ b/slither/core/solidity_types/array_type.py @@ -1,3 +1,4 @@ +from functools import cache from typing import Union, Optional, Tuple, Any, TYPE_CHECKING from slither.core.expressions.expression import Expression @@ -66,6 +67,7 @@ def storage_size(self) -> Tuple[int, bool]: return elem_size * int(str(self._length_value)), True return 32, True + @cache def __str__(self) -> str: if self._length: return str(self._type) + f"[{str(self._length_value)}]" @@ -77,4 +79,4 @@ def __eq__(self, other: Any) -> bool: return self._type == other.type and self._length_value == other.length_value def __hash__(self) -> int: - return hash(str(self)) + return hash(self._type) diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index a9f45c8d8..63c3071ed 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -1,4 +1,5 @@ import itertools +from functools import cache from typing import Tuple, Optional, Any from slither.core.solidity_types.type import Type @@ -216,6 +217,7 @@ def max(self) -> int: return MaxValues[self.name] raise SlitherException(f"{self.name} does not have a max value") + @cache def __str__(self) -> str: return self._type @@ -225,4 +227,4 @@ def __eq__(self, other: Any) -> bool: return self.type == other.type def __hash__(self) -> int: - return hash(str(self)) + return hash(self._type) diff --git a/slither/core/solidity_types/mapping_type.py b/slither/core/solidity_types/mapping_type.py index 9741569ed..e7adb461c 100644 --- a/slither/core/solidity_types/mapping_type.py +++ b/slither/core/solidity_types/mapping_type.py @@ -1,3 +1,4 @@ +from functools import cache from typing import Union, Tuple, TYPE_CHECKING, Any from slither.core.solidity_types.type import Type @@ -35,6 +36,7 @@ def storage_size(self) -> Tuple[int, bool]: def is_dynamic(self) -> bool: return True + @cache def __str__(self) -> str: return f"mapping({str(self._from)} => {str(self._to)})" @@ -44,4 +46,4 @@ def __eq__(self, other: Any) -> bool: return self.type_from == other.type_from and self.type_to == other.type_to def __hash__(self) -> int: - return hash(str(self)) + return hash(f"{self._from}.{self._to}") diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index 36efeef33..b3bb0d46a 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -1,3 +1,4 @@ +import concurrent.futures from collections import defaultdict import json import logging @@ -73,6 +74,64 @@ def _handle_import_aliases( ) +def generate_slithir_contract(contract): + contract.add_constructor_variables() + + for func in contract.functions + contract.modifiers: + try: + func.generate_slithir_and_analyze() + + except AttributeError as e: + # This can happen for example if there is a call to an interface + # And the interface is redefined due to contract's name reuse + # But the available version misses some functions + # self._underlying_contract_to_parser[contract].log_incorrect_parsing( + # f"Impossible to generate IR for {contract.name}.{func.name} ({func.source_mapping}):\n {e}" + # ) + # TODO(dm) + pass + except Exception as e: + func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) + logger.error( + f"\nFailed to generate IR for {contract.name}.{func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{contract.name}.{func.name} ({func.source_mapping}):\n " + f"{func_expressions}" + ) + raise e + try: + contract.convert_expression_to_slithir_ssa() + except Exception as exc: + logger.error( + f"\nFailed to convert IR to SSA for {contract.name} contract. Please open an issue https://github.com/crytic/slither/issues.\n " + ) + raise exc + + +def generate_slithir_function(func): + try: + func.generate_slithir_and_analyze() + except AttributeError as e: + logger.error( + f"Impossible to generate IR for top level function {func.name} ({func.source_mapping}):\n {e}" + ) + except Exception as e: + func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) + logger.error( + f"\nFailed to generate IR for top level function {func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{func.name} ({func.source_mapping}):\n " + f"{func_expressions}" + ) + raise e + + try: + func.generate_slithir_ssa({}) + except Exception as e: + func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) + logger.error( + f"\nFailed to convert IR to SSA for top level function {func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{func.name} ({func.source_mapping}):\n " + f"{func_expressions}" + ) + raise e + + class SlitherCompilationUnitSolc(CallerContextExpression): # pylint: disable=too-many-instance-attributes def __init__(self, compilation_unit: SlitherCompilationUnit) -> None: @@ -803,60 +862,22 @@ def _analyze_variables_modifiers_functions(self, contract: ContractSolc) -> None contract.set_is_analyzed(True) def _convert_to_slithir(self) -> None: - for contract in self._compilation_unit.contracts: - contract.add_constructor_variables() - - for func in contract.functions + contract.modifiers: - try: - func.generate_slithir_and_analyze() - - except AttributeError as e: - # This can happens for example if there is a call to an interface - # And the interface is redefined due to contract's name reuse - # But the available version misses some functions - self._underlying_contract_to_parser[contract].log_incorrect_parsing( - f"Impossible to generate IR for {contract.name}.{func.name} ({func.source_mapping}):\n {e}" - ) - except Exception as e: - func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) - logger.error( - f"\nFailed to generate IR for {contract.name}.{func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{contract.name}.{func.name} ({func.source_mapping}):\n " - f"{func_expressions}" - ) - raise e - try: - contract.convert_expression_to_slithir_ssa() - except Exception as e: - logger.error( - f"\nFailed to convert IR to SSA for {contract.name} contract. Please open an issue https://github.com/crytic/slither/issues.\n " - ) - raise e - - for func in self._compilation_unit.functions_top_level: - try: - func.generate_slithir_and_analyze() - except AttributeError as e: - logger.error( - f"Impossible to generate IR for top level function {func.name} ({func.source_mapping}):\n {e}" - ) - except Exception as e: - func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) - logger.error( - f"\nFailed to generate IR for top level function {func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{func.name} ({func.source_mapping}):\n " - f"{func_expressions}" - ) - raise e - - try: - func.generate_slithir_ssa({}) - except Exception as e: - func_expressions = "\n".join([f"\t{ex}" for ex in func.expressions]) - logger.error( - f"\nFailed to convert IR to SSA for top level function {func.name}. Please open an issue https://github.com/crytic/slither/issues.\n{func.name} ({func.source_mapping}):\n " - f"{func_expressions}" - ) - raise e + generate_slithir_contract(contract) + + for function in self._compilation_unit.functions_top_level: + generate_slithir_function(function) + # + # with concurrent.futures.ProcessPoolExecutor() as executor: + # + # futures = [ + # executor.submit(generate_slithir_contract, contract) for contract in self._compilation_unit.contracts + # ] + [ + # executor.submit(generate_slithir_function, function) for function in self._compilation_unit.functions_top_level + # ] + # + # for future in concurrent.futures.as_completed(futures): + # future.result() self._compilation_unit.propagate_function_calls() for contract in self._compilation_unit.contracts: