Merge pull request #2128 from nspcc-dev/vm-update-int
Some VM optimizations
This commit is contained in:
commit
5b12dd2025
13 changed files with 222 additions and 219 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
15
pkg/vm/stackitem/reference.go
Normal file
15
pkg/vm/stackitem/reference.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue