@@ -317,6 +317,7 @@ export class GDBDebugSession extends LoggingDebugSession {
317
317
response . body . supportsFunctionBreakpoints = true ;
318
318
response . body . supportsEvaluateForHovers = true ;
319
319
response . body . supportsSetVariable = true ;
320
+ response . body . supportsSetExpression = true ;
320
321
321
322
// We no longer support a 'Restart' request. However, VSCode will implement a replacement by terminating the
322
323
// current session and starting a new one from scratch. But, we still have to support the launch.json
@@ -1938,6 +1939,8 @@ export class GDBDebugSession extends LoggingDebugSession {
1938
1939
}
1939
1940
}
1940
1941
1942
+ // Note that setVariableRequest is called to set member variables of watched expressions. So, don't
1943
+ // make assumptions of what names you might see
1941
1944
protected async setVariableRequest ( response : DebugProtocol . SetVariableResponse , args : DebugProtocol . SetVariableArguments ) : Promise < void > {
1942
1945
try {
1943
1946
let name = args . name ;
@@ -1985,7 +1988,36 @@ export class GDBDebugSession extends LoggingDebugSession {
1985
1988
} ;
1986
1989
this . sendResponse ( response ) ;
1987
1990
} catch ( err ) {
1988
- this . sendErrorResponse ( response , 11 , `Could not set variable: ${ err } ` ) ;
1991
+ this . sendErrorResponse ( response , 11 , `Could not set variable '${ args . name } '\n${ err } ` ) ;
1992
+ }
1993
+ }
1994
+
1995
+ protected async setExpressionRequest ( response : DebugProtocol . SetVariableResponse , args : DebugProtocol . SetExpressionArguments ) : Promise < void > {
1996
+ try {
1997
+ const [ threadId , frameId ] = args . frameId ? decodeReference ( args . frameId ) : [ undefined , undefined ] ;
1998
+ const exp = args . expression ;
1999
+ const varObjName = this . createVarNameFromExpr ( args . expression , args . frameId , 'watch' ) ;
2000
+ let varId = this . variableHandlesReverse . get ( varObjName ) ;
2001
+ if ( varId === undefined ) {
2002
+ let varObj : VariableObject ;
2003
+ if ( args . frameId === undefined ) {
2004
+ varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' ) ; // Create floating variable
2005
+ } else {
2006
+ varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' , threadId , frameId ) ;
2007
+ }
2008
+ varId = this . findOrCreateVariable ( varObj ) ;
2009
+ varObj . exp = exp ;
2010
+ varObj . id = varId ;
2011
+ }
2012
+ await this . miDebugger . exprAssign ( varObjName , args . value , threadId , frameId ) ;
2013
+ const evalRsp = await this . evalExprInternal ( args . expression , args . frameId , 'watch' , threadId , frameId ) ;
2014
+ response . body = {
2015
+ ...evalRsp ,
2016
+ value : evalRsp . result ,
2017
+ } ;
2018
+ this . sendResponse ( response ) ;
2019
+ } catch ( err ) {
2020
+ this . sendErrorResponse ( response , 11 , `Could not set expression '${ args . expression } '\n${ err } ` ) ;
1989
2021
}
1990
2022
}
1991
2023
@@ -3204,61 +3236,7 @@ export class GDBDebugSession extends LoggingDebugSession {
3204
3236
3205
3237
if ( args . context !== 'repl' ) {
3206
3238
try {
3207
- const exp = args . expression ;
3208
- const hasher = crypto . createHash ( 'sha256' ) ;
3209
- hasher . update ( exp ) ;
3210
- if ( args . frameId !== undefined ) {
3211
- hasher . update ( args . frameId . toString ( 16 ) ) ;
3212
- }
3213
- const exprName = hasher . digest ( 'hex' ) ;
3214
- const varObjName = `${ args . context } _${ exprName } ` ;
3215
- let varObj : VariableObject ;
3216
- let varId = this . variableHandlesReverse . get ( varObjName ) ;
3217
- let createNewVar = varId === undefined ;
3218
- let updateError ;
3219
- if ( ! createNewVar ) {
3220
- try {
3221
- const changes = await this . miDebugger . varUpdate ( varObjName , threadId , frameId ) ;
3222
- const changelist = changes . result ( 'changelist' ) ;
3223
- for ( const change of changelist || [ ] ) {
3224
- const inScope = MINode . valueOf ( change , 'in_scope' ) ;
3225
- if ( inScope === 'true' ) {
3226
- const name = MINode . valueOf ( change , 'name' ) ;
3227
- const vId = this . variableHandlesReverse . get ( name ) ;
3228
- const v = this . variableHandles . get ( vId ) as any ;
3229
- v . applyChanges ( change ) ;
3230
- } else {
3231
- const msg = `${ exp } currently not in scope` ;
3232
- await this . miDebugger . sendCommand ( `var-delete ${ varObjName } ` ) ;
3233
- if ( this . args . showDevDebugOutput ) {
3234
- this . handleMsg ( 'log' , `Expression ${ msg } . Will try to create again\n` ) ;
3235
- }
3236
- createNewVar = true ;
3237
- throw new Error ( msg ) ;
3238
- }
3239
- }
3240
- varObj = this . variableHandles . get ( varId ) as any ;
3241
- } catch ( err ) {
3242
- updateError = err ;
3243
- }
3244
- }
3245
- if ( ! this . isBusy ( ) && ( createNewVar || ( ( updateError instanceof MIError && updateError . message === VarNotFoundMsg ) ) ) ) {
3246
- // We always create a floating variable so it will be updated in the context of the current frame
3247
- // Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
3248
- // from previous stack frames and returns the wrong results or says nothing changed when in fact it has
3249
- if ( args . frameId === undefined ) {
3250
- varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' ) ; // Create floating variable
3251
- } else {
3252
- varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' , threadId , frameId ) ;
3253
- }
3254
-
3255
- varId = this . findOrCreateVariable ( varObj ) ;
3256
- varObj . exp = exp ;
3257
- varObj . id = varId ;
3258
- } else if ( ! varObj ) {
3259
- throw updateError || new Error ( 'evaluateRequest: unknown error' ) ;
3260
- }
3261
- response . body = varObj . toProtocolEvaluateResponseBody ( ) ;
3239
+ response . body = await this . evalExprInternal ( args . expression , args . frameId , args . context , threadId , frameId ) ;
3262
3240
this . sendResponse ( response ) ;
3263
3241
} catch ( err ) {
3264
3242
if ( this . isBusy ( ) ) {
@@ -3315,6 +3293,70 @@ export class GDBDebugSession extends LoggingDebugSession {
3315
3293
return this . evaluateQ . add ( doit , r , a ) ;
3316
3294
}
3317
3295
3296
+ private async evalExprInternal (
3297
+ exp : string , frameRef : number | undefined , context : string ,
3298
+ threadId : number , frameId : number ) : Promise < DebugProtocol . EvaluateResponse [ 'body' ] > {
3299
+ const varObjName = this . createVarNameFromExpr ( exp , frameRef , context ) ;
3300
+ let varObj : VariableObject ;
3301
+ let varId = this . variableHandlesReverse . get ( varObjName ) ;
3302
+ let createNewVar = varId === undefined ;
3303
+ let updateError ;
3304
+ if ( ! createNewVar ) {
3305
+ try {
3306
+ const changes = await this . miDebugger . varUpdate ( varObjName , threadId , frameId ) ;
3307
+ const changelist = changes . result ( 'changelist' ) ;
3308
+ for ( const change of changelist || [ ] ) {
3309
+ const inScope = MINode . valueOf ( change , 'in_scope' ) ;
3310
+ if ( inScope === 'true' ) {
3311
+ const name = MINode . valueOf ( change , 'name' ) ;
3312
+ const vId = this . variableHandlesReverse . get ( name ) ;
3313
+ const v = this . variableHandles . get ( vId ) as any ;
3314
+ v . applyChanges ( change ) ;
3315
+ } else {
3316
+ const msg = `${ exp } currently not in scope` ;
3317
+ await this . miDebugger . sendCommand ( `var-delete ${ varObjName } ` ) ;
3318
+ if ( this . args . showDevDebugOutput ) {
3319
+ this . handleMsg ( 'log' , `Expression ${ msg } . Will try to create again\n` ) ;
3320
+ }
3321
+ createNewVar = true ;
3322
+ throw new Error ( msg ) ;
3323
+ }
3324
+ }
3325
+ varObj = this . variableHandles . get ( varId ) as any ;
3326
+ } catch ( err ) {
3327
+ updateError = err ;
3328
+ }
3329
+ }
3330
+ if ( ! this . isBusy ( ) && ( createNewVar || ( ( updateError instanceof MIError && updateError . message === VarNotFoundMsg ) ) ) ) {
3331
+ // We always create a floating variable so it will be updated in the context of the current frame
3332
+ // Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
3333
+ // from previous stack frames and returns the wrong results or says nothing changed when in fact it has
3334
+ if ( frameRef === undefined ) {
3335
+ varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' ) ; // Create floating variable
3336
+ } else {
3337
+ varObj = await this . miDebugger . varCreate ( 0 , exp , varObjName , '@' , threadId , frameId ) ;
3338
+ }
3339
+
3340
+ varId = this . findOrCreateVariable ( varObj ) ;
3341
+ varObj . exp = exp ;
3342
+ varObj . id = varId ;
3343
+ } else if ( ! varObj ) {
3344
+ throw updateError || new Error ( 'evaluateRequest: unknown error' ) ;
3345
+ }
3346
+ return varObj . toProtocolEvaluateResponseBody ( ) ;
3347
+ }
3348
+
3349
+ private createVarNameFromExpr ( exp : string , encodedFrameId : number | undefined , context : string ) : string {
3350
+ const hasher = crypto . createHash ( 'sha256' ) ;
3351
+ hasher . update ( exp ) ;
3352
+ if ( encodedFrameId !== undefined ) {
3353
+ hasher . update ( encodedFrameId . toString ( 16 ) ) ;
3354
+ }
3355
+ const exprName = hasher . digest ( 'hex' ) ;
3356
+ const varObjName = `${ context || 'watch' } _${ exprName } ` ;
3357
+ return varObjName ;
3358
+ }
3359
+
3318
3360
protected async gotoTargetsRequest ( response : DebugProtocol . GotoTargetsResponse , args : DebugProtocol . GotoTargetsArguments ) : Promise < void > {
3319
3361
try {
3320
3362
const done = await this . miDebugger . goto ( args . source . path , args . line ) ;
0 commit comments