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)
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) )
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
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