Merge branch 'master' of github.com:WebFreak001/code-debug

This commit is contained in:
WebFreak001 2017-05-26 23:21:49 +02:00
commit 7aec463e19
7 changed files with 402 additions and 118 deletions

View file

@ -101,6 +101,16 @@
"description": "Additional arguments to pass to GDB", "description": "Additional arguments to pass to GDB",
"default": [] "default": []
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all GDB calls to the console", "description": "Prints all GDB calls to the console",
@ -192,6 +202,16 @@
"description": "If true this will connect to a gdbserver instead of attaching to a PID", "description": "If true this will connect to a gdbserver instead of attaching to a PID",
"default": false "default": false
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all GDB calls to the console", "description": "Prints all GDB calls to the console",
@ -466,6 +486,16 @@
"description": "Additional arguments to pass to LLDB", "description": "Additional arguments to pass to LLDB",
"default": [] "default": []
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all lldb calls to the console", "description": "Prints all lldb calls to the console",
@ -552,6 +582,16 @@
"type": "string", "type": "string",
"description": "PID of running program or program name" "description": "PID of running program or program name"
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all LLDB calls to the console", "description": "Prints all LLDB calls to the console",
@ -713,6 +753,16 @@
"description": "Additional arguments to pass to mago", "description": "Additional arguments to pass to mago",
"default": [] "default": []
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all mago calls to the console", "description": "Prints all mago calls to the console",
@ -739,6 +789,16 @@
"type": "string", "type": "string",
"description": "PID of running program or program name" "description": "PID of running program or program name"
}, },
"valuesFormatting": {
"type": "string",
"description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any",
"default": "parseText",
"enum": [
"disabled",
"parseText",
"prettyPrinters"
]
},
"printCalls": { "printCalls": {
"type": "boolean", "type": "boolean",
"description": "Prints all mago calls to the console", "description": "Prints all mago calls to the console",
@ -834,4 +894,4 @@
"@types/node": "^7.0.5", "@types/node": "^7.0.5",
"@types/mocha": "^2.2.39" "@types/mocha": "^2.2.39"
} }
} }

View file

