Exemple #1
0
 def struct_literals(expr, name, context):
     o = {}
     members = {}
     for key, value in zip(expr.keys, expr.values):
         if not isinstance(key, ast.Name):
             raise TypeMismatchException(
                 "Invalid member variable for struct: %r" %
                 vars(key).get('id', key),
                 key,
             )
         check_valid_varname(
             key.id,
             context.custom_units,
             context.structs,
             context.constants,
             "Invalid member variable for struct",
         )
         if key.id in o:
             raise TypeMismatchException(
                 "Member variable duplicated: " + key.id, key)
         o[key.id] = Expr(value, context).lll_node
         members[key.id] = o[key.id].typ
     return LLLnode.from_list(
         ["multi"] + [o[key] for key in (list(o.keys()))],
         typ=StructType(members, name, is_literal=True),
         pos=getpos(expr),
     )
Exemple #2
0
def make_struct_type(name, location, members, custom_units, custom_structs,
                     constants):
    o = OrderedDict()
    for key, value in members:

        if not isinstance(key, ast.Name):
            raise InvalidTypeException(
                "Invalid member variable for struct %r, expected a name." %
                key.id,
                key,
            )
        check_valid_varname(
            key.id,
            custom_units,
            custom_structs,
            constants,
            "Invalid member variable for struct",
        )

        o[key.id] = parse_type(
            value,
            location,
            custom_units=custom_units,
            custom_structs=custom_structs,
            constants=constants,
        )
    return StructType(o, name)
Exemple #3
0
 def struct_literals(expr, name, context):
     member_subs = {}
     member_typs = {}
     for key, value in zip(expr.keys, expr.values):
         if not isinstance(key, vy_ast.Name):
             raise TypeMismatch(
                 f"Invalid member variable for struct: {getattr(key, 'id', '')}",
                 key,
             )
         check_valid_varname(
             key.id,
             context.structs,
             context.constants,
             "Invalid member variable for struct",
         )
         if key.id in member_subs:
             raise TypeMismatch("Member variable duplicated: " + key.id,
                                key)
         sub = Expr(value, context).lll_node
         member_subs[key.id] = sub
         member_typs[key.id] = sub.typ
     return LLLnode.from_list(
         ["multi"] + [member_subs[key] for key in member_subs.keys()],
         typ=StructType(member_typs, name, is_literal=True),
         pos=getpos(expr),
     )
Exemple #4
0
 def make_struct(self, name, body):
     members = []
     for item in body:
         if isinstance(item, ast.AnnAssign):
             member_name = item.target
             member_type = item.annotation
             # Check well-formedness of member names
             if not isinstance(member_name, ast.Name):
                 raise InvalidTypeException(
                     "Invalid member name for struct %r, needs to be a valid name. "
                     % name, item)
             check_valid_varname(member_name.id, self._custom_units,
                                 self._structs, self._constants, item,
                                 "Invalid member name for struct. ")
             # Check well-formedness of member types
             # Note this kicks out mutually recursive structs,
             # raising an exception instead of stackoverflow.
             # A struct must be defined before it is referenced.
             # This feels like a semantic step and maybe should be pushed
             # to a later compilation stage.
             parse_type(member_type,
                        'storage',
                        custom_units=self._custom_units,
                        custom_structs=self._structs,
                        constants=self._constants)
             members.append((member_name, member_type))
         else:
             raise StructureException("Structs can only contain variables",
                                      item)
     return members
Exemple #5
0
 def is_valid_varname(self, name, pos):
     # Global context check first.
     if self.global_ctx.is_valid_varname(name, pos):
         check_valid_varname(
             name, custom_structs=self.structs, pos=pos,
         )
     return True
Exemple #6
0
 def is_valid_varname(self, name, item):
     """ Valid variable name, checked against global context. """
     check_valid_varname(name, self._structs, self._constants, item)
     if name in self._globals:
         raise VariableDeclarationException(
             f'Invalid name "{name}", previously defined as global.', item)
     return True
