Skip to content

Commit

Permalink
Change behavior of evaluate request handling
Browse files Browse the repository at this point in the history
As discussed in eclipse-cdt-cloud#47, this patch changes how evaluate requests are
handled.  Currently, they are handled as GDB commands.  The intent of
the request is more to evaluate expressions in the target language.

This is already what we do when context is 'watch'.  This patch makes it
so we handle it the same way regardless of the context.

A simple test for the evaluate request is added.

Signed-off-by: Simon Marchi <[email protected]>
  • Loading branch information
Simon Marchi committed Jan 17, 2019
1 parent deda840 commit b321388
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 58 deletions.
105 changes: 48 additions & 57 deletions src/GDBDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,71 +443,62 @@ export class GDBDebugSession extends LoggingDebugSession {
args: DebugProtocol.EvaluateArguments): Promise<void> {
response.body = { result: 'Error: could not evaluate expression', variablesReference: 0 }; // default response
try {
switch (args.context) {
case 'repl':
response.body = { result: 'placeholder text', variablesReference: 0 };
await this.gdb.sendCommand(args.expression);
if (args.frameId) {
const frame = this.frameHandles.get(args.frameId);
if (!frame) {
this.sendResponse(response);
break;
case 'watch': {
if (args.frameId) {
const frame = this.frameHandles.get(args.frameId);
if (!frame) {
this.sendResponse(response);
return;
}
try {
const stackDepth = await mi.sendStackInfoDepth(this.gdb, { maxDepth: 100 });
const depth = parseInt(stackDepth.depth, 10);
let varobj = varMgr.getVar(frame.frameId, frame.threadId, depth, args.expression);
if (!varobj) {
return;
}
try {
const stackDepth = await mi.sendStackInfoDepth(this.gdb, { maxDepth: 100 });
const depth = parseInt(stackDepth.depth, 10);
let varobj = varMgr.getVar(frame.frameId, frame.threadId, depth, args.expression);
if (!varobj) {
const varCreateResponse = await mi.sendVarCreate(this.gdb,
{ expression: args.expression, frame: 'current' });
varobj = varMgr.addVar(frame.frameId, frame.threadId, depth, args.expression, false,
false, varCreateResponse);
} else {
const vup = await mi.sendVarUpdate(this.gdb, {
threadId: frame.threadId,
name: varobj.varname,
});
const update = vup.changelist[0];
if (update) {
if (update.in_scope === 'true') {
if (update.name === varobj.varname) {
varobj.value = update.value;
}
} else {
varMgr.removeVar(this.gdb, frame.frameId, frame.threadId, depth,
varobj.varname);
await mi.sendVarDelete(this.gdb, { varname: varobj.varname });
const varCreateResponse = await mi.sendVarCreate(this.gdb,
{ expression: args.expression, frame: 'current' });
varobj = varMgr.addVar(frame.frameId, frame.threadId, depth, args.expression, false,
false, varCreateResponse);
} else {
const vup = await mi.sendVarUpdate(this.gdb, {
threadId: frame.threadId,
name: varobj.varname,
});
const update = vup.changelist[0];
if (update) {
if (update.in_scope === 'true') {
if (update.name === varobj.varname) {
varobj.value = update.value;
}
} else {
varMgr.removeVar(this.gdb, frame.frameId, frame.threadId, depth,
varobj.varname);
await mi.sendVarDelete(this.gdb, { varname: varobj.varname });
const varCreateResponse = await mi.sendVarCreate(this.gdb,
{ expression: args.expression, frame: 'current' });
varobj = varMgr.addVar(frame.frameId, frame.threadId, depth, args.expression,
false, false, varCreateResponse);
}
}
}
if (varobj) {
response.body = {
result: varobj.value,
type: varobj.type,
variablesReference: parseInt(varobj.numchild, 10) > 0
? this.variableHandles.create({
type: 'object',
frameHandle: args.frameId,
varobjName: varobj.varname,
})
: 0,
};
varobj = varMgr.addVar(frame.frameId, frame.threadId, depth, args.expression,
false, false, varCreateResponse);
}
} catch (err) {
// if any of the gdb calls fail, just report we can't complete the evaluation
}
}
this.sendResponse(response);
break;
if (varobj) {
response.body = {
result: varobj.value,
type: varobj.type,
variablesReference: parseInt(varobj.numchild, 10) > 0
? this.variableHandles.create({
type: 'object',
frameHandle: args.frameId,
varobjName: varobj.varname,
})
: 0,
};
}
} catch (err) {
// if any of the gdb calls fail, just report we can't complete the evaluation
}
}

this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
Expand Down
60 changes: 60 additions & 0 deletions src/integration-tests/evaluate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*********************************************************************
* Copyright (c) 2019 Ericsson and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*********************************************************************/

import { expect } from 'chai';
import * as path from 'path';
import { DebugClient } from 'vscode-debugadapter-testsupport/lib/debugClient';
import { getScopes, Scope, standardBefore, standardBeforeEach, testProgramsDir } from './utils';

// Allow non-arrow functions: https://mochajs.org/#arrow-functions
// tslint:disable:only-arrow-functions

let dc: DebugClient;
let scope: Scope;

const evaluateProgram = path.join(testProgramsDir, 'evaluate');
const evaluateSrc = path.join(testProgramsDir, 'evaluate.cpp');

before(standardBefore);

beforeEach(async function() {
// Move the timeout out of the way if the adapter is going to be debugged.
if (process.env.INSPECT_DEBUG_ADAPTER) {
this.timeout(9999999);
}
dc = await standardBeforeEach();
await dc.hitBreakpoint({
verbose: true,
program: evaluateProgram,
logFile: '/tmp/gdb.log',
}, { path: evaluateSrc, line: 2 });
scope = await getScopes(dc);
});

afterEach(async function() {
await dc.stop();
});

describe('evaluate request', function() {
// Move the timeout out of the way if the adapter is going to be debugged.
if (process.env.INSPECT_DEBUG_ADAPTER) {
this.timeout(9999999);
}

it('should evaluate a simple literal expression', async function() {
const res = await dc.evaluateRequest({
context: 'repl',
expression: '2',
frameId: scope.frameId,
});

expect(res.body.result).eq('2');
});
});
1 change: 1 addition & 0 deletions src/integration-tests/test-programs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
empty
evaluate
mem
vars
*.o
Expand Down
5 changes: 4 additions & 1 deletion src/integration-tests/test-programs/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BINS = empty vars vars_cpp mem
BINS = empty evaluate vars vars_cpp mem

.PHONY: all
all: $(BINS)
Expand All @@ -11,6 +11,9 @@ LINK_CXX = $(CXX) -o $@ $^
empty: empty.o
$(LINK)

evaluate: evaluate.o
$(LINK)

mem: mem.o
$(LINK)

Expand Down
3 changes: 3 additions & 0 deletions src/integration-tests/test-programs/evaluate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main() {
return 0;
}

0 comments on commit b321388

Please sign in to comment.