Reuse variable objects, utilize var-update
Same variables now have same ids in each VariablesResponse. This allows vscode to keep track of changes properly and prevents collapsing of all variables after every step.
This commit is contained in:
parent
77b689443f
commit
2551dba725
3 changed files with 137 additions and 54 deletions
|
|
@ -1,3 +1,6 @@
|
|||
import { MINode } from "./mi_parse";
|
||||
import { DebugProtocol } from "vscode-debugprotocol/lib/debugProtocol";
|
||||
|
||||
export interface Breakpoint {
|
||||
file?: string;
|
||||
line?: number;
|
||||
|
|
@ -59,4 +62,62 @@ export interface IBackend {
|
|||
isReady(): boolean;
|
||||
changeVariable(name: string, rawValue: string): Thenable<any>;
|
||||
examineMemory(from: number, to: number): Thenable<any>;
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableObject {
|
||||
name: string;
|
||||
exp: string;
|
||||
numchild: number;
|
||||
type: string;
|
||||
value: string;
|
||||
threadId: string;
|
||||
frozen: boolean;
|
||||
dynamic: boolean;
|
||||
displayhint: string;
|
||||
has_more: boolean;
|
||||
id: number;
|
||||
constructor(node: any) {
|
||||
this.name = MINode.valueOf(node, "name");
|
||||
this.exp = MINode.valueOf(node, "exp");
|
||||
this.numchild = parseInt(MINode.valueOf(node, "numchild"));
|
||||
this.type = MINode.valueOf(node, "type");
|
||||
this.value = MINode.valueOf(node, "value");
|
||||
this.threadId = MINode.valueOf(node, "thread-id");
|
||||
this.frozen = !!MINode.valueOf(node, "frozen");
|
||||
this.dynamic = !!MINode.valueOf(node, "dynamic");
|
||||
this.displayhint = MINode.valueOf(node, "displayhint");
|
||||
// TODO: use has_more when it's > 0
|
||||
this.has_more = !!MINode.valueOf(node, "has_more");
|
||||
}
|
||||
|
||||
public applyChanges(node: MINode) {
|
||||
this.value = MINode.valueOf(node, "value");
|
||||
if (!!MINode.valueOf(node, "type_changed")) {
|
||||
this.type = MINode.valueOf(node, "new_type");
|
||||
}
|
||||
this.dynamic = !!MINode.valueOf(node, "dynamic");
|
||||
this.displayhint = MINode.valueOf(node, "displayhint");
|
||||
this.has_more = !!MINode.valueOf(node, "has_more");
|
||||
}
|
||||
|
||||
public isCompound(): boolean {
|
||||
return this.numchild > 0 ||
|
||||
this.value === "{...}" ||
|
||||
(this.dynamic && (this.displayhint === "array" || this.displayhint === "map"));
|
||||
}
|
||||
|
||||
public toProtocolVariable(): DebugProtocol.Variable {
|
||||
let res: DebugProtocol.Variable = {
|
||||
name: this.exp,
|
||||
evaluateName: this.name,
|
||||
value: (this.value === void 0) ? "<unknown>" : this.value,
|
||||
type: this.type,
|
||||
// kind: this.displayhint,
|
||||
variablesReference: this.id
|
||||
};
|
||||
if (this.displayhint) {
|
||||
res.kind = this.displayhint;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Breakpoint, IBackend, Stack, SSHArguments, Variable } from "../backend"
|
||||
import { Breakpoint, IBackend, Stack, SSHArguments, Variable, VariableObject } from "../backend"
|
||||
import * as ChildProcess from "child_process"
|
||||
import { EventEmitter } from "events"
|
||||
import { parseMI, MINode } from '../mi_parse';
|
||||
|
|
@ -661,17 +661,33 @@ export class MI2 extends EventEmitter implements IBackend {
|
|||
});
|
||||
}
|
||||
|
||||
async varCreate(expression: string): Promise<MINode> {
|
||||
async varCreate(expression: string, name: string = "-"): Promise<VariableObject> {
|
||||
if (trace)
|
||||
this.log("stderr", "varCreate");
|
||||
return this.sendCommand(`var-create - * "${expression}"`);
|
||||
const res = await this.sendCommand(`var-create ${name} @ "${expression}"`);
|
||||
return new VariableObject(res.result(""));
|
||||
}
|
||||
|
||||
async varListChildren(name: string): Promise<MINode> {
|
||||
async varEvalExpression(name: string): Promise < MINode > {
|
||||
if (trace)
|
||||
this.log("stderr", "varEvalExpression");
|
||||
return this.sendCommand(`var-evaluate-expression ${name}`);
|
||||
}
|
||||
|
||||
async varListChildren(name: string): Promise<VariableObject[]> {
|
||||
if (trace)
|
||||
this.log("stderr", "varListChildren");
|
||||
//TODO: add `from` and `to` arguments
|
||||
return this.sendCommand(`var-list-children --simple-values ${name}`);
|
||||
const res = await this.sendCommand(`var-list-children --all-values ${name}`);
|
||||
const children = res.result("children");
|
||||
let omg: VariableObject[] = children.map(child => new VariableObject(child[1]));
|
||||
return omg;
|
||||
}
|
||||
|
||||
async varUpdate(name: string = "*"): Promise<MINode> {
|
||||
if (trace)
|
||||
this.log("stderr", "varUpdate");
|
||||
return this.sendCommand(`var-update --all-values ${name}`)
|
||||
}
|
||||
|
||||
logNoNewLine(type: string, msg: string) {
|
||||
|
|
|
|||
102
src/mibase.ts
102
src/mibase.ts
|
|
@ -1,6 +1,6 @@
|
|||
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
|
||||
import { DebugProtocol } from 'vscode-debugprotocol';
|
||||
import { Breakpoint, IBackend, Variable } from './backend/backend';
|
||||
import { Breakpoint, IBackend, Variable, VariableObject } from './backend/backend';
|
||||
import { MINode } from './backend/mi_parse';
|
||||
import { expandValue, isExpandable } from './backend/gdb_expansion';
|
||||
import { MI2 } from './backend/mi2/mi2';
|
||||
|
|
@ -22,7 +22,8 @@ const STACK_HANDLES_START = 1000;
|
|||
const VAR_HANDLES_START = 2000;
|
||||
|
||||
export class MI2DebugSession extends DebugSession {
|
||||
protected variableHandles = new Handles<string | ExtendedVariable>(VAR_HANDLES_START);
|
||||
protected variableHandles = new Handles<string | VariableObject | ExtendedVariable>(VAR_HANDLES_START);
|
||||
protected variableHandlesReverse: { [id: string]: number } = {};
|
||||
protected quit: boolean;
|
||||
protected attached: boolean;
|
||||
protected needContinue: boolean;
|
||||
|
|
@ -266,7 +267,7 @@ export class MI2DebugSession extends DebugSession {
|
|||
|
||||
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
|
||||
const variables: DebugProtocol.Variable[] = [];
|
||||
let id: number | string | ExtendedVariable;
|
||||
let id: number | string | VariableObject | ExtendedVariable;
|
||||
if (args.variablesReference < VAR_HANDLES_START) {
|
||||
id = args.variablesReference - STACK_HANDLES_START;
|
||||
}
|
||||
|
|
@ -281,28 +282,16 @@ export class MI2DebugSession extends DebugSession {
|
|||
return this.variableHandles.create(arg);
|
||||
};
|
||||
|
||||
let miVarObjToVariable = (varObj: any): DebugProtocol.Variable => {
|
||||
const evaluateName = MINode.valueOf(varObj, "name");
|
||||
const value = MINode.valueOf(varObj, "value");
|
||||
const numChild = parseInt(MINode.valueOf(varObj, "numchild"));
|
||||
const dynamic = MINode.valueOf(varObj, "dynamic") || 0;
|
||||
let displayHint, hasMore;
|
||||
if (dynamic) {
|
||||
displayHint = MINode.valueOf(varObj, "displayhint");
|
||||
hasMore = parseInt(MINode.valueOf(varObj, "has_more"));
|
||||
let findOrCreateVariable = (varObj: VariableObject): number => {
|
||||
let id: number;
|
||||
if (this.variableHandlesReverse.hasOwnProperty(varObj.name)) {
|
||||
id = this.variableHandlesReverse[varObj.name];
|
||||
}
|
||||
const isCompound = numChild > 0 ||
|
||||
value === "{...}" ||
|
||||
(dynamic > 0 && (displayHint === "array" || displayHint === "map"));
|
||||
|
||||
let res = {
|
||||
name: MINode.valueOf(varObj, "exp"),
|
||||
evaluateName,
|
||||
type: MINode.valueOf(varObj, "type"),
|
||||
value: value || "<value>",
|
||||
variablesReference: isCompound ? createVariable(evaluateName) : 0
|
||||
} as DebugProtocol.Variable;
|
||||
return res;
|
||||
else {
|
||||
id = createVariable(varObj);
|
||||
this.variableHandlesReverse[varObj.name] = id;
|
||||
}
|
||||
return varObj.isCompound() ? id : 0;
|
||||
};
|
||||
|
||||
if (typeof id == "number") {
|
||||
|
|
@ -311,10 +300,26 @@ export class MI2DebugSession extends DebugSession {
|
|||
stack = await this.miDebugger.getStackVariables(this.threadID, id);
|
||||
for (const variable of stack) {
|
||||
try {
|
||||
const varObj = await this.miDebugger.varCreate(variable.name);
|
||||
let v = miVarObjToVariable(varObj.resultRecords.results);
|
||||
v.name = variable.name;
|
||||
variables.push(v);
|
||||
let varObj: VariableObject;
|
||||
try {
|
||||
const changes = await this.miDebugger.varUpdate(variable.name);
|
||||
const changelist = changes.result("changelist");
|
||||
changelist.forEach((change) => {
|
||||
const name = MINode.valueOf(change, "name");
|
||||
const vId = this.variableHandlesReverse[variable.name];
|
||||
const v = this.variableHandles.get(vId) as any;
|
||||
v.applyChanges(change);
|
||||
});
|
||||
const varId = this.variableHandlesReverse[variable.name];
|
||||
varObj = this.variableHandles.get(varId) as any;
|
||||
}
|
||||
catch (err) {
|
||||
varObj = await this.miDebugger.varCreate(variable.name, variable.name);
|
||||
const varId = findOrCreateVariable(varObj);
|
||||
varObj.exp = variable.name;
|
||||
varObj.id = varId;
|
||||
}
|
||||
variables.push(varObj.toProtocolVariable());
|
||||
}
|
||||
catch (err) {
|
||||
variables.push({
|
||||
|
|
@ -333,27 +338,28 @@ export class MI2DebugSession extends DebugSession {
|
|||
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
|
||||
}
|
||||
}
|
||||
else if (typeof id == "string") {
|
||||
// Variable members
|
||||
let listChildren;
|
||||
try {
|
||||
listChildren = await this.miDebugger.varListChildren(id);
|
||||
const children: any[] = listChildren.result("children");
|
||||
// TODO: use hasMore when it's > 0
|
||||
// const hasMore = parseInt(listChildren.result("has_more"));
|
||||
const vars = children.map(child => miVarObjToVariable(child[1]));
|
||||
|
||||
response.body = {
|
||||
variables: vars
|
||||
}
|
||||
this.sendResponse(response);
|
||||
}
|
||||
catch (err) {
|
||||
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
|
||||
}
|
||||
}
|
||||
else if (typeof id == "object") {
|
||||
if (id instanceof ExtendedVariable) {
|
||||
if (id instanceof VariableObject) {
|
||||
// Variable members
|
||||
let children: VariableObject[];
|
||||
try {
|
||||
children = await this.miDebugger.varListChildren(id.name);
|
||||
const vars = children.map(child => {
|
||||
const varId = findOrCreateVariable(child);
|
||||
child.id = varId;
|
||||
return child.toProtocolVariable();
|
||||
});
|
||||
|
||||
response.body = {
|
||||
variables: vars
|
||||
}
|
||||
this.sendResponse(response);
|
||||
}
|
||||
catch (err) {
|
||||
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
|
||||
}
|
||||
}
|
||||
else if (id instanceof ExtendedVariable) {
|
||||
let varReq = id;
|
||||
if (varReq.options.arg) {
|
||||
let strArr = [];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue