Example #1
0
    def __include_variable(self,
                           var_id: str,
                           var_type_id: Union[str, IType],
                           source_node: ast.AST,
                           var_enumerate_type: IType = Type.none,
                           assignment: bool = True):
        """
        Includes the variable in the symbol table if the id was not used

        :param var_id: variable id
        :param var_type_id: variable type id
        :type var_type_id: str or IType
        :param var_enumerate_type: variable value type id if var_type_id is a SequenceType
        """
        if not isinstance(var_id, str) and isinstance(var_id, Iterable):
            variables = [
                var_name.id for var_name in source_node.targets[0].elts
            ]
            var_types = self.visit(source_node.value)
        else:
            variables = [var_id]
            var_types = [var_type_id]

        for x in range(min(len(variables), len(var_types))):
            var_id, var_type_id = variables[x], var_types[x]
            if var_id not in self._current_symbol_scope.symbols:
                outer_symbol = self.get_symbol(var_id)
                if outer_symbol is not None:
                    self._log_warning(
                        CompilerWarning.NameShadowing(source_node.lineno,
                                                      source_node.col_offset,
                                                      outer_symbol, var_id))

                var_type = None
                if isinstance(var_type_id, SequenceType):
                    var_type = var_type_id
                    var_enumerate_type = self.get_enumerate_type(var_type)
                elif isinstance(var_type_id, IType):
                    var_type = var_type_id

                # when setting a None value to a variable, set the variable as any type
                if var_type is Type.none:
                    var_type = Type.any

                if isinstance(var_type, IType) or var_type is None:
                    # if type is None, the variable type depends on the type of a expression
                    if isinstance(var_type, SequenceType):
                        var_type = var_type.build_collection(
                            var_enumerate_type)
                    var = Variable(var_type, origin_node=source_node)

                    self._current_symbol_scope.include_symbol(var_id, var)
                    if isinstance(source_node, ast.AnnAssign):
                        self._annotated_variables.append(var_id)
            if hasattr(self._current_scope, 'assign_variable') and assignment:
                self._global_assigned_variables.append(var_id)
Example #2
0
    def _log_using_specific_exception_warning(self, node: ast.AST):
        if node is None:
            exc_id = 'Exception'
        elif isinstance(node, ast.Name):
            exc_id = node.id
        else:
            exc_id = self.get_type(node).identifier

        self._log_warning(
            CompilerWarning.UsingSpecificException(line=node.lineno,
                                                   col=node.col_offset,
                                                   exception_id=exc_id)
        )
Example #3
0
    def visit_Call(self, node: ast.Call) -> ast.AST:
        # check if the call can be evaluated during compile time
        # TODO: right now only UInt160 and UInt256 constructors are evaluated
        literal_args = []
        args_are_literal = True

        for index, arg in enumerate(node.args.copy()):
            updated_arg = self.visit(
                arg)  # first try to optimize the arguments
            if updated_arg != arg:
                node.args[index] = updated_arg

            if args_are_literal:
                value = self.literal_eval(updated_arg)
                if value is Undefined:
                    # don't break if one argument is not literal to make sure that all arguments were checked
                    # if they can be optimized
                    args_are_literal = False

                literal_args.append(value)

        if args_are_literal:
            # try to get the result
            try:
                func_id = self.get_symbol_id(node.func)
            except BaseException:
                return node
            func = self.get_symbol(func_id)

            if isinstance(func, IBuiltinMethod):
                try:
                    result = func.evaluate_literal(*literal_args)
                    if result is not Undefined:
                        return self.parse_to_node(str(result),
                                                  node,
                                                  is_origin_str=isinstance(
                                                      result, str))
                except BaseException:
                    self._log_warning(
                        CompilerWarning.InvalidArgument(
                            node.lineno, node.col_offset))

        return node
Example #4
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