Exemplo n.º 1
0
    def from_annotation(
        cls,
        node: Union[vy_ast.Name, vy_ast.Call, vy_ast.Subscript],
        location: DataLocation = DataLocation.UNSET,
        is_immutable: bool = False,
        is_public: bool = False,
    ) -> MappingDefinition:
        if (
            not isinstance(node, vy_ast.Subscript)
            or not isinstance(node.slice, vy_ast.Index)
            or not isinstance(node.slice.value, vy_ast.Tuple)
            or len(node.slice.value.elements) != 2
        ):
            raise StructureException(
                "HashMap must be defined with a key type and a value type", node
            )
        if location != DataLocation.STORAGE:
            raise StructureException("HashMap can only be declared as a storage variable", node)

        key_type = get_type_from_annotation(node.slice.value.elements[0], DataLocation.UNSET)
        value_type = get_type_from_annotation(node.slice.value.elements[1], DataLocation.STORAGE)
        return MappingDefinition(
            value_type,
            key_type,
            f"HashMap[{key_type}, {value_type}]",
            location,
            is_immutable,
            is_public,
        )
Exemplo n.º 2
0
 def _validate_single(self, arg, expected_type):
     # TODO using "TYPE_DEFINITION" is a kludge in derived classes,
     # refactor me.
     if expected_type == "TYPE_DEFINITION":
         # try to parse the type - call get_type_from_annotation
         # for its side effects (will throw if is not a type)
         get_type_from_annotation(arg, DataLocation.UNSET)
     else:
         validate_expected_type(arg, expected_type)
Exemplo n.º 3
0
    def from_annotation(
        cls,
        node: Union[vy_ast.Name, vy_ast.Call, vy_ast.Subscript],
        location: DataLocation = DataLocation.UNSET,
        is_constant: bool = False,
        is_public: bool = False,
        is_immutable: bool = False,
    ) -> DynamicArrayDefinition:
        # TODO fix circular import
        from vyper.semantics.types.utils import get_type_from_annotation

        if (not isinstance(node, vy_ast.Subscript)
                or not isinstance(node.slice, vy_ast.Index)
                or not isinstance(node.slice.value, vy_ast.Tuple)
                or not isinstance(node.slice.value.elements[1], vy_ast.Int)
                or len(node.slice.value.elements) != 2):
            raise StructureException(
                "DynArray must be defined with base type and max length, e.g. DynArray[bool, 5]",
                node,
            )

        value_type = get_type_from_annotation(node.slice.value.elements[0],
                                              location, is_constant, is_public,
                                              is_immutable)

        max_length = node.slice.value.elements[1].value
        return DynamicArrayDefinition(value_type, max_length, location,
                                      is_constant, is_public, is_immutable)
Exemplo n.º 4
0
def test_base_types_as_multidimensional_arrays(build_node, type_str, location,
                                               first, second):
    node = build_node(f"{type_str}[{first}][{second}]")

    type_definition = get_type_from_annotation(node, location)

    assert type_definition.size_in_bytes == first * second * 32
Exemplo n.º 5
0
def test_array_value_types(build_node, type_str):
    node = build_node(f"{type_str}[1]")
    primitive = get_primitive_types()[type_str]

    type_definition = get_type_from_annotation(node, DataLocation.STORAGE)

    assert isinstance(type_definition, primitive._type)
Exemplo n.º 6
0
def build_primitive_from_node(base_node: vy_ast.EventDef) -> StructPrimitive:
    """
    Generate a `StructPrimitive` object from a Vyper ast node.

    Arguments
    ---------
    node : EventDef
        Vyper ast node defining the struct
    Returns
    -------
    StructPrimitive
        Primitive struct type
    """

    members: OrderedDict = OrderedDict()
    for node in base_node.body:
        if not isinstance(node, vy_ast.AnnAssign):
            raise StructureException("Structs can only variable definitions",
                                     node)
        if node.value is not None:
            raise StructureException(
                "Cannot assign a value during struct declaration", node)
        if not isinstance(node.target, vy_ast.Name):
            raise StructureException("Invalid syntax for struct member name",
                                     node.target)
        member_name = node.target.id
        if member_name in members:
            raise NamespaceCollision(
                f"Struct member '{member_name}' has already been declared",
                node.target)
        members[member_name] = get_type_from_annotation(
            node.annotation, DataLocation.UNSET)

    return StructPrimitive(base_node.name, members)
