コード例 #1
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def __insert_code(self, vm_code: VMCode):
        """
        Inserts one vmcode into the bytecode

        :param vm_code: the opcode that will be inserted
        """
        VMCodeMapping.instance().insert_code(vm_code)
コード例 #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))
コード例 #3
0
    def visit_Call(self, call: ast.Call):
        """
        Visitor of a function call node

        :param call: the python ast function call node
        """
        # the parameters are included into the stack in the reversed order
        function_id = self.visit(call.func)
        symbol = self.generator.get_symbol(function_id)
        args_addresses: List[int] = []

        if isinstance(symbol, IBuiltinMethod) and symbol.push_self_first():
            args = call.args[1:]
            args_addresses.append(VMCodeMapping.instance().bytecode_size)
            self.visit_to_generate(call.args[0])
        else:
            args = call.args

        for arg in reversed(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))
        else:
            self.generator.convert_load_symbol(function_id, args_addresses)
コード例 #4
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()
コード例 #5
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_assert(self):
        asserted_type = self._stack[-1] if len(self._stack) > 0 else Type.any

        if not isinstance(asserted_type, PrimitiveType):
            len_pos = VMCodeMapping.instance().bytecode_size
            # if the value is an array, a map or a struct, asserts it is not empty
            self.convert_builtin_method_call(Builtin.Len)
            len_code = VMCodeMapping.instance().code_map[len_pos]

            if asserted_type is Type.any:
                # need to check in runtime
                self.duplicate_stack_top_item()
                self.__insert1(OpcodeInfo.ISTYPE, StackItemType.Array)
                self._insert_jump(OpcodeInfo.JMPIF, len_code)

                self.duplicate_stack_top_item()
                self.__insert1(OpcodeInfo.ISTYPE, StackItemType.Map)
                self._insert_jump(OpcodeInfo.JMPIF, len_code)

                self.duplicate_stack_top_item()
                self.__insert1(OpcodeInfo.ISTYPE, StackItemType.Struct)
                self._insert_jump(OpcodeInfo.JMPIFNOT, 2)

                VMCodeMapping.instance().move_to_end(len_pos, len_pos)

        self.__insert1(OpcodeInfo.ASSERT)
コード例 #6
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_try_finally(self,
                                last_address: int,
                                start_address: int,
                                has_try_body: bool = False):
        """
        Converts the end of the try finally statement

        :param last_address: the address of the try except last opcode.
        :param start_address: the address of the try first opcode
        :param has_try_body: whether this try statement has a finally body.
        :return: the last address of the except body
        """
        if has_try_body:
            self.__insert1(OpcodeInfo.ENDFINALLY)
            vmcode_mapping_instance = VMCodeMapping.instance()

            try_vm_code = vmcode_mapping_instance.get_code(start_address)
            try_last_code = vmcode_mapping_instance.get_code(last_address)

            finally_start_address = vmcode_mapping_instance.get_end_address(
                try_last_code) + 1
            finally_start_code = vmcode_mapping_instance.get_code(
                finally_start_address)

            if isinstance(try_vm_code, TryCode):
                try_vm_code.set_finally_code(finally_start_code)
            self._update_jump(vmcode_mapping_instance.bytecode_size,
                              self.last_code_start_address)

        self._update_jump(last_address, VMCodeMapping.instance().bytecode_size)
コード例 #7
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_get_sub_array(self, value_addresses: List[int] = None):
        """
        Converts the end of get a slice in the beginning of an array

        :param value_addresses: the start and end values addresses
        """
        # top: length, index, array
        if len(self._stack) > 2 and isinstance(self._stack[-3], SequenceType):
            if value_addresses is not None:
                opcodes = [
                    VMCodeMapping.instance().code_map[address]
                    for address in value_addresses
                ]
                for code in opcodes:
                    self.fix_negative_index(
                        VMCodeMapping.instance().get_end_address(code) + 1)

            if self._stack[-3].stack_item in (StackItemType.ByteString,
                                              StackItemType.Buffer):
                self.duplicate_stack_item(2)
                self.convert_operation(BinaryOp.Sub)
                self.convert_get_substring()
            else:
                array = self._stack[-3]
                self.duplicate_stack_item(
                    3
                )  # if slice end is greater than the array size, fixes them
                self.convert_builtin_method_call(Builtin.Len)

                # TODO: change to convert_builtin_method_call(Builtin.Min) when min(a, b) is implemented
                self.__insert1(OpcodeInfo.MIN)
                self._stack.pop()
                self.convert_get_array_slice(array)
コード例 #8
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def last_code(self) -> Optional[VMCode]:
        """
        Gets the last code in the bytecode

        :return: the last code. If the bytecode is empty, returns None
        :rtype: VMCode or None
        """
        if len(VMCodeMapping.instance().codes) > 0:
            return VMCodeMapping.instance().codes[-1]
        else:
            return None
コード例 #9
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def _update_break_jumps(self, loop_start_address) -> int:
        jump_target = VMCodeMapping.instance().bytecode_size

        if loop_start_address in self._jumps_to_loop_break:
            jump_addresses = self._jumps_to_loop_break.pop(loop_start_address)
            for address in jump_addresses:
                self._update_jump(address, jump_target)
コード例 #10
0
 def _get_method_debug_info(self, method_id: str,
                            method: Method) -> Dict[str, Any]:
     from boa3.compiler.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()
         ]
     }
コード例 #11
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_for(self, start_address: int) -> int:
        """
        Converts the end of the for statement

        :param start_address: the address of the for first opcode
        :return: the address of the loop condition
        """
        self.__insert1(OpcodeInfo.INC)  # index += 1
        if len(self._stack) < 1 or self._stack[-1] is not Type.int:
            self._stack.append(Type.int)
        for_increment = self.last_code_start_address
        test_address = VMCodeMapping.instance().bytecode_size
        self._update_continue_jumps(start_address, for_increment)

        self.duplicate_stack_top_item()  # dup index and sequence
        self.duplicate_stack_item(3)
        self.convert_builtin_method_call(Builtin.Len)
        self.convert_operation(
            BinaryOp.Lt)  # continue loop condition: index < len(sequence)

        self.convert_end_while(start_address, test_address)

        self.remove_stack_top_item()  # removes index and sequence from stack
        self.remove_stack_top_item()

        return test_address
コード例 #12
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_method(self):
        """
        Converts the end of the method
        """
        if (self._current_method.init_bytecode is None
                and self._current_method.init_address
                in VMCodeMapping.instance().code_map):
            self._current_method.init_bytecode = VMCodeMapping.instance(
            ).code_map[self._current_method.init_address]

        if self.last_code.opcode is not Opcode.RET:
            self.insert_return()

        self._current_method.end_bytecode = self.last_code
        self._current_method = None
        self._stack.clear()
コード例 #13
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def bytecode_size(self) -> int:
        """
        Gets the current bytecode size

        :return: the current bytecode size
        """
        return VMCodeMapping.instance().bytecode_size
コード例 #14
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_get_array_slice(self, array: SequenceType):
        """
        Converts the end of get a substring
        """
        self.convert_new_empty_array(0, array)  # slice = []
        self.duplicate_stack_item(3)  # index = slice_start

        start_jump = self.convert_begin_while()  # while index < slice_end
        self.duplicate_stack_top_item()  # if index >= slice_start
        self.duplicate_stack_item(5)
        self.convert_operation(BinaryOp.GtE)
        is_valid_index = self.convert_begin_if()

        self.duplicate_stack_item(2)  # slice.append(array[index])
        self.duplicate_stack_item(6)
        self.duplicate_stack_item(3)
        self.convert_get_item()
        self.convert_builtin_method_call(Builtin.SequenceAppend)
        self.convert_end_if(is_valid_index)

        self.__insert1(OpcodeInfo.INC)  # index += 1

        condition_address = VMCodeMapping.instance().bytecode_size
        self.duplicate_stack_top_item()  # end while index < slice_end
        self.duplicate_stack_item(4)
        self.convert_operation(BinaryOp.Lt)
        self.convert_end_while(start_jump, condition_address)

        self.remove_stack_top_item(
        )  # removes from the stack the arguments and the index
        self.swap_reverse_stack_items(
            4)  # doesn't use CLEAR opcode because this would delete
        self.remove_stack_top_item()  # data from external scopes
        self.remove_stack_top_item()
        self.remove_stack_top_item()
コード例 #15
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_try(self,
                        start_address: int,
                        end_address: Optional[int] = None) -> int:
        """
        Converts the end of the try statement

        :param start_address: the address of the try first opcode
        :param end_address: the address of the try last opcode. If it is None, there's no except body.
        :return: the last address of the except body
        """
        self.__insert1(OpcodeInfo.ENDTRY)
        if end_address is not None:
            vmcode_mapping_instance = VMCodeMapping.instance()

            try_vm_code = vmcode_mapping_instance.get_code(start_address)
            try_jump = vmcode_mapping_instance.get_code(end_address)

            except_start_address = vmcode_mapping_instance.get_end_address(
                try_jump) + 1
            except_start_code = vmcode_mapping_instance.get_code(
                except_start_address)

            if isinstance(try_vm_code, TryCode):
                try_vm_code.set_except_code(except_start_code)
            self._update_jump(end_address, self.last_code_start_address)

        return self.last_code_start_address
コード例 #16
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def bytecode(self) -> bytes:
        """
        Gets the bytecode of the translated code

        :return: the generated bytecode
        """
        self.set_code_targets()
        return VMCodeMapping.instance().bytecode()
コード例 #17
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def _insert_jump(self,
                     op_info: OpcodeInformation,
                     jump_to: Union[int, VMCode] = 0):
        """
        Inserts a jump opcode into the bytecode

        :param op_info: info of the opcode  that will be inserted
        :param jump_to: data of the opcode
        """
        if isinstance(jump_to, VMCode):
            jump_to = VMCodeMapping.instance().get_start_address(
                jump_to) - VMCodeMapping.instance().bytecode_size

        if self.last_code.opcode is not Opcode.RET:
            data: bytes = self._get_jump_data(op_info, jump_to)
            self.__insert1(op_info, data)
        for x in range(op_info.stack_items):
            self._stack.pop()
コード例 #18
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_if(self, start_address: int):
        """
        Converts the end of the if statement

        :param start_address: the address of the if first opcode
        """
        # updates the begin jmp with the target address
        self._update_jump(start_address,
                          VMCodeMapping.instance().bytecode_size)
コード例 #19
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_begin_if(self) -> int:
        """
        Converts the beginning of the if statement

        :return: the address of the if first opcode
        """
        # it will be updated when the if ends
        self._insert_jump(OpcodeInfo.JMPIFNOT)
        return VMCodeMapping.instance().get_start_address(self.last_code)
コード例 #20
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_end_while(self, start_address: int, test_address: int):
        """
        Converts the end of the while statement

        :param start_address: the address of the while first opcode
        :param test_address: the address of the while test fist opcode
        """
        # updates the begin jmp with the target address
        self._update_jump(start_address, test_address)
        self._update_continue_jumps(start_address, test_address)

        # inserts end jmp
        while_begin: VMCode = VMCodeMapping.instance().code_map[start_address]
        while_body: int = VMCodeMapping.instance().get_end_address(
            while_begin) + 1
        end_jmp_to: int = while_body - VMCodeMapping.instance().bytecode_size
        self._insert_jump(OpcodeInfo.JMPIF, end_jmp_to)

        self._current_loop.pop()
コード例 #21
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def set_code_targets(self):
        for target, vmcodes in self._missing_target.copy().items():
            if target is None:
                for code in vmcodes.copy():
                    relative_address: int = Integer.from_bytes(code.raw_data,
                                                               signed=True)
                    code_address: int = VMCodeMapping.instance(
                    ).get_start_address(code)
                    absolute_address = code_address + relative_address
                    code.set_target(
                        VMCodeMapping.instance().get_code(absolute_address))

                    vmcodes.remove(code)
            else:
                for code in vmcodes.copy():
                    code.set_target(VMCodeMapping.instance().get_code(target))
                    vmcodes.remove(code)

            if len(vmcodes) == 0:
                self._missing_target.pop(target)
コード例 #22
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def last_code_start_address(self) -> int:
        """
        Gets the first address from last code in the bytecode

        :return: the last code's first address
        """
        instance = VMCodeMapping.instance()
        if len(instance.codes) > 0:
            return instance.get_start_address(instance.codes[-1])
        else:
            return 0
