Enhance debugging support by adding type information to evaluation results and updating visualization backend to handle additional type parameters
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
target
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
@@ -87,4 +88,4 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|||||||
16
demos/lists/python/.vscode/launch.json
vendored
Normal file
16
demos/lists/python/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Python Debugger: Current File",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
21
demos/lists/rust/.vscode/launch.json
vendored
Normal file
21
demos/lists/rust/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug list_example",
|
||||||
|
"program": "${workspaceFolder}/target/debug/list_example",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "cargo build",
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"miDebuggerPath": "/usr/bin/rust-gdb",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"externalConsole": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
demos/lists/rust/.vscode/tasks.json
vendored
Normal file
17
demos/lists/rust/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cargo",
|
||||||
|
"command": "build",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$rustc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"label": "cargo build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
demos/lists/rust/Cargo.lock
generated
Normal file
7
demos/lists/rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "list_example"
|
||||||
|
version = "0.1.0"
|
||||||
@@ -206,7 +206,9 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
primary.result,
|
primary.result,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
visited
|
visited,
|
||||||
|
undefined,
|
||||||
|
primary.type
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
nodes.set("root", {
|
nodes.set("root", {
|
||||||
@@ -321,7 +323,9 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
v.value,
|
v.value,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
visited
|
visited,
|
||||||
|
undefined,
|
||||||
|
v.type
|
||||||
);
|
);
|
||||||
roots.push({ name: v.name, value: id, kind: "local", type: v.type });
|
roots.push({ name: v.name, value: id, kind: "local", type: v.type });
|
||||||
} else {
|
} else {
|
||||||
@@ -430,10 +434,15 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recursively walks a DAP variablesReference into `nodes`/`edges`,
|
// Recursively walks a DAP variablesReference into `nodes`/`edges`,
|
||||||
// returning the id (a real address when one is present in the variable's
|
// returning the id of the node it produced -- a real address when one
|
||||||
// own printed value, else a synthesized id) of the node it produced --
|
// is present in the variable's own printed value, else the DAP
|
||||||
// mirrors the old scripts' hex(ptr_int)/id(obj)-derived node ids, giving
|
// variablesReference itself (verified against a real debugpy session,
|
||||||
// the same cross-reference cycle dedup via `visited`. Routes to either a
|
// including through a genuine self-referential cycle, to be stably
|
||||||
|
// reused for the same underlying object -- not just an arbitrary
|
||||||
|
// per-call counter, which would neither dedup re-visits of the same
|
||||||
|
// addressless object nor terminate a true cycle). Mirrors the old
|
||||||
|
// scripts' hex(ptr_int)/id(obj)-derived node ids, giving the same
|
||||||
|
// cross-reference cycle dedup via `visited`. Routes to either a
|
||||||
// regular struct node or an array-table node depending on shape, so an
|
// regular struct node or an array-table node depending on shape, so an
|
||||||
// array of structs gets the compact table rendering everywhere it
|
// array of structs gets the compact table rendering everywhere it
|
||||||
// appears -- as the primary root, a struct's own field, a local, or a
|
// appears -- as the primary root, a struct's own field, a local, or a
|
||||||
@@ -444,9 +453,10 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
nodes: Map<string, GraphNodeData>,
|
nodes: Map<string, GraphNodeData>,
|
||||||
edges: GraphEdge[],
|
edges: GraphEdge[],
|
||||||
visited: Set<string>,
|
visited: Set<string>,
|
||||||
prefetchedChildren?: DapVariable[]
|
prefetchedChildren?: DapVariable[],
|
||||||
|
ownType?: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const id = this.extractAddress(ownValue) || `synthetic:${nodes.size}`;
|
const id = this.extractAddress(ownValue) || `vref:${variablesReference}`;
|
||||||
if (visited.has(id)) {
|
if (visited.has(id)) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -458,7 +468,7 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
if (this.looksLikeArrayOfStructs(children)) {
|
if (this.looksLikeArrayOfStructs(children)) {
|
||||||
await this.buildArrayTableNode(id, children, nodes, edges, visited);
|
await this.buildArrayTableNode(id, children, nodes, edges, visited);
|
||||||
} else {
|
} else {
|
||||||
await this.buildStructFields(id, children, nodes, edges, visited);
|
await this.buildStructFields(id, children, nodes, edges, visited, ownType);
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -468,13 +478,14 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
children: DapVariable[],
|
children: DapVariable[],
|
||||||
nodes: Map<string, GraphNodeData>,
|
nodes: Map<string, GraphNodeData>,
|
||||||
edges: GraphEdge[],
|
edges: GraphEdge[],
|
||||||
visited: Set<string>
|
visited: Set<string>,
|
||||||
|
ownType?: string
|
||||||
) {
|
) {
|
||||||
const fields: NodeField[] = [];
|
const fields: NodeField[] = [];
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
await this.addField(child, fields, edges, nodes, visited, id, 0);
|
await this.addField(child, fields, edges, nodes, visited, id, 0, ownType);
|
||||||
}
|
}
|
||||||
nodes.set(id, { id, fields });
|
nodes.set(id, { id, fields, type: ownType });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds a single node holding every element's members as columns
|
// Builds a single node holding every element's members as columns
|
||||||
@@ -519,7 +530,9 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
m.value,
|
m.value,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
visited
|
visited,
|
||||||
|
undefined,
|
||||||
|
m.type
|
||||||
);
|
);
|
||||||
edges.push({
|
edges.push({
|
||||||
from: id,
|
from: id,
|
||||||
@@ -566,26 +579,50 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
nodes: Map<string, GraphNodeData>,
|
nodes: Map<string, GraphNodeData>,
|
||||||
visited: Set<string>,
|
visited: Set<string>,
|
||||||
ownerId: string,
|
ownerId: string,
|
||||||
depth: number
|
depth: number,
|
||||||
|
ownerType?: string
|
||||||
) {
|
) {
|
||||||
const addr = this.extractAddress(child.value);
|
const addr = this.extractAddress(child.value);
|
||||||
if (addr && this.looksLikePointer(child)) {
|
// A field whose *static type* matches the type of the struct that
|
||||||
fields.push({
|
// directly contains it (not necessarily the original top-level
|
||||||
name: child.name,
|
// owner -- see the recursive call below, which passes child.type
|
||||||
value: addr,
|
// as the new ownerType) can only be sound if it's some kind of
|
||||||
isPointer: true,
|
// reference/handle rather than a true by-value member: a value type
|
||||||
typeHint: child.type,
|
// genuinely containing itself would have infinite size. This is
|
||||||
});
|
// what catches Python's (and similarly Java/C#'s) recursive
|
||||||
|
// dataclasses/objects, which never print an address in their repr
|
||||||
|
// (looksLikePointer's <object at 0x...> check only fires for
|
||||||
|
// classes that haven't overridden __repr__ -- dataclasses always
|
||||||
|
// do) and so would otherwise fall through to the inline-flatten
|
||||||
|
// path below and get capped/mangled at INLINE_FLATTEN_MAX_DEPTH
|
||||||
|
// instead of forming a proper linked node graph.
|
||||||
|
const isSelfReferential = !!ownerType && !!child.type && child.type === ownerType;
|
||||||
|
if ((addr && this.looksLikePointer(child)) || isSelfReferential) {
|
||||||
|
let targetId: string | undefined;
|
||||||
if (child.variablesReference > 0) {
|
if (child.variablesReference > 0) {
|
||||||
const targetId = await this.walkNodeOrArrayTable(
|
targetId = await this.walkNodeOrArrayTable(
|
||||||
child.variablesReference,
|
child.variablesReference,
|
||||||
child.value,
|
child.value,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
visited
|
visited,
|
||||||
|
undefined,
|
||||||
|
child.type
|
||||||
);
|
);
|
||||||
edges.push({ from: ownerId, to: targetId, label: child.name });
|
edges.push({ from: ownerId, to: targetId, label: child.name });
|
||||||
}
|
}
|
||||||
|
fields.push({
|
||||||
|
name: child.name,
|
||||||
|
// Real pointers show their address; a self-referential
|
||||||
|
// field with no address of its own shows the id of the
|
||||||
|
// node it resolves to instead (still a stable, clickable
|
||||||
|
// cross-reference) -- child.value as a last-resort fallback
|
||||||
|
// covers a null/childless self-typed field (unusual, but
|
||||||
|
// keeps this from rendering an empty value).
|
||||||
|
value: addr || targetId || child.value,
|
||||||
|
isPointer: true,
|
||||||
|
typeHint: child.type,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +663,8 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
|||||||
nodes,
|
nodes,
|
||||||
visited,
|
visited,
|
||||||
ownerId,
|
ownerId,
|
||||||
depth + 1
|
depth + 1,
|
||||||
|
child.type
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class DebugSessionProxy {
|
|||||||
expression: string;
|
expression: string;
|
||||||
frameId: number | undefined;
|
frameId: number | undefined;
|
||||||
context: "watch" | "repl" | "copy";
|
context: "watch" | "repl" | "copy";
|
||||||
}): Promise<{ result: string; variablesReference: number }> {
|
}): Promise<{ result: string; variablesReference: number; type?: string }> {
|
||||||
const reply = await this.session.customRequest("evaluate", {
|
const reply = await this.session.customRequest("evaluate", {
|
||||||
expression: args.expression,
|
expression: args.expression,
|
||||||
frameId: args.frameId,
|
frameId: args.frameId,
|
||||||
@@ -94,6 +94,7 @@ export class DebugSessionProxy {
|
|||||||
return {
|
return {
|
||||||
result: reply.result,
|
result: reply.result,
|
||||||
variablesReference: reply.variablesReference,
|
variablesReference: reply.variablesReference,
|
||||||
|
type: reply.type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user