Exemplo n.º 7
0
    def from_AnnAssign(cls, node: vy_ast.AnnAssign) -> "ContractFunction":
        """
        Generate a `ContractFunction` object from an `AnnAssign` node.

        Used to create getter functions for public variables.

        Arguments
        ---------
        node : AnnAssign
            Vyper ast node to generate the function definition from.

        Returns
        -------
        ContractFunction
        """
        if not isinstance(node.annotation, vy_ast.Call):
            raise CompilerPanic("Annotation must be a call to public()")
        type_ = get_type_from_annotation(node.annotation.args[0],
                                         location=DataLocation.STORAGE)
        arguments, return_type = type_.get_signature()
        args_dict: OrderedDict = OrderedDict()
        for item in arguments:
            args_dict[f"arg{len(args_dict)}"] = item
        return cls(
            node.target.id,
            args_dict,
            len(arguments),
            len(arguments),
            return_type,
            function_visibility=FunctionVisibility.EXTERNAL,
            state_mutability=StateMutability.VIEW,
        )
Exemplo n.º 8
0
def test_mapping(build_node, type_str, type_str2):
    node = build_node(f"HashMap[{type_str}, {type_str2}]")
    primitives = get_primitive_types()

    type_definition = get_type_from_annotation(node, DataLocation.STORAGE)

    assert isinstance(type_definition, MappingDefinition)
    assert isinstance(type_definition.key_type, primitives[type_str]._type)
    assert isinstance(type_definition.value_type, primitives[type_str2]._type)
Exemplo n.º 9
0
def test_base_types_as_arrays(build_node, type_str):
    node = build_node(f"{type_str}[3]")
    primitive = get_primitive_types()[type_str]

    type_definition = get_type_from_annotation(node, DataLocation.STORAGE)

    assert isinstance(type_definition, ArrayDefinition)
    assert type_definition.length == 3
    assert isinstance(type_definition.value_type, primitive._type)
Exemplo n.º 10
0
    def from_EventDef(cls, base_node: vy_ast.EventDef) -> "Event":
        """
        Generate an `Event` object from a Vyper ast node.

        Arguments
        ---------
        base_node : EventDef
            Vyper ast node defining the event
        Returns
        -------
        Event
        """
        members: OrderedDict = OrderedDict()
        indexed: List = []

        if len(base_node.body) == 1 and isinstance(base_node.body[0],
                                                   vy_ast.Pass):
            return Event(base_node.name, members, indexed)

        for node in base_node.body:
            if not isinstance(node, vy_ast.AnnAssign):
                raise StructureException(
                    "Events can only contain variable definitions", node)
            if node.value is not None:
                raise StructureException(
                    "Cannot assign a value during event declaration", node)
            if not isinstance(node.target, vy_ast.Name):
                raise StructureException(
                    "Invalid syntax for event member name", node.target)
            member_name = node.target.id
            if member_name in members:
                raise NamespaceCollision(
                    f"Event member '{member_name}' has already been declared",
                    node.target)

            annotation = node.annotation
            if isinstance(
                    annotation,
                    vy_ast.Call) and annotation.get("func.id") == "indexed":
                validate_call_args(annotation, 1)
                if indexed.count(True) == 3:
                    raise EventDeclarationException(
                        "Event cannot have more than three indexed arguments",
                        annotation)
                indexed.append(True)
                annotation = annotation.args[0]
            else:
                indexed.append(False)

            members[member_name] = get_type_from_annotation(
                annotation, DataLocation.UNSET)

        return Event(base_node.name, members, indexed)
Exemplo n.º 11
0
    def visit_AnnAssign(self, node):
        name = node.get("target.id")
        if name is None:
            raise VariableDeclarationException("Invalid assignment", node)

        if not node.value:
            raise VariableDeclarationException(
                "Memory variables must be declared with an initial value",
                node)

        type_definition = get_type_from_annotation(node.annotation,
                                                   DataLocation.MEMORY)
        validate_expected_type(node.value, type_definition)

        try:
            self.namespace[name] = type_definition
        except VyperException as exc:
            raise exc.with_annotation(node) from None
