diff --git a/examples/iterator/iterator.go b/examples/iterator/iterator.go index 534bd0885..5f91204a0 100644 --- a/examples/iterator/iterator.go +++ b/examples/iterator/iterator.go @@ -9,13 +9,8 @@ import ( // NotifyKeysAndValues sends notification with `foo` storage keys and values func NotifyKeysAndValues() bool { iter := storage.Find(storage.GetContext(), []byte("foo")) - values := iterator.Values(iter) - keys := iterator.Keys(iter) - - runtime.Notify("found storage values", values) - // For illustration purposes event is emitted with 'Any' type. - var typedKeys interface{} = keys - runtime.Notify("found storage keys", typedKeys) - + for iterator.Next(iter) { + runtime.Notify("found storage key-value pair", iterator.Value(iter)) + } return true } diff --git a/examples/iterator/iterator.yml b/examples/iterator/iterator.yml index e714e9d8a..3f881c477 100644 --- a/examples/iterator/iterator.yml +++ b/examples/iterator/iterator.yml @@ -1,11 +1,7 @@ name: "Iterator example" supportedstandards: [] events: - - name: found storage values + - name: found storage key-value pair parameters: - - name: values - type: InteropInterface - - name: found storage keys - parameters: - - name: keys + - name: value type: Any diff --git a/examples/storage/storage.go b/examples/storage/storage.go index 9b43c45b6..963dfa2a4 100644 --- a/examples/storage/storage.go +++ b/examples/storage/storage.go @@ -35,9 +35,8 @@ func Find(value []byte) []string { iter := storage.Find(ctx, value) result := []string{} for iterator.Next(iter) { - val := iterator.Value(iter) - key := iterator.Key(iter) - result = append(result, key.(string)+":"+val.(string)) + val := iterator.Value(iter).([]string) + result = append(result, val[0]+":"+val[1]) } return result } diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index eacbe5c7f..ead24c899 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -35,18 +35,10 @@ var syscalls = map[string]map[string]string{ "RIPEMD160": interopnames.NeoCryptoRIPEMD160, "SHA256": interopnames.NeoCryptoSHA256, }, - "enumerator": { - "Create": interopnames.SystemEnumeratorCreate, - "Next": interopnames.SystemEnumeratorNext, - "Value": interopnames.SystemEnumeratorValue, - }, "iterator": { "Create": interopnames.SystemIteratorCreate, - "Key": interopnames.SystemIteratorKey, - "Keys": interopnames.SystemIteratorKeys, - "Next": interopnames.SystemEnumeratorNext, - "Value": interopnames.SystemEnumeratorValue, - "Values": interopnames.SystemIteratorValues, + "Next": interopnames.SystemIteratorNext, + "Value": interopnames.SystemIteratorValue, }, "json": { "Deserialize": interopnames.SystemJSONDeserialize, diff --git a/pkg/core/interop/enumerator/interop.go b/pkg/core/interop/enumerator/interop.go deleted file mode 100644 index b75daff0c..000000000 --- a/pkg/core/interop/enumerator/interop.go +++ /dev/null @@ -1,22 +0,0 @@ -package enumerator - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" -) - -// Create creates an enumerator from an array-like or bytearray-like stack item. -func Create(ic *interop.Context) error { - return vm.EnumeratorCreate(ic.VM) -} - -// Next advances the enumerator, pushes true if is it was successful -// and false otherwise. -func Next(ic *interop.Context) error { - return vm.EnumeratorNext(ic.VM) -} - -// Value returns the current value of the enumerator. -func Value(ic *interop.Context) error { - return vm.EnumeratorValue(ic.VM) -} diff --git a/pkg/core/interop/enumerator/interop_test.go b/pkg/core/interop/enumerator/interop_test.go deleted file mode 100644 index a77a369a3..000000000 --- a/pkg/core/interop/enumerator/interop_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package enumerator - -import ( - "math/big" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/stretchr/testify/require" -) - -// Enumerator is thoroughly tested in VM package, these are smoke tests. -func TestEnumerator(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - full := []byte{4, 8, 15} - ic.VM.Estack().PushVal(full) - require.NoError(t, Create(ic)) - - res := ic.VM.Estack().Pop().Item() - for i := range full { - ic.VM.Estack().PushVal(res) - require.NoError(t, Next(ic)) - require.True(t, ic.VM.Estack().Pop().Bool()) - ic.VM.Estack().PushVal(res) - require.NoError(t, Value(ic)) - require.Equal(t, big.NewInt(int64(full[i])), ic.VM.Estack().Pop().BigInt()) - } - - ic.VM.Estack().PushVal(res) - require.NoError(t, Next(ic)) - require.False(t, ic.VM.Estack().Pop().Bool()) -} diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index 23b3fa3a4..8a76df3dd 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -26,13 +26,9 @@ const ( SystemContractGetCallFlags = "System.Contract.GetCallFlags" SystemContractNativeOnPersist = "System.Contract.NativeOnPersist" SystemContractNativePostPersist = "System.Contract.NativePostPersist" - SystemEnumeratorCreate = "System.Enumerator.Create" - SystemEnumeratorNext = "System.Enumerator.Next" - SystemEnumeratorValue = "System.Enumerator.Value" SystemIteratorCreate = "System.Iterator.Create" - SystemIteratorKey = "System.Iterator.Key" - SystemIteratorKeys = "System.Iterator.Keys" - SystemIteratorValues = "System.Iterator.Values" + SystemIteratorNext = "System.Iterator.Next" + SystemIteratorValue = "System.Iterator.Value" SystemJSONDeserialize = "System.Json.Deserialize" SystemJSONSerialize = "System.Json.Serialize" SystemRuntimeCheckWitness = "System.Runtime.CheckWitness" @@ -89,13 +85,9 @@ var names = []string{ SystemContractGetCallFlags, SystemContractNativeOnPersist, SystemContractNativePostPersist, - SystemEnumeratorCreate, - SystemEnumeratorNext, - SystemEnumeratorValue, SystemIteratorCreate, - SystemIteratorKey, - SystemIteratorKeys, - SystemIteratorValues, + SystemIteratorNext, + SystemIteratorValue, SystemJSONDeserialize, SystemJSONSerialize, SystemRuntimeCheckWitness, diff --git a/pkg/core/interop/iterator/interop.go b/pkg/core/interop/iterator/interop.go index 7692a8c2f..f9efa9d09 100644 --- a/pkg/core/interop/iterator/interop.go +++ b/pkg/core/interop/iterator/interop.go @@ -10,17 +10,14 @@ func Create(ic *interop.Context) error { return vm.IteratorCreate(ic.VM) } -// Key returns current iterator key. -func Key(ic *interop.Context) error { - return vm.IteratorKey(ic.VM) +// Next advances the iterator, pushes true on success and false otherwise. +func Next(ic *interop.Context) error { + return vm.IteratorNext(ic.VM) } -// Keys returns keys of the iterator. -func Keys(ic *interop.Context) error { - return vm.IteratorKeys(ic.VM) -} - -// Values returns values of the iterator. -func Values(ic *interop.Context) error { - return vm.IteratorValues(ic.VM) +// Value returns current iterator value and depends on iterator type: +// For slices the result is just value. +// For maps the result is key-value pair packed in a struct. +func Value(ic *interop.Context) error { + return vm.IteratorValue(ic.VM) } diff --git a/pkg/core/interop/iterator/interop_test.go b/pkg/core/interop/iterator/interop_test.go index c8324b9ed..fe830fea8 100644 --- a/pkg/core/interop/iterator/interop_test.go +++ b/pkg/core/interop/iterator/interop_test.go @@ -17,29 +17,19 @@ func TestIterator(t *testing.T) { require.NoError(t, Create(ic)) res := ic.VM.Estack().Pop().Item() - ic.VM.Estack().PushVal(res) - require.NoError(t, vm.EnumeratorNext(ic.VM)) - require.True(t, ic.VM.Estack().Pop().Bool()) + for i := range full { + ic.VM.Estack().PushVal(res) + require.NoError(t, Next(ic)) + require.True(t, ic.VM.Estack().Pop().Bool()) + + ic.VM.Estack().PushVal(res) + require.NoError(t, Value(ic)) + + value := ic.VM.Estack().Pop().Item().Value() + require.Equal(t, big.NewInt(int64(full[i])), value) + } ic.VM.Estack().PushVal(res) - require.NoError(t, Key(ic)) - require.Equal(t, big.NewInt(0), ic.VM.Estack().Pop().BigInt()) - - ic.VM.Estack().PushVal(res) - require.NoError(t, vm.EnumeratorValue(ic.VM)) - require.Equal(t, big.NewInt(int64(full[0])), ic.VM.Estack().Pop().BigInt()) - - ic.VM.Estack().PushVal(res) - require.NoError(t, vm.EnumeratorNext(ic.VM)) - require.True(t, ic.VM.Estack().Pop().Bool()) - - ic.VM.Estack().PushVal(res) - require.NoError(t, Keys(ic)) - require.NoError(t, vm.EnumeratorValue(ic.VM)) - require.Equal(t, big.NewInt(1), ic.VM.Estack().Pop().BigInt()) - - ic.VM.Estack().PushVal(res) - require.NoError(t, Values(ic)) - require.NoError(t, vm.EnumeratorValue(ic.VM)) - require.Equal(t, big.NewInt(int64(full[1])), ic.VM.Estack().Pop().BigInt()) + require.NoError(t, Next(ic)) + require.False(t, false, ic.VM.Estack().Pop().Bool()) } diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 53fd9de61..8c2bdf7db 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -7,7 +7,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -67,33 +66,25 @@ func TestStorageFind(t *testing.T) { require.NoError(t, err) var iter *stackitem.Interop - require.NotPanics(t, func() { iter = v.Estack().Top().Interop() }) + require.NotPanics(t, func() { iter = v.Estack().Pop().Interop() }) - require.NoError(t, enumerator.Next(context)) - require.True(t, v.Estack().Pop().Bool()) + for _, id := range []int{2, 0} { // sorted indices with mathing prefix + v.Estack().PushVal(iter) + require.NoError(t, iterator.Next(context)) + require.True(t, v.Estack().Pop().Bool()) + + v.Estack().PushVal(iter) + require.NoError(t, iterator.Value(context)) + + kv, ok := v.Estack().Pop().Value().([]stackitem.Item) + require.True(t, ok) + require.Len(t, kv, 2) + require.Equal(t, skeys[id], kv[0].Value()) + require.Equal(t, items[id].Value, kv[1].Value()) + } v.Estack().PushVal(iter) - require.NoError(t, iterator.Key(context)) - require.Equal(t, []byte{0x01, 0x01}, v.Estack().Pop().Bytes()) - - v.Estack().PushVal(iter) - require.NoError(t, enumerator.Value(context)) - require.Equal(t, []byte{0x03, 0x04, 0x05, 0x06}, v.Estack().Pop().Bytes()) - - v.Estack().PushVal(iter) - require.NoError(t, enumerator.Next(context)) - require.True(t, v.Estack().Pop().Bool()) - - v.Estack().PushVal(iter) - require.NoError(t, iterator.Key(context)) - require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes()) - - v.Estack().PushVal(iter) - require.NoError(t, enumerator.Value(context)) - require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes()) - - v.Estack().PushVal(iter) - require.NoError(t, enumerator.Next(context)) + require.NoError(t, iterator.Next(context)) require.False(t, v.Estack().Pop().Bool()) }) @@ -104,7 +95,7 @@ func TestStorageFind(t *testing.T) { err := storageFind(context) require.NoError(t, err) - require.NoError(t, enumerator.Next(context)) + require.NoError(t, iterator.Next(context)) require.False(t, v.Estack().Pop().Bool()) }) @@ -122,7 +113,7 @@ func TestStorageFind(t *testing.T) { v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID})) require.NoError(t, storageFind(context)) - require.NoError(t, enumerator.Next(context)) + require.NoError(t, iterator.Next(context)) require.False(t, v.Estack().Pop().Bool()) }) } diff --git a/pkg/core/interops.go b/pkg/core/interops.go index eff9006ac..08dccd64d 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -12,7 +12,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/binary" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" - "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/interop/json" @@ -58,13 +57,9 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10}, {Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: callflag.WriteStates}, {Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: callflag.WriteStates}, - {Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 1 << 4, ParamCount: 1}, - {Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1 << 15, ParamCount: 1}, - {Name: interopnames.SystemEnumeratorValue, Func: enumerator.Value, Price: 1 << 4, ParamCount: 1}, {Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 1 << 4, ParamCount: 1}, - {Name: interopnames.SystemIteratorKey, Func: iterator.Key, Price: 1 << 4, ParamCount: 1}, - {Name: interopnames.SystemIteratorKeys, Func: iterator.Keys, Price: 1 << 4, ParamCount: 1}, - {Name: interopnames.SystemIteratorValues, Func: iterator.Values, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemIteratorNext, Func: iterator.Next, Price: 1 << 15, ParamCount: 1}, + {Name: interopnames.SystemIteratorValue, Func: iterator.Value, Price: 1 << 4, ParamCount: 1}, {Name: interopnames.SystemJSONDeserialize, Func: json.Deserialize, Price: 1 << 14, ParamCount: 1}, {Name: interopnames.SystemJSONSerialize, Func: json.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 1 << 10, diff --git a/pkg/interop/enumerator/enumerator.go b/pkg/interop/enumerator/enumerator.go deleted file mode 100644 index 740f270a3..000000000 --- a/pkg/interop/enumerator/enumerator.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Package enumerator provides functions to work with enumerators. -*/ -package enumerator - -// Enumerator represents NEO enumerator type, it's an opaque data structure -// that can be used with functions from this package. It's similar to more -// widely used Iterator (see `iterator` package), but ranging over arrays -// or structures that have values with no explicit keys. -type Enumerator struct{} - -// Create creates a new enumerator from the given items (slice, structure, byte -// array and integer or boolean converted to byte array). New enumerator points -// at index -1 of its items, so the user of it has to advance it first with Next. -// This function uses `System.Enumerator.Create` syscall. -func Create(items interface{}) Enumerator { - return Enumerator{} -} - -// Next moves position of the given enumerator by one and returns a bool that -// tells whether there is a new value present in this new position. If it is, -// you can use Value to get it, if not then there are no more values in this -// enumerator. This function uses `System.Enumerator.Next` syscall. -func Next(e Enumerator) bool { - return true -} - -// Value returns current enumerator's item value, it's only valid to call it -// after Next returning true. This function uses `System.Enumerator.Value` syscall. -func Value(e Enumerator) interface{} { - return nil -} diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index d99729518..ff124bb84 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -3,8 +3,6 @@ Package iterator provides functions to work with Neo iterators. */ package iterator -import "github.com/nspcc-dev/neo-go/pkg/interop/enumerator" - // Iterator represents a Neo iterator, it's an opaque data structure that can // be properly created by Create or storage.Find. Unlike enumerators, iterators // range over key-value pairs, so it's convenient to use them for maps. This @@ -19,37 +17,17 @@ func Create(items interface{}) Iterator { return Iterator{} } -// Key returns iterator's key at current position. It's only valid to call after -// successful Next call. This function uses `System.Iterator.Key` syscall. -func Key(it Iterator) interface{} { - return nil -} - -// Keys returns Enumerator ranging over keys or the given Iterator. Note that -// this Enumerator is actually directly tied to the underlying Iterator, so that -// advancing it with Next will actually advance the Iterator too. This function -// uses `System.Iterator.Keys` syscall. -func Keys(it Iterator) enumerator.Enumerator { - return enumerator.Enumerator{} -} - // Next advances the iterator returning true if it is was successful (and you // can use Key or Value) and false otherwise (and there are no more elements in -// this Iterator). This function uses `System.Enumerator.Next` syscall. +// this Iterator). This function uses `System.Iterator.Next` syscall. func Next(it Iterator) bool { return true } // Value returns iterator's current value. It's only valid to call after -// successful Next call. This function uses `System.Enumerator.Value` syscall. +// successful Next call. This function uses `System.Iterator.Value` syscall. +// For slices the result is just value. +// For maps the result can be casted to a slice of 2 elements: key and value. func Value(it Iterator) interface{} { return nil } - -// Values returns Enumerator ranging over values or the given Iterator. Note that -// this Enumerator is actually directly tied to the underlying Iterator, so that -// advancing it with Next will actually advance the Iterator too. This function -// uses `System.Iterator.Values` syscall. -func Values(it Iterator) enumerator.Enumerator { - return enumerator.Enumerator{} -} diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index 295d5f3cc..cd4f65d17 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -27,20 +27,12 @@ var defaultVMInterops = []interopIDFuncPrice{ Func: runtimeLog, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeNotify)), Func: runtimeNotify, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, - {ID: interopnames.ToID([]byte(interopnames.SystemEnumeratorCreate)), - Func: EnumeratorCreate, Price: 1 << 4}, - {ID: interopnames.ToID([]byte(interopnames.SystemEnumeratorNext)), - Func: EnumeratorNext, Price: 1 << 15}, - {ID: interopnames.ToID([]byte(interopnames.SystemEnumeratorValue)), - Func: EnumeratorValue, Price: 1 << 4}, {ID: interopnames.ToID([]byte(interopnames.SystemIteratorCreate)), Func: IteratorCreate, Price: 1 << 4}, - {ID: interopnames.ToID([]byte(interopnames.SystemIteratorKey)), - Func: IteratorKey, Price: 1 << 4}, - {ID: interopnames.ToID([]byte(interopnames.SystemIteratorKeys)), - Func: IteratorKeys, Price: 1 << 4}, - {ID: interopnames.ToID([]byte(interopnames.SystemIteratorValues)), - Func: IteratorValues, Price: 1 << 4}, + {ID: interopnames.ToID([]byte(interopnames.SystemIteratorNext)), + Func: IteratorNext, Price: 1 << 15}, + {ID: interopnames.ToID([]byte(interopnames.SystemIteratorValue)), + Func: IteratorValue, Price: 1 << 4}, } func init() { @@ -112,45 +104,19 @@ func init() { }) } -// EnumeratorCreate handles syscall System.Enumerator.Create. -func EnumeratorCreate(v *VM) error { - var interop interface{} - switch t := v.Estack().Pop().value.(type) { - case *stackitem.Array, *stackitem.Struct: - interop = &arrayWrapper{ - index: -1, - value: t.Value().([]stackitem.Item), - } - default: - data, err := t.TryBytes() - if err != nil { - return fmt.Errorf("can not create enumerator from type %s: %w", t.Type(), err) - } - interop = &byteArrayWrapper{ - index: -1, - value: data, - } - } - v.Estack().Push(&Element{ - value: stackitem.NewInterop(interop), - }) - - return nil -} - -// EnumeratorNext handles syscall System.Enumerator.Next. -func EnumeratorNext(v *VM) error { +// IteratorNext handles syscall System.Enumerator.Next. +func IteratorNext(v *VM) error { iop := v.Estack().Pop().Interop() - arr := iop.Value().(enumerator) + arr := iop.Value().(iterator) v.Estack().PushVal(arr.Next()) return nil } -// EnumeratorValue handles syscall System.Enumerator.Value. -func EnumeratorValue(v *VM) error { +// IteratorValue handles syscall System.Enumerator.Value. +func IteratorValue(v *VM) error { iop := v.Estack().Pop().Interop() - arr := iop.Value().(enumerator) + arr := iop.Value().(iterator) v.Estack().Push(&Element{value: arr.Value()}) return nil @@ -190,34 +156,3 @@ func NewMapIterator(m *stackitem.Map) *stackitem.Interop { m: m.Value().([]stackitem.MapElement), }) } - -// IteratorKey handles syscall System.Iterator.Key. -func IteratorKey(v *VM) error { - iop := v.estack.Pop().Interop() - iter := iop.Value().(iterator) - v.Estack().Push(&Element{value: iter.Key()}) - - return nil -} - -// IteratorKeys handles syscall System.Iterator.Keys. -func IteratorKeys(v *VM) error { - iop := v.estack.Pop().Interop() - iter := iop.Value().(iterator) - v.Estack().Push(&Element{value: stackitem.NewInterop( - &keysWrapper{iter}, - )}) - - return nil -} - -// IteratorValues handles syscall System.Iterator.Values. -func IteratorValues(v *VM) error { - iop := v.estack.Pop().Interop() - iter := iop.Value().(iterator) - v.Estack().Push(&Element{value: stackitem.NewInterop( - &valuesWrapper{iter}, - )}) - - return nil -} diff --git a/pkg/vm/interop_iterators.go b/pkg/vm/interop_iterators.go index d87396c96..2fc07b5f7 100644 --- a/pkg/vm/interop_iterators.go +++ b/pkg/vm/interop_iterators.go @@ -7,7 +7,7 @@ import ( ) type ( - enumerator interface { + iterator interface { Next() bool Value() stackitem.Item } @@ -22,26 +22,10 @@ type ( value []byte } -) - -type ( - iterator interface { - enumerator - Key() stackitem.Item - } - mapWrapper struct { index int m []stackitem.MapElement } - - keysWrapper struct { - iter iterator - } - - valuesWrapper struct { - iter iterator - } ) func (a *arrayWrapper) Next() bool { @@ -57,10 +41,6 @@ func (a *arrayWrapper) Value() stackitem.Item { return a.value[a.index] } -func (a *arrayWrapper) Key() stackitem.Item { - return stackitem.Make(a.index) -} - func (a *byteArrayWrapper) Next() bool { if next := a.index + 1; next < len(a.value) { a.index = next @@ -74,10 +54,6 @@ func (a *byteArrayWrapper) Value() stackitem.Item { return stackitem.NewBigInteger(big.NewInt(int64(a.value[a.index]))) } -func (a *byteArrayWrapper) Key() stackitem.Item { - return stackitem.Make(a.index) -} - func (m *mapWrapper) Next() bool { if next := m.index + 1; next < len(m.m) { m.index = next @@ -88,25 +64,8 @@ func (m *mapWrapper) Next() bool { } func (m *mapWrapper) Value() stackitem.Item { - return m.m[m.index].Value -} - -func (m *mapWrapper) Key() stackitem.Item { - return m.m[m.index].Key -} - -func (e *keysWrapper) Next() bool { - return e.iter.Next() -} - -func (e *keysWrapper) Value() stackitem.Item { - return e.iter.Key() -} - -func (e *valuesWrapper) Next() bool { - return e.iter.Next() -} - -func (e *valuesWrapper) Value() stackitem.Item { - return e.iter.Value() + return stackitem.NewStruct([]stackitem.Item{ + m.m[m.index].Key, + m.m[m.index].Value, + }) } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index be0c03103..4855d28ea 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -468,20 +468,16 @@ func TestPushData4BigN(t *testing.T) { checkVMFailed(t, vm) } -func getEnumeratorProg(n int, isIter bool) (prog []byte) { +func getIteratorProg(n int) (prog []byte) { prog = []byte{byte(opcode.INITSSLOT), 1, byte(opcode.STSFLD0)} for i := 0; i < n; i++ { prog = append(prog, byte(opcode.LDSFLD0)) - prog = append(prog, getSyscallProg(interopnames.SystemEnumeratorNext)...) + prog = append(prog, getSyscallProg(interopnames.SystemIteratorNext)...) prog = append(prog, byte(opcode.LDSFLD0)) - prog = append(prog, getSyscallProg(interopnames.SystemEnumeratorValue)...) - if isIter { - prog = append(prog, byte(opcode.LDSFLD0)) - prog = append(prog, getSyscallProg(interopnames.SystemIteratorKey)...) - } + prog = append(prog, getSyscallProg(interopnames.SystemIteratorValue)...) } prog = append(prog, byte(opcode.LDSFLD0)) - prog = append(prog, getSyscallProg(interopnames.SystemEnumeratorNext)...) + prog = append(prog, getSyscallProg(interopnames.SystemIteratorNext)...) return } @@ -494,10 +490,9 @@ func checkEnumeratorStack(t *testing.T, vm *VM, arr []stackitem.Item) { } } -func testIterableCreate(t *testing.T, typ string, isByteArray bool) { - isIter := typ == "Iterator" - prog := getSyscallProg("System." + typ + ".Create") - prog = append(prog, getEnumeratorProg(2, isIter)...) +func testIterableCreate(t *testing.T, isByteArray bool) { + prog := getSyscallProg(interopnames.SystemIteratorCreate) + prog = append(prog, getIteratorProg(2)...) vm := load(prog) arr := []stackitem.Item{ @@ -512,32 +507,16 @@ func testIterableCreate(t *testing.T, typ string, isByteArray bool) { } runVM(t, vm) - if isIter { - checkEnumeratorStack(t, vm, []stackitem.Item{ - stackitem.Make(1), arr[1], stackitem.NewBool(true), - stackitem.Make(0), arr[0], stackitem.NewBool(true), - }) - } else { - checkEnumeratorStack(t, vm, []stackitem.Item{ - arr[1], stackitem.NewBool(true), - arr[0], stackitem.NewBool(true), - }) - } -} - -func TestEnumeratorCreate(t *testing.T) { - t.Run("Array", func(t *testing.T) { testIterableCreate(t, "Enumerator", false) }) - t.Run("ByteArray", func(t *testing.T) { testIterableCreate(t, "Enumerator", true) }) - t.Run("Interop", func(t *testing.T) { - v := New() - v.Estack().PushVal(stackitem.NewInterop([]byte{42})) - require.Error(t, EnumeratorCreate(v)) + checkEnumeratorStack(t, vm, []stackitem.Item{ + arr[1], stackitem.NewBool(true), + arr[0], stackitem.NewBool(true), }) } func TestIteratorCreate(t *testing.T) { - t.Run("Array", func(t *testing.T) { testIterableCreate(t, "Iterator", false) }) - t.Run("ByteArray", func(t *testing.T) { testIterableCreate(t, "Iterator", true) }) + t.Run("Array", func(t *testing.T) { testIterableCreate(t, false) }) + t.Run("ByteArray", func(t *testing.T) { testIterableCreate(t, true) }) + t.Run("Map", func(f *testing.T) {}) t.Run("Interop", func(t *testing.T) { v := New() v.Estack().PushVal(stackitem.NewInterop([]byte{42})) @@ -545,54 +524,6 @@ func TestIteratorCreate(t *testing.T) { }) } -func TestIteratorKeys(t *testing.T) { - prog := getSyscallProg(interopnames.SystemIteratorCreate) - prog = append(prog, getSyscallProg(interopnames.SystemIteratorKeys)...) - prog = append(prog, getEnumeratorProg(2, false)...) - - v := load(prog) - arr := stackitem.NewArray([]stackitem.Item{ - stackitem.NewBool(false), - stackitem.NewBigInteger(big.NewInt(42)), - }) - v.estack.PushVal(arr) - - runVM(t, v) - - checkEnumeratorStack(t, v, []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBool(true), - stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBool(true), - }) -} - -func TestIteratorValues(t *testing.T) { - prog := getSyscallProg(interopnames.SystemIteratorCreate) - prog = append(prog, getSyscallProg(interopnames.SystemIteratorValues)...) - prog = append(prog, getEnumeratorProg(2, false)...) - - v := load(prog) - m := stackitem.NewMap() - m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBool(false)) - m.Add(stackitem.NewByteArray([]byte{32}), stackitem.NewByteArray([]byte{7})) - v.estack.PushVal(m) - - runVM(t, v) - require.Equal(t, 5, v.estack.Len()) - require.Equal(t, stackitem.NewBool(false), v.estack.Peek(0).value) - - // Map values can be enumerated in any order. - i1, i2 := 1, 3 - if _, ok := v.estack.Peek(i1).value.(*stackitem.Bool); !ok { - i1, i2 = i2, i1 - } - - require.Equal(t, stackitem.NewBool(false), v.estack.Peek(i1).value) - require.Equal(t, stackitem.NewByteArray([]byte{7}), v.estack.Peek(i2).value) - - require.Equal(t, stackitem.NewBool(true), v.estack.Peek(2).value) - require.Equal(t, stackitem.NewBool(true), v.estack.Peek(4).value) -} - func getSyscallProg(name string) (prog []byte) { buf := io.NewBufBinWriter() emit.Syscall(buf.BinWriter, name)