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
|
||||
*.log
|
||||
target
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -87,4 +88,4 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$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,
|
||||
nodes,
|
||||
edges,
|
||||
visited
|
||||
visited,
|
||||
undefined,
|
||||
primary.type
|
||||
);
|
||||
} else {
|
||||
nodes.set("root", {
|
||||
@@ -321,7 +323,9 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
v.value,
|
||||
nodes,
|
||||
edges,
|
||||
visited
|
||||
visited,
|
||||
undefined,
|
||||
v.type
|
||||
);
|
||||
roots.push({ name: v.name, value: id, kind: "local", type: v.type });
|
||||
} else {
|
||||
@@ -430,10 +434,15 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
}
|
||||
|
||||
// Recursively walks a DAP variablesReference into `nodes`/`edges`,
|
||||
// returning the id (a real address when one is present in the variable's
|
||||
// own printed value, else a synthesized id) of the node it produced --
|
||||
// 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
|
||||
// returning the id of the node it produced -- a real address when one
|
||||
// is present in the variable's own printed value, else the DAP
|
||||
// variablesReference itself (verified against a real debugpy session,
|
||||
// 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
|
||||
// array of structs gets the compact table rendering everywhere it
|
||||
// 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>,
|
||||
edges: GraphEdge[],
|
||||
visited: Set<string>,
|
||||
prefetchedChildren?: DapVariable[]
|
||||
prefetchedChildren?: DapVariable[],
|
||||
ownType?: string
|
||||
): Promise<string> {
|
||||
const id = this.extractAddress(ownValue) || `synthetic:${nodes.size}`;
|
||||
const id = this.extractAddress(ownValue) || `vref:${variablesReference}`;
|
||||
if (visited.has(id)) {
|
||||
return id;
|
||||
}
|
||||
@@ -458,7 +468,7 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
if (this.looksLikeArrayOfStructs(children)) {
|
||||
await this.buildArrayTableNode(id, children, nodes, edges, visited);
|
||||
} else {
|
||||
await this.buildStructFields(id, children, nodes, edges, visited);
|
||||
await this.buildStructFields(id, children, nodes, edges, visited, ownType);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
@@ -468,13 +478,14 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
children: DapVariable[],
|
||||
nodes: Map<string, GraphNodeData>,
|
||||
edges: GraphEdge[],
|
||||
visited: Set<string>
|
||||
visited: Set<string>,
|
||||
ownType?: string
|
||||
) {
|
||||
const fields: NodeField[] = [];
|
||||
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
|
||||
@@ -519,7 +530,9 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
m.value,
|
||||
nodes,
|
||||
edges,
|
||||
visited
|
||||
visited,
|
||||
undefined,
|
||||
m.type
|
||||
);
|
||||
edges.push({
|
||||
from: id,
|
||||
@@ -566,26 +579,50 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
nodes: Map<string, GraphNodeData>,
|
||||
visited: Set<string>,
|
||||
ownerId: string,
|
||||
depth: number
|
||||
depth: number,
|
||||
ownerType?: string
|
||||
) {
|
||||
const addr = this.extractAddress(child.value);
|
||||
if (addr && this.looksLikePointer(child)) {
|
||||
fields.push({
|
||||
name: child.name,
|
||||
value: addr,
|
||||
isPointer: true,
|
||||
typeHint: child.type,
|
||||
});
|
||||
// A field whose *static type* matches the type of the struct that
|
||||
// directly contains it (not necessarily the original top-level
|
||||
// owner -- see the recursive call below, which passes child.type
|
||||
// as the new ownerType) can only be sound if it's some kind of
|
||||
// reference/handle rather than a true by-value member: a value 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) {
|
||||
const targetId = await this.walkNodeOrArrayTable(
|
||||
targetId = await this.walkNodeOrArrayTable(
|
||||
child.variablesReference,
|
||||
child.value,
|
||||
nodes,
|
||||
edges,
|
||||
visited
|
||||
visited,
|
||||
undefined,
|
||||
child.type
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -626,7 +663,8 @@ export class NodeTableGraphVisualizationBackend extends VisualizationBackendBase
|
||||
nodes,
|
||||
visited,
|
||||
ownerId,
|
||||
depth + 1
|
||||
depth + 1,
|
||||
child.type
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -85,7 +85,7 @@ export class DebugSessionProxy {
|
||||
expression: string;
|
||||
frameId: number | undefined;
|
||||
context: "watch" | "repl" | "copy";
|
||||
}): Promise<{ result: string; variablesReference: number }> {
|
||||
}): Promise<{ result: string; variablesReference: number; type?: string }> {
|
||||
const reply = await this.session.customRequest("evaluate", {
|
||||
expression: args.expression,
|
||||
frameId: args.frameId,
|
||||
@@ -94,6 +94,7 @@ export class DebugSessionProxy {
|
||||
return {
|
||||
result: reply.result,
|
||||
variablesReference: reply.variablesReference,
|
||||
type: reply.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user