Exemple #1
0
    def visit_Subscript_Slice(self, subscript: ast.Subscript):
        """
        Visitor of a subscript node with slice

        :param subscript: the python ast subscript node
        """
        lower_omitted = subscript.slice.lower is None
        upper_omitted = subscript.slice.upper is None

        self.visit_to_generate(subscript.value)
        # if both are explicit
        if not lower_omitted and not upper_omitted:
            addresses = [VMCodeMapping.instance().bytecode_size]
            self.visit_to_generate(subscript.slice.lower)

            # length of slice
            addresses.append(VMCodeMapping.instance().bytecode_size)
            self.visit_to_generate(subscript.slice.upper)

            self.generator.convert_get_sub_array(addresses)
        # only one of them is omitted
        elif lower_omitted != upper_omitted:
            # end position is omitted
            if lower_omitted:
                self.visit_to_generate(subscript.slice.upper)
                self.generator.convert_get_array_beginning()
            # start position is omitted
            else:
                self.generator.duplicate_stack_top_item()
                # length of slice
                self.generator.convert_builtin_method_call(Builtin.Len)
                self.visit_to_generate(subscript.slice.lower)
                self.generator.convert_get_array_ending()
        else:
            self.generator.convert_copy()
Exemple #2
0
 def include_instruction(self, node: ast.AST, address: int):
     if self.current_method is not None and address in VMCodeMapping.instance(
     ).code_map:
         bytecode = VMCodeMapping.instance().code_map[address]
         from boa3.model.debuginstruction import DebugInstruction
         self.current_method.include_instruction(
             DebugInstruction.build(node, bytecode))
Exemple #3
0
    def store_variable(self, *var_ids: Tuple[str, Optional[ast.AST]],
                       value: ast.AST):
        # if the value is None, it is a variable declaration
        if value is not None:
            if len(var_ids) == 1:
                # it's a simple assignment
                var_id, index = var_ids[0]
                if index is None:
                    # if index is None, then it is a variable assignment
                    result_type = self.visit_to_generate(value)

                    if result_type is Type.none and not self.generator.is_none_inserted(
                    ):
                        self.generator.convert_literal(None)
                    self.generator.convert_store_variable(var_id)
                else:
                    # if not, it is an array assignment
                    self.generator.convert_load_symbol(var_id)
                    self.visit_to_generate(index)

                    aux_index = VMCodeMapping.instance().bytecode_size
                    value_address = self.visit_to_generate(value)
                    if isinstance(value, ast.Name):
                        value_address = aux_index

                    self.generator.convert_set_item(value_address)

            elif len(var_ids) > 0:
                # it's a chained assignment
                self.visit_to_generate(value)
                for pos, (var_id, index) in enumerate(reversed(var_ids)):
                    if index is None:
                        # if index is None, then it is a variable assignment
                        if pos < len(var_ids) - 1:
                            self.generator.duplicate_stack_top_item()
                        self.generator.convert_store_variable(var_id)
                    else:
                        # if not, it is an array assignment
                        if pos < len(var_ids) - 1:
                            self.generator.convert_load_symbol(var_id)
                            self.visit_to_generate(index)
                            fix_index = VMCodeMapping.instance().bytecode_size
                            self.generator.duplicate_stack_item(3)
                        else:
                            self.visit_to_generate(index)
                            fix_index = VMCodeMapping.instance().bytecode_size
                            self.generator.convert_load_symbol(var_id)
                            self.generator.swap_reverse_stack_items(3)
                        self.generator.convert_set_item(fix_index)
 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()
         ]
     }
Exemple #5
0
    def end_address(self) -> Optional[int]:
        """
        Gets the address of this method's last operation in the bytecode

        :return: the last address of the method
        """
        if self.end_bytecode is None:
            return self.start_address
        else:
            from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping
            return VMCodeMapping.instance().get_end_address(self.end_bytecode)
Exemple #6
0
    def start_address(self) -> Optional[int]:
        """
        Gets the address where this method starts in the bytecode

        :return: the first address of the method
        """
        if self.init_bytecode is None and self.init_defaults_bytecode is None:
            return self.init_address
        else:
            from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping
            return VMCodeMapping.instance().get_start_address(
                self.init_bytecode)
Exemple #7
0
    def visit_to_map(self, node: ast.AST, generate: bool = False):
        address: int = VMCodeMapping.instance().bytecode_size
        if isinstance(node, ast.Expr):
            value = self.visit_Expr(node, generate)
        elif generate:
            value = self.visit_to_generate(node)
        else:
            value = self.visit(node)

        if not hasattr(node, 'test'):
            # control flow nodes must map each of their instructions
            self.include_instruction(node, address)
        return value
Exemple #8
0
    def append(self, value: IType, code: VMCode):
        states = self.stack_map
        index = VMCodeMapping.instance().get_start_address(code)
        if index in states:
            states[index].append(value)

        else:
            if self._current_stack is not None:
                stack = self._current_stack.copy()
            else:
                stack = NeoStack()
            stack.append(value)

            self._stacks.append((code, stack))
            self._current_stack = stack
 def _construct_abi_method(self, method_id: str, method: Method) -> Dict[str, Any]:
     from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping
     return {
         "name": method_id,
         "offset": (VMCodeMapping.instance().get_start_address(method.start_bytecode)
                    if method.start_bytecode is not None else 0),
         "parameters": [
             {
                 "name": arg_id,
                 "type": arg.type.abi_type
             } for arg_id, arg in method.args.items()
         ],
         "returntype": method.type.abi_type,
         "safe": False
     }
Exemple #10
0
    def _get_raw_data(self, opcode: VMCode) -> bytes:
        """
        Gets the Neo VM raw data of the code

        :return: the unformatted data in bytes of the code.
        """
        min_len = self._info.data_len // 2  # for try opcode, data_len adds catch and finally addresses
        if opcode is None:
            return bytes(min_len)
        else:
            from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping
            code_mapping = VMCodeMapping.instance()
            self_start = code_mapping.get_start_address(self)
            target_start = code_mapping.get_start_address(opcode)

            return (Integer(target_start - self_start).to_byte_array(
                signed=True, min_length=min_len))
Exemple #11
0
    def visit_While(self, while_node: ast.While):
        """
        Visitor of a while statement node

        :param while_node: the python ast while statement node
        """
        start_addr: int = self.generator.convert_begin_while()
        for stmt in while_node.body:
            self.visit_to_map(stmt, generate=True)

        test_address: int = VMCodeMapping.instance().bytecode_size
        self.visit_to_map(while_node.test, generate=True)
        self.generator.convert_end_while(start_addr, test_address)

        else_begin_address: int = self.generator.last_code_start_address
        for stmt in while_node.orelse:
            self.visit_to_map(stmt, generate=True)

        self.generator.convert_end_loop_else(start_addr, else_begin_address, len(while_node.orelse) > 0)
Exemple #12
0
    def _construct_abi_method(self, method_id: str,
                              method: Method) -> Dict[str, Any]:
        from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping

        abi_method_name = method.external_name if isinstance(
            method.external_name, str) else method_id
        logging.info(f"'{abi_method_name}' method included in the manifest")

        return {
            "name":
            abi_method_name,
            "offset":
            (VMCodeMapping.instance().get_start_address(method.start_bytecode)
             if method.start_bytecode is not None else 0),
            "parameters": [{
                "name": arg_id,
                "type": arg.type.abi_type
            } for arg_id, arg in method.args.items()],
            "returntype":
            method.type.abi_type,
            "safe":
            method.is_safe
        }
Exemple #13
0
    def visit_Call(self, call: ast.Call) -> IType:
        """
        Visitor of a function call node

        :param call: the python ast function call node
        :returns: The called function return type
        """
        # the parameters are included into the stack in the reversed order
        last_address = VMCodeMapping.instance().bytecode_size
        last_stack = self.generator.stack_size

        function_id = self.visit(call.func)
        if not isinstance(function_id, str):
            if not isinstance(function_id, tuple) or len(function_id) != 2:
                return Type.none

            class_type, identifier = function_id
            if (isinstance(class_type, ClassType) and identifier in class_type.symbols
                    and isinstance(class_type.symbols[identifier], IExpression)):
                symbol = class_type.symbols[identifier]
                function_id = identifier
            else:
                return Type.none
        else:
            is_internal = hasattr(call, 'is_internal_call') and call.is_internal_call
            symbol = self.generator.get_symbol(function_id, is_internal=is_internal)

        if isinstance(symbol, ClassType):
            symbol = symbol.constructor_method()
        args_addresses: List[int] = []

        if VMCodeMapping.instance().bytecode_size > last_address:
            # remove opcodes inserted during the evaluation of the symbol
            VMCodeMapping.instance().remove_opcodes(last_address, VMCodeMapping.instance().bytecode_size)
        if last_stack < self.generator.stack_size:
            # remove any additional values pushed to the stack during the evalution of the symbol
            for _ in range(self.generator.stack_size - last_stack):
                self.generator._stack_pop()

        if isinstance(symbol, Method):
            args_to_generate = [arg for index, arg in enumerate(call.args) if index in symbol.args_to_be_generated()]
        else:
            args_to_generate = call.args

        if isinstance(symbol, IBuiltinMethod):
            reordered_args = []
            for index in symbol.generation_order:
                if 0 <= index < len(args_to_generate):
                    reordered_args.append(args_to_generate[index])

            args = reordered_args
        else:
            args = reversed(args_to_generate)

        for arg in args:
            args_addresses.append(
                VMCodeMapping.instance().bytecode_size
            )
            self.visit_to_generate(arg)

        if self.is_exception_name(function_id):
            self.generator.convert_new_exception(len(call.args))
        elif isinstance(symbol, IBuiltinMethod):
            self.generator.convert_builtin_method_call(symbol, args_addresses)
        else:
            self.generator.convert_load_symbol(function_id, args_addresses)

        return symbol.type if isinstance(symbol, IExpression) else symbol
Exemple #14
0
 def debug_map(self) -> List[DebugInstruction]:
     """
     Returns a list with the debug information of each mapped Python instruction inside this method
     """
     from boa3.compiler.codegenerator.vmcodemapping import VMCodeMapping
     return sorted(self._debug_map, key=lambda instr: VMCodeMapping.instance().get_start_address(instr.code))
Exemple #15
0
 def stack_map(self) -> Dict[int, NeoStack]:
     vm_code_mapping = VMCodeMapping.instance()
     return {vm_code_mapping.get_start_address(vmcode): stack for vmcode, stack in self._stacks}