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

View file

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

View file

@ -97,9 +97,9 @@ func (c *Context) NextIP() int {
return c.nextip return c.nextip
} }
// Next returns the next instruction to execute with its parameter if any. After // Next returns the next instruction to execute with its parameter if any.
// its invocation the instruction pointer points to the instruction being // The parameter is not copied and shouldn't be written to. After its invocation
// returned. // the instruction pointer points to the instruction being returned.
func (c *Context) Next() (opcode.Opcode, []byte, error) { func (c *Context) Next() (opcode.Opcode, []byte, error) {
var err error var err error
@ -171,8 +171,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
if err != nil { if err != nil {
return instr, nil, err return instr, nil, err
} }
parameter := make([]byte, numtoread) parameter := c.prog[c.nextip : c.nextip+numtoread]
copy(parameter, c.prog[c.nextip:c.nextip+numtoread])
c.nextip += numtoread c.nextip += numtoread
return instr, parameter, nil 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()) require.Equal(t, val, ac.Value().(*big.Int).Int64())
case *stackitem.ByteArray: case *stackitem.ByteArray:
require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64()) require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64())
case *stackitem.Bool: case stackitem.Bool:
if ac.Value().(bool) { if ac.Value().(bool) {
require.Equal(t, val, int64(1)) require.Equal(t, val, int64(1))
} else { } else {
@ -208,6 +208,28 @@ func compareItems(t *testing.T, a, b stackitem.Item) {
p, ok := b.(*stackitem.Pointer) p, ok := b.(*stackitem.Pointer)
require.True(t, ok) require.True(t, ok)
require.Equal(t, si.Position(), p.Position()) // there no script in test files 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: default:
require.Equal(t, a, b) require.Equal(t, a, b)
} }

View file

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

View file

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

View file

@ -15,67 +15,67 @@ var makeStackItemTestCases = []struct {
}{ }{
{ {
input: int64(3), input: int64(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: int16(3), input: int16(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: 3, input: 3,
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: uint8(3), input: uint8(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: uint16(3), input: uint16(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: uint32(3), input: uint32(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: uint64(3), input: uint64(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: big.NewInt(3), input: big.NewInt(3),
result: &BigInteger{value: big.NewInt(3)}, result: (*BigInteger)(big.NewInt(3)),
}, },
{ {
input: []byte{1, 2, 3, 4}, input: []byte{1, 2, 3, 4},
result: &ByteArray{value: []byte{1, 2, 3, 4}}, result: NewByteArray([]byte{1, 2, 3, 4}),
}, },
{ {
input: []byte{}, input: []byte{},
result: &ByteArray{value: []byte{}}, result: NewByteArray([]byte{}),
}, },
{ {
input: "bla", input: "bla",
result: &ByteArray{value: []byte("bla")}, result: NewByteArray([]byte("bla")),
}, },
{ {
input: "", input: "",
result: &ByteArray{value: []byte{}}, result: NewByteArray([]byte{}),
}, },
{ {
input: true, input: true,
result: &Bool{value: true}, result: Bool(true),
}, },
{ {
input: false, input: false,
result: &Bool{value: false}, result: Bool(false),
}, },
{ {
input: []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{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}}}, result: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}},
}, },
{ {
input: []int{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, result: false,
}, },
{ {
item1: 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)}}), item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}),
result: false, result: false,
}, },
{ {
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}}), item1: NewArray([]Item{(*BigInteger)(big.NewInt(1))}),
item2: NewBigInteger(big.NewInt(1)), item2: NewBigInteger(big.NewInt(1)),
result: false, result: false,
}, },
{ {
item1: 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(4)}}), item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(4))}),
result: false, result: false,
}, },
}, },
@ -441,7 +441,7 @@ var marshalJSONTestCases = []struct {
result: []byte(`"010203"`), 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"]`), result: []byte(`[3,"010203"]`),
}, },
{ {
@ -459,8 +459,8 @@ func TestMarshalJSON(t *testing.T) {
switch testCase.input.(type) { switch testCase.input.(type) {
case *BigInteger: case *BigInteger:
actual, err = testCase.input.(*BigInteger).MarshalJSON() actual, err = testCase.input.(*BigInteger).MarshalJSON()
case *Bool: case Bool:
actual, err = testCase.input.(*Bool).MarshalJSON() actual, err = testCase.input.(Bool).MarshalJSON()
case *ByteArray: case *ByteArray:
actual, err = testCase.input.(*ByteArray).MarshalJSON() actual, err = testCase.input.(*ByteArray).MarshalJSON()
case *Array: case *Array:
@ -532,7 +532,9 @@ func TestDeepCopy(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
actual := DeepCopy(tc.item) actual := DeepCopy(tc.item)
require.Equal(t, tc.item, actual) require.Equal(t, tc.item, actual)
if tc.item.Type() != BooleanT {
require.False(t, actual == tc.item) 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, '}') data = append(data, '}')
seen[item] = sliceNoPointer{start, len(data)} seen[item] = sliceNoPointer{start, len(data)}
case *BigInteger: 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) return nil, fmt.Errorf("%w (MaxAllowedInteger)", ErrInvalidValue)
} }
data = append(data, it.value.String()...) data = append(data, it.Big().String()...)
case *ByteArray, *Buffer: case *ByteArray, *Buffer:
raw, err := itemToJSONString(it) raw, err := itemToJSONString(it)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data = append(data, raw...) data = append(data, raw...)
case *Bool: case Bool:
if it.value { if it {
data = append(data, "true"...) data = append(data, "true"...)
} else { } else {
data = append(data, "false"...) data = append(data, "false"...)
@ -288,12 +288,12 @@ func toJSONWithTypes(item Item, seen map[Item]bool) (interface{}, error) {
} }
value = arr value = arr
delete(seen, item) delete(seen, item)
case *Bool: case Bool:
value = it.value value = bool(it)
case *Buffer, *ByteArray: case *Buffer, *ByteArray:
value = base64.StdEncoding.EncodeToString(it.Value().([]byte)) value = base64.StdEncoding.EncodeToString(it.Value().([]byte))
case *BigInteger: case *BigInteger:
value = it.value.String() value = it.Big().String()
case *Map: case *Map:
if seen[item] { if seen[item] {
return "", ErrRecursive 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) data := t.Value().([]byte)
w.appendVarUint(uint64(len(data))) w.appendVarUint(uint64(len(data)))
w.data = append(w.data, data...) w.data = append(w.data, data...)
case *Bool: case Bool:
w.data = append(w.data, byte(BooleanT)) w.data = append(w.data, byte(BooleanT))
if t.Value().(bool) { if t {
w.data = append(w.data, 1) w.data = append(w.data, 1)
} else { } else {
w.data = append(w.data, 0) w.data = append(w.data, 0)

View file

@ -105,7 +105,6 @@ func NewWithTrigger(t trigger.Type) *VM {
Invocations: make(map[util.Uint160]int), Invocations: make(map[util.Uint160]int),
} }
vm.refs.items = make(map[stackitem.Item]int)
initStack(&vm.istack, "invocation", nil) initStack(&vm.istack, "invocation", nil)
vm.estack = newStack("evaluation", &vm.refs) vm.estack = newStack("evaluation", &vm.refs)
return vm return vm
@ -520,7 +519,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if errRecover := recover(); errRecover != nil { if errRecover := recover(); errRecover != nil {
v.state = FaultState v.state = FaultState
err = newError(ctx.ip, op, errRecover) err = newError(ctx.ip, op, errRecover)
} else if v.refs.size > MaxStackSize { } else if v.refs > MaxStackSize {
v.state = FaultState v.state = FaultState
err = newError(ctx.ip, op, "stack is too big") 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") require.NoError(t, vm.Step(), "failed to initialize static slot")
for i := range expected { for i := range expected {
require.NoError(t, vm.Step()) 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 { if result != nil {
f = func(t *testing.T, v *VM) { f = func(t *testing.T, v *VM) {
require.Equal(t, 1, v.estack.Len()) 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...) return getCustomTestFuncForVM(prog, f, args...)
@ -1761,7 +1761,7 @@ func TestPACK_UNPACK_MaxSize(t *testing.T) {
vm.estack.PushVal(len(elements)) vm.estack.PushVal(len(elements))
runVM(t, vm) runVM(t, vm)
// check reference counter = 1+1+1024 // 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, 1+1+len(elements), vm.estack.Len()) // canary + length + elements
assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64()) assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64())
for i := 0; i < len(elements); i++ { for i := 0; i < len(elements); i++ {
@ -1784,7 +1784,7 @@ func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) {
vm.estack.PushVal(len(elements)) vm.estack.PushVal(len(elements))
runVM(t, vm) runVM(t, vm)
// check reference counter = 1+1+1024 // 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()) assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array() a := vm.estack.Peek(0).Array()
assert.Equal(t, len(elements), len(a)) assert.Equal(t, len(elements), len(a))
@ -1959,7 +1959,7 @@ func testCLEARITEMS(t *testing.T, item stackitem.Item) {
v.estack.PushVal(item) v.estack.PushVal(item)
runVM(t, v) runVM(t, v)
require.Equal(t, 2, v.estack.Len()) 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()) require.EqualValues(t, 0, v.estack.Pop().BigInt().Int64())
} }