diff --git a/gnovm/cmd/gno/repl.go b/gnovm/cmd/gno/repl.go index 0110b44b58a..8a6274eb6eb 100644 --- a/gnovm/cmd/gno/repl.go +++ b/gnovm/cmd/gno/repl.go @@ -74,6 +74,7 @@ func execRepl(cfg *replCfg, args []string) error { // gno> import "gno.land/p/demo/avl" // import the p/demo/avl package // gno> func a() string { return "a" } // declare a new function named a // gno> /src // print current generated source +// gno> /debug // activate the GnoVM debugger // gno> /editor // enter in multi-line mode, end with ';' // gno> /reset // remove all previously inserted code // gno> println(a()) // print the result of calling a() @@ -141,6 +142,8 @@ func handleInput(r *repl.Repl, input string) error { switch strings.TrimSpace(input) { case "/reset": r.Reset() + case "/debug": + r.Debug() case "/src": fmt.Fprintln(os.Stdout, r.Src()) case "/exit": diff --git a/gnovm/pkg/gnolang/debugger.go b/gnovm/pkg/gnolang/debugger.go index 284120ef6c5..839b6a691de 100644 --- a/gnovm/pkg/gnolang/debugger.go +++ b/gnovm/pkg/gnolang/debugger.go @@ -43,14 +43,29 @@ type Debugger struct { out io.Writer // debugger output, defaults to Stdout scanner *bufio.Scanner // to parse input per line - state DebugState // current state of debugger - lastCmd string // last debugger command - lastArg string // last debugger command arguments - loc Location // source location of the current machine instruction - prevLoc Location // source location of the previous machine instruction - breakpoints []Location // list of breakpoints set by user, as source locations - call []Location // for function tracking, ideally should be provided by machine frame - frameLevel int // frame level of the current machine instruction + state DebugState // current state of debugger + lastCmd string // last debugger command + lastArg string // last debugger command arguments + loc Location // source location of the current machine instruction + prevLoc Location // source location of the previous machine instruction + breakpoints []Location // list of breakpoints set by user, as source locations + call []Location // for function tracking, ideally should be provided by machine frame + frameLevel int // frame level of the current machine instruction + getSrc func(string) string // helper to access source from repl or others +} + +// Enable makes the debugger d active, using in as input reader, out as output writer and f as a source helper. +func (d *Debugger) Enable(in io.Reader, out io.Writer, f func(string) string) { + d.in = in + d.out = out + d.enabled = true + d.state = DebugAtInit + d.getSrc = f +} + +// Disable makes the debugger d inactive. +func (d *Debugger) Disable() { + d.enabled = false } type debugCommand struct { @@ -484,7 +499,13 @@ func debugList(m *Machine, arg string) (err error) { } src, err := fileContent(m.Store, loc.PkgPath, loc.File) if err != nil { - return err + // Use optional getSrc helper as fallback to get source. + if m.Debugger.getSrc != nil { + src = m.Debugger.getSrc(loc.File) + } + if src == "" { + return err + } } lines, offset := linesAround(src, loc.Line, 10) for i, l := range lines { diff --git a/gnovm/pkg/repl/repl.go b/gnovm/pkg/repl/repl.go index c7786cf08b0..e7b5ecea96f 100644 --- a/gnovm/pkg/repl/repl.go +++ b/gnovm/pkg/repl/repl.go @@ -10,6 +10,7 @@ import ( "go/printer" "go/token" "io" + "os" "text/template" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" @@ -106,6 +107,7 @@ type Repl struct { stdout io.Writer stderr io.Writer stdin io.Reader + debug bool } // NewRepl creates a Repl struct. It is able to process input source code and eventually run it. @@ -151,6 +153,14 @@ func (r *Repl) Process(input string) (out string, err error) { }() r.state.id++ + if r.debug { + r.state.machine.Debugger.Enable(os.Stdin, os.Stdout, func(file string) string { + return r.state.files[file] + }) + r.debug = false + defer r.state.machine.Debugger.Disable() + } + decl, declErr := r.parseDeclaration(input) if declErr == nil { return r.handleDeclarations(decl) @@ -317,3 +327,8 @@ func (r *Repl) Src() string { return b.String() } + +// Debug activates the GnoVM debugger for the next evaluation. +func (r *Repl) Debug() { + r.debug = true +}