コード例 #23
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.vmcodemapping import VMCodeMapping
            return VMCodeMapping.instance().get_end_address(self.end_bytecode)
コード例 #24
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.vmcodemapping import VMCodeMapping
            return VMCodeMapping.instance().get_start_address(
                self.init_bytecode)
コード例 #25
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def fix_negative_index(self, value_index: int = None):
        self._can_append_target = not self._can_append_target

        value_code = self.last_code_start_address
        size = VMCodeMapping.instance().bytecode_size

        self.duplicate_stack_top_item()
        self.__insert1(OpcodeInfo.SIGN)
        self.convert_literal(-1)

        jmp_address = VMCodeMapping.instance().bytecode_size
        self._insert_jump(OpcodeInfo.JMPNE)  # if index < 0

        self.duplicate_stack_item(2)  # index += len(array)
        self.convert_builtin_method_call(Builtin.Len)
        self.convert_operation(BinaryOp.Add)

        if not isinstance(value_index, int):
            value_index = VMCodeMapping.instance().bytecode_size
        jmp_target = value_index if value_index < size else VMCodeMapping.instance(
        ).bytecode_size
        self._update_jump(jmp_address, jmp_target)

        VMCodeMapping.instance().move_to_end(value_index, value_code)

        self._can_append_target = not self._can_append_target
コード例 #26
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
                    self.visit_to_generate(value)
                    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)
                    value_address = self.visit_to_generate(value)
                    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)
コード例 #27
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
コード例 #28
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def _update_codes_with_target(self, vm_code: VMCode):
        """
        Verifies if there are any instructions targeting the code. If it exists, updates each instruction found

        :param vm_code: targeted instruction
        """
        instance = VMCodeMapping.instance()
        vm_code_start_address = instance.get_start_address(vm_code)
        for target_address, codes in list(self._missing_target.items()):
            if target_address is not None and target_address <= vm_code_start_address:
                for code in codes:
                    code.set_target(vm_code)
                self._missing_target.pop(target_address)
コード例 #29
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def __insert1(self, op_info: OpcodeInformation, data: bytes = None):
        """
        Inserts one opcode into the bytecode

        :param op_info: info of the opcode  that will be inserted
        :param data: data of the opcode, if needed
        """
        vm_code = VMCode(op_info, data)

        if op_info.opcode.has_target():
            data = vm_code.data
            relative_address: int = Integer.from_bytes(data, signed=True)
            actual_address = VMCodeMapping.instance(
            ).bytecode_size + relative_address
            if (self._can_append_target and relative_address != 0
                    and actual_address in VMCodeMapping.instance().code_map):
                vm_code.set_target(
                    VMCodeMapping.instance().code_map[actual_address])
            else:
                self._include_missing_target(vm_code, actual_address)

        self.__insert_code(vm_code)
        self._update_codes_with_target(vm_code)
コード例 #30
0
ファイル: codegenerator.py プロジェクト: EdgeDLT/neo3-boa
    def convert_builtin_method_call(self,
                                    function: IBuiltinMethod,
                                    args_address: List[int] = None):
        """
        Converts a builtin method function call

        :param function: the function to be converted
        :param args_address: a list with each function arguments' first addresses
        """
        if args_address is None:
            args_address = []
        store_opcode: OpcodeInformation = None
        store_data: bytes = b''

        if function.stores_on_slot and 0 < len(
                function.args) <= len(args_address):
            address = args_address[-len(function.args)]
            load_instr = VMCodeMapping.instance().code_map[address]
            if load_instr.opcode.is_load_slot:
                store: Opcode = Opcode.get_store_from_load(load_instr.opcode)
                store_opcode = OpcodeInfo.get_info(store)
                store_data = load_instr.data

        for opcode, data in function.opcode:
            op_info = OpcodeInfo.get_info(opcode)
            self.__insert1(op_info, data)

        if store_opcode is not None:
            self._insert_jump(OpcodeInfo.JMP)
            jump = self.last_code_start_address
            self.__insert1(store_opcode, store_data)
            self._update_jump(jump, VMCodeMapping.instance().bytecode_size)

        for _ in range(function.args_on_stack):
            self._stack.pop()
        if function.return_type not in (None, Type.none):
            self._stack.append(function.return_type)