@@ -16,17 +16,28 @@ import { IExtraProperty, IScopeRef, IVariableContainer } from './variableStore';
16
16
17
17
const localize = nls . loadMessageBundle ( ) ;
18
18
19
+ export interface IFrameElement {
20
+ /** Formats the stack element as V8 would format it */
21
+ formatAsNative ( ) : Promise < string > ;
22
+ /** Pretty formats the stack element as text */
23
+ format ( ) : Promise < string > ;
24
+ /** Formats the element for DAP */
25
+ toDap ( format ?: Dap . StackFrameFormat ) : Promise < Dap . StackFrame > ;
26
+ }
27
+
28
+ type FrameElement = StackFrame | AsyncSeparator ;
29
+
19
30
export class StackTrace {
20
- public readonly frames : StackFrame [ ] = [ ] ;
31
+ public readonly frames : FrameElement [ ] = [ ] ;
21
32
private _frameById : Map < number , StackFrame > = new Map ( ) ;
22
33
private _asyncStackTraceId ?: Cdp . Runtime . StackTraceId ;
23
34
private _lastFrameThread ?: Thread ;
24
35
25
36
public static fromRuntime ( thread : Thread , stack : Cdp . Runtime . StackTrace ) : StackTrace {
26
37
const result = new StackTrace ( thread ) ;
27
- for ( let frameNo = 0 ; frameNo < stack . callFrames . length ; frameNo ++ ) {
28
- if ( ! stack . callFrames [ frameNo ] . url . endsWith ( SourceConstants . InternalExtension ) ) {
29
- result . frames . push ( StackFrame . fromRuntime ( thread , stack . callFrames [ frameNo ] , false ) ) ;
38
+ for ( const frame of stack . callFrames ) {
39
+ if ( ! frame . url . endsWith ( SourceConstants . InternalExtension ) ) {
40
+ result . frames . push ( StackFrame . fromRuntime ( thread , frame , false ) ) ;
30
41
}
31
42
}
32
43
@@ -88,7 +99,7 @@ export class StackTrace {
88
99
this . _lastFrameThread = thread ;
89
100
}
90
101
91
- async loadFrames ( limit : number ) : Promise < StackFrame [ ] > {
102
+ async loadFrames ( limit : number ) : Promise < FrameElement [ ] > {
92
103
while ( this . frames . length < limit && this . _asyncStackTraceId ) {
93
104
if ( this . _asyncStackTraceId . debuggerId )
94
105
this . _lastFrameThread = Thread . threadForDebuggerId ( this . _asyncStackTraceId . debuggerId ) ;
@@ -117,9 +128,10 @@ export class StackTrace {
117
128
stackTrace . callFrames . shift ( ) ;
118
129
119
130
if ( stackTrace . callFrames . length ) {
120
- this . _appendFrame ( StackFrame . asyncSeparator ( thread , stackTrace . description || 'async' ) ) ;
121
- for ( const callFrame of stackTrace . callFrames )
131
+ this . _appendFrame ( new AsyncSeparator ( stackTrace . description || 'async' ) ) ;
132
+ for ( const callFrame of stackTrace . callFrames ) {
122
133
this . _appendFrame ( StackFrame . fromRuntime ( thread , callFrame , true ) ) ;
134
+ }
123
135
}
124
136
125
137
if ( stackTrace . parentId ) {
@@ -131,9 +143,11 @@ export class StackTrace {
131
143
}
132
144
}
133
145
134
- _appendFrame ( frame : StackFrame ) {
146
+ _appendFrame ( frame : FrameElement ) {
135
147
this . frames . push ( frame ) ;
136
- this . _frameById . set ( frame . _id , frame ) ;
148
+ if ( frame instanceof StackFrame ) {
149
+ this . _frameById . set ( frame . _id , frame ) ;
150
+ }
137
151
}
138
152
139
153
async formatAsNative ( ) : Promise < string > {
@@ -174,7 +188,23 @@ interface IScope {
174
188
callFrameId : string ;
175
189
}
176
190
177
- export class StackFrame {
191
+ export class AsyncSeparator implements IFrameElement {
192
+ constructor ( private readonly label = 'async' ) { }
193
+
194
+ public async toDap ( ) : Promise < Dap . StackFrame > {
195
+ return { name : this . label , id : 0 , line : 0 , column : 0 , presentationHint : 'label' } ;
196
+ }
197
+
198
+ public async formatAsNative ( ) : Promise < string > {
199
+ return ` --- ${ this . label } ---` ;
200
+ }
201
+
202
+ public async format ( ) : Promise < string > {
203
+ return `◀ ${ this . label } ▶` ;
204
+ }
205
+ }
206
+
207
+ export class StackFrame implements IFrameElement {
178
208
private static _lastFrameId = 0 ;
179
209
180
210
_id : number ;
@@ -184,7 +214,6 @@ export class StackFrame {
184
214
| Promise < IPreferredUiLocation | undefined >
185
215
| IPreferredUiLocation
186
216
| undefined ;
187
- private _isAsyncSeparator = false ;
188
217
private _scope : IScope | undefined ;
189
218
private _thread : Thread ;
190
219
@@ -198,11 +227,11 @@ export class StackFrame {
198
227
callFrame : Cdp . Runtime . CallFrame ,
199
228
isAsync : boolean ,
200
229
) : StackFrame {
201
- return new StackFrame ( thread , callFrame . functionName , thread . rawLocation ( callFrame ) , isAsync ) ;
230
+ return new StackFrame ( thread , callFrame , thread . rawLocation ( callFrame ) , isAsync ) ;
202
231
}
203
232
204
233
static fromDebugger ( thread : Thread , callFrame : Cdp . Debugger . CallFrame ) : StackFrame {
205
- const result = new StackFrame ( thread , callFrame . functionName , thread . rawLocation ( callFrame ) ) ;
234
+ const result = new StackFrame ( thread , callFrame , thread . rawLocation ( callFrame ) ) ;
206
235
result . _scope = {
207
236
chain : callFrame . scopeChain ,
208
237
thisObject : callFrame . this ,
@@ -214,37 +243,32 @@ export class StackFrame {
214
243
return result ;
215
244
}
216
245
217
- static asyncSeparator ( thread : Thread , name : string ) : StackFrame {
218
- // todo@connor 4312: this should probably be a different type of object
219
- const result = new StackFrame (
220
- thread ,
221
- name ,
222
- { lineNumber : 1 , columnNumber : 1 , scriptId : '' } ,
223
- true ,
224
- ) ;
225
- result . _isAsyncSeparator = true ;
226
- return result ;
227
- }
228
-
229
246
constructor (
230
247
thread : Thread ,
231
- name : string ,
248
+ private readonly callFrame : Cdp . Debugger . CallFrame | Cdp . Runtime . CallFrame ,
232
249
rawLocation : RawLocation ,
233
250
private readonly isAsync = false ,
234
251
) {
235
252
this . _id = ++ StackFrame . _lastFrameId ;
236
- this . _name = name || '<anonymous>' ;
253
+ this . _name = callFrame . functionName || '<anonymous>' ;
237
254
this . _rawLocation = rawLocation ;
238
255
this . uiLocation = once ( ( ) => thread . rawLocationToUiLocation ( rawLocation ) ) ;
239
256
this . _thread = thread ;
240
257
}
241
258
259
+ /**
260
+ * Gets whether the runtime explicitly said this frame can be restarted.
261
+ */
262
+ public get canExplicitlyBeRestarted ( ) {
263
+ return ! ! ( this . callFrame as Cdp . Debugger . CallFrame ) . canBeRestarted ;
264
+ }
265
+
242
266
/**
243
267
* Gets whether this stackframe is at the same position as the other frame.
244
268
*/
245
- public equivalentTo ( other : StackFrame | undefined ) {
269
+ public equivalentTo ( other : unknown ) {
246
270
return (
247
- other &&
271
+ other instanceof StackFrame &&
248
272
other . _rawLocation . columnNumber === this . _rawLocation . columnNumber &&
249
273
other . _rawLocation . lineNumber === this . _rawLocation . lineNumber &&
250
274
other . _rawLocation . scriptId === this . _rawLocation . scriptId
@@ -336,15 +360,12 @@ export class StackFrame {
336
360
return { scopes } ;
337
361
}
338
362
363
+ /** @inheritdoc */
339
364
async toDap ( format ?: Dap . StackFrameFormat ) : Promise < Dap . StackFrame > {
340
365
const uiLocation = await this . uiLocation ( ) ;
341
366
const source = uiLocation ? await uiLocation . source . toDap ( ) : undefined ;
342
367
const isSmartStepped = await shouldStepOverStackFrame ( this ) ;
343
- const presentationHint = this . _isAsyncSeparator
344
- ? 'label'
345
- : isSmartStepped
346
- ? 'deemphasize'
347
- : 'normal' ;
368
+ const presentationHint = isSmartStepped ? 'deemphasize' : 'normal' ;
348
369
if ( isSmartStepped && source ) {
349
370
source . origin =
350
371
isSmartStepped === StackFrameStepOverReason . SmartStep
@@ -374,12 +395,14 @@ export class StackFrame {
374
395
column,
375
396
source,
376
397
presentationHint,
377
- canRestart : ! this . isAsync ,
398
+ // If `canBeRestarted` is present, use that
399
+ // https://github.com/microsoft/vscode-js-debug/issues/1283
400
+ canRestart : ( this . callFrame as Cdp . Debugger . CallFrame ) . canBeRestarted ?? ! this . isAsync ,
378
401
} as Dap . StackFrame ;
379
402
}
380
403
404
+ /** @inheritdoc */
381
405
async formatAsNative ( ) : Promise < string > {
382
- if ( this . _isAsyncSeparator ) return ` --- ${ this . _name } ---` ;
383
406
const uiLocation = await this . uiLocation ( ) ;
384
407
const url =
385
408
( await uiLocation ?. source . existingAbsolutePath ( ) ) ||
@@ -389,8 +412,8 @@ export class StackFrame {
389
412
return ` at ${ this . _name } (${ url } :${ lineNumber } :${ columnNumber } )` ;
390
413
}
391
414
415
+ /** @inheritdoc */
392
416
async format ( ) : Promise < string > {
393
- if ( this . _isAsyncSeparator ) return `◀ ${ this . _name } ▶` ;
394
417
const uiLocation = await this . uiLocation ( ) ;
395
418
const prettyName = ( await uiLocation ?. source . prettyName ( ) ) || '<unknown>' ;
396
419
const anyLocation = uiLocation || this . _rawLocation ;
0 commit comments