Merge pull request #2128 from nspcc-dev/vm-update-int

Some VM optimizations
This commit is contained in:
Roman Khimov 2021-08-13 16:16:01 +03:00 committed by GitHub
commit 5b12dd2025
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 222 additions and 219 deletions

View file

@ -2097,20 +2097,17 @@ func (c *codegen) resolveFuncDecls(f *ast.File, pkg *types.Package) {
func (c *codegen) writeJumps(b []byte) ([]byte, error) {
ctx := vm.NewContext(b)
var offsets []int
for op, _, err := ctx.Next(); err == nil && ctx.IP() < len(b); op, _, err = ctx.Next() {
for op, param, err := ctx.Next(); err == nil && ctx.IP() < len(b); op, param, err = ctx.Next() {
switch op {
case opcode.JMP, opcode.JMPIFNOT, opcode.JMPIF, opcode.CALL,
opcode.JMPEQ, opcode.JMPNE,
opcode.JMPGT, opcode.JMPGE, opcode.JMPLE, opcode.JMPLT:
case opcode.TRYL:
nextIP := ctx.NextIP()
catchArg := b[nextIP-8:]
_, err := c.replaceLabelWithOffset(ctx.IP(), catchArg)
_, err := c.replaceLabelWithOffset(ctx.IP(), param)
if err != nil {
return nil, err
}
finallyArg := b[nextIP-4:]
_, err = c.replaceLabelWithOffset(ctx.IP(), finallyArg)
_, err = c.replaceLabelWithOffset(ctx.IP(), param[4:])
if err != nil {
return nil, err
}
@ -2118,10 +2115,7 @@ func (c *codegen) writeJumps(b []byte) ([]byte, error) {
opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLEL, opcode.JMPLTL,
opcode.CALLL, opcode.PUSHA, opcode.ENDTRYL:
// we can't use arg returned by ctx.Next() because it is copied
nextIP := ctx.NextIP()
arg := b[nextIP-4:]
offset, err := c.replaceLabelWithOffset(ctx.IP(), arg)
offset, err := c.replaceLabelWithOffset(ctx.IP(), param)
if err != nil {
return nil, err
}

View file

@ -17,7 +17,7 @@ func ParameterFromStackItem(i stackitem.Item, seen map[stackitem.Item]bool) Para
Type: IntegerType,
Value: i.Value().(*big.Int).Int64(),
}
case *stackitem.Bool:
case stackitem.Bool:
return Parameter{
Type: BoolType,
Value: i.Value().(bool),

View file

@ -97,9 +97,9 @@ func (c *Context) NextIP() int {
return c.nextip
}
// Next returns the next instruction to execute with its parameter if any. After
// its invocation the instruction pointer points to the instruction being
// returned.
// Next returns the next instruction to execute with its parameter if any.
// The parameter is not copied and shouldn't be written to. After its invocation
// the instruction pointer points to the instruction being returned.
func (c *Context) Next() (opcode.Opcode, []byte, error) {
var err error
@ -171,8 +171,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
if err != nil {
return instr, nil, err
}
parameter := make([]byte, numtoread)
copy(parameter, c.prog[c.nextip:c.nextip+numtoread])
parameter := c.prog[c.nextip : c.nextip+numtoread]
c.nextip += numtoread
return instr, parameter, nil
}

View file

@ -195,7 +195,7 @@ func compareItems(t *testing.T, a, b stackitem.Item) {
require.Equal(t, val, ac.Value().(*big.Int).Int64())
case *stackitem.ByteArray:
require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64())
case *stackitem.Bool:
case stackitem.Bool:
if ac.Value().(bool) {
require.Equal(t, val, int64(1))
} else {
@ -208,6 +208,28 @@ func compareItems(t *testing.T, a, b stackitem.Item) {
p, ok := b.(*stackitem.Pointer)
require.True(t, ok)
require.Equal(t, si.Position(), p.Position()) // there no script in test files
case *stackitem.Array, *stackitem.Struct:
require.Equal(t, a.Type(), b.Type())
as := a.Value().([]stackitem.Item)
bs := a.Value().([]stackitem.Item)
require.Equal(t, len(as), len(bs))
for i := range as {
compareItems(t, as[i], bs[i])
}
case *stackitem.Map:
require.Equal(t, a.Type(), b.Type())
as := a.Value().([]stackitem.MapElement)
bs := a.Value().([]stackitem.MapElement)
require.Equal(t, len(as), len(bs))
for i := range as {
compareItems(t, as[i].Key, bs[i].Key)
compareItems(t, as[i].Value, bs[i].Value)
}
default:
require.Equal(t, a, b)
}

View file

@ -5,15 +5,19 @@ import (
)
// refCounter represents reference counter for the VM.
type refCounter struct {
items map[stackitem.Item]int
size int
}
type refCounter int
type (
rcInc interface {
IncRC() int
}
rcDec interface {
DecRC() int
}
)
func newRefCounter() *refCounter {
return &refCounter{
items: make(map[stackitem.Item]int),
}
return new(refCounter)
}
// Add adds an item to the reference counter.
@ -21,23 +25,20 @@ func (r *refCounter) Add(item stackitem.Item) {
if r == nil {
return
}
r.size++
*r++
switch item.(type) {
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item]++; r.items[item] > 1 {
return
irc, ok := item.(rcInc)
if !ok || irc.IncRC() > 1 {
return
}
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Add(it)
}
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Add(it)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Add(t.Value().([]stackitem.MapElement)[i].Value)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Add(t.Value().([]stackitem.MapElement)[i].Value)
}
}
}
@ -47,26 +48,20 @@ func (r *refCounter) Remove(item stackitem.Item) {
if r == nil {
return
}
r.size--
*r--
switch item.(type) {
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item] > 1 {
r.items[item]--
return
irc, ok := item.(rcDec)
if !ok || irc.DecRC() > 0 {
return
}
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Remove(it)
}
delete(r.items, item)
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Remove(it)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
}
}
}

View file

@ -10,24 +10,34 @@ import (
func TestRefCounter_Add(t *testing.T) {
r := newRefCounter()
require.Equal(t, 0, r.size)
require.Equal(t, 0, int(*r))
r.Add(stackitem.Null{})
require.Equal(t, 1, r.size)
require.Equal(t, 1, int(*r))
r.Add(stackitem.Null{})
require.Equal(t, 2, r.size) // count scalar items twice
require.Equal(t, 2, int(*r)) // count scalar items twice
arr := stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1}), stackitem.NewBool(false)})
r.Add(arr)
require.Equal(t, 5, r.size) // array + 2 elements
require.Equal(t, 5, int(*r)) // array + 2 elements
r.Add(arr)
require.Equal(t, 6, r.size) // count only array
require.Equal(t, 6, int(*r)) // count only array
r.Remove(arr)
require.Equal(t, 5, r.size)
require.Equal(t, 5, int(*r))
r.Remove(arr)
require.Equal(t, 2, r.size)
require.Equal(t, 2, int(*r))
}
func BenchmarkRefCounter_Add(b *testing.B) {
a := stackitem.NewArray(nil)
rc := newRefCounter()
b.ResetTimer()
for i := 0; i < b.N; i++ {
rc.Add(a)
}
}

View file

@ -2,7 +2,6 @@ package stackitem
import (
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
@ -90,45 +89,23 @@ func mkInvConversion(from Item, to Type) error {
func Make(v interface{}) Item {
switch val := v.(type) {
case int:
return &BigInteger{
value: big.NewInt(int64(val)),
}
return (*BigInteger)(big.NewInt(int64(val)))
case int64:
return &BigInteger{
value: big.NewInt(val),
}
return (*BigInteger)(big.NewInt(val))
case uint8:
return &BigInteger{
value: big.NewInt(int64(val)),
}
return (*BigInteger)(big.NewInt(int64(val)))
case uint16:
return &BigInteger{
value: big.NewInt(int64(val)),
}
return (*BigInteger)(big.NewInt(int64(val)))
case uint32:
return &BigInteger{
value: big.NewInt(int64(val)),
}
return (*BigInteger)(big.NewInt(int64(val)))
case uint64:
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, val)
bigInt := big.NewInt(0)
bigInt.SetBytes(b)
return &BigInteger{
value: bigInt,
}
return (*BigInteger)(new(big.Int).SetUint64(val))
case []byte:
return &ByteArray{
value: val,
}
return NewByteArray(val)
case string:
return &ByteArray{
value: []byte(val),
}
return NewByteArray([]byte(val))
case bool:
return &Bool{
value: val,
}
return Bool(val)
case []Item:
return &Array{
value: val,
@ -207,6 +184,7 @@ func convertPrimitive(item Item, typ Type) (Item, error) {
// Struct represents a struct on the stack.
type Struct struct {
value []Item
rc
}
// NewStruct returns an new Struct object.
@ -330,7 +308,7 @@ func (i *Struct) Clone() (*Struct, error) {
}
func (i *Struct) clone(limit *int) (*Struct, error) {
ret := &Struct{make([]Item, len(i.value))}
ret := &Struct{value: make([]Item, len(i.value))}
for j := range i.value {
*limit--
if *limit < 0 {
@ -402,9 +380,7 @@ func (i Null) Convert(typ Type) (Item, error) {
}
// BigInteger represents a big integer on the stack.
type BigInteger struct {
value *big.Int
}
type BigInteger big.Int
// NewBigInteger returns an new BigInteger object.
func NewBigInteger(value *big.Int) *BigInteger {
@ -420,19 +396,22 @@ func NewBigInteger(value *big.Int) *BigInteger {
panic(errTooBigInteger)
}
}
return &BigInteger{
value: value,
}
return (*BigInteger)(value)
}
// Big casts i to the big.Int type.
func (i *BigInteger) Big() *big.Int {
return (*big.Int)(i)
}
// Bytes converts i to a slice of bytes.
func (i *BigInteger) Bytes() []byte {
return bigint.ToBytes(i.value)
return bigint.ToBytes(i.Big())
}
// TryBool implements Item interface.
func (i *BigInteger) TryBool() (bool, error) {
return i.value.Sign() != 0, nil
return i.Big().Sign() != 0, nil
}
// TryBytes implements Item interface.
@ -442,7 +421,7 @@ func (i *BigInteger) TryBytes() ([]byte, error) {
// TryInteger implements Item interface.
func (i *BigInteger) TryInteger() (*big.Int, error) {
return i.value, nil
return i.Big(), nil
}
// Equals implements Item interface.
@ -453,12 +432,12 @@ func (i *BigInteger) Equals(s Item) bool {
return false
}
val, ok := s.(*BigInteger)
return ok && i.value.Cmp(val.value) == 0
return ok && i.Big().Cmp(val.Big()) == 0
}
// Value implements Item interface.
func (i *BigInteger) Value() interface{} {
return i.value
return i.Big()
}
func (i *BigInteger) String() string {
@ -468,7 +447,7 @@ func (i *BigInteger) String() string {
// Dup implements Item interface.
func (i *BigInteger) Dup() Item {
n := new(big.Int)
return &BigInteger{n.Set(i.value)}
return (*BigInteger)(n.Set(i.Big()))
}
// Type implements Item interface.
@ -481,103 +460,95 @@ func (i *BigInteger) Convert(typ Type) (Item, error) {
// MarshalJSON implements the json.Marshaler interface.
func (i *BigInteger) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value)
return json.Marshal(i.Big())
}
// Bool represents a boolean Item.
type Bool struct {
value bool
}
type Bool bool
// NewBool returns an new Bool object.
func NewBool(val bool) *Bool {
return &Bool{
value: val,
}
func NewBool(val bool) Bool {
return Bool(val)
}
// Value implements Item interface.
func (i *Bool) Value() interface{} {
return i.value
func (i Bool) Value() interface{} {
return bool(i)
}
// MarshalJSON implements the json.Marshaler interface.
func (i *Bool) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value)
func (i Bool) MarshalJSON() ([]byte, error) {
return json.Marshal(bool(i))
}
func (i *Bool) String() string {
func (i Bool) String() string {
return "Boolean"
}
// Dup implements Item interface.
func (i *Bool) Dup() Item {
return &Bool{i.value}
func (i Bool) Dup() Item {
return i
}
// TryBool implements Item interface.
func (i *Bool) TryBool() (bool, error) { return i.value, nil }
func (i Bool) TryBool() (bool, error) { return bool(i), nil }
// Bytes converts Bool to bytes.
func (i *Bool) Bytes() []byte {
if i.value {
func (i Bool) Bytes() []byte {
if i {
return []byte{1}
}
return []byte{0}
}
// TryBytes implements Item interface.
func (i *Bool) TryBytes() ([]byte, error) {
func (i Bool) TryBytes() ([]byte, error) {
return i.Bytes(), nil
}
// TryInteger implements Item interface.
func (i *Bool) TryInteger() (*big.Int, error) {
if i.value {
func (i Bool) TryInteger() (*big.Int, error) {
if i {
return big.NewInt(1), nil
}
return big.NewInt(0), nil
}
// Equals implements Item interface.
func (i *Bool) Equals(s Item) bool {
func (i Bool) Equals(s Item) bool {
if i == s {
return true
} else if s == nil {
return false
}
val, ok := s.(*Bool)
return ok && i.value == val.value
val, ok := s.(Bool)
return ok && i == val
}
// Type implements Item interface.
func (i *Bool) Type() Type { return BooleanT }
func (i Bool) Type() Type { return BooleanT }
// Convert implements Item interface.
func (i *Bool) Convert(typ Type) (Item, error) {
func (i Bool) Convert(typ Type) (Item, error) {
return convertPrimitive(i, typ)
}
// ByteArray represents a byte array on the stack.
type ByteArray struct {
value []byte
}
type ByteArray []byte
// NewByteArray returns an new ByteArray object.
func NewByteArray(b []byte) *ByteArray {
return &ByteArray{
value: b,
}
return (*ByteArray)(&b)
}
// Value implements Item interface.
func (i *ByteArray) Value() interface{} {
return i.value
return []byte(*i)
}
// MarshalJSON implements the json.Marshaler interface.
func (i *ByteArray) MarshalJSON() ([]byte, error) {
return json.Marshal(hex.EncodeToString(i.value))
return json.Marshal(hex.EncodeToString(*i))
}
func (i *ByteArray) String() string {
@ -586,10 +557,10 @@ func (i *ByteArray) String() string {
// TryBool implements Item interface.
func (i *ByteArray) TryBool() (bool, error) {
if len(i.value) > MaxBigIntegerSizeBits/8 {
if len(*i) > MaxBigIntegerSizeBits/8 {
return false, errTooBigInteger
}
for _, b := range i.value {
for _, b := range *i {
if b != 0 {
return true, nil
}
@ -598,21 +569,21 @@ func (i *ByteArray) TryBool() (bool, error) {
}
// TryBytes implements Item interface.
func (i *ByteArray) TryBytes() ([]byte, error) {
return i.value, nil
func (i ByteArray) TryBytes() ([]byte, error) {
return i, nil
}
// TryInteger implements Item interface.
func (i *ByteArray) TryInteger() (*big.Int, error) {
if len(i.value) > MaxBigIntegerSizeBits/8 {
func (i ByteArray) TryInteger() (*big.Int, error) {
if len(i) > MaxBigIntegerSizeBits/8 {
return nil, errTooBigInteger
}
return bigint.FromBytes(i.value), nil
return bigint.FromBytes(i), nil
}
// Equals implements Item interface.
func (i *ByteArray) Equals(s Item) bool {
if len(i.value) > MaxByteArrayComparableSize {
if len(*i) > MaxByteArrayComparableSize {
panic(errTooBigComparable)
}
if i == s {
@ -624,15 +595,16 @@ func (i *ByteArray) Equals(s Item) bool {
if !ok {
return false
}
if len(val.value) > MaxByteArrayComparableSize {
if len(*val) > MaxByteArrayComparableSize {
panic(errTooBigComparable)
}
return bytes.Equal(i.value, val.value)
return bytes.Equal(*i, *val)
}
// Dup implements Item interface.
func (i *ByteArray) Dup() Item {
return &ByteArray{slice.Copy(i.value)}
ba := slice.Copy(*i)
return (*ByteArray)(&ba)
}
// Type implements Item interface.
@ -646,6 +618,7 @@ func (i *ByteArray) Convert(typ Type) (Item, error) {
// Array represents a new Array object.
type Array struct {
value []Item
rc
}
// NewArray returns a new Array object.
@ -746,6 +719,7 @@ type MapElement struct {
// if need be.
type Map struct {
value []MapElement
rc
}
// NewMap returns new Map object.
@ -861,7 +835,7 @@ func (i *Map) Drop(index int) {
// key.
func IsValidMapKey(key Item) error {
switch key.(type) {
case *Bool, *BigInteger:
case Bool, *BigInteger:
return nil
case *ByteArray:
size := len(key.Value().([]byte))
@ -1045,20 +1019,16 @@ func (p *Pointer) Position() int {
}
// Buffer represents represents Buffer stack item.
type Buffer struct {
value []byte
}
type Buffer []byte
// NewBuffer returns a new Buffer object.
func NewBuffer(b []byte) *Buffer {
return &Buffer{
value: b,
}
return (*Buffer)(&b)
}
// Value implements Item interface.
func (i *Buffer) Value() interface{} {
return i.value
return []byte(*i)
}
// String implements fmt.Stringer interface.
@ -1073,7 +1043,7 @@ func (i *Buffer) TryBool() (bool, error) {
// TryBytes implements Item interface.
func (i *Buffer) TryBytes() ([]byte, error) {
return i.value, nil
return *i, nil
}
// TryInteger implements Item interface.
@ -1093,7 +1063,7 @@ func (i *Buffer) Dup() Item {
// MarshalJSON implements the json.Marshaler interface.
func (i *Buffer) MarshalJSON() ([]byte, error) {
return json.Marshal(hex.EncodeToString(i.value))
return json.Marshal(hex.EncodeToString(*i))
}
// Type implements Item interface.
@ -1107,12 +1077,12 @@ func (i *Buffer) Convert(typ Type) (Item, error) {
case BufferT:
return i, nil
case ByteArrayT:
return NewByteArray(slice.Copy(i.value)), nil
return NewByteArray(slice.Copy(*i)), nil
case IntegerT:
if len(i.value) > MaxBigIntegerSizeBits/8 {
if len(*i) > MaxBigIntegerSizeBits/8 {
return nil, errTooBigInteger
}
return NewBigInteger(bigint.FromBytes(i.value)), nil
return NewBigInteger(bigint.FromBytes(*i)), nil
default:
return nil, mkInvConversion(i, typ)
}
@ -1120,7 +1090,7 @@ func (i *Buffer) Convert(typ Type) (Item, error) {
// Len returns length of Buffer value.
func (i *Buffer) Len() int {
return len(i.value)
return len(*i)
}
// DeepCopy returns new deep copy of the provided item.
@ -1162,17 +1132,14 @@ func deepCopy(item Item, seen map[Item]Item) Item {
}
return m
case *BigInteger:
bi := new(big.Int).SetBytes(it.value.Bytes())
if it.value.Sign() == -1 {
bi.Neg(bi)
}
return NewBigInteger(bi)
bi := new(big.Int).Set(it.Big())
return (*BigInteger)(bi)
case *ByteArray:
return NewByteArray(slice.Copy(it.value))
return NewByteArray(slice.Copy(*it))
case *Buffer:
return NewBuffer(slice.Copy(it.value))
case *Bool:
return NewBool(it.value)
return NewBuffer(slice.Copy(*it))
case Bool:
return it
case *Pointer:
return NewPointerWithHash(it.pos, it.script, it.hash)
case *Interop:

View file

@ -15,67 +15,67 @@ var makeStackItemTestCases = []struct {
}{
{
input: int64(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: int16(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: 3,
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: uint8(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: uint16(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: uint32(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: uint64(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: big.NewInt(3),
result: &BigInteger{value: big.NewInt(3)},
result: (*BigInteger)(big.NewInt(3)),
},
{
input: []byte{1, 2, 3, 4},
result: &ByteArray{value: []byte{1, 2, 3, 4}},
result: NewByteArray([]byte{1, 2, 3, 4}),
},
{
input: []byte{},
result: &ByteArray{value: []byte{}},
result: NewByteArray([]byte{}),
},
{
input: "bla",
result: &ByteArray{value: []byte("bla")},
result: NewByteArray([]byte("bla")),
},
{
input: "",
result: &ByteArray{value: []byte{}},
result: NewByteArray([]byte{}),
},
{
input: true,
result: &Bool{value: true},
result: Bool(true),
},
{
input: false,
result: &Bool{value: false},
result: Bool(false),
},
{
input: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}},
result: &Array{value: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}}},
input: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})},
result: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}},
},
{
input: []int{1, 2, 3},
result: &Array{value: []Item{&BigInteger{value: big.NewInt(1)}, &BigInteger{value: big.NewInt(2)}, &BigInteger{value: big.NewInt(3)}}},
result: &Array{value: []Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}},
},
}
@ -281,18 +281,18 @@ var equalsTestCases = map[string][]struct {
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
item2: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}),
item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}),
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}}),
item1: NewArray([]Item{(*BigInteger)(big.NewInt(1))}),
item2: NewBigInteger(big.NewInt(1)),
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
item2: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(4)}}),
item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}),
item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(4))}),
result: false,
},
},
@ -441,7 +441,7 @@ var marshalJSONTestCases = []struct {
result: []byte(`"010203"`),
},
{
input: &Array{value: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}}},
input: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}},
result: []byte(`[3,"010203"]`),
},
{
@ -459,8 +459,8 @@ func TestMarshalJSON(t *testing.T) {
switch testCase.input.(type) {
case *BigInteger:
actual, err = testCase.input.(*BigInteger).MarshalJSON()
case *Bool:
actual, err = testCase.input.(*Bool).MarshalJSON()
case Bool:
actual, err = testCase.input.(Bool).MarshalJSON()
case *ByteArray:
actual, err = testCase.input.(*ByteArray).MarshalJSON()
case *Array:
@ -532,7 +532,9 @@ func TestDeepCopy(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
actual := DeepCopy(tc.item)
require.Equal(t, tc.item, actual)
require.False(t, actual == tc.item)
if tc.item.Type() != BooleanT {
require.False(t, actual == tc.item)
}
})
}

View file

@ -110,18 +110,18 @@ func toJSON(data []byte, seen map[Item]sliceNoPointer, item Item) ([]byte, error
data = append(data, '}')
seen[item] = sliceNoPointer{start, len(data)}
case *BigInteger:
if it.value.CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 {
if it.Big().CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 {
return nil, fmt.Errorf("%w (MaxAllowedInteger)", ErrInvalidValue)
}
data = append(data, it.value.String()...)
data = append(data, it.Big().String()...)
case *ByteArray, *Buffer:
raw, err := itemToJSONString(it)
if err != nil {
return nil, err
}
data = append(data, raw...)
case *Bool:
if it.value {
case Bool:
if it {
data = append(data, "true"...)
} else {
data = append(data, "false"...)
@ -288,12 +288,12 @@ func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
}
value = arr
delete(seen, item)
case *Bool:
value = it.value
case Bool:
value = bool(it)
case *Buffer, *ByteArray:
value = base64.StdEncoding.EncodeToString(it.Value().([]byte))
case *BigInteger:
value = it.value.String()
value = it.Big().String()
case *Map:
if seen[item] {
return "", ErrRecursive

View file

@ -0,0 +1,15 @@
package stackitem
type rc struct {
count int
}
func (r *rc) IncRC() int {
r.count++
return r.count
}
func (r *rc) DecRC() int {
r.count--
return r.count
}

View file

@ -103,9 +103,9 @@ func (w *serContext) serialize(item Item) error {
data := t.Value().([]byte)
w.appendVarUint(uint64(len(data)))
w.data = append(w.data, data...)
case *Bool:
case Bool:
w.data = append(w.data, byte(BooleanT))
if t.Value().(bool) {
if t {
w.data = append(w.data, 1)
} else {
w.data = append(w.data, 0)

View file

@ -105,7 +105,6 @@ func NewWithTrigger(t trigger.Type) *VM {
Invocations: make(map[util.Uint160]int),
}
vm.refs.items = make(map[stackitem.Item]int)
initStack(&vm.istack, "invocation", nil)
vm.estack = newStack("evaluation", &vm.refs)
return vm
@ -520,7 +519,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if errRecover := recover(); errRecover != nil {
v.state = FaultState
err = newError(ctx.ip, op, errRecover)
} else if v.refs.size > MaxStackSize {
} else if v.refs > MaxStackSize {
v.state = FaultState
err = newError(ctx.ip, op, "stack is too big")
}

View file

@ -415,7 +415,7 @@ func TestStackLimit(t *testing.T) {
require.NoError(t, vm.Step(), "failed to initialize static slot")
for i := range expected {
require.NoError(t, vm.Step())
require.Equal(t, expected[i].size, vm.refs.size, "i: %d", i)
require.Equal(t, expected[i].size, int(vm.refs), "i: %d", i)
}
}
@ -829,7 +829,7 @@ func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func
if result != nil {
f = func(t *testing.T, v *VM) {
require.Equal(t, 1, v.estack.Len())
require.Equal(t, stackitem.Make(result), v.estack.Pop().value)
require.Equal(t, stackitem.Make(result).Value(), v.estack.Pop().Value())
}
}
return getCustomTestFuncForVM(prog, f, args...)
@ -1761,7 +1761,7 @@ func TestPACK_UNPACK_MaxSize(t *testing.T) {
vm.estack.PushVal(len(elements))
runVM(t, vm)
// check reference counter = 1+1+1024
assert.Equal(t, 1+1+len(elements), vm.refs.size)
assert.Equal(t, 1+1+len(elements), int(vm.refs))
assert.Equal(t, 1+1+len(elements), vm.estack.Len()) // canary + length + elements
assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64())
for i := 0; i < len(elements); i++ {
@ -1784,7 +1784,7 @@ func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) {
vm.estack.PushVal(len(elements))
runVM(t, vm)
// check reference counter = 1+1+1024
assert.Equal(t, 1+1+len(elements), vm.refs.size)
assert.Equal(t, 1+1+len(elements), int(vm.refs))
assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array()
assert.Equal(t, len(elements), len(a))
@ -1959,7 +1959,7 @@ func testCLEARITEMS(t *testing.T, item stackitem.Item) {
v.estack.PushVal(item)
runVM(t, v)
require.Equal(t, 2, v.estack.Len())
require.EqualValues(t, 2, v.refs.size) // empty collection + it's size
require.EqualValues(t, 2, int(v.refs)) // empty collection + it's size
require.EqualValues(t, 0, v.estack.Pop().BigInt().Int64())
}