| 28 | }; |
| 29 | |
| 30 | class DapBackend implements DebuggerBackend { |
| 31 | readonly kind = 'dap' as const; |
| 32 | |
| 33 | private readonly executor: CommandExecutor; |
| 34 | private readonly spawner: InteractiveSpawner; |
| 35 | private readonly requestTimeoutMs: number; |
| 36 | private readonly logEvents: boolean; |
| 37 | |
| 38 | private transport: DapTransport | null = null; |
| 39 | private unsubscribeEvents: (() => void) | null = null; |
| 40 | private attached = false; |
| 41 | private disposed = false; |
| 42 | private queue: Promise<unknown> = Promise.resolve(); |
| 43 | |
| 44 | private lastStoppedThreadId: number | null = null; |
| 45 | private executionState: DebugExecutionState = { status: 'unknown' }; |
| 46 | private breakpointsById = new Map<number, BreakpointRecord>(); |
| 47 | private fileLineBreakpointsByFile = new Map<string, FileLineBreakpointRecord[]>(); |
| 48 | private functionBreakpoints: FunctionBreakpointRecord[] = []; |
| 49 | private nextSyntheticId = -1; |
| 50 | |
| 51 | constructor(opts: { |
| 52 | executor: CommandExecutor; |
| 53 | spawner: InteractiveSpawner; |
| 54 | requestTimeoutMs: number; |
| 55 | logEvents: boolean; |
| 56 | }) { |
| 57 | this.executor = opts.executor; |
| 58 | this.spawner = opts.spawner; |
| 59 | this.requestTimeoutMs = opts.requestTimeoutMs; |
| 60 | this.logEvents = opts.logEvents; |
| 61 | } |
| 62 | |
| 63 | async attach(opts: { pid: number; simulatorId: string; waitFor?: boolean }): Promise<void> { |
| 64 | void opts.simulatorId; |
| 65 | return this.enqueue(async () => { |
| 66 | if (this.disposed) { |
| 67 | throw new Error('DAP backend disposed'); |
| 68 | } |
| 69 | if (this.attached) { |
| 70 | throw new Error('DAP backend already attached'); |
| 71 | } |
| 72 | |
| 73 | const adapterCommand = await resolveLldbDapCommand({ executor: this.executor }); |
| 74 | const transport = new DapTransport({ |
| 75 | spawner: this.spawner, |
| 76 | adapterCommand, |
| 77 | logPrefix: LOG_PREFIX, |
| 78 | }); |
| 79 | this.transport = transport; |
| 80 | this.unsubscribeEvents = transport.onEvent((event) => this.handleEvent(event)); |
| 81 | |
| 82 | try { |
| 83 | const init = await this.request< |
| 84 | { |
| 85 | clientID: string; |
| 86 | clientName: string; |
| 87 | adapterID: string; |