Skip to content

Commit

Permalink
Merge pull request #40 from CityOfZion/convert-bytearray
Browse files Browse the repository at this point in the history
Convert Bytes type
  • Loading branch information
melanke committed Jun 15, 2020
2 parents 9e79f42 + 4c1a9b2 commit a12ff4f
Show file tree
Hide file tree
Showing 22 changed files with 303 additions and 45 deletions.
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

0 comments on commit a12ff4f

Please sign in to comment.