Merge pull request #2148 from nspcc-dev/microopt-vm
Another round of VM microoptimizations
This commit is contained in:
commit
296734d367
11 changed files with 133 additions and 112 deletions
|
@ -70,7 +70,7 @@ func Call(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
hasReturn := md.ReturnType != smartcontract.VoidType
|
hasReturn := md.ReturnType != smartcontract.VoidType
|
||||||
if !hasReturn {
|
if !hasReturn {
|
||||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushItem(stackitem.Null{})
|
||||||
}
|
}
|
||||||
return callInternal(ic, cs, method, fs, hasReturn, args)
|
return callInternal(ic, cs, method, fs, hasReturn, args)
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
|
||||||
ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, hasReturn, uint16(len(args)))
|
ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, hasReturn, uint16(len(args)))
|
||||||
ic.VM.Context().NEF = &cs.NEF
|
ic.VM.Context().NEF = &cs.NEF
|
||||||
for i := len(args) - 1; i >= 0; i-- {
|
for i := len(args) - 1; i >= 0; i-- {
|
||||||
ic.VM.Estack().PushVal(args[i])
|
ic.VM.Estack().PushItem(args[i])
|
||||||
}
|
}
|
||||||
// use Jump not Call here because context was loaded in LoadScript above.
|
// use Jump not Call here because context was loaded in LoadScript above.
|
||||||
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
ic.VM.Jump(ic.VM.Context(), md.Offset)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
|
// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using
|
||||||
|
@ -32,7 +33,7 @@ func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
|
||||||
return errors.New("more signatures than there are keys")
|
return errors.New("more signatures than there are keys")
|
||||||
}
|
}
|
||||||
sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hash.NetSha256(ic.Network, ic.Container).BytesBE(), pkeys, sigs)
|
sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hash.NetSha256(ic.Network, ic.Container).BytesBE(), pkeys, sigs)
|
||||||
ic.VM.Estack().PushVal(sigok)
|
ic.VM.Estack().PushItem(stackitem.Bool(sigok))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +46,6 @@ func ECDSASecp256r1CheckSig(ic *interop.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := pkey.VerifyHashable(signature, ic.Network, ic.Container)
|
res := pkey.VerifyHashable(signature, ic.Network, ic.Container)
|
||||||
ic.VM.Estack().PushVal(res)
|
ic.VM.Estack().PushItem(stackitem.Bool(res))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type iterator interface {
|
||||||
func Next(ic *interop.Context) error {
|
func Next(ic *interop.Context) error {
|
||||||
iop := ic.VM.Estack().Pop().Interop()
|
iop := ic.VM.Estack().Pop().Interop()
|
||||||
arr := iop.Value().(iterator)
|
arr := iop.Value().(iterator)
|
||||||
ic.VM.Estack().PushVal(arr.Next())
|
ic.VM.Estack().PushItem(stackitem.Bool(arr.Next()))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func Next(ic *interop.Context) error {
|
||||||
func Value(ic *interop.Context) error {
|
func Value(ic *interop.Context) error {
|
||||||
iop := ic.VM.Estack().Pop().Interop()
|
iop := ic.VM.Estack().Pop().Interop()
|
||||||
arr := iop.Value().(iterator)
|
arr := iop.Value().(iterator)
|
||||||
ic.VM.Estack().PushVal(arr.Value())
|
ic.VM.Estack().PushItem(arr.Value())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package runtime
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -28,7 +29,7 @@ func GetExecutingScriptHash(ic *interop.Context) error {
|
||||||
// one native to another, no operations are performed on invocation stack.
|
// one native to another, no operations are performed on invocation stack.
|
||||||
func GetCallingScriptHash(ic *interop.Context) error {
|
func GetCallingScriptHash(ic *interop.Context) error {
|
||||||
h := ic.VM.GetCallingScriptHash()
|
h := ic.VM.GetCallingScriptHash()
|
||||||
ic.VM.Estack().PushVal(h.BytesBE())
|
ic.VM.Estack().PushItem(stackitem.NewByteArray(h.BytesBE()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +40,13 @@ func GetEntryScriptHash(ic *interop.Context) error {
|
||||||
|
|
||||||
// Platform returns the name of the platform.
|
// Platform returns the name of the platform.
|
||||||
func Platform(ic *interop.Context) error {
|
func Platform(ic *interop.Context) error {
|
||||||
ic.VM.Estack().PushVal([]byte("NEO"))
|
ic.VM.Estack().PushItem(stackitem.NewByteArray([]byte("NEO")))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTrigger returns the script trigger.
|
// GetTrigger returns the script trigger.
|
||||||
func GetTrigger(ic *interop.Context) error {
|
func GetTrigger(ic *interop.Context) error {
|
||||||
ic.VM.Estack().PushVal(byte(ic.Trigger))
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.Trigger))))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ func Log(ic *interop.Context) error {
|
||||||
// GetTime returns timestamp of the block being verified, or the latest
|
// GetTime returns timestamp of the block being verified, or the latest
|
||||||
// one in the blockchain if no block is given to Context.
|
// one in the blockchain if no block is given to Context.
|
||||||
func GetTime(ic *interop.Context) error {
|
func GetTime(ic *interop.Context) error {
|
||||||
ic.VM.Estack().PushVal(ic.Block.Timestamp)
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(new(big.Int).SetUint64(ic.Block.Timestamp)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package runtime
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -16,9 +17,9 @@ import (
|
||||||
// GasLeft returns remaining amount of GAS.
|
// GasLeft returns remaining amount of GAS.
|
||||||
func GasLeft(ic *interop.Context) error {
|
func GasLeft(ic *interop.Context) error {
|
||||||
if ic.VM.GasLimit == -1 {
|
if ic.VM.GasLimit == -1 {
|
||||||
ic.VM.Estack().PushVal(ic.VM.GasLimit)
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit)))
|
||||||
} else {
|
} else {
|
||||||
ic.VM.Estack().PushVal(ic.VM.GasLimit - ic.VM.GasConsumed())
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit - ic.VM.GasConsumed())))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ func GetNotifications(ic *interop.Context) error {
|
||||||
})
|
})
|
||||||
arr.Append(ev)
|
arr.Append(ev)
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(arr)
|
ic.VM.Estack().PushItem(arr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,21 +68,21 @@ func GetInvocationCounter(ic *interop.Context) error {
|
||||||
count = 1
|
count = 1
|
||||||
ic.VM.Invocations[currentScriptHash] = count
|
ic.VM.Invocations[currentScriptHash] = count
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(count)
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(count))))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNetwork returns chain network number.
|
// GetNetwork returns chain network number.
|
||||||
func GetNetwork(ic *interop.Context) error {
|
func GetNetwork(ic *interop.Context) error {
|
||||||
m := ic.Chain.GetConfig().Magic
|
m := ic.Chain.GetConfig().Magic
|
||||||
ic.VM.Estack().PushVal(uint32(m))
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(m))))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRandom returns pseudo-random number which depends on block nonce and transaction hash.
|
// GetRandom returns pseudo-random number which depends on block nonce and transaction hash.
|
||||||
func GetRandom(ic *interop.Context) error {
|
func GetRandom(ic *interop.Context) error {
|
||||||
res := murmur128(ic.NonceData[:], ic.Network)
|
res := murmur128(ic.NonceData[:], ic.Network)
|
||||||
ic.VM.Estack().PushVal(bigint.FromBytesUnsigned(res))
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(bigint.FromBytesUnsigned(res)))
|
||||||
copy(ic.NonceData[:], res)
|
copy(ic.NonceData[:], res)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckHashedWitness checks given hash against current list of script hashes
|
// CheckHashedWitness checks given hash against current list of script hashes
|
||||||
|
@ -97,6 +98,6 @@ func CheckWitness(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check witness: %w", err)
|
return fmt.Errorf("failed to check witness: %w", err)
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(res)
|
ic.VM.Estack().PushItem(stackitem.Bool(res))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
@ -44,7 +45,7 @@ func engineGetScriptContainer(ic *interop.Context) error {
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown script container")
|
return errors.New("unknown script container")
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(item)
|
ic.VM.Estack().PushItem(item)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +73,9 @@ func storageGet(ic *interop.Context) error {
|
||||||
key := ic.VM.Estack().Pop().Bytes()
|
key := ic.VM.Estack().Pop().Bytes()
|
||||||
si := ic.DAO.GetStorageItem(stc.ID, key)
|
si := ic.DAO.GetStorageItem(stc.ID, key)
|
||||||
if si != nil {
|
if si != nil {
|
||||||
ic.VM.Estack().PushVal([]byte(si))
|
ic.VM.Estack().PushItem(stackitem.NewByteArray([]byte(si)))
|
||||||
} else {
|
} else {
|
||||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
ic.VM.Estack().PushItem(stackitem.Null{})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,7 @@ func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
|
||||||
ID: contract.ID,
|
ID: contract.ID,
|
||||||
ReadOnly: isReadOnly,
|
ReadOnly: isReadOnly,
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(stackitem.NewInterop(sc))
|
ic.VM.Estack().PushItem(stackitem.NewInterop(sc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +158,7 @@ func storageContextAsReadOnly(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
stc = stx
|
stc = stx
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(stackitem.NewInterop(stc))
|
ic.VM.Estack().PushItem(stackitem.NewInterop(stc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +211,7 @@ func storageFind(ic *interop.Context) error {
|
||||||
|
|
||||||
filteredMap := stackitem.NewMapWithValue(arr)
|
filteredMap := stackitem.NewMapWithValue(arr)
|
||||||
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
||||||
ic.VM.Estack().PushVal(stackitem.NewInterop(item))
|
ic.VM.Estack().PushItem(stackitem.NewInterop(item))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -236,7 +237,7 @@ func contractCreateMultisigAccount(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(hash.Hash160(script).BytesBE())
|
ic.VM.Estack().PushItem(stackitem.NewByteArray(hash.Hash160(script).BytesBE()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,12 +248,12 @@ func contractCreateStandardAccount(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ic.VM.Estack().PushVal(p.GetScriptHash().BytesBE())
|
ic.VM.Estack().PushItem(stackitem.NewByteArray(p.GetScriptHash().BytesBE()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractGetCallFlags returns current context calling flags.
|
// contractGetCallFlags returns current context calling flags.
|
||||||
func contractGetCallFlags(ic *interop.Context) error {
|
func contractGetCallFlags(ic *interop.Context) error {
|
||||||
ic.VM.Estack().PushVal(ic.VM.Context().GetCallFlags())
|
ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.VM.Context().GetCallFlags()))))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func Call(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
result := m.Func(ic, args)
|
result := m.Func(ic, args)
|
||||||
if m.MD.ReturnType != smartcontract.VoidType {
|
if m.MD.ReturnType != smartcontract.VoidType {
|
||||||
ctx.Estack().PushVal(result)
|
ctx.Estack().PushItem(result)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,11 +79,10 @@ func NewContext(b []byte) *Context {
|
||||||
// return value count and initial position in script.
|
// return value count and initial position in script.
|
||||||
func NewContextWithParams(b []byte, pcount int, rvcount int, pos int) *Context {
|
func NewContextWithParams(b []byte, pcount int, rvcount int, pos int) *Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
prog: b,
|
prog: b,
|
||||||
breakPoints: []int{},
|
ParamCount: pcount,
|
||||||
ParamCount: pcount,
|
RetCount: rvcount,
|
||||||
RetCount: rvcount,
|
nextip: pos,
|
||||||
nextip: pos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,8 +286,7 @@ func (v *VM) getContextScriptHash(n int) util.Uint160 {
|
||||||
return util.Uint160{}
|
return util.Uint160{}
|
||||||
}
|
}
|
||||||
element := istack.Peek(n)
|
element := istack.Peek(n)
|
||||||
ctxIface := element.Value()
|
ctx := element.value.(*Context)
|
||||||
ctx := ctxIface.(*Context)
|
|
||||||
return ctx.ScriptHash()
|
return ctx.ScriptHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{}) {
|
||||||
|
|
167
pkg/vm/vm.go
167
pkg/vm/vm.go
|
@ -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
|
||||||
|
@ -322,7 +322,7 @@ func (v *VM) Context() *Context {
|
||||||
if v.istack.Len() == 0 {
|
if v.istack.Len() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return v.istack.Peek(0).Value().(*Context)
|
return v.istack.Peek(0).value.(*Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopResult is used to pop the first item of the evaluation stack. This allows
|
// PopResult is used to pop the first item of the evaluation stack. This allows
|
||||||
|
@ -360,6 +360,8 @@ func (v *VM) Ready() bool {
|
||||||
|
|
||||||
// Run starts the execution of the loaded program.
|
// Run starts the execution of the loaded program.
|
||||||
func (v *VM) Run() error {
|
func (v *VM) Run() error {
|
||||||
|
var ctx *Context
|
||||||
|
|
||||||
if !v.Ready() {
|
if !v.Ready() {
|
||||||
v.state = FaultState
|
v.state = FaultState
|
||||||
return errors.New("no program loaded")
|
return errors.New("no program loaded")
|
||||||
|
@ -372,6 +374,7 @@ func (v *VM) Run() error {
|
||||||
}
|
}
|
||||||
// HaltState (the default) or BreakState are safe to continue.
|
// HaltState (the default) or BreakState are safe to continue.
|
||||||
v.state = NoneState
|
v.state = NoneState
|
||||||
|
ctx = v.Context()
|
||||||
for {
|
for {
|
||||||
switch {
|
switch {
|
||||||
case v.state.HasFlag(FaultState):
|
case v.state.HasFlag(FaultState):
|
||||||
|
@ -382,7 +385,7 @@ func (v *VM) Run() error {
|
||||||
// Normal exit from this loop.
|
// Normal exit from this loop.
|
||||||
return nil
|
return nil
|
||||||
case v.state == NoneState:
|
case v.state == NoneState:
|
||||||
if err := v.Step(); err != nil {
|
if err := v.step(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -390,7 +393,7 @@ func (v *VM) Run() error {
|
||||||
return errors.New("unknown state")
|
return errors.New("unknown state")
|
||||||
}
|
}
|
||||||
// check for breakpoint before executing the next instruction
|
// check for breakpoint before executing the next instruction
|
||||||
ctx := v.Context()
|
ctx = v.Context()
|
||||||
if ctx != nil && ctx.atBreakPoint() {
|
if ctx != nil && ctx.atBreakPoint() {
|
||||||
v.state = BreakState
|
v.state = BreakState
|
||||||
}
|
}
|
||||||
|
@ -400,6 +403,11 @@ func (v *VM) Run() error {
|
||||||
// Step 1 instruction in the program.
|
// Step 1 instruction in the program.
|
||||||
func (v *VM) Step() error {
|
func (v *VM) Step() error {
|
||||||
ctx := v.Context()
|
ctx := v.Context()
|
||||||
|
return v.step(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// step executes one instruction in given context.
|
||||||
|
func (v *VM) step(ctx *Context) error {
|
||||||
op, param, err := ctx.Next()
|
op, param, err := ctx.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.state = FaultState
|
v.state = FaultState
|
||||||
|
@ -532,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,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])
|
||||||
|
@ -571,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 {
|
||||||
|
@ -599,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()
|
||||||
|
@ -615,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()
|
||||||
|
@ -631,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()
|
||||||
|
@ -650,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())
|
||||||
|
@ -685,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())
|
||||||
|
@ -703,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())
|
||||||
|
@ -716,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())
|
||||||
|
@ -726,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 {
|
||||||
|
@ -818,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 {
|
||||||
|
@ -841,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()
|
||||||
|
@ -905,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()
|
||||||
|
@ -913,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()
|
||||||
|
@ -931,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()
|
||||||
|
@ -981,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()
|
||||||
|
@ -990,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()
|
||||||
|
@ -999,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())
|
||||||
|
@ -1027,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()
|
||||||
|
@ -1060,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()
|
||||||
|
@ -1085,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:
|
||||||
|
@ -1223,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,
|
||||||
|
@ -1285,7 +1296,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcode.RET:
|
case opcode.RET:
|
||||||
oldCtx := v.istack.Pop().Value().(*Context)
|
oldCtx := v.istack.Pop().value.(*Context)
|
||||||
oldEstack := v.estack
|
oldEstack := v.estack
|
||||||
|
|
||||||
v.unloadContext(oldCtx)
|
v.unloadContext(oldCtx)
|
||||||
|
@ -1309,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 {
|
||||||
|
@ -1326,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 {
|
||||||
|
@ -1351,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 {
|
||||||
|
@ -1361,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 ^^
|
||||||
|
@ -1409,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)
|
||||||
|
@ -1512,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1552,7 +1565,7 @@ func calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
|
||||||
func (v *VM) handleException() {
|
func (v *VM) handleException() {
|
||||||
for pop := 0; pop < v.istack.Len(); pop++ {
|
for pop := 0; pop < v.istack.Len(); pop++ {
|
||||||
ictxv := v.istack.Peek(pop)
|
ictxv := v.istack.Peek(pop)
|
||||||
ictx := ictxv.Value().(*Context)
|
ictx := ictxv.value.(*Context)
|
||||||
for j := 0; j < ictx.tryStack.Len(); j++ {
|
for j := 0; j < ictx.tryStack.Len(); j++ {
|
||||||
e := ictx.tryStack.Peek(j)
|
e := ictx.tryStack.Peek(j)
|
||||||
ectx := e.Value().(*exceptionHandlingContext)
|
ectx := e.Value().(*exceptionHandlingContext)
|
||||||
|
@ -1562,12 +1575,12 @@ func (v *VM) handleException() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for i := 0; i < pop; i++ {
|
for i := 0; i < pop; i++ {
|
||||||
ctx := v.istack.Pop().Value().(*Context)
|
ctx := v.istack.Pop().value.(*Context)
|
||||||
v.unloadContext(ctx)
|
v.unloadContext(ctx)
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
|
Loading…
Reference in a new issue