Browse Source

Proper expanding of nodes.

Refactored ObjectGraphBuilder to better support expanding of nodes.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4291 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Martin Koníček 16 years ago
parent
commit
7d12d7c92f
  1. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
  2. 122
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
  3. 11
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
  4. 50
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

2
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) 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();
} }
} }
} }

122
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. /// The underlying debugger service used for getting expression values.
/// </summary> /// </summary>
private WindowsDebugger debuggerService; private WindowsDebugger debuggerService;
private ObjectGraph resultGraph;
/// <summary> /// <summary>
/// The resulting object graph. /// Underlying object graph data struture.
/// </summary> /// </summary>
private ObjectGraph resultGraph; public ObjectGraph ResultGraph { get { return this.resultGraph; } }
/// <summary> /// <summary>
/// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee. /// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee.
/// </summary> /// </summary>
@ -92,37 +95,52 @@ namespace Debugger.AddIn.Visualizers.Graph
// empty graph for null expression // empty graph for null expression
if (!debuggerService.GetValueFromName(expression).IsNull) 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; 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);
}
/// <summary> /// <summary>
/// Builds the subgraph representing given value. /// Returns node in the graph that represents given value, or returns new node if no node found.
/// </summary> /// </summary>
/// <param name="rootValue">The Value for which the subgraph will be built.</param> /// <param name="value">Value for which to obtain the node/</param>
/// <returns>ObjectNode representing the value + all recursive members.</returns> /// <param name="createdNew">True if new node was created, false if existing node was returned.</param>
private ObjectNode buildGraphRecursive(Value rootValue, ExpandedNodes expandedNodes) public ObjectNode ObtainNodeForValue(Value value, out bool createdNew)
{ {
ObjectNode thisNode = createNewNode(rootValue); createdNew = false;
ObjectNode nodeForValue = getExistingNodeForValue(value);
// David: by calling this, we get an array of values, most of them probably invalid, if (nodeForValue == null)
// 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)
{ {
//Value memberValuePerm = memberValue.GetPermanentReference(); // if no node for memberValue exists, create it
nodeForValue = createNewNode(value);
}*/ loadNodeProperties(nodeForValue);
createdNew = true;
}
return nodeForValue;
}
foreach(Expression memberExpr in rootValue.Expression.AppendObjectMembers(rootValue.Type, _bindingFlags)) /// <summary>
/// Fills node contents by adding properties.
/// </summary>
/// <param name="thisNode"></param>
private void loadNodeProperties(ObjectNode thisNode)
{
foreach(Expression memberExpr in thisNode.DebuggerValue.Expression.AppendObjectMembers(thisNode.DebuggerValue.Type, _bindingFlags))
{ {
checkIsOfSupportedType(memberExpr); checkIsOfSupportedType(memberExpr);
@ -131,34 +149,48 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
// atomic members are added to the list of node's "properties" // atomic members are added to the list of node's "properties"
string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString; string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString;
thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr); thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr);
} }
else else
{ {
// for object members, complex properties are added
ObjectNode targetNode = null; ObjectNode targetNode = null;
bool memberIsNull = isNull(memberExpr); 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
{
targetNode = null;
}
thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull); thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull);
} }
} }
}
return thisNode; /// <summary>
/// Creates child nodes of this node for each complex property and connects them to property.TargetNode.
/// </summary>
/// <param name="thisNode"></param>
/// <param name="expandedNodes"></param>
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)
{
// if member node is new, recursively build its subtree
loadChildrenRecursive(targetNode, expandedNodes);
}
}
else
{
targetNode = null;
}
complexProperty.TargetNode = targetNode;
}
} }
/// <summary> /// <summary>
@ -177,7 +209,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// 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 // and matching nodes in animations
newNode.PermanentReference = permanentReference; newNode.DebuggerValue = permanentReference;
return newNode; return newNode;
} }
@ -187,7 +219,7 @@ namespace Debugger.AddIn.Visualizers.Graph
/// </summary> /// </summary>
/// <param name="value">Valid value representing an instance.</param> /// <param name="value">Valid value representing an instance.</param>
/// <returns></returns> /// <returns></returns>
private ObjectNode getNodeForValue(Value value) private ObjectNode getExistingNodeForValue(Value value)
{ {
int objectHashCode = invokeGetHashCode(value); int objectHashCode = invokeGetHashCode(value);
// are there any nodes with the same hash code? // 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) // (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier)
ulong objectAddress = value.GetObjectAddress(); ulong objectAddress = value.GetObjectAddress();
ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find( ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find(
node => { return objectAddress == node.PermanentReference.GetObjectAddress(); } ); node => { return node.DebuggerValue.GetObjectAddress() == objectAddress; } );
return nodeWithSameAddress; return nodeWithSameAddress;
} }
} }

11
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 public class ObjectNode
{ {
/// <summary> /// <summary>
/// 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.
/// </summary>
internal Debugger.Value DebuggerValue { get; set; }
/// <summary>
/// Hash code in the debuggee of the DebuggerValue this node represents.
/// </summary> /// </summary>
internal Debugger.Value PermanentReference { get; set; }
internal int HashCode { get; set; } internal int HashCode { get; set; }
/// <summary>
/// Expression used to obtain this node.
/// </summary>
public Expressions.Expression Expression { get { return this.DebuggerValue.Expression; } }
/*private List<ObjectEdge> _edges = new List<ObjectEdge>(); /*private List<ObjectEdge> _edges = new List<ObjectEdge>();
/// <summary> /// <summary>

50
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 WindowsDebugger debuggerService;
private EnumViewModel<LayoutDirection> layoutViewModel; private EnumViewModel<LayoutDirection> layoutViewModel;
private ObjectGraph objectGraph; private ObjectGraph objectGraph;
private ObjectGraphBuilder objectGraphBuilder;
private PositionedGraph oldGraph; private PositionedGraph oldPosGraph;
private PositionedGraph currentGraph; private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer; private GraphDrawer graphDrawer;
/// <summary> /// <summary>
@ -91,42 +92,43 @@ namespace Debugger.AddIn.Visualizers.Graph
void refreshGraph() void refreshGraph()
{ {
ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(debuggerService); this.objectGraph = rebuildGraph();
this.objectGraph = null; layoutGraph(this.objectGraph);
//GraphDrawer drawer = new GraphDrawer(graph);
//drawer.Draw(canvas);
}
ObjectGraph rebuildGraph()
{
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
try try
{ {
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); 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) catch(DebuggerVisualizerException ex)
{ {
guiHandleException(ex); guiHandleException(ex);
return; return null;
} }
catch(Debugger.GetValueException ex) catch(Debugger.GetValueException ex)
{ {
guiHandleException(ex); guiHandleException(ex);
return; return null;
} }
}
layoutGraph(this.objectGraph);
//GraphDrawer drawer = new GraphDrawer(graph);
//drawer.Draw(canvas);
}
void layoutGraph(ObjectGraph graph) void layoutGraph(ObjectGraph graph)
{ {
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
Layout.TreeLayouter layouter = new Layout.TreeLayouter(); Layout.TreeLayouter layouter = new Layout.TreeLayouter();
this.oldGraph = this.currentGraph; this.oldPosGraph = this.currentPosGraph;
this.currentGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes); this.currentPosGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes);
registerExpandCollapseEvents(this.currentGraph); registerExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldGraph, currentGraph); var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
this.graphDrawer.StartAnimation(oldGraph, currentGraph, graphDiff); this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(currentGraph); //this.graphDrawer.Draw(currentGraph);
} }
@ -146,14 +148,22 @@ namespace Debugger.AddIn.Visualizers.Graph
void node_Expanded(object sender, PositionedPropertyEventArgs e) void node_Expanded(object sender, PositionedPropertyEventArgs e)
{ {
// remember this property is expanded (for later graph rebuilds)
expandedNodes.SetExpanded(e.Property.Expression.Code); 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) void node_Collapsed(object sender, PositionedPropertyEventArgs e)
{ {
// remember this property is collapsed (for later graph rebuilds)
expandedNodes.SetCollapsed(e.Property.Expression.Code); 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);
} }
} }
} }
Loading…
Cancel
Save