def parse_yul_function_call(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]: args = [parse_yul(root, node, arg) for arg in ast["arguments"]] ident = parse_yul(root, node, ast["functionName"]) if not isinstance(ident, Identifier): raise SlitherException( "expected identifier from parsing function name") if isinstance(ident.value, YulBuiltin): name = ident.value.name if name in binary_ops: if name in ["shl", "shr", "sar"]: # lmao ok return BinaryOperation(args[1], args[0], binary_ops[name]) return BinaryOperation(args[0], args[1], binary_ops[name]) if name in unary_ops: return UnaryOperation(args[0], unary_ops[name]) ident = Identifier( SolidityFunction(format_function_descriptor(ident.value.name))) if isinstance(ident.value, Function): return CallExpression(ident, args, vars_to_typestr(ident.value.returns)) if isinstance(ident.value, SolidityFunction): return CallExpression(ident, args, vars_to_typestr(ident.value.return_type)) raise SlitherException( f"unexpected function call target type {str(type(ident.value))}")
def _analyze_content_top_level_function(self): try: for func_parser in self._functions_top_level_parser: func_parser.analyze_content() except (VariableNotFound, KeyError) as e: raise SlitherException( f"Missing {e} during top level function analyze") from e
def state_variable(self): if self._name.endswith("_slot"): return self._name[:-5] if self._name.endswith("_offset"): return self._name[:-7] to_log = f"Incorrect YUL parsing. {self} is not a solidity variable that can be seen as a state variable" raise SlitherException(to_log)
def convert_subdenomination( value: str, sub: str ) -> int: # pylint: disable=too-many-return-statements # to allow 0.1 ether conversion if value[0:2] == "0x": decimal_value = Decimal(int(value, 16)) else: decimal_value = Decimal(value) if sub == "wei": return int(decimal_value) if sub == "szabo": return int(decimal_value * int(1e12)) if sub == "finney": return int(decimal_value * int(1e15)) if sub == "ether": return int(decimal_value * int(1e18)) if sub == "seconds": return int(decimal_value) if sub == "minutes": return int(decimal_value * 60) if sub == "hours": return int(decimal_value * 60 * 60) if sub == "days": return int(decimal_value * 60 * 60 * 24) if sub == "weeks": return int(decimal_value * 60 * 60 * 24 * 7) if sub == "years": return int(decimal_value * 60 * 60 * 24 * 7 * 365) raise SlitherException(f"Subdemonination conversion impossible {decimal_value} {sub}")
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] if name in builtins: return Identifier(YulBuiltin(name)) # check function-scoped variables parent_func = root.parent_func if parent_func: variable = parent_func.get_local_variable_from_name(name) if variable: return Identifier(variable) if isinstance(parent_func, FunctionContract): variable = parent_func.contract.get_state_variable_from_name(name) if variable: return Identifier(variable) # check yul-scoped variable variable = root.get_yul_local_variable_from_name(name) if variable: return Identifier(variable.underlying) # check yul-scoped function func = root.get_yul_local_function_from_name(name) if func: return Identifier(func.underlying) magic_suffix = _parse_yul_magic_suffixes(name, root) if magic_suffix: return magic_suffix raise SlitherException(f"unresolved reference to identifier {name}")
def _analyze_top_level_variables(self): try: for var in self._variables_top_level_parser: var.analyze(var) except (VariableNotFound, KeyError) as e: raise SlitherException( f"Missing {e} during variable analyze") from e
def storage_size(self) -> Tuple[int, bool]: from slither.core.declarations.structure import Structure from slither.core.declarations.enum import Enum from slither.core.declarations.contract import Contract if isinstance(self._type, Contract): return 20, False if isinstance(self._type, Enum): return int(math.ceil(math.log2(len(self._type.values)) / 8)), False if isinstance(self._type, Structure): # todo there's some duplicate logic here and slither_core, can we refactor this? slot = 0 offset = 0 for elem in self._type.elems_ordered: size, new_slot = elem.type.storage_size if new_slot: if offset > 0: slot += 1 offset = 0 elif size + offset > 32: slot += 1 offset = 0 if new_slot: slot += math.ceil(size / 32) else: offset += size if offset > 0: slot += 1 return slot * 32, True to_log = f"{self} does not have storage size" raise SlitherException(to_log)
def convert_subdenomination(value, sub): # to allow 0.1 ether conversion if value[0:2] == "0x": value = float(int(value, 16)) else: value = float(value) if sub == 'wei': return int(value) if sub == 'szabo': return int(value * int(1e12)) if sub == 'finney': return int(value * int(1e15)) if sub == 'ether': return int(value * int(1e18)) if sub == 'seconds': return int(value) if sub == 'minutes': return int(value * 60) if sub == 'hours': return int(value * 60 * 60) if sub == 'days': return int(value * 60 * 60 * 24) if sub == 'weeks': return int(value * 60 * 60 * 24 * 7) if sub == 'years': return int(value * 60 * 60 * 24 * 7 * 365) raise SlitherException(f'Subdemonination conversion impossible {value} {sub}')
def analyze_contracts(self): # pylint: disable=too-many-statements,too-many-branches if not self._parsed: raise SlitherException("Parse the contract before running analyses") self._convert_to_slithir() compute_dependency(self._core) self._core.compute_storage_layout() self._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" ) from e
def _get_source_code(self, contract): src_mapping = contract.source_mapping content = self._slither.source_code[src_mapping['filename_absolute']] start = src_mapping['start'] end = src_mapping['start'] + src_mapping['length'] # interface must use external if self._external_to_public and contract.contract_kind != "interface": # to_patch is a list of (index, bool). The bool indicates # if the index is for external -> public (true) # or a calldata -> memory (false) to_patch = [] for f in contract.functions_declared: # fallback must be external if f.is_fallback or f.is_constructor_variables: continue if f.visibility == 'external': attributes_start = ( f.parameters_src.source_mapping['start'] + f.parameters_src.source_mapping['length']) attributes_end = f.returns_src.source_mapping['start'] attributes = content[attributes_start:attributes_end] regex = re.search( r'((\sexternal)\s+)|(\sexternal)$|(\)external)$', attributes) if regex: to_patch.append( (attributes_start + regex.span()[0] + 1, True)) else: raise SlitherException( f'External keyword not found {f.name} {attributes}' ) for var in f.parameters: if var.location == "calldata": calldata_start = var.source_mapping['start'] calldata_end = calldata_start + var.source_mapping[ 'length'] calldata_idx = content[ calldata_start:calldata_end].find(' calldata ') to_patch.append( (calldata_start + calldata_idx + 1, False)) to_patch.sort(key=lambda x: x[0], reverse=True) content = content[start:end] for (index, is_external) in to_patch: index = index - start if is_external: content = content[:index] + 'public' + content[ index + len('external'):] else: content = content[:index] + 'memory' + content[ index + len('calldata'):] else: content = content[start:end] self._source_codes[contract] = content
def new_node(self, node_type, src) -> YulNode: if self._function: node = self._function.new_node(node_type, src, self.node_scope) else: raise SlitherException("standalone yul objects are not supported yet") yul_node = YulNode(node, self) self._nodes.append(yul_node) return yul_node
def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode: if self._parent_func: node = self._parent_func.new_node(node_type, src, self.node_scope) else: raise SlitherException("standalone yul objects are not supported yet") yul_node = YulNode(node, self) self._nodes.append(yul_node) return yul_node
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] if name in builtins: return Identifier(YulBuiltin(name)) # check function-scoped variables if root.parent_func: variable = root.parent_func.get_local_variable_from_name(name) if variable: return Identifier(variable) variable = root.parent_func.contract.get_state_variable_from_name(name) if variable: return Identifier(variable) # check yul-scoped variable variable = root.get_yul_local_variable_from_name(name) if variable: return Identifier(variable.underlying) # check yul-scoped function func = root.get_yul_local_function_from_name(name) if func: return Identifier(func.underlying) # check for magic suffixes if name.endswith("_slot"): potential_name = name[:-5] var = root.function.contract.get_state_variable_from_name( potential_name) if var: return Identifier(var) var = root.function.get_local_variable_from_name(potential_name) if var and var.is_storage: return Identifier(var) if name.endswith("_offset"): potential_name = name[:-7] var = root.function.contract.get_state_variable_from_name( potential_name) if var: return Identifier(var) raise SlitherException(f"unresolved reference to identifier {name}")
def _get_source_code(self, contract): src_mapping = contract.source_mapping content = self._slither.source_code[src_mapping['filename_absolute']] start = src_mapping['start'] end = src_mapping['start'] + src_mapping['length'] to_patch = [] # interface must use external if self._external_to_public and contract.contract_kind != "interface": for f in contract.functions_declared: # fallback must be external if f.is_fallback or f.is_constructor_variables: continue if f.visibility == 'external': attributes_start = ( f.parameters_src.source_mapping['start'] + f.parameters_src.source_mapping['length']) attributes_end = f.returns_src.source_mapping['start'] attributes = content[attributes_start:attributes_end] regex = re.search( r'((\sexternal)\s+)|(\sexternal)$|(\)external)$', attributes) if regex: to_patch.append( Patch(attributes_start + regex.span()[0] + 1, 'public_to_external')) else: raise SlitherException( f'External keyword not found {f.name} {attributes}' ) for var in f.parameters: if var.location == "calldata": calldata_start = var.source_mapping['start'] calldata_end = calldata_start + var.source_mapping[ 'length'] calldata_idx = content[ calldata_start:calldata_end].find(' calldata ') to_patch.append( Patch(calldata_start + calldata_idx + 1, 'calldata_to_memory')) if self._private_to_internal: for variable in contract.state_variables_declared: if variable.visibility == 'private': print(variable.source_mapping) attributes_start = variable.source_mapping['start'] attributes_end = attributes_start + variable.source_mapping[ 'length'] attributes = content[attributes_start:attributes_end] print(attributes) regex = re.search(r' private ', attributes) if regex: to_patch.append( Patch(attributes_start + regex.span()[0] + 1, 'private_to_internal')) else: raise SlitherException( f'private keyword not found {v.name} {attributes}') if self._remove_assert: for function in contract.functions_and_modifiers_declared: for node in function.nodes: for ir in node.irs: if isinstance( ir, SolidityCall ) and ir.function == SolidityFunction('assert(bool)'): to_patch.append( Patch(node.source_mapping['start'], 'line_removal')) logger.info( f'Code commented: {node.expression} ({node.source_mapping_str})' ) to_patch.sort(key=lambda x: x.index, reverse=True) content = content[start:end] for patch in to_patch: patch_type = patch.patch_type index = patch.index index = index - start if patch_type == 'public_to_external': content = content[:index] + 'public' + content[ index + len('external'):] if patch_type == 'private_to_internal': content = content[:index] + 'internal' + content[ index + len('private'):] elif patch_type == 'calldata_to_memory': content = content[:index] + 'memory' + content[ index + len('calldata'):] else: assert patch_type == 'line_removal' content = content[:index] + ' // ' + content[index:] self._source_codes[contract] = content
def min(self) -> int: if self.name in MinValues: return MinValues[self.name] raise SlitherException(f"{self.name} does not have a min value")
def parse_contracts(self): # pylint: disable=too-many-statements,too-many-branches if not self._underlying_contract_to_parser: logger.info( f"No contract were found in {self._core.filename}, check the correct compilation" ) if self._parsed: raise Exception("Contract analysis can be run only once!") # First we save all the contracts in a dict # the key is the contractid for contract in self._underlying_contract_to_parser: if ( contract.name.startswith("SlitherInternalTopLevelContract") and not contract.is_top_level ): raise SlitherException( """Your codebase has a contract named 'SlitherInternalTopLevelContract'. Please rename it, this name is reserved for Slither's internals""" ) if contract.name in self._core.contracts_as_dict: if contract.id != self._core.contracts_as_dict[contract.name].id: self._core.contract_name_collisions[contract.name].append( contract.source_mapping_str ) self._core.contract_name_collisions[contract.name].append( self._core.contracts_as_dict[contract.name].source_mapping_str ) else: self._contracts_by_id[contract.id] = contract self._core.contracts_as_dict[contract.name] = contract # Update of the inheritance for contract_parser in self._underlying_contract_to_parser.values(): # remove the first elem in linearizedBaseContracts as it is the contract itself ancestors = [] fathers = [] father_constructors = [] # try: # Resolve linearized base contracts. missing_inheritance = False for i in contract_parser.linearized_base_contracts[1:]: if i in contract_parser.remapping: ancestors.append( self._core.get_contract_from_name(contract_parser.remapping[i]) ) elif i in self._contracts_by_id: ancestors.append(self._contracts_by_id[i]) else: missing_inheritance = True # Resolve immediate base contracts for i in contract_parser.baseContracts: if i in contract_parser.remapping: fathers.append(self._core.get_contract_from_name(contract_parser.remapping[i])) elif i in self._contracts_by_id: fathers.append(self._contracts_by_id[i]) else: missing_inheritance = True # Resolve immediate base constructor calls for i in contract_parser.baseConstructorContractsCalled: if i in contract_parser.remapping: father_constructors.append( self._core.get_contract_from_name(contract_parser.remapping[i]) ) elif i in self._contracts_by_id: father_constructors.append(self._contracts_by_id[i]) else: missing_inheritance = True contract_parser.underlying_contract.set_inheritance( ancestors, fathers, father_constructors ) if missing_inheritance: self._core.contracts_with_missing_inheritance.add( contract_parser.underlying_contract ) contract_parser.log_incorrect_parsing(f"Missing inheritance {contract_parser}") contract_parser.set_is_analyzed(True) contract_parser.delete_content() contracts_to_be_analyzed = list(self._underlying_contract_to_parser.values()) # Any contract can refer another contract enum without need for inheritance self._analyze_all_enums(contracts_to_be_analyzed) # pylint: disable=expression-not-assigned [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()] libraries = [ c for c in contracts_to_be_analyzed if c.underlying_contract.contract_kind == "library" ] contracts_to_be_analyzed = [ c for c in contracts_to_be_analyzed if c.underlying_contract.contract_kind != "library" ] # We first parse the struct/variables/functions/contract self._analyze_first_part(contracts_to_be_analyzed, libraries) # pylint: disable=expression-not-assigned [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()] # We analyze the struct and parse and analyze the events # A contract can refer in the variables a struct or a event from any contract # (without inheritance link) self._analyze_second_part(contracts_to_be_analyzed, libraries) [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()] # Then we analyse state variables, functions and modifiers self._analyze_third_part(contracts_to_be_analyzed, libraries) self._parsed = True
def max(self) -> int: if self.name in MaxValues: return MaxValues[self.name] raise SlitherException(f"{self.name} does not have a max value")
def parse_yul_unsupported(_root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: raise SlitherException( f"no parser available for {ast['nodeType']} {json.dumps(ast, indent=2)}" )
def convert_yul_unsupported(root: YulScope, parent: YulNode, ast: Dict) -> YulNode: raise SlitherException( f"no converter available for {ast['nodeType']} {json.dumps(ast, indent=2)}" )
def parse_top_level_from_loaded_json( self, data_loaded: Dict, filename: str ): # pylint: disable=too-many-branches,too-many-statements if "nodeType" in data_loaded: self._is_compact_ast = True if "sourcePaths" in data_loaded: for sourcePath in data_loaded["sourcePaths"]: if os.path.isfile(sourcePath): self._compilation_unit.core.add_source_code(sourcePath) if data_loaded[self.get_key()] == "root": logger.error("solc <0.4 is not supported") return if data_loaded[self.get_key()] == "SourceUnit": self._parse_source_unit(data_loaded, filename) else: logger.error("solc version is not supported") return for top_level_data in data_loaded[self.get_children()]: if top_level_data[self.get_key()] == "ContractDefinition": contract = Contract(self._compilation_unit) contract_parser = ContractSolc(self, contract, top_level_data) if "src" in top_level_data: contract.set_offset(top_level_data["src"], self._compilation_unit) self._underlying_contract_to_parser[contract] = contract_parser elif top_level_data[self.get_key()] == "PragmaDirective": if self._is_compact_ast: pragma = Pragma(top_level_data["literals"]) else: pragma = Pragma(top_level_data["attributes"]["literals"]) pragma.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.pragma_directives.append(pragma) elif top_level_data[self.get_key()] == "ImportDirective": if self.is_compact_ast: import_directive = Import(top_level_data["absolutePath"]) # TODO investigate unitAlias in version < 0.7 and legacy ast if "unitAlias" in top_level_data: import_directive.alias = top_level_data["unitAlias"] else: import_directive = Import(top_level_data["attributes"].get("absolutePath", "")) import_directive.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.import_directives.append(import_directive) elif top_level_data[self.get_key()] == "StructDefinition": st = StructureTopLevel() st.set_offset(top_level_data["src"], self._compilation_unit) st_parser = StructureTopLevelSolc(st, top_level_data, self) self._compilation_unit.structures_top_level.append(st) self._structures_top_level_parser.append(st_parser) 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) 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._compilation_unit) self._compilation_unit.variables_top_level.append(var) self._variables_top_level_parser.append(var_parser) elif top_level_data[self.get_key()] == "FunctionDefinition": func = FunctionTopLevel(self._compilation_unit) func_parser = FunctionSolc(func, top_level_data, None, self) self._compilation_unit.functions_top_level.append(func) self._functions_top_level_parser.append(func_parser) self.add_function_or_modifier_parser(func_parser) else: raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")
def _get_source_code(self, contract: Contract): # pylint: disable=too-many-branches,too-many-statements """ Save the source code of the contract in self._source_codes Patch the source code :param contract: :return: """ src_mapping = contract.source_mapping content = self._slither.source_code[ src_mapping["filename_absolute"]].encode("utf8") start = src_mapping["start"] end = src_mapping["start"] + src_mapping["length"] to_patch = [] # interface must use external if self._external_to_public and contract.contract_kind != "interface": for f in contract.functions_declared: # fallback must be external if f.is_fallback or f.is_constructor_variables: continue if f.visibility == "external": attributes_start = ( f.parameters_src().source_mapping["start"] + f.parameters_src().source_mapping["length"]) attributes_end = f.returns_src().source_mapping["start"] attributes = content[attributes_start:attributes_end] regex = re.search( r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes) if regex: to_patch.append( Patch( attributes_start + regex.span()[0] + 1, "public_to_external", )) else: raise SlitherException( f"External keyword not found {f.name} {attributes}" ) for var in f.parameters: if var.location == "calldata": calldata_start = var.source_mapping["start"] calldata_end = calldata_start + var.source_mapping[ "length"] calldata_idx = content[ calldata_start:calldata_end].find(" calldata ") to_patch.append( Patch( calldata_start + calldata_idx + 1, "calldata_to_memory", )) if self._private_to_internal: for variable in contract.state_variables_declared: if variable.visibility == "private": print(variable.source_mapping) attributes_start = variable.source_mapping["start"] attributes_end = attributes_start + variable.source_mapping[ "length"] attributes = content[attributes_start:attributes_end] print(attributes) regex = re.search(r" private ", attributes) if regex: to_patch.append( Patch( attributes_start + regex.span()[0] + 1, "private_to_internal", )) else: raise SlitherException( f"private keyword not found {variable.name} {attributes}" ) if self._remove_assert: for function in contract.functions_and_modifiers_declared: for node in function.nodes: for ir in node.irs: if isinstance( ir, SolidityCall ) and ir.function == SolidityFunction("assert(bool)"): to_patch.append( Patch(node.source_mapping["start"], "line_removal")) logger.info( f"Code commented: {node.expression} ({node.source_mapping_str})" ) to_patch.sort(key=lambda x: x.index, reverse=True) content = content[start:end] for patch in to_patch: patch_type = patch.patch_type index = patch.index index = index - start if patch_type == "public_to_external": content = content[:index] + "public" + content[ index + len("external"):] if patch_type == "private_to_internal": content = content[:index] + "internal" + content[ index + len("private"):] elif patch_type == "calldata_to_memory": content = content[:index] + "memory" + content[ index + len("calldata"):] else: assert patch_type == "line_removal" content = content[:index] + " // " + content[index:] self._source_codes[contract] = content.decode("utf8")
def parse_top_level_from_loaded_json(self, data_loaded: Dict, filename: str): # pylint: disable=too-many-branches,too-many-statements,too-many-locals if "nodeType" in data_loaded: self._is_compact_ast = True if "sourcePaths" in data_loaded: for sourcePath in data_loaded["sourcePaths"]: if os.path.isfile(sourcePath): self._compilation_unit.core.add_source_code(sourcePath) if data_loaded[self.get_key()] == "root": logger.error("solc <0.4 is not supported") return if data_loaded[self.get_key()] == "SourceUnit": self._parse_source_unit(data_loaded, filename) else: logger.error("solc version is not supported") return if self.get_children() not in data_loaded: return scope = self.compilation_unit.get_scope(filename) for top_level_data in data_loaded[self.get_children()]: if top_level_data[self.get_key()] == "ContractDefinition": contract = Contract(self._compilation_unit, scope) contract_parser = ContractSolc(self, contract, top_level_data) scope.contracts[contract.name] = contract if "src" in top_level_data: contract.set_offset(top_level_data["src"], self._compilation_unit) self._underlying_contract_to_parser[contract] = contract_parser elif top_level_data[self.get_key()] == "PragmaDirective": if self._is_compact_ast: pragma = Pragma(top_level_data["literals"], scope) scope.pragmas.add(pragma) else: pragma = Pragma(top_level_data["attributes"]["literals"], scope) scope.pragmas.add(pragma) pragma.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.pragma_directives.append(pragma) elif top_level_data[self.get_key()] == "ImportDirective": if self.is_compact_ast: import_directive = Import( Path(top_level_data["absolutePath"], ), scope, ) scope.imports.add(import_directive) # TODO investigate unitAlias in version < 0.7 and legacy ast if "unitAlias" in top_level_data: import_directive.alias = top_level_data["unitAlias"] if "symbolAliases" in top_level_data: symbol_aliases = top_level_data["symbolAliases"] _handle_import_aliases(symbol_aliases, import_directive, scope) else: import_directive = Import( Path( top_level_data["attributes"].get( "absolutePath", ""), ), scope, ) scope.imports.add(import_directive) # TODO investigate unitAlias in version < 0.7 and legacy ast if ("attributes" in top_level_data and "unitAlias" in top_level_data["attributes"]): import_directive.alias = top_level_data["attributes"][ "unitAlias"] import_directive.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.import_directives.append( import_directive) get_imported_scope = self.compilation_unit.get_scope( import_directive.filename) scope.accessible_scopes.append(get_imported_scope) elif top_level_data[self.get_key()] == "StructDefinition": scope = self.compilation_unit.get_scope(filename) st = StructureTopLevel(self.compilation_unit, scope) st.set_offset(top_level_data["src"], self._compilation_unit) st_parser = StructureTopLevelSolc(st, top_level_data, self) scope.structures[st.name] = st self._compilation_unit.structures_top_level.append(st) self._structures_top_level_parser.append(st_parser) 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, filename) elif top_level_data[self.get_key()] == "VariableDeclaration": var = TopLevelVariable(scope) var_parser = TopLevelVariableSolc(var, top_level_data, self) var.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.variables_top_level.append(var) self._variables_top_level_parser.append(var_parser) scope.variables[var.name] = var elif top_level_data[self.get_key()] == "FunctionDefinition": scope = self.compilation_unit.get_scope(filename) func = FunctionTopLevel(self._compilation_unit, scope) scope.functions.add(func) func.set_offset(top_level_data["src"], self._compilation_unit) func_parser = FunctionSolc(func, top_level_data, None, self) self._compilation_unit.functions_top_level.append(func) self._functions_top_level_parser.append(func_parser) self.add_function_or_modifier_parser(func_parser) elif top_level_data[self.get_key()] == "ErrorDefinition": scope = self.compilation_unit.get_scope(filename) custom_error = CustomErrorTopLevel(self._compilation_unit, scope) custom_error.set_offset(top_level_data["src"], self._compilation_unit) custom_error_parser = CustomErrorSolc(custom_error, top_level_data, self) scope.custom_errors.add(custom_error) self._compilation_unit.custom_errors.append(custom_error) self._custom_error_parser.append(custom_error_parser) elif top_level_data[ self.get_key()] == "UserDefinedValueTypeDefinition": assert "name" in top_level_data alias = top_level_data["name"] assert "underlyingType" in top_level_data underlying_type = top_level_data["underlyingType"] assert ("nodeType" in underlying_type and underlying_type["nodeType"] == "ElementaryTypeName") assert "name" in underlying_type original_type = ElementaryType(underlying_type["name"]) user_defined_type = TypeAliasTopLevel(original_type, alias, scope) user_defined_type.set_offset(top_level_data["src"], self._compilation_unit) scope.user_defined_types[alias] = user_defined_type else: raise SlitherException( f"Top level {top_level_data[self.get_key()]} not supported" )
def _analyze_contracts(self): if not self._contractsNotParsed: logger.info( f'No contract were found in {self.filename}, check the correct compilation' ) if self._analyzed: raise Exception('Contract analysis can be run only once!') # First we save all the contracts in a dict # the key is the contractid for contract in self._contractsNotParsed: if contract.name.startswith('SlitherInternalTopLevelContract' ) and not contract.is_top_level: raise SlitherException( """Your codebase has a contract named 'SlitherInternalTopLevelContract'. Please rename it, this name is reserved for Slither's internals""") if contract.name in self._contracts: if contract.id != self._contracts[contract.name].id: self._contract_name_collisions[contract.name].append( contract.source_mapping_str) self._contract_name_collisions[contract.name].append( self._contracts[contract.name].source_mapping_str) else: self._contracts_by_id[contract.id] = contract self._contracts[contract.name] = contract # Update of the inheritance for contract in self._contractsNotParsed: # remove the first elem in linearizedBaseContracts as it is the contract itself ancestors = [] fathers = [] father_constructors = [] # try: # Resolve linearized base contracts. missing_inheritance = False for i in contract.linearizedBaseContracts[1:]: if i in contract.remapping: ancestors.append( self.get_contract_from_name(contract.remapping[i])) elif i in self._contracts_by_id: ancestors.append(self._contracts_by_id[i]) else: missing_inheritance = True # Resolve immediate base contracts for i in contract.baseContracts: if i in contract.remapping: fathers.append( self.get_contract_from_name(contract.remapping[i])) elif i in self._contracts_by_id: fathers.append(self._contracts_by_id[i]) else: missing_inheritance = True # Resolve immediate base constructor calls for i in contract.baseConstructorContractsCalled: if i in contract.remapping: father_constructors.append( self.get_contract_from_name(contract.remapping[i])) elif i in self._contracts_by_id: father_constructors.append(self._contracts_by_id[i]) else: missing_inheritance = True contract.setInheritance(ancestors, fathers, father_constructors) if missing_inheritance: self._contract_with_missing_inheritance.add(contract) contract.log_incorrect_parsing( f'Missing inheritance {contract}') contract.set_is_analyzed(True) contract.delete_content() contracts_to_be_analyzed = self.contracts # Any contract can refer another contract enum without need for inheritance self._analyze_all_enums(contracts_to_be_analyzed) [c.set_is_analyzed(False) for c in self.contracts] libraries = [ c for c in contracts_to_be_analyzed if c.contract_kind == 'library' ] contracts_to_be_analyzed = [ c for c in contracts_to_be_analyzed if c.contract_kind != 'library' ] # We first parse the struct/variables/functions/contract self._analyze_first_part(contracts_to_be_analyzed, libraries) [c.set_is_analyzed(False) for c in self.contracts] # We analyze the struct and parse and analyze the events # A contract can refer in the variables a struct or a event from any contract # (without inheritance link) self._analyze_second_part(contracts_to_be_analyzed, libraries) [c.set_is_analyzed(False) for c in self.contracts] # Then we analyse state variables, functions and modifiers self._analyze_third_part(contracts_to_be_analyzed, libraries) self._analyzed = True self._convert_to_slithir() compute_dependency(self)