diff --git a/cli/vm/cli.go b/cli/vm/cli.go index 21125ed67..431ed6f56 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -110,6 +110,26 @@ Example: > break 12`, Action: handleBreak, }, + { + Name: "delete", + Usage: "Remove a breakpoint", + UsageText: `delete `, + Description: ` is mandatory parameter. + +Example: +> delete 12`, + Action: handleRemoveBreak, + }, + { + Name: "ib", + Usage: "List breakpoints", + UsageText: `ib`, + Description: `List breakpoints. + +Example: +> ib`, + Action: handleListBreak, + }, { Name: "jump", Usage: "Jump to the specified instruction (absolute IP value)", @@ -597,6 +617,33 @@ func handleBreak(c *cli.Context) error { return nil } +func handleRemoveBreak(c *cli.Context) error { + if !checkVMIsReady(c.App) { + return nil + } + n, err := getInstructionParameter(c) + if err != nil { + return err + } + + v := getVMFromContext(c.App) + v.RemoveBreakPoint(n) + fmt.Fprintf(c.App.Writer, "breakpoint removed at instruction %d\n", n) + return nil +} + +func handleListBreak(c *cli.Context) error { + if !checkVMIsReady(c.App) { + return nil + } + + v := getVMFromContext(c.App) + for _, bp := range v.Context().BreakPoints() { + fmt.Fprintf(c.App.Writer, "%d\n", bp) + } + return nil +} + func handleJump(c *cli.Context) error { if !checkVMIsReady(c.App) { return nil diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 985b5510f..106a9e6a6 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -339,6 +339,31 @@ func TestRun_WithNewVMContextAndBreakpoints(t *testing.T) { e.checkNextLine(t, "at breakpoint 10 (ADD)*") e.checkStack(t, 13) }) + t.Run("contract breakpoints", func(t *testing.T) { + src := `package kek + func Main(a, b int) int { + var c = a + b + return c + 5 + }` + tmpDir := t.TempDir() + filename := prepareLoadgoSrc(t, tmpDir, src) + + e := newTestVMCLI(t) + e.runProgWithTimeout(t, 10*time.Second, + "loadgo "+filename, + "break 7", + "break 8", + "ib", + "delete 7", + ) + + e.checkNextLine(t, "READY: loaded \\d* instructions") + e.checkNextLine(t, "breakpoint added at instruction 7") + e.checkNextLine(t, "breakpoint added at instruction 8") + e.checkNextLine(t, "7") + e.checkNextLine(t, "8") + e.checkNextLine(t, "breakpoint removed at instruction 7") + }) } // prepareLoadgoSrc prepares provided SC source file for loading into VM via `loadgo` command. diff --git a/pkg/vm/debug_test.go b/pkg/vm/debug_test.go index 5441d5de6..afcf30a96 100644 --- a/pkg/vm/debug_test.go +++ b/pkg/vm/debug_test.go @@ -58,4 +58,11 @@ func TestContext_BreakPoints(t *testing.T) { // New context -> clean breakpoints. v.loadScriptWithCallingHash(prog, nil, nil, util.Uint160{}, util.Uint160{}, callflag.All, 1, 3, nil) require.Nil(t, v.Context().BreakPoints()) + + v.AddBreakPoint(3) + v.AddBreakPoint(3) + v.AddBreakPoint(5) + require.Equal(t, []int{3, 3, 5}, v.Context().BreakPoints()) + v.RemoveBreakPoint(3) + require.Equal(t, []int{5}, v.Context().BreakPoints()) } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 1608ebb36..49c19db2a 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -275,6 +275,14 @@ func (v *VM) AddBreakPoint(n int) { ctx.sc.breakPoints = append(ctx.sc.breakPoints, n) } +// RemoveBreakPoint removes the breakpoint in the current context. +func (v *VM) RemoveBreakPoint(n int) { + ctx := v.Context() + ctx.sc.breakPoints = slices.DeleteFunc(ctx.sc.breakPoints, func(i int) bool { + return i == n + }) +} + // AddBreakPointRel adds a breakpoint relative to the current // instruction pointer. func (v *VM) AddBreakPointRel(n int) {