def __init__(self, val, constant_type=None, subdenomination=None): # pylint: disable=too-many-branches super().__init__() assert isinstance(val, str) self._original_value = val self._subdenomination = subdenomination if subdenomination: val = str(convert_subdenomination(val, subdenomination)) if constant_type: # pylint: disable=too-many-nested-blocks assert isinstance(constant_type, ElementaryType) self._type = constant_type if constant_type.type in Int + Uint + ["address"]: self._val = convert_string_to_int(val) elif constant_type.type == "bool": self._val = (val == "true") | (val == "True") else: self._val = val else: if val.isdigit(): self._type = ElementaryType("uint256") self._val = int(Decimal(val)) else: self._type = ElementaryType("string") self._val = val
def __init__(self, val, type=None): super(Constant, self).__init__() assert isinstance(val, str) self._original_value = val if type: assert isinstance(type, ElementaryType) self._type = type if type.type in Int + Uint + ['address']: if val.startswith('0x') or val.startswith('0X'): self._val = int(val, 16) else: if 'e' in val: base, expo = val.split('e') self._val = int(float(base) * (10**int(expo))) elif 'E' in val: base, expo = val.split('E') self._val = int(float(base) * (10**int(expo))) else: self._val = int(float(val)) elif type.type == 'bool': self._val = val == 'true' else: self._val = val else: if val.isdigit(): self._type = ElementaryType('uint256') self._val = int(val) else: self._type = ElementaryType('string') self._val = val
def __init__(self, val): super(Constant, self).__init__() assert isinstance(val, str) if val.isdigit(): self._type = ElementaryType('uint256') self._val = int(val) else: self._type = ElementaryType('string') self._val = val
def _detect_missing_zero_address_validation(self, contract): """ Detects if addresses are zero address validated before use. :param contract: The contract to check :return: Functions with nodes where addresses used are not zero address validated earlier """ results = [] for function in contract.functions_entry_points: var_nodes = defaultdict(list) for node in function.nodes: sv_addrs_written = [ sv for sv in node.state_variables_written if sv.type == ElementaryType("address") ] addr_calls = False for ir in node.irs: if isinstance(ir, (Send, Transfer, LowLevelCall)): addr_calls = True # Continue if no address-typed state variables are written and if no send/transfer/call if not sv_addrs_written and not addr_calls: continue # Check local variables used in such nodes for var in node.local_variables_read: # Check for address types that are tainted but not by msg.sender if var.type == ElementaryType("address") and is_tainted( var, function, ignore_generic_taint=True ): # Check for zero address validation of variable # in the context of modifiers used or prior function context if not ( self._zero_address_validation_in_modifier( var, function.modifiers_statements ) or self._zero_address_validation(var, node, []) ): # Report a variable only once per function var_nodes[var].append(node) if var_nodes: results.append((function, var_nodes)) return results
def _init_from_declaration(self, var, init): if self._is_compact_ast: attributes = var self._typeName = attributes['typeDescriptions']['typeString'] else: assert len(var['children']) <= 2 assert var['name'] == 'VariableDeclaration' attributes = var['attributes'] self._typeName = attributes['type'] self._name = attributes['name'] self._arrayDepth = 0 self._isMapping = False self._mappingFrom = None self._mappingTo = False self._initial_expression = None self._type = None if 'constant' in attributes: self._is_constant = attributes['constant'] self._analyze_variable_attributes(attributes) if self._is_compact_ast: if var['typeName']: self._elem_to_parse = var['typeName'] else: self._elem_to_parse = UnknownType( var['typeDescriptions']['typeString']) else: if not var['children']: # It happens on variable declared inside loop declaration try: self._type = ElementaryType(self._typeName) self._elem_to_parse = None except NonElementaryType: self._elem_to_parse = UnknownType(self._typeName) else: self._elem_to_parse = var['children'][0] if self._is_compact_ast: self._initializedNotParsed = init if init: self._initialized = True else: if init: # there are two way to init a var local in the AST assert len(var['children']) <= 1 self._initialized = True self._initializedNotParsed = init elif len(var['children']) in [0, 1]: self._initialized = False self._initializedNotParsed = [] else: assert len(var['children']) == 2 self._initialized = True self._initializedNotParsed = var['children'][1]
def __init__(self, variable: Slither_Local_Variable): super().__init__(variable) self._name = variable.name if isinstance(variable.type, UserDefinedType): if isinstance(variable.type.type, ContractSolc04): self._type = ElementaryType('address') else: self._type = variable.type else: self._type = variable.type
def __init__(self, val, type=None, subdenomination=None): super(Constant, self).__init__() assert isinstance(val, str) self._original_value = val self._subdenomination = subdenomination if subdenomination: val = str(convert_subdenomination(val, subdenomination)) if type: assert isinstance(type, ElementaryType) self._type = type if type.type in Int + Uint + ['address']: if val.startswith('0x') or val.startswith('0X'): self._val = int(val, 16) else: if 'e' in val: base, expo = val.split('e') self._val = int(Decimal(base) * (10**int(expo))) elif 'E' in val: base, expo = val.split('E') self._val = int(Decimal(base) * (10**int(expo))) else: self._val = int(Decimal(val)) elif type.type == 'bool': self._val = (val == 'true') | (val == 'True') else: self._val = val else: if val.isdigit(): self._type = ElementaryType('uint256') self._val = int(Decimal(val)) else: self._type = ElementaryType('string') self._val = val
def _detect_missing_events(contract): """ Detects if critical contract parameters set by owners and used in access control are missing events :param contract: The contract to check :return: Functions with nodes of critical operations but no events """ results = [] # pylint: disable=too-many-nested-blocks for function in contract.functions_entry_points: nodes = [] # Check for any events in the function and skip if found # Note: not checking if event corresponds to critical parameter if any(ir for node in function.nodes for ir in node.irs if isinstance(ir, EventCall)): continue # Ignore constructors and private/internal functions # Heuristic-1: functions with critical operations are typically "protected". Skip unprotected functions. if function.is_constructor or not function.is_protected(): continue # Heuristic-2: Critical operations are where state variables are written and tainted # Heuristic-3: Variables of interest are address type that are used in modifiers i.e. access control # Heuristic-4: Critical operations present but no events in the function is not a good practice for node in function.nodes: for sv in node.state_variables_written: if is_tainted( sv, function) and sv.type == ElementaryType("address"): for mod in function.contract.modifiers: if sv in mod.state_variables_read: nodes.append((node, sv, mod)) if nodes: results.append((function, nodes)) return results
def _find_from_type_name(name, contract, contracts, structures, enums): name_elementary = name.split(' ')[0] if '[' in name_elementary: name_elementary = name_elementary[0:name_elementary.find('[')] if name_elementary in ElementaryTypeName: depth = name.count('[') if depth: return ArrayType(ElementaryType(name_elementary), Literal(depth, 'uint256')) else: return ElementaryType(name_elementary) # We first look for contract # To avoid collision # Ex: a structure with the name of a contract name_contract = name if name_contract.startswith('contract '): name_contract = name_contract[len('contract '):] if name_contract.startswith('library '): name_contract = name_contract[len('library '):] var_type = next((c for c in contracts if c.name == name_contract), None) if not var_type: var_type = next((st for st in structures if st.name == name), None) if not var_type: var_type = next((e for e in enums if e.name == name), None) if not var_type: # any contract can refer to another contract's enum enum_name = name if enum_name.startswith('enum '): enum_name = enum_name[len('enum '):] all_enums = [c.enums for c in contracts] all_enums = [item for sublist in all_enums for item in sublist] var_type = next((e for e in all_enums if e.name == enum_name), None) if not var_type: var_type = next( (e for e in all_enums if e.canonical_name == enum_name), None) if not var_type: # any contract can refer to another contract's structure name_struct = name if name_struct.startswith('struct '): name_struct = name_struct[len('struct '):] name_struct = name_struct.split(' ')[ 0] # remove stuff like storage pointer at the end all_structures = [c.structures for c in contracts] all_structures = [ item for sublist in all_structures for item in sublist ] var_type = next( (st for st in all_structures if st.name == name_struct), None) if not var_type: var_type = next( (st for st in all_structures if st.canonical_name == name_struct), None) # case where struct xxx.xx[] where not well formed in the AST if not var_type: depth = 0 while name_struct.endswith('[]'): name_struct = name_struct[0:-2] depth += 1 var_type = next( (st for st in all_structures if st.canonical_name == name_struct), None) if var_type: return ArrayType(UserDefinedType(var_type), Literal(depth, 'uint256')) if not var_type: var_type = next((f for f in contract.functions if f.name == name), None) if not var_type: if name.startswith('function '): found = re.findall( 'function \(([ ()a-zA-Z0-9\.,]*)\) returns \(([a-zA-Z0-9\.,]*)\)', name) assert len(found) == 1 params = found[0][0].split(',') return_values = found[0][1].split(',') params = [ _find_from_type_name(p, contract, contracts, structures, enums) for p in params ] return_values = [ _find_from_type_name(r, contract, contracts, structures, enums) for r in return_values ] params_vars = [] return_vars = [] for p in params: var = FunctionTypeVariable() var.set_type(p) params_vars.append(var) for r in return_values: var = FunctionTypeVariable() var.set_type(r) return_vars.append(var) return FunctionType(params_vars, return_vars) if not var_type: if name.startswith('mapping('): # nested mapping declared with var if name.count('mapping(') == 1: found = re.findall( 'mapping\(([a-zA-Z0-9\.]*) => ([a-zA-Z0-9\.\[\]]*)\)', name) else: found = re.findall( 'mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)', name) assert len(found) == 1 from_ = found[0][0] to_ = found[0][1] from_type = _find_from_type_name(from_, contract, contracts, structures, enums) to_type = _find_from_type_name(to_, contract, contracts, structures, enums) return MappingType(from_type, to_type) if not var_type: raise ParsingError('Type not found ' + str(name)) return UserDefinedType(var_type)
def parse_type(t, caller_context): # local import to avoid circular dependency from slither.solc_parsing.expressions.expression_parsing import parse_expression from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc if isinstance(caller_context, Contract): contract = caller_context elif isinstance(caller_context, Function): contract = caller_context.contract else: raise ParsingError('Incorrect caller context') is_compact_ast = caller_context.is_compact_ast if is_compact_ast: key = 'nodeType' else: key = 'name' structures = contract.structures enums = contract.enums contracts = contract.slither.contracts if isinstance(t, UnknownType): return _find_from_type_name(t.name, contract, contracts, structures, enums) elif t[key] == 'ElementaryTypeName': if is_compact_ast: return ElementaryType(t['name']) return ElementaryType(t['attributes'][key]) elif t[key] == 'UserDefinedTypeName': if is_compact_ast: return _find_from_type_name(t['typeDescriptions']['typeString'], contract, contracts, structures, 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], contract, contracts, structures, enums) elif t[key] == 'ArrayTypeName': length = None if is_compact_ast: if t['length']: length = parse_expression(t['length'], caller_context) array_type = parse_type(t['baseType'], contract) 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], contract) return ArrayType(array_type, length) elif t[key] == 'Mapping': if is_compact_ast: mappingFrom = parse_type(t['keyType'], contract) mappingTo = parse_type(t['valueType'], contract) else: assert len(t['children']) == 2 mappingFrom = parse_type(t['children'][0], contract) mappingTo = parse_type(t['children'][1], contract) return MappingType(mappingFrom, mappingTo) elif 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 = [] return_values_vars = [] for p in params[index]: var = FunctionTypeVariableSolc(p) var.set_offset(p['src'], caller_context.slither) var.analyze(caller_context) params_vars.append(var) for p in return_values[index]: var = FunctionTypeVariableSolc(p) var.set_offset(p['src'], caller_context.slither) var.analyze(caller_context) return_values_vars.append(var) return FunctionType(params_vars, return_values_vars) raise ParsingError('Type name not found ' + str(t))
def _init_from_declaration(self, var: Dict, init: bool): # pylint: disable=too-many-branches if self._is_compact_ast: attributes = var self._typeName = attributes["typeDescriptions"]["typeString"] else: assert len(var["children"]) <= 2 assert var["name"] == "VariableDeclaration" attributes = var["attributes"] self._typeName = attributes["type"] self._variable.name = attributes["name"] # self._arrayDepth = 0 # self._isMapping = False # self._mappingFrom = None # self._mappingTo = False # self._initial_expression = None # self._type = None # Only for comapct ast format # the id can be used later if referencedDeclaration # is provided if "id" in var: self._reference_id = var["id"] if "constant" in attributes: self._variable.is_constant = attributes["constant"] self._analyze_variable_attributes(attributes) if self._is_compact_ast: if var["typeName"]: self._elem_to_parse = var["typeName"] else: self._elem_to_parse = UnknownType( var["typeDescriptions"]["typeString"]) else: if not var["children"]: # It happens on variable declared inside loop declaration try: self._variable.type = ElementaryType(self._typeName) self._elem_to_parse = None except NonElementaryType: self._elem_to_parse = UnknownType(self._typeName) else: self._elem_to_parse = var["children"][0] if self._is_compact_ast: self._initializedNotParsed = init if init: self._variable.initialized = True else: if init: # there are two way to init a var local in the AST assert len(var["children"]) <= 1 self._variable.initialized = True self._initializedNotParsed = init elif len(var["children"]) in [0, 1]: self._variable.initialized = False self._initializedNotParsed = [] else: assert len(var["children"]) == 2 self._variable.initialized = True self._initializedNotParsed = var["children"][1]
def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,too-many-statements,too-many-arguments name: str, functions_direct_access: List["Function"], contracts_direct_access: List["Contract"], structures_direct_access: List["Structure"], all_structures: List["Structure"], enums_direct_access: List["Enum"], all_enums: List["Enum"], ) -> Type: name_elementary = name.split(" ")[0] if "[" in name_elementary: name_elementary = name_elementary[0 : name_elementary.find("[")] if name_elementary in ElementaryTypeName: depth = name.count("[") if depth: return ArrayType(ElementaryType(name_elementary), Literal(depth, "uint256")) return ElementaryType(name_elementary) # We first look for contract # To avoid collision # Ex: a structure with the name of a contract name_contract = name if name_contract.startswith("contract "): name_contract = name_contract[len("contract ") :] if name_contract.startswith("library "): name_contract = name_contract[len("library ") :] var_type = next((c for c in contracts_direct_access if c.name == name_contract), None) if not var_type: var_type = next((st for st in structures_direct_access if st.name == name), None) if not var_type: var_type = next((e for e in enums_direct_access if e.name == name), None) if not var_type: # any contract can refer to another contract's enum enum_name = name if enum_name.startswith("enum "): enum_name = enum_name[len("enum ") :] elif enum_name.startswith("type(enum"): enum_name = enum_name[len("type(enum ") : -1] # all_enums = [c.enums for c in contracts] # all_enums = [item for sublist in all_enums for item in sublist] # all_enums += contract.slither.enums_top_level var_type = next((e for e in all_enums if e.name == enum_name), None) if not var_type: var_type = next((e for e in all_enums if e.canonical_name == enum_name), None) if not var_type: # any contract can refer to another contract's structure name_struct = name if name_struct.startswith("struct "): name_struct = name_struct[len("struct ") :] name_struct = name_struct.split(" ")[0] # remove stuff like storage pointer at the end # all_structures = [c.structures for c in contracts] # all_structures = [item for sublist in all_structures for item in sublist] # all_structures += contract.slither.structures_top_level var_type = next((st for st in all_structures if st.name == name_struct), None) if not var_type: var_type = next((st for st in all_structures if st.canonical_name == name_struct), None) # case where struct xxx.xx[] where not well formed in the AST if not var_type: depth = 0 while name_struct.endswith("[]"): name_struct = name_struct[0:-2] depth += 1 var_type = next((st for st in all_structures if st.canonical_name == name_struct), None) if var_type: return ArrayType(UserDefinedType(var_type), Literal(depth, "uint256")) if not var_type: var_type = next((f for f in functions_direct_access if f.name == name), None) if not var_type: if name.startswith("function "): found = re.findall( "function \(([ ()\[\]a-zA-Z0-9\.,]*?)\)(?: payable)?(?: (?:external|internal|pure|view))?(?: returns \(([a-zA-Z0-9() \.,]*)\))?", name, ) assert len(found) == 1 params = [v for v in found[0][0].split(",") if v != ""] return_values = ( [v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else [] ) params = [ _find_from_type_name( p, functions_direct_access, contracts_direct_access, structures_direct_access, all_structures, enums_direct_access, all_enums, ) for p in params ] return_values = [ _find_from_type_name( r, functions_direct_access, contracts_direct_access, structures_direct_access, all_structures, enums_direct_access, all_enums, ) for r in return_values ] params_vars = [] return_vars = [] for p in params: var = FunctionTypeVariable() var.set_type(p) params_vars.append(var) for r in return_values: var = FunctionTypeVariable() var.set_type(r) return_vars.append(var) return FunctionType(params_vars, return_vars) if not var_type: if name.startswith("mapping("): # nested mapping declared with var if name.count("mapping(") == 1: found = re.findall("mapping\(([a-zA-Z0-9\.]*) => ([ a-zA-Z0-9\.\[\]]*)\)", name) else: found = re.findall( "mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)", name, ) assert len(found) == 1 from_ = found[0][0] to_ = found[0][1] from_type = _find_from_type_name( from_, functions_direct_access, contracts_direct_access, structures_direct_access, all_structures, enums_direct_access, all_enums, ) to_type = _find_from_type_name( to_, functions_direct_access, contracts_direct_access, structures_direct_access, all_structures, enums_direct_access, all_enums, ) return MappingType(from_type, to_type) if not var_type: raise ParsingError("Type not found " + str(name)) return UserDefinedType(var_type)
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.slitherSolc import SlitherSolc # 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, SlitherSolc) or ( isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None ): if isinstance(caller_context, SlitherSolc): sl = caller_context.core next_context = caller_context else: assert isinstance(caller_context, FunctionSolc) sl = caller_context.underlying_function.slither 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.slither.structures_top_level all_structuress = [c.structures for c in contract.slither.contracts] all_structures = [item for sublist in all_structuress for item in sublist] all_structures += contract.slither.structures_top_level enums_direct_access = contract.enums + contract.slither.enums_top_level all_enumss = [c.enums for c in contract.slither.contracts] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += contract.slither.enums_top_level contracts = contract.slither.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, ) 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.slither) 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.slither) 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 set_type(self, t): if isinstance(t, str): t = ElementaryType(t) assert isinstance(t, (Type, list)) or t is None self._type = t
def test_functions(): # pylint: disable=too-many-statements slither = Slither("tests/test_function.sol") compilation_unit = slither.compilation_units[0] functions = compilation_unit.get_contract_from_name( "TestFunction")[0].available_functions_as_dict() f = functions["external_payable(uint256)"] assert f.name == "external_payable" assert f.full_name == "external_payable(uint256)" assert f.canonical_name == "TestFunction.external_payable(uint256)" assert f.solidity_signature == "external_payable(uint256)" assert f.signature_str == "external_payable(uint256) returns(uint256)" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is True assert f.visibility == "external" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters[0].name == "_a" assert f.parameters[0].type == ElementaryType("uint256") assert f.return_type[0] == ElementaryType("uint256") f = functions["public_reenter()"] assert f.name == "public_reenter" assert f.full_name == "public_reenter()" assert f.canonical_name == "TestFunction.public_reenter()" assert f.solidity_signature == "public_reenter()" assert f.signature_str == "public_reenter() returns()" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is True assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is False assert f.visibility == "public" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters == [] assert f.return_type is None f = functions["public_payable_reenter_send(bool)"] assert f.name == "public_payable_reenter_send" assert f.full_name == "public_payable_reenter_send(bool)" assert f.canonical_name == "TestFunction.public_payable_reenter_send(bool)" assert f.solidity_signature == "public_payable_reenter_send(bool)" assert f.signature_str == "public_payable_reenter_send(bool) returns()" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is True assert f.can_send_eth() is True assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is True assert f.visibility == "public" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters[0].name == "_b" assert f.parameters[0].type == ElementaryType("bool") assert f.return_type is None f = functions["external_send(uint8)"] assert f.name == "external_send" assert f.full_name == "external_send(uint8)" assert f.canonical_name == "TestFunction.external_send(uint8)" assert f.solidity_signature == "external_send(uint8)" assert f.signature_str == "external_send(uint8) returns()" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is True assert f.can_send_eth() is True assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is False assert f.visibility == "external" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters[0].name == "_c" assert f.parameters[0].type == ElementaryType("uint8") assert f.return_type is None f = functions["internal_assembly(bytes)"] assert f.name == "internal_assembly" assert f.full_name == "internal_assembly(bytes)" assert f.canonical_name == "TestFunction.internal_assembly(bytes)" assert f.solidity_signature == "internal_assembly(bytes)" assert f.signature_str == "internal_assembly(bytes) returns(uint256)" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is True assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is False assert f.visibility == "internal" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters[0].name == "_d" assert f.parameters[0].type == ElementaryType("bytes") assert f.return_type[0] == ElementaryType("uint256") f = functions["fallback()"] assert f.name == "fallback" assert f.full_name == "fallback()" assert f.canonical_name == "TestFunction.fallback()" assert f.solidity_signature == "fallback()" assert f.signature_str == "fallback() returns()" assert f.function_type == FunctionType.FALLBACK assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is True assert f.is_receive is False assert f.payable is False assert f.visibility == "external" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is True assert f.parameters == [] assert f.return_type is None f = functions["receive()"] assert f.name == "receive" assert f.full_name == "receive()" assert f.canonical_name == "TestFunction.receive()" assert f.solidity_signature == "receive()" assert f.signature_str == "receive() returns()" assert f.function_type == FunctionType.RECEIVE assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is True assert f.payable is True assert f.visibility == "external" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is True assert f.parameters == [] assert f.return_type is None f = functions["constructor(address)"] assert f.name == "constructor" assert f.full_name == "constructor(address)" assert f.canonical_name == "TestFunction.constructor(address)" assert f.solidity_signature == "constructor(address)" assert f.signature_str == "constructor(address) returns()" assert f.function_type == FunctionType.CONSTRUCTOR assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor assert f.is_fallback is False assert f.is_receive is False assert f.payable is True assert f.visibility == "public" assert f.view is False assert f.pure is False assert f.is_implemented is True assert f.is_empty is True assert f.parameters[0].name == "_e" assert f.parameters[0].type == ElementaryType("address") assert f.return_type is None f = functions["private_view()"] assert f.name == "private_view" assert f.full_name == "private_view()" assert f.canonical_name == "TestFunction.private_view()" assert f.solidity_signature == "private_view()" assert f.signature_str == "private_view() returns(bool)" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is False assert f.visibility == "private" assert f.view is True assert f.pure is False assert f.is_implemented is True assert f.is_empty is False assert f.parameters == [] assert f.return_type[0] == ElementaryType("bool") f = functions["public_pure()"] assert f.name == "public_pure" assert f.full_name == "public_pure()" assert f.canonical_name == "TestFunction.public_pure()" assert f.solidity_signature == "public_pure()" assert f.signature_str == "public_pure() returns(bool)" assert f.function_type == FunctionType.NORMAL assert f.contains_assembly is False assert f.can_reenter() is False assert f.can_send_eth() is False assert f.is_constructor is False assert f.is_fallback is False assert f.is_receive is False assert f.payable is False assert f.visibility == "public" assert f.view is True assert f.pure is True assert f.is_implemented is True assert f.is_empty is False assert f.parameters == [] assert f.return_type[0] == ElementaryType("bool")
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 if isinstance(caller_context, ContractSolc): contract = caller_context.underlying_contract contract_parser = caller_context is_compact_ast = caller_context.is_compact_ast elif isinstance(caller_context, FunctionSolc): contract = caller_context.underlying_function.contract contract_parser = caller_context.contract_parser is_compact_ast = caller_context.is_compact_ast else: raise ParsingError(f"Incorrect caller context: {type(caller_context)}") if is_compact_ast: key = "nodeType" else: key = "name" structures = contract.structures + contract.slither.top_level_structures enums = contract.enums + contract.slither.top_level_enums contracts = contract.slither.contracts if isinstance(t, UnknownType): return _find_from_type_name(t.name, contract, contracts, structures, 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"], contract, contracts, structures, 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], contract, contracts, structures, enums ) 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"], contract_parser) 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], contract_parser) return ArrayType(array_type, length) if t[key] == "Mapping": if is_compact_ast: mappingFrom = parse_type(t["keyType"], contract_parser) mappingTo = parse_type(t["valueType"], contract_parser) else: assert len(t["children"]) == 2 mappingFrom = parse_type(t["children"][0], contract_parser) mappingTo = parse_type(t["children"][1], contract_parser) 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.slither) 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.slither) 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_expression(expression, caller_context): """ Returns: str: expression """ # Expression # = Expression ('++' | '--') # | NewExpression # | IndexAccess # | MemberAccess # | FunctionCall # | '(' Expression ')' # | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression # | Expression '**' Expression # | Expression ('*' | '/' | '%') Expression # | Expression ('+' | '-') Expression # | Expression ('<<' | '>>') Expression # | Expression '&' Expression # | Expression '^' Expression # | Expression '|' Expression # | Expression ('<' | '>' | '<=' | '>=') Expression # | Expression ('==' | '!=') Expression # | Expression '&&' Expression # | Expression '||' Expression # | Expression '?' Expression ':' Expression # | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression # | PrimaryExpression # The AST naming does not follow the spec name = expression['name'] if name == 'UnaryOperation': attributes = expression['attributes'] assert 'prefix' in attributes operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix']) assert len(expression['children']) == 1 expression = parse_expression(expression['children'][0], caller_context) unary_op = UnaryOperation(expression, operation_type) return unary_op elif name == 'BinaryOperation': attributes = expression['attributes'] operation_type = BinaryOperationType.get_type(attributes['operator']) assert len(expression['children']) == 2 left_expression = parse_expression(expression['children'][0], caller_context) right_expression = parse_expression(expression['children'][1], caller_context) binary_op = BinaryOperation(left_expression, right_expression, operation_type) return binary_op elif name == 'FunctionCall': return parse_call(expression, caller_context) elif name == 'TupleExpression': """ For expression like (a,,c) = (1,2,3) the AST provides only two children in the left side We check the type provided (tuple(uint256,,uint256)) To determine that there is an empty variable Otherwhise we would not be able to determine that a = 1, c = 3, and 2 is lost Note: this is only possible with Solidity >= 0.4.12 """ if 'children' not in expression: attributes = expression['attributes'] components = attributes['components'] expressions = [ parse_expression(c, caller_context) if c else None for c in components ] else: expressions = [ parse_expression(e, caller_context) for e in expression['children'] ] # Add none for empty tuple items if "attributes" in expression: if "type" in expression['attributes']: t = expression['attributes']['type'] if ',,' in t or '(,' in t or ',)' in t: t = t[len('tuple('):-1] elems = t.split(',') for idx in range(len(elems)): if elems[idx] == '': expressions.insert(idx, None) t = TupleExpression(expressions) return t elif name == 'Conditional': children = expression['children'] assert len(children) == 3 if_expression = parse_expression(children[0], caller_context) then_expression = parse_expression(children[1], caller_context) else_expression = parse_expression(children[2], caller_context) conditional = ConditionalExpression(if_expression, then_expression, else_expression) #print(conditional) return conditional elif name == 'Assignment': attributes = expression['attributes'] children = expression['children'] assert len(expression['children']) == 2 left_expression = parse_expression(children[0], caller_context) right_expression = parse_expression(children[1], caller_context) operation_type = AssignmentOperationType.get_type( attributes['operator']) operation_return_type = attributes['type'] assignement = AssignmentOperation(left_expression, right_expression, operation_type, operation_return_type) return assignement elif name == 'Literal': assert 'children' not in expression value = expression['attributes']['value'] if value is None: # for literal declared as hex # see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals assert 'hexvalue' in expression['attributes'] value = '0x' + expression['attributes']['hexvalue'] literal = Literal(value) return literal elif name == 'Identifier': assert 'children' not in expression value = expression['attributes']['value'] if 'type' in expression['attributes']: t = expression['attributes']['type'] if t: found = re.findall( '[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t) assert len(found) <= 1 if found: value = value + '(' + found[0] + ')' value = filter_name(value) var = find_variable(value, caller_context) identifier = Identifier(var) return identifier elif name == 'IndexAccess': index_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 2 left_expression = parse_expression(children[0], caller_context) right_expression = parse_expression(children[1], caller_context) index = IndexAccess(left_expression, right_expression, index_type) return index elif name == 'MemberAccess': member_name = expression['attributes']['member_name'] member_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 1 member_expression = parse_expression(children[0], caller_context) if str(member_expression) == 'super': super_name = parse_super_name(expression) if isinstance(caller_context, Contract): inheritance = caller_context.inheritance else: assert isinstance(caller_context, Function) inheritance = caller_context.contract.inheritance var = None for father in inheritance: try: var = find_variable(super_name, father) break except VariableNotFound: continue if var is None: raise VariableNotFound( 'Variable not found: {}'.format(super_name)) return SuperIdentifier(var) member_access = MemberAccess(member_name, member_type, member_expression) if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: return Identifier(SolidityVariableComposed(str(member_access))) return member_access elif name == 'ElementaryTypeNameExpression': # nop exression # uint; assert 'children' not in expression value = expression['attributes']['value'] t = parse_type(UnknownType(value), caller_context) return ElementaryTypeNameExpression(t) # NewExpression is not a root expression, it's always the child of another expression elif name == 'NewExpression': new_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 1 #new_expression = parse_expression(children[0]) child = children[0] if child['name'] == 'ArrayTypeName': depth = 0 while child['name'] == 'ArrayTypeName': # Note: dont conserve the size of the array if provided #assert len(child['children']) == 1 child = child['children'][0] depth += 1 if child['name'] == 'ElementaryTypeName': array_type = ElementaryType(child['attributes']['name']) elif child['name'] == 'UserDefinedTypeName': array_type = parse_type( UnknownType(child['attributes']['name']), caller_context) else: logger.error('Incorrect type array {}'.format(child)) exit(-1) array = NewArray(depth, array_type) return array if child['name'] == 'ElementaryTypeName': elem_type = ElementaryType(child['attributes']['name']) new_elem = NewElementaryType(elem_type) return new_elem assert child['name'] == 'UserDefinedTypeName' contract_name = child['attributes']['name'] new = NewContract(contract_name) return new elif name == 'ModifierInvocation': children = expression['children'] called = parse_expression(children[0], caller_context) arguments = [ parse_expression(a, caller_context) for a in children[1::] ] call = CallExpression(called, arguments, 'Modifier') return call logger.error('Expression not parsed %s' % name) exit(-1)
def parse_type(t, caller_context): # local import to avoid circular dependency from slither.solc_parsing.expressions.expression_parsing import parse_expression from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc if isinstance(caller_context, Contract): contract = caller_context elif isinstance(caller_context, Function): contract = caller_context.contract else: logger.error('Incorrect caller context') exit(-1) structures = contract.structures enums = contract.enums contracts = contract.slither.contracts if isinstance(t, UnknownType): return _find_from_type_name(t.name, contract, contracts, structures, enums) elif t['name'] == 'ElementaryTypeName': return ElementaryType(t['attributes']['name']) elif t['name'] == 'UserDefinedTypeName': return _find_from_type_name(t['attributes']['name'], contract, contracts, structures, enums) elif t['name'] == 'ArrayTypeName': length = None 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], contract) return ArrayType(array_type, length) elif t['name'] == 'Mapping': assert len(t['children']) == 2 mappingFrom = parse_type(t['children'][0], contract) mappingTo = parse_type(t['children'][1], contract) return MappingType(mappingFrom, mappingTo) elif t['name'] == 'FunctionTypeName': assert len(t['children']) == 2 params = t['children'][0] return_values = t['children'][1] assert params['name'] == 'ParameterList' assert return_values['name'] == 'ParameterList' params_vars = [] return_values_vars = [] for p in params['children']: var = FunctionTypeVariableSolc(p) var.set_offset(p['src'], caller_context.slither) var.analyze(caller_context) params_vars.append(var) for p in return_values['children']: var = FunctionTypeVariableSolc(p) var.set_offset(p['src'], caller_context.slither) var.analyze(caller_context) return_values_vars.append(var) return FunctionType(params_vars, return_values_vars) logger.error('Type name not found ' + str(t)) exit(-1)
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_expression(expression, caller_context): """ Returns: str: expression """ # Expression # = Expression ('++' | '--') # | NewExpression # | IndexAccess # | MemberAccess # | FunctionCall # | '(' Expression ')' # | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression # | Expression '**' Expression # | Expression ('*' | '/' | '%') Expression # | Expression ('+' | '-') Expression # | Expression ('<<' | '>>') Expression # | Expression '&' Expression # | Expression '^' Expression # | Expression '|' Expression # | Expression ('<' | '>' | '<=' | '>=') Expression # | Expression ('==' | '!=') Expression # | Expression '&&' Expression # | Expression '||' Expression # | Expression '?' Expression ':' Expression # | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression # | PrimaryExpression # The AST naming does not follow the spec name = expression['name'] if name == 'UnaryOperation': attributes = expression['attributes'] assert 'prefix' in attributes operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix']) assert len(expression['children']) == 1 expression = parse_expression(expression['children'][0], caller_context) unary_op = UnaryOperation(expression, operation_type) return unary_op elif name == 'BinaryOperation': attributes = expression['attributes'] operation_type = BinaryOperationType.get_type(attributes['operator']) assert len(expression['children']) == 2 left_expression = parse_expression(expression['children'][0], caller_context) right_expression = parse_expression(expression['children'][1], caller_context) binary_op = BinaryOperation(left_expression, right_expression, operation_type) return binary_op elif name == 'FunctionCall': return parse_call(expression, caller_context) elif name == 'TupleExpression': if 'children' not in expression: attributes = expression['attributes'] components = attributes['components'] expressions = [ parse_expression(c, caller_context) if c else None for c in components ] else: expressions = [ parse_expression(e, caller_context) for e in expression['children'] ] t = TupleExpression(expressions) return t elif name == 'Conditional': children = expression['children'] assert len(children) == 3 if_expression = parse_expression(children[0], caller_context) then_expression = parse_expression(children[1], caller_context) else_expression = parse_expression(children[2], caller_context) conditional = ConditionalExpression(if_expression, then_expression, else_expression) #print(conditional) return conditional elif name == 'Assignment': attributes = expression['attributes'] children = expression['children'] assert len(expression['children']) == 2 left_expression = parse_expression(children[0], caller_context) right_expression = parse_expression(children[1], caller_context) operation_type = AssignmentOperationType.get_type( attributes['operator']) operation_return_type = attributes['type'] assignement = AssignmentOperation(left_expression, right_expression, operation_type, operation_return_type) return assignement elif name == 'Literal': assert 'children' not in expression value = expression['attributes']['value'] literal = Literal(value) return literal elif name == 'Identifier': assert 'children' not in expression value = expression['attributes']['value'] if 'type' in expression['attributes']: t = expression['attributes']['type'] if t: found = re.findall( '[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t) assert len(found) <= 1 if found: value = value + '(' + found[0] + ')' value = filter_name(value) var = find_variable(value, caller_context) identifier = Identifier(var) return identifier elif name == 'IndexAccess': index_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 2 left_expression = parse_expression(children[0], caller_context) right_expression = parse_expression(children[1], caller_context) index = IndexAccess(left_expression, right_expression, index_type) return index elif name == 'MemberAccess': member_name = expression['attributes']['member_name'] member_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 1 member_expression = parse_expression(children[0], caller_context) if str(member_expression) == 'super': super_name = parse_super_name(expression) if isinstance(caller_context, Contract): inheritance = caller_context.inheritance else: assert isinstance(caller_context, Function) inheritance = caller_context.contract.inheritance var = None for father in inheritance: try: var = find_variable(super_name, father) break except VariableNotFound: continue if var is None: raise VariableNotFound( 'Variable not found: {}'.format(super_name)) return SuperIdentifier(var) member_access = MemberAccess(member_name, member_type, member_expression) if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: return Identifier(SolidityVariableComposed(str(member_access))) return member_access elif name == 'ElementaryTypeNameExpression': # nop exression # uint; assert 'children' not in expression value = expression['attributes']['value'] t = parse_type(UnknownType(value), caller_context) return ElementaryTypeNameExpression(t) # NewExpression is not a root expression, it's always the child of another expression elif name == 'NewExpression': new_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 1 #new_expression = parse_expression(children[0]) child = children[0] if child['name'] == 'ArrayTypeName': depth = 0 while child['name'] == 'ArrayTypeName': # Note: dont conserve the size of the array if provided #assert len(child['children']) == 1 child = child['children'][0] depth += 1 if child['name'] == 'ElementaryTypeName': array_type = ElementaryType(child['attributes']['name']) elif child['name'] == 'UserDefinedTypeName': array_type = parse_type( UnknownType(child['attributes']['name']), caller_context) else: logger.error('Incorrect type array {}'.format(child)) exit(-1) array = NewArray(depth, array_type) return array if child['name'] == 'ElementaryTypeName': elem_type = ElementaryType(child['attributes']['name']) new_elem = NewElementaryType(elem_type) return new_elem assert child['name'] == 'UserDefinedTypeName' contract_name = child['attributes']['name'] new = NewContract(contract_name) return new elif name == 'ModifierInvocation': children = expression['children'] called = parse_expression(children[0], caller_context) arguments = [ parse_expression(a, caller_context) for a in children[1::] ] call = CallExpression(called, arguments, 'Modifier') return call logger.error('Expression not parsed %s' % name) exit(-1)
def __init__(self, value, lvalue): assert is_valid_rvalue(value) assert is_valid_lvalue(lvalue) self._value = value self._lvalue = lvalue lvalue.set_type(ElementaryType('uint256'))