def analyze_expressions(self, caller_context): if self.type == NodeType.VARIABLE and not self._expression: self._expression = self.variable_declaration.expression if self._unparsed_expression: expression = parse_expression(self._unparsed_expression, caller_context) self._expression = expression self._unparsed_expression = None if self.expression: if self.type == NodeType.VARIABLE: # Update the expression to be an assignement to the variable #print(self.variable_declaration) _expression = AssignmentOperation( Identifier(self.variable_declaration), self.expression, AssignmentOperationType.ASSIGN, self.variable_declaration.type) _expression.set_offset(self.expression.source_mapping, self.slither) self._expression = _expression expression = self.expression pp = ReadVar(expression) self._expression_vars_read = pp.result() # self._vars_read = [item for sublist in vars_read for item in sublist] # self._state_vars_read = [x for x in self.variables_read if\ # isinstance(x, (StateVariable))] # self._solidity_vars_read = [x for x in self.variables_read if\ # isinstance(x, (SolidityVariable))] pp = WriteVar(expression) self._expression_vars_written = pp.result() # self._vars_written = [item for sublist in vars_written for item in sublist] # self._state_vars_written = [x for x in self.variables_written if\ # isinstance(x, StateVariable)] pp = FindCalls(expression) self._expression_calls = pp.result() self._external_calls_as_expressions = [ c for c in self.calls_as_expression if not isinstance(c.called, Identifier) ] self._internal_calls_as_expressions = [ c for c in self.calls_as_expression if isinstance(c.called, Identifier) ]
def analyze_expressions(self, caller_context): if self.type == NodeType.VARIABLE and not self._expression: self._expression = self.variable_declaration.expression if self._unparsed_expression: expression = parse_expression(self._unparsed_expression, caller_context) self._expression = expression self._unparsed_expression = None if self.expression: if self.type == NodeType.VARIABLE: # Update the expression to be an assignement to the variable #print(self.variable_declaration) self._expression = AssignmentOperation(Identifier(self.variable_declaration), self.expression, AssignmentOperationType.ASSIGN, self.variable_declaration.type) expression = self.expression pp = ReadVar(expression) self._expression_vars_read = pp.result() vars_read = [ExportValues(v).result() for v in self._expression_vars_read] self._vars_read = [item for sublist in vars_read for item in sublist] self._state_vars_read = [x for x in self.variables_read if\ isinstance(x, (StateVariable))] self._solidity_vars_read = [x for x in self.variables_read if\ isinstance(x, (SolidityVariable))] pp = WriteVar(expression) self._expression_vars_written = pp.result() vars_written = [ExportValues(v).result() for v in self._expression_vars_written] self._vars_written = [item for sublist in vars_written for item in sublist] self._state_vars_written = [x for x in self.variables_written if\ isinstance(x, StateVariable)] pp = FindCalls(expression) self._expression_calls = pp.result() calls = [ExportValues(c).result() for c in self.calls_as_expression] calls = [item for sublist in calls for item in sublist] self._internal_calls = [c for c in calls if isinstance(c, (Function, SolidityFunction))] self._external_calls = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]
def analyze_expressions(self, caller_context): if self._node.type == NodeType.VARIABLE and not self._node.expression: self._node.add_expression( self._node.variable_declaration.expression) if self._unparsed_expression: expression = parse_expression(self._unparsed_expression, caller_context) self._node.add_expression(expression) # self._unparsed_expression = None if self._node.expression: if self._node.type == NodeType.VARIABLE: # Update the expression to be an assignement to the variable _expression = AssignmentOperation( Identifier(self._node.variable_declaration), self._node.expression, AssignmentOperationType.ASSIGN, self._node.variable_declaration.type, ) _expression.set_offset(self._node.expression.source_mapping, self._node.slither) self._node.add_expression(_expression, bypass_verif_empty=True) expression = self._node.expression read_var = ReadVar(expression) self._node.variables_read_as_expression = read_var.result() write_var = WriteVar(expression) self._node.variables_written_as_expression = write_var.result() find_call = FindCalls(expression) self._node.calls_as_expression = find_call.result() self._node.external_calls_as_expressions = [ c for c in self._node.calls_as_expression if not isinstance(c.called, Identifier) ] self._node.internal_calls_as_expressions = [ c for c in self._node.calls_as_expression if isinstance(c.called, Identifier) ]
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[caller_context.get_key()] is_compact_ast = caller_context.is_compact_ast if name == 'UnaryOperation': if is_compact_ast: attributes = expression else: attributes = expression['attributes'] assert 'prefix' in attributes operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix']) if is_compact_ast: expression = parse_expression(expression['subExpression'], caller_context) else: 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': if is_compact_ast: attributes = expression else: attributes = expression['attributes'] operation_type = BinaryOperationType.get_type(attributes['operator']) if is_compact_ast: left_expression = parse_expression(expression['leftExpression'], caller_context) right_expression = parse_expression(expression['rightExpression'], caller_context) else: 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 is_compact_ast: expressions = [ parse_expression(e, caller_context) if e else None for e in expression['components'] ] else: 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': if is_compact_ast: if_expression = parse_expression(expression['condition'], caller_context) then_expression = parse_expression(expression['trueExpression'], caller_context) else_expression = parse_expression(expression['falseExpression'], caller_context) else: 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) return conditional elif name == 'Assignment': if is_compact_ast: left_expression = parse_expression(expression['leftHandSide'], caller_context) right_expression = parse_expression(expression['rightHandSide'], caller_context) operation_type = AssignmentOperationType.get_type( expression['operator']) operation_return_type = expression['typeDescriptions'][ 'typeString'] else: 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 if is_compact_ast: value = expression['value'] if not value: value = '0x' + expression['hexValue'] else: 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 t = None if caller_context.is_compact_ast: value = expression['name'] t = expression['typeDescriptions']['typeString'] else: 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': if is_compact_ast: index_type = expression['typeDescriptions']['typeString'] left = expression['baseExpression'] right = expression['indexExpression'] else: index_type = expression['attributes']['type'] children = expression['children'] assert len(children) == 2 left = children[0] right = children[1] left_expression = parse_expression(left, caller_context) right_expression = parse_expression(right, caller_context) index = IndexAccess(left_expression, right_expression, index_type) return index elif name == 'MemberAccess': if caller_context.is_compact_ast: member_name = expression['memberName'] member_type = expression['typeDescriptions']['typeString'] member_expression = parse_expression(expression['expression'], caller_context) else: 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, is_compact_ast) 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; if is_compact_ast: value = expression['typeName'] else: 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': if is_compact_ast: type_name = expression['typeName'] else: children = expression['children'] assert len(children) == 1 type_name = children[0] if type_name[caller_context.get_key()] == 'ArrayTypeName': depth = 0 while type_name[caller_context.get_key()] == 'ArrayTypeName': # Note: dont conserve the size of the array if provided # We compute it directly if is_compact_ast: type_name = type_name['baseType'] else: type_name = type_name['children'][0] depth += 1 if type_name[caller_context.get_key()] == 'ElementaryTypeName': if is_compact_ast: array_type = ElementaryType(type_name['name']) else: array_type = ElementaryType( type_name['attributes']['name']) elif type_name[caller_context.get_key()] == 'UserDefinedTypeName': if is_compact_ast: array_type = parse_type(UnknownType(type_name['name']), caller_context) else: array_type = parse_type( UnknownType(type_name['attributes']['name']), caller_context) else: logger.error('Incorrect type array {}'.format(type_name)) exit(-1) array = NewArray(depth, array_type) return array if type_name[caller_context.get_key()] == 'ElementaryTypeName': if is_compact_ast: elem_type = ElementaryType(type_name['name']) else: elem_type = ElementaryType(type_name['attributes']['name']) new_elem = NewElementaryType(elem_type) return new_elem assert type_name[caller_context.get_key()] == 'UserDefinedTypeName' if is_compact_ast: contract_name = type_name['name'] else: contract_name = type_name['attributes']['name'] new = NewContract(contract_name) return new elif name == 'ModifierInvocation': if is_compact_ast: called = parse_expression(expression['modifierName'], caller_context) arguments = [] if expression['arguments']: arguments = [ parse_expression(a, caller_context) for a in expression['arguments'] ] else: 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_expression(expression: Dict, caller_context: CallerContext) -> "Expression": # pylint: disable=too-many-nested-blocks,too-many-statements """ 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[caller_context.get_key()] is_compact_ast = caller_context.is_compact_ast src = expression["src"] if name == "UnaryOperation": if is_compact_ast: attributes = expression else: attributes = expression["attributes"] assert "prefix" in attributes operation_type = UnaryOperationType.get_type(attributes["operator"], attributes["prefix"]) if is_compact_ast: expression = parse_expression(expression["subExpression"], caller_context) else: assert len(expression["children"]) == 1 expression = parse_expression(expression["children"][0], caller_context) unary_op = UnaryOperation(expression, operation_type) unary_op.set_offset(src, caller_context.slither) return unary_op if name == "BinaryOperation": if is_compact_ast: attributes = expression else: attributes = expression["attributes"] operation_type = BinaryOperationType.get_type(attributes["operator"]) if is_compact_ast: left_expression = parse_expression(expression["leftExpression"], caller_context) right_expression = parse_expression(expression["rightExpression"], caller_context) else: 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) binary_op.set_offset(src, caller_context.slither) return binary_op if name in "FunctionCall": return parse_call(expression, caller_context) if name == "FunctionCallOptions": # call/gas info are handled in parse_call if is_compact_ast: called = parse_expression(expression["expression"], caller_context) else: called = parse_expression(expression["children"][0], caller_context) assert isinstance(called, (MemberAccess, NewContract, Identifier, TupleExpression)) return called if 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 is_compact_ast: expressions = [ parse_expression(e, caller_context) if e else None for e in expression["components"] ] else: 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 enumerate(elems): if elems[idx] == "": expressions.insert(idx, None) t = TupleExpression(expressions) t.set_offset(src, caller_context.slither) return t if name == "Conditional": if is_compact_ast: if_expression = parse_expression(expression["condition"], caller_context) then_expression = parse_expression(expression["trueExpression"], caller_context) else_expression = parse_expression(expression["falseExpression"], caller_context) else: 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) conditional.set_offset(src, caller_context.slither) return conditional if name == "Assignment": if is_compact_ast: left_expression = parse_expression(expression["leftHandSide"], caller_context) right_expression = parse_expression(expression["rightHandSide"], caller_context) operation_type = AssignmentOperationType.get_type(expression["operator"]) operation_return_type = expression["typeDescriptions"]["typeString"] else: 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 ) assignement.set_offset(src, caller_context.slither) return assignement if name == "Literal": subdenomination = None assert "children" not in expression if is_compact_ast: value = expression["value"] if value: if "subdenomination" in expression and expression["subdenomination"]: subdenomination = expression["subdenomination"] elif not value and value != "": value = "0x" + expression["hexValue"] type_candidate = expression["typeDescriptions"]["typeString"] # Length declaration for array was None until solc 0.5.5 if type_candidate is None: if expression["kind"] == "number": type_candidate = "int_const" else: value = expression["attributes"]["value"] if value: if ( "subdenomination" in expression["attributes"] and expression["attributes"]["subdenomination"] ): subdenomination = expression["attributes"]["subdenomination"] elif 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"] type_candidate = expression["attributes"]["type"] if type_candidate is None: if value.isdecimal(): type_candidate = ElementaryType("uint256") else: type_candidate = ElementaryType("string") elif type_candidate.startswith("int_const "): type_candidate = ElementaryType("uint256") elif type_candidate.startswith("bool"): type_candidate = ElementaryType("bool") elif type_candidate.startswith("address"): type_candidate = ElementaryType("address") else: type_candidate = ElementaryType("string") literal = Literal(value, type_candidate, subdenomination) literal.set_offset(src, caller_context.slither) return literal if name == "Identifier": assert "children" not in expression t = None if caller_context.is_compact_ast: value = expression["name"] t = expression["typeDescriptions"]["typeString"] else: 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) if "referencedDeclaration" in expression: referenced_declaration = expression["referencedDeclaration"] else: referenced_declaration = None var = find_variable(value, caller_context, referenced_declaration) identifier = Identifier(var) identifier.set_offset(src, caller_context.slither) return identifier if name == "IndexAccess": if is_compact_ast: index_type = expression["typeDescriptions"]["typeString"] left = expression["baseExpression"] right = expression.get("indexExpression", None) else: index_type = expression["attributes"]["type"] children = expression["children"] left = children[0] right = children[1] if len(children) > 1 else None # IndexAccess is used to describe ElementaryTypeNameExpression # if abi.decode is used # For example, abi.decode(data, ...(uint[]) ) if right is None: ret = parse_expression(left, caller_context) # Nested array are not yet available in abi.decode if isinstance(ret, ElementaryTypeNameExpression): old_type = ret.type ret.type = ArrayType(old_type, None) return ret left_expression = parse_expression(left, caller_context) right_expression = parse_expression(right, caller_context) index = IndexAccess(left_expression, right_expression, index_type) index.set_offset(src, caller_context.slither) return index if name == "MemberAccess": if caller_context.is_compact_ast: member_name = expression["memberName"] member_type = expression["typeDescriptions"]["typeString"] # member_type = parse_type( # UnknownType(expression["typeDescriptions"]["typeString"]), caller_context # ) member_expression = parse_expression(expression["expression"], caller_context) else: member_name = expression["attributes"]["member_name"] member_type = expression["attributes"]["type"] # member_type = parse_type(UnknownType(expression["attributes"]["type"]), caller_context) 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, is_compact_ast) var = find_variable(super_name, caller_context, is_super=True) if var is None: raise VariableNotFound("Variable not found: {}".format(super_name)) sup = SuperIdentifier(var) sup.set_offset(src, caller_context.slither) return sup member_access = MemberAccess(member_name, member_type, member_expression) member_access.set_offset(src, caller_context.slither) if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: id_idx = Identifier(SolidityVariableComposed(str(member_access))) id_idx.set_offset(src, caller_context.slither) return id_idx return member_access if name == "ElementaryTypeNameExpression": return _parse_elementary_type_name_expression(expression, is_compact_ast, caller_context) # NewExpression is not a root expression, it's always the child of another expression if name == "NewExpression": if is_compact_ast: type_name = expression["typeName"] else: children = expression["children"] assert len(children) == 1 type_name = children[0] if type_name[caller_context.get_key()] == "ArrayTypeName": depth = 0 while type_name[caller_context.get_key()] == "ArrayTypeName": # Note: dont conserve the size of the array if provided # We compute it directly if is_compact_ast: type_name = type_name["baseType"] else: type_name = type_name["children"][0] depth += 1 if type_name[caller_context.get_key()] == "ElementaryTypeName": if is_compact_ast: array_type = ElementaryType(type_name["name"]) else: array_type = ElementaryType(type_name["attributes"]["name"]) elif type_name[caller_context.get_key()] == "UserDefinedTypeName": if is_compact_ast: array_type = parse_type(UnknownType(type_name["name"]), caller_context) else: array_type = parse_type( UnknownType(type_name["attributes"]["name"]), caller_context ) elif type_name[caller_context.get_key()] == "FunctionTypeName": array_type = parse_type(type_name, caller_context) else: raise ParsingError("Incorrect type array {}".format(type_name)) array = NewArray(depth, array_type) array.set_offset(src, caller_context.slither) return array if type_name[caller_context.get_key()] == "ElementaryTypeName": if is_compact_ast: elem_type = ElementaryType(type_name["name"]) else: elem_type = ElementaryType(type_name["attributes"]["name"]) new_elem = NewElementaryType(elem_type) new_elem.set_offset(src, caller_context.slither) return new_elem assert type_name[caller_context.get_key()] == "UserDefinedTypeName" if is_compact_ast: # Changed introduced in Solidity 0.8 # see https://github.com/crytic/slither/issues/794 # TODO explore more the changes introduced in 0.8 and the usage of pathNode/IdentifierPath if "name" not in type_name: assert "pathNode" in type_name and "name" in type_name["pathNode"] contract_name = type_name["pathNode"]["name"] else: contract_name = type_name["name"] else: contract_name = type_name["attributes"]["name"] new = NewContract(contract_name) new.set_offset(src, caller_context.slither) return new if name == "ModifierInvocation": if is_compact_ast: called = parse_expression(expression["modifierName"], caller_context) arguments = [] if expression.get("arguments", None): arguments = [parse_expression(a, caller_context) for a in expression["arguments"]] else: 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") call.set_offset(src, caller_context.slither) return call if name == "IndexRangeAccess": # For now, we convert array slices to a direct array access # As a result the generated IR will lose the slices information # As far as I understand, array slice are only used in abi.decode # https://solidity.readthedocs.io/en/v0.6.12/types.html # TODO: Investigate array slices usage and implication for the IR base = parse_expression(expression["baseExpression"], caller_context) return base # Introduced with solc 0.8 if name == "IdentifierPath": if caller_context.is_compact_ast: value = expression["name"] if "referencedDeclaration" in expression: referenced_declaration = expression["referencedDeclaration"] else: referenced_declaration = None var = find_variable(value, caller_context, referenced_declaration) identifier = Identifier(var) identifier.set_offset(src, caller_context.slither) return identifier raise ParsingError("IdentifierPath not currently supported for the legacy ast") raise ParsingError("Expression not parsed %s" % name)
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)