diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj index 6f3c568f27..7858f8536c 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -121,7 +121,16 @@ DebuggerEventForm.cs + + + + + + + + + DebuggeeExceptionForm.cs @@ -167,7 +176,6 @@ - @@ -195,11 +203,9 @@ - - - + @@ -309,9 +315,11 @@ + + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/DebuggerVisualizerException.cs similarity index 94% rename from src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs rename to src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/DebuggerVisualizerException.cs index cc86d867f2..b1a7efb017 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/DebuggerVisualizerException.cs @@ -6,7 +6,7 @@ // using System; -namespace Debugger.AddIn.Visualizers.Graph +namespace Debugger.AddIn.Visualizers { /// /// Description of DebuggerVisualizerException. diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/IEvaluate.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/IEvaluate.cs new file mode 100644 index 0000000000..bcccf43ac6 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/IEvaluate.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers +{ + /// + /// Object that has its state partially uninitialized, + /// and can initialize its state fully on demand. + /// + public interface IEvaluate + { + /// + /// On-demand evaluation of internal state. + /// + void Evaluate(); + + /// + /// Is this object fully evaluated? + /// + bool IsEvaluated { get; } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/ObjectPropertyComparer.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/ObjectPropertyComparer.cs new file mode 100644 index 0000000000..e3fb83d9f8 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Common/ObjectPropertyComparer.cs @@ -0,0 +1,31 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Common +{ + /// + /// Provides sort ordering on ObjectProperties. + /// + class ObjectPropertyComparer : IComparer + { + public int Compare(ObjectProperty prop1, ObjectProperty prop2) + { + // order by IsAtomic, Name + int comparedAtomic = prop2.IsAtomic.CompareTo(prop1.IsAtomic); + if (comparedAtomic != 0) + { + return comparedAtomic; + } + else + { + return prop1.Name.CompareTo(prop2.Name); + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs index 481801c3a1..6327cadcb4 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs @@ -4,6 +4,7 @@ // // $Revision$ // +using Debugger.AddIn.Visualizers.Utils; using System; using System.Collections.Generic; using System.Windows; diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs index 35f5d63f93..5ce5b17f24 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs @@ -16,11 +16,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// public class PositionedNode { - private ObjectNode objectNode; + private ObjectGraphNode objectNode; /// /// Underlying ObjectNode. /// - public ObjectNode ObjectNode + public ObjectGraphNode ObjectNode { get { return objectNode; } } @@ -51,7 +51,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// Creates new PositionedNode. /// /// Underlying ObjectNode. - public PositionedNode(ObjectNode objectNode) + public PositionedNode(ObjectGraphNode objectNode) { this.objectNode = objectNode; diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs index d2f022e550..5240da4267 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs @@ -24,8 +24,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout PositionedGraph resultGraph = null; - Dictionary treeNodeFor = new Dictionary(); - Dictionary seenNodes = new Dictionary(); + Dictionary treeNodeFor = new Dictionary(); + Dictionary seenNodes = new Dictionary(); public TreeLayouter() { @@ -41,8 +41,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout layoutDirection = direction; resultGraph = new PositionedGraph(); - treeNodeFor = new Dictionary(); - seenNodes = new Dictionary(); + treeNodeFor = new Dictionary(); + seenNodes = new Dictionary(); TreeNode tree = buildTreeRecursive(objectGraph.Root, expandedNodes); calculateNodePosRecursive(tree, 0, 0); @@ -53,7 +53,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout return resultGraph; } - private TreeNode buildTreeRecursive(ObjectNode objectGraphNode, ExpandedNodes expandedNodes) + private TreeNode buildTreeRecursive(ObjectGraphNode objectGraphNode, ExpandedNodes expandedNodes) { seenNodes.Add(objectGraphNode, null); @@ -64,11 +64,13 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout treeNodeFor[objectGraphNode] = newTreeNode; double subtreeSize = 0; - foreach (ObjectGraphProperty property in objectGraphNode.Properties) + foreach (AbstractNode absNode in objectGraphNode.Content.Childs) { + ObjectGraphProperty property = ((PropertyNode)absNode).Property; + if (property.TargetNode != null) { - ObjectNode neighbor = property.TargetNode; + ObjectGraphNode neighbor = property.TargetNode; TreeNode targetTreeNode = null; bool newEdgeIsTreeEdge = false; if (seenNodes.ContainsKey(neighbor)) diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs index 39a723e3a7..a43c7f6133 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs @@ -19,7 +19,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// public abstract class TreeNode : PositionedNode { - public static TreeNode Create(LayoutDirection direction, ObjectNode objectNode) + public static TreeNode Create(LayoutDirection direction, ObjectGraphNode objectNode) { switch (direction) { case LayoutDirection.TopBottom: return new TreeNodeTB(objectNode); @@ -31,7 +31,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout public double HorizontalMargin { get; set; } public double VerticalMargin { get; set; } - protected TreeNode(ObjectNode objectNode) : base(objectNode) + protected TreeNode(ObjectGraphNode objectNode) : base(objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs index 916ea5eb4e..86ad93870c 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs @@ -14,7 +14,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// public class TreeNodeLR: TreeNode { - public TreeNodeLR(ObjectNode objectNode) : base(objectNode) + public TreeNodeLR(ObjectGraphNode objectNode) : base(objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs index ce09a53ff8..d2950bac2c 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs @@ -14,7 +14,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// public class TreeNodeTB : TreeNode { - public TreeNodeTB(ObjectNode objectNode) : base(objectNode) + public TreeNodeTB(ObjectGraphNode objectNode) : base(objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs index 49a5ad5ca1..95980df74a 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs @@ -6,7 +6,7 @@ // using System; -namespace Debugger.AddIn.Visualizers.Graph +namespace Debugger.AddIn.Visualizers { /// /// Named edge connecting 2 objects of same type. diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs index a668b6aa7e..7078174e66 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs @@ -18,22 +18,22 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// Root of the graph. /// - public ObjectNode Root { get; internal set; } + public ObjectGraphNode Root { get; internal set; } /// /// Adds node to the graph. /// /// node to be added - internal void AddNode(ObjectNode node) + internal void AddNode(ObjectGraphNode node) { _nodes.Add(node); } - private List _nodes = new List(); + private List _nodes = new List(); /// /// All nodes in the graph. /// - public IEnumerable Nodes + public IEnumerable Nodes { get { return _nodes; } } 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 8dcc6724bc..6d913edff3 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 @@ -50,20 +50,15 @@ namespace Debugger.AddIn.Visualizers.Graph /// public ObjectGraph ResultGraph { get { return this.resultGraph; } } - /// - /// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee. - /// - private MethodInfo hashCodeMethod; - /// /// Given hash code, lookup already existing node(s) with this hash code. /// - private Lookup objectNodesForHashCode = new Lookup(); + private Lookup objectNodesForHashCode = new Lookup(); /// /// Binding flags for getting member expressions. /// - private readonly Debugger.MetaData.BindingFlags _bindingFlags = + private readonly Debugger.MetaData.BindingFlags memberBindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; /// @@ -73,9 +68,6 @@ namespace Debugger.AddIn.Visualizers.Graph public ObjectGraphBuilder(WindowsDebugger debuggerService) { this.debuggerService = debuggerService; - - DebugType typeRuntimeHelpers = DebugType.Create(debuggerService.DebuggedProcess, null, "System.Runtime.CompilerServices.RuntimeHelpers"); - this.hashCodeMethod = typeRuntimeHelpers.GetMember("GetHashCode", BindingFlags.Public | BindingFlags.Static | BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; } /// @@ -97,19 +89,19 @@ namespace Debugger.AddIn.Visualizers.Graph { //resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes); resultGraph.Root = createNewNode(debuggerService.GetValueFromName(expression).GetPermanentReference()); - loadNodeProperties(resultGraph.Root); - loadChildrenRecursive(resultGraph.Root, expandedNodes); + loadContent(resultGraph.Root); + loadNeighborsRecursive(resultGraph.Root, expandedNodes); } return resultGraph; } - public ObjectNode ObtainNodeForExpression(Expression expr, out bool createdNewNode) + public ObjectGraphNode ObtainNodeForExpression(Expression expr, out bool createdNewNode) { - return ObtainNodeForValue(getPermanentReference(expr), out createdNewNode); + return ObtainNodeForValue(expr.GetPermanentReference(), out createdNewNode); } - public ObjectNode ObtainNodeForExpression(Expression expr) + public ObjectGraphNode ObtainNodeForExpression(Expression expr) { bool createdNewNode; // ignore (caller is not interested, otherwise he would use the other overload) return ObtainNodeForExpression(expr, out createdNewNode); @@ -120,62 +112,67 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// 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) + public ObjectGraphNode ObtainNodeForValue(Value value, out bool createdNew) { createdNew = false; - ObjectNode nodeForValue = getExistingNodeForValue(value); + ObjectGraphNode nodeForValue = getExistingNodeForValue(value); if (nodeForValue == null) { // if no node for memberValue exists, create it nodeForValue = createNewNode(value); - loadNodeProperties(nodeForValue); + loadContent(nodeForValue); createdNew = true; } return nodeForValue; } /// - /// Fills node contents by adding properties. + /// Fills node Content property tree. /// /// - private void loadNodeProperties(ObjectNode thisNode) + private void loadContent(ObjectGraphNode thisNode) { + NestedNode contentRoot = new NestedNode(NestedNodeType.ThisNode); + thisNode.Content = contentRoot; + // take all properties for this value (type = value's real type) - foreach(Expression memberExpr in thisNode.PermanentReference.Expression.AppendObjectMembers(thisNode.PermanentReference.Type, _bindingFlags)) + foreach(Expression memberExpr in thisNode.PermanentReference.Expression.AppendObjectMembers(thisNode.PermanentReference.Type, this.memberBindingFlags)) { checkIsOfSupportedType(memberExpr); string memberName = memberExpr.CodeTail; - if (isOfAtomicType(memberExpr)) + if (memberExpr.IsOfAtomicType()) { - // atomic members are added to the list of node's "properties" - string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString; - thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr); + // properties are now lazy-evaluated + // string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).InvokeToString(); + AddAtomicPropertyChild(contentRoot, memberName, memberExpr); } else { - // for object members, complex properties are added - ObjectNode targetNode = null; - bool memberIsNull = isNull(memberExpr); - thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull); + ObjectGraphNode targetNode = null; + bool memberIsNull = memberExpr.IsNull(); + AddComplexPropertyChild(contentRoot, memberName, memberExpr, targetNode, memberIsNull); } } } /// - /// Creates child nodes of this node for each complex property and connects them to property.TargetNode. + /// For each complex property of this node, create s neighbor graph node if needed and connects the neighbor to ObjectProperty.TargetNode. /// /// /// - private void loadChildrenRecursive(ObjectNode thisNode, ExpandedNodes expandedNodes) + private void loadNeighborsRecursive(ObjectGraphNode thisNode, ExpandedNodes expandedNodes) { foreach(ObjectGraphProperty complexProperty in thisNode.ComplexProperties) { Expression memberExpr = complexProperty.Expression; - ObjectNode targetNode = null; + ObjectGraphNode targetNode = null; + // we are only evaluating expanded nodes here + // (we have to do this to know the "shape" of the graph) + // property laziness makes sense, as we are not evaluating atomic and non-expanded properties out of user's view if (!complexProperty.IsNull && expandedNodes.IsExpanded(memberExpr.Code)) { - Value memberValue = getPermanentReference(memberExpr); + Value memberValue = memberExpr.GetPermanentReference(); bool createdNew; // get existing node (loop) or create new @@ -183,7 +180,7 @@ namespace Debugger.AddIn.Visualizers.Graph if (createdNew) { // if member node is new, recursively build its subtree - loadChildrenRecursive(targetNode, expandedNodes); + loadNeighborsRecursive(targetNode, expandedNodes); } } else @@ -199,10 +196,10 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// Value, has to be valid. /// New empty object node representing the value. - private ObjectNode createNewNode(Value permanentReference) + private ObjectGraphNode createNewNode(Value permanentReference) { - ObjectNode newNode = new ObjectNode(); - newNode.HashCode = invokeGetHashCode(permanentReference); + ObjectGraphNode newNode = new ObjectGraphNode(); + newNode.HashCode = permanentReference.InvokeDefaultGetHashCode(); resultGraph.AddNode(newNode); // remember this node's hashcode for quick lookup @@ -220,11 +217,11 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// Valid value representing an instance. /// - private ObjectNode getExistingNodeForValue(Value value) + private ObjectGraphNode getExistingNodeForValue(Value value) { - int objectHashCode = invokeGetHashCode(value); + int objectHashCode = value.InvokeDefaultGetHashCode(); // are there any nodes with the same hash code? - LookupValueCollection nodesWithSameHashCode = objectNodesForHashCode[objectHashCode]; + LookupValueCollection nodesWithSameHashCode = objectNodesForHashCode[objectHashCode]; if (nodesWithSameHashCode == null) { return null; @@ -234,28 +231,30 @@ namespace Debugger.AddIn.Visualizers.Graph // if there is a node with same hash code, check if it has also the same address // (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier) ulong objectAddress = value.GetObjectAddress(); - ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find( + ObjectGraphNode nodeWithSameAddress = nodesWithSameHashCode.Find( node => { return node.PermanentReference.GetObjectAddress() == objectAddress; } ); return nodeWithSameAddress; } } /// - /// Invokes GetHashCode on given value. + /// Adds primitive property. /// - /// Valid value. - /// Hash code of the object in the debugee. - private int invokeGetHashCode(Value value) + public void AddAtomicPropertyChild(NestedNode node, string name, Expression expression) { - // David: I had hard time finding out how to invoke static method - // value.InvokeMethod is nice for instance methods. - // what about MethodInfo.Invoke() ? - // also, there could be an overload that takes 1 parameter instead of array - string defaultHashCode = Eval.InvokeMethod(this.hashCodeMethod, null, new Value[]{value}).AsString; - - //MethodInfo method = value.Type.GetMember("GetHashCode", BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; - //string hashCode = value.InvokeMethod(method, null).AsString; - return int.Parse(defaultHashCode); + // add child with no value (will be lazy-evaluated later) + node.AddChild(new PropertyNode(new ObjectGraphProperty + { Name = name, Value = "", Expression = expression, IsAtomic = true, TargetNode = null })); + } + + /// + /// Adds complex property. + /// + public void AddComplexPropertyChild(NestedNode node, string name, Expression expression, ObjectGraphNode targetNode, bool isNull) + { + // add child with no value (will be lazy-evaluated later) + node.AddChild(new PropertyNode(new ObjectGraphProperty + { Name = name, Value = "", Expression = expression, IsAtomic = false, TargetNode = targetNode, IsNull = isNull })); } /// @@ -271,41 +270,5 @@ namespace Debugger.AddIn.Visualizers.Graph throw new DebuggerVisualizerException("Arrays are not supported yet"); } } - - /// - /// Checks whether given expression's type is atomic. - /// - /// Expression. - /// True if expression's type is atomic, False otherwise. - private bool isOfAtomicType(Expression expr) - { - DebugType typeOfValue = expr.Evaluate(debuggerService.DebuggedProcess).Type; - return !(typeOfValue.IsClass || typeOfValue.IsValueType); - //return (!typeOfValue.IsClass) || typeOfValue.IsString; - } - - #region Expression helpers - - private Value getPermanentReference(Expression expr) - { - return expr.Evaluate(debuggerService.DebuggedProcess).GetPermanentReference(); - } - - private bool isNull(Expression expr) - { - return expr.Evaluate(debuggerService.DebuggedProcess).IsNull; - } - - private DebugType getType(Expression expr) - { - return expr.Evaluate(debuggerService.DebuggedProcess).Type; - } - - private ulong getObjectAddress(Expression expr) - { - return expr.Evaluate(debuggerService.DebuggedProcess).GetObjectAddress(); - } - - #endregion } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphNode.cs new file mode 100644 index 0000000000..372cec5c7c --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphNode.cs @@ -0,0 +1,59 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; +using Debugger.Expressions; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Node in the . + /// + public class ObjectGraphNode + { + /// + /// Permanent reference to the value in the the debugee this node represents. + /// + internal Debugger.Value PermanentReference { get; set; } // needed for graph building and matching, since hashCodes are not unique + /// + /// Hash code in the debuggee of the DebuggerValue this node represents. + /// + internal int HashCode { get; set; } + /// + /// Expression used to obtain this node. + /// + public Expressions.Expression Expression { get { return this.PermanentReference.Expression; } } + + /// + /// Property tree of this node. + /// + public NestedNode Content + { + get; set; + } + + // TODO just for ObjectGraphBuilder, remove + /// + /// Only complex properties filtered out of + /// + public IEnumerable ComplexProperties + { + get + { + if (this.Content == null) + throw new InvalidOperationException("Cannot enumerate properties, Content is null"); + + foreach (var child in this.Content.Childs) + { + if (child is PropertyNode && !(((PropertyNode)child).Property.IsAtomic)) + yield return ((PropertyNode)child).Property; + } + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphProperty.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphProperty.cs index 9224ce40b1..af68e6ff10 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphProperty.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphProperty.cs @@ -18,6 +18,6 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// Node that this property points to. Can be null. Always null if is true. /// - public ObjectNode TargetNode { get; set; } + public ObjectGraphNode TargetNode { get; set; } } } 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 deleted file mode 100644 index dfb92a4f19..0000000000 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs +++ /dev/null @@ -1,102 +0,0 @@ -// -// -// -// -// $Revision$ -// -using System; -using System.Collections.Generic; -using System.Text; -using Debugger.Expressions; - -namespace Debugger.AddIn.Visualizers.Graph -{ - /// - /// Node in the . - /// - public class ObjectNode - { - /// - /// Permanent reference to the value in the the debugee this node represents. - /// - internal Debugger.Value PermanentReference { get; set; } // needed for graph building and matching, since hashCodes are not unique - /// - /// Hash code in the debuggee of the DebuggerValue this node represents. - /// - internal int HashCode { get; set; } - /// - /// Expression used to obtain this node. - /// - public Expressions.Expression Expression { get { return this.PermanentReference.Expression; } } - - class PropertyComparer : IComparer - { - public int Compare(ObjectGraphProperty prop1, ObjectGraphProperty prop2) - { - // order by IsAtomic, Name - int comparedAtomic = prop2.IsAtomic.CompareTo(prop1.IsAtomic); - if (comparedAtomic != 0) - { - return comparedAtomic; - } - else - { - return prop1.Name.CompareTo(prop2.Name); - } - } - } - - private static PropertyComparer propertySortComparer = new PropertyComparer(); - - private bool sorted = false; - - private List properties = new List(); - /// - /// Properties (either atomic or complex) of the object this node represents. - /// - public List Properties - { - get - { - if (!sorted) - { - properties.Sort(propertySortComparer); - sorted = true; - } - return properties; - } - } - /// - /// Only complex properties filtered out of - /// - public IEnumerable ComplexProperties - { - get - { - foreach (var property in Properties) - { - if (!property.IsAtomic) - yield return property; - } - } - } - - /// - /// Adds primitive property. - /// - internal void AddAtomicProperty(string name, string value, Expression expression) - { - properties.Add(new ObjectGraphProperty - { Name = name, Value = value, Expression = expression, IsAtomic = true, TargetNode = null }); - } - - /// - /// Adds complex property. - /// - internal void AddComplexProperty(string name, string value, Expression expression, ObjectNode targetNode, bool isNull) - { - properties.Add(new ObjectGraphProperty - { Name = name, Value = value, Expression = expression, IsAtomic = false, TargetNode = targetNode, IsNull = isNull }); - } - } -} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/AbstractNode.cs similarity index 58% rename from src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs rename to src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/AbstractNode.cs index 2363b0f37f..d6e9d5c8fa 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/AbstractNode.cs @@ -5,15 +5,18 @@ // $Revision$ // using System; -using System.Collections.Generic; -using System.Text; namespace Debugger.AddIn.Visualizers.Graph { - /// - /// Named edge in the . - /// - /*public class ObjectEdge : NamedEdge - { - }*/ + /// + /// Description of AbstractNode. + /// + public class AbstractNode + { + + + public AbstractNode() + { + } + } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNode.cs new file mode 100644 index 0000000000..e8388a84a5 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNode.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Description of NestedNode. + /// + public class NestedNode : AbstractNode + { + private List childs = new List(); + + private NestedNodeType nodeType; + public NestedNodeType NodeType + { + get { return nodeType; } + } + + public IEnumerable Childs + { + get + { + foreach (var child in childs) + yield return child; + } + } + + public void AddChild(AbstractNode child) + { + this.childs.Add(child); + } + + public NestedNode(NestedNodeType nodeType) + { + this.nodeType = nodeType; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNodeType.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNodeType.cs new file mode 100644 index 0000000000..68c327e2c2 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/NestedNodeType.cs @@ -0,0 +1,22 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Description of NestedNodeType. + /// + public enum NestedNodeType + { + ThisNode, + BaseClassNode, + NonPublicInstanceMembersNode, + StaticMembersNode, + NonPublicStaticMembersNode + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/PropertyNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/PropertyNode.cs new file mode 100644 index 0000000000..29170c9cea --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/TreeModel/PropertyNode.cs @@ -0,0 +1,53 @@ +// +// +// +// +// $Revision$ +// +using ICSharpCode.SharpDevelop.Services; +using System; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Node containing ObjectGraphProperty, with lazy evaluation. + /// + public class PropertyNode : AbstractNode, IEvaluate + { + public PropertyNode(ObjectGraphProperty objectGraphProperty) + { + if (objectGraphProperty == null) + throw new ArgumentNullException("objectGraphProperty"); + + this.property = objectGraphProperty; + } + + private ObjectGraphProperty property; + public ObjectGraphProperty Property + { + get { return this.property; } + } + + #region IEvaluate members + + bool evaluateCalled = false; + public bool IsEvaluated + { + get { return this.evaluateCalled; } + } + + public void Evaluate() + { + if (this.Property.Expression == null) + { + throw new DebuggerVisualizerException("Cannot evaluate property with missing Expression"); + } + this.Property.Value = + this.Property.Expression.Evaluate(WindowsDebugger.CurrentProcess).InvokeToString(); + + this.evaluateCalled = true; + } + + #endregion + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/AtomicType.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/AtomicType.cs new file mode 100644 index 0000000000..527c4b568e --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/AtomicType.cs @@ -0,0 +1,30 @@ +// +// +// +// +// $Revision$ +// +using Debugger.MetaData; +using System; +using Debugger.Expressions; +using ICSharpCode.SharpDevelop.Services; + +namespace Debugger.AddIn.Visualizers.Utils +{ + /// + /// Atomic type is a type that "cannot be expanded". + /// + public static class AtomicType + { + /// + /// Checks whether given expression's type is atomic. + /// + /// Expression. + /// True if expression's type is atomic, False otherwise. + public static bool IsOfAtomicType(this Expression expr) + { + DebugType typeOfValue = expr.Evaluate(WindowsDebugger.CurrentProcess).Type; + return !(typeOfValue.IsClass || typeOfValue.IsValueType /* = struct in C# */); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/DebuggerHelpers.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/DebuggerHelpers.cs index 9d426c2aa9..ebffc7abae 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/DebuggerHelpers.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Utils/DebuggerHelpers.cs @@ -4,8 +4,11 @@ // // $Revision$ // +using Debugger.Expressions; using System; +using Debugger.MetaData; using Debugger.Wrappers.CorDebug; +using ICSharpCode.SharpDevelop.Services; namespace Debugger.AddIn.Visualizers.Utils { @@ -19,5 +22,61 @@ namespace Debugger.AddIn.Visualizers.Utils ICorDebugReferenceValue refVal = val.CorValue.CastTo(); return refVal.Value; } + + /// + /// Evaluates expression and gets underlying address of object in the debuggee. + /// + public static ulong GetObjectAddress(this Expression expr) + { + return expr.Evaluate(WindowsDebugger.CurrentProcess).GetObjectAddress(); + } + + /// + /// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee. + /// + private static MethodInfo hashCodeMethod; + + /// + /// Invokes RuntimeHelpers.GetHashCode on given value, that is a default hashCode ignoring user overrides. + /// + /// Valid value. + /// Hash code of the object in the debugee. + public static int InvokeDefaultGetHashCode(this Value value) + { + if (DebuggerHelpers.hashCodeMethod == null) + { + DebugType typeRuntimeHelpers = DebugType.Create(WindowsDebugger.CurrentProcess, null, "System.Runtime.CompilerServices.RuntimeHelpers"); + DebuggerHelpers.hashCodeMethod = typeRuntimeHelpers.GetMember("GetHashCode", BindingFlags.Public | BindingFlags.Static | BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; + if (DebuggerHelpers.hashCodeMethod == null) + { + throw new DebuggerException("Cannot obtain method System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode"); + } + } + + // David: I had hard time finding out how to invoke static method. + // value.InvokeMethod is nice for instance methods. + // what about MethodInfo.Invoke() ? + // also, there could be an overload that takes 1 parameter instead of array + string defaultHashCode = Eval.InvokeMethod(DebuggerHelpers.hashCodeMethod, null, new Value[]{value}).AsString; + + //MethodInfo method = value.Type.GetMember("GetHashCode", BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; + //string hashCode = value.InvokeMethod(method, null).AsString; + return int.Parse(defaultHashCode); + } + + public static Value GetPermanentReference(this Expression expr) + { + return expr.Evaluate(WindowsDebugger.CurrentProcess).GetPermanentReference(); + } + + public static bool IsNull(this Expression expr) + { + return expr.Evaluate(WindowsDebugger.CurrentProcess).IsNull; + } + + public static DebugType GetType(this Expression expr) + { + return expr.Evaluate(WindowsDebugger.CurrentProcess).Type; + } } }