def _convert_to_id(d): """ Id keeps the source mapping of the node, otherwise we risk to consider two different node as the same :param d: :return: """ if isinstance(d, str): return d if not isinstance(d, SourceMapping): raise SlitherError( f"{d} does not inherit from SourceMapping, conversion impossible") if isinstance(d, Node): if d.expression: return f"{d.expression} ({d.source_mapping_str})" return f"{str(d)} ({d.source_mapping_str})" if isinstance(d, Pragma): return f"{d} ({d.source_mapping_str})" if hasattr(d, "canonical_name"): return f"{d.canonical_name}" if hasattr(d, "name"): return f"{d.name}" raise SlitherError( f"{type(d)} cannot be converted (no name, or canonical_name")
def _convert_to_id(d): ''' Id keeps the source mapping of the node, otherwise we risk to consider two different node as the same :param d: :return: ''' if isinstance(d, str): return d if not isinstance(d, SourceMapping): raise SlitherError( f'{d} does not inherit from SourceMapping, conversion impossible') if isinstance(d, Node): if d.expression: return f'{d.expression} ({d.source_mapping_str})' else: return f'{str(d)} ({d.source_mapping_str})' if hasattr(d, 'canonical_name'): return f'{d.canonical_name}' if hasattr(d, 'name'): return f'{d.name}' raise SlitherError( f'{type(d)} cannot be converted (no name, or canonical_name')
def add(self, add: SupportedOutput, additional_fields: Optional[Dict] = None): if not self._data["first_markdown_element"]: self._data[ "first_markdown_element"] = add.source_mapping_to_markdown( self._markdown_root) if isinstance(add, Variable): self.add_variable(add, additional_fields=additional_fields) elif isinstance(add, Contract): self.add_contract(add, additional_fields=additional_fields) elif isinstance(add, Function): self.add_function(add, additional_fields=additional_fields) elif isinstance(add, Enum): self.add_enum(add, additional_fields=additional_fields) elif isinstance(add, Event): self.add_event(add, additional_fields=additional_fields) elif isinstance(add, Structure): self.add_struct(add, additional_fields=additional_fields) elif isinstance(add, Pragma): self.add_pragma(add, additional_fields=additional_fields) elif isinstance(add, Node): self.add_node(add, additional_fields=additional_fields) else: raise SlitherError(f"Impossible to add {type(add)} to the json")
def _get_evm_instructions_function(function_info): function = function_info["function"] # CFG depends on function being constructor or not if function.is_constructor: cfg = function_info["contract_info"]["cfg_init"] # _dispatcher is the only function recognised by evm-cfg-builder in bytecode_init. # _dispatcher serves the role of the constructor in init code, # given that there are no other functions. # Todo: Could rename it appropriately in evm-cfg-builder # by detecting that init bytecode is being parsed. name = "_dispatcher" func_hash = "" else: cfg = function_info["contract_info"]["cfg"] name = function.name # Get first four bytes of function singature's keccak-256 hash used as function selector func_hash = str(hex(get_function_id(function.full_name))) function_evm = _get_function_evm(cfg, name, func_hash) if function_evm is None: to_log = "Function " + function.name + " not found in the EVM code" logger.error(to_log) raise SlitherError("Function " + function.name + " not found in the EVM code") function_ins = [] for basic_block in sorted(function_evm.basic_blocks, key=lambda x: x.start.pc): for ins in basic_block.instructions: function_ins.append(ins) return function_ins
def _init_from_raw_json(self, filename): if not os.path.isfile(filename): raise SlitherError( "{} does not exist (are you in the correct directory?)".format(filename) ) assert filename.endswith("json") with open(filename, encoding="utf8") as astFile: stdout = astFile.read() if not stdout: to_log = f"Empty AST file: {filename}" raise SlitherError(to_log) contracts_json = stdout.split("\n=") self._parser = SlitherSolc(filename, self) for c in contracts_json: self._parser.parse_top_level_from_json(c)
def _visit_expression(self, expression): self._pre_visit(expression) if isinstance(expression, AssignmentOperation): self._visit_assignement_operation(expression) elif isinstance(expression, BinaryOperation): self._visit_binary_operation(expression) elif isinstance(expression, CallExpression): self._visit_call_expression(expression) elif isinstance(expression, ConditionalExpression): self._visit_conditional_expression(expression) elif isinstance(expression, ElementaryTypeNameExpression): self._visit_elementary_type_name_expression(expression) elif isinstance(expression, Identifier): self._visit_identifier(expression) elif isinstance(expression, IndexAccess): self._visit_index_access(expression) elif isinstance(expression, Literal): self._visit_literal(expression) elif isinstance(expression, MemberAccess): self._visit_member_access(expression) elif isinstance(expression, NewArray): self._visit_new_array(expression) elif isinstance(expression, NewContract): self._visit_new_contract(expression) elif isinstance(expression, NewElementaryType): self._visit_new_elementary_type(expression) elif isinstance(expression, TupleExpression): self._visit_tuple_expression(expression) elif isinstance(expression, TypeConversion): self._visit_type_conversion(expression) elif isinstance(expression, UnaryOperation): self._visit_unary_operation(expression) elif expression is None: pass else: raise SlitherError('Expression not handled: {}'.format(expression)) self._post_visit(expression)
def _find_variable_init( caller_context: CallerContextExpression, ) -> Tuple[List[Contract], List["Function"], FileScope,]: from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc direct_contracts: List[Contract] direct_functions_parser: List[Function] scope: FileScope if isinstance(caller_context, FileScope): direct_contracts = [] direct_functions_parser = [] scope = caller_context elif isinstance(caller_context, ContractSolc): direct_contracts = [caller_context.underlying_contract] direct_functions_parser = [ f.underlying_function for f in caller_context.functions_parser + caller_context.modifiers_parser ] scope = caller_context.underlying_contract.file_scope elif isinstance(caller_context, FunctionSolc): if caller_context.contract_parser: direct_contracts = [caller_context.contract_parser.underlying_contract] direct_functions_parser = [ f.underlying_function for f in caller_context.contract_parser.functions_parser + caller_context.contract_parser.modifiers_parser ] else: # Top level functions direct_contracts = [] direct_functions_parser = [] underlying_function = caller_context.underlying_function if isinstance(underlying_function, FunctionTopLevel): scope = underlying_function.file_scope else: assert isinstance(underlying_function, FunctionContract) scope = underlying_function.contract.file_scope elif isinstance(caller_context, StructureTopLevelSolc): direct_contracts = [] direct_functions_parser = [] scope = caller_context.underlying_structure.file_scope elif isinstance(caller_context, TopLevelVariableSolc): direct_contracts = [] direct_functions_parser = [] scope = caller_context.underlying_variable.file_scope else: raise SlitherError( f"{type(caller_context)} ({caller_context} is not valid for find_variable" ) return direct_contracts, direct_functions_parser, scope
def _convert_to_markdown(d, markdown_root): if isinstance(d, str): return d if not isinstance(d, SourceMapping): raise SlitherError(f'{d} does not inherit from SourceMapping, conversion impossible') if isinstance(d, Node): if d.expression: return f'[{d.expression}]({d.source_mapping_to_markdown(markdown_root)})' else: return f'[{str(d)}]({d.source_mapping_to_markdown(markdown_root)})' if hasattr(d, 'canonical_name'): return f'[{d.canonical_name}]({d.source_mapping_to_markdown(markdown_root)})' if hasattr(d, 'name'): return f'[{d.name}]({d.source_mapping_to_markdown(markdown_root)})' raise SlitherError(f'{type(d)} cannot be converted (no name, or canonical_name')
def _convert_to_description(d): if isinstance(d, str): return d if not isinstance(d, SourceMapping): raise SlitherError( f"{d} does not inherit from SourceMapping, conversion impossible") if isinstance(d, Node): if d.expression: return f"{d.expression} ({d.source_mapping_str})" return f"{str(d)} ({d.source_mapping_str})" if hasattr(d, "canonical_name"): return f"{d.canonical_name} ({d.source_mapping_str})" if hasattr(d, "name"): return f"{d.name} ({d.source_mapping_str})" raise SlitherError( f"{type(d)} cannot be converted (no name, or canonical_name")
def load_evm_cfg_builder(): try: # Avoiding the addition of evm_cfg_builder as permanent dependency from evm_cfg_builder.cfg import CFG return CFG except ImportError: logger.error( "To use evm features, you need to install evm-cfg-builder") logger.error( "Documentation: https://github.com/crytic/evm_cfg_builder") logger.error("Installation: pip install evm-cfg-builder") raise SlitherError("evm-cfg-builder not installed.")
def _get_function_or_variable(contract, signature): f = contract.get_function_from_signature(signature) if f: return f for variable in contract.state_variables: # Todo: can lead to incorrect variable in case of shadowing if variable.visibility in ["public"]: if variable.name + "()" == signature: return variable raise SlitherError(f"Function id checks: {signature} not found in {contract.name}")
def _pre_visit(self, expression): # pylint: disable=too-many-branches if isinstance(expression, AssignmentOperation): self._pre_assignement_operation(expression) elif isinstance(expression, BinaryOperation): self._pre_binary_operation(expression) elif isinstance(expression, CallExpression): self._pre_call_expression(expression) elif isinstance(expression, ConditionalExpression): self._pre_conditional_expression(expression) elif isinstance(expression, ElementaryTypeNameExpression): self._pre_elementary_type_name_expression(expression) elif isinstance(expression, Identifier): self._pre_identifier(expression) elif isinstance(expression, IndexAccess): self._pre_index_access(expression) elif isinstance(expression, Literal): self._pre_literal(expression) elif isinstance(expression, MemberAccess): self._pre_member_access(expression) elif isinstance(expression, NewArray): self._pre_new_array(expression) elif isinstance(expression, NewContract): self._pre_new_contract(expression) elif isinstance(expression, NewElementaryType): self._pre_new_elementary_type(expression) elif isinstance(expression, TupleExpression): self._pre_tuple_expression(expression) elif isinstance(expression, TypeConversion): self._pre_type_conversion(expression) elif isinstance(expression, UnaryOperation): self._pre_unary_operation(expression) elif expression is None: pass else: raise SlitherError("Expression not handled: {}".format(expression))
def add(self, add, additional_fields=None): if isinstance(add, Variable): self.add_variable(add, additional_fields=additional_fields) elif isinstance(add, Contract): self.add_contract(add, additional_fields=additional_fields) elif isinstance(add, Function): self.add_function(add, additional_fields=additional_fields) elif isinstance(add, Enum): self.add_enum(add, additional_fields=additional_fields) elif isinstance(add, Event): self.add_event(add, additional_fields=additional_fields) elif isinstance(add, Structure): self.add_struct(add, additional_fields=additional_fields) elif isinstance(add, Pragma): self.add_pragma(add, additional_fields=additional_fields) elif isinstance(add, Node): self.add_node(add, additional_fields=additional_fields) else: raise SlitherError(f'Impossible to add {type(add)} to the json')
def convert_string_to_int(val: str) -> int: if val.startswith("0x") or val.startswith("0X"): return int(val, 16) if "e" in val or "E" in val: base, expo = val.split("e") if "e" in val else val.split("E") base, expo = Decimal(base), int(expo) # The resulting number must be < 2**256-1, otherwise solc # Would not be able to compile it # 10**77 is the largest exponent that fits # See https://github.com/ethereum/solidity/blob/9e61f92bd4d19b430cb8cb26f1c7cf79f1dff380/libsolidity/ast/Types.cpp#L1281-L1290 if expo > 77: if base != Decimal(0): raise SlitherError( f"{base}e{expo} is too large to fit in any Solidity integer size" ) return 0 return int(Decimal(base) * Decimal(10 ** expo)) return int(Decimal(val))
def _find_variable_init( caller_context: CallerContext, ) -> Tuple[List[Contract], Union[List["FunctionSolc"]], "SlitherCompilationUnit", "SlitherCompilationUnitSolc", ]: from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.function import FunctionSolc direct_contracts: List[Contract] direct_functions_parser: List[FunctionSolc] if isinstance(caller_context, SlitherCompilationUnitSolc): direct_contracts = [] direct_functions_parser = [] sl = caller_context.compilation_unit sl_parser = caller_context elif isinstance(caller_context, ContractSolc): direct_contracts = [caller_context.underlying_contract] direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser sl = caller_context.slither_parser.compilation_unit sl_parser = caller_context.slither_parser elif isinstance(caller_context, FunctionSolc): if caller_context.contract_parser: direct_contracts = [ caller_context.contract_parser.underlying_contract ] direct_functions_parser = ( caller_context.contract_parser.functions_parser + caller_context.contract_parser.modifiers_parser) else: # Top level functions direct_contracts = [] direct_functions_parser = [] sl = caller_context.underlying_function.compilation_unit sl_parser = caller_context.slither_parser else: raise SlitherError( f"{type(caller_context)} ({caller_context} is not valid for find_variable" ) return direct_contracts, direct_functions_parser, sl, sl_parser
def get_evm_instructions(obj): assert isinstance(obj, (Function, Contract, Node)) if KEY_EVM_INS not in obj.context: CFG = load_evm_cfg_builder() slither = obj.slither if not slither.crytic_compile: raise SlitherError( "EVM features require to compile with crytic-compile") contract_info = {} function_info = {} node_info = {} if isinstance(obj, Node): contract_info["contract"] = obj.function.contract elif isinstance(obj, Function): contract_info["contract"] = obj.contract else: contract_info["contract"] = obj # Get contract runtime bytecode, srcmap and cfg contract_info[ "bytecode_runtime"] = slither.crytic_compile.bytecode_runtime( contract_info["contract"].name) contract_info[ "srcmap_runtime"] = slither.crytic_compile.srcmap_runtime( contract_info["contract"].name) contract_info["cfg"] = CFG(contract_info["bytecode_runtime"]) # Get contract init bytecode, srcmap and cfg contract_info["bytecode_init"] = slither.crytic_compile.bytecode_init( contract_info["contract"].name) contract_info["srcmap_init"] = slither.crytic_compile.srcmap_init( contract_info["contract"].name) contract_info["cfg_init"] = CFG(contract_info["bytecode_init"]) # Get evm instructions if isinstance(obj, Contract): # Get evm instructions for contract obj.context[KEY_EVM_INS] = _get_evm_instructions_contract( contract_info) elif isinstance(obj, Function): # Get evm instructions for function function_info["function"] = obj function_info["contract_info"] = contract_info obj.context[KEY_EVM_INS] = _get_evm_instructions_function( function_info) else: # Get evm instructions for node node_info["node"] = obj # CFG and srcmap depend on function being constructor or not if node_info["node"].function.is_constructor: cfg = contract_info["cfg_init"] srcmap = contract_info["srcmap_init"] else: cfg = contract_info["cfg"] srcmap = contract_info["srcmap_runtime"] node_info["cfg"] = cfg node_info["srcmap"] = srcmap node_info["contract"] = contract_info["contract"] node_info["slither"] = slither obj.context[KEY_EVM_INS] = _get_evm_instructions_node(node_info) return obj.context.get(KEY_EVM_INS, [])
def __init__(self, target: Union[str, CryticCompile], **kwargs): """ Args: target (str | CryticCompile) Keyword Args: solc (str): solc binary location (default 'solc') disable_solc_warnings (bool): True to disable solc warnings (default false) solc_arguments (str): solc arguments (default '') ast_format (str): ast format (default '--ast-compact-json') filter_paths (list(str)): list of path to filter (default []) triage_mode (bool): if true, switch to triage mode (default false) exclude_dependencies (bool): if true, exclude results that are only related to dependencies generate_patches (bool): if true, patches are generated (json output only) truffle_ignore (bool): ignore truffle.js presence (default false) truffle_build_directory (str): build truffle directory (default 'build/contracts') truffle_ignore_compile (bool): do not run truffle compile (default False) truffle_version (str): use a specific truffle version (default None) embark_ignore (bool): ignore embark.js presence (default false) embark_ignore_compile (bool): do not run embark build (default False) embark_overwrite_config (bool): overwrite original config file (default false) """ super().__init__() self._disallow_partial: bool = kwargs.get("disallow_partial", False) self._skip_assembly: bool = kwargs.get("skip_assembly", False) self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False) self._parsers: List[SlitherCompilationUnitSolc] = [] try: if isinstance(target, CryticCompile): crytic_compile = target else: crytic_compile = CryticCompile(target, **kwargs) self._crytic_compile = crytic_compile except InvalidCompilation as e: # pylint: disable=raise-missing-from raise SlitherError(f"Invalid compilation: \n{str(e)}") for compilation_unit in crytic_compile.compilation_units.values(): compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit) self._compilation_units.append(compilation_unit_slither) parser = SlitherCompilationUnitSolc(compilation_unit_slither) self._parsers.append(parser) for path, ast in compilation_unit.asts.items(): parser.parse_top_level_from_loaded_json(ast, path) self.add_source_code(path) _update_file_scopes(compilation_unit_slither.scopes.values()) if kwargs.get("generate_patches", False): self.generate_patches = True self._markdown_root = kwargs.get("markdown_root", "") self._detectors = [] self._printers = [] filter_paths = kwargs.get("filter_paths", []) for p in filter_paths: self.add_path_to_filter(p) self._exclude_dependencies = kwargs.get("exclude_dependencies", False) triage_mode = kwargs.get("triage_mode", False) self._triage_mode = triage_mode for parser in self._parsers: parser.parse_contracts() # skip_analyze is only used for testing if not kwargs.get("skip_analyze", False): for parser in self._parsers: parser.analyze_contracts()
def __init__(self, target, **kwargs): """ Args: target (str | list(json) | CryticCompile) Keyword Args: solc (str): solc binary location (default 'solc') disable_solc_warnings (bool): True to disable solc warnings (default false) solc_arguments (str): solc arguments (default '') ast_format (str): ast format (default '--ast-compact-json') filter_paths (list(str)): list of path to filter (default []) triage_mode (bool): if true, switch to triage mode (default false) exclude_dependencies (bool): if true, exclude results that are only related to dependencies generate_patches (bool): if true, patches are generated (json output only) truffle_ignore (bool): ignore truffle.js presence (default false) truffle_build_directory (str): build truffle directory (default 'build/contracts') truffle_ignore_compile (bool): do not run truffle compile (default False) truffle_version (str): use a specific truffle version (default None) embark_ignore (bool): ignore embark.js presence (default false) embark_ignore_compile (bool): do not run embark build (default False) embark_overwrite_config (bool): overwrite original config file (default false) """ super().__init__() self._parser: SlitherSolc # This could be another parser, like SlitherVyper, interface needs to be determined self._disallow_partial: bool = kwargs.get("disallow_partial", False) self._skip_assembly: bool = kwargs.get("skip_assembly", False) self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False) # list of files provided (see --splitted option) if isinstance(target, list): self._init_from_list(target) elif isinstance(target, str) and target.endswith(".json"): self._init_from_raw_json(target) else: self._parser = SlitherSolc("", self) try: if isinstance(target, CryticCompile): crytic_compile = target else: crytic_compile = CryticCompile(target, **kwargs) self._crytic_compile = crytic_compile except InvalidCompilation as e: # pylint: disable=raise-missing-from raise SlitherError(f"Invalid compilation: \n{str(e)}") for path, ast in crytic_compile.asts.items(): self._parser.parse_top_level_from_loaded_json(ast, path) self.add_source_code(path) if kwargs.get("generate_patches", False): self.generate_patches = True self._markdown_root = kwargs.get("markdown_root", "") self._detectors = [] self._printers = [] filter_paths = kwargs.get("filter_paths", []) for p in filter_paths: self.add_path_to_filter(p) self._exclude_dependencies = kwargs.get("exclude_dependencies", False) triage_mode = kwargs.get("triage_mode", False) self._triage_mode = triage_mode self._parser.parse_contracts() # skip_analyze is only used for testing if not kwargs.get("skip_analyze", False): self._parser.analyze_contracts()
def parse_type( t: Union[Dict, UnknownType], caller_context: Union[CallerContextExpression, "SlitherCompilationUnitSolc"], ) -> Type: """ caller_context can be a SlitherCompilationUnitSolc because we recursively call the function and go up in the context's scope. If we are really lost we just go over the SlitherCompilationUnitSolc :param t: :type t: :param caller_context: :type caller_context: :return: :rtype: """ # local import to avoid circular dependency # pylint: disable=too-many-locals,too-many-branches,too-many-statements # pylint: disable=import-outside-toplevel from slither.solc_parsing.expressions.expression_parsing import parse_expression 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.declarations.custom_error import CustomErrorSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc sl: "SlitherCompilationUnit" renaming: Dict[str, str] user_defined_types: Dict[str, TypeAlias] # Note: for convenicence top level functions use the same parser than function in contract # but contract_parser is set to None if isinstance(caller_context, SlitherCompilationUnitSolc) or ( isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None): structures_direct_access: List["Structure"] if isinstance(caller_context, SlitherCompilationUnitSolc): sl = caller_context.compilation_unit next_context = caller_context renaming = {} user_defined_types = {} else: assert isinstance(caller_context, FunctionSolc) sl = caller_context.underlying_function.compilation_unit next_context = caller_context.slither_parser renaming = caller_context.underlying_function.file_scope.renaming user_defined_types = caller_context.underlying_function.file_scope.user_defined_types structures_direct_access = sl.structures_top_level all_structuress = [c.structures for c in sl.contracts] all_structures = [ item for sublist in all_structuress for item in sublist ] all_structures += structures_direct_access enums_direct_access = sl.enums_top_level all_enumss = [c.enums for c in sl.contracts] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += enums_direct_access contracts = sl.contracts functions = [] elif isinstance( caller_context, (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc)): if isinstance(caller_context, StructureTopLevelSolc): scope = caller_context.underlying_structure.file_scope elif isinstance(caller_context, TopLevelVariableSolc): scope = caller_context.underlying_variable.file_scope else: assert isinstance(caller_context, CustomErrorSolc) custom_error = caller_context.underlying_custom_error if isinstance(custom_error, CustomErrorTopLevel): scope = custom_error.file_scope else: assert isinstance(custom_error, CustomErrorContract) scope = custom_error.contract.file_scope next_context = caller_context.slither_parser structures_direct_access = list(scope.structures.values()) all_structuress = [c.structures for c in scope.contracts.values()] all_structures = [ item for sublist in all_structuress for item in sublist ] all_structures += structures_direct_access enums_direct_access = [] all_enums = scope.enums.values() contracts = scope.contracts.values() functions = list(scope.functions) renaming = scope.renaming user_defined_types = scope.user_defined_types elif isinstance(caller_context, (ContractSolc, FunctionSolc)): if isinstance(caller_context, FunctionSolc): underlying_func = caller_context.underlying_function # If contract_parser is set to None, then underlying_function is a functionContract # See note above assert isinstance(underlying_func, FunctionContract) contract = underlying_func.contract next_context = caller_context.contract_parser scope = caller_context.underlying_function.file_scope else: contract = caller_context.underlying_contract next_context = caller_context scope = caller_context.underlying_contract.file_scope structures_direct_access = contract.structures structures_direct_access += contract.file_scope.structures.values() all_structuress = [ c.structures for c in contract.file_scope.contracts.values() ] all_structures = [ item for sublist in all_structuress for item in sublist ] all_structures += contract.file_scope.structures.values() enums_direct_access: List["Enum"] = contract.enums enums_direct_access += contract.file_scope.enums.values() all_enumss = [c.enums for c in contract.file_scope.contracts.values()] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += contract.file_scope.enums.values() contracts = contract.file_scope.contracts.values() functions = contract.functions + contract.modifiers renaming = scope.renaming user_defined_types = scope.user_defined_types 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" if isinstance(t, UnknownType): name = t.name if name in renaming: name = renaming[name] if name in user_defined_types: return user_defined_types[name] return _find_from_type_name( name, functions, contracts, structures_direct_access, all_structures, enums_direct_access, all_enums, ) if t[key] == "ElementaryTypeName": if is_compact_ast: return ElementaryType(t["name"]) return ElementaryType(t["attributes"][key]) if t[key] == "UserDefinedTypeName": if is_compact_ast: name = t["typeDescriptions"]["typeString"] if name in renaming: name = renaming[name] if name in user_defined_types: return user_defined_types[name] return _find_from_type_name( name, 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 name = t["attributes"][type_name_key] if name in renaming: name = renaming[name] if name in user_defined_types: return user_defined_types[name] return _find_from_type_name( name, functions, contracts, structures_direct_access, all_structures, enums_direct_access, all_enums, ) # Introduced with Solidity 0.8 if t[key] == "IdentifierPath": if is_compact_ast: name = t["name"] if name in renaming: name = renaming[name] if name in user_defined_types: return user_defined_types[name] return _find_from_type_name( name, functions, contracts, structures_direct_access, all_structures, enums_direct_access, all_enums, ) raise SlitherError("Solidity 0.8 not supported with the legacy AST") if t[key] == "ArrayTypeName": length = None if is_compact_ast: if t.get("length", None): length = parse_expression(t["length"], caller_context) array_type = parse_type(t["baseType"], next_context) else: if len(t["children"]) == 2: length = parse_expression(t["children"][1], caller_context) else: assert len(t["children"]) == 1 array_type = parse_type(t["children"][0], next_context) return ArrayType(array_type, length) if t[key] == "Mapping": if is_compact_ast: mappingFrom = parse_type(t["keyType"], next_context) mappingTo = parse_type(t["valueType"], next_context) else: assert len(t["children"]) == 2 mappingFrom = parse_type(t["children"][0], next_context) mappingTo = parse_type(t["children"][1], next_context) return MappingType(mappingFrom, mappingTo) if t[key] == "FunctionTypeName": if is_compact_ast: params = t["parameterTypes"] return_values = t["returnParameterTypes"] index = "parameters" else: assert len(t["children"]) == 2 params = t["children"][0] return_values = t["children"][1] index = "children" assert params[key] == "ParameterList" assert return_values[key] == "ParameterList" params_vars: List[FunctionTypeVariable] = [] return_values_vars: List[FunctionTypeVariable] = [] for p in params[index]: var = FunctionTypeVariable() var.set_offset(p["src"], caller_context.compilation_unit) var_parser = FunctionTypeVariableSolc(var, p) var_parser.analyze(caller_context) params_vars.append(var) for p in return_values[index]: var = FunctionTypeVariable() var.set_offset(p["src"], caller_context.compilation_unit) var_parser = FunctionTypeVariableSolc(var, p) var_parser.analyze(caller_context) return_values_vars.append(var) return FunctionType(params_vars, return_values_vars) raise ParsingError("Type name not found " + str(t))
def parse_type(t: Union[Dict, UnknownType], caller_context): # local import to avoid circular dependency # pylint: disable=too-many-locals,too-many-branches,too-many-statements # pylint: disable=import-outside-toplevel from slither.solc_parsing.expressions.expression_parsing import parse_expression 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.slither_compilation_unit_solc import SlitherCompilationUnitSolc sl: "SlitherCompilationUnit" # Note: for convenicence top level functions use the same parser than function in contract # but contract_parser is set to None if isinstance(caller_context, SlitherCompilationUnitSolc) or ( isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None): if isinstance(caller_context, SlitherCompilationUnitSolc): sl = caller_context.compilation_unit next_context = caller_context else: assert isinstance(caller_context, FunctionSolc) sl = caller_context.underlying_function.compilation_unit next_context = caller_context.slither_parser structures_direct_access = sl.structures_top_level all_structuress = [c.structures for c in sl.contracts] all_structures = [ item for sublist in all_structuress for item in sublist ] all_structures += structures_direct_access enums_direct_access = sl.enums_top_level all_enumss = [c.enums for c in sl.contracts] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += enums_direct_access contracts = sl.contracts functions = [] elif isinstance(caller_context, (ContractSolc, FunctionSolc)): if isinstance(caller_context, FunctionSolc): underlying_func = caller_context.underlying_function # If contract_parser is set to None, then underlying_function is a functionContract # See note above assert isinstance(underlying_func, FunctionContract) contract = underlying_func.contract next_context = caller_context.contract_parser else: contract = caller_context.underlying_contract next_context = caller_context structures_direct_access = ( contract.structures + contract.compilation_unit.structures_top_level) all_structuress = [ c.structures for c in contract.compilation_unit.contracts ] all_structures = [ item for sublist in all_structuress for item in sublist ] all_structures += contract.compilation_unit.structures_top_level enums_direct_access = contract.enums + contract.compilation_unit.enums_top_level all_enumss = [c.enums for c in contract.compilation_unit.contracts] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += contract.compilation_unit.enums_top_level contracts = contract.compilation_unit.contracts functions = contract.functions + contract.modifiers 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" if isinstance(t, UnknownType): 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: return ElementaryType(t["name"]) return ElementaryType(t["attributes"][key]) if t[key] == "UserDefinedTypeName": if is_compact_ast: return _find_from_type_name( 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], functions, contracts, structures_direct_access, all_structures, enums_direct_access, all_enums, ) # Introduced with Solidity 0.8 if t[key] == "IdentifierPath": if is_compact_ast: return _find_from_type_name( t["name"], functions, contracts, structures_direct_access, all_structures, enums_direct_access, all_enums, ) raise SlitherError("Solidity 0.8 not supported with the legacy AST") if t[key] == "ArrayTypeName": length = None if is_compact_ast: if t.get("length", None): length = parse_expression(t["length"], caller_context) array_type = parse_type(t["baseType"], next_context) else: if len(t["children"]) == 2: length = parse_expression(t["children"][1], caller_context) else: assert len(t["children"]) == 1 array_type = parse_type(t["children"][0], next_context) return ArrayType(array_type, length) if t[key] == "Mapping": if is_compact_ast: mappingFrom = parse_type(t["keyType"], next_context) mappingTo = parse_type(t["valueType"], next_context) else: assert len(t["children"]) == 2 mappingFrom = parse_type(t["children"][0], next_context) mappingTo = parse_type(t["children"][1], next_context) return MappingType(mappingFrom, mappingTo) if t[key] == "FunctionTypeName": if is_compact_ast: params = t["parameterTypes"] return_values = t["returnParameterTypes"] index = "parameters" else: assert len(t["children"]) == 2 params = t["children"][0] return_values = t["children"][1] index = "children" assert params[key] == "ParameterList" assert return_values[key] == "ParameterList" params_vars: List[FunctionTypeVariable] = [] return_values_vars: List[FunctionTypeVariable] = [] for p in params[index]: var = FunctionTypeVariable() var.set_offset(p["src"], caller_context.compilation_unit) var_parser = FunctionTypeVariableSolc(var, p) var_parser.analyze(caller_context) params_vars.append(var) for p in return_values[index]: var = FunctionTypeVariable() var.set_offset(p["src"], caller_context.compilation_unit) var_parser = FunctionTypeVariableSolc(var, p) var_parser.analyze(caller_context) return_values_vars.append(var) return FunctionType(params_vars, return_values_vars) raise ParsingError("Type name not found " + str(t))