mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 23:42:23 +00:00
vm: use new Context.Next() to properly dump programs
Fix #295, deduplicate code and add `inspect` parameter to the vm command to dump AVMs (`contract inspect` works with Go code).
This commit is contained in:
parent
53a3b18652
commit
dca332f333
3 changed files with 71 additions and 29 deletions
32
cli/vm/vm.go
32
cli/vm/vm.go
|
@ -1,6 +1,10 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
vmcli "github.com/CityOfZion/neo-go/pkg/vm/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -14,6 +18,19 @@ func NewCommand() cli.Command {
|
|||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d"},
|
||||
},
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "inspect",
|
||||
Usage: "dump instructions of the avm file given",
|
||||
Action: inspect,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "in, i",
|
||||
Usage: "input file of the program (AVM)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,3 +38,18 @@ func startVMPrompt(ctx *cli.Context) error {
|
|||
p := vmcli.New()
|
||||
return p.Run()
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
avm := ctx.String("in")
|
||||
if len(avm) == 0 {
|
||||
return cli.NewExitError(errors.New("no input file given"), 1)
|
||||
}
|
||||
b, err := ioutil.ReadFile(avm)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
v := vm.New(0)
|
||||
v.LoadScript(b)
|
||||
v.PrintOps()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
"golang.org/x/tools/go/loader"
|
||||
|
@ -108,25 +107,9 @@ func CompileAndInspect(src string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
||||
for i := 0; i <= len(b)-1; {
|
||||
instr := vm.Instruction(b[i])
|
||||
paramlength := 0
|
||||
fmt.Fprintf(w, "%d\t0x%x\t%s\t\n", i, b[i], instr)
|
||||
i++
|
||||
if instr >= vm.PUSHBYTES1 && instr <= vm.PUSHBYTES75 {
|
||||
paramlength = int(instr)
|
||||
}
|
||||
if instr == vm.JMP || instr == vm.JMPIF || instr == vm.JMPIFNOT || instr == vm.CALL {
|
||||
paramlength = 2
|
||||
}
|
||||
for x := 0; x < paramlength; x++ {
|
||||
fmt.Fprintf(w, "%d\t0x%x\t%s\t\n", i, b[i+1+x], string(b[i+1+x]))
|
||||
}
|
||||
i += paramlength
|
||||
}
|
||||
w.Flush()
|
||||
v := vm.New(0)
|
||||
v.LoadScript(b)
|
||||
v.PrintOps()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
45
pkg/vm/vm.go
45
pkg/vm/vm.go
|
@ -10,6 +10,7 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"text/tabwriter"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
|
@ -119,19 +120,45 @@ func (v *VM) LoadArgs(method []byte, args []StackItem) {
|
|||
|
||||
// PrintOps will print the opcodes of the current loaded program to stdout.
|
||||
func (v *VM) PrintOps() {
|
||||
prog := v.Context().Program()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
||||
cursor := ""
|
||||
ip, _ := v.Context().CurrInstr()
|
||||
for i := 0; i < len(prog); i++ {
|
||||
if i == ip {
|
||||
fmt.Fprintln(w, "INDEX\tOPCODE\tPARAMETER\t")
|
||||
realctx := v.Context()
|
||||
ctx := realctx.Copy()
|
||||
ctx.ip = 0
|
||||
ctx.nextip = 0
|
||||
for {
|
||||
cursor := ""
|
||||
instr, parameter, err := ctx.Next()
|
||||
if ctx.ip == realctx.ip {
|
||||
cursor = "<<"
|
||||
} else {
|
||||
cursor = ""
|
||||
}
|
||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Instruction(prog[i]).String(), cursor)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%d\t%s\tERROR: %s\t%s\n", ctx.ip, instr, err, cursor)
|
||||
break
|
||||
}
|
||||
var desc = ""
|
||||
if parameter != nil {
|
||||
switch instr {
|
||||
case JMP, JMPIF, JMPIFNOT, CALL:
|
||||
offset := int16(binary.LittleEndian.Uint16(parameter))
|
||||
desc = fmt.Sprintf("%d (%d/%x)", ctx.ip+int(offset), offset, parameter)
|
||||
case SYSCALL:
|
||||
desc = fmt.Sprintf("%q", parameter)
|
||||
case APPCALL, TAILCALL:
|
||||
desc = fmt.Sprintf("%x", parameter)
|
||||
default:
|
||||
if utf8.Valid(parameter) {
|
||||
desc = fmt.Sprintf("%x (%q)", parameter, parameter)
|
||||
} else {
|
||||
desc = fmt.Sprintf("%x", parameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%s\n", ctx.ip, instr, desc, cursor)
|
||||
if ctx.nextip >= len(ctx.prog) {
|
||||
break
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue