debug

package
v1.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package debug is the embedder-facing debugger: breakpoints, stepping, and the break-prompt command surface a REPL renders.

It sits between the VM and its consumers. Debugger wraps machine.Debugger so github.com/aalpar/wile/pkg/wile.Engine never exposes VM types, and DebugContext binds that same debugger to the comma-prefixed commands pkg/repl dispatches. Both live here so pkg/wile and pkg/repl carry no debugger-specific code beyond attaching one.

The break state a consumer reads is a snapshot frozen at the stop, not the live VM context: the context is pool-recycled and zeroed once the evaluation ends.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CanonicalDebugCommand

func CanonicalDebugCommand(token string) (string, bool)

CanonicalDebugCommand maps a typed token (comma already stripped) to the canonical debug command name, so alias handling lives in one table and the break prompt cannot drift from ,help.

Types

type BreakAction

type BreakAction int

BreakAction is a suspension handler's verdict: what the VM should do with the computation it stopped. It is returned by the callback registered with Debugger.OnBreakSuspend, which runs while the VM is suspended.

const (
	// BreakContinue resumes the suspended computation with stepping off.
	BreakContinue BreakAction = iota
	// BreakStep resumes and stops at the next source line, entering calls.
	BreakStep
	// BreakNext resumes and stops at the next source line in the same or a
	// shallower frame, running nested calls to completion.
	BreakNext
	// BreakFinish resumes and stops once execution reaches a strictly
	// shallower frame than the one that was suspended.
	BreakFinish
	// BreakAbandon does not resume: the computation is discarded and the
	// evaluation returns void. dynamic-wind after-thunks between the break
	// point and the top level still run.
	BreakAbandon
)

type BreakpointInfo

type BreakpointInfo struct {
	ID       int
	File     string
	Line     int
	Column   int
	Enabled  bool
	HitCount int
}

BreakpointInfo holds read-only breakpoint state for display.

type CommandMeta

type CommandMeta struct {
	Name    string
	Aliases []string
	Summary string
	Detail  string
}

CommandMeta is the static half of a debug command: everything but the bound handler. Separated from DebugCommandInfo so a help renderer can list the commands without allocating a DebugContext (and its Debugger).

func Commands

func Commands() []CommandMeta

Commands returns the debug-command catalog with no handlers bound. Bind them by taking DebugContext.DebugCommands instead.

type DebugCommandInfo

type DebugCommandInfo struct {
	Name    string
	Aliases []string
	Summary string
	Detail  string
	Handler func(args []string, out io.Writer)
}

DebugCommandInfo describes a single debug command: its canonical name, aliases, summary text, detail text, and handler function.

type DebugContext

type DebugContext struct {
	// contains filtered or unexported fields
}

DebugContext holds the state for debug commands. The break state itself — a snapshot frozen at the most recent break, NOT a live VM context — is owned by the wrapped Debugger and read through p.debugger.CurrentState(). DebugContext deliberately keeps no parallel copy, so the two can never disagree. Handing back the live context instead is what made ,where and ,backtrace report "No source location available": it is pool-recycled and zeroed the moment the evaluation ends.

func NewDebugContext

func NewDebugContext() *DebugContext

NewDebugContext creates a new debug context.

func (*DebugContext) DebugCommands

func (p *DebugContext) DebugCommands() []DebugCommandInfo

DebugCommands returns the canonical list of debug commands with bound handlers.

func (*DebugContext) Debugger

func (p *DebugContext) Debugger() *Debugger

Debugger returns the debugger instance.

func (*DebugContext) HandleDebugCommand

func (p *DebugContext) HandleDebugCommand(line string, out io.Writer) bool

HandleDebugCommand processes a debug command starting with ','. Returns true if a command was handled, false otherwise.

type Debugger

type Debugger struct {
	// contains filtered or unexported fields
}

Debugger controls breakpoints and stepping for an Engine. It wraps the internal machine.Debugger to avoid exposing VM types.

func NewDebugger

