Example #1
0
    def create_new_event(self, create_call: ast.Call) -> Event:
        event = Event('')
        event_args = create_call.args

        if len(event_args) < 0:
            self._log_error(
                CompilerError.UnfilledArgument(line=create_call.lineno,
                                               col=create_call.col_offset,
                                               param=list(
                                                   Builtin.NewEvent.args)[0]))
        elif len(event_args) > 0:
            args_type = self.get_type(event_args[0])
            if not Type.list.is_type_of(args_type):
                self._log_error(
                    CompilerError.MismatchedTypes(
                        line=event_args[0].lineno,
                        col=event_args[0].col_offset,
                        expected_type_id=Type.list.identifier,
                        actual_type_id=args_type.identifier))
            else:
                for value in event_args[0].elts:
                    if not isinstance(value, ast.Tuple):
                        CompilerError.MismatchedTypes(
                            line=value.lineno,
                            col=value.col_offset,
                            expected_type_id=Type.tuple.identifier,
                            actual_type_id=self.get_type(value).identifier)
                    elif len(value.elts) < 2:
                        self._log_error(
                            CompilerError.UnfilledArgument(
                                line=value.lineno,
                                col=value.col_offset,
                                param=list(Builtin.NewEvent.args)[0]))
                    elif not (isinstance(value.elts[0], ast.Str) and
                              (isinstance(value.elts[1], ast.Name)
                               and isinstance(
                                   self.get_symbol(value.elts[1].id), IType))):
                        CompilerError.MismatchedTypes(
                            line=value.lineno,
                            col=value.col_offset,
                            expected_type_id=Type.tuple.identifier,
                            actual_type_id=self.get_type(value).identifier)
                    else:
                        arg_name = value.elts[0].s
                        arg_type = self.get_symbol(value.elts[1].id)
                        event.args[arg_name] = Variable(arg_type)

            if len(event_args) > 1:
                if not isinstance(event_args[1], ast.Str):
                    name_type = self.get_type(event_args[1])
                    CompilerError.MismatchedTypes(
                        line=event_args[1].lineno,
                        col=event_args[1].col_offset,
                        expected_type_id=Type.str.identifier,
                        actual_type_id=name_type.identifier)
                else:
                    event.name = event_args[1].s

        return event
Example #2
0
    def validate_values(self, *params: Any) -> List[Any]:
        values = []
        if len(params) != 2:
            return values

        origin, visitor = params
        from boa3.analyser.astanalyser import IAstAnalyser
        if not isinstance(origin, ast.Call) or not isinstance(
                visitor, IAstAnalyser):
            return [self.name, self.safe]

        # read the called arguments
        args_names = list(self.args.keys())
        args_len = min(len(origin.args), len(args_names))
        for x in range(args_len):
            values.append(visitor.visit(origin.args[x]))

        if len(values) <= 1:
            if len(values) == 0:
                values.append('')
            values.append(self.safe)

        # read the called keyword arguments
        for kwarg in origin.keywords:
            if kwarg.arg in args_names:
                x = args_names.index(kwarg.arg)
                value = visitor.visit(kwarg.value)
                values[x] = value

        from boa3.exception import CompilerError
        name, safe = values
        if not isinstance(name, str):
            visitor._log_error(
                CompilerError.MismatchedTypes(
                    origin.lineno,
                    origin.col_offset,
                    expected_type_id=Type.str.identifier,
                    actual_type_id=type(name).__name__))

        if not isinstance(safe, bool):
            visitor._log_error(
                CompilerError.MismatchedTypes(
                    origin.lineno,
                    origin.col_offset,
                    expected_type_id=Type.bool.identifier,
                    actual_type_id=type(safe).__name__))

        return values
Example #3
0
    def validate_type_variable_assign(self, node: ast.AST, value: Any, target: Any = None) -> bool:
        value_type: IType = self.get_type(value)
        if isinstance(value, ast.AST):
            value = self.visit(value)

        if target is not None:
            target_type = self.get_type(target)
        elif not isinstance(node, ast.Name):
            target_type = self.get_type(node)
        else:
            var: ISymbol = self.get_symbol(node.id)
            if not isinstance(var, Variable):
                self._log_error(
                    CompilerError.UnresolvedReference(
                        node.lineno, node.col_offset,
                        symbol_id=node.id
                    ))
                return False
            if var.type is None:
                # it is an declaration with assignment and the value is neither literal nor another variable
                var.set_type(value_type)
            target_type = var.type

        if not target_type.is_type_of(value_type) and value != target_type.default_value:
            self._log_error(
                CompilerError.MismatchedTypes(
                    node.lineno, node.col_offset,
                    actual_type_id=value_type.identifier,
                    expected_type_id=target_type.identifier
                ))
            return False

        return True
Example #4
0
    def get_bin_op(self, operator: Operator, right: Any, left: Any) -> IOperation:
        """
        Returns the binary operation specified by the operator and the types of the operands

        :param operator: the operator
        :param right: right operand
        :param left: left operand

        :return: Returns the corresponding :class:`BinaryOperation` if the types are valid.
        :raise MismatchedTypes: raised if the types aren't valid for the operator
        """
        l_type: IType = self.get_type(left)
        r_type: IType = self.get_type(right)

        if l_type is None or r_type is None:
            return BinaryOp.get_operation_by_operator(operator, l_type if l_type is not None else r_type)

        actual_types = (l_type.identifier, r_type.identifier)
        operation: IOperation = BinaryOp.validate_type(operator, l_type, r_type)

        if operation is not None:
            return operation
        else:
            expected_op: BinaryOperation = BinaryOp.get_operation_by_operator(operator, l_type)
            expected_types = (expected_op.left_type.identifier, expected_op.right_type.identifier)
            raise CompilerError.MismatchedTypes(0, 0, expected_types, actual_types)
Example #5
0
    def _validate_IsInstanceMethod(self, method: IsInstanceMethod,
                                   args_types: List[IType]):
        """
        Validates the arguments for `isinstance` method

        :param method: instance of the builtin method
        :param args_types: types of the arguments
        """
        last_arg = self.call.args[-1]
        if isinstance(last_arg, ast.Tuple) and all(
                isinstance(tpe, ast.Name) for tpe in last_arg.elts):
            if len(last_arg.elts) == 1:
                # if the types tuple has only one type, remove it from inside the tuple
                last_arg = self.call.args[-1] = last_arg.elts[-1]
                args_types[-1] = args_types[-1].value_type
            elif len(last_arg.elts) > 1:
                # if there are more than one type, updates information in the instance of the method
                types: List[IType] = [
                    self.get_symbol(name.id) for name in last_arg.elts
                ]
                method.set_instance_type(types)
                self.call.args[-1] = last_arg.elts[-1]
                return

        if not isinstance(last_arg, ast.Name) or (
                last_arg.id != args_types[-1].identifier
                and last_arg.id != args_types[-1].raw_identifier):
            # if the value is not the identifier of a type
            self._log_error(
                CompilerError.MismatchedTypes(
                    last_arg.lineno,
                    last_arg.col_offset,
                    expected_type_id=type.__name__,
                    actual_type_id=args_types[-1].identifier))
Example #6
0
    def visit_ExceptHandler(self, node: ast.ExceptHandler):
        """
        Visitor of the try except node

        :param node: the python ast try except node
        """
        logged_errors = False

        exception_type: IType = self.get_type(node.type)
        if node.type is not None and exception_type is not Type.exception:
            self._log_error(
                CompilerError.MismatchedTypes(line=node.type.lineno,
                                              col=node.type.col_offset,
                                              expected_type_id=Type.exception.identifier,
                                              actual_type_id=exception_type.identifier)
            )
            logged_errors = True

        if node.name is not None:
            # TODO: remove when getting the exception is implemented
            self._log_error(
                CompilerError.NotSupportedOperation(line=node.lineno,
                                                    col=node.col_offset,
                                                    symbol_id='naming exceptions')
            )
            logged_errors = True

        if not logged_errors and node.type is not None:
            self._log_using_specific_exception_warning(node.type)

        self.generic_visit(node)
Example #7
0
    def visit_For(self, for_node: ast.For):
        """
        Verifies if the type of for iterator is valid

        :param for_node: the python ast for node
        """
        iterator = self.visit(for_node.iter)
        iterator_type: IType = self.get_type(iterator)

        if not isinstance(iterator_type, Collection):
            self._log_error(
                CompilerError.MismatchedTypes(
                    for_node.lineno, for_node.col_offset,
                    actual_type_id=iterator_type.identifier,
                    expected_type_id=Type.sequence.identifier)
            )

        # TODO: change when optimizing for loops
        elif self.get_type(for_node.target) != iterator_type.item_type:
            target_id = self.visit(for_node.target)
            if isinstance(target_id, ast.Name):
                target_id = target_id.id
            symbol = self.get_symbol(target_id)
            symbol.set_type(iterator_type.item_type)

        # continue to walk through the tree
        for stmt in for_node.body:
            self.visit(stmt)
        for stmt in for_node.orelse:
            self.visit(stmt)
Example #8
0
    def validate_slice(self, subscript: ast.Subscript, slice_node: ast.Slice) -> IType:
        """
        Verifies if the subscribed value is a sequence and if the slice is valid to this sequence

        :param subscript: the python ast subscript node
        :param slice_node: the subscript slice
        :return: the type of the accessed value if it is valid. Type.none otherwise.
        """
        value = self.visit(subscript.value)
        lower, upper, step = (self.get_type(value) for value in self.visit(slice_node))

        if step is not Type.none:
            # TODO: remove when slices with stride are implemented
            raise NotImplementedError
        # is not allowed to store into a slice
        if isinstance(subscript.ctx, ast.Store):
            self._log_error(
                CompilerError.NotSupportedOperation(
                    subscript.lineno, subscript.col_offset,
                    symbol_id=Operator.Subscript
                )
            )

        symbol_type: IType = self.get_type(value)
        # only collection types can be subscribed
        if not isinstance(symbol_type, Collection):
            self._log_error(
                CompilerError.UnresolvedOperation(
                    subscript.lineno, subscript.col_offset,
                    type_id=symbol_type.identifier,
                    operation_id=Operator.Subscript)
            )
            return Type.none

        lower = lower if lower is not Type.none else symbol_type.valid_key
        upper = upper if upper is not Type.none else symbol_type.valid_key

        # TODO: remove when slices of other sequence types are implemented
        if (not symbol_type.is_valid_key(lower)
                or not symbol_type.is_valid_key(upper)
                or (step is not Type.none and not symbol_type.is_valid_key(step))
            ):
            actual: Tuple[IType, ...] = (lower, upper) if step is Type.none else (lower, upper, step)
            self._log_error(
                CompilerError.MismatchedTypes(
                    subscript.lineno, subscript.col_offset,
                    expected_type_id=[symbol_type.valid_key.identifier for value in actual],
                    actual_type_id=[value.identifier for value in actual]
                )
            )
        else:
            return symbol_type

        return Type.none
Example #9
0
    def validate_get_or_set(self, subscript: ast.Subscript, index_node: ast.Index) -> IType:
        """
        Verifies if the subscribed value is a sequence and if the index is valid to this sequence

        :param subscript: the python ast subscript node
        :param index_node: the subscript index
        :return: the type of the accessed value if it is valid. Type.none otherwise.
        """
        value = self.visit(subscript.value)
        index = self.visit(index_node)

        if isinstance(value, ast.Name):
            value = self.get_symbol(value.id)
        if isinstance(index, ast.Name):
            index = self.get_symbol(index.id)
        if not isinstance(index, tuple):
            index = (index,)

        # if it is a type hint, returns the outer type
        if isinstance(value, IType) and all(isinstance(i, IType) for i in index):
            return value

        symbol_type: IType = self.get_type(value)
        index_type: IType = self.get_type(index[0])

        # only sequence types can be subscribed
        if not isinstance(symbol_type, Collection):
            self._log_error(
                CompilerError.UnresolvedOperation(
                    subscript.lineno, subscript.col_offset,
                    type_id=symbol_type.identifier,
                    operation_id=Operator.Subscript)
            )
            return symbol_type
        # the sequence can't use the given type as index
        if not symbol_type.is_valid_key(index_type):
            self._log_error(
                CompilerError.MismatchedTypes(
                    subscript.lineno, subscript.col_offset,
                    actual_type_id=index_type.identifier,
                    expected_type_id=symbol_type.valid_key.identifier)
            )
        # it is setting a value in a sequence that doesn't allow reassign values
        elif isinstance(subscript.ctx, ast.Store) and not symbol_type.can_reassign_values:
            self._log_error(
                CompilerError.UnresolvedOperation(
                    subscript.lineno, subscript.col_offset,
                    type_id=symbol_type.identifier,
                    operation_id=Operator.Subscript)
            )
        return symbol_type.item_type
    def _validate_IsInstanceMethod(self, method: IsInstanceMethod,
                                   args_types: List[IType]):
        """
        Validates the arguments for `isinstance` method

        :param method: instance of the builtin method
        :param args_types: types of the arguments
        """
        last_arg = self.call.args[-1]
        if isinstance(last_arg, ast.Tuple) and all(
                isinstance(tpe, (ast.Attribute, ast.Name))
                for tpe in last_arg.elts):
            if len(last_arg.elts) == 1:
                # if the types tuple has only one type, remove it from inside the tuple
                last_arg = self.call.args[-1] = last_arg.elts[-1]
                args_types[-1] = args_types[-1].value_type
            elif len(last_arg.elts) > 1:
                # if there are more than one type, updates information in the instance of the method
                types: List[IType] = [
                    self.get_symbol_from_node(name) for name in last_arg.elts
                ]
                method.set_instance_type(types)
                self.call.args[-1] = last_arg.elts[-1]
                return

        from boa3.model.type.annotation.metatype import MetaType
        from boa3.model.type.type import Type
        is_ast_valid = (isinstance(last_arg, ast.Name)
                        or (isinstance(last_arg, ast.NameConstant)
                            and args_types[-1] is Type.none))

        is_id_valid = (hasattr(last_arg, 'id')
                       and last_arg.id != args_types[-1].identifier
                       and last_arg.id != args_types[-1].raw_identifier
                       and isinstance(
                           self.get_type(last_arg, use_metadata=True),
                           MetaType))

        if not is_ast_valid or is_id_valid:
            # if the value is not the identifier of a type
            self._log_error(
                CompilerError.MismatchedTypes(
                    last_arg.lineno,
                    last_arg.col_offset,
                    expected_type_id=type.__name__,
                    actual_type_id=args_types[-1].identifier))
Example #11
0
    def visit_Raise(self, raise_node: ast.Raise):
        """
        Visitor of the raise node

        Verifies if the raised object is a exception

        :param raise_node: the python ast raise node
        """
        raised = self.visit(raise_node.exc)
        raised_type: IType = self.get_type(raised)
        if raised_type is not Type.exception:
            self._log_error(
                CompilerError.MismatchedTypes(
                    raise_node.lineno, raise_node.col_offset,
                    actual_type_id=raised_type.identifier,
                    expected_type_id=Type.exception.identifier
                ))
Example #12
0
    def validate_if(self, if_node: ast.AST):
        """
        Verifies if the type of if test is valid

        :param if_node: the python ast if statement node
        :type if_node: ast.If or ast.IfExp
        """
        test = self.visit(if_node.test)
        test_type: IType = self.get_type(test)

        if test_type is not Type.bool:
            self._log_error(
                CompilerError.MismatchedTypes(
                    if_node.lineno, if_node.col_offset,
                    actual_type_id=test_type.identifier,
                    expected_type_id=Type.bool.identifier)
            )
Example #13
0
    def validate_passed_arguments(self, call: ast.Call, args_types: List[IType], callable_id: str, callable: Callable):
        if isinstance(callable, IBuiltinMethod):
            builtin_analyser = BuiltinFunctionCallAnalyser(self, call, callable_id, callable)
            if builtin_analyser.validate():
                self.errors.extend(builtin_analyser.errors)
                self.warnings.extend(builtin_analyser.warnings)
                return

        for index, (arg_id, arg_value) in enumerate(callable.args.items()):
            param = call.args[index]
            param_type = self.get_type(param)
            args_types.append(param_type)
            if not arg_value.type.is_type_of(param_type):
                self._log_error(
                    CompilerError.MismatchedTypes(
                        param.lineno, param.col_offset,
                        arg_value.type.identifier,
                        param_type.identifier
                    ))
Example #14
0
    def get_un_op(self, operator: Operator, operand: Any) -> UnaryOperation:
        """
        Returns the binary operation specified by the operator and the types of the operands

        :param operator: the operator
        :param operand: the operand

        :return: Returns the corresponding :class:`UnaryOperation` if the types are valid.
        :raise MismatchedTypes: raised if the types aren't valid for the operator
        """
        op_type: IType = self.get_type(operand)

        actual_type: str = op_type.identifier
        operation: UnaryOperation = UnaryOp.validate_type(operator, op_type)

        if operation is not None:
            return operation
        else:
            expected_op: UnaryOperation = UnaryOp.get_operation_by_operator(operator)
            expected_type: str = expected_op.operand_type.identifier
            raise CompilerError.MismatchedTypes(0, 0, expected_type, actual_type)
Example #15
0
    def visit_While(self, while_node: ast.While):
        """
        Verifies if the type of while test is valid

        :param while_node: the python ast while statement node
        """
        test = self.visit(while_node.test)
        test_type: IType = self.get_type(test)

        if test_type is not Type.bool:
            self._log_error(
                CompilerError.MismatchedTypes(
                    while_node.lineno, while_node.col_offset,
                    actual_type_id=test_type.identifier,
                    expected_type_id=Type.bool.identifier)
            )

        # continue to walk through the tree
        for stmt in while_node.body:
            self.visit(stmt)
        for stmt in while_node.orelse:
            self.visit(stmt)
Example #16
0
    def visit_FunctionDef(self, function: ast.FunctionDef):
        """
        Visitor of the function node

        Performs the type checking in the body of the function

        :param function: the python ast function definition node
        """
        self.visit(function.args)
        method = self.symbols[function.name]
        from boa3.model.event import Event
        if isinstance(method, Method):
            self._current_method = method

            for stmt in function.body:
                self.visit(stmt)

            self._validate_return(function)

            if (len(function.body) > 0
                    and not isinstance(function.body[-1], ast.Return)
                    and method.return_type is Type.none):
                # include return None in void functions
                default_value: str = str(method.return_type.default_value)
                node: ast.AST = ast.parse(default_value).body[0].value
                function.body.append(
                    ast.Return(lineno=function.lineno, col_offset=function.col_offset, value=node)
                )
            self._current_method = None
        elif (isinstance(method, Event)  # events don't have return
              and function.returns is not None):
            return_type = self.get_type(function.returns)
            if return_type is not Type.none:
                self._log_error(
                    CompilerError.MismatchedTypes(line=function.lineno, col=function.col_offset,
                                                  expected_type_id=Type.none.identifier,
                                                  actual_type_id=return_type.identifier)
                )
Example #17
0
    def _read_metadata_object(self, function: ast.FunctionDef):
        """
        Gets the metadata object defined in this function

        :param function:
        """
        if self._metadata is not None:
            # metadata function has been defined already
            self._log_warning(
                CompilerWarning.RedeclaredSymbol(
                    line=function.lineno,
                    col=function.col_offset,
                    symbol_id=Builtin.Metadata.identifier))
        # this function must have a return and no arguments
        elif len(function.args.args) != 0:
            self._log_error(
                CompilerError.UnexpectedArgument(line=function.lineno,
                                                 col=function.col_offset))
        elif not any(isinstance(stmt, ast.Return) for stmt in function.body):
            self._log_error(
                CompilerError.MissingReturnStatement(line=function.lineno,
                                                     col=function.col_offset,
                                                     symbol_id=function.name))
        else:
            function.returns = None
            function.decorator_list = []
            module: ast.Module = ast.parse('')
            module.body = [
                node for node in self._tree.body
                if isinstance(node, (ast.ImportFrom, ast.Import))
            ]
            module.body.append(function)
            ast.copy_location(module, function)

            # executes the function
            code = compile(module, filename='<boa3>', mode='exec')
            namespace = {}
            exec(code, namespace)
            obj: Any = namespace[function.name]()

            node: ast.AST = function.body[-1] if len(
                function.body) > 0 else function
            # return must be a NeoMetadata object
            if not isinstance(obj, NeoMetadata):
                obj_type = self.get_type(obj).identifier if self.get_type(
                    obj) is not Type.any else type(obj).__name__
                self._log_error(
                    CompilerError.MismatchedTypes(
                        line=node.lineno,
                        col=node.col_offset,
                        expected_type_id=NeoMetadata.__name__,
                        actual_type_id=obj_type))
                return

            # validates the metadata attributes types
            attributes: Dict[str, Any] = {
                attr: value
                for attr, value in dict(obj.__dict__).items()
                if attr in Builtin.metadata_fields
            }
            if any(not isinstance(value, Builtin.metadata_fields[attr])
                   for attr, value in attributes.items()):
                for expected, actual in [
                    (Builtin.metadata_fields[attr], type(v_type))
                        for attr, v_type in attributes.items() if
                        not isinstance(v_type, Builtin.metadata_fields[attr])
                ]:
                    if isinstance(expected, Iterable):
                        expected_id = 'Union[{0}]'.format(', '.join(
                            [tpe.__name__ for tpe in expected]))
                    elif hasattr(expected, '__name__'):
                        expected_id = expected.__name__
                    else:
                        expected_id = str(expected)

                    self._log_error(
                        CompilerError.MismatchedTypes(
                            line=node.lineno,
                            col=node.col_offset,
                            expected_type_id=expected_id,
                            actual_type_id=actual.__name__))
            else:
                # if the function was defined correctly, sets the metadata object of the smart contract
                self._metadata = obj
                self._metadata_node = function  # for error messages only