forked from TrueCloudLab/neoneo-go
core,vm: remove System.Enumerator.*
interops
Map iterator now returns key-value pair, while array/byte-array iterators work like old enumerators. Follow neo-project/neo#2190.
This commit is contained in:
parent
d04b000748
commit
2130e17f0c
16 changed files with 86 additions and 422 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue