def __init__(self, user_class: UserClass): self_var = Variable(user_class) args = {'self': self_var} base_init = None if len(user_class.bases) == 1: # TODO: change when class inheritance with multiple bases is implemented # use update to keep the original order base_init = user_class.bases[0].constructor_method() args.update(base_init.args) # but change the self type args['self'] = self_var Method.__init__(self, args=args, return_type=user_class) IdentifiedSymbol.__init__(self, identifier=constants.INIT_METHOD_ID) self.defined_by_entry = False self.is_init = True self.origin_class = user_class # __init__ method behave like class methods from boa3.model.builtin.builtin import Builtin self.decorators.append(Builtin.ClassMethodDecorator) if base_init is not None: origin = self.origin if self.origin else self base_init.add_call_origin(origin)
def initialize_static_fields(self): """ Converts the signature of the method :return: whether there are static fields to be initialized """ if self.initialized_static_fields: return False num_static_fields = len(self._globals) if num_static_fields > 0: init_data = bytearray([num_static_fields]) self.__insert1(OpcodeInfo.INITSSLOT, init_data) from boa3.constants import INITIALIZE_METHOD_ID if INITIALIZE_METHOD_ID in self.symbol_table: from boa3.helpers import get_auxiliary_name method = self.symbol_table.pop(INITIALIZE_METHOD_ID) new_id = get_auxiliary_name(INITIALIZE_METHOD_ID, method) self.symbol_table[new_id] = method init_method = Method(is_public=True) init_method.init_bytecode = self.last_code self.symbol_table[INITIALIZE_METHOD_ID] = init_method return num_static_fields > 0
def convert_begin_method(self, method: Method): """ Converts the signature of the method :param method: method that is being converted """ num_args: int = len(method.args) num_vars: int = len(method.locals) method.init_address = VMCodeMapping.instance().bytecode_size if num_args > 0 or num_vars > 0: init_data = bytearray([num_vars, num_args]) self.__insert1(OpcodeInfo.INITSLOT, init_data) method.init_bytecode = self.last_code self._current_method = method
def _get_method_debug_info(self, method_id: str, method: Method) -> Dict[str, Any]: from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping return { "id": str(id(method)), "name": ',{0}'.format(method_id), # TODO: include module name "range": '{0}-{1}'.format(method.start_address, method.end_address), "params": [ '{0},{1}'.format(name, var.type.abi_type) for name, var in method.args.items() ], "return": method.return_type.abi_type, "variables": [ '{0},{1}'.format(name, var.type.abi_type) for name, var in method.locals.items() ], "sequence-points": [ '{0}[{1}]{2}:{3}-{4}:{5}'.format( VMCodeMapping.instance().get_start_address( instruction.code), self._get_method_origin_index(method), instruction.start_line, instruction.start_col, instruction.end_line, instruction.end_col) for instruction in method.debug_map() ] }
def visit_FunctionDef(self, function: ast.FunctionDef): """ Visitor of the function node Includes the method in the scope of its module :param function: """ fun_args = self.visit(function.args) fun_rtype_symbol = self.visit( function.returns) if function.returns is not None else Type.none if fun_rtype_symbol is None: # it is a function with None return: Main(a: int) -> None: raise NotImplementedError if isinstance(fun_rtype_symbol, str): symbol = self.get_symbol(function.returns.id) fun_rtype_symbol = self.get_type(symbol) fun_return: IType = self.get_type(fun_rtype_symbol) fun_decorators: List[Method] = self._get_function_decorators(function) if Builtin.Metadata in fun_decorators: self._read_metadata_object(function) return Builtin.Metadata method = Method(args=fun_args, defaults=function.args.defaults, return_type=fun_return, origin_node=function, is_public=Builtin.Public in fun_decorators) self._current_method = method # don't evaluate constant expression - for example: string for documentation from boa3.constants import SYS_VERSION_INFO if SYS_VERSION_INFO >= (3, 8): function.body = [ stmt for stmt in function.body if not (isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant)) ] else: function.body = [ stmt for stmt in function.body if not (isinstance(stmt, ast.Expr) and (hasattr(stmt.value, 'n') or hasattr(stmt.value, 's'))) ] for stmt in function.body: self.visit(stmt) self.__include_callable(function.name, method) self._current_method = None
def visit_FunctionDef(self, function: ast.FunctionDef): """ Visitor of the function node Includes the method in the scope of its module :param function: """ fun_args = self.visit(function.args) fun_rtype_symbol = self.visit(function.returns) if function.returns is not None else Type.none if fun_rtype_symbol is None: # it is a function with None return: Main(a: int) -> None: raise NotImplementedError if isinstance(fun_rtype_symbol, str): symbol = self.get_symbol(function.returns.id) fun_rtype_symbol = self.get_type(symbol) fun_return: IType = self.get_type(fun_rtype_symbol) fun_decorators: List[Method] = self._get_function_decorators(function) if Builtin.Metadata in fun_decorators: self._read_metadata_object(function) return Builtin.Metadata method = Method(args=fun_args, defaults=function.args.defaults, return_type=fun_return, origin_node=function, is_public=Builtin.Public in fun_decorators) self._current_method = method self._scope_stack.append(SymbolScope()) # don't evaluate constant expression - for example: string for documentation from boa3.constants import SYS_VERSION_INFO if SYS_VERSION_INFO >= (3, 8): function.body = [stmt for stmt in function.body if not (isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant))] else: function.body = [stmt for stmt in function.body if not (isinstance(stmt, ast.Expr) and (hasattr(stmt.value, 'n') or hasattr(stmt.value, 's')) )] for stmt in function.body: self.visit(stmt) self.__include_callable(function.name, method) method_scope = self._scope_stack.pop() global_scope_symbols = self._scope_stack[0].symbols if len(self._scope_stack) > 0 else {} for var_id, var in method_scope.symbols.items(): if isinstance(var, Variable) and var_id not in self._annotated_variables: method.include_variable(var_id, Variable(UndefinedType, var.origin)) else: method.include_symbol(var_id, var) self._annotated_variables.clear() self._current_method = None
def _validate_metadata_symbols(self): """ Validates if the symbols match the requirements from the given metadata """ if self._metadata is not None: if self._metadata.is_payable: """ Payable smart contracts requires the implementation of a 'verify' function, that must implement the following signature: ``` @public def verify(*args) -> bool ``` """ verify_id: str = 'verify' if (verify_id not in self.global_symbols # couldn't find verify or not isinstance(self.global_symbols[verify_id], Method) # verify is not a function or self.global_symbols[verify_id].origin is None # verify is not a user function ): self._log_error( CompilerError.MetadataImplementationMissing( line=self._metadata_node.lineno, col=self._metadata_node.col_offset, symbol_id=verify_id, metadata_attr_id='is_payable')) elif (self.global_symbols[verify_id].return_type != Type.bool or not self.global_symbols[verify_id].is_public): actual = self.global_symbols[verify_id] expected = Method(actual.args, Type.bool, True) self._log_error( CompilerError.MetadataIncorrectImplementation( line=actual.origin.lineno, col=actual.origin.col_offset, symbol_id=verify_id, expected_symbol=expected, actual_symbol=actual))