Exemplo n.º 12
0
def replace_user_defined_constants(vyper_module: vy_ast.Module) -> int:
    """
    Find user-defined constant assignments, and replace references
    to the constants with their literal values.

    Arguments
    ---------
    vyper_module : Module
        Top-level Vyper AST node.

    Returns
    -------
    int
        Number of nodes that were replaced.
    """
    changed_nodes = 0

    for node in vyper_module.get_children(vy_ast.AnnAssign):
        if not isinstance(node.target, vy_ast.Name):
            # left-hand-side of assignment is not a variable
            continue
        if node.get("annotation.func.id") != "constant":
            # annotation is not wrapped in `constant(...)`
            continue

        # Extract type definition from propagated annotation
        constant_annotation = node.get("annotation.args")[0]
        try:
            type_ = (
                get_type_from_annotation(constant_annotation, DataLocation.UNSET)
                if constant_annotation
                else None
            )
        except UnknownType:
            # handle user-defined types e.g. structs - it's OK to not
            # propagate the type annotation here because user-defined
            # types can be unambiguously inferred at typechecking time
            type_ = None

        changed_nodes += replace_constant(
            vyper_module, node.target.id, node.value, False, type_=type_
        )

    return changed_nodes
Exemplo n.º 13
0
def replace_user_defined_constants(vyper_module: vy_ast.Module) -> int:
    """
    Find user-defined constant assignments, and replace references
    to the constants with their literal values.

    Arguments
    ---------
    vyper_module : Module
        Top-level Vyper AST node.

    Returns
    -------
    int
        Number of nodes that were replaced.
    """
    changed_nodes = 0

    for node in vyper_module.get_children(vy_ast.AnnAssign):
        if not isinstance(node.target, vy_ast.Name):
            # left-hand-side of assignment is not a variable
            continue
        if node.get("annotation.func.id") != "constant":
            # annotation is not wrapped in `constant(...)`
            continue

        # Extract type definition from propagated annotation
        constant_annotation = node.get("annotation.args")[0]
        type_ = (get_type_from_annotation(constant_annotation,
                                          DataLocation.UNSET)
                 if constant_annotation else None)

        changed_nodes += replace_constant(vyper_module,
                                          node.target.id,
                                          node.value,
                                          False,
                                          type_=type_)

    return changed_nodes
Exemplo n.º 14
0
def test_array_value_types_as_arrays(build_node, type_str):
    node = build_node(f"{type_str}[1][1]")

    with pytest.raises(StructureException):
        get_type_from_annotation(node, DataLocation.STORAGE)
Exemplo n.º 15
0
def test_base_types_as_arrays(build_node, type_str, location, length):
    node = build_node(f"{type_str}[{length}]")
    type_definition = get_type_from_annotation(node, location)

    assert type_definition.size_in_bytes == length * 32
Exemplo n.º 16
0
def test_dynamic_array_lengths(build_node, type_str, location, length):
    node = build_node(f"DynArray[{type_str}, {length}]")
    type_definition = get_type_from_annotation(node, location)

    assert type_definition.size_in_bytes == 32 + length * 32
Exemplo n.º 17
0
def test_array_value_types(build_node, type_str, location, length, size):
    node = build_node(f"{type_str}[{length}]")
    type_definition = get_type_from_annotation(node, location)

    assert type_definition.size_in_bytes == size
Exemplo n.º 18
0
def test_base_types(build_node, type_str, location):
    node = build_node(type_str)
    type_definition = get_type_from_annotation(node, location)

    assert type_definition.size_in_bytes == 32
Exemplo n.º 19
0
def test_invalid_index(build_node, idx, type_str):
    node = build_node(f"{type_str}[{idx}]")
    with pytest.raises((ArrayIndexException, InvalidType, StructureException,
                        UndeclaredDefinition)):
        get_type_from_annotation(node, DataLocation.STORAGE)
Exemplo n.º 20
0
    def visit_AnnAssign(self, node):
        name = node.get("target.id")
        if name is None:
            raise VariableDeclarationException("Invalid module-level assignment", node)

        if name == "implements":
            interface_name = node.annotation.id
            self.namespace[interface_name].validate_implements(node)
            return

        is_constant, is_public, is_immutable = False, False, False
        annotation = node.annotation
        if isinstance(annotation, vy_ast.Call):
            # the annotation is a function call, e.g. `foo: constant(uint256)`
            call_name = annotation.get("func.id")
            if call_name in ("constant", "public", "immutable"):
                validate_call_args(annotation, 1)
                if call_name == "constant":
                    # declaring a constant
                    is_constant = True

                elif call_name == "public":
                    # declaring a public variable
                    is_public = True

                    # generate function type and add to metadata
                    # we need this when builing the public getter
                    node._metadata["func_type"] = ContractFunction.from_AnnAssign(node)

                elif call_name == "immutable":
                    # declaring an immutable variable
                    is_immutable = True

                    # mutability is checked automatically preventing assignment
                    # outside of the constructor, here we just check a value is assigned,
                    # not necessarily where
                    assignments = self.ast.get_descendants(
                        vy_ast.Assign, filters={"target.id": node.target.id}
                    )
                    if not assignments:
                        # Special error message for common wrong usages via `self.<immutable name>`
                        wrong_self_attribute = self.ast.get_descendants(
                            vy_ast.Attribute, {"value.id": "self", "attr": node.target.id}
                        )
                        message = (
                            "Immutable variables must be accessed without 'self'"
                            if len(wrong_self_attribute) > 0
                            else "Immutable definition requires an assignment in the constructor"
                        )
                        raise SyntaxException(
                            message, node.node_source_code, node.lineno, node.col_offset
                        )

                # remove the outer call node, to handle cases such as `public(map(..))`
                annotation = annotation.args[0]

        data_loc = DataLocation.CODE if is_immutable else DataLocation.STORAGE
        type_definition = get_type_from_annotation(
            annotation, data_loc, is_constant, is_public, is_immutable
        )
        node._metadata["type"] = type_definition

        if is_constant:
            if not node.value:
                raise VariableDeclarationException("Constant must be declared with a value", node)
            if not check_constant(node.value):
                raise StateAccessViolation("Value must be a literal", node.value)

            validate_expected_type(node.value, type_definition)
            try:
                self.namespace[name] = type_definition
            except VyperException as exc:
                raise exc.with_annotation(node) from None
            return

        if node.value:
            var_type = "Immutable" if is_immutable else "Storage"
            raise VariableDeclarationException(
                f"{var_type} variables cannot have an initial value", node.value
            )

        if is_immutable:
            try:
                # block immutable if storage variable already exists
                if name in self.namespace["self"].members:
                    raise NamespaceCollision(
                        f"Value '{name}' has already been declared", node
                    ) from None
                self.namespace[name] = type_definition
            except VyperException as exc:
                raise exc.with_annotation(node) from None
            return

        try:
            self.namespace.validate_assignment(name)
        except NamespaceCollision as exc:
            raise exc.with_annotation(node) from None
        try:
            self.namespace["self"].add_member(name, type_definition)
            node.target._metadata["type"] = type_definition
        except NamespaceCollision:
            raise NamespaceCollision(f"Value '{name}' has already been declared", node) from None
        except VyperException as exc:
            raise exc.with_annotation(node) from None
Exemplo n.º 21
0
def test_array_value_types(build_node, type_str, location, length, size):
    node = build_node(f"{type_str}[{length}]")
    type_definition = get_type_from_annotation(node, location)

    # TODO once storage of bytes is optimized, remove the +32
    assert type_definition.size_in_bytes == size + 32
