Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Bytes type #40

Merged
merged 1 commit into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions boa3/analyser/typeanalyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def validate_get_or_set(self, subscript: ast.Subscript, index_node: ast.Index) -
)
return symbol_type
# the sequence can't use the given type as index
elif not symbol_type.is_valid_key(index_type):
if not symbol_type.is_valid_key(index_type):
self._log_error(
CompilerError.MismatchedTypes(
subscript.lineno, subscript.col_offset,
Expand All @@ -350,9 +350,7 @@ def validate_get_or_set(self, subscript: ast.Subscript, index_node: ast.Index) -
type_id=symbol_type.identifier,
operation_id=Operator.Subscript)
)
else:
return symbol_type.value_type
return Type.none
return symbol_type.value_type

def validate_slice(self, subscript: ast.Subscript, slice_node: ast.Slice) -> IType:
"""
Expand Down Expand Up @@ -866,14 +864,23 @@ def visit_Num(self, num: ast.Num) -> int:
)
return num.n

def visit_Str(self, str: ast.Str) -> str:
def visit_Str(self, string: ast.Str) -> str:
"""
Visitor of literal string node

:param str: the python ast string node
:param string: the python ast string node
:return: the value of the string
"""
return str.s
return string.s

def visit_Bytes(self, bts: ast.Bytes) -> bytes:
"""
Visitor of literal bytes node

:param bts: the python ast bytes node
:return: the value of the bytes
"""
return bts.s

def visit_Tuple(self, tup_node: ast.Tuple) -> Tuple[Any, ...]:
"""
Expand Down
66 changes: 41 additions & 25 deletions boa3/compiler/codegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from boa3.neo.vm.opcode.OpcodeInfo import OpcodeInfo
from boa3.neo.vm.opcode.OpcodeInformation import OpcodeInformation
from boa3.neo.vm.type.Integer import Integer
from boa3.neo.vm.type.StackItemType import StackItemType


class CodeGenerator:
Expand Down Expand Up @@ -301,6 +300,8 @@ def convert_literal(self, value: Any):
self.convert_string_literal(value)
elif value is None:
self.insert_none()
elif isinstance(value, (bytes, bytearray)):
self.convert_byte_array(value)
else:
# TODO: convert other python literals as they are implemented
raise NotImplementedError
Expand All @@ -318,9 +319,9 @@ def convert_integer_literal(self, value: int):
self.__insert1(op_info)
else:
array = Integer(value).to_byte_array()
self.convert_byte_array(array)
self.insert_push_data(array)
# cast the value to integer
self.__insert1(OpcodeInfo.CONVERT, StackItemType.Integer)
self.convert_cast(Type.int)
self._stack.append(Type.int)

def convert_string_literal(self, value: str):
Expand All @@ -330,9 +331,8 @@ def convert_string_literal(self, value: str):
:param value: the value to be converted
"""
array = bytes(value, sys.getdefaultencoding())
self.convert_byte_array(array)
self._stack.pop()
self._stack.append(Type.str)
self.insert_push_data(array)
self.convert_cast(Type.str)

def convert_bool_literal(self, value: bool):
"""
Expand All @@ -352,17 +352,26 @@ def convert_byte_array(self, array: bytes):

:param array: the value to be converted
"""
data_len: int = len(array)
self.insert_push_data(array)
self.convert_cast(Type.bytes)

def insert_push_data(self, data: bytes):
"""
Inserts a push data value

:param data: the value to be converted
"""
data_len: int = len(data)
if data_len <= ONE_BYTE_MAX_VALUE:
op_info = OpcodeInfo.PUSHDATA1
elif data_len <= TWO_BYTES_MAX_VALUE:
op_info = OpcodeInfo.PUSHDATA2
else:
op_info = OpcodeInfo.PUSHDATA4

data = Integer(data_len).to_byte_array(min_length=op_info.data_len) + array
data = Integer(data_len).to_byte_array(min_length=op_info.data_len) + data
self.__insert1(op_info, data)
self._stack.append(Type.none) # TODO: change to bytearray when implemented
self._stack.append(Type.str) # push data pushes a ByteString value in the stack

def insert_none(self):
"""
Expand All @@ -371,6 +380,19 @@ def insert_none(self):
self.__insert1(OpcodeInfo.PUSHNULL)
self._stack.append(Type.none)

def convert_cast(self, value_type: IType):
"""
Converts casting types in Neo VM
"""
stack_top_type: IType = self._stack[-1]
if (not value_type.is_generic
and not stack_top_type.is_generic
and value_type.stack_item != stack_top_type.stack_item
and value_type.stack_item is not Type.any.stack_item):
self.__insert1(OpcodeInfo.CONVERT, value_type.stack_item)
self._stack.pop()
self._stack.append(value_type)

def convert_new_empty_array(self, length: int, array_type: IType):
"""
Converts the creation of a new empty array
Expand All @@ -396,18 +418,11 @@ def convert_new_array(self, length: int, array_type: IType):
self.convert_new_empty_array(length, array_type)
else:
self.__insert1(OpcodeInfo.PACK)
self._stack.pop() # array size
for x in range(length):
self._stack.pop()
self._stack.append(array_type)

def convert_set_new_array_item_at(self, index: int):
"""
Converts the beginning of setting af a value in an array

:param index: the index of the array that will be set
"""
self.duplicate_stack_top_item()
self.convert_literal_index(index)
self.convert_literal(index)

def convert_set_array_item(self):
"""
Converts the end of setting af a value in an array
Expand Down Expand Up @@ -537,13 +552,14 @@ def convert_store_variable(self, var_id: str):

index: int = scope.index(var_id)
opcode = Opcode.get_store(index, local, is_arg)
op_info = OpcodeInfo.get_info(opcode)
if opcode is not None:
op_info = OpcodeInfo.get_info(opcode)

if op_info.data_len > 0:
self.__insert1(op_info, Integer(index).to_byte_array())
else:
self.__insert1(op_info)
self._stack.pop()
if op_info.data_len > 0:
self.__insert1(op_info, Integer(index).to_byte_array())
else:
self.__insert1(op_info)
self._stack.pop()

def convert_builtin_method_call(self, function: IBuiltinMethod):
"""
Expand Down
15 changes: 12 additions & 3 deletions boa3/compiler/codegeneratorvisitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,22 @@ def visit_Num(self, num: ast.Num):
"""
self.generator.convert_literal(num.n)

def visit_Str(self, str: ast.Str):
def visit_Str(self, string: ast.Str):
"""
Visitor of literal string node

:param str: the python ast string node
:param string: the python ast string node
"""
self.generator.convert_literal(str.s)
self.generator.convert_literal(string.s)

def visit_Bytes(self, bts: ast.Bytes):
"""
Visitor of literal bytes node

:param bts: the python ast bytes node
:return: the value of the bytes
"""
self.generator.convert_literal(bts.s)

def visit_Tuple(self, tup_node: ast.Tuple):
"""
Expand Down
19 changes: 19 additions & 0 deletions boa3/model/type/itype.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from boa3.model.symbol import ISymbol
from boa3.neo.vm.type.AbiType import AbiType
from boa3.neo.vm.type.StackItemType import StackItemType


class IType(ISymbol):
Expand Down Expand Up @@ -36,6 +37,24 @@ def abi_type(self) -> AbiType:
"""
return AbiType.Any

@property
def stack_item(self) -> StackItemType:
"""
Get the Neo VM stack item type representation for this type

:return: the stack item type of this type. Any by default.
"""
return StackItemType.Any

@property
def is_generic(self) -> bool:
"""
Verifies if this is a generic type

:return: True if this is a generic type. False otherwise.
"""
return False

@classmethod
@abstractmethod
def _is_type_of(cls, value: Any) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions boa3/model/type/primitive/booltype.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from boa3.model.type.itype import IType
from boa3.neo.vm.type.AbiType import AbiType
from boa3.neo.vm.type.StackItemType import StackItemType


class BoolType(IType):
Expand All @@ -21,6 +22,10 @@ def default_value(self) -> Any:
def abi_type(self) -> AbiType:
return AbiType.Boolean

@property
def stack_item(self) -> StackItemType:
return StackItemType.Boolean

@classmethod
def build(cls, value: Any):
if cls._is_type_of(value):
Expand Down
60 changes: 60 additions & 0 deletions boa3/model/type/primitive/bytestype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import Any

from boa3.model.type.itype import IType
from boa3.model.type.sequence.sequencetype import SequenceType
from boa3.neo.vm.type.AbiType import AbiType
from boa3.neo.vm.type.StackItemType import StackItemType


class BytesType(SequenceType):
"""
A class used to represent Python bytes type
"""

def __init__(self):
identifier = 'bytes'
from boa3.model.type.primitive.inttype import IntType
values_type = [IntType()]
super().__init__(identifier, values_type)

@property
def identifier(self) -> str:
return self._identifier

@property
def abi_type(self) -> AbiType:
return AbiType.ByteArray

@property
def stack_item(self) -> StackItemType:
return StackItemType.Buffer

@property
def default_value(self) -> Any:
return bytes()

def is_valid_key(self, value_type: IType) -> bool:
return value_type == self.valid_key

@property
def valid_key(self) -> IType:
from boa3.model.type.type import Type
return Type.int

@classmethod
def build(cls, value: Any):
from boa3.model.type.type import Type
return Type.bytes

@classmethod
def build_sequence(cls, value_type: IType):
from boa3.model.type.type import Type
return Type.bytes

@classmethod
def _is_type_of(cls, value: Any):
return type(value) is bytes or isinstance(value, BytesType)

@property
def can_reassign_values(self) -> bool:
return False
5 changes: 5 additions & 0 deletions boa3/model/type/primitive/inttype.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from boa3.model.type.itype import IType
from boa3.neo.vm.type.AbiType import AbiType
from boa3.neo.vm.type.StackItemType import StackItemType


class IntType(IType):
Expand All @@ -21,6 +22,10 @@ def default_value(self) -> Any:
def abi_type(self) -> AbiType:
return AbiType.Integer

@property
def stack_item(self) -> StackItemType:
return StackItemType.Integer

@classmethod
def build(cls, value: Any):
if cls._is_type_of(value):
Expand Down
5 changes: 5 additions & 0 deletions boa3/model/type/primitive/strtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from boa3.model.type.itype import IType
from boa3.model.type.sequence.sequencetype import SequenceType
from boa3.neo.vm.type.AbiType import AbiType
from boa3.neo.vm.type.StackItemType import StackItemType


class StrType(SequenceType):
Expand All @@ -26,6 +27,10 @@ def default_value(self) -> Any:
def abi_type(self) -> AbiType:
return AbiType.String

@property
def stack_item(self) -> StackItemType:
return StackItemType.ByteString

@classmethod
def build(cls, value: Any):
if cls._is_type_of(value):
Expand Down
4 changes: 4 additions & 0 deletions boa3/model/type/sequence/genericsequencetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def valid_key(self) -> IType:
from boa3.model.type.type import Type
return Type.int

@property
def is_generic(self) -> bool:
return True

@classmethod
def build(cls, value: Any):
if cls._is_type_of(value):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def valid_key(self) -> IType:
from boa3.model.type.type import Type
return Type.int

@property
def is_generic(self) -> bool:
return True

@classmethod
def build(cls, value: Any):
if cls._is_type_of(value):
Expand Down
Loading