debug: support evalaution when no stack frame exist

fixes #14571
This commit is contained in:
isidor 2016-10-27 11:33:45 +02:00
parent f492317890
commit f178d6c30f
4 changed files with 63 additions and 60 deletions

View file

@ -24,39 +24,6 @@ function massageValue(value: string): string {
return value ? value.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t') : value;
}
export function evaluateExpression(stackFrame: debug.IStackFrame, expression: Expression, context: string): TPromise<Expression> {
if (!stackFrame || !stackFrame.thread.process) {
expression.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate") : Expression.DEFAULT_VALUE;
expression.available = false;
expression.reference = 0;
return TPromise.as(expression);
}
expression.stackFrame = stackFrame;
return stackFrame.thread.process.session.evaluate({
expression: expression.name,
frameId: stackFrame ? stackFrame.frameId : undefined,
context
}).then(response => {
expression.available = !!(response && response.body);
if (response && response.body) {
expression.value = response.body.result;
expression.reference = response.body.variablesReference;
expression.namedVariables = response.body.namedVariables;
expression.indexedVariables = response.body.indexedVariables;
expression.type = response.body.type;
}
return expression;
}, err => {
expression.value = err.message;
expression.available = false;
expression.reference = 0;
return expression;
});
}
export class OutputElement implements debug.ITreeElement {
private static ID_COUNTER = 0;
@ -238,6 +205,39 @@ export class Expression extends ExpressionContainer implements debug.IExpression
this.value = Expression.DEFAULT_VALUE;
this.available = false;
}
public evaluate(process: debug.IProcess, stackFrame: debug.IStackFrame, context: string): TPromise<void> {
if (!process) {
this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate") : Expression.DEFAULT_VALUE;
this.available = false;
this.reference = 0;
return TPromise.as(null);
}
// Create a fake stack frame which is just used as a container for the process.
// TODO@Isidor revisit if variables should have a reference to the StackFrame or a process after all
this.stackFrame = stackFrame || new StackFrame(new Thread(process, undefined, undefined), undefined, undefined, undefined, undefined, undefined);
return process.session.evaluate({
expression: this.name,
frameId: stackFrame ? stackFrame.frameId : undefined,
context
}).then(response => {
this.available = !!(response && response.body);
if (response && response.body) {
this.value = response.body.result;
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type;
}
}, err => {
this.value = err.message;
this.available = false;
this.reference = 0;
});
}
}
export class Variable extends ExpressionContainer implements debug.IExpression {
@ -800,10 +800,10 @@ export class Model implements debug.IModel {
return this.replElements;
}
public addReplExpression(stackFrame: debug.IStackFrame, name: string): TPromise<void> {
public addReplExpression(process: debug.IProcess, stackFrame: debug.IStackFrame, name: string): TPromise<void> {
const expression = new Expression(name, true);
this.addReplElements([expression]);
return evaluateExpression(stackFrame, expression, 'repl')
return expression.evaluate(process, stackFrame, 'repl')
.then(() => this._onDidChangeREPLElements.fire());
}
@ -875,7 +875,7 @@ export class Model implements debug.IModel {
return this.watchExpressions;
}
public addWatchExpression(stackFrame: debug.IStackFrame, name: string): TPromise<void> {
public addWatchExpression(process: debug.IProcess, stackFrame: debug.IStackFrame, name: string): TPromise<void> {
const we = new Expression(name, false);
this.watchExpressions.push(we);
if (!name) {
@ -883,14 +883,14 @@ export class Model implements debug.IModel {
return TPromise.as(null);
}
return this.evaluateWatchExpressions(stackFrame, we.getId());
return this.evaluateWatchExpressions(process, stackFrame, we.getId());
}
public renameWatchExpression(stackFrame: debug.IStackFrame, id: string, newName: string): TPromise<void> {
public renameWatchExpression(process: debug.IProcess, stackFrame: debug.IStackFrame, id: string, newName: string): TPromise<void> {
const filtered = this.watchExpressions.filter(we => we.getId() === id);
if (filtered.length === 1) {
filtered[0].name = newName;
return evaluateExpression(stackFrame, filtered[0], 'watch').then(() => {
return filtered[0].evaluate(process, stackFrame, 'watch').then(() => {
this._onDidChangeWatchExpressions.fire(filtered[0]);
});
}
@ -898,19 +898,19 @@ export class Model implements debug.IModel {
return TPromise.as(null);
}
public evaluateWatchExpressions(stackFrame: debug.IStackFrame, id: string = null): TPromise<void> {
public evaluateWatchExpressions(process: debug.IProcess, stackFrame: debug.IStackFrame, id: string = null): TPromise<void> {
if (id) {
const filtered = this.watchExpressions.filter(we => we.getId() === id);
if (filtered.length !== 1) {
return TPromise.as(null);
}
return evaluateExpression(stackFrame, filtered[0], 'watch').then(() => {
return filtered[0].evaluate(process, stackFrame, 'watch').then(() => {
this._onDidChangeWatchExpressions.fire(filtered[0]);
});
}
return TPromise.join(this.watchExpressions.map(we => evaluateExpression(stackFrame, we, 'watch'))).then(() => {
return TPromise.join(this.watchExpressions.map(we => we.evaluate(process, stackFrame, 'watch'))).then(() => {
this._onDidChangeWatchExpressions.fire();
});
}

View file

@ -15,7 +15,7 @@ import { IConfigurationChangedEvent } from 'vs/editor/common/editorCommon';
import editorbrowser = require('vs/editor/browser/editorBrowser');
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import debug = require('vs/workbench/parts/debug/common/debug');
import { evaluateExpression, Expression } from 'vs/workbench/parts/debug/common/debugModel';
import { Expression } from 'vs/workbench/parts/debug/common/debugModel';
import viewer = require('vs/workbench/parts/debug/electron-browser/debugViewer');
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Position } from 'vs/editor/common/core/position';
@ -158,13 +158,16 @@ export class DebugHoverWidget implements editorbrowser.IContentWidget {
const expressionRange = this.getExactExpressionRange(lineContent, range);
// use regex to extract the sub-expression #9821
const matchingExpression = lineContent.substring(expressionRange.startColumn - 1, expressionRange.endColumn);
let promise: TPromise<debug.IExpression>;
if (process.session.configuration.capabilities.supportsEvaluateForHovers) {
const result = new Expression(matchingExpression, true);
promise = result.evaluate(process, focusedStackFrame, 'hover').then(() => result);
} else {
promise = this.findExpressionInStackFrame(matchingExpression.split('.').map(word => word.trim()).filter(word => !!word));
}
const evaluatedExpression = process.session.configuration.capabilities.supportsEvaluateForHovers ?
evaluateExpression(focusedStackFrame, new Expression(matchingExpression, true), 'hover') :
this.findExpressionInStackFrame(matchingExpression.split('.').map(word => word.trim()).filter(word => !!word));
return evaluatedExpression.then(expression => {
if (!expression || !expression.available) {
return promise.then(expression => {
if (!expression || (expression instanceof Expression && !expression.available)) {
this.hide();
return;
}

View file

@ -451,7 +451,7 @@ export class DebugService implements debug.IDebugService {
this.viewModel.setFocusedStackFrame(focusedStackFrame, process);
this._onDidChangeState.fire();
if (focusedStackFrame) {
return this.model.evaluateWatchExpressions(focusedStackFrame);
return this.model.evaluateWatchExpressions(process, focusedStackFrame);
} else {
this.model.clearWatchExpressionValues();
return TPromise.as(null);
@ -512,7 +512,7 @@ export class DebugService implements debug.IDebugService {
public addReplExpression(name: string): TPromise<void> {
this.telemetryService.publicLog('debugService/addReplExpression');
return this.model.addReplExpression(this.viewModel.focusedStackFrame, name)
return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
// Evaluate all watch expressions again since repl evaluation might have changed some.
.then(() => this.setFocusedStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
}
@ -530,11 +530,11 @@ export class DebugService implements debug.IDebugService {
}
public addWatchExpression(name: string): TPromise<void> {
return this.model.addWatchExpression(this.viewModel.focusedStackFrame, name);
return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
}
public renameWatchExpression(id: string, newName: string): TPromise<void> {
return this.model.renameWatchExpression(this.viewModel.focusedStackFrame, id, newName);
return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
}
public removeWatchExpressions(id?: string): void {

View file

@ -294,13 +294,13 @@ suite('Debug - Model', () => {
const process = new debugmodel.Process('mockProcess', rawSession);
const thread = new debugmodel.Thread(process, 'mockthread', 1);
const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
model.addWatchExpression(stackFrame, 'console').done();
model.addWatchExpression(stackFrame, 'console').done();
model.addWatchExpression(process, stackFrame, 'console').done();
model.addWatchExpression(process, stackFrame, 'console').done();
const watchExpressions = model.getWatchExpressions();
assertWatchExpressions(watchExpressions, 'console');
model.renameWatchExpression(stackFrame, watchExpressions[0].getId(), 'new_name').done();
model.renameWatchExpression(stackFrame, watchExpressions[1].getId(), 'new_name').done();
model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name').done();
model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name').done();
assertWatchExpressions(model.getWatchExpressions(), 'new_name');
model.clearWatchExpressionValues();
@ -315,9 +315,9 @@ suite('Debug - Model', () => {
const process = new debugmodel.Process('mockProcess', rawSession);
const thread = new debugmodel.Thread(process, 'mockthread', 1);
const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
model.addReplExpression(stackFrame, 'myVariable').done();
model.addReplExpression(stackFrame, 'myVariable').done();
model.addReplExpression(stackFrame, 'myVariable').done();
model.addReplExpression(process, stackFrame, 'myVariable').done();
model.addReplExpression(process, stackFrame, 'myVariable').done();
model.addReplExpression(process, stackFrame, 'myVariable').done();
assert.equal(model.getReplElements().length, 3);
model.getReplElements().forEach(re => {