Exemplo n.º 22
0
    def visit_AnnAssign(self, node):
        name = node.get("target.id")
        if name is None:
            raise VariableDeclarationException(
                "Invalid module-level assignment", node)

        if name == "implements":
            interface_name = node.annotation.id
            self.namespace[interface_name].validate_implements(node)
            return

        is_immutable, is_public = False, False
        annotation = node.annotation
        if isinstance(annotation, vy_ast.Call):
            # the annotation is a function call, e.g. `foo: constant(uint256)`
            call_name = annotation.get("func.id")
            if call_name in ("constant", "public"):
                validate_call_args(annotation, 1)
                if call_name == "constant":
                    # declaring a constant
                    is_immutable = True

                elif call_name == "public":
                    # declaring a public variable
                    is_public = True

                    # generate function type and add to metadata
                    # we need this when builing the public getter
                    node._metadata[
                        "func_type"] = ContractFunction.from_AnnAssign(node)

                # remove the outer call node, to handle cases such as `public(map(..))`
                annotation = annotation.args[0]

        type_definition = get_type_from_annotation(annotation,
                                                   DataLocation.STORAGE,
                                                   is_immutable, is_public)
        node._metadata["type"] = type_definition

        if is_immutable:
            if not node.value:
                raise VariableDeclarationException(
                    "Constant must be declared with a value", node)
            if not check_literal(node.value):
                raise StateAccessViolation("Value must be a literal",
                                           node.value)

            validate_expected_type(node.value, type_definition)
            try:
                self.namespace[name] = type_definition
            except VyperException as exc:
                raise exc.with_annotation(node) from None
            return

        if node.value:
            raise VariableDeclarationException(
                "Storage variables cannot have an initial value", node.value)

        try:
            self.namespace.validate_assignment(name)
        except NamespaceCollision as exc:
            raise exc.with_annotation(node) from None
        try:
            self.namespace["self"].add_member(name, type_definition)
            node.target._metadata["type"] = type_definition
        except NamespaceCollision:
            raise NamespaceCollision(
                f"Value '{name}' has already been declared", node) from None
        except VyperException as exc:
            raise exc.with_annotation(node) from None
Exemplo n.º 23
0
    def from_FunctionDef(
            cls,
            node: vy_ast.FunctionDef,
            is_interface: Optional[bool] = False) -> "ContractFunction":
        """
        Generate a `ContractFunction` object from a `FunctionDef` node.

        Arguments
        ---------
        node : FunctionDef
            Vyper ast node to generate the function definition from.
        is_interface: bool, optional
            Boolean indicating if the function definition is part of an interface.

        Returns
        -------
        ContractFunction
        """
        kwargs: Dict[str, Any] = {}
        if is_interface:
            # FunctionDef with stateMutability in body (Interface defintions)
            if (len(node.body) == 1 and isinstance(node.body[0], vy_ast.Expr)
                    and isinstance(node.body[0].value, vy_ast.Name)
                    and StateMutability.is_valid_value(node.body[0].value.id)):
                # Interfaces are always public
                kwargs["function_visibility"] = FunctionVisibility.EXTERNAL
                kwargs["state_mutability"] = StateMutability(
                    node.body[0].value.id)
            elif len(node.body) == 1 and node.body[0].get("value.id") in (
                    "constant", "modifying"):
                if node.body[0].value.id == "constant":
                    expected = "view or pure"
                else:
                    expected = "payable or nonpayable"
                raise StructureException(
                    f"State mutability should be set to {expected}",
                    node.body[0])
            else:
                raise StructureException(
                    "Body must only contain state mutability label",
                    node.body[0])

        else:

            # FunctionDef with decorators (normal functions)
            for decorator in node.decorator_list:

                if isinstance(decorator, vy_ast.Call):
                    if "nonreentrant" in kwargs:
                        raise StructureException(
                            "nonreentrant decorator is already set with key: "
                            f"{kwargs['nonreentrant']}",
                            node,
                        )

                    if decorator.get("func.id") != "nonreentrant":
                        raise StructureException("Decorator is not callable",
                                                 decorator)
                    if len(decorator.args) != 1 or not isinstance(
                            decorator.args[0], vy_ast.Str):
                        raise StructureException(
                            "@nonreentrant name must be given as a single string literal",
                            decorator)

                    if node.name == "__init__":
                        msg = "Nonreentrant decorator disallowed on `__init__`"
                        raise FunctionDeclarationException(msg, decorator)

                    kwargs["nonreentrant"] = decorator.args[0].value

                elif isinstance(decorator, vy_ast.Name):
                    if FunctionVisibility.is_valid_value(decorator.id):
                        if "function_visibility" in kwargs:
                            raise FunctionDeclarationException(
                                f"Visibility is already set to: {kwargs['function_visibility']}",
                                node,
                            )
                        kwargs["function_visibility"] = FunctionVisibility(
                            decorator.id)

                    elif StateMutability.is_valid_value(decorator.id):
                        if "state_mutability" in kwargs:
                            raise FunctionDeclarationException(
                                f"Mutability is already set to: {kwargs['state_mutability']}",
                                node)
                        kwargs["state_mutability"] = StateMutability(
                            decorator.id)

                    else:
                        if decorator.id == "constant":
                            warnings.warn(
                                "'@constant' decorator has been removed (see VIP2040). "
                                "Use `@view` instead.",
                                DeprecationWarning,
                            )
                        raise FunctionDeclarationException(
                            f"Unknown decorator: {decorator.id}", decorator)

                else:
                    raise StructureException("Bad decorator syntax", decorator)

        if "function_visibility" not in kwargs:
            raise FunctionDeclarationException(
                f"Visibility must be set to one of: {', '.join(FunctionVisibility.values())}",
                node)

        if node.name == "__default__":
            if kwargs["function_visibility"] != FunctionVisibility.EXTERNAL:
                raise FunctionDeclarationException(
                    "Default function must be marked as `@external`", node)
            if node.args.args:
                raise FunctionDeclarationException(
                    "Default function may not receive any arguments",
                    node.args.args[0])

        if "state_mutability" not in kwargs:
            # Assume nonpayable if not set at all (cannot accept Ether, but can modify state)
            kwargs["state_mutability"] = StateMutability.NONPAYABLE

        if kwargs[
                "state_mutability"] == StateMutability.PURE and "nonreentrant" in kwargs:
            raise StructureException(
                "Cannot use reentrancy guard on pure functions", node)

        # call arguments
        if node.args.defaults and node.name == "__init__":
            raise FunctionDeclarationException(
                "Constructor may not use default arguments",
                node.args.defaults[0])

        arguments = OrderedDict()
        max_arg_count = len(node.args.args)
        min_arg_count = max_arg_count - len(node.args.defaults)
        defaults = [None] * min_arg_count + node.args.defaults

        namespace = get_namespace()
        for arg, value in zip(node.args.args, defaults):
            if arg.arg in ("gas", "value", "skip_contract_check",
                           "default_return_value"):
                raise ArgumentException(
                    f"Cannot use '{arg.arg}' as a variable name in a function input",
                    arg)
            if arg.arg in arguments:
                raise ArgumentException(
                    f"Function contains multiple inputs named {arg.arg}", arg)
            if arg.arg in namespace:
                raise NamespaceCollision(arg.arg, arg)

            if arg.annotation is None:
                raise ArgumentException(
                    f"Function argument '{arg.arg}' is missing a type", arg)

            type_definition = get_type_from_annotation(
                arg.annotation,
                location=DataLocation.CALLDATA,
                is_constant=True)
            if value is not None:
                if not check_kwargable(value):
                    raise StateAccessViolation(
                        "Value must be literal or environment variable", value)
                validate_expected_type(value, type_definition)

            arguments[arg.arg] = type_definition

        # return types
        if node.returns is None:
            return_type = None
        elif node.name == "__init__":
            raise FunctionDeclarationException(
                "Constructor may not have a return type", node.returns)
        elif isinstance(node.returns,
                        (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)):
            return_type = get_type_from_annotation(
                node.returns, location=DataLocation.MEMORY)
        elif isinstance(node.returns, vy_ast.Tuple):
            tuple_types: Tuple = ()
            for n in node.returns.elements:
                tuple_types += (get_type_from_annotation(
                    n, location=DataLocation.MEMORY), )
            return_type = TupleDefinition(tuple_types)
        else:
            raise InvalidType(
                "Function return value must be a type name or tuple",
                node.returns)

        return cls(node.name, arguments, min_arg_count, max_arg_count,
                   return_type, **kwargs)