@@ -317,6 +317,7 @@ export class GDBDebugSession extends LoggingDebugSession {
317317 response . body . supportsFunctionBreakpoints = true ;
318318 response . body . supportsEvaluateForHovers = true ;
319319 response . body . supportsSetVariable = true ;
320+ response . body . supportsSetExpression = true ;
320321
321322 // We no longer support a 'Restart' request. However, VSCode will implement a replacement by terminating the
322323 // 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 {
19381939 }
19391940 }
19401941
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
19411944 protected async setVariableRequest ( response : DebugProtocol . SetVariableResponse , args : DebugProtocol . SetVariableArguments ) : Promise < void > {
19421945 try {
19431946 let name = args . name ;
@@ -1985,7 +1988,36 @@ export class GDBDebugSession extends LoggingDebugSession {
19851988 } ;
19861989 this . sendResponse ( response ) ;
19871990 } 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 } ` ) ;
19892021 }
19902022 }
19912023
@@ -3204,61 +3236,7 @@ export class GDBDebugSession extends LoggingDebugSession {
32043236
32053237 if ( args . context !== 'repl' ) {
32063238 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 ) ;
32623240 this . sendResponse ( response ) ;
32633241 } catch ( err ) {
32643242 if ( this . isBusy ( ) ) {
@@ -3315,6 +3293,70 @@ export class GDBDebugSession extends LoggingDebugSession {
33153293 return this . evaluateQ . add ( doit , r , a ) ;
33163294 }
33173295
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+
33183360 protected async gotoTargetsRequest ( response : DebugProtocol . GotoTargetsResponse , args : DebugProtocol . GotoTargetsArguments ) : Promise < void > {
33193361 try {
33203362 const done = await this . miDebugger . goto ( args . source . path , args . line ) ;
0 commit comments