1
1
import {
2
2
DataExtractionResult ,
3
3
DataExtractorId ,
4
+ GraphNode ,
5
+ GraphVisualizationData ,
4
6
} from "@hediet/debug-visualizer-data-extraction" ;
5
7
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession" ;
6
8
import {
@@ -38,13 +40,36 @@ export class GenericEvaluator implements Evaluator {
38
40
expression,
39
41
preferredExtractorId,
40
42
} ) ;
41
- let reply ;
43
+ let reply : { result : string , variablesReference : number } ;
42
44
try {
43
45
reply = await this . session . evaluate ( {
44
46
expression : finalExpression ,
45
47
frameId,
46
48
context : this . getContext ( ) ,
47
49
} ) ;
50
+
51
+ // Use structural information about variables
52
+ // from the evaluation response if present.
53
+ if ( reply . variablesReference ) {
54
+ const graph = await this . constructGraphFromVariablesReference ( reply . result , reply . variablesReference ) ;
55
+
56
+ return {
57
+ kind : "data" ,
58
+ result : {
59
+ availableExtractors : [ ] ,
60
+ usedExtractor : {
61
+ id : "generic" as any ,
62
+ name : "Generic" ,
63
+ priority : 1 ,
64
+ } ,
65
+ data : graph ,
66
+ } ,
67
+ }
68
+ } else {
69
+ return parseEvaluationResultFromGenericDebugAdapter ( reply . result , {
70
+ debugAdapterType : this . session . session . configuration . type ,
71
+ } ) ;
72
+ }
48
73
} catch ( error ) {
49
74
return {
50
75
kind : "error" ,
@@ -65,10 +90,87 @@ export class GenericEvaluator implements Evaluator {
65
90
} ,
66
91
} ;
67
92
}
93
+ }
68
94
69
- return parseEvaluationResultFromGenericDebugAdapter ( reply . result , {
70
- debugAdapterType : this . session . session . configuration . type ,
71
- } ) ;
95
+ /**
96
+ * Constructs GraphVisualizationData from a DAP variables
97
+ * reference by successively querying the debug adapter for
98
+ * variables. Objects are considered to be equivalent if
99
+ * they share the same variables reference (this is important
100
+ * for representing cyclic relationships).
101
+ *
102
+ * @param rootLabel - The root object's label
103
+ * @param rootVariablesReference - The root object's DAP variables reference
104
+ * @param maxDepth - The maximum depth to search at
105
+ * @param maxKnownNodes - The maximum number of nodes
106
+ */
107
+ private async constructGraphFromVariablesReference (
108
+ rootLabel : string ,
109
+ rootVariablesReference : number ,
110
+ maxDepth : number = 30 ,
111
+ maxKnownNodes : number = 50 ,
112
+ ) : Promise < GraphVisualizationData > {
113
+ // Perform a breadth-first search on the object to construct the graph
114
+
115
+ const graph : GraphVisualizationData = {
116
+ kind : { graph : true } ,
117
+ nodes : [ ] ,
118
+ edges : [ ]
119
+ } ;
120
+ const knownNodeIds : { [ ref : number ] : string ; } = { } ;
121
+ const bfsQueue : { source : { id : string , name : string } | undefined , label : string , variablesReference : number , depth : number } [ ] = [ {
122
+ source : undefined ,
123
+ label : rootLabel ,
124
+ variablesReference : rootVariablesReference ,
125
+ depth : 0 ,
126
+ } ] ;
127
+
128
+ let knownCount : number = 0 ;
129
+
130
+ do {
131
+ const variable = bfsQueue . shift ( ) ! ;
132
+ const hasChilds = variable . variablesReference > 0 ;
133
+
134
+ if ( variable . depth > maxDepth ) {
135
+ break ;
136
+ }
137
+
138
+ let nodeId : string ;
139
+
140
+ if ( ! hasChilds || ! ( variable . variablesReference in knownNodeIds ) ) {
141
+ // The variable is a leaf or an unvisited object: create the node.
142
+
143
+ const node : GraphNode = {
144
+ id : hasChilds ? `${ variable . variablesReference } ` : `__${ variable . label } @${ knownCount } __` ,
145
+ label : variable . label ,
146
+ color : variable . depth == 0 ? "lightblue" : undefined ,
147
+ shape : "box" ,
148
+ } ;
149
+
150
+ graph . nodes . push ( node ) ;
151
+ knownCount ++ ;
152
+
153
+ if ( hasChilds ) {
154
+ knownNodeIds [ variable . variablesReference ] = node . id ;
155
+
156
+ for ( const child of await this . session . getVariables ( { variablesReference : variable . variablesReference } ) ) {
157
+ bfsQueue . push ( { source : { id : node . id , name : child . name } , label : child . value , variablesReference : child . variablesReference , depth : variable . depth + 1 } ) ;
158
+ }
159
+ }
160
+
161
+ nodeId = node . id ;
162
+ } else {
163
+ // The variable is a visited object (e.g. due to a cyclic reference)
164
+
165
+ nodeId = knownNodeIds [ variable . variablesReference ] ;
166
+ }
167
+
168
+ if ( variable . source ) {
169
+ graph . edges . push ( { from : variable . source . id , to : nodeId , label : variable . source . name } ) ;
170
+ }
171
+ } while ( bfsQueue . length > 0 && knownCount <= maxKnownNodes ) ;
172
+
173
+ return graph ;
72
174
}
73
175
74
176
protected getFinalExpression ( args : {
0 commit comments