-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathprotocol.go
More file actions
247 lines (213 loc) · 7.89 KB
/
protocol.go
File metadata and controls
247 lines (213 loc) · 7.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package dap
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
)
// Breakpoint represents a source breakpoint with optional condition.
type Breakpoint struct {
File string `json:"file"`
Line int `json:"line"`
Condition string `json:"condition,omitempty"`
}
// LocationKey returns "file:line" — the identity key for merging/removing.
func (b Breakpoint) LocationKey() string {
return fmt.Sprintf("%s:%d", b.File, b.Line)
}
// String returns a human-readable representation.
func (b Breakpoint) String() string {
if b.Condition != "" {
return fmt.Sprintf("%s:%d (when %s)", b.File, b.Line, b.Condition)
}
return fmt.Sprintf("%s:%d", b.File, b.Line)
}
// Request is sent from CLI to daemon over Unix socket.
type Request struct {
Command string `json:"command"`
Args json.RawMessage `json:"args,omitempty"`
}
// Response is sent from daemon to CLI over Unix socket.
type Response struct {
Status string `json:"status"` // "stopped", "terminated", "error", "ok"
Error string `json:"error,omitempty"`
Data *ContextResult `json:"data,omitempty"`
}
// ExceptionInfo holds details about an exception that caused a stop.
type ExceptionInfo struct {
ExceptionID string `json:"exception_id"`
Description string `json:"description,omitempty"`
Details string `json:"details,omitempty"`
}
// ContextResult holds the auto-context returned by execution commands.
type ContextResult struct {
Reason string `json:"reason,omitempty"`
Location *Location `json:"location,omitempty"`
Source []SourceLine `json:"source,omitempty"`
Locals []Variable `json:"locals,omitempty"`
Stack []StackFrame `json:"stack,omitempty"`
Output string `json:"output,omitempty"`
ExitCode *int `json:"exit_code,omitempty"`
EvalResult *EvalResult `json:"eval_result,omitempty"`
ExceptionInfo *ExceptionInfo `json:"exception_info,omitempty"`
InspectResult *InspectResult `json:"inspect_result,omitempty"`
// Warnings from unverified breakpoints (drained on each response)
Warnings []string `json:"warnings,omitempty"`
// Thread list results
Threads []ThreadInfo `json:"threads,omitempty"`
IsThreadList bool `json:"is_thread_list,omitempty"`
// Break list results
Breakpoints []Breakpoint `json:"breakpoints,omitempty"`
ExceptionFilters []string `json:"exception_filters,omitempty"`
IsBreakList bool `json:"is_break_list,omitempty"`
}
// Location identifies a position in source code.
type Location struct {
File string `json:"file"`
Line int `json:"line"`
Function string `json:"function"`
}
// SourceLine is a single line of source code.
type SourceLine struct {
Line int `json:"line"`
Text string `json:"text"`
Current bool `json:"current,omitempty"`
}
// Variable represents a local variable.
type Variable struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
Value string `json:"value"`
Len int `json:"len,omitempty"`
}
// StackFrame represents a frame in the call stack.
type StackFrame struct {
Frame int `json:"frame"`
Function string `json:"function"`
File string `json:"file,omitempty"`
Line int `json:"line,omitempty"`
}
// EvalResult holds the result of an eval command.
type EvalResult struct {
Value string `json:"value"`
Type string `json:"type,omitempty"`
}
// --- IPC command args ---
// BreakpointUpdates holds breakpoint modifications shared across commands.
type BreakpointUpdates struct {
Breaks []Breakpoint `json:"breaks,omitempty"` // breakpoints to add (additive)
RemoveBreaks []Breakpoint `json:"remove_breaks,omitempty"` // breakpoints to remove
ExceptionFilters []string `json:"exception_filters,omitempty"` // backend-specific filter IDs (additive, merged with existing)
}
// DebugArgs are arguments for the "debug" command.
type DebugArgs struct {
Script string `json:"script"`
Backend string `json:"backend,omitempty"`
Breaks []Breakpoint `json:"breaks,omitempty"`
StopOnEntry bool `json:"stop_on_entry,omitempty"`
Attach string `json:"attach,omitempty"` // "host:port" for remote
PID int `json:"pid,omitempty"` // PID for local attach
ProgramArgs []string `json:"program_args,omitempty"`
ExceptionFilters []string `json:"exception_filters,omitempty"` // backend-specific filter IDs
ContextLines int `json:"context_lines,omitempty"`
Python string `json:"python,omitempty"` // debugpy only: interpreter path; resolved from $VIRTUAL_ENV if empty
}
// StepArgs are arguments for the "step" command.
type StepArgs struct {
Mode string `json:"mode"` // "over", "in", "out"
ContextLines int `json:"context_lines,omitempty"`
BreakpointUpdates
}
// PauseArgs are arguments for the "pause" command.
type PauseArgs struct {
BreakpointUpdates
}
// ContinueArgs are arguments for the "continue" command.
type ContinueArgs struct {
ContinueTo *Breakpoint `json:"continue_to,omitempty"`
ContextLines int `json:"context_lines,omitempty"`
BreakpointUpdates
}
// EvalArgs are arguments for the "eval" command.
type EvalArgs struct {
Expression string `json:"expression"`
Frame int `json:"frame,omitempty"`
BreakpointUpdates
}
// ContextArgs are arguments for the "context" command.
type ContextArgs struct {
Frame int `json:"frame,omitempty"`
ContextLines int `json:"context_lines,omitempty"`
BreakpointUpdates
}
// OutputArgs are arguments for the "output" command.
type OutputArgs struct {
BreakpointUpdates
}
// ThreadInfo represents a thread in the debugged program.
type ThreadInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Current bool `json:"current,omitempty"`
}
// ThreadArgs are arguments for the "thread" command.
type ThreadArgs struct {
ThreadID int `json:"thread_id"`
ContextLines int `json:"context_lines,omitempty"`
}
// InspectArgs are arguments for the "inspect" command.
type InspectArgs struct {
Variable string `json:"variable"`
Depth int `json:"depth,omitempty"` // default 1, max 5
Frame int `json:"frame,omitempty"`
}
// InspectResult holds the result of an inspect command.
type InspectResult struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
Value string `json:"value"`
Children []InspectResult `json:"children,omitempty"`
}
// BreakAddArgs are arguments for the "break_add" command.
type BreakAddArgs struct {
Breaks []Breakpoint `json:"breaks,omitempty"`
ExceptionFilters []string `json:"exception_filters,omitempty"`
}
// BreakRemoveArgs are arguments for the "break_remove" command.
type BreakRemoveArgs struct {
Breaks []Breakpoint `json:"breaks,omitempty"`
ExceptionFilters []string `json:"exception_filters,omitempty"`
}
// --- Length-prefixed JSON IPC ---
// WriteIPC writes a length-prefixed JSON message to w.
func WriteIPC(w io.Writer, msg any) error {
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("marshal: %w", err)
}
if err := binary.Write(w, binary.LittleEndian, uint32(len(data))); err != nil {
return fmt.Errorf("write length: %w", err)
}
if _, err := w.Write(data); err != nil {
return fmt.Errorf("write data: %w", err)
}
return nil
}
// ReadIPC reads a length-prefixed JSON message from r into msg.
func ReadIPC(r io.Reader, msg any) error {
var length uint32
if err := binary.Read(r, binary.LittleEndian, &length); err != nil {
return fmt.Errorf("read length: %w", err)
}
if length > 10*1024*1024 { // 10MB sanity limit
return fmt.Errorf("message too large: %d bytes", length)
}
data := make([]byte, length)
if _, err := io.ReadFull(r, data); err != nil {
return fmt.Errorf("read data: %w", err)
}
if err := json.Unmarshal(data, msg); err != nil {
return fmt.Errorf("unmarshal: %w", err)
}
return nil
}