Exemple #7
0
 def is_valid_varname(self, name, pos):
     # Global context check first.
     if self.global_ctx.is_valid_varname(name, pos):
         check_valid_varname(name, custom_units=self.custom_units, custom_structs=self.structs, constants=self.constants, pos=pos)
         # Local context duplicate context check.
         if any((name in self.vars, name in self.globals, name in self.constants)):
             raise VariableDeclarationException("Duplicate variable name: %s" % name, name)
     return True
Exemple #8
0
 def is_valid_varname(self, name, pos):
     # Global context check first.
     if self.global_ctx.is_valid_varname(name, pos):
         check_valid_varname(
             name, custom_structs=self.structs, constants=self.constants, pos=pos,
         )
         # Local context duplicate context check.
         if any((name in self.vars, name in self.globals, name in self.constants)):
             raise TypeCheckFailure(f"Duplicate variable name: {name}")
     return True
Exemple #9
0
    def from_declaration(cls, class_node, global_ctx):
        name = class_node.name
        pos = 0

        check_valid_varname(
            name,
            global_ctx._structs,
            global_ctx._constants,
            pos=class_node,
            error_prefix="Event name invalid. ",
            exc=EventDeclarationException,
        )

        args = []
        indexed_list = []
        if len(class_node.body) != 1 or not isinstance(class_node.body[0],
                                                       vy_ast.Pass):
            for node in class_node.body:
                arg_item = node.target
                arg = node.target.id
                typ = node.annotation

                if isinstance(typ,
                              vy_ast.Call) and typ.get("func.id") == "indexed":
                    indexed_list.append(True)
                    typ = typ.args[0]
                else:
                    indexed_list.append(False)
                check_valid_varname(
                    arg,
                    global_ctx._structs,
                    global_ctx._constants,
                    pos=arg_item,
                    error_prefix="Event argument name invalid or reserved.",
                )
                if arg in (x.name for x in args):
                    raise TypeCheckFailure(
                        f"Duplicate function argument name: {arg}")
                # Can struct be logged?
                parsed_type = global_ctx.parse_type(typ, None)
                args.append(VariableRecord(arg, pos, parsed_type, False))
                if isinstance(parsed_type, ByteArrayType):
                    pos += ceil32(typ.slice.value.n)
                else:
                    pos += get_size_of_type(parsed_type) * 32

        sig = (name + "(" + ",".join([
            canonicalize_type(arg.typ, indexed_list[pos])
            for pos, arg in enumerate(args)
        ]) + ")")  # noqa F812
        event_id = bytes_to_int(keccak256(bytes(sig, "utf-8")))
        return cls(name, args, indexed_list, event_id, sig)
Exemple #10
0
 def struct_literals(expr, name, context):
     member_subs = {}
     member_typs = {}
     for key, value in zip(expr.keys, expr.values):
         if not isinstance(key, vy_ast.Name):
             return
         check_valid_varname(
             key.id, context.structs, "Invalid member variable for struct",
         )
         if key.id in member_subs:
             return
         sub = Expr(value, context).lll_node
         member_subs[key.id] = sub
         member_typs[key.id] = sub.typ
     return LLLnode.from_list(
         ["multi"] + [member_subs[key] for key in member_subs.keys()],
         typ=StructType(member_typs, name, is_literal=True),
         pos=getpos(expr),
     )
Exemple #11
0
    def new_variable(self, name: str, typ: NodeType, pos: VyperNode = None) -> int:
        """
        Allocate memory for a user-defined variable.

        Arguments
        ---------
        name : str
            Name of the variable
        typ : NodeType
            Variable type, used to determine the size of memory allocation
        pos : VyperNode
            AST node corresponding to the location where the variable was created,
            used for annotating exceptions

        Returns
        -------
        int
            Memory offset for the variable
        """
        self.global_ctx.is_valid_varname(name, pos)
        check_valid_varname(name, custom_structs=self.structs, pos=pos)

        var_size = 32 * get_size_of_type(typ)
        return self._new_variable(name, typ, var_size, False)
Exemple #12
0
    def add_globals_and_events(self, item):
        item_attributes = {"public": False}

        # Make sure we have a valid variable name.
        if not isinstance(item.target, ast.Name):
            raise StructureException('Invalid global variable name',
                                     item.target)

        # Handle constants.
        if self.get_call_func_name(item) == "constant":
            self._constants.add_constant(item, global_ctx=self)
            return

        # Handle events.
        if not (self.get_call_func_name(item) == "event"):
            item_name, item_attributes = self.get_item_name_and_attributes(
                item, item_attributes)
            if not all([
                    attr in valid_global_keywords
                    for attr in item_attributes.keys()
            ]):
                raise StructureException(
                    'Invalid global keyword used: %s' % item_attributes, item)

        if item.value is not None:
            raise StructureException(
                'May not assign value whilst defining type', item)
        elif self.get_call_func_name(item) == "event":
            if self._globals or len(self._defs):
                raise EventDeclarationException(
                    "Events must all come before global declarations and function definitions",
                    item)
            self._events.append(item)
        elif not isinstance(item.target, ast.Name):
            raise StructureException(
                "Can only assign type to variable in top-level statement",
                item)

        # Is this a custom unit definition.
        elif item.target.id == 'units':
            if not self._custom_units:
                if not isinstance(item.annotation, ast.Dict):
                    raise VariableDeclarationException(
                        "Define custom units using units: { }.", item.target)
                for key, value in zip(item.annotation.keys,
                                      item.annotation.values):
                    if not isinstance(value, ast.Str):
                        raise VariableDeclarationException(
                            "Custom unit description must be a valid string",
                            value)
                    if not isinstance(key, ast.Name):
                        raise VariableDeclarationException(
                            "Custom unit name must be a valid string", key)
                    check_valid_varname(key.id, self._custom_units,
                                        self._structs, self._constants, key,
                                        "Custom unit invalid.")
                    self._custom_units.add(key.id)
                    self._custom_units_descriptions[key.id] = value.s
            else:
                raise VariableDeclarationException(
                    "Custom units can only be defined once", item.target)

        # Check if variable name is valid.
        # Don't move this check higher, as unit parsing has to happen first.
        elif not self.is_valid_varname(item.target.id, item):
            pass

        elif len(self._defs):
            raise StructureException(
                "Global variables must all come before function definitions",
                item)
        # If the type declaration is of the form public(<type here>), then proceed with
        # the underlying type but also add getters
        elif self.get_call_func_name(item) == "address":
            if item.annotation.args[0].id not in premade_contracts:
                raise VariableDeclarationException(
                    "Unsupported premade contract declaration",
                    item.annotation.args[0])
            premade_contract = premade_contracts[item.annotation.args[0].id]
            self._contracts[item.target.id] = self.make_contract(
                premade_contract.body)
            self._globals[item.target.id] = VariableRecord(
                item.target.id, len(self._globals), BaseType('address'), True)

        elif item_name in self._contracts:
            self._globals[item.target.id] = ContractRecord(
                item.target.id, len(self._globals), ContractType(item_name),
                True)
            if item_attributes["public"]:
                typ = ContractType(item_name)
                for getter in self.mk_getter(item.target.id, typ):
                    self._getters.append(
                        self.parse_line('\n' * (item.lineno - 1) + getter))
                    self._getters[-1].pos = getpos(item)

        elif self.get_call_func_name(item) == "public":
            if isinstance(item.annotation.args[0],
                          ast.Name) and item_name in self._contracts:
                typ = ContractType(item_name)
            else:
                typ = parse_type(item.annotation.args[0],
                                 'storage',
                                 custom_units=self._custom_units,
                                 custom_structs=self._structs,
                                 constants=self._constants)
            self._globals[item.target.id] = VariableRecord(
                item.target.id, len(self._globals), typ, True)
            # Adding getters here
            for getter in self.mk_getter(item.target.id, typ):
                self._getters.append(
                    self.parse_line('\n' * (item.lineno - 1) + getter))
                self._getters[-1].pos = getpos(item)

        elif isinstance(item.annotation, (ast.Name, ast.Call, ast.Subscript)):
            self._globals[item.target.id] = VariableRecord(
                item.target.id, len(self._globals),
                parse_type(item.annotation,
                           'storage',
                           custom_units=self._custom_units,
                           custom_structs=self._structs,
                           constants=self._constants), True)
        else:
            raise InvalidTypeException('Invalid global type specified', item)
Exemple #13
0
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_units=None,
                        custom_structs=None,
                        contract_def=False,
                        constants=None,
                        constant_override=False):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_units, custom_structs,
                                           constants)
        if not valid_name and (not name.lower() in FUNCTION_WHITELIST):
            raise FunctionDeclarationException("Function name invalid. " + msg,
                                               code)

        # Validate default values.
        for default_value in getattr(code.args, 'defaults', []):
            validate_default_values(default_value)

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidTypeException("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(
                arg.arg,
                custom_units,
                custom_structs,
                constants,
                arg,
                "Argument name invalid or reserved. ",
                FunctionDeclarationException,
            )
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg,
                    arg,
                )
            parsed_type = parse_type(
                typ,
                None,
                sigs,
                custom_units=custom_units,
                custom_structs=custom_structs,
                constants=constants,
            )
            args.append(
                VariableRecord(
                    arg.arg,
                    mem_pos,
                    parsed_type,
                    False,
                    defined_at=getpos(arg),
                ))

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        const = constant_override
        payable = False
        private = False
        public = False
        nonreentrant_key = ''

        # Update function properties from decorators
        for dec in code.decorator_list:
            if isinstance(dec, ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, ast.Name) and dec.id == "public":
                public = True
            elif isinstance(dec, ast.Call) and dec.func.id == "nonreentrant":
                if nonreentrant_key:
                    raise StructureException(
                        "Only one @nonreentrant decorator allowed per function",
                        dec)
                if dec.args and len(dec.args) == 1 and isinstance(
                        dec.args[0], ast.Str) and dec.args[0].s:  # noqa: E501
                    nonreentrant_key = dec.args[0].s
                else:
                    raise StructureException(
                        "@nonreentrant decorator requires a non-empty string to use as a key.",
                        dec)
            else:
                raise StructureException("Bad decorator", dec)

        if public and private:
            raise StructureException(
                f"Cannot use public and private decorators on the same function: {name}"
            )
        if payable and const:
            raise StructureException(
                f"Function {name} cannot be both constant and payable.")
        if payable and private:
            raise StructureException(
                f"Function {name} cannot be both private and payable.")
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code,
            )
        if const and nonreentrant_key:
            raise StructureException(
                "@nonreentrant makes no sense on a @constant function.", code)

        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
                code.returns,
            (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)):
            output_type = parse_type(
                code.returns,
                None,
                sigs,
                custom_units=custom_units,
                custom_structs=custom_structs,
                constants=constants,
            )
        else:
            raise InvalidTypeException(
                f"Output type invalid or unsupported: {parse_type(code.returns, None)}",
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_units,
                               custom_structs, constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private,
                   nonreentrant_key, sig, method_id, custom_units, code)
Exemple #14
0
    def add_globals_and_events(self, item):
        item_attributes = {"public": False}

        if len(self._globals) > NONRENTRANT_STORAGE_OFFSET:
            raise ParserException(
                f"Too many globals defined, only {NONRENTRANT_STORAGE_OFFSET} globals are allowed",
                item,
            )

        # Make sure we have a valid variable name.
        if not isinstance(item.target, ast.Name):
            raise StructureException('Invalid global variable name',
                                     item.target)

        # Handle constants.
        if self.get_call_func_name(item) == "constant":
            self._constants.add_constant(item, global_ctx=self)
            return

        # Handle events.
        if not (self.get_call_func_name(item) == "event"):
            item_name, item_attributes = self.get_item_name_and_attributes(
                item, item_attributes)
            if not all([
                    attr in VALID_GLOBAL_KEYWORDS
                    for attr in item_attributes.keys()
            ]):
                raise StructureException(
                    f'Invalid global keyword used: {item_attributes}', item)

        if item.value is not None:
            raise StructureException(
                'May not assign value whilst defining type', item)
        elif self.get_call_func_name(item) == "event":
            if self._globals or len(self._defs):
                raise EventDeclarationException(
                    "Events must all come before global declarations and function definitions",
                    item)
            self._events.append(item)
        elif not isinstance(item.target, ast.Name):
            raise StructureException(
                "Can only assign type to variable in top-level statement",
                item)

        # Is this a custom unit definition.
        elif item.target.id == 'units':
            if not self._custom_units:
                if not isinstance(item.annotation, ast.Dict):
                    raise VariableDeclarationException(
                        "Define custom units using units: { }.", item.target)
                for key, value in zip(item.annotation.keys,
                                      item.annotation.values):
                    if not isinstance(value, ast.Str):
                        raise VariableDeclarationException(
                            "Custom unit description must be a valid string",
                            value)
                    if not isinstance(key, ast.Name):
                        raise VariableDeclarationException(
                            "Custom unit name must be a valid string", key)
                    check_valid_varname(key.id, self._custom_units,
                                        self._structs, self._constants, key,
                                        "Custom unit invalid.")
                    self._custom_units.add(key.id)
                    self._custom_units_descriptions[key.id] = value.s
            else:
                raise VariableDeclarationException(
                    "Custom units can only be defined once", item.target)

        # Check if variable name is valid.
        # Don't move this check higher, as unit parsing has to happen first.
        elif not self.is_valid_varname(item.target.id, item):
            pass

        elif len(self._defs):
            raise StructureException(
                "Global variables must all come before function definitions",
                item,
            )

        elif item_name in self._contracts or item_name in self._interfaces:
            if self.get_call_func_name(item) == "address":
                raise StructureException(
                    f"Persistent address({item_name}) style contract declarations "
                    "are not support anymore."
                    f" Use {item.target.id}: {item_name} instead")
            self._globals[item.target.id] = ContractRecord(
                item.target.id,
                len(self._globals),
                ContractType(item_name),
                True,
            )
            if item_attributes["public"]:
                typ = ContractType(item_name)
                for getter in self.mk_getter(item.target.id, typ):
                    self._getters.append(
                        self.parse_line('\n' * (item.lineno - 1) + getter))
                    self._getters[-1].pos = getpos(item)
        elif self.get_call_func_name(item) == "public":
            if isinstance(item.annotation.args[0],
                          ast.Name) and item_name in self._contracts:
                typ = ContractType(item_name)
            else:
                typ = parse_type(
                    item.annotation.args[0],
                    'storage',
                    custom_units=self._custom_units,
                    custom_structs=self._structs,
                    constants=self._constants,
                )
            self._globals[item.target.id] = VariableRecord(
                item.target.id,
                len(self._globals),
                typ,
                True,
            )
            # Adding getters here
            for getter in self.mk_getter(item.target.id, typ):
                self._getters.append(
                    self.parse_line('\n' * (item.lineno - 1) + getter))
                self._getters[-1].pos = getpos(item)

        elif isinstance(item.annotation, (ast.Name, ast.Call, ast.Subscript)):
            self._globals[item.target.id] = VariableRecord(
                item.target.id, len(self._globals),
                parse_type(item.annotation,
                           'storage',
                           custom_units=self._custom_units,
                           custom_structs=self._structs,
                           constants=self._constants), True)
        else:
            raise InvalidTypeException('Invalid global type specified', item)
Exemple #15
0
    def from_declaration(cls, code, global_ctx):
        name = code.target.id
        pos = 0

        check_valid_varname(
            name, global_ctx._custom_units, global_ctx._structs, global_ctx._constants,
            pos=code, error_prefix="Event name invalid. ", exc=EventDeclarationException
        )

        # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ...
        args = []
        indexed_list = []
        topics_count = 1
        if code.annotation.args:
            keys = code.annotation.args[0].keys
            values = code.annotation.args[0].values
            for i in range(len(keys)):
                typ = values[i]
                if not isinstance(keys[i], ast.Name):
                    raise EventDeclarationException(
                        'Invalid key type, expected a valid name.',
                        keys[i],
                    )
                if not isinstance(typ, (ast.Name, ast.Call, ast.Subscript)):
                    raise EventDeclarationException('Invalid event argument type.', typ)
                if isinstance(typ, ast.Call) and not isinstance(typ.func, ast.Name):
                    raise EventDeclarationException('Invalid event argument type', typ)
                arg = keys[i].id
                arg_item = keys[i]
                is_indexed = False

                # Check to see if argument is a topic
                if isinstance(typ, ast.Call) and typ.func.id == 'indexed':
                    typ = values[i].args[0]
                    indexed_list.append(True)
                    topics_count += 1
                    is_indexed = True
                else:
                    indexed_list.append(False)
                if isinstance(typ, ast.Subscript) and getattr(typ.value, 'id', None) == 'bytes' and typ.slice.value.n > 32 and is_indexed:  # noqa: E501
                    raise EventDeclarationException("Indexed arguments are limited to 32 bytes")
                if topics_count > 4:
                    raise EventDeclarationException(
                        "Maximum of 3 topics {} given".format(topics_count - 1),
                        arg,
                    )
                if not isinstance(arg, str):
                    raise VariableDeclarationException("Argument name invalid", arg)
                if not typ:
                    raise InvalidTypeException("Argument must have type", arg)
                check_valid_varname(
                    arg,
                    global_ctx._custom_units,
                    global_ctx._structs,
                    global_ctx._constants,
                    pos=arg_item,
                    error_prefix="Event argument name invalid or reserved.",
                )
                if arg in (x.name for x in args):
                    raise VariableDeclarationException(
                        "Duplicate function argument name: " + arg,
                        arg_item,
                    )
                # Can struct be logged?
                parsed_type = global_ctx.parse_type(typ, None)
                args.append(VariableRecord(arg, pos, parsed_type, False))
                if isinstance(parsed_type, ByteArrayType):
                    pos += ceil32(typ.slice.value.n)
                else:
                    pos += get_size_of_type(parsed_type) * 32
        sig = name + '(' + ','.join([canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args)]) + ')'  # noqa F812
        event_id = bytes_to_int(sha3(bytes(sig, 'utf-8')))
        return cls(name, args, indexed_list, event_id, sig)
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_units=None,
                        custom_structs=None,
                        contract_def=False,
                        constants=None,
                        constant=False):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_units, custom_structs,
                                           constants)
        if not valid_name and (not name.lower() in function_whitelist):
            raise FunctionDeclarationException("Function name invalid. " + msg,
                                               code)

        # Determine the arguments, expects something of the form def foo(arg1: int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidTypeException("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(arg.arg, custom_units, custom_structs,
                                constants, arg,
                                "Argument name invalid or reserved. ",
                                FunctionDeclarationException)
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg, arg)
            parsed_type = parse_type(typ,
                                     None,
                                     sigs,
                                     custom_units=custom_units,
                                     custom_structs=custom_structs,
                                     constants=constants)
            args.append(
                VariableRecord(arg.arg,
                               mem_pos,
                               parsed_type,
                               False,
                               defined_at=getpos(arg)))

            if isinstance(parsed_type, ByteArrayType):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        # Apply decorators
        const, payable, private, public = False, False, False, False
        for dec in code.decorator_list:
            if isinstance(dec, ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, ast.Name) and dec.id == "public":
                public = True
            else:
                raise StructureException("Bad decorator", dec)

        if public and private:
            raise StructureException(
                "Cannot use public and private decorators on the same function: {}"
                .format(name))
        if payable and const:
            raise StructureException(
                "Function {} cannot be both constant and payable.".format(
                    name))
        if payable and private:
            raise StructureException(
                "Function {} cannot be both private and payable.".format(name))
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code)
        if constant:
            const = True
        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
                code.returns,
            (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)):
            output_type = parse_type(code.returns,
                                     None,
                                     sigs,
                                     custom_units=custom_units,
                                     custom_structs=custom_structs,
                                     constants=constants)
        else:
            raise InvalidTypeException(
                "Output type invalid or unsupported: %r" %
                parse_type(code.returns, None),
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_units,
                               custom_structs, constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private, sig,
                   method_id, custom_units)
Exemple #17
0
    def from_definition(
        cls,
        code,
        sigs=None,
        custom_structs=None,
        interface_def=False,
        constants=None,
        constant_override=False,
        is_from_json=False,
    ):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_structs, constants)
        if not valid_name and (not name.lower() in FUNCTION_WHITELIST):
            raise FunctionDeclarationException("Function name invalid. " + msg, code)

        # Validate default values.
        for default_value in getattr(code.args, "defaults", []):
            validate_default_values(default_value)

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidType("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(
                arg.arg,
                custom_structs,
                constants,
                arg,
                "Argument name invalid or reserved. ",
                FunctionDeclarationException,
            )
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg, arg,
                )
            parsed_type = parse_type(
                typ, None, sigs, custom_structs=custom_structs, constants=constants,
            )
            args.append(
                VariableRecord(arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg),)
            )

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        mutability = "nonpayable"  # Assume nonpayable by default
        internal = False
        external = False
        nonreentrant_key = ""

        # Update function properties from decorators
        # NOTE: Can't import enums here because of circular import
        for dec in code.decorator_list:
            if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view", "pure"):
                mutability = dec.id
            elif isinstance(dec, vy_ast.Name) and dec.id == "internal":
                internal = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "external":
                external = True
            elif isinstance(dec, vy_ast.Call) and dec.func.id == "nonreentrant":
                if nonreentrant_key:
                    raise StructureException(
                        "Only one @nonreentrant decorator allowed per function", dec
                    )
                if (
                    dec.args
                    and len(dec.args) == 1
                    and isinstance(dec.args[0], vy_ast.Str)
                    and dec.args[0].s
                ):  # noqa: E501
                    nonreentrant_key = dec.args[0].s
                else:
                    raise StructureException(
                        "@nonreentrant decorator requires a non-empty string to use as a key.", dec
                    )
            else:
                raise StructureException("Bad decorator", dec)

        if constant_override:
            # In case this override is abused, match previous behavior
            if mutability == "payable":
                raise StructureException(f"Function {name} cannot be both constant and payable.")
            mutability = "view"

        if external and internal:
            raise StructureException(
                f"Cannot use external and internal decorators on the same function: {name}"
            )
        if mutability == "payable" and internal:
            raise StructureException(f"Function {name} cannot be both internal and payable.")
        if (not external and not internal) and not interface_def:
            raise StructureException(
                "Function visibility must be declared (@external or @internal)", code,
            )
        if mutability in ("view", "pure") and nonreentrant_key:
            raise StructureException(
                f"@nonreentrant makes no sense on a @{mutability} function.", code
            )

        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
            code.returns, (vy_ast.Name, vy_ast.Compare, vy_ast.Subscript, vy_ast.Call, vy_ast.Tuple)
        ):
            output_type = parse_type(
                code.returns, None, sigs, custom_structs=custom_structs, constants=constants,
            )
        else:
            raise InvalidType(
                f"Output type invalid or unsupported: {parse_type(code.returns, None)}",
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type, TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs, constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4])
        return cls(
            name,
            args,
            output_type,
            mutability,
            internal,
            nonreentrant_key,
            sig,
            method_id,
            code,
            is_from_json,
        )
 def is_valid_varname(self, name, item):
     """ Valid variable name, checked against global context. """
     check_valid_varname(name, self._structs, item)
     return True