func NewDebugger() *Debugger

NewDebugger creates a new Debugger.

func (*Debugger) Attach

func (p *Debugger) Attach(mc *machine.MachineContext, closureEnv *environment.EnvironmentFrame)

Attach installs this debugger on mc for the run about to start, and arms the break boundary when — and only when — a suspension handler is registered AND the debugger has something that could stop this run. Otherwise a break falls back to the render-only callback and no prompt frame is pushed.

Both conditions are load-bearing, because the boundary is not free: it is a real continuation frame, so it costs one unit of the call-depth budget for the whole run and changes the tail-call shape at the top level. The REPL registers a suspension handler for its entire session, so without the second condition every evaluation typed at the prompt paid for a debugger nobody had armed.

closureEnv is the frame the break handler closes over — the caller's mutable runtime environment, or its compilation environment when there is none.

func (*Debugger) Breakpoints

func (p *Debugger) Breakpoints() []BreakpointInfo

Breakpoints returns all breakpoints.

func (*Debugger) Continue

func (p *Debugger) Continue()

Continue resumes execution.

func (*Debugger) CurrentState

func (p *Debugger) CurrentState() values.DebugState

CurrentState returns the DebugState from the most recent break, or nil if no break has occurred.

It is a snapshot taken at the break, not the live VM context. The context is pool-recycled and zeroed when the evaluation ends, so handing it back was how ,where and ,backtrace came to report "No source location available" and "Empty stack trace" on a breakpoint that had just printed its location.

func (*Debugger) DisableBreakpoint

func (p *Debugger) DisableBreakpoint(id int) bool

DisableBreakpoint disables a breakpoint by ID.

func (*Debugger) EnableBreakpoint

func (p *Debugger) EnableBreakpoint(id int) bool

EnableBreakpoint enables a breakpoint by ID.

func (*Debugger) IsStepping

func (p *Debugger) IsStepping() bool

IsStepping returns true if the debugger is in step mode.

func (*Debugger) OnBreak

func (p *Debugger) OnBreak(fn func(state values.DebugState, bp *BreakpointInfo))

OnBreak sets the render-only callback for a breakpoint hit or a completed step. The DebugState provides source location and stack trace access without exposing VM internals.

It is the fallback, not the general notification: a stop that SUSPENDS invokes Debugger.OnBreakSuspend instead and does not invoke this. With a suspension handler registered, the stops that still reach here are the ones that cannot suspend — breaks inside load and eval, and any run the boundary was not armed for.

func (*Debugger) OnBreakSuspend

func (p *Debugger) OnBreakSuspend(fn func(state values.DebugState, bp *BreakpointInfo) BreakAction)

OnBreakSuspend installs the handler that runs while the VM is SUSPENDED at a breakpoint or step stop, and whose verdict decides what happens next.

nil means NONE: with no suspend handler the VM does not suspend at all and the render-only Debugger.OnBreak callback is invoked inline instead, which is the behaviour an embedder with a Go-only debugger has always had.

Breaks inside load and eval do not suspend. Their freshly compiled template runs on a sub-context whose own continuation chain does not carry the break prompt, so those stops fall back to OnBreak.

func (*Debugger) RemoveBreakpoint

func (p *Debugger) RemoveBreakpoint(id int) bool

RemoveBreakpoint removes a breakpoint by ID.

func (*Debugger) SetBreakpoint

func (p *Debugger) SetBreakpoint(file string, line, col int) int

SetBreakpoint adds a breakpoint at the given source location. Returns the breakpoint ID.

func (*Debugger) StepInto

func (p *Debugger) StepInto()

StepInto enables step-into mode.

func (*Debugger) StepOut

func (p *Debugger) StepOut()

StepOut enables step-out mode relative to the most recent break. It is a no-op when no break has happened.

func (*Debugger) StepOver

func (p *Debugger) StepOver()

StepOver enables step-over mode relative to the most recent break. It is a no-op when no break has happened, because there is no depth to step over.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL