diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 25871e69c..8a2bd1673 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -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 } diff --git a/pkg/smartcontract/convertor.go b/pkg/smartcontract/convertor.go index c592807b8..d4d9e45d7 100644 --- a/pkg/smartcontract/convertor.go +++ b/pkg/smartcontract/convertor.go @@ -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), diff --git a/pkg/vm/context.go b/pkg/vm/context.go index af20090cc..e682cda1b 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -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 } diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index fd3f87860..5d7f6acf5 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -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) } diff --git a/pkg/vm/ref_counter.go b/pkg/vm/ref_counter.go index 182177f6c..e3d787a0a 100644 --- a/pkg/vm/ref_counter.go +++ b/pkg/vm/ref_counter.go @@ -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) } } } diff --git a/pkg/vm/ref_counter_test.go b/pkg/vm/ref_counter_test.go index b50390609..9e0a82d99 100644 --- a/pkg/vm/ref_counter_test.go +++ b/pkg/vm/ref_counter_test.go @@ -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) + } } diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 2f9e95ac1..14ed782e8 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -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: diff --git a/pkg/vm/stackitem/item_test.go b/pkg/vm/stackitem/item_test.go index c114229ce..72920c8e2 100644 --- a/pkg/vm/stackitem/item_test.go +++ b/pkg/vm/stackitem/item_test.go @@ -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) + } }) } diff --git a/pkg/vm/stackitem/json.go b/pkg/vm/stackitem/json.go index 51a89b43f..bb01076b0 100644 --- a/pkg/vm/stackitem/json.go +++ b/pkg/vm/stackitem/json.go @@ -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 diff --git a/pkg/vm/stackitem/reference.go b/pkg/vm/stackitem/reference.go new file mode 100644 index 000000000..0102d6b0e --- /dev/null +++ b/pkg/vm/stackitem/reference.go @@ -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 +} diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index 84e01bedc..8db760e37 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -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) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 4856270e4..2f973ed25 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -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") } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 8fb07646f..3391ddeca 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -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()) }