Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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)
Пример #5
0
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)