Skip to content

Commit f37569d

Browse files
authored
Add support set expression request (#535)
* Support 'setExpressionRequest' * Send 'invalidated' event right after setting expressions or variables
1 parent 8d22efb commit f37569d

3 files changed

Lines changed: 115 additions & 6 deletions

File tree

src/gdb/GDBDebugSessionBase.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
429429
response.body.supportsHitConditionalBreakpoints = true;
430430
response.body.supportsLogPoints = true;
431431
response.body.supportsFunctionBreakpoints = true;
432-
// response.body.supportsSetExpression = true;
432+
response.body.supportsSetExpression = true;
433433
response.body.supportsDisassembleRequest = true;
434434
response.body.supportsReadMemoryRequest = true;
435435
response.body.supportsWriteMemoryRequest = true;
@@ -2214,13 +2214,54 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
22142214
);
22152215
}
22162216
this.sendResponse(response);
2217+
this.sendEvent(new InvalidatedEvent(['variables']));
22172218
}
22182219

2219-
// protected async setExpressionRequest(response: DebugProtocol.SetExpressionResponse,
2220-
// args: DebugProtocol.SetExpressionArguments): Promise<void> {
2221-
// logger.error('got setExpressionRequest');
2222-
// this.sendResponse(response);
2223-
// }
2220+
protected async setExpressionRequest(
2221+
response: DebugProtocol.SetExpressionResponse,
2222+
args: DebugProtocol.SetExpressionArguments
2223+
): Promise<void> {
2224+
if (!this.canRequestProceed()) {
2225+
this.logger.verbose(
2226+
'Debug adapter cannot process set variable request, skipping it.'
2227+
);
2228+
this.sendResponse(response);
2229+
return;
2230+
}
2231+
try {
2232+
const initialFrameRef = args.frameId
2233+
? this.frameHandles.get(args.frameId)
2234+
: undefined;
2235+
const [gdb, frameRef, depth] =
2236+
await this.getFrameContext(initialFrameRef);
2237+
const varObj = gdb.varManager.getVar(
2238+
frameRef,
2239+
depth,
2240+
args.expression
2241+
);
2242+
// Not handling requests for expressions that do not have a dedicated GDB variable object for now.
2243+
if (!varObj) {
2244+
throw new Error(`Variable ${args.expression} not found`);
2245+
} else {
2246+
const assign = await mi.sendVarAssign(gdb, {
2247+
varname: varObj.varname,
2248+
expression: args.value,
2249+
frameRef,
2250+
});
2251+
response.body = {
2252+
value: assign.value,
2253+
};
2254+
}
2255+
this.sendResponse(response);
2256+
this.sendEvent(new InvalidatedEvent(['variables']));
2257+
} catch (err) {
2258+
this.sendErrorResponse(
2259+
response,
2260+
1,
2261+
err instanceof Error ? err.message : String(err)
2262+
);
2263+
}
2264+
}
22242265

22252266
/**
22262267
* Send command to backend.

src/integration-tests/evaluate.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,72 @@ describe('evaluate request global variables', function () {
465465
});
466466
});
467467
});
468+
469+
it('should be able to set a simple global variable and have that change reflected in the debuggee', async function () {
470+
// Read global_int using watch
471+
const watchRes = await dc.evaluateRequest({
472+
context: 'watch',
473+
expression: 'global_int',
474+
frameId: scope.frame.id,
475+
});
476+
expect(watchRes.body.result).to.equal('42');
477+
478+
// Set global_int using setExpressionRequest
479+
const setRes = await dc.customRequest('setExpression', {
480+
expression: 'global_int',
481+
value: '100',
482+
frameId: scope.frame.id,
483+
});
484+
expect(setRes.body.value).to.equal('100');
485+
486+
// Read global_int again to check updated value is returned
487+
const watchResUpdated = await dc.evaluateRequest({
488+
context: 'watch',
489+
expression: 'global_int',
490+
frameId: scope.frame.id,
491+
});
492+
expect(watchResUpdated.body.result).to.equal('100');
493+
});
494+
495+
it('should be able to set a complex global variable and have that change reflected in the debuggee', async function () {
496+
// Read global variable using watch
497+
const watchResParent = await dc.evaluateRequest({
498+
context: 'watch',
499+
expression: 's1',
500+
frameId: scope.frame.id,
501+
});
502+
const watchResChild = await dc.evaluateRequest({
503+
context: 'watch',
504+
expression: 's1.m',
505+
frameId: scope.frame.id,
506+
});
507+
expect(watchResParent.body.result).to.endWith('{...}');
508+
expect(watchResChild.body.result).to.equal('10');
509+
510+
// Set global variable using setExpressionRequest
511+
const setRes = await dc.customRequest('setExpression', {
512+
expression: 's1.m',
513+
value: '20',
514+
frameId: scope.frame.id,
515+
});
516+
expect(setRes.body.value).to.equal('20');
517+
518+
// Read global variable again to check updated value is returned
519+
const watchResChildUpdated = await dc.evaluateRequest({
520+
context: 'watch',
521+
expression: 's1.m',
522+
frameId: scope.frame.id,
523+
});
524+
expect(watchResChildUpdated.body.result).to.equal('20');
525+
526+
// Check updated value is reflected in the debuggee by evaluating an expression that uses the variable
527+
const evalRes = await dc.evaluateRequest({
528+
context: 'repl',
529+
expression: 's1.m + 5',
530+
frameId: scope.frame.id,
531+
});
532+
expect(evalRes.body.result).to.equal('25');
533+
});
468534
});
469535

470536
describe('evaluate request - watch local variable across lexical scope transition', function () {

src/integration-tests/test-programs/vars_globals.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ volatile PARENT_STRUCT s1 = {
3636

3737
volatile PARENT_STRUCT *p_s1 = &s1;
3838

39+
volatile int global_int = 42;
40+
3941
int main()
4042
{
4143
// Struct with array

0 commit comments

Comments
 (0)