vm: don't use PushVal when item type is known

PushVal is very convenient, but type switch is somewhat expensive.

name                    old time/op    new time/op    delta
ScriptFibonacci-8          736µs ± 1%     602µs ± 1%  -18.21%  (p=0.000 n=9+10)
ScriptNestedRefCount-8    1.08ms ± 2%    0.96ms ± 1%  -11.13%  (p=0.000 n=10+10)
ScriptPushPop/4-8         1.48µs ± 3%    1.35µs ± 2%   -9.14%  (p=0.000 n=10+9)
ScriptPushPop/16-8        3.59µs ± 1%    3.38µs ± 1%   -6.01%  (p=0.000 n=10+10)
ScriptPushPop/128-8       23.7µs ± 1%    22.6µs ± 1%   -4.39%  (p=0.000 n=10+8)
ScriptPushPop/1024-8       176µs ± 2%     167µs ± 3%   -5.24%  (p=0.000 n=9+10)

name                    old alloc/op   new alloc/op   delta
ScriptFibonacci-8          123kB ± 0%     114kB ± 0%   -6.88%  (p=0.000 n=10+9)
ScriptNestedRefCount-8     266kB ± 0%     241kB ± 0%   -9.23%  (p=0.000 n=8+10)
ScriptPushPop/4-8           160B ± 0%      160B ± 0%     ~     (all equal)
ScriptPushPop/16-8          640B ± 0%      640B ± 0%     ~     (all equal)
ScriptPushPop/128-8       8.70kB ± 0%    8.70kB ± 0%     ~     (all equal)
ScriptPushPop/1024-8      73.2kB ± 0%    73.2kB ± 0%     ~     (all equal)

name                    old allocs/op  new allocs/op  delta
ScriptFibonacci-8          3.53k ± 0%     3.17k ± 0%   -9.98%  (p=0.000 n=10+10)
ScriptNestedRefCount-8     11.8k ± 0%     10.7k ± 0%   -8.70%  (p=0.000 n=10+10)
ScriptPushPop/4-8           8.00 ± 0%      8.00 ± 0%     ~     (all equal)
ScriptPushPop/16-8          32.0 ± 0%      32.0 ± 0%     ~     (all equal)
ScriptPushPop/128-8          259 ± 0%       259 ± 0%     ~     (all equal)
ScriptPushPop/1024-8       2.05k ± 0%     2.05k ± 0%     ~     (all equal)
This commit is contained in:
Roman Khimov 2021-08-28 22:35:43 +03:00
parent bc31c97c32
commit a3892aa662
3 changed files with 82 additions and 72 deletions

View file

@ -294,6 +294,6 @@ func (v *VM) getContextScriptHash(n int) util.Uint160 {
// invocation stack element number n. // invocation stack element number n.
func (v *VM) PushContextScriptHash(n int) error { func (v *VM) PushContextScriptHash(n int) error {
h := v.getContextScriptHash(n) h := v.getContextScriptHash(n)
v.Estack().PushVal(h.BytesBE()) v.Estack().PushItem(stackitem.NewByteArray(h.BytesBE()))
return nil return nil
} }

View file

@ -170,6 +170,11 @@ func (s *Stack) Push(e Element) {
s.refs.Add(e.value) s.refs.Add(e.value)
} }
// PushItem pushed an Item to the stack.
func (s *Stack) PushItem(i stackitem.Item) {
s.Push(Element{i})
}
// PushVal pushes the given value on the stack. It will infer the // PushVal pushes the given value on the stack. It will infer the
// underlying Item to its corresponding type. // underlying Item to its corresponding type.
func (s *Stack) PushVal(v interface{}) { func (s *Stack) PushVal(v interface{}) {

View file

@ -286,7 +286,7 @@ func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
ctx.callFlag = f ctx.callFlag = f
ctx.static = newSlot(&v.refs) ctx.static = newSlot(&v.refs)
ctx.callingScriptHash = v.GetCurrentScriptHash() ctx.callingScriptHash = v.GetCurrentScriptHash()
v.istack.PushVal(ctx) v.istack.PushItem(ctx)
} }
// LoadScriptWithHash if similar to the LoadScriptWithFlags method, but it also loads // LoadScriptWithHash if similar to the LoadScriptWithFlags method, but it also loads
@ -540,7 +540,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
if op <= opcode.PUSHINT256 { if op <= opcode.PUSHINT256 {
v.estack.PushVal(bigint.FromBytes(parameter)) v.estack.PushItem(stackitem.NewBigInteger(bigint.FromBytes(parameter)))
return return
} }
@ -551,26 +551,26 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15,
opcode.PUSH16: opcode.PUSH16:
val := int(op) - int(opcode.PUSH0) val := int(op) - int(opcode.PUSH0)
v.estack.PushVal(val) v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(val))))
case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4: case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4:
v.estack.PushVal(parameter) v.estack.PushItem(stackitem.NewByteArray(parameter))
case opcode.PUSHA: case opcode.PUSHA:
n := getJumpOffset(ctx, parameter) n := getJumpOffset(ctx, parameter)
ptr := stackitem.NewPointerWithHash(n, ctx.prog, ctx.ScriptHash()) ptr := stackitem.NewPointerWithHash(n, ctx.prog, ctx.ScriptHash())
v.estack.PushVal(ptr) v.estack.PushItem(ptr)
case opcode.PUSHNULL: case opcode.PUSHNULL:
v.estack.PushVal(stackitem.Null{}) v.estack.PushItem(stackitem.Null{})
case opcode.ISNULL: case opcode.ISNULL:
_, ok := v.estack.Pop().value.(stackitem.Null) _, ok := v.estack.Pop().value.(stackitem.Null)
v.estack.PushVal(ok) v.estack.PushItem(stackitem.Bool(ok))
case opcode.ISTYPE: case opcode.ISTYPE:
res := v.estack.Pop().Item() res := v.estack.Pop().Item()
v.estack.PushVal(res.Type() == stackitem.Type(parameter[0])) v.estack.PushItem(stackitem.Bool(res.Type() == stackitem.Type(parameter[0])))
case opcode.CONVERT: case opcode.CONVERT:
typ := stackitem.Type(parameter[0]) typ := stackitem.Type(parameter[0])
@ -579,7 +579,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if err != nil { if err != nil {
panic(err) panic(err)
} }
v.estack.PushVal(result) v.estack.PushItem(result)
case opcode.INITSSLOT: case opcode.INITSSLOT:
if parameter[0] == 0 { if parameter[0] == 0 {
@ -607,11 +607,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6: case opcode.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6:
item := ctx.static.Get(int(op - opcode.LDSFLD0)) item := ctx.static.Get(int(op - opcode.LDSFLD0))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.LDSFLD: case opcode.LDSFLD:
item := ctx.static.Get(int(parameter[0])) item := ctx.static.Get(int(parameter[0]))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode.STSFLD5, opcode.STSFLD6: case opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode.STSFLD5, opcode.STSFLD6:
item := v.estack.Pop().Item() item := v.estack.Pop().Item()
@ -623,11 +623,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.LDLOC0, opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode.LDLOC6: case opcode.LDLOC0, opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode.LDLOC6:
item := ctx.local.Get(int(op - opcode.LDLOC0)) item := ctx.local.Get(int(op - opcode.LDLOC0))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.LDLOC: case opcode.LDLOC:
item := ctx.local.Get(int(parameter[0])) item := ctx.local.Get(int(parameter[0]))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.STLOC0, opcode.STLOC1, opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6: case opcode.STLOC0, opcode.STLOC1, opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6:
item := v.estack.Pop().Item() item := v.estack.Pop().Item()
@ -639,11 +639,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6: case opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6:
item := ctx.arguments.Get(int(op - opcode.LDARG0)) item := ctx.arguments.Get(int(op - opcode.LDARG0))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.LDARG: case opcode.LDARG:
item := ctx.arguments.Get(int(parameter[0])) item := ctx.arguments.Get(int(parameter[0]))
v.estack.PushVal(item) v.estack.PushItem(item)
case opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4, opcode.STARG5, opcode.STARG6: case opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4, opcode.STARG5, opcode.STARG6:
item := v.estack.Pop().Item() item := v.estack.Pop().Item()
@ -658,7 +658,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n < 0 || n > stackitem.MaxSize { if n < 0 || n > stackitem.MaxSize {
panic("invalid size") panic("invalid size")
} }
v.estack.PushVal(stackitem.NewBuffer(make([]byte, n))) v.estack.PushItem(stackitem.NewBuffer(make([]byte, n)))
case opcode.MEMCPY: case opcode.MEMCPY:
n := toInt(v.estack.Pop().BigInt()) n := toInt(v.estack.Pop().BigInt())
@ -693,7 +693,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
ab := make([]byte, l) ab := make([]byte, l)
copy(ab, a) copy(ab, a)
copy(ab[len(a):], b) copy(ab[len(a):], b)
v.estack.PushVal(stackitem.NewBuffer(ab)) v.estack.PushItem(stackitem.NewBuffer(ab))
case opcode.SUBSTR: case opcode.SUBSTR:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -711,7 +711,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
res := make([]byte, l) res := make([]byte, l)
copy(res, s[o:last]) copy(res, s[o:last])
v.estack.PushVal(stackitem.NewBuffer(res)) v.estack.PushItem(stackitem.NewBuffer(res))
case opcode.LEFT: case opcode.LEFT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -724,7 +724,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
res := make([]byte, l) res := make([]byte, l)
copy(res, s[:l]) copy(res, s[:l])
v.estack.PushVal(stackitem.NewBuffer(res)) v.estack.PushItem(stackitem.NewBuffer(res))
case opcode.RIGHT: case opcode.RIGHT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -734,10 +734,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
s := v.estack.Pop().Bytes() s := v.estack.Pop().Bytes()
res := make([]byte, l) res := make([]byte, l)
copy(res, s[len(s)-l:]) copy(res, s[len(s)-l:])
v.estack.PushVal(stackitem.NewBuffer(res)) v.estack.PushItem(stackitem.NewBuffer(res))
case opcode.DEPTH: case opcode.DEPTH:
v.estack.PushVal(v.estack.Len()) v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(v.estack.Len()))))
case opcode.DROP: case opcode.DROP:
if v.estack.Len() < 1 { if v.estack.Len() < 1 {
@ -826,22 +826,22 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// Bit operations. // Bit operations.
case opcode.INVERT: case opcode.INVERT:
i := v.estack.Pop().BigInt() i := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Not(i)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Not(i)))
case opcode.AND: case opcode.AND:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).And(b, a)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).And(b, a)))
case opcode.OR: case opcode.OR:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Or(b, a)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Or(b, a)))
case opcode.XOR: case opcode.XOR:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Xor(b, a)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Xor(b, a)))
case opcode.EQUAL, opcode.NOTEQUAL: case opcode.EQUAL, opcode.NOTEQUAL:
if v.estack.Len() < 2 { if v.estack.Len() < 2 {
@ -849,63 +849,64 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
b := v.estack.Pop() b := v.estack.Pop()
a := v.estack.Pop() a := v.estack.Pop()
v.estack.PushVal(a.value.Equals(b.value) == (op == opcode.EQUAL)) res := stackitem.Bool(a.value.Equals(b.value) == (op == opcode.EQUAL))
v.estack.PushItem(res)
// Numeric operations. // Numeric operations.
case opcode.SIGN: case opcode.SIGN:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Sign()) v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(x.Sign()))))
case opcode.ABS: case opcode.ABS:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Abs(x)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Abs(x)))
case opcode.NEGATE: case opcode.NEGATE:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Neg(x)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Neg(x)))
case opcode.INC: case opcode.INC:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
a := new(big.Int).Add(x, big.NewInt(1)) a := new(big.Int).Add(x, big.NewInt(1))
v.estack.PushVal(a) v.estack.PushItem(stackitem.NewBigInteger(a))
case opcode.DEC: case opcode.DEC:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
a := new(big.Int).Sub(x, big.NewInt(1)) a := new(big.Int).Sub(x, big.NewInt(1))
v.estack.PushVal(a) v.estack.PushItem(stackitem.NewBigInteger(a))
case opcode.ADD: case opcode.ADD:
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
c := new(big.Int).Add(a, b) c := new(big.Int).Add(a, b)
v.estack.PushVal(c) v.estack.PushItem(stackitem.NewBigInteger(c))
case opcode.SUB: case opcode.SUB:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
c := new(big.Int).Sub(a, b) c := new(big.Int).Sub(a, b)
v.estack.PushVal(c) v.estack.PushItem(stackitem.NewBigInteger(c))
case opcode.MUL: case opcode.MUL:
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
c := new(big.Int).Mul(a, b) c := new(big.Int).Mul(a, b)
v.estack.PushVal(c) v.estack.PushItem(stackitem.NewBigInteger(c))
case opcode.DIV: case opcode.DIV:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Quo(a, b)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Quo(a, b)))
case opcode.MOD: case opcode.MOD:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Rem(a, b)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Rem(a, b)))
case opcode.POW: case opcode.POW:
exp := v.estack.Pop().BigInt() exp := v.estack.Pop().BigInt()
@ -913,7 +914,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if ei := exp.Uint64(); !exp.IsUint64() || ei > maxSHLArg { if ei := exp.Uint64(); !exp.IsUint64() || ei > maxSHLArg {
panic("invalid exponent") panic("invalid exponent")
} }
v.estack.PushVal(new(big.Int).Exp(a, exp, nil)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Exp(a, exp, nil)))
case opcode.SQRT: case opcode.SQRT:
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
@ -921,7 +922,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("negative value") panic("negative value")
} }
v.estack.PushVal(new(big.Int).Sqrt(a)) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a)))
case opcode.SHL, opcode.SHR: case opcode.SHL, opcode.SHR:
b := v.estack.Pop().BigInt().Int64() b := v.estack.Pop().BigInt().Int64()
@ -939,35 +940,35 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
item.Rsh(a, uint(b)) item.Rsh(a, uint(b))
} }
v.estack.PushVal(&item) v.estack.PushItem(stackitem.NewBigInteger(&item))
case opcode.NOT: case opcode.NOT:
x := v.estack.Pop().Bool() x := v.estack.Pop().Bool()
v.estack.PushVal(!x) v.estack.PushItem(stackitem.Bool(!x))
case opcode.BOOLAND: case opcode.BOOLAND:
b := v.estack.Pop().Bool() b := v.estack.Pop().Bool()
a := v.estack.Pop().Bool() a := v.estack.Pop().Bool()
v.estack.PushVal(a && b) v.estack.PushItem(stackitem.Bool(a && b))
case opcode.BOOLOR: case opcode.BOOLOR:
b := v.estack.Pop().Bool() b := v.estack.Pop().Bool()
a := v.estack.Pop().Bool() a := v.estack.Pop().Bool()
v.estack.PushVal(a || b) v.estack.PushItem(stackitem.Bool(a || b))
case opcode.NZ: case opcode.NZ:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Sign() != 0) v.estack.PushItem(stackitem.Bool(x.Sign() != 0))
case opcode.NUMEQUAL: case opcode.NUMEQUAL:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == 0) v.estack.PushItem(stackitem.Bool(a.Cmp(b) == 0))
case opcode.NUMNOTEQUAL: case opcode.NUMNOTEQUAL:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) != 0) v.estack.PushItem(stackitem.Bool(a.Cmp(b) != 0))
case opcode.LT, opcode.LE, opcode.GT, opcode.GE: case opcode.LT, opcode.LE, opcode.GT, opcode.GE:
eb := v.estack.Pop() eb := v.estack.Pop()
@ -989,7 +990,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
res = cmp >= 0 res = cmp >= 0
} }
} }
v.estack.PushVal(res) v.estack.PushItem(stackitem.Bool(res))
case opcode.MIN: case opcode.MIN:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
@ -998,7 +999,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if a.Cmp(b) == 1 { if a.Cmp(b) == 1 {
val = b val = b
} }
v.estack.PushVal(val) v.estack.PushItem(stackitem.NewBigInteger(val))
case opcode.MAX: case opcode.MAX:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
@ -1007,17 +1008,17 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if a.Cmp(b) == -1 { if a.Cmp(b) == -1 {
val = b val = b
} }
v.estack.PushVal(val) v.estack.PushItem(stackitem.NewBigInteger(val))
case opcode.WITHIN: case opcode.WITHIN:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1) v.estack.PushItem(stackitem.Bool(a.Cmp(x) <= 0 && x.Cmp(b) == -1))
// Object operations // Object operations
case opcode.NEWARRAY0: case opcode.NEWARRAY0:
v.estack.PushVal(stackitem.NewArray([]stackitem.Item{})) v.estack.PushItem(stackitem.NewArray([]stackitem.Item{}))
case opcode.NEWARRAY, opcode.NEWARRAYT, opcode.NEWSTRUCT: case opcode.NEWARRAY, opcode.NEWARRAYT, opcode.NEWSTRUCT:
n := toInt(v.estack.Pop().BigInt()) n := toInt(v.estack.Pop().BigInt())
@ -1035,10 +1036,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} else { } else {
res = stackitem.NewArray(items) res = stackitem.NewArray(items)
} }
v.estack.PushVal(res) v.estack.PushItem(res)
case opcode.NEWSTRUCT0: case opcode.NEWSTRUCT0:
v.estack.PushVal(stackitem.NewStruct([]stackitem.Item{})) v.estack.PushItem(stackitem.NewStruct([]stackitem.Item{}))
case opcode.APPEND: case opcode.APPEND:
itemElem := v.estack.Pop() itemElem := v.estack.Pop()
@ -1068,15 +1069,15 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
items[i] = v.estack.Pop().value items[i] = v.estack.Pop().value
} }
v.estack.PushVal(items) v.estack.PushItem(stackitem.NewArray(items))
case opcode.UNPACK: case opcode.UNPACK:
a := v.estack.Pop().Array() a := v.estack.Pop().Array()
l := len(a) l := len(a)
for i := l - 1; i >= 0; i-- { for i := l - 1; i >= 0; i-- {
v.estack.PushVal(a[i]) v.estack.PushItem(a[i])
} }
v.estack.PushVal(l) v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(l))))
case opcode.PICKITEM: case opcode.PICKITEM:
key := v.estack.Pop() key := v.estack.Pop()
@ -1093,20 +1094,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("PICKITEM: invalid index") panic("PICKITEM: invalid index")
} }
item := arr[index].Dup() item := arr[index].Dup()
v.estack.PushVal(item) v.estack.PushItem(item)
case *stackitem.Map: case *stackitem.Map:
index := t.Index(key.Item()) index := t.Index(key.Item())
if index < 0 { if index < 0 {
panic("invalid key") panic("invalid key")
} }
v.estack.Push(Element{value: t.Value().([]stackitem.MapElement)[index].Value.Dup()}) v.estack.PushItem(t.Value().([]stackitem.MapElement)[index].Value.Dup())
default: default:
arr := obj.Bytes() arr := obj.Bytes()
if index < 0 || index >= len(arr) { if index < 0 || index >= len(arr) {
panic("PICKITEM: invalid index") panic("PICKITEM: invalid index")
} }
item := arr[index] item := arr[index]
v.estack.PushVal(int(item)) v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(item))))
} }
case opcode.SETITEM: case opcode.SETITEM:
@ -1231,20 +1232,22 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
item.Remove(index) item.Remove(index)
} }
v.refs.Remove(elem) v.refs.Remove(elem)
v.estack.PushVal(elem) v.estack.PushItem(elem)
case opcode.SIZE: case opcode.SIZE:
elem := v.estack.Pop() elem := v.estack.Pop()
var res int
// Cause there is no native (byte) item type here, hence we need to check // Cause there is no native (byte) item type here, hence we need to check
// the type of the item for array size operations. // the type of the item for array size operations.
switch t := elem.Value().(type) { switch t := elem.Value().(type) {
case []stackitem.Item: case []stackitem.Item:
v.estack.PushVal(len(t)) res = len(t)
case []stackitem.MapElement: case []stackitem.MapElement:
v.estack.PushVal(len(t)) res = len(t)
default: default:
v.estack.PushVal(len(elem.Bytes())) res = len(elem.Bytes())
} }
v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(res))))
case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL, case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL,
opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
@ -1317,7 +1320,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
case opcode.NEWMAP: case opcode.NEWMAP:
v.estack.Push(Element{value: stackitem.NewMap()}) v.estack.PushItem(stackitem.NewMap())
case opcode.KEYS: case opcode.KEYS:
if v.estack.Len() == 0 { if v.estack.Len() == 0 {
@ -1334,7 +1337,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
for k := range m.Value().([]stackitem.MapElement) { for k := range m.Value().([]stackitem.MapElement) {
arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup()) arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup())
} }
v.estack.PushVal(arr) v.estack.PushItem(stackitem.NewArray(arr))
case opcode.VALUES: case opcode.VALUES:
if v.estack.Len() == 0 { if v.estack.Len() == 0 {
@ -1359,7 +1362,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("not a Map, Array or Struct") panic("not a Map, Array or Struct")
} }
v.estack.PushVal(arr) v.estack.PushItem(stackitem.NewArray(arr))
case opcode.HASKEY: case opcode.HASKEY:
if v.estack.Len() < 2 { if v.estack.Len() < 2 {
@ -1369,24 +1372,26 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
validateMapKey(key) validateMapKey(key)
c := v.estack.Pop() c := v.estack.Pop()
var res bool
switch t := c.value.(type) { switch t := c.value.(type) {
case *stackitem.Array, *stackitem.Struct: case *stackitem.Array, *stackitem.Struct:
index := key.BigInt().Int64() index := key.BigInt().Int64()
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
v.estack.PushVal(index < int64(len(c.Array()))) res = index < int64(len(c.Array()))
case *stackitem.Map: case *stackitem.Map:
v.estack.PushVal(t.Has(key.Item())) res = t.Has(key.Item())
case *stackitem.Buffer: case *stackitem.Buffer:
index := key.BigInt().Int64() index := key.BigInt().Int64()
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
v.estack.PushVal(index < int64(t.Len())) res = index < int64(t.Len())
default: default:
panic("wrong collection type") panic("wrong collection type")
} }
v.estack.PushItem(stackitem.Bool(res))
case opcode.NOP: case opcode.NOP:
// unlucky ^^ // unlucky ^^
@ -1417,7 +1422,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
fOffset = -1 fOffset = -1
} }
eCtx := newExceptionHandlingContext(cOffset, fOffset) eCtx := newExceptionHandlingContext(cOffset, fOffset)
ctx.tryStack.PushVal(eCtx) ctx.tryStack.PushItem(eCtx)
case opcode.ENDTRY, opcode.ENDTRYL: case opcode.ENDTRY, opcode.ENDTRYL:
eCtx := ctx.tryStack.Peek(0).Value().(*exceptionHandlingContext) eCtx := ctx.tryStack.Peek(0).Value().(*exceptionHandlingContext)
@ -1520,7 +1525,7 @@ func (v *VM) call(ctx *Context, offset int) {
newCtx.arguments = nil newCtx.arguments = nil
initStack(&newCtx.tryStack, "exception", nil) initStack(&newCtx.tryStack, "exception", nil)
newCtx.NEF = ctx.NEF newCtx.NEF = ctx.NEF
v.istack.PushVal(newCtx) v.istack.PushItem(newCtx)
v.Jump(newCtx, offset) v.Jump(newCtx, offset)
} }
@ -1575,7 +1580,7 @@ func (v *VM) handleException() {
} }
if ectx.State == eTry && ectx.HasCatch() { if ectx.State == eTry && ectx.HasCatch() {
ectx.State = eCatch ectx.State = eCatch
v.estack.PushVal(v.uncaughtException) v.estack.PushItem(v.uncaughtException)
v.uncaughtException = nil v.uncaughtException = nil
v.Jump(ictx, ectx.CatchOffset) v.Jump(ictx, ectx.CatchOffset)
} else { } else {