Skip to content

Commit 272bcb4

Browse files
authored
Merge pull request #3307 from jsternberg/dap-variables
dap: implement variable references
2 parents 266db93 + 1886e23 commit 272bcb4

File tree

5 files changed

+318
-53
lines changed

5 files changed

+318
-53
lines changed

dap/adapter.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ func (d *Adapter[C]) newThread(ctx Context, name string) (t *thread) {
216216
sourceMap: &d.sourceMap,
217217
breakpointMap: d.breakpointMap,
218218
idPool: d.idPool,
219+
variables: newVariableReferences(),
219220
}
220221
d.threads[t.id] = t
221222
d.nextThreadID++
@@ -240,6 +241,11 @@ func (d *Adapter[C]) getThread(id int) (t *thread) {
240241

241242
func (d *Adapter[C]) deleteThread(ctx Context, t *thread) {
242243
d.threadsMu.Lock()
244+
if t := d.threads[t.id]; t != nil {
245+
if t.variables != nil {
246+
t.variables.Reset()
247+
}
248+
}
243249
delete(d.threads, t.id)
244250
d.threadsMu.Unlock()
245251

@@ -252,6 +258,18 @@ func (d *Adapter[C]) deleteThread(ctx Context, t *thread) {
252258
}
253259
}
254260

261+
func (d *Adapter[T]) getThreadByFrameID(id int) (t *thread) {
262+
d.threadsMu.RLock()
263+
defer d.threadsMu.RUnlock()
264+
265+
for _, t := range d.threads {
266+
if t.hasFrame(id) {
267+
return t
268+
}
269+
}
270+
return nil
271+
}
272+
255273
type evaluateRequest struct {
256274
name string
257275
c gateway.Client
@@ -330,6 +348,35 @@ func (d *Adapter[C]) StackTrace(c Context, req *dap.StackTraceRequest, resp *dap
330348
return nil
331349
}
332350

351+
func (d *Adapter[C]) Scopes(c Context, req *dap.ScopesRequest, resp *dap.ScopesResponse) error {
352+
t := d.getThreadByFrameID(req.Arguments.FrameId)
353+
if t == nil {
354+
return errors.Errorf("no such frame id: %d", req.Arguments.FrameId)
355+
}
356+
357+
resp.Body.Scopes = t.Scopes(req.Arguments.FrameId)
358+
for i, s := range resp.Body.Scopes {
359+
resp.Body.Scopes[i].VariablesReference = (t.id << 24) | s.VariablesReference
360+
}
361+
return nil
362+
}
363+
364+
func (d *Adapter[C]) Variables(c Context, req *dap.VariablesRequest, resp *dap.VariablesResponse) error {
365+
tid := req.Arguments.VariablesReference >> 24
366+
367+
t := d.getThread(tid)
368+
if t == nil {
369+
return errors.Errorf("no such thread: %d", tid)
370+
}
371+
372+
varRef := req.Arguments.VariablesReference & ((1 << 24) - 1)
373+
resp.Body.Variables = t.Variables(varRef)
374+
for i, ref := range resp.Body.Variables {
375+
resp.Body.Variables[i].VariablesReference = (tid << 24) | ref.VariablesReference
376+
}
377+
return nil
378+
}
379+
333380
func (d *Adapter[C]) Source(c Context, req *dap.SourceRequest, resp *dap.SourceResponse) error {
334381
fname := req.Arguments.Source.Path
335382

@@ -378,6 +425,8 @@ func (d *Adapter[C]) dapHandler() Handler {
378425
Disconnect: d.Disconnect,
379426
Threads: d.Threads,
380427
StackTrace: d.StackTrace,
428+
Scopes: d.Scopes,
429+
Variables: d.Variables,
381430
Source: d.Source,
382431
}
383432
}

dap/handler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type Handler struct {
5555
Restart HandlerFunc[*dap.RestartRequest, *dap.RestartResponse]
5656
Threads HandlerFunc[*dap.ThreadsRequest, *dap.ThreadsResponse]
5757
StackTrace HandlerFunc[*dap.StackTraceRequest, *dap.StackTraceResponse]
58+
Scopes HandlerFunc[*dap.ScopesRequest, *dap.ScopesResponse]
59+
Variables HandlerFunc[*dap.VariablesRequest, *dap.VariablesResponse]
5860
Evaluate HandlerFunc[*dap.EvaluateRequest, *dap.EvaluateResponse]
5961
Source HandlerFunc[*dap.SourceRequest, *dap.SourceResponse]
6062
}

dap/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ func (s *Server) handleMessage(c Context, m dap.Message) (dap.ResponseMessage, e
125125
return s.h.Threads.Do(c, req)
126126
case *dap.StackTraceRequest:
127127
return s.h.StackTrace.Do(c, req)
128+
case *dap.ScopesRequest:
129+
return s.h.Scopes.Do(c, req)
130+
case *dap.VariablesRequest:
131+
return s.h.Variables.Do(c, req)
128132
case *dap.EvaluateRequest:
129133
return s.h.Evaluate.Do(c, req)
130134
case *dap.SourceRequest:

dap/thread.go

Lines changed: 50 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type thread struct {
2626
idPool *idPool
2727
sourceMap *sourceMap
2828
breakpointMap *breakpointMap
29+
variables *variableReferences
2930

3031
// Inputs to the evaluate call.
3132
c gateway.Client
@@ -48,11 +49,10 @@ type thread struct {
4849
mu sync.Mutex
4950

5051
// Attributes set when a thread is paused.
51-
rCtx *build.ResultHandle
52-
curPos digest.Digest
53-
54-
// Lazy attributes that are set when a thread is paused.
55-
stackTrace []dap.StackFrame
52+
rCtx *build.ResultHandle
53+
curPos digest.Digest
54+
stackTrace []int32
55+
frames map[int32]*frame
5656
}
5757

5858
type region struct {
@@ -154,6 +154,7 @@ func (t *thread) pause(c Context, err error, event dap.StoppedEventBody) <-chan
154154
}
155155
}
156156
}
157+
t.collectStackTrace()
157158

158159
event.ThreadId = t.id
159160
c.C() <- &dap.StoppedEvent{
@@ -178,18 +179,7 @@ func (t *thread) resume(step stepType) {
178179
if t.paused == nil {
179180
return
180181
}
181-
182-
if t.rCtx != nil {
183-
t.rCtx.Done()
184-
t.rCtx = nil
185-
}
186-
187-
if t.stackTrace != nil {
188-
for _, frame := range t.stackTrace {
189-
t.idPool.Put(int64(frame.Id))
190-
}
191-
t.stackTrace = nil
192-
}
182+
t.releaseState()
193183

194184
t.paused <- step
195185
close(t.paused)
@@ -207,10 +197,23 @@ func (t *thread) StackTrace() []dap.StackFrame {
207197
return []dap.StackFrame{}
208198
}
209199

210-
if t.stackTrace == nil {
211-
t.stackTrace = t.makeStackTrace()
200+
frames := make([]dap.StackFrame, len(t.stackTrace))
201+
for i, id := range t.stackTrace {
202+
frames[i] = t.frames[id].StackFrame
212203
}
213-
return t.stackTrace
204+
return frames
205+
}
206+
207+
func (t *thread) Scopes(frameID int) []dap.Scope {
208+
t.mu.Lock()
209+
defer t.mu.Unlock()
210+
211+
frame := t.frames[int32(frameID)]
212+
return frame.Scopes()
213+
}
214+
215+
func (t *thread) Variables(id int) []dap.Variable {
216+
return t.variables.Get(id)
214217
}
215218

216219
func (t *thread) getLLBState(ctx Context) error {
@@ -502,15 +505,16 @@ func (t *thread) solve(ctx context.Context, target digest.Digest) (gateway.Refer
502505
return res.SingleRef()
503506
}
504507

505-
func (t *thread) newStackFrame() dap.StackFrame {
506-
return dap.StackFrame{
507-
Id: int(t.idPool.Get()),
508+
func (t *thread) releaseState() {
509+
if t.rCtx != nil {
510+
t.rCtx.Done()
511+
t.rCtx = nil
508512
}
513+
t.stackTrace = nil
514+
t.frames = nil
509515
}
510516

511-
func (t *thread) makeStackTrace() []dap.StackFrame {
512-
var frames []dap.StackFrame
513-
517+
func (t *thread) collectStackTrace() {
514518
region := t.regionsByDigest[t.curPos]
515519
r := t.regions[region]
516520

@@ -519,45 +523,38 @@ func (t *thread) makeStackTrace() []dap.StackFrame {
519523
digests = digests[:index+1]
520524
}
521525

526+
t.frames = make(map[int32]*frame)
522527
for i := len(digests) - 1; i >= 0; i-- {
523528
dgst := digests[i]
524529

525-
frame := t.newStackFrame()
530+
frame := &frame{}
531+
frame.Id = int(t.idPool.Get())
532+
526533
if meta, ok := t.def.Metadata[dgst]; ok {
527-
fillStackFrameMetadata(&frame, meta)
534+
frame.setNameFromMeta(meta)
528535
}
529536
if loc, ok := t.def.Source.Locations[string(dgst)]; ok {
530-
t.fillStackFrameLocation(&frame, loc)
537+
frame.fillLocation(t.def, loc, t.sourcePath)
531538
}
532-
frames = append(frames, frame)
533-
}
534-
return frames
535-
}
536539

537-
func fillStackFrameMetadata(frame *dap.StackFrame, meta llb.OpMetadata) {
538-
if name, ok := meta.Description["llb.customname"]; ok {
539-
frame.Name = name
540-
} else if cmd, ok := meta.Description["com.docker.dockerfile.v1.command"]; ok {
541-
frame.Name = cmd
540+
if op := t.ops[dgst]; op != nil {
541+
frame.fillVarsFromOp(op, t.variables)
542+
}
543+
t.stackTrace = append(t.stackTrace, int32(frame.Id))
544+
t.frames[int32(frame.Id)] = frame
542545
}
543-
// TODO: should we infer the name from somewhere else?
544546
}
545547

546-
func (t *thread) fillStackFrameLocation(frame *dap.StackFrame, loc *pb.Locations) {
547-
for _, l := range loc.Locations {
548-
for _, r := range l.Ranges {
549-
frame.Line = int(r.Start.Line)
550-
frame.Column = int(r.Start.Character)
551-
frame.EndLine = int(r.End.Line)
552-
frame.EndColumn = int(r.End.Character)
553-
554-
info := t.def.Source.Infos[l.SourceIndex]
555-
frame.Source = &dap.Source{
556-
Path: filepath.Join(t.sourcePath, info.Filename),
557-
}
558-
return
559-
}
548+
func (t *thread) hasFrame(id int) bool {
549+
t.mu.Lock()
550+
defer t.mu.Unlock()
551+
552+
if t.paused == nil {
553+
return false
560554
}
555+
556+
_, ok := t.frames[int32(id)]
557+
return ok
561558
}
562559

563560
func pop[S ~[]E, E any](s *S) E {

0 commit comments

Comments
 (0)