Skip to content

Commit 2551dba

Browse files
committed
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.
1 parent 77b6894 commit 2551dba

File tree

3 files changed

+135
-52
lines changed

3 files changed

+135
-52
lines changed

src/backend/backend.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { MINode } from "./mi_parse";
2+
import { DebugProtocol } from "vscode-debugprotocol/lib/debugProtocol";
3+
14
export interface Breakpoint {
25
file?: string;
36
line?: number;
@@ -59,4 +62,62 @@ export interface IBackend {
5962
isReady(): boolean;
6063
changeVariable(name: string, rawValue: string): Thenable<any>;
6164
examineMemory(from: number, to: number): Thenable<any>;
62-
}
65+
}
66+
67+
export class VariableObject {
68+
name: string;
69+
exp: string;
70+
numchild: number;
71+
type: string;
72+
value: string;
73+
threadId: string;
74+
frozen: boolean;
75+
dynamic: boolean;
76+
displayhint: string;
77+
has_more: boolean;
78+
id: number;
79+
constructor(node: any) {
80+
this.name = MINode.valueOf(node, "name");
81+
this.exp = MINode.valueOf(node, "exp");
82+
this.numchild = parseInt(MINode.valueOf(node, "numchild"));
83+
this.type = MINode.valueOf(node, "type");
84+
this.value = MINode.valueOf(node, "value");
85+
this.threadId = MINode.valueOf(node, "thread-id");
86+
this.frozen = !!MINode.valueOf(node, "frozen");
87+
this.dynamic = !!MINode.valueOf(node, "dynamic");
88+
this.displayhint = MINode.valueOf(node, "displayhint");
89+
// TODO: use has_more when it's > 0
90+
this.has_more = !!MINode.valueOf(node, "has_more");
91+
}
92+
93+
public applyChanges(node: MINode) {
94+
this.value = MINode.valueOf(node, "value");
95+
if (!!MINode.valueOf(node, "type_changed")) {
96+
this.type = MINode.valueOf(node, "new_type");
97+
}
98+
this.dynamic = !!MINode.valueOf(node, "dynamic");
99+
this.displayhint = MINode.valueOf(node, "displayhint");
100+
this.has_more = !!MINode.valueOf(node, "has_more");
101+
}
102+
103+
public isCompound(): boolean {
104+
return this.numchild > 0 ||
105+
this.value === "{...}" ||
106+
(this.dynamic && (this.displayhint === "array" || this.displayhint === "map"));
107+
}
108+
109+
public toProtocolVariable(): DebugProtocol.Variable {
110+
let res: DebugProtocol.Variable = {
111+
name: this.exp,
112+
evaluateName: this.name,
113+
value: (this.value === void 0) ? "<unknown>" : this.value,
114+
type: this.type,
115+
// kind: this.displayhint,
116+
variablesReference: this.id
117+
};
118+
if (this.displayhint) {
119+
res.kind = this.displayhint;
120+
}
121+
return res;
122+
}
123+
}

src/backend/mi2/mi2.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Breakpoint, IBackend, Stack, SSHArguments, Variable } from "../backend"
1+
import { Breakpoint, IBackend, Stack, SSHArguments, Variable, VariableObject } from "../backend"
22
import * as ChildProcess from "child_process"
33
import { EventEmitter } from "events"
44
import { parseMI, MINode } from '../mi_parse';
@@ -661,17 +661,33 @@ export class MI2 extends EventEmitter implements IBackend {
661661
});
662662
}
663663

664-
async varCreate(expression: string): Promise<MINode> {
664+
async varCreate(expression: string, name: string = "-"): Promise<VariableObject> {
665665
if (trace)
666666
this.log("stderr", "varCreate");
667-
return this.sendCommand(`var-create - * "${expression}"`);
667+
const res = await this.sendCommand(`var-create ${name} @ "${expression}"`);
668+
return new VariableObject(res.result(""));
668669
}
669670

670-
async varListChildren(name: string): Promise<MINode> {
671+
async varEvalExpression(name: string): Promise < MINode > {
672+
if (trace)
673+
this.log("stderr", "varEvalExpression");
674+
return this.sendCommand(`var-evaluate-expression ${name}`);
675+
}
676+
677+
async varListChildren(name: string): Promise<VariableObject[]> {
671678
if (trace)
672679
this.log("stderr", "varListChildren");
673680
//TODO: add `from` and `to` arguments
674-
return this.sendCommand(`var-list-children --simple-values ${name}`);
681+
const res = await this.sendCommand(`var-list-children --all-values ${name}`);
682+
const children = res.result("children");
683+
let omg: VariableObject[] = children.map(child => new VariableObject(child[1]));
684+
return omg;
685+
}
686+
687+
async varUpdate(name: string = "*"): Promise<MINode> {
688+
if (trace)
689+
this.log("stderr", "varUpdate");
690+
return this.sendCommand(`var-update --all-values ${name}`)
675691
}
676692

677693
logNoNewLine(type: string, msg: string) {

src/mibase.ts

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
22
import { DebugProtocol } from 'vscode-debugprotocol';
3-
import { Breakpoint, IBackend, Variable } from './backend/backend';
3+
import { Breakpoint, IBackend, Variable, VariableObject } from './backend/backend';
44
import { MINode } from './backend/mi_parse';
55
import { expandValue, isExpandable } from './backend/gdb_expansion';
66
import { MI2 } from './backend/mi2/mi2';
@@ -22,7 +22,8 @@ const STACK_HANDLES_START = 1000;
2222
const VAR_HANDLES_START = 2000;
2323

2424
export class MI2DebugSession extends DebugSession {
25-
protected variableHandles = new Handles<string | ExtendedVariable>(VAR_HANDLES_START);
25+
protected variableHandles = new Handles<string | VariableObject | ExtendedVariable>(VAR_HANDLES_START);
26+
protected variableHandlesReverse: { [id: string]: number } = {};
2627
protected quit: boolean;
2728
protected attached: boolean;
2829
protected needContinue: boolean;
@@ -266,7 +267,7 @@ export class MI2DebugSession extends DebugSession {
266267

267268
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
268269
const variables: DebugProtocol.Variable[] = [];
269-
let id: number | string | ExtendedVariable;
270+
let id: number | string | VariableObject | ExtendedVariable;
270271
if (args.variablesReference < VAR_HANDLES_START) {
271272
id = args.variablesReference - STACK_HANDLES_START;
272273
}
@@ -281,28 +282,16 @@ export class MI2DebugSession extends DebugSession {
281282
return this.variableHandles.create(arg);
282283
};
283284

284-
let miVarObjToVariable = (varObj: any): DebugProtocol.Variable => {
285-
const evaluateName = MINode.valueOf(varObj, "name");
286-
const value = MINode.valueOf(varObj, "value");
287-
const numChild = parseInt(MINode.valueOf(varObj, "numchild"));
288-
const dynamic = MINode.valueOf(varObj, "dynamic") || 0;
289-
let displayHint, hasMore;
290-
if (dynamic) {
291-
displayHint = MINode.valueOf(varObj, "displayhint");
292-
hasMore = parseInt(MINode.valueOf(varObj, "has_more"));
285+
let findOrCreateVariable = (varObj: VariableObject): number => {
286+
let id: number;
287+
if (this.variableHandlesReverse.hasOwnProperty(varObj.name)) {
288+
id = this.variableHandlesReverse[varObj.name];
293289
}
294-
const isCompound = numChild > 0 ||
295-
value === "{...}" ||
296-
(dynamic > 0 && (displayHint === "array" || displayHint === "map"));
297-
298-
let res = {
299-
name: MINode.valueOf(varObj, "exp"),
300-
evaluateName,
301-
type: MINode.valueOf(varObj, "type"),
302-
value: value || "<value>",
303-
variablesReference: isCompound ? createVariable(evaluateName) : 0
304-
} as DebugProtocol.Variable;
305-
return res;
290+
else {
291+
id = createVariable(varObj);
292+
this.variableHandlesReverse[varObj.name] = id;
293+
}
294+
return varObj.isCompound() ? id : 0;
306295
};
307296

308297
if (typeof id == "number") {
@@ -311,10 +300,26 @@ export class MI2DebugSession extends DebugSession {
311300
stack = await this.miDebugger.getStackVariables(this.threadID, id);
312301
for (const variable of stack) {
313302
try {
314-
const varObj = await this.miDebugger.varCreate(variable.name);
315-
let v = miVarObjToVariable(varObj.resultRecords.results);
316-
v.name = variable.name;
317-
variables.push(v);
303+
let varObj: VariableObject;
304+
try {
305+
const changes = await this.miDebugger.varUpdate(variable.name);
306+
const changelist = changes.result("changelist");
307+
changelist.forEach((change) => {
308+
const name = MINode.valueOf(change, "name");
309+
const vId = this.variableHandlesReverse[variable.name];
310+
const v = this.variableHandles.get(vId) as any;
311+
v.applyChanges(change);
312+
});
313+
const varId = this.variableHandlesReverse[variable.name];
314+
varObj = this.variableHandles.get(varId) as any;
315+
}
316+
catch (err) {
317+
varObj = await this.miDebugger.varCreate(variable.name, variable.name);
318+
const varId = findOrCreateVariable(varObj);
319+
varObj.exp = variable.name;
320+
varObj.id = varId;
321+
}
322+
variables.push(varObj.toProtocolVariable());
318323
}
319324
catch (err) {
320325
variables.push({
@@ -333,27 +338,28 @@ export class MI2DebugSession extends DebugSession {
333338
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
334339
}
335340
}
336-
else if (typeof id == "string") {
337-
// Variable members
338-
let listChildren;
339-
try {
340-
listChildren = await this.miDebugger.varListChildren(id);
341-
const children: any[] = listChildren.result("children");
342-
// TODO: use hasMore when it's > 0
343-
// const hasMore = parseInt(listChildren.result("has_more"));
344-
const vars = children.map(child => miVarObjToVariable(child[1]));
341+
else if (typeof id == "object") {
342+
if (id instanceof VariableObject) {
343+
// Variable members
344+
let children: VariableObject[];
345+
try {
346+
children = await this.miDebugger.varListChildren(id.name);
347+
const vars = children.map(child => {
348+
const varId = findOrCreateVariable(child);
349+
child.id = varId;
350+
return child.toProtocolVariable();
351+
});
345352

346-
response.body = {
347-
variables: vars
353+
response.body = {
354+
variables: vars
355+
}
356+
this.sendResponse(response);
357+
}
358+
catch (err) {
359+
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
348360
}
349-
this.sendResponse(response);
350-
}
351-
catch (err) {
352-
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
353361
}
354-
}
355-
else if (typeof id == "object") {
356-
if (id instanceof ExtendedVariable) {
362+
else if (id instanceof ExtendedVariable) {
357363
let varReq = id;
358364
if (varReq.options.arg) {
359365
let strArr = [];

0 commit comments

Comments
 (0)