From c507b3b99f8adbe5c504fa09f26d152911dc918c Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 14 Mar 2023 16:14:39 -0500 Subject: [PATCH] Move code generation to a new util Rather than on the core objects. --- slither/core/declarations/contract.py | 19 ------ slither/core/declarations/function.py | 35 ---------- slither/core/declarations/structure.py | 6 -- slither/utils/code_generation.py | 94 ++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 60 deletions(-) create mode 100644 slither/utils/code_generation.py diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index fc6fa0824..eb2ac9a2e 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -953,25 +953,6 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods """ return all((not f.is_implemented) for f in self.functions) - def generate_interface(self) -> str: - interface = f"interface I{self.name} {{\n" - for event in self.events: - name, args = event.signature - interface += f" event {name}({','.join(args)});\n" - for struct in self.structures: - if isinstance(struct.interface_def_str(), str): - interface += struct.interface_def_str() - for var in self.state_variables_entry_points: - interface += ( - f" function {var.signature_str.replace('returns', 'external returns ')};\n" - ) - for func in self.functions_entry_points: - if func.is_constructor or func.is_fallback or func.is_receive: - continue - interface += f" function {func.interface_signature_str};\n" - interface += "}\n\n" - return interface - # endregion ################################################################################### ################################################################################### diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 4b4bbf82c..ad765bfc0 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1004,41 +1004,6 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu ) return self._signature_str - @property - def interface_signature_str(self) -> Optional[str]: - """ - str: func_name(type1,type2) external {payable/view/pure} returns (type3) - Return the function interface as a str (contains the return values) - Returns None if the function is private or internal, or is a constructor/fallback/receive - """ - from slither.core.declarations.contract import Contract - - if self._interface_signature_str is None: - name, parameters, return_vars = self.signature - visibility = self.visibility - if ( - visibility in ["private", "internal"] - or self.is_constructor - or self.is_fallback - or self.is_receive - ): - return None - view = " view" if self.view else "" - pure = " pure" if self.pure else "" - payable = " payable" if self.payable else "" - returns = [ - "address" - if isinstance(ret.type, UserDefinedType) and isinstance(ret.type.type, Contract) - else str(ret.type) - for ret in self.returns - ] - self._interface_signature_str = ( - name + "(" + ",".join(parameters) + ") external" + payable + pure + view - ) - if len(return_vars) > 0: - self._interface_signature_str += " returns (" + ",".join(returns) + ")" - return self._interface_signature_str - # endregion ################################################################################### ################################################################################### diff --git a/slither/core/declarations/structure.py b/slither/core/declarations/structure.py index 2800f809c..8f6d8c50a 100644 --- a/slither/core/declarations/structure.py +++ b/slither/core/declarations/structure.py @@ -49,11 +49,5 @@ class Structure(SourceMapping): ret.append(self._elems[e]) return ret - def interface_def_str(self) -> str: - definition = f" struct {self.name} {{\n" - for elem in self.elems_ordered: - definition += f" {elem.type} {elem.name};\n" - definition += " }\n" - def __str__(self) -> str: return self.name diff --git a/slither/utils/code_generation.py b/slither/utils/code_generation.py new file mode 100644 index 000000000..7f5c88b2f --- /dev/null +++ b/slither/utils/code_generation.py @@ -0,0 +1,94 @@ +# Functions for generating Solidity code +from typing import TYPE_CHECKING, List + +if TYPE_CHECKING: + from slither.core.declarations import Function, Contract, Structure + + +def generate_interface(contract: "Contract") -> str: + """ + Generates code for a Solidity interface to the contract. + Args: + contract: A Contract object + + Returns: + A string with the code for an interface, with function stubs for all public or external functions and + state variables, as well as any events or structs declared in the contract. + """ + interface = f"interface I{contract.name} {{\n" + for event in contract.events: + name, args = event.signature + interface += f" event {name}({','.join(args)});\n" + for struct in contract.structures: + interface += generate_struct_interface_str(struct) + for var in contract.state_variables_entry_points: + interface += ( + f" function {var.signature_str.replace('returns', 'external returns ')};\n" + ) + for func in contract.functions_entry_points: + if func.is_constructor or func.is_fallback or func.is_receive: + continue + interface += f" function {generate_interface_function_signature(func)};\n" + interface += "}\n\n" + return interface + + +def generate_interface_function_signature(func: "Function") -> Optional[str]: + """ + Generates a string of the form: + func_name(type1,type2) external {payable/view/pure} returns (type3) + + Args: + func: A Function object + + Returns: + The function interface as a str (contains the return values). + Returns None if the function is private or internal, or is a constructor/fallback/receive. + """ + from slither.core.declarations.contract import Contract + + name, parameters, return_vars = func.signature + visibility = func.visibility + if ( + visibility in ["private", "internal"] + or func.is_constructor + or func.is_fallback + or func.is_receive + ): + return None + view = " view" if func.view else "" + pure = " pure" if func.pure else "" + payable = " payable" if func.payable else "" + returns = [ + "address" + if isinstance(ret.type, UserDefinedType) and isinstance(ret.type.type, Contract) + else str(ret.type) + for ret in func.returns + ] + _interface_signature_str = ( + name + "(" + ",".join(parameters) + ") external" + payable + pure + view + ) + if len(return_vars) > 0: + _interface_signature_str += " returns (" + ",".join(returns) + ")" + return _interface_signature_str + + +def generate_struct_interface_str(struct: "Structure") -> str: + """ + Generates code for a structure declaration in an interface of the form: + struct struct_name { + elem1_type elem1_name; + elem2_type elem2_name; + ... ... + } + Args: + struct: A Structure object + + Returns: + The structure declaration code as a string. + """ + definition = f" struct {struct.name} {{\n" + for elem in struct.elems_ordered: + definition += f" {elem.type} {elem.name};\n" + definition += " }\n" + return definition