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

Feature: Create new array with length #42

Merged
merged 12 commits into from
May 15, 2019
31 changes: 24 additions & 7 deletions vm/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,35 @@ func (a *Array) At(index uint16) ([]byte, error) {
return result, err
}

// Insert sets an element at a certain index of the array
func (a *Array) Insert(index uint16, element []byte) error {
// First remove the current element at the index
err := a.Remove(index)
if err != nil {
return err
}

var f action = func(array *Array, i uint16, s uint16) ([]byte, error) {
tmp := Array{}
tmp = append(tmp, (*a)[:i]...)
tmp.Append(element)
*a = append(tmp, (*a)[i:]...)
return []byte{}, nil
arraySize, err := a.GetSize()

if err != nil {
return err
}
_, err = a.goToIndex(index, f)

// The last element can directly be appended
if arraySize == index {
err = a.Append(element)

} else {
var f action = func(array *Array, i uint16, s uint16) ([]byte, error) {
tmp := Array{}
tmp = append(tmp, (*a)[:i]...)
err := tmp.Append(element)
*a = append(tmp, (*a)[i:]...)
return []byte{}, err
}
_, err = a.goToIndex(index, f)
}

return err
}

Expand All @@ -104,7 +119,9 @@ func (a *Array) Append(ba []byte) error {
return err
}

// Remove removes the element with the given index from the array
func (a *Array) Remove(index uint16) error {
// This function actually removes the element at the given index from the array
var f action = func(array *Array, k uint16, s uint16) ([]byte, error) {
tmp := Array{}
tmp = append(tmp, (*a)[:k]...)
Expand Down
10 changes: 0 additions & 10 deletions vm/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,5 @@ func (s *Struct) storeField(index uint16, element []byte) error {
if index >= size {
return errors.New("index out of bounds")
}

// Array insert does not work for an array with size = 1
if size == index+1 {
err := array.Remove(index)
if err != nil {
return err
}
err = array.Append(element)
return err
}
return array.Insert(index, element)
}
21 changes: 20 additions & 1 deletion vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,9 +1031,28 @@ func (vm *VM) Exec(trace bool) bool {
}

case NewArr:
length, err := vm.PopUnsignedBigInt(opCode)

if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}

a := NewArray()
vm.evaluationStack.Push(a)

for i := big.NewInt(0); i.Cmp(&length) == -1; i.Add(i, big.NewInt(1)) {
err := a.Append([]byte{0})
if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
}

err = vm.evaluationStack.Push(a)
if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
case ArrAppend:
a, aerr := vm.PopBytes(opCode)
v, verr := vm.PopBytes(opCode)
Expand Down
75 changes: 65 additions & 10 deletions vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,7 @@ func TestVM_Exec_MapRemove(t *testing.T) {

func TestVM_Exec_NewArr(t *testing.T) {
code := []byte{
PushInt, 1, 0, 1,
NewArr,
Halt,
}
Expand All @@ -1890,16 +1891,37 @@ func TestVM_Exec_NewArr(t *testing.T) {
if err != nil {
t.Errorf("%v", err)
}
expectedSize := []byte{0x00, 0x00}
expectedSize := []byte{0x00, 0x01}
actualSize := arr[1:3]
if !bytes.Equal(expectedSize, actualSize) {
t.Errorf("invalid size, Expected %v but was '%v'", expectedSize, actualSize)
}
}

func TestVM_Exec_NewArrWithoutInitialization(t *testing.T) {
code := []byte{
PushInt, 1, 0, 2,
NewArr,
ArrLen,
Halt,
}

vm, isSuccess := execCode(code)
assert.Assert(t, isSuccess)

lengthBytes, _ := vm.evaluationStack.Pop()

length, _ := ByteArrayToUI16(lengthBytes)

if length != 2 {
t.Errorf("Array length should be 2 but is %v", length)
}
}

func TestVM_Exec_ArrAppend(t *testing.T) {
code := []byte{
Push, 2, 0xFF, 0x00,
PushInt, 1, 0, 0,
NewArr,
ArrAppend,
Halt,
Expand All @@ -1909,6 +1931,7 @@ func TestVM_Exec_ArrAppend(t *testing.T) {
mc := NewMockContext(code)
vm.context = mc
exec := vm.Exec(false)
mc.PersistChanges()
if !exec {
errorMessage, _ := vm.evaluationStack.Pop()
t.Errorf("VM.Exec terminated with Error: %v", string(errorMessage))
Expand All @@ -1933,6 +1956,7 @@ func TestVM_Exec_ArrInsert(t *testing.T) {

Push, 1, 0xFE, // value [254] at index 1
Push, 1, 0xFF, // value [255] at index 0
PushInt, 1, 0, 0,
NewArr,
ArrAppend,
ArrAppend,
Expand All @@ -1950,6 +1974,8 @@ func TestVM_Exec_ArrInsert(t *testing.T) {
t.Errorf("VM.Exec terminated with Error: %v", string(errorMessage))
}

mc.PersistChanges()

actual, err := vm.evaluationStack.Pop()
if err != nil {
t.Errorf("%v", err)
Expand All @@ -1973,6 +1999,7 @@ func TestVM_Exec_ArrRemove(t *testing.T) {
Push, 2, 0xAA, 0x00,
Push, 2, 0xFF, 0x00,

PushInt, 1, 0, 0,
NewArr,

ArrAppend,
Expand Down Expand Up @@ -2030,6 +2057,7 @@ func TestVM_Exec_ArrAt(t *testing.T) {
Push, 2, 0xAA, 0x00,
Push, 2, 0xFF, 0x00,

PushInt, 1, 0, 0,
NewArr,

ArrAppend,
Expand Down Expand Up @@ -2911,8 +2939,9 @@ func TestMultipleReturnValuesDifferentTypes(t *testing.T) {
}
}

func TestArrayLengthEmptyArray(t *testing.T) {
func TestNewArrayFromLengthOnStack(t *testing.T) {
code := []byte{
PushInt, 1, 0, 2,
NewArr,
ArrLen,
Halt,
Expand All @@ -2921,18 +2950,43 @@ func TestArrayLengthEmptyArray(t *testing.T) {
vm, isSuccess := execCode(code)
assert.Assert(t, isSuccess)

length_bytes, _ := vm.evaluationStack.Pop()
lengthBytes, _ := vm.evaluationStack.Pop()

length, _ := ByteArrayToUI16(length_bytes)
length, _ := ByteArrayToUI16(lengthBytes)

if length != 0 {
t.Errorf("Array length should be 0 but is %v", length)
if length != 2 {
t.Errorf("Array length should be 2 but is %v", length)
}
}

func TestArrayInsert(t *testing.T) {
code := []byte{
PushInt, 1, 0, 2,
PushInt, 1, 0, 0,
PushInt, 1, 0, 1,
NewArr,
ArrInsert,
Halt,
}

vm, isSuccess := execCode(code)
assert.Assert(t, isSuccess)

arrayBytes, _ := vm.evaluationStack.Pop()

offset := 3
numberOfBytes := 2
arrayValue := ByteArrayToInt(arrayBytes[0+offset : 0+offset+numberOfBytes])
if arrayValue != 2 {
t.Errorf("Expected value at position 0 to be 2 but was %v", arrayValue)
}

}

func TestArrayLength(t *testing.T) {
code := []byte{
PushInt, 1, 0, 2,
PushInt, 1, 0, 0,
NewArr,
ArrAppend,
ArrLen,
Expand All @@ -2942,9 +2996,9 @@ func TestArrayLength(t *testing.T) {
vm, isSuccess := execCode(code)
assert.Assert(t, isSuccess)

length_bytes, _ := vm.evaluationStack.Pop()
lengthBytes, _ := vm.evaluationStack.Pop()

length, _ := ByteArrayToUI16(length_bytes)
length, _ := ByteArrayToUI16(lengthBytes)

if length != 1 {
t.Errorf("Array length should be 1 but is %v", length)
Expand All @@ -2955,6 +3009,7 @@ func TestArrayLengthMultipleElements(t *testing.T) {
code := []byte{
PushInt, 1, 0, 2, // will be appended at index 1
PushInt, 1, 0, 1, // will be appended at index 0
PushInt, 1, 0, 0,
NewArr,
ArrAppend,
ArrAppend,
Expand All @@ -2965,9 +3020,9 @@ func TestArrayLengthMultipleElements(t *testing.T) {
vm, isSuccess := execCode(code)
assert.Assert(t, isSuccess)

length_bytes, _ := vm.evaluationStack.Pop()
lengthBytes, _ := vm.evaluationStack.Pop()

length, _ := ByteArrayToUI16(length_bytes)
length, _ := ByteArrayToUI16(lengthBytes)

if length != 2 {
t.Errorf("Array length should be 2 but is %v", length)
Expand Down