@ -1,3 +1,8 @@
import { MINode } from "./mi_parse";
import { DebugProtocol } from "vscode-debugprotocol/lib/debugProtocol";
export type ValuesFormattingMode = "disabled" | "parseText" | "prettyPrinters";
export interface Breakpoint { export interface Breakpoint {
file?: string; file?: string;
line?: number; line?: number;
@ -59,4 +64,62 @@ export interface IBackend {
isReady(): boolean; isReady(): boolean;
changeVariable(name: string, rawValue: string): Thenable<any>; changeVariable(name: string, rawValue: string): Thenable<any>;
examineMemory(from: number, to: number): 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;
}
}

View file

@ -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 * as ChildProcess from "child_process"
import { EventEmitter } from "events" import { EventEmitter } from "events"
import { parseMI, MINode } from '../mi_parse'; import { parseMI, MINode } from '../mi_parse';
@ -196,6 +196,9 @@ export class MI2 extends EventEmitter implements IBackend {
]; ];
if (!attach) if (!attach)
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")); cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
if (this.prettyPrint)
cmds.push(this.sendCommand("enable-pretty-printing"));
return cmds; return cmds;
} }
@ -617,27 +620,25 @@ export class MI2 extends EventEmitter implements IBackend {
}); });
} }
getStackVariables(thread: number, frame: number): Thenable<Variable[]> { async getStackVariables(thread: number, frame: number): Promise<Variable[]> {
if (trace) if (trace)
this.log("stderr", "getStackVariables"); this.log("stderr", "getStackVariables");
return new Promise((resolve, reject) => {
this.sendCommand("stack-list-variables --thread " + thread + " --frame " + frame + " --simple-values").then((result) => { const result = await this.sendCommand(`stack-list-variables --thread ${thread} --frame ${frame} --simple-values`);
let variables = result.result("variables"); const variables = result.result("variables");
let ret: Variable[] = []; let ret: Variable[] = [];
variables.forEach(element => { for (const element of variables) {
const key = MINode.valueOf(element, "name"); const key = MINode.valueOf(element, "name");
const value = MINode.valueOf(element, "value"); const value = MINode.valueOf(element, "value");
const type = MINode.valueOf(element, "type"); const type = MINode.valueOf(element, "type");
ret.push({ ret.push({
name: key, name: key,
valueStr: value, valueStr: value,
type: type, type: type,
raw: element raw: element
}); });
}); }
resolve(ret); return ret;
}, reject);
});
} }
examineMemory(from: number, length: number): Thenable<any> { examineMemory(from: number, length: number): Thenable<any> {
@ -660,6 +661,41 @@ export class MI2 extends EventEmitter implements IBackend {
}); });
} }
async varCreate(expression: string, name: string = "-"): Promise<VariableObject> {
if (trace)
this.log("stderr", "varCreate");
const res = await this.sendCommand(`var-create ${name} @ "${expression}"`);
return new VariableObject(res.result(""));
}
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
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}`)
}
async varAssign(name: string, rawValue: string): Promise<MINode> {
if (trace)
this.log("stderr", "varAssign");
return this.sendCommand(`var-assign ${name} ${rawValue}`);
}
logNoNewLine(type: string, msg: string) { logNoNewLine(type: string, msg: string) {
this.emit("msg", type, msg); this.emit("msg", type, msg);
} }
@ -710,6 +746,7 @@ export class MI2 extends EventEmitter implements IBackend {
return this.isSSH ? this.sshReady : !!this.process; return this.isSSH ? this.sshReady : !!this.process;
} }
prettyPrint: boolean = true;
printCalls: boolean; printCalls: boolean;
debugOutput: boolean; debugOutput: boolean;
public procEnv: any; public procEnv: any;

View file

@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase';
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol'; import { DebugProtocol } from 'vscode-debugprotocol';
import { MI2 } from "./backend/mi2/mi2"; import { MI2 } from "./backend/mi2/mi2";
import { SSHArguments } from './backend/backend'; import { SSHArguments, ValuesFormattingMode } from './backend/backend';
export interface LaunchRequestArguments { export interface LaunchRequestArguments {
cwd: string; cwd: string;
@ -14,6 +14,7 @@ export interface LaunchRequestArguments {
terminal: string; terminal: string;
autorun: string[]; autorun: string[];
ssh: SSHArguments; ssh: SSHArguments;
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -28,6 +29,7 @@ export interface AttachRequestArguments {
remote: boolean; remote: boolean;
autorun: string[]; autorun: string[];
ssh: SSHArguments; ssh: SSHArguments;
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -54,6 +56,7 @@ class GDBDebugSession extends MI2DebugSession {
this.started = false; this.started = false;
this.crashed = false; this.crashed = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
if (args.ssh !== undefined) { if (args.ssh !== undefined) {
@ -121,6 +124,7 @@ class GDBDebugSession extends MI2DebugSession {
this.needContinue = true; this.needContinue = true;
this.isSSH = false; this.isSSH = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
if (args.ssh !== undefined) { if (args.ssh !== undefined) {
@ -177,4 +181,4 @@ class GDBDebugSession extends MI2DebugSession {
} }
} }
DebugSession.run(GDBDebugSession); DebugSession.run(GDBDebugSession);

View file

@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase';
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol'; import { DebugProtocol } from 'vscode-debugprotocol';
import { MI2_LLDB } from "./backend/mi2/mi2lldb"; import { MI2_LLDB } from "./backend/mi2/mi2lldb";
import { SSHArguments } from './backend/backend'; import { SSHArguments, ValuesFormattingMode } from './backend/backend';
export interface LaunchRequestArguments { export interface LaunchRequestArguments {
cwd: string; cwd: string;
@ -13,6 +13,7 @@ export interface LaunchRequestArguments {
arguments: string; arguments: string;
autorun: string[]; autorun: string[];
ssh: SSHArguments; ssh: SSHArguments;
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -25,6 +26,7 @@ export interface AttachRequestArguments {
debugger_args: string[]; debugger_args: string[];
executable: string; executable: string;
autorun: string[]; autorun: string[];
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -49,6 +51,7 @@ class LLDBDebugSession extends MI2DebugSession {
this.started = false; this.started = false;
this.crashed = false; this.crashed = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
if (args.ssh !== undefined) { if (args.ssh !== undefined) {
@ -108,6 +111,7 @@ class LLDBDebugSession extends MI2DebugSession {
this.needContinue = true; this.needContinue = true;
this.isSSH = false; this.isSSH = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => { this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
@ -120,4 +124,4 @@ class LLDBDebugSession extends MI2DebugSession {
} }
} }
DebugSession.run(LLDBDebugSession); DebugSession.run(LLDBDebugSession);

View file

@ -2,7 +2,7 @@ import { MI2DebugSession } from './mibase';
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol'; import { DebugProtocol } from 'vscode-debugprotocol';
import { MI2_Mago } from "./backend/mi2/mi2mago"; import { MI2_Mago } from "./backend/mi2/mi2mago";
import { SSHArguments } from './backend/backend'; import { SSHArguments, ValuesFormattingMode } from './backend/backend';
export interface LaunchRequestArguments { export interface LaunchRequestArguments {
cwd: string; cwd: string;
@ -12,6 +12,7 @@ export interface LaunchRequestArguments {
debugger_args: string[]; debugger_args: string[];
arguments: string; arguments: string;
autorun: string[]; autorun: string[];
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -24,6 +25,7 @@ export interface AttachRequestArguments {
debugger_args: string[]; debugger_args: string[];
executable: string; executable: string;
autorun: string[]; autorun: string[];
valuesFormatting: ValuesFormattingMode;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -56,6 +58,7 @@ class MagoDebugSession extends MI2DebugSession {
this.started = false; this.started = false;
this.crashed = false; this.crashed = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
this.miDebugger.load(args.cwd, args.target, args.arguments, undefined).then(() => { this.miDebugger.load(args.cwd, args.target, args.arguments, undefined).then(() => {
@ -83,6 +86,7 @@ class MagoDebugSession extends MI2DebugSession {
this.needContinue = true; this.needContinue = true;
this.isSSH = false; this.isSSH = false;
this.debugReady = false; this.debugReady = false;
this.setValuesFormattingMode(args.valuesFormatting);
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => { this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
@ -95,4 +99,4 @@ class MagoDebugSession extends MI2DebugSession {
} }
} }
DebugSession.run(MagoDebugSession); DebugSession.run(MagoDebugSession);

View file

@ -1,6 +1,6 @@
import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol'; import { DebugProtocol } from 'vscode-debugprotocol';
import { Breakpoint, IBackend } from './backend/backend'; import { Breakpoint, IBackend, Variable, VariableObject, ValuesFormattingMode } from './backend/backend';
import { MINode } from './backend/mi_parse'; import { MINode } from './backend/mi_parse';
import { expandValue, isExpandable } from './backend/gdb_expansion'; import { expandValue, isExpandable } from './backend/gdb_expansion';
import { MI2 } from './backend/mi2/mi2'; import { MI2 } from './backend/mi2/mi2';
@ -18,8 +18,13 @@ class ExtendedVariable {
} }
} }
const STACK_HANDLES_START = 1000;
const VAR_HANDLES_START = 2000;
export class MI2DebugSession extends DebugSession { export class MI2DebugSession extends DebugSession {
protected variableHandles = new Handles<any>(); protected variableHandles = new Handles<string | VariableObject | ExtendedVariable>(VAR_HANDLES_START);
protected variableHandlesReverse: { [id: string]: number } = {};
protected useVarObjects: boolean;
protected quit: boolean; protected quit: boolean;
protected attached: boolean; protected attached: boolean;
protected needContinue: boolean; protected needContinue: boolean;
@ -78,6 +83,23 @@ export class MI2DebugSession extends DebugSession {
} }
} }
protected setValuesFormattingMode(mode: ValuesFormattingMode) {
switch (mode) {
case "disabled":
this.useVarObjects = true;
this.miDebugger.prettyPrint = false;
break;
case "prettyPrinters":
this.useVarObjects = true;
this.miDebugger.prettyPrint = true;
break;
case "parseText":
default:
this.useVarObjects = false;
this.miDebugger.prettyPrint = false;
}
}
protected handleMsg(type: string, msg: string) { protected handleMsg(type: string, msg: string) {
if (type == "target") if (type == "target")
type = "stdout"; type = "stdout";
@ -126,15 +148,31 @@ export class MI2DebugSession extends DebugSession {
this.sendResponse(response); this.sendResponse(response);
} }
protected setVariableRequest(response: DebugProtocol.SetVariableResponse, args: DebugProtocol.SetVariableArguments): void { protected async setVariableRequest(response: DebugProtocol.SetVariableResponse, args: DebugProtocol.SetVariableArguments): Promise<void> {
this.miDebugger.changeVariable(args.name, args.value).then(() => { try {
response.body = { if (this.useVarObjects) {
value: args.value let name = args.name;
}; if (args.variablesReference >= VAR_HANDLES_START) {
const parent = this.variableHandles.get(args.variablesReference) as VariableObject;
name = `${parent.name}.${name}`;
}
let res = await this.miDebugger.varAssign(name, args.value);
response.body = {
value: res.result("value")
};
}
else {
await this.miDebugger.changeVariable(args.name, args.value);
response.body = {
value: args.value
};
}
this.sendResponse(response); this.sendResponse(response);
}, err => { }
catch (err) {
this.sendErrorResponse(response, 11, `Could not continue: ${err}`); this.sendErrorResponse(response, 11, `Could not continue: ${err}`);
}); };
} }
protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, args: DebugProtocol.SetFunctionBreakpointsArguments): void { protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, args: DebugProtocol.SetFunctionBreakpointsArguments): void {
@ -253,7 +291,7 @@ export class MI2DebugSession extends DebugSession {
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void { protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
const scopes = new Array<Scope>(); const scopes = new Array<Scope>();
scopes.push(new Scope("Local", this.variableHandles.create("@frame:" + (args.frameId || 0)), false)); scopes.push(new Scope("Local", STACK_HANDLES_START + (parseInt(args.frameId as any) || 0), false));
response.body = { response.body = {
scopes: scopes scopes: scopes
@ -261,9 +299,15 @@ export class MI2DebugSession extends DebugSession {
this.sendResponse(response); this.sendResponse(response);
} }
protected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): void { protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise<void> {
const variables: DebugProtocol.Variable[] = []; const variables: DebugProtocol.Variable[] = [];
const id = this.variableHandles.get(args.variablesReference); let id: number | string | VariableObject | ExtendedVariable;
if (args.variablesReference < VAR_HANDLES_START) {
id = args.variablesReference - STACK_HANDLES_START;
}
else {
id = this.variableHandles.get(args.variablesReference);
}
let createVariable = (arg, options?) => { let createVariable = (arg, options?) => {
if (options) if (options)
@ -272,12 +316,57 @@ export class MI2DebugSession extends DebugSession {
return this.variableHandles.create(arg); return this.variableHandles.create(arg);
}; };
if (typeof id == "string") { let findOrCreateVariable = (varObj: VariableObject): number => {
if (id.startsWith("@frame:")) { let id: number;
this.miDebugger.getStackVariables(this.threadID, parseInt(id.substr("@frame:".length))).then(stack => { if (this.variableHandlesReverse.hasOwnProperty(varObj.name)) {
stack.forEach(variable => { id = this.variableHandlesReverse[varObj.name];
}
else {
id = createVariable(varObj);
this.variableHandlesReverse[varObj.name] = id;
}
return varObj.isCompound() ? id : 0;
};
if (typeof id == "number") {
let stack: Variable[];
try {
stack = await this.miDebugger.getStackVariables(this.threadID, id);
for (const variable of stack) {
if (this.useVarObjects) {
try {
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({
name: variable.name,
value: `<${err}>`,
variablesReference: 0
});
}
}
else {
if (variable.valueStr !== undefined) { if (variable.valueStr !== undefined) {
let expanded = expandValue(createVariable, "{" + variable.name + "=" + variable.valueStr + ")", "", variable.raw); let expanded = expandValue(createVariable, `{${variable.name}=${variable.valueStr})`, "", variable.raw);
if (expanded) { if (expanded) {
if (typeof expanded[0] == "string") if (typeof expanded[0] == "string")
expanded = [ expanded = [
@ -296,49 +385,73 @@ export class MI2DebugSession extends DebugSession {
value: "<unknown>", value: "<unknown>",
variablesReference: createVariable(variable.name) variablesReference: createVariable(variable.name)
}); });
}); }
response.body = { }
variables: variables response.body = {
}; variables: variables
this.sendResponse(response); };
}, err => { this.sendResponse(response);
this.sendErrorResponse(response, 1, "Could not expand variable: " + err);
});
} }
else { catch (err) {
// Variable members this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
this.miDebugger.evalExpression(JSON.stringify(id)).then(variable => { }
try { }
let expanded = expandValue(createVariable, variable.result("value"), id, variable); else if (typeof id == "string") {
if (!expanded) { // Variable members
this.sendErrorResponse(response, 2, `Could not expand variable`); let variable;
} try {
else { variable = await this.miDebugger.evalExpression(JSON.stringify(id));
if (typeof expanded[0] == "string") try {
expanded = [ let expanded = expandValue(createVariable, variable.result("value"), id, variable);
{ if (!expanded) {
name: "<value>", this.sendErrorResponse(response, 2, `Could not expand variable`);
value: prettyStringArray(expanded),
variablesReference: 0
}
];
response.body = {
variables: expanded
};
this.sendResponse(response);
}
} }
catch (e) { else {
this.sendErrorResponse(response, 2, `Could not expand variable: ` + e); if (typeof expanded[0] == "string")
expanded = [
{
name: "<value>",
value: prettyStringArray(expanded),
variablesReference: 0
}
];
response.body = {
variables: expanded
};
this.sendResponse(response);
} }
}, err => { }
this.sendErrorResponse(response, 1, `Could not expand variable`); catch (e) {
}); this.sendErrorResponse(response, 2, `Could not expand variable: ${e}`);
}
}
catch (err) {
this.sendErrorResponse(response, 1, `Could not expand variable: ${err}`);
} }
} }
else if (typeof id == "object") { else if (typeof id == "object") {
if (id instanceof ExtendedVariable) { if (id instanceof VariableObject) {
let varReq = <ExtendedVariable>id; // 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) { if (varReq.options.arg) {
let strArr = []; let strArr = [];
let argsPart = true; let argsPart = true;
@ -349,55 +462,54 @@ export class MI2DebugSession extends DebugSession {
}; };
this.sendResponse(response); this.sendResponse(response);
}; };
let addOne = () => { let addOne = async () => {
this.miDebugger.evalExpression(JSON.stringify(varReq.name + "+" + arrIndex + ")")).then(variable => { const variable = await this.miDebugger.evalExpression(JSON.stringify(`${varReq.name}+${arrIndex})`));
try { try {
let expanded = expandValue(createVariable, variable.result("value"), varReq.name, variable); let expanded = expandValue(createVariable, variable.result("value"), varReq.name, variable);
if (!expanded) { if (!expanded) {
this.sendErrorResponse(response, 15, `Could not expand variable`); this.sendErrorResponse(response, 15, `Could not expand variable`);
} }
else { else {
if (typeof expanded == "string") { if (typeof expanded == "string") {
if (expanded == "<nullptr>") { if (expanded == "<nullptr>") {
if (argsPart) if (argsPart)
argsPart = false; argsPart = false;
else else
return submit();
}
else if (expanded[0] != '"') {
strArr.push({
name: "[err]",
value: expanded,
variablesReference: 0
});
return submit(); return submit();
}
strArr.push({
name: "[" + (arrIndex++) + "]",
value: expanded,
variablesReference: 0
});
addOne();
} }
else { else if (expanded[0] != '"') {
strArr.push({ strArr.push({
name: "[err]", name: "[err]",
value: expanded, value: expanded,
variablesReference: 0 variablesReference: 0
}); });
submit(); return submit();
} }
strArr.push({
name: `[${(arrIndex++)}]`,
value: expanded,
variablesReference: 0
});
addOne();
}
else {
strArr.push({
name: "[err]",
value: expanded,
variablesReference: 0
});
submit();
} }
} }
catch (e) { }
this.sendErrorResponse(response, 14, `Could not expand variable: ` + e); catch (e) {
} this.sendErrorResponse(response, 14, `Could not expand variable: ${e}`);
}); }
}; };
addOne(); addOne();
} }
else else
this.sendErrorResponse(response, 13, `Unimplemented variable request options: ` + JSON.stringify(varReq.options)); this.sendErrorResponse(response, 13, `Unimplemented variable request options: ${JSON.stringify(varReq.options)}`);
} }
else { else {
response.body = { response.body = {
@ -509,4 +621,4 @@ function prettyStringArray(strings) {
return JSON.stringify(strings); return JSON.stringify(strings);
} }
else return strings; else return strings;
} }