diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
index e8a583c588..3dc739887c 100644
--- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
+++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
@@ -94,7 +94,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private bool isSameAddress(PositionedNode node1, PositionedNode node2)
{
- return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress();
+ return node1.ObjectNode.DebuggerValue.GetObjectAddress() == node2.ObjectNode.DebuggerValue.GetObjectAddress();
}
}
}
diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
index 5daaabf17d..87ca058879 100644
--- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
+++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
@@ -43,10 +43,13 @@ namespace Debugger.AddIn.Visualizers.Graph
/// The underlying debugger service used for getting expression values.
///
private WindowsDebugger debuggerService;
+
+ private ObjectGraph resultGraph;
///
- /// The resulting object graph.
+ /// Underlying object graph data struture.
///
- private ObjectGraph resultGraph;
+ public ObjectGraph ResultGraph { get { return this.resultGraph; } }
+
///
/// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee.
///
@@ -60,7 +63,7 @@ namespace Debugger.AddIn.Visualizers.Graph
///
/// Binding flags for getting member expressions.
///
- private readonly Debugger.MetaData.BindingFlags _bindingFlags =
+ private readonly Debugger.MetaData.BindingFlags _bindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty;
///
@@ -92,37 +95,52 @@ namespace Debugger.AddIn.Visualizers.Graph
// empty graph for null expression
if (!debuggerService.GetValueFromName(expression).IsNull)
{
- resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes);
+ //resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes);
+ resultGraph.Root = createNewNode(debuggerService.GetValueFromName(expression).GetPermanentReference());
+ loadNodeProperties(resultGraph.Root);
+ loadChildrenRecursive(resultGraph.Root, expandedNodes);
}
return resultGraph;
}
+ public ObjectNode ObtainNodeForExpression(Expression expr, out bool createdNewNode)
+ {
+ return ObtainNodeForValue(getPermanentReference(expr), out createdNewNode);
+ }
+
+ public ObjectNode ObtainNodeForExpression(Expression expr)
+ {
+ bool createdNewNode; // ignore (caller is not interested, otherwise he would use the other overload)
+ return ObtainNodeForExpression(expr, out createdNewNode);
+ }
+
///
- /// Builds the subgraph representing given value.
+ /// Returns node in the graph that represents given value, or returns new node if no node found.
///
- /// The Value for which the subgraph will be built.
- /// ObjectNode representing the value + all recursive members.
- private ObjectNode buildGraphRecursive(Value rootValue, ExpandedNodes expandedNodes)
+ /// Value for which to obtain the node/
+ /// True if new node was created, false if existing node was returned.
+ public ObjectNode ObtainNodeForValue(Value value, out bool createdNew)
{
- ObjectNode thisNode = createNewNode(rootValue);
-
- // David: by calling this, we get an array of values, most of them probably invalid,
- // it would be nice to be able to get a collection of 'values'
- // that are valid (basically a snapshot of object's state)
- // - a collection of custom objects,
- // that contain the string value and DebugType,
- // and can enumerate child values.
- // It would be also nice to return IEnumerable or ReadonlyCollection
- // http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx
- /*string[] memberValues = rootValue.GetMemberValuesAsString(_bindingFlags);
- foreach (string memberValue in memberValues)
+ createdNew = false;
+ ObjectNode nodeForValue = getExistingNodeForValue(value);
+ if (nodeForValue == null)
{
- //Value memberValuePerm = memberValue.GetPermanentReference();
-
- }*/
-
- foreach(Expression memberExpr in rootValue.Expression.AppendObjectMembers(rootValue.Type, _bindingFlags))
+ // if no node for memberValue exists, create it
+ nodeForValue = createNewNode(value);
+ loadNodeProperties(nodeForValue);
+ createdNew = true;
+ }
+ return nodeForValue;
+ }
+
+ ///
+ /// Fills node contents by adding properties.
+ ///
+ ///
+ private void loadNodeProperties(ObjectNode thisNode)
+ {
+ foreach(Expression memberExpr in thisNode.DebuggerValue.Expression.AppendObjectMembers(thisNode.DebuggerValue.Type, _bindingFlags))
{
checkIsOfSupportedType(memberExpr);
@@ -131,34 +149,48 @@ namespace Debugger.AddIn.Visualizers.Graph
{
// atomic members are added to the list of node's "properties"
string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString;
- thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr);
+ thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr);
}
else
{
+ // for object members, complex properties are added
ObjectNode targetNode = null;
bool memberIsNull = isNull(memberExpr);
- if (!memberIsNull && expandedNodes.IsExpanded(memberExpr.Code))
- {
- // for object members, edges are added
- Value memberValue = getPermanentReference(memberExpr);
-
- // if node for memberValue already exists, only add edge to it (so loops etc. are solved correctly)
- targetNode = getNodeForValue(memberValue);
- if (targetNode == null)
- {
- // if no node for memberValue exists, build the subgraph for the value
- targetNode = buildGraphRecursive(memberValue, expandedNodes);
- }
- }
- else
+ thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull);
+ }
+ }
+ }
+
+ ///
+ /// Creates child nodes of this node for each complex property and connects them to property.TargetNode.
+ ///
+ ///
+ ///
+ private void loadChildrenRecursive(ObjectNode thisNode, ExpandedNodes expandedNodes)
+ {
+ foreach(ObjectProperty complexProperty in thisNode.ComplexProperties)
+ {
+ Expression memberExpr = complexProperty.Expression;
+ ObjectNode targetNode = null;
+ if (!complexProperty.IsNull && expandedNodes.IsExpanded(memberExpr.Code))
+ {
+ Value memberValue = getPermanentReference(memberExpr);
+
+ bool createdNew;
+ // get existing node (loop) or create new
+ targetNode = ObtainNodeForValue(memberValue, out createdNew);
+ if (createdNew)
{
- targetNode = null;
+ // if member node is new, recursively build its subtree
+ loadChildrenRecursive(targetNode, expandedNodes);
}
- thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull);
}
+ else
+ {
+ targetNode = null;
+ }
+ complexProperty.TargetNode = targetNode;
}
-
- return thisNode;
}
///
@@ -175,9 +207,9 @@ namespace Debugger.AddIn.Visualizers.Graph
// remember this node's hashcode for quick lookup
objectNodesForHashCode.Add(newNode.HashCode, newNode);
- // permanent reference to the object this node represents is useful for graph building,
+ // permanent reference to the object this node represents is useful for graph building,
// and matching nodes in animations
- newNode.PermanentReference = permanentReference;
+ newNode.DebuggerValue = permanentReference;
return newNode;
}
@@ -187,7 +219,7 @@ namespace Debugger.AddIn.Visualizers.Graph
///
/// Valid value representing an instance.
///
- private ObjectNode getNodeForValue(Value value)
+ private ObjectNode getExistingNodeForValue(Value value)
{
int objectHashCode = invokeGetHashCode(value);
// are there any nodes with the same hash code?
@@ -202,7 +234,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier)
ulong objectAddress = value.GetObjectAddress();
ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find(
- node => { return objectAddress == node.PermanentReference.GetObjectAddress(); } );
+ node => { return node.DebuggerValue.GetObjectAddress() == objectAddress; } );
return nodeWithSameAddress;
}
}
@@ -251,7 +283,7 @@ namespace Debugger.AddIn.Visualizers.Graph
}
#region Expression helpers
-
+
private Value getPermanentReference(Expression expr)
{
return expr.Evaluate(debuggerService.DebuggedProcess).GetPermanentReference();
diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
index 61ecc65193..562fad418c 100644
--- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
+++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
@@ -17,10 +17,17 @@ namespace Debugger.AddIn.Visualizers.Graph
public class ObjectNode
{
///
- /// Additional info useful for internal algorithms, not to be visible to the user.
+ /// Permanent reference to the value in the the debugee this node represents.
+ ///
+ internal Debugger.Value DebuggerValue { get; set; }
+ ///
+ /// Hash code in the debuggee of the DebuggerValue this node represents.
///
- internal Debugger.Value PermanentReference { get; set; }
internal int HashCode { get; set; }
+ ///
+ /// Expression used to obtain this node.
+ ///
+ public Expressions.Expression Expression { get { return this.DebuggerValue.Expression; } }
/*private List _edges = new List();
///
diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
index 08457c8a09..05efc25f66 100644
--- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
+++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
@@ -31,9 +31,10 @@ namespace Debugger.AddIn.Visualizers.Graph
private WindowsDebugger debuggerService;
private EnumViewModel layoutViewModel;
private ObjectGraph objectGraph;
+ private ObjectGraphBuilder objectGraphBuilder;
- private PositionedGraph oldGraph;
- private PositionedGraph currentGraph;
+ private PositionedGraph oldPosGraph;
+ private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer;
///
@@ -91,42 +92,43 @@ namespace Debugger.AddIn.Visualizers.Graph
void refreshGraph()
{
- ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(debuggerService);
- this.objectGraph = null;
-
+ this.objectGraph = rebuildGraph();
+ layoutGraph(this.objectGraph);
+ //GraphDrawer drawer = new GraphDrawer(graph);
+ //drawer.Draw(canvas);
+ }
+
+ ObjectGraph rebuildGraph()
+ {
+ this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
try
{
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
- this.objectGraph = graphBuilder.BuildGraphForExpression(txtExpression.Text, expandedNodes);
+ return this.objectGraphBuilder.BuildGraphForExpression(txtExpression.Text, this.expandedNodes);
}
catch(DebuggerVisualizerException ex)
{
guiHandleException(ex);
- return;
+ return null;
}
catch(Debugger.GetValueException ex)
{
guiHandleException(ex);
- return;
+ return null;
}
-
- layoutGraph(this.objectGraph);
-
- //GraphDrawer drawer = new GraphDrawer(graph);
- //drawer.Draw(canvas);
- }
+ }
void layoutGraph(ObjectGraph graph)
{
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
Layout.TreeLayouter layouter = new Layout.TreeLayouter();
- this.oldGraph = this.currentGraph;
- this.currentGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes);
- registerExpandCollapseEvents(this.currentGraph);
+ this.oldPosGraph = this.currentPosGraph;
+ this.currentPosGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes);
+ registerExpandCollapseEvents(this.currentPosGraph);
- var graphDiff = new GraphMatcher().MatchGraphs(oldGraph, currentGraph);
- this.graphDrawer.StartAnimation(oldGraph, currentGraph, graphDiff);
+ var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
+ this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(currentGraph);
}
@@ -146,14 +148,22 @@ namespace Debugger.AddIn.Visualizers.Graph
void node_Expanded(object sender, PositionedPropertyEventArgs e)
{
+ // remember this property is expanded (for later graph rebuilds)
expandedNodes.SetExpanded(e.Property.Expression.Code);
- refreshGraph();
+
+ // add edge (+ possibly node) to underlying object graph (no need to rebuild)
+ e.Property.ObjectProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
+ layoutGraph(this.objectGraph);
}
void node_Collapsed(object sender, PositionedPropertyEventArgs e)
{
+ // remember this property is collapsed (for later graph rebuilds)
expandedNodes.SetCollapsed(e.Property.Expression.Code);
- refreshGraph();
+
+ // just remove edge from underlying object graph (no need to rebuild)
+ e.Property.ObjectProperty.TargetNode = null;
+ layoutGraph(this.objectGraph);
}
}
}
\ No newline at end of file