Merge pull request #703 from crytic/dev-memoize

Memoize properties
pull/706/head
Feist Josselin 4 years ago committed by GitHub
commit 59ace02822
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      slither/core/declarations/contract.py
  2. 76
      slither/core/declarations/function.py

@ -71,12 +71,15 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._is_upgradeable: Optional[bool] = None
self._is_upgradeable_proxy: Optional[bool] = None
self._is_top_level = False
self.is_top_level = False # heavily used, so no @property
self._initial_state_variables: List["StateVariable"] = [] # ssa
self._is_incorrectly_parsed: bool = False
self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None
self._all_functions_called: Optional[List["InternalCallType"]] = None
###################################################################################
###################################################################################
# region General's properties
@ -392,7 +395,11 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
return list(self._functions.values())
def available_functions_as_dict(self) -> Dict[str, "Function"]:
return {f.full_name: f for f in self._functions.values() if not f.is_shadowed}
if self._available_functions_as_dict is None:
self._available_functions_as_dict = {
f.full_name: f for f in self._functions.values() if not f.is_shadowed
}
return self._available_functions_as_dict
def add_function(self, func: "Function"):
self._functions[func.canonical_name] = func
@ -731,17 +738,19 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
list(Function): List of functions reachable from the contract
Includes super, and private/internal functions not shadowed
"""
all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore
all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
all_calls = [item for sublist in all_callss for item in sublist]
all_calls = list(set(all_calls))
if self._all_functions_called is None:
all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore
all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
all_calls = [item for sublist in all_callss for item in sublist]
all_calls = list(set(all_calls))
all_constructors = [c.constructor for c in self.inheritance if c.constructor]
all_constructors = list(set(all_constructors))
all_constructors = [c.constructor for c in self.inheritance if c.constructor]
all_constructors = list(set(all_constructors))
set_all_calls = set(all_calls + list(all_constructors))
set_all_calls = set(all_calls + list(all_constructors))
return [c for c in set_all_calls if isinstance(c, Function)]
self._all_functions_called = [c for c in set_all_calls if isinstance(c, Function)]
return self._all_functions_called
@property
def all_state_variables_written(self) -> List["StateVariable"]:
@ -1200,19 +1209,6 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
for func in self.functions + self.modifiers:
func.fix_phi(last_state_variables_instances, initial_state_variables_instances)
@property
def is_top_level(self) -> bool:
"""
The "TopLevel" contract is used to hold structures and enums defined at the top level
ie. structures and enums that are represented outside of any contract
:return:
"""
return self._is_top_level
@is_top_level.setter
def is_top_level(self, t: bool):
self._is_top_level = t
# endregion
###################################################################################
###################################################################################

@ -197,6 +197,15 @@ class Function(
self._counter_nodes = 0
# Memoize parameters:
# TODO: identify all the memoize parameters and add a way to undo the memoization
self._full_name: Optional[str] = None
self._signature: Optional[Tuple[str, List[str], List[str]]] = None
self._solidity_signature: Optional[str] = None
self._signature_str: Optional[str] = None
self._canonical_name: Optional[str] = None
self._is_protected: Optional[bool] = None
###################################################################################
###################################################################################
# region General properties
@ -244,8 +253,11 @@ class Function(
str: func_name(type1,type2)
Return the function signature without the return values
"""
name, parameters, _ = self.signature
return ".".join(self._scope + [name]) + "(" + ",".join(parameters) + ")"
if self._full_name is None:
name, parameters, _ = self.signature
full_name = ".".join(self._scope + [name]) + "(" + ",".join(parameters) + ")"
self._full_name = full_name
return self._full_name
@property
def canonical_name(self) -> str:
@ -253,13 +265,15 @@ class Function(
str: contract.func_name(type1,type2)
Return the function signature without the return values
"""
name, parameters, _ = self.signature
return (
".".join([self.contract_declarer.name] + self._scope + [name])
+ "("
+ ",".join(parameters)
+ ")"
)
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
@property
def contains_assembly(self) -> bool:
@ -921,8 +935,12 @@ class Function(
Contract and converted into address
:return: the solidity signature
"""
parameters = [self._convert_type_for_solidity_signature(x.type) for x in self.parameters]
return self.name + "(" + ",".join(parameters) + ")"
if self._solidity_signature is None:
parameters = [
self._convert_type_for_solidity_signature(x.type) for x in self.parameters
]
self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
return self._solidity_signature
@property
def signature(self) -> Tuple[str, List[str], List[str]]:
@ -930,11 +948,14 @@ class Function(
(str, list(str), list(str)): Function signature as
(name, list parameters type, list return values type)
"""
return (
self.name,
[str(x.type) for x in self.parameters],
[str(x.type) for x in self.returns],
)
if self._signature is None:
signature = (
self.name,
[str(x.type) for x in self.parameters],
[str(x.type) for x in self.returns],
)
self._signature = signature
return self._signature
@property
def signature_str(self) -> str:
@ -942,8 +963,12 @@ class Function(
str: func_name(type1,type2) returns (type3)
Return the function signature as a str (contains the return values)
"""
name, parameters, returnVars = self.signature
return name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
if self._signature_str is None:
name, parameters, returnVars = self.signature
self._signature_str = (
name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
)
return self._signature_str
# endregion
###################################################################################
@ -1407,11 +1432,16 @@ class Function(
(bool)
"""
if self.is_constructor:
return True
conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
args_vars = self.all_solidity_variables_used_as_args()
return SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
if self._is_protected is None:
if self.is_constructor:
self._is_protected = True
return True
conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
args_vars = self.all_solidity_variables_used_as_args()
self._is_protected = (
SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
)
return self._is_protected
# endregion
###################################################################################

Loading…
Cancel
Save