mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-25 03:56:34 +00:00
Merge pull request #2365 from nspcc-dev/vm/replace-cli
vm CLI: replace ishell with urfave/cli
This commit is contained in:
commit
9224f57323
6 changed files with 436 additions and 305 deletions
|
@ -3,7 +3,7 @@ package vm
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/abiosoft/readline"
|
"github.com/chzyer/readline"
|
||||||
vmcli "github.com/nspcc-dev/neo-go/pkg/vm/cli"
|
vmcli "github.com/nspcc-dev/neo-go/pkg/vm/cli"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -21,9 +21,6 @@ func NewCommands() []cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func startVMPrompt(ctx *cli.Context) error {
|
func startVMPrompt(ctx *cli.Context) error {
|
||||||
p := vmcli.NewWithConfig(true, os.Exit, &readline.Config{
|
p := vmcli.NewWithConfig(true, os.Exit, &readline.Config{})
|
||||||
Stdout: ctx.App.Writer,
|
|
||||||
Stderr: ctx.App.ErrWriter,
|
|
||||||
})
|
|
||||||
return p.Run()
|
return p.Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/abiosoft/readline"
|
"github.com/chzyer/readline"
|
||||||
"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/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -1,13 +1,13 @@
|
||||||
module github.com/nspcc-dev/neo-go
|
module github.com/nspcc-dev/neo-go
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/abiosoft/ishell/v2 v2.0.2
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db
|
|
||||||
github.com/btcsuite/btcd v0.22.0-beta
|
github.com/btcsuite/btcd v0.22.0-beta
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/holiman/uint256 v1.2.0
|
github.com/holiman/uint256 v1.2.0
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/mr-tron/base58 v1.2.0
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac
|
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -6,11 +6,8 @@ github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1
|
||||||
github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I=
|
github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I=
|
||||||
github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA=
|
github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA=
|
||||||
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
|
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
|
||||||
github.com/abiosoft/ishell/v2 v2.0.2 h1:5qVfGiQISaYM8TkbBl7RFO6MddABoXpATrsFbVI+SNo=
|
|
||||||
github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
|
github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
||||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
@ -47,6 +44,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
@ -70,9 +68,7 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
|
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
||||||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
||||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||||
|
@ -142,6 +138,8 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
@ -155,12 +153,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,9 +16,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/abiosoft/ishell/v2"
|
"github.com/chzyer/readline"
|
||||||
"github.com/abiosoft/readline"
|
"github.com/kballard/go-shellquote"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"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/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
|
@ -27,119 +29,136 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"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"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
vmKey = "vm"
|
vmKey = "vm"
|
||||||
manifestKey = "manifest"
|
manifestKey = "manifest"
|
||||||
|
exitFuncKey = "exitFunc"
|
||||||
|
readlineInstanceKey = "readlineKey"
|
||||||
|
printLogoKey = "printLogoKey"
|
||||||
boolType = "bool"
|
boolType = "bool"
|
||||||
boolFalse = "false"
|
boolFalse = "false"
|
||||||
boolTrue = "true"
|
boolTrue = "true"
|
||||||
intType = "int"
|
intType = "int"
|
||||||
stringType = "string"
|
stringType = "string"
|
||||||
exitFunc = "exitFunc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands = []*ishell.Cmd{
|
var commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "exit",
|
Name: "exit",
|
||||||
Help: "Exit the VM prompt",
|
Usage: "Exit the VM prompt",
|
||||||
LongHelp: "Exit the VM prompt",
|
Description: "Exit the VM prompt",
|
||||||
Func: handleExit,
|
Action: handleExit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ip",
|
Name: "ip",
|
||||||
Help: "Show current instruction",
|
Usage: "Show current instruction",
|
||||||
LongHelp: "Show current instruction",
|
Description: "Show current instruction",
|
||||||
Func: handleIP,
|
Action: handleIP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "break",
|
Name: "break",
|
||||||
Help: "Place a breakpoint",
|
Usage: "Place a breakpoint",
|
||||||
LongHelp: `Usage: break <ip>
|
UsageText: `break <ip>`,
|
||||||
|
Description: `break <ip>
|
||||||
<ip> is mandatory parameter, example:
|
<ip> is mandatory parameter, example:
|
||||||
> break 12`,
|
> break 12`,
|
||||||
Func: handleBreak,
|
Action: handleBreak,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "estack",
|
Name: "estack",
|
||||||
Help: "Show evaluation stack contents",
|
Usage: "Show evaluation stack contents",
|
||||||
LongHelp: "Show evaluation stack contents",
|
Description: "Show evaluation stack contents",
|
||||||
Func: handleXStack,
|
Action: handleXStack,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "istack",
|
Name: "istack",
|
||||||
Help: "Show invocation stack contents",
|
Usage: "Show invocation stack contents",
|
||||||
LongHelp: "Show invocation stack contents",
|
Description: "Show invocation stack contents",
|
||||||
Func: handleXStack,
|
Action: handleXStack,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "sslot",
|
Name: "sslot",
|
||||||
Help: "Show static slot contents",
|
Usage: "Show static slot contents",
|
||||||
LongHelp: "Show static slot contents",
|
Description: "Show static slot contents",
|
||||||
Func: handleSlots,
|
Action: handleSlots,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "lslot",
|
Name: "lslot",
|
||||||
Help: "Show local slot contents",
|
Usage: "Show local slot contents",
|
||||||
LongHelp: "Show local slot contents",
|
Description: "Show local slot contents",
|
||||||
Func: handleSlots,
|
Action: handleSlots,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "aslot",
|
Name: "aslot",
|
||||||
Help: "Show arguments slot contents",
|
Usage: "Show arguments slot contents",
|
||||||
LongHelp: "Show arguments slot contents",
|
Description: "Show arguments slot contents",
|
||||||
Func: handleSlots,
|
Action: handleSlots,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "loadnef",
|
Name: "loadnef",
|
||||||
Help: "Load a NEF-consistent script into the VM",
|
Usage: "Load a NEF-consistent script into the VM",
|
||||||
LongHelp: `Usage: loadnef <file> <manifest>
|
UsageText: `loadnef <file> <manifest>`,
|
||||||
|
Description: `loadnef <file> <manifest>
|
||||||
both parameters are mandatory, example:
|
both parameters are mandatory, example:
|
||||||
> loadnef /path/to/script.nef /path/to/manifest.json`,
|
> loadnef /path/to/script.nef /path/to/manifest.json`,
|
||||||
Func: handleLoadNEF,
|
Action: handleLoadNEF,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "loadbase64",
|
Name: "loadbase64",
|
||||||
Help: "Load a base64-encoded script string into the VM",
|
Usage: "Load a base64-encoded script string into the VM",
|
||||||
LongHelp: `Usage: loadbase64 <string>
|
UsageText: `loadbase64 <string>`,
|
||||||
|
Description: `loadbase64 <string>
|
||||||
|
|
||||||
<string> is mandatory parameter, example:
|
<string> is mandatory parameter, example:
|
||||||
> loadbase64 AwAQpdToAAAADBQV9ehtQR1OrVZVhtHtoUHRfoE+agwUzmFvf3Rhfg/EuAVYOvJgKiON9j8TwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4`,
|
> loadbase64 AwAQpdToAAAADBQV9ehtQR1OrVZVhtHtoUHRfoE+agwUzmFvf3Rhfg/EuAVYOvJgKiON9j8TwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4`,
|
||||||
Func: handleLoadBase64,
|
Action: handleLoadBase64,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "loadhex",
|
Name: "loadhex",
|
||||||
Help: "Load a hex-encoded script string into the VM",
|
Usage: "Load a hex-encoded script string into the VM",
|
||||||
LongHelp: `Usage: loadhex <string>
|
UsageText: `loadhex <string>`,
|
||||||
|
Description: `loadhex <string>
|
||||||
|
|
||||||
<string> is mandatory parameter, example:
|
<string> is mandatory parameter, example:
|
||||||
> loadhex 0c0c48656c6c6f20776f726c6421`,
|
> loadhex 0c0c48656c6c6f20776f726c6421`,
|
||||||
Func: handleLoadHex,
|
Action: handleLoadHex,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "loadgo",
|
Name: "loadgo",
|
||||||
Help: "Compile and load a Go file with the manifest into the VM",
|
Usage: "Compile and load a Go file with the manifest into the VM",
|
||||||
LongHelp: `Usage: loadgo <file>
|
UsageText: `loadgo <file>`,
|
||||||
|
Description: `loadgo <file>
|
||||||
|
|
||||||
<file> is mandatory parameter, example:
|
<file> is mandatory parameter, example:
|
||||||
> loadgo /path/to/file.go`,
|
> loadgo /path/to/file.go`,
|
||||||
Func: handleLoadGo,
|
Action: handleLoadGo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "reset",
|
||||||
|
Usage: "Unload compiled script from the VM",
|
||||||
|
Action: handleReset,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "parse",
|
Name: "parse",
|
||||||
Help: "Parse provided argument and convert it into other possible formats",
|
Usage: "Parse provided argument and convert it into other possible formats",
|
||||||
LongHelp: `Usage: parse <arg>
|
UsageText: `parse <arg>`,
|
||||||
|
Description: `parse <arg>
|
||||||
|
|
||||||
<arg> is an argument which is tried to be interpreted as an item of different types
|
<arg> is an argument which is tried to be interpreted as an item of different types
|
||||||
and converted to other formats. Strings are escaped and output in quotes.`,
|
and converted to other formats. Strings are escaped and output in quotes.`,
|
||||||
Func: handleParse,
|
Action: handleParse,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Help: "Execute the current loaded script",
|
Usage: "Execute the current loaded script",
|
||||||
LongHelp: `Usage: run [<method> [<parameter>...]]
|
UsageText: `run [<method> [<parameter>...]]`,
|
||||||
|
Description: `run [<method> [<parameter>...]]
|
||||||
|
|
||||||
<method> is a contract method, specified in manifest (and it
|
<method> is a contract method, specified in manifest. It can be '_' which will push
|
||||||
can't be 'help' at the moment). It can be '_' which will push parameters
|
parameters onto the stack and execute from the current offset.
|
||||||
onto the stack and execute from the current offset.
|
|
||||||
<parameter> is a parameter (can be repeated multiple times) that can be specified
|
<parameter> is a parameter (can be repeated multiple times) that can be specified
|
||||||
as <type>:<value>, where type can be:
|
as <type>:<value>, where type can be:
|
||||||
'` + boolType + `': supports '` + boolFalse + `' and '` + boolTrue + `' values
|
'` + boolType + `': supports '` + boolFalse + `' and '` + boolTrue + `' values
|
||||||
|
@ -153,54 +172,72 @@ both parameters are mandatory, example:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
> run put ` + stringType + `:"Something to put"`,
|
> run put ` + stringType + `:"Something to put"`,
|
||||||
Func: handleRun,
|
Action: handleRun,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "cont",
|
Name: "cont",
|
||||||
Help: "Continue execution of the current loaded script",
|
Usage: "Continue execution of the current loaded script",
|
||||||
LongHelp: "Continue execution of the current loaded script",
|
Description: "Continue execution of the current loaded script",
|
||||||
Func: handleCont,
|
Action: handleCont,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "step",
|
Name: "step",
|
||||||
Help: "Step (n) instruction in the program",
|
Usage: "Step (n) instruction in the program",
|
||||||
LongHelp: `Usage: step [<n>]
|
UsageText: `step [<n>]`,
|
||||||
|
Description: `step [<n>]
|
||||||
<n> is optional parameter to specify number of instructions to run, example:
|
<n> is optional parameter to specify number of instructions to run, example:
|
||||||
> step 10`,
|
> step 10`,
|
||||||
Func: handleStep,
|
Action: handleStep,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "stepinto",
|
Name: "stepinto",
|
||||||
Help: "Stepinto instruction to take in the debugger",
|
Usage: "Stepinto instruction to take in the debugger",
|
||||||
LongHelp: `Usage: stepInto
|
Description: `Usage: stepInto
|
||||||
example:
|
example:
|
||||||
> stepinto`,
|
> stepinto`,
|
||||||
Func: handleStepInto,
|
Action: handleStepInto,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "stepout",
|
Name: "stepout",
|
||||||
Help: "Stepout instruction to take in the debugger",
|
Usage: "Stepout instruction to take in the debugger",
|
||||||
LongHelp: `Usage: stepOut
|
Description: `stepOut
|
||||||
example:
|
example:
|
||||||
> stepout`,
|
> stepout`,
|
||||||
Func: handleStepOut,
|
Action: handleStepOut,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "stepover",
|
Name: "stepover",
|
||||||
Help: "Stepover instruction to take in the debugger",
|
Usage: "Stepover instruction to take in the debugger",
|
||||||
LongHelp: `Usage: stepOver
|
Description: `stepOver
|
||||||
example:
|
example:
|
||||||
> stepover`,
|
> stepover`,
|
||||||
Func: handleStepOver,
|
Action: handleStepOver,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ops",
|
Name: "ops",
|
||||||
Help: "Dump opcodes of the current loaded program",
|
Usage: "Dump opcodes of the current loaded program",
|
||||||
LongHelp: "Dump opcodes of the current loaded program",
|
Description: "Dump opcodes of the current loaded program",
|
||||||
Func: handleOps,
|
Action: handleOps,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var completer *readline.PrefixCompleter
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var pcItems []readline.PrefixCompleterInterface
|
||||||
|
for _, c := range commands {
|
||||||
|
if !c.Hidden {
|
||||||
|
var flagsItems []readline.PrefixCompleterInterface
|
||||||
|
for _, f := range c.Flags {
|
||||||
|
names := strings.SplitN(f.GetName(), ", ", 2) // only long name will be offered
|
||||||
|
flagsItems = append(flagsItems, readline.PcItem("--"+names[0]))
|
||||||
|
}
|
||||||
|
pcItems = append(pcItems, readline.PcItem(c.Name, flagsItems...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completer = readline.NewPrefixCompleter(pcItems...)
|
||||||
|
}
|
||||||
|
|
||||||
// Various errors.
|
// Various errors.
|
||||||
var (
|
var (
|
||||||
ErrMissingParameter = errors.New("missing argument")
|
ErrMissingParameter = errors.New("missing argument")
|
||||||
|
@ -210,119 +247,165 @@ var (
|
||||||
// VMCLI object for interacting with the VM.
|
// VMCLI object for interacting with the VM.
|
||||||
type VMCLI struct {
|
type VMCLI struct {
|
||||||
vm *vm.VM
|
vm *vm.VM
|
||||||
shell *ishell.Shell
|
shell *cli.App
|
||||||
// printLogo specifies if logo is printed.
|
|
||||||
printLogo bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new VMCLI object.
|
// New returns a new VMCLI object.
|
||||||
func New() *VMCLI {
|
func New() *VMCLI {
|
||||||
return NewWithConfig(true, os.Exit, &readline.Config{
|
return NewWithConfig(true, os.Exit, &readline.Config{
|
||||||
Prompt: ">>>",
|
Prompt: "\033[32mNEO-GO-VM >\033[0m ", // green prompt ^^
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWithConfig returns new VMCLI instance using provided config.
|
// NewWithConfig returns new VMCLI instance using provided config.
|
||||||
func NewWithConfig(printLogo bool, onExit func(int), c *readline.Config) *VMCLI {
|
func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config) *VMCLI {
|
||||||
|
if c.AutoComplete == nil {
|
||||||
|
// Autocomplete commands/flags on TAB.
|
||||||
|
c.AutoComplete = completer
|
||||||
|
}
|
||||||
|
l, err := readline.NewEx(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ctl := cli.NewApp()
|
||||||
|
ctl.Name = "VM CLI"
|
||||||
|
|
||||||
|
// Note: need to set empty `ctl.HelpName` and `ctl.UsageText`, otherwise
|
||||||
|
// `filepath.Base(os.Args[0])` will be used which is `neo-go`.
|
||||||
|
ctl.HelpName = ""
|
||||||
|
ctl.UsageText = ""
|
||||||
|
|
||||||
|
ctl.Writer = l.Stdout()
|
||||||
|
ctl.ErrWriter = l.Stderr()
|
||||||
|
ctl.Version = config.Version
|
||||||
|
ctl.Usage = "Official VM CLI for Neo-Go"
|
||||||
|
|
||||||
|
// Override default error handler in order not to exit on error.
|
||||||
|
ctl.ExitErrHandler = func(context *cli.Context, err error) {}
|
||||||
|
|
||||||
|
ctl.Commands = commands
|
||||||
|
|
||||||
vmcli := VMCLI{
|
vmcli := VMCLI{
|
||||||
vm: vm.New(),
|
vm: vm.New(),
|
||||||
shell: ishell.NewWithConfig(c),
|
shell: ctl,
|
||||||
printLogo: printLogo,
|
|
||||||
}
|
}
|
||||||
vmcli.shell.Set(vmKey, vmcli.vm)
|
|
||||||
vmcli.shell.Set(manifestKey, new(manifest.Manifest))
|
vmcli.shell.Metadata = map[string]interface{}{
|
||||||
vmcli.shell.Set(exitFunc, onExit)
|
vmKey: vmcli.vm,
|
||||||
for _, c := range commands {
|
manifestKey: new(manifest.Manifest),
|
||||||
vmcli.shell.AddCmd(c)
|
exitFuncKey: onExit,
|
||||||
|
readlineInstanceKey: l,
|
||||||
|
printLogoKey: printLogotype,
|
||||||
}
|
}
|
||||||
changePrompt(vmcli.shell, vmcli.vm)
|
changePrompt(vmcli.shell)
|
||||||
return &vmcli
|
return &vmcli
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVMFromContext(c *ishell.Context) *vm.VM {
|
func getExitFuncFromContext(app *cli.App) func(int) {
|
||||||
return c.Get(vmKey).(*vm.VM)
|
return app.Metadata[exitFuncKey].(func(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getManifestFromContext(c *ishell.Context) *manifest.Manifest {
|
func getReadlineInstanceFromContext(app *cli.App) *readline.Instance {
|
||||||
return c.Get(manifestKey).(*manifest.Manifest)
|
return app.Metadata[readlineInstanceKey].(*readline.Instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setManifestInContext(c *ishell.Context, m *manifest.Manifest) {
|
func getVMFromContext(app *cli.App) *vm.VM {
|
||||||
old := getManifestFromContext(c)
|
return app.Metadata[vmKey].(*vm.VM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVMInContext(app *cli.App, v *vm.VM) {
|
||||||
|
old := getVMFromContext(app)
|
||||||
|
*old = *v
|
||||||
|
}
|
||||||
|
|
||||||
|
func getManifestFromContext(app *cli.App) *manifest.Manifest {
|
||||||
|
return app.Metadata[manifestKey].(*manifest.Manifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrintLogoFromContext(app *cli.App) bool {
|
||||||
|
return app.Metadata[printLogoKey].(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setManifestInContext(app *cli.App, m *manifest.Manifest) {
|
||||||
|
old := getManifestFromContext(app)
|
||||||
*old = *m
|
*old = *m
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkVMIsReady(c *ishell.Context) bool {
|
func checkVMIsReady(app *cli.App) bool {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(app)
|
||||||
if v == nil || !v.Ready() {
|
if v == nil || !v.Ready() {
|
||||||
c.Err(errors.New("VM is not ready: no program loaded"))
|
writeErr(app.Writer, errors.New("VM is not ready: no program loaded"))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleExit(c *ishell.Context) {
|
func handleExit(c *cli.Context) error {
|
||||||
c.Println("Bye!")
|
l := getReadlineInstanceFromContext(c.App)
|
||||||
c.Get(exitFunc).(func(int))(0)
|
_ = l.Close()
|
||||||
|
exit := getExitFuncFromContext(c.App)
|
||||||
|
fmt.Fprintln(c.App.Writer, "Bye!")
|
||||||
|
exit(0)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleIP(c *ishell.Context) {
|
func handleIP(c *cli.Context) error {
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
ctx := v.Context()
|
ctx := v.Context()
|
||||||
if ctx.NextIP() < ctx.LenInstr() {
|
if ctx.NextIP() < ctx.LenInstr() {
|
||||||
ip, opcode := v.Context().NextInstr()
|
ip, opcode := v.Context().NextInstr()
|
||||||
c.Printf("instruction pointer at %d (%s)\n", ip, opcode)
|
fmt.Fprintf(c.App.Writer, "instruction pointer at %d (%s)\n", ip, opcode)
|
||||||
} else {
|
} else {
|
||||||
c.Println("execution has finished")
|
fmt.Fprintln(c.App.Writer, "execution has finished")
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleBreak(c *ishell.Context) {
|
func handleBreak(c *cli.Context) error {
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) != 1 {
|
args := c.Args()
|
||||||
c.Err(fmt.Errorf("%w: <ip>", ErrMissingParameter))
|
if len(args) != 1 {
|
||||||
return
|
return fmt.Errorf("%w: <ip>", ErrMissingParameter)
|
||||||
}
|
}
|
||||||
n, err := strconv.Atoi(c.Args[0])
|
n, err := strconv.Atoi(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(fmt.Errorf("%w: %v", ErrInvalidParameter, err))
|
return fmt.Errorf("%w: %s", ErrInvalidParameter, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v.AddBreakPoint(n)
|
v.AddBreakPoint(n)
|
||||||
c.Printf("breakpoint added at instruction %d\n", n)
|
fmt.Fprintf(c.App.Writer, "breakpoint added at instruction %d\n", n)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleXStack(c *ishell.Context) {
|
func handleXStack(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
var stackDump string
|
var stackDump string
|
||||||
switch c.Cmd.Name {
|
switch c.Command.Name {
|
||||||
case "estack":
|
case "estack":
|
||||||
stackDump = v.DumpEStack()
|
stackDump = v.DumpEStack()
|
||||||
case "istack":
|
case "istack":
|
||||||
stackDump = v.DumpIStack()
|
stackDump = v.DumpIStack()
|
||||||
default:
|
default:
|
||||||
c.Err(errors.New("unknown stack"))
|
return errors.New("unknown stack")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Println(stackDump)
|
fmt.Fprintln(c.App.Writer, stackDump)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSlots(c *ishell.Context) {
|
func handleSlots(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
vmCtx := v.Context()
|
vmCtx := v.Context()
|
||||||
if vmCtx == nil {
|
if vmCtx == nil {
|
||||||
c.Err(errors.New("no program loaded"))
|
return errors.New("no program loaded")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var rawSlot string
|
var rawSlot string
|
||||||
switch c.Cmd.Name {
|
switch c.Command.Name {
|
||||||
case "sslot":
|
case "sslot":
|
||||||
rawSlot = vmCtx.DumpStaticSlot()
|
rawSlot = vmCtx.DumpStaticSlot()
|
||||||
case "lslot":
|
case "lslot":
|
||||||
|
@ -330,89 +413,93 @@ func handleSlots(c *ishell.Context) {
|
||||||
case "aslot":
|
case "aslot":
|
||||||
rawSlot = vmCtx.DumpArgumentsSlot()
|
rawSlot = vmCtx.DumpArgumentsSlot()
|
||||||
default:
|
default:
|
||||||
c.Err(errors.New("unknown slot"))
|
return errors.New("unknown slot")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Println(rawSlot)
|
fmt.Fprintln(c.App.Writer, rawSlot)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadNEF(c *ishell.Context) {
|
func handleLoadNEF(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) < 2 {
|
args := c.Args()
|
||||||
c.Err(fmt.Errorf("%w: <file> <manifest>", ErrMissingParameter))
|
if len(args) < 2 {
|
||||||
return
|
return fmt.Errorf("%w: <file> <manifest>", ErrMissingParameter)
|
||||||
}
|
}
|
||||||
if err := v.LoadFileWithFlags(c.Args[0], callflag.All); err != nil {
|
if err := v.LoadFileWithFlags(args[0], callflag.All); err != nil {
|
||||||
c.Err(err)
|
return fmt.Errorf("failed to read nef: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
m, err := getManifestFromFile(c.Args[1])
|
m, err := getManifestFromFile(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
return fmt.Errorf("failed to read manifest: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||||
setManifestInContext(c, m)
|
setManifestInContext(c.App, m)
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadBase64(c *ishell.Context) {
|
func handleLoadBase64(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) < 1 {
|
args := c.Args()
|
||||||
c.Err(fmt.Errorf("%w: <string>", ErrMissingParameter))
|
if len(args) < 1 {
|
||||||
return
|
return fmt.Errorf("%w: <string>", ErrMissingParameter)
|
||||||
}
|
}
|
||||||
b, err := base64.StdEncoding.DecodeString(c.Args[0])
|
b, err := base64.StdEncoding.DecodeString(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(fmt.Errorf("%w: %v", ErrInvalidParameter, err))
|
return fmt.Errorf("%w: %s", ErrInvalidParameter, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
v.LoadWithFlags(b, callflag.All)
|
v.LoadWithFlags(b, callflag.All)
|
||||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadHex(c *ishell.Context) {
|
func handleLoadHex(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) < 1 {
|
args := c.Args()
|
||||||
c.Err(fmt.Errorf("%w: <string>", ErrMissingParameter))
|
if len(args) < 1 {
|
||||||
return
|
return fmt.Errorf("%w: <string>", ErrMissingParameter)
|
||||||
}
|
}
|
||||||
b, err := hex.DecodeString(c.Args[0])
|
b, err := hex.DecodeString(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(fmt.Errorf("%w: %v", ErrInvalidParameter, err))
|
return fmt.Errorf("%w: %s", ErrInvalidParameter, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
v.LoadWithFlags(b, callflag.All)
|
v.LoadWithFlags(b, callflag.All)
|
||||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadGo(c *ishell.Context) {
|
func handleLoadGo(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) < 1 {
|
args := c.Args()
|
||||||
c.Err(fmt.Errorf("%w: <file>", ErrMissingParameter))
|
if len(args) < 1 {
|
||||||
return
|
return fmt.Errorf("%w: <file>", ErrMissingParameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := strings.TrimSuffix(c.Args[0], ".go")
|
name := strings.TrimSuffix(args[0], ".go")
|
||||||
b, di, err := compiler.CompileWithOptions(c.Args[0], nil, &compiler.Options{Name: name})
|
b, di, err := compiler.CompileWithOptions(args[0], nil, &compiler.Options{Name: name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
return fmt.Errorf("failed to compile: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't perform checks, just load.
|
// Don't perform checks, just load.
|
||||||
m, err := di.ConvertToManifest(&compiler.Options{})
|
m, err := di.ConvertToManifest(&compiler.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(fmt.Errorf("can't create manifest: %w", err))
|
return fmt.Errorf("can't create manifest: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
setManifestInContext(c, m)
|
setManifestInContext(c.App, m)
|
||||||
|
|
||||||
v.LoadWithFlags(b.Script, callflag.All)
|
v.LoadWithFlags(b.Script, callflag.All)
|
||||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleReset(c *cli.Context) error {
|
||||||
|
setVMInContext(c.App, vm.New())
|
||||||
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
||||||
|
@ -428,27 +515,26 @@ func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
||||||
return &m, nil
|
return &m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRun(c *ishell.Context) {
|
func handleRun(c *cli.Context) error {
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
m := getManifestFromContext(c)
|
m := getManifestFromContext(c.App)
|
||||||
if len(c.Args) != 0 {
|
args := c.Args()
|
||||||
|
if len(args) != 0 {
|
||||||
var (
|
var (
|
||||||
params []stackitem.Item
|
params []stackitem.Item
|
||||||
offset int
|
offset int
|
||||||
err error
|
err error
|
||||||
runCurrent = c.Args[0] != "_"
|
runCurrent = args[0] != "_"
|
||||||
)
|
)
|
||||||
|
|
||||||
params, err = parseArgs(c.Args[1:])
|
params, err = parseArgs(args[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if runCurrent {
|
if runCurrent {
|
||||||
md := m.ABI.GetMethod(c.Args[0], len(params))
|
md := m.ABI.GetMethod(args[0], len(params))
|
||||||
if md == nil {
|
if md == nil {
|
||||||
c.Err(fmt.Errorf("%w: method not found", ErrInvalidParameter))
|
return fmt.Errorf("%w: method not found", ErrInvalidParameter)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
offset = md.Offset
|
offset = md.Offset
|
||||||
}
|
}
|
||||||
|
@ -457,8 +543,7 @@ func handleRun(c *ishell.Context) {
|
||||||
}
|
}
|
||||||
if runCurrent {
|
if runCurrent {
|
||||||
if !v.Ready() {
|
if !v.Ready() {
|
||||||
c.Err(fmt.Errorf("no program loaded"))
|
return errors.New("no program loaded")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
v.Context().Jump(offset)
|
v.Context().Jump(offset)
|
||||||
if initMD := m.ABI.GetMethod(manifest.MethodInit, 0); initMD != nil {
|
if initMD := m.ABI.GetMethod(manifest.MethodInit, 0); initMD != nil {
|
||||||
|
@ -466,15 +551,17 @@ func handleRun(c *ishell.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runVMWithHandling(c, v)
|
runVMWithHandling(c)
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runVMWithHandling runs VM with handling errors and additional state messages.
|
// runVMWithHandling runs VM with handling errors and additional state messages.
|
||||||
func runVMWithHandling(c *ishell.Context, v *vm.VM) {
|
func runVMWithHandling(c *cli.Context) {
|
||||||
|
v := getVMFromContext(c.App)
|
||||||
err := v.Run()
|
err := v.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
writeErr(c.App.ErrWriter, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var message string
|
var message string
|
||||||
|
@ -493,58 +580,59 @@ func runVMWithHandling(c *ishell.Context, v *vm.VM) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if message != "" {
|
if message != "" {
|
||||||
c.Println(message)
|
fmt.Fprintln(c.App.Writer, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleCont(c *ishell.Context) {
|
func handleCont(c *cli.Context) error {
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
runVMWithHandling(c)
|
||||||
runVMWithHandling(c, v)
|
changePrompt(c.App)
|
||||||
changePrompt(c, v)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStep(c *ishell.Context) {
|
func handleStep(c *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
n = 1
|
n = 1
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
if len(c.Args) > 0 {
|
args := c.Args()
|
||||||
n, err = strconv.Atoi(c.Args[0])
|
if len(args) > 0 {
|
||||||
|
n, err = strconv.Atoi(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(fmt.Errorf("%w: %v", ErrInvalidParameter, err))
|
return fmt.Errorf("%w: %s", ErrInvalidParameter, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.AddBreakPointRel(n)
|
v.AddBreakPointRel(n)
|
||||||
runVMWithHandling(c, v)
|
runVMWithHandling(c)
|
||||||
changePrompt(c, v)
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStepInto(c *ishell.Context) {
|
func handleStepInto(c *cli.Context) error {
|
||||||
handleStepType(c, "into")
|
return handleStepType(c, "into")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStepOut(c *ishell.Context) {
|
func handleStepOut(c *cli.Context) error {
|
||||||
handleStepType(c, "out")
|
return handleStepType(c, "out")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStepOver(c *ishell.Context) {
|
func handleStepOver(c *cli.Context) error {
|
||||||
handleStepType(c, "over")
|
return handleStepType(c, "over")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStepType(c *ishell.Context, stepType string) {
|
func handleStepType(c *cli.Context, stepType string) error {
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
var err error
|
var err error
|
||||||
switch stepType {
|
switch stepType {
|
||||||
case "into":
|
case "into":
|
||||||
|
@ -555,47 +643,69 @@ func handleStepType(c *ishell.Context, stepType string) {
|
||||||
err = v.StepOver()
|
err = v.StepOver()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
return err
|
||||||
} else {
|
|
||||||
handleIP(c)
|
|
||||||
}
|
}
|
||||||
changePrompt(c, v)
|
_ = handleIP(c)
|
||||||
|
changePrompt(c.App)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleOps(c *ishell.Context) {
|
func handleOps(c *cli.Context) error {
|
||||||
if !checkVMIsReady(c) {
|
if !checkVMIsReady(c.App) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
v := getVMFromContext(c)
|
v := getVMFromContext(c.App)
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
v.PrintOps(out)
|
v.PrintOps(out)
|
||||||
c.Println(out.String())
|
fmt.Fprintln(c.App.Writer, out.String())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func changePrompt(c ishell.Actions, v *vm.VM) {
|
func changePrompt(app *cli.App) {
|
||||||
|
v := getVMFromContext(app)
|
||||||
|
l := getReadlineInstanceFromContext(app)
|
||||||
if v.Ready() && v.Context().NextIP() >= 0 && v.Context().NextIP() < v.Context().LenInstr() {
|
if v.Ready() && v.Context().NextIP() >= 0 && v.Context().NextIP() < v.Context().LenInstr() {
|
||||||
c.SetPrompt(fmt.Sprintf("NEO-GO-VM %d > ", v.Context().NextIP()))
|
l.SetPrompt(fmt.Sprintf("\033[32mNEO-GO-VM %d >\033[0m ", v.Context().NextIP()))
|
||||||
} else {
|
} else {
|
||||||
c.SetPrompt("NEO-GO-VM > ")
|
l.SetPrompt("\033[32mNEO-GO-VM >\033[0m ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run waits for user input from Stdin and executes the passed command.
|
// Run waits for user input from Stdin and executes the passed command.
|
||||||
func (c *VMCLI) Run() error {
|
func (c *VMCLI) Run() error {
|
||||||
if c.printLogo {
|
if getPrintLogoFromContext(c.shell) {
|
||||||
printLogo(c.shell)
|
printLogo(c.shell.Writer)
|
||||||
|
}
|
||||||
|
l := getReadlineInstanceFromContext(c.shell)
|
||||||
|
for {
|
||||||
|
line, err := l.Readline()
|
||||||
|
if err == io.EOF || err == readline.ErrInterrupt {
|
||||||
|
return nil // OK, stop execution.
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read input: %w", err) // Critical error, stop execution.
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := shellquote.Split(line)
|
||||||
|
if err != nil {
|
||||||
|
writeErr(c.shell.ErrWriter, fmt.Errorf("failed to parse arguments: %w", err))
|
||||||
|
continue // Not a critical error, continue execution.
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.shell.Run(append([]string{"vm"}, args...))
|
||||||
|
if err != nil {
|
||||||
|
writeErr(c.shell.ErrWriter, err) // Various command/flags parsing errors and execution errors.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.shell.Run()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleParse(c *ishell.Context) {
|
func handleParse(c *cli.Context) error {
|
||||||
res, err := Parse(c.Args)
|
res, err := Parse(c.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err(err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Print(res)
|
fmt.Fprintln(c.App.Writer, res)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse converts it's argument to other formats.
|
// Parse converts it's argument to other formats.
|
||||||
|
@ -708,8 +818,13 @@ const logo = `
|
||||||
/_/ |_/_____/\____/ \____/\____/ |___/_/ /_/
|
/_/ |_/_____/\____/ \____/\____/ |___/_/ /_/
|
||||||
`
|
`
|
||||||
|
|
||||||
func printLogo(c *ishell.Shell) {
|
func printLogo(w io.Writer) {
|
||||||
c.Print(logo)
|
fmt.Fprint(w, logo)
|
||||||
c.Println()
|
fmt.Fprintln(w)
|
||||||
c.Println()
|
fmt.Fprintln(w)
|
||||||
|
fmt.Fprintln(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeErr(w io.Writer, err error) {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/abiosoft/readline"
|
"github.com/chzyer/readline"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
@ -75,6 +75,7 @@ func newTestVMCLIWithLogo(t *testing.T, printLogo bool) *executor {
|
||||||
&readline.Config{
|
&readline.Config{
|
||||||
Prompt: "",
|
Prompt: "",
|
||||||
Stdin: e.in,
|
Stdin: e.in,
|
||||||
|
Stderr: e.out,
|
||||||
Stdout: e.out,
|
Stdout: e.out,
|
||||||
FuncIsTerminal: func() bool {
|
FuncIsTerminal: func() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -205,11 +206,12 @@ func TestLoad(t *testing.T) {
|
||||||
}`
|
}`
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
t.Run("loadgo", func(t *testing.T) {
|
checkLoadgo := func(t *testing.T, tName, cName, cErrName string) {
|
||||||
filename := filepath.Join(tmpDir, "vmtestcontract.go")
|
t.Run("loadgo "+tName, func(t *testing.T) {
|
||||||
|
filename := filepath.Join(tmpDir, cName)
|
||||||
require.NoError(t, ioutil.WriteFile(filename, []byte(src), os.ModePerm))
|
require.NoError(t, ioutil.WriteFile(filename, []byte(src), os.ModePerm))
|
||||||
filename = "'" + filename + "'"
|
filename = "'" + filename + "'"
|
||||||
filenameErr := filepath.Join(tmpDir, "vmtestcontract_err.go")
|
filenameErr := filepath.Join(tmpDir, cErrName)
|
||||||
require.NoError(t, ioutil.WriteFile(filenameErr, []byte(src+"invalid_token"), os.ModePerm))
|
require.NoError(t, ioutil.WriteFile(filenameErr, []byte(src+"invalid_token"), os.ModePerm))
|
||||||
filenameErr = "'" + filenameErr + "'"
|
filenameErr = "'" + filenameErr + "'"
|
||||||
goMod := []byte(`module test.example/vmcli
|
goMod := []byte(`module test.example/vmcli
|
||||||
|
@ -228,6 +230,11 @@ go 1.16`)
|
||||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||||
e.checkStack(t, 8)
|
e.checkStack(t, 8)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLoadgo(t, "simple", "vmtestcontract.go", "vmtestcontract_err.go")
|
||||||
|
checkLoadgo(t, "utf-8 with spaces", "тестовый контракт.go", "тестовый контракт с ошибкой.go")
|
||||||
|
|
||||||
t.Run("loadgo, check calling flags", func(t *testing.T) {
|
t.Run("loadgo, check calling flags", func(t *testing.T) {
|
||||||
srcAllowNotify := `package kek
|
srcAllowNotify := `package kek
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
|
@ -640,3 +647,19 @@ func TestExit(t *testing.T) {
|
||||||
e.runProg(t, "exit")
|
e.runProg(t, "exit")
|
||||||
require.True(t, e.exit.Load())
|
require.True(t, e.exit.Load())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReset(t *testing.T) {
|
||||||
|
script := []byte{byte(opcode.PUSH1)}
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadhex "+hex.EncodeToString(script),
|
||||||
|
"ops",
|
||||||
|
"reset",
|
||||||
|
"ops")
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded 1 instructions")
|
||||||
|
e.checkNextLine(t, "INDEX.*OPCODE.*PARAMETER")
|
||||||
|
e.checkNextLine(t, "0.*PUSH1.*")
|
||||||
|
e.checkNextLine(t, "")
|
||||||
|
e.checkError(t, fmt.Errorf("VM is not ready: no program loaded"))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue