mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 23:25:22 +00:00
Merge pull request #385 from nspcc-dev/vm-contract-invoke-parameters-332-part1
VM CLI improvements, part 1 of #332 work.
This commit is contained in:
commit
14c02cb3b1
4 changed files with 321 additions and 172 deletions
7
go.mod
7
go.mod
|
@ -1,13 +1,19 @@
|
|||
module github.com/CityOfZion/neo-go
|
||||
|
||||
require (
|
||||
github.com/abiosoft/ishell v2.0.0+incompatible // indirect
|
||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||
github.com/go-redis/redis v6.10.2+incompatible
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 // indirect
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.9 // indirect
|
||||
github.com/mr-tron/base58 v1.1.2
|
||||
github.com/nspcc-dev/rfc6979 v0.1.0
|
||||
github.com/onsi/gomega v1.4.2 // indirect
|
||||
|
@ -21,6 +27,7 @@ require (
|
|||
golang.org/x/crypto v0.0.0-20180316180149-374053ea96cb
|
||||
golang.org/x/text v0.3.0
|
||||
golang.org/x/tools v0.0.0-20180318012157-96caea41033d
|
||||
gopkg.in/abiosoft/ishell.v2 v2.0.0
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
|
||||
)
|
||||
|
|
21
go.sum
21
go.sum
|
@ -1,12 +1,22 @@
|
|||
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/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
|
||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
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/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/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-redis/redis v6.10.2+incompatible h1:SLbqrO/Ik1nhXA5/cbEs1P5MUBo1Qq4ihlNfGnnipPw=
|
||||
|
@ -21,6 +31,11 @@ github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNu
|
|||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78=
|
||||
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/nspcc-dev/rfc6979 v0.1.0 h1:Lwg7esRRoyK1Up/IN1vAef1EmvrBeMHeeEkek2fAJ6c=
|
||||
|
@ -51,12 +66,16 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh
|
|||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180318012157-96caea41033d h1:Xmo0nLTRYewf0eXDvo12nMSuOgNQ4283hdbOHIUf7h8=
|
||||
golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/abiosoft/ishell.v2 v2.0.0 h1:/J5yh3nWYSSGFjALcitTI9CLE0Tu27vBYHX0srotqOc=
|
||||
gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
|
|
@ -1,197 +1,323 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
||||
"gopkg.in/abiosoft/ishell.v2"
|
||||
)
|
||||
|
||||
// command describes a VM command.
|
||||
type command struct {
|
||||
// number of minimum arguments the command needs.
|
||||
args int
|
||||
// description of the command.
|
||||
usage string
|
||||
// whether the VM needs to be "ready" to execute this command.
|
||||
ready bool
|
||||
const (
|
||||
vmKey = "vm"
|
||||
boolType = "bool"
|
||||
boolFalse = "false"
|
||||
boolTrue = "true"
|
||||
intType = "int"
|
||||
stringType = "string"
|
||||
)
|
||||
|
||||
var commands = []*ishell.Cmd{
|
||||
{
|
||||
Name: "exit",
|
||||
Help: "Exit the VM prompt",
|
||||
LongHelp: "Exit the VM prompt",
|
||||
Func: handleExit,
|
||||
},
|
||||
{
|
||||
Name: "ip",
|
||||
Help: "Show current instruction",
|
||||
LongHelp: "Show current instruction",
|
||||
Func: handleIP,
|
||||
},
|
||||
{
|
||||
Name: "break",
|
||||
Help: "Place a breakpoint",
|
||||
LongHelp: `Usage: break <ip>
|
||||
<ip> is mandatory parameter, example:
|
||||
> break 12`,
|
||||
Func: handleBreak,
|
||||
},
|
||||
{
|
||||
Name: "estack",
|
||||
Help: "Show evaluation stack contents",
|
||||
LongHelp: "Show evaluation stack contents",
|
||||
Func: handleXStack,
|
||||
},
|
||||
{
|
||||
Name: "astack",
|
||||
Help: "Show alt stack contents",
|
||||
LongHelp: "Show alt stack contents",
|
||||
Func: handleXStack,
|
||||
},
|
||||
{
|
||||
Name: "istack",
|
||||
Help: "Show invocation stack contents",
|
||||
LongHelp: "Show invocation stack contents",
|
||||
Func: handleXStack,
|
||||
},
|
||||
{
|
||||
Name: "loadavm",
|
||||
Help: "Load an avm script into the VM",
|
||||
LongHelp: `Usage: loadavm <file>
|
||||
<file> is mandatory parameter, example:
|
||||
> load /path/to/script.avm`,
|
||||
Func: handleLoadAVM,
|
||||
},
|
||||
{
|
||||
Name: "loadhex",
|
||||
Help: "Load a hex-encoded script string into the VM",
|
||||
LongHelp: `Usage: loadhex <string>
|
||||
<string> is mandatory parameter, example:
|
||||
> load 006166`,
|
||||
Func: handleLoadHex,
|
||||
},
|
||||
{
|
||||
Name: "loadgo",
|
||||
Help: "Compile and load a Go file into the VM",
|
||||
LongHelp: `Usage: loadhex <file>
|
||||
<file> is mandatory parameter, example:
|
||||
> load /path/to/file.go`,
|
||||
Func: handleLoadGo,
|
||||
},
|
||||
{
|
||||
Name: "run",
|
||||
Help: "Execute the current loaded script",
|
||||
LongHelp: `Usage: run [<operation> [<parameter>...]]
|
||||
|
||||
<operation> is an operation name, passed as a first parameter to Main() (and it
|
||||
can't be 'help' at the moment)
|
||||
<parameter> is a parameter (can be repeated multiple times) that can be specified
|
||||
as <type>:<value>, where type can be:
|
||||
'` + boolType + `': supports '` + boolFalse + `' and '` + boolTrue + `' values
|
||||
'` + intType + `': supports integers as values
|
||||
'` + stringType + `': supports strings as values (that are pushed as a byte array
|
||||
values to the stack)
|
||||
or can be just <value>, for which the type will be detected automatically
|
||||
following these rules: '` + boolTrue + `' and '` + boolFalse + `' are treated as respective
|
||||
boolean values, everything that can be converted to integer is treated as
|
||||
integer and everything else is treated like a string.
|
||||
|
||||
Passing parameters without operation is not supported. Parameters are packed
|
||||
into array before they're passed to the script, so effectively 'run' only
|
||||
supports contracts with signatures like this:
|
||||
func Main(operation string, args []interface{}) interface{}
|
||||
|
||||
Example:
|
||||
> run put ` + stringType + `:"Something to put"`,
|
||||
Func: handleRun,
|
||||
},
|
||||
{
|
||||
Name: "cont",
|
||||
Help: "Continue execution of the current loaded script",
|
||||
LongHelp: "Continue execution of the current loaded script",
|
||||
Func: handleCont,
|
||||
},
|
||||
{
|
||||
Name: "step",
|
||||
Help: "Step (n) instruction in the program",
|
||||
LongHelp: `Usage: step [<n>]
|
||||
<n> is optional parameter to specify number of instructions to run, example:
|
||||
> step 10`,
|
||||
Func: handleStep,
|
||||
},
|
||||
{
|
||||
Name: "ops",
|
||||
Help: "Dump opcodes of the current loaded program",
|
||||
LongHelp: "Dump opcodes of the current loaded program",
|
||||
Func: handleOps,
|
||||
},
|
||||
}
|
||||
|
||||
var commands = map[string]command{
|
||||
"help": {0, "show available commands", false},
|
||||
"exit": {0, "exit the VM prompt", false},
|
||||
"ip": {0, "show the current instruction", true},
|
||||
"break": {1, "place a breakpoint (> break 1)", true},
|
||||
"estack": {0, "show evaluation stack details", false},
|
||||
"astack": {0, "show alt stack details", false},
|
||||
"istack": {0, "show invocation stack details", false},
|
||||
"loadavm": {1, "load an avm script into the VM (> load /path/to/script.avm)", false},
|
||||
"loadhex": {1, "load a hex string into the VM (> loadhex 006166 )", false},
|
||||
"loadgo": {1, "compile and load a .go file into the VM (> load /path/to/file.go)", false},
|
||||
"run": {0, "execute the current loaded script", true},
|
||||
"cont": {0, "continue execution of the current loaded script", true},
|
||||
"step": {0, "step (n) instruction in the program (> step 10)", true},
|
||||
"ops": {0, "show the opcodes of the current loaded program", true},
|
||||
}
|
||||
|
||||
// VMCLI object for interacting with the VM.
|
||||
// VMCLI object for interacting with the VM.
|
||||
type VMCLI struct {
|
||||
vm *vm.VM
|
||||
vm *vm.VM
|
||||
shell *ishell.Shell
|
||||
}
|
||||
|
||||
// New returns a new VMCLI object.
|
||||
func New() *VMCLI {
|
||||
return &VMCLI{
|
||||
vm: vm.New(0),
|
||||
vmcli := VMCLI{
|
||||
vm: vm.New(0),
|
||||
shell: ishell.New(),
|
||||
}
|
||||
vmcli.shell.Set(vmKey, vmcli.vm)
|
||||
for _, c := range commands {
|
||||
vmcli.shell.AddCmd(c)
|
||||
}
|
||||
changePrompt(vmcli.shell, vmcli.vm)
|
||||
return &vmcli
|
||||
}
|
||||
|
||||
func (c *VMCLI) handleCommand(cmd string, args ...string) {
|
||||
com, ok := commands[cmd]
|
||||
if !ok {
|
||||
fmt.Printf("unknown command (%s)\n", cmd)
|
||||
func getVMFromContext(c *ishell.Context) *vm.VM {
|
||||
return c.Get(vmKey).(*vm.VM)
|
||||
}
|
||||
|
||||
func checkVMIsReady(c *ishell.Context) bool {
|
||||
v := getVMFromContext(c)
|
||||
if v == nil || !v.Ready() {
|
||||
c.Err(errors.New("VM is not ready: no program loaded"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleExit(c *ishell.Context) {
|
||||
c.Println("Bye!")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func handleIP(c *ishell.Context) {
|
||||
if !checkVMIsReady(c) {
|
||||
return
|
||||
}
|
||||
if (len(args) < com.args || len(args) > com.args) && cmd != "run" {
|
||||
fmt.Printf("command (%s) takes at least %d arguments\n", cmd, com.args)
|
||||
v := getVMFromContext(c)
|
||||
ip, opcode := v.Context().CurrInstr()
|
||||
c.Printf("instruction pointer at %d (%s)\n", ip, opcode)
|
||||
}
|
||||
|
||||
func handleBreak(c *ishell.Context) {
|
||||
if !checkVMIsReady(c) {
|
||||
return
|
||||
}
|
||||
if com.ready && !c.vm.Ready() {
|
||||
fmt.Println("VM is not ready: no program loaded")
|
||||
v := getVMFromContext(c)
|
||||
if len(c.Args) != 1 {
|
||||
c.Err(errors.New("Missing parameter <ip>"))
|
||||
}
|
||||
n, err := strconv.Atoi(c.Args[0])
|
||||
if err != nil {
|
||||
c.Err(fmt.Errorf("argument conversion error: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "help":
|
||||
printHelp()
|
||||
v.AddBreakPoint(n)
|
||||
c.Printf("breakpoint added at instruction %d\n", n)
|
||||
}
|
||||
|
||||
case "exit":
|
||||
fmt.Println("Bye!")
|
||||
os.Exit(0)
|
||||
func handleXStack(c *ishell.Context) {
|
||||
v := getVMFromContext(c)
|
||||
c.Println(v.Stack(c.Cmd.Name))
|
||||
}
|
||||
|
||||
case "ip":
|
||||
ip, opcode := c.vm.Context().CurrInstr()
|
||||
fmt.Printf("instruction pointer at %d (%s)\n", ip, opcode)
|
||||
func handleLoadAVM(c *ishell.Context) {
|
||||
v := getVMFromContext(c)
|
||||
if err := v.LoadFile(c.Args[0]); err != nil {
|
||||
c.Err(err)
|
||||
} else {
|
||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||
}
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
case "break":
|
||||
n, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
fmt.Printf("argument conversion error: %s\n", err)
|
||||
return
|
||||
}
|
||||
func handleLoadHex(c *ishell.Context) {
|
||||
v := getVMFromContext(c)
|
||||
b, err := hex.DecodeString(c.Args[0])
|
||||
if err != nil {
|
||||
c.Err(err)
|
||||
return
|
||||
}
|
||||
v.Load(b)
|
||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
c.vm.AddBreakPoint(n)
|
||||
fmt.Printf("breakpoint added at instruction %d\n", n)
|
||||
func handleLoadGo(c *ishell.Context) {
|
||||
v := getVMFromContext(c)
|
||||
fb, err := ioutil.ReadFile(c.Args[0])
|
||||
if err != nil {
|
||||
c.Err(err)
|
||||
return
|
||||
}
|
||||
b, err := compiler.Compile(bytes.NewReader(fb), &compiler.Options{})
|
||||
if err != nil {
|
||||
c.Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
case "estack", "istack", "astack":
|
||||
fmt.Println(c.vm.Stack(cmd))
|
||||
v.Load(b)
|
||||
c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
case "loadavm":
|
||||
if err := c.vm.LoadFile(args[0]); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr())
|
||||
}
|
||||
|
||||
case "loadhex":
|
||||
b, err := hex.DecodeString(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
c.vm.Load(b)
|
||||
fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr())
|
||||
|
||||
case "loadgo":
|
||||
fb, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
b, err := compiler.Compile(bytes.NewReader(fb), &compiler.Options{})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
c.vm.Load(b)
|
||||
fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr())
|
||||
|
||||
case "run":
|
||||
if len(args) != 0 {
|
||||
var (
|
||||
method []byte
|
||||
params []vm.StackItem
|
||||
err error
|
||||
start int
|
||||
)
|
||||
if isMethodArg(args[0]) {
|
||||
method = []byte(args[0])
|
||||
start = 1
|
||||
}
|
||||
params, err = parseArgs(args[start:])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
c.vm.LoadArgs(method, params)
|
||||
}
|
||||
c.vm.Run()
|
||||
|
||||
case "cont":
|
||||
c.vm.Run()
|
||||
|
||||
case "step":
|
||||
func handleRun(c *ishell.Context) {
|
||||
v := getVMFromContext(c)
|
||||
if len(c.Args) != 0 {
|
||||
var (
|
||||
n = 1
|
||||
err error
|
||||
method []byte
|
||||
params []vm.StackItem
|
||||
err error
|
||||
)
|
||||
if len(args) > 0 {
|
||||
n, err = strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
fmt.Printf("argument conversion error: %s\n", err)
|
||||
return
|
||||
}
|
||||
method = []byte(c.Args[0])
|
||||
params, err = parseArgs(c.Args[1:])
|
||||
if err != nil {
|
||||
c.Err(err)
|
||||
return
|
||||
}
|
||||
c.vm.AddBreakPointRel(n)
|
||||
c.vm.Run()
|
||||
v.LoadArgs(method, params)
|
||||
}
|
||||
v.Run()
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
case "ops":
|
||||
c.vm.PrintOps()
|
||||
func handleCont(c *ishell.Context) {
|
||||
if !checkVMIsReady(c) {
|
||||
return
|
||||
}
|
||||
v := getVMFromContext(c)
|
||||
v.Run()
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
func handleStep(c *ishell.Context) {
|
||||
var (
|
||||
n = 1
|
||||
err error
|
||||
)
|
||||
|
||||
if !checkVMIsReady(c) {
|
||||
return
|
||||
}
|
||||
v := getVMFromContext(c)
|
||||
if len(c.Args) > 0 {
|
||||
n, err = strconv.Atoi(c.Args[0])
|
||||
if err != nil {
|
||||
c.Err(fmt.Errorf("argument conversion error: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
v.AddBreakPointRel(n)
|
||||
v.Run()
|
||||
changePrompt(c, v)
|
||||
}
|
||||
|
||||
func handleOps(c *ishell.Context) {
|
||||
if !checkVMIsReady(c) {
|
||||
return
|
||||
}
|
||||
v := getVMFromContext(c)
|
||||
v.PrintOps()
|
||||
}
|
||||
|
||||
func changePrompt(c ishell.Actions, v *vm.VM) {
|
||||
if v.Ready() && v.Context().IP()-1 >= 0 {
|
||||
c.SetPrompt(fmt.Sprintf("NEO-GO-VM %d > ", v.Context().IP()-1))
|
||||
} else {
|
||||
c.SetPrompt("NEO-GO-VM > ")
|
||||
}
|
||||
}
|
||||
|
||||
// Run waits for user input from Stdin and executes the passed command.
|
||||
func (c *VMCLI) Run() error {
|
||||
printLogo()
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
if c.vm.Ready() && c.vm.Context().IP()-1 >= 0 {
|
||||
fmt.Printf("NEO-GO-VM %d > ", c.vm.Context().IP()-1)
|
||||
} else {
|
||||
fmt.Print("NEO-GO-VM > ")
|
||||
}
|
||||
input, _ := reader.ReadString('\n')
|
||||
input = strings.Trim(input, "\n")
|
||||
if len(input) != 0 {
|
||||
parts := strings.Split(input, " ")
|
||||
cmd := parts[0]
|
||||
var args []string
|
||||
if len(parts) > 1 {
|
||||
args = parts[1:]
|
||||
}
|
||||
c.handleCommand(cmd, args...)
|
||||
}
|
||||
}
|
||||
c.shell.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func isMethodArg(s string) bool {
|
||||
|
@ -201,22 +327,38 @@ func isMethodArg(s string) bool {
|
|||
func parseArgs(args []string) ([]vm.StackItem, error) {
|
||||
items := make([]vm.StackItem, len(args))
|
||||
for i, arg := range args {
|
||||
var typ, value string
|
||||
typeAndVal := strings.Split(arg, ":")
|
||||
if len(typeAndVal) < 2 {
|
||||
return nil, errors.New("arguments need to be specified as <typ:val>")
|
||||
if typeAndVal[0] == boolFalse || typeAndVal[0] == boolTrue {
|
||||
typ = boolType
|
||||
} else if _, err := strconv.Atoi(typeAndVal[0]); err == nil {
|
||||
typ = intType
|
||||
} else {
|
||||
typ = stringType
|
||||
}
|
||||
value = typeAndVal[0]
|
||||
} else {
|
||||
typ = typeAndVal[0]
|
||||
value = typeAndVal[1]
|
||||
}
|
||||
|
||||
typ := typeAndVal[0]
|
||||
value := typeAndVal[1]
|
||||
|
||||
switch typ {
|
||||
case "int":
|
||||
case boolType:
|
||||
if value == boolFalse {
|
||||
items[i] = vm.NewBoolItem(false)
|
||||
} else if value == boolTrue {
|
||||
items[i] = vm.NewBoolItem(true)
|
||||
} else {
|
||||
return nil, errors.New("failed to parse bool parameter")
|
||||
}
|
||||
case intType:
|
||||
val, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items[i] = vm.NewBigIntegerItem(val)
|
||||
case "string":
|
||||
case stringType:
|
||||
items[i] = vm.NewByteArrayItem([]byte(value))
|
||||
}
|
||||
}
|
||||
|
@ -224,25 +366,6 @@ func parseArgs(args []string) ([]vm.StackItem, error) {
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func printHelp() {
|
||||
names := make([]string, len(commands))
|
||||
i := 0
|
||||
for name := range commands {
|
||||
names[i] = name
|
||||
i++
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||
fmt.Fprintln(w, "COMMAND\tUSAGE")
|
||||
for _, name := range names {
|
||||
fmt.Fprintf(w, "%s\t%s\n", name, commands[name].usage)
|
||||
}
|
||||
w.Flush()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func printLogo() {
|
||||
logo := `
|
||||
_ ____________ __________ _ ____ ___
|
||||
|
|
|
@ -49,7 +49,7 @@ func (c *Context) LenInstr() int {
|
|||
// CurrInstr returns the current instruction and opcode.
|
||||
func (c *Context) CurrInstr() (int, Instruction) {
|
||||
if c.ip < 0 {
|
||||
return c.ip, Instruction(0x00)
|
||||
return c.ip, NOP
|
||||
}
|
||||
return c.ip, Instruction(c.prog[c.ip])
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue