From b321388cf7cabfe097e2fb538ba9ba88c181d728 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Wed, 16 Jan 2019 22:45:27 -0500 Subject: [PATCH] Change behavior of evaluate request handling As discussed in #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 --- src/GDBDebugSession.ts | 105 ++++++++---------- src/integration-tests/evaluate.spec.ts | 60 ++++++++++ .../test-programs/.gitignore | 1 + src/integration-tests/test-programs/Makefile | 5 +- .../test-programs/evaluate.cpp | 3 + 5 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 src/integration-tests/evaluate.spec.ts create mode 100644 src/integration-tests/test-programs/evaluate.cpp diff --git a/src/GDBDebugSession.ts b/src/GDBDebugSession.ts index a71ae352..2f888368 100644 --- a/src/GDBDebugSession.ts +++ b/src/GDBDebugSession.ts @@ -443,71 +443,62 @@ export class GDBDebugSession extends LoggingDebugSession { args: DebugProtocol.EvaluateArguments): Promise { 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); } diff --git a/src/integration-tests/evaluate.spec.ts b/src/integration-tests/evaluate.spec.ts new file mode 100644 index 00000000..d2e44e44 --- /dev/null +++ b/src/integration-tests/evaluate.spec.ts @@ -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'); + }); +}); diff --git a/src/integration-tests/test-programs/.gitignore b/src/integration-tests/test-programs/.gitignore index 3193bac1..a61fc1dd 100644 --- a/src/integration-tests/test-programs/.gitignore +++ b/src/integration-tests/test-programs/.gitignore @@ -1,4 +1,5 @@ empty +evaluate mem vars *.o diff --git a/src/integration-tests/test-programs/Makefile b/src/integration-tests/test-programs/Makefile index 9fbe8fb4..86532055 100644 --- a/src/integration-tests/test-programs/Makefile +++ b/src/integration-tests/test-programs/Makefile @@ -1,4 +1,4 @@ -BINS = empty vars vars_cpp mem +BINS = empty evaluate vars vars_cpp mem .PHONY: all all: $(BINS) @@ -11,6 +11,9 @@ LINK_CXX = $(CXX) -o $@ $^ empty: empty.o $(LINK) +evaluate: evaluate.o + $(LINK) + mem: mem.o $(LINK) diff --git a/src/integration-tests/test-programs/evaluate.cpp b/src/integration-tests/test-programs/evaluate.cpp new file mode 100644 index 00000000..33c14ce1 --- /dev/null +++ b/src/integration-tests/test-programs/evaluate.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +}