11package dap
22
33import (
4+ "bytes"
45 "context"
56 "encoding/json"
67 "fmt"
78 "io"
9+ "path"
810 "sync"
11+ "sync/atomic"
912
1013 "github.com/docker/buildx/build"
1114 "github.com/google/go-dap"
@@ -28,6 +31,9 @@ type Adapter[T any] struct {
2831 threads map [int ]* thread
2932 threadsMu sync.RWMutex
3033 nextThreadID int
34+
35+ sourceMap sourceMap
36+ idPool * idPool
3137}
3238
3339func New [T any ](cfg * build.InvokeConfig ) * Adapter [T ] {
@@ -38,6 +44,7 @@ func New[T any](cfg *build.InvokeConfig) *Adapter[T] {
3844 evaluateReqCh : make (chan * evaluateRequest ),
3945 threads : make (map [int ]* thread ),
4046 nextThreadID : 1 ,
47+ idPool : new (idPool ),
4148 }
4249 if cfg != nil {
4350 d .cfg = * cfg
@@ -131,7 +138,16 @@ func (d *Adapter[T]) Continue(c Context, req *dap.ContinueRequest, resp *dap.Con
131138 t := d .threads [req .Arguments .ThreadId ]
132139 d .threadsMu .RUnlock ()
133140
134- t .Resume (c )
141+ t .Continue ()
142+ return nil
143+ }
144+
145+ func (d * Adapter [T ]) Next (c Context , req * dap.NextRequest , resp * dap.NextResponse ) error {
146+ d .threadsMu .RLock ()
147+ t := d .threads [req .Arguments .ThreadId ]
148+ d .threadsMu .RUnlock ()
149+
150+ t .Next ()
135151 return nil
136152}
137153
@@ -182,7 +198,7 @@ func (d *Adapter[T]) launch(c Context) {
182198 started := c .Go (func (c Context ) {
183199 defer d .deleteThread (c , t )
184200 defer close (req .errCh )
185- req .errCh <- t .Evaluate (c , req .c , req .ref , req .meta , d . cfg )
201+ req .errCh <- t .Evaluate (c , req .c , req .ref , req .meta , req . inputs )
186202 })
187203
188204 if ! started {
@@ -197,8 +213,10 @@ func (d *Adapter[T]) newThread(ctx Context, name string) (t *thread) {
197213 d .threadsMu .Lock ()
198214 id := d .nextThreadID
199215 t = & thread {
200- id : id ,
201- name : name ,
216+ id : id ,
217+ name : name ,
218+ sourceMap : & d .sourceMap ,
219+ idPool : d .idPool ,
202220 }
203221 d .threads [t .id ] = t
204222 d .nextThreadID ++
@@ -236,41 +254,43 @@ func (d *Adapter[T]) deleteThread(ctx Context, t *thread) {
236254}
237255
238256type evaluateRequest struct {
239- name string
240- c gateway.Client
241- ref gateway.Reference
242- meta map [string ][]byte
243- errCh chan <- error
257+ name string
258+ c gateway.Client
259+ ref gateway.Reference
260+ meta map [string ][]byte
261+ inputs build.Inputs
262+ errCh chan <- error
244263}
245264
246- func (d * Adapter [T ]) EvaluateResult (ctx context.Context , name string , c gateway.Client , res * gateway.Result ) error {
265+ func (d * Adapter [T ]) EvaluateResult (ctx context.Context , name string , c gateway.Client , res * gateway.Result , inputs build. Inputs ) error {
247266 eg , _ := errgroup .WithContext (ctx )
248267 if res .Ref != nil {
249268 eg .Go (func () error {
250- return d .evaluateRef (ctx , name , c , res .Ref , res .Metadata )
269+ return d .evaluateRef (ctx , name , c , res .Ref , res .Metadata , inputs )
251270 })
252271 }
253272
254273 for k , ref := range res .Refs {
255274 refName := fmt .Sprintf ("%s (%s)" , name , k )
256275 eg .Go (func () error {
257- return d .evaluateRef (ctx , refName , c , ref , res .Metadata )
276+ return d .evaluateRef (ctx , refName , c , ref , res .Metadata , inputs )
258277 })
259278 }
260279 return eg .Wait ()
261280}
262281
263- func (d * Adapter [T ]) evaluateRef (ctx context.Context , name string , c gateway.Client , ref gateway.Reference , meta map [string ][]byte ) error {
282+ func (d * Adapter [T ]) evaluateRef (ctx context.Context , name string , c gateway.Client , ref gateway.Reference , meta map [string ][]byte , inputs build. Inputs ) error {
264283 errCh := make (chan error , 1 )
265284
266285 // Send a solve request to the launch routine
267286 // which will perform the solve in the context of the server.
268287 ereq := & evaluateRequest {
269- name : name ,
270- c : c ,
271- ref : ref ,
272- meta : meta ,
273- errCh : errCh ,
288+ name : name ,
289+ c : c ,
290+ ref : ref ,
291+ meta : meta ,
292+ inputs : inputs ,
293+ errCh : errCh ,
274294 }
275295 select {
276296 case d .evaluateReqCh <- ereq :
@@ -307,16 +327,28 @@ func (d *Adapter[T]) StackTrace(c Context, req *dap.StackTraceRequest, resp *dap
307327 return errors .Errorf ("no such thread: %d" , req .Arguments .ThreadId )
308328 }
309329
310- resp .Body .StackFrames = t .StackFrames ()
330+ resp .Body .StackFrames = t .StackTrace ()
331+ return nil
332+ }
333+
334+ func (d * Adapter [T ]) Source (c Context , req * dap.SourceRequest , resp * dap.SourceResponse ) error {
335+ fname := req .Arguments .Source .Path
336+
337+ dt , ok := d .sourceMap .Get (fname )
338+ if ! ok {
339+ return errors .Errorf ("file not found: %s" , fname )
340+ }
341+
342+ resp .Body .Content = string (dt )
311343 return nil
312344}
313345
314- func (d * Adapter [T ]) evaluate (ctx context.Context , name string , c gateway.Client , res * gateway.Result ) error {
346+ func (d * Adapter [T ]) evaluate (ctx context.Context , name string , c gateway.Client , res * gateway.Result , opt build. Options ) error {
315347 errCh := make (chan error , 1 )
316348
317349 started := d .srv .Go (func (ctx Context ) {
318350 defer close (errCh )
319- errCh <- d .EvaluateResult (ctx , name , c , res )
351+ errCh <- d .EvaluateResult (ctx , name , c , res , opt . Inputs )
320352 })
321353 if ! started {
322354 return context .Canceled
@@ -341,93 +373,16 @@ func (d *Adapter[T]) dapHandler() Handler {
341373 Initialize : d .Initialize ,
342374 Launch : d .Launch ,
343375 Continue : d .Continue ,
376+ Next : d .Next ,
344377 SetBreakpoints : d .SetBreakpoints ,
345378 ConfigurationDone : d .ConfigurationDone ,
346379 Disconnect : d .Disconnect ,
347380 Threads : d .Threads ,
348381 StackTrace : d .StackTrace ,
382+ Source : d .Source ,
349383 }
350384}
351385
352- type thread struct {
353- id int
354- name string
355-
356- paused chan struct {}
357- rCtx * build.ResultHandle
358- mu sync.Mutex
359- }
360-
361- func (t * thread ) Evaluate (ctx Context , c gateway.Client , ref gateway.Reference , meta map [string ][]byte , cfg build.InvokeConfig ) error {
362- err := ref .Evaluate (ctx )
363- if reason , desc := t .needsDebug (cfg , err ); reason != "" {
364- rCtx := build .NewResultHandle (ctx , c , ref , meta , err )
365-
366- select {
367- case <- t .pause (ctx , rCtx , reason , desc ):
368- case <- ctx .Done ():
369- t .Resume (ctx )
370- return context .Cause (ctx )
371- }
372- }
373- return err
374- }
375-
376- func (t * thread ) needsDebug (cfg build.InvokeConfig , err error ) (reason , desc string ) {
377- if ! cfg .NeedsDebug (err ) {
378- return
379- }
380-
381- if err != nil {
382- reason = "exception"
383- desc = "Encountered an error during result evaluation"
384- } else {
385- reason = "pause"
386- desc = "Result evaluation completed"
387- }
388- return
389- }
390-
391- func (t * thread ) pause (c Context , rCtx * build.ResultHandle , reason , desc string ) <- chan struct {} {
392- if t .paused == nil {
393- t .paused = make (chan struct {})
394- }
395- t .rCtx = rCtx
396-
397- c .C () <- & dap.StoppedEvent {
398- Event : dap.Event {Event : "stopped" },
399- Body : dap.StoppedEventBody {
400- Reason : reason ,
401- Description : desc ,
402- ThreadId : t .id ,
403- },
404- }
405- return t .paused
406- }
407-
408- func (t * thread ) Resume (c Context ) {
409- t .mu .Lock ()
410- defer t .mu .Unlock ()
411-
412- if t .paused == nil {
413- return
414- }
415-
416- if t .rCtx != nil {
417- t .rCtx .Done ()
418- t .rCtx = nil
419- }
420-
421- close (t .paused )
422- t .paused = nil
423- }
424-
425- // TODO: return a suitable stack frame for the thread.
426- // For now, just returns nothing.
427- func (t * thread ) StackFrames () []dap.StackFrame {
428- return []dap.StackFrame {}
429- }
430-
431386func (d * Adapter [T ]) Out () io.Writer {
432387 return & adapterWriter [T ]{d }
433388}
@@ -454,3 +409,63 @@ func (d *adapterWriter[T]) Write(p []byte) (n int, err error) {
454409 }
455410 return n , nil
456411}
412+
413+ type idPool struct {
414+ next atomic.Int64
415+ }
416+
417+ func (p * idPool ) Get () int64 {
418+ return p .next .Add (1 )
419+ }
420+
421+ func (p * idPool ) Put (x int64 ) {
422+ // noop
423+ }
424+
425+ type sourceMap struct {
426+ m sync.Map
427+ }
428+
429+ func (s * sourceMap ) Put (c Context , fname string , dt []byte ) {
430+ for {
431+ old , loaded := s .m .LoadOrStore (fname , dt )
432+ if ! loaded {
433+ c .C () <- & dap.LoadedSourceEvent {
434+ Event : dap.Event {Event : "loadedSource" },
435+ Body : dap.LoadedSourceEventBody {
436+ Reason : "new" ,
437+ Source : dap.Source {
438+ Name : path .Base (fname ),
439+ Path : fname ,
440+ },
441+ },
442+ }
443+ }
444+
445+ if bytes .Equal (old .([]byte ), dt ) {
446+ // Nothing to do.
447+ return
448+ }
449+
450+ if s .m .CompareAndSwap (fname , old , dt ) {
451+ c .C () <- & dap.LoadedSourceEvent {
452+ Event : dap.Event {Event : "loadedSource" },
453+ Body : dap.LoadedSourceEventBody {
454+ Reason : "changed" ,
455+ Source : dap.Source {
456+ Name : path .Base (fname ),
457+ Path : fname ,
458+ },
459+ },
460+ }
461+ }
462+ }
463+ }
464+
465+ func (s * sourceMap ) Get (fname string ) ([]byte , bool ) {
466+ v , ok := s .m .Load (fname )
467+ if ! ok {
468+ return nil , false
469+ }
470+ return v .([]byte ), true
471+ }
0 commit comments