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 3c14dc9d30..88ec113eae 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -208,7 +208,9 @@ + + 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 834a55afb6..2c97dda54e 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 @@ -74,8 +74,8 @@ namespace Debugger.AddIn.Visualizers.Graph { this.debuggerService = debuggerService; - DebugType helpersType = DebugType.Create(debuggerService.DebuggedProcess, null, "System.Runtime.CompilerServices.RuntimeHelpers"); - this.hashCodeMethod = helpersType.GetMember("GetHashCode", BindingFlags.Public | BindingFlags.Static | BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; + 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; } /// @@ -140,6 +140,7 @@ namespace Debugger.AddIn.Visualizers.Graph /// private void loadNodeProperties(ObjectNode thisNode) { + // take all properties for this value (type = value's real type) foreach(Expression memberExpr in thisNode.DebuggerValue.Expression.AppendObjectMembers(thisNode.DebuggerValue.Type, _bindingFlags)) { checkIsOfSupportedType(memberExpr); @@ -272,7 +273,7 @@ namespace Debugger.AddIn.Visualizers.Graph } /// - /// Checks whether given expression's type is atomic - atomic values will be added to node's property list. + /// Checks whether given expression's type is atomic. /// /// Expression. /// True if expression's type is atomic, False otherwise. 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 05efc25f66..19480e7d17 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 @@ -23,43 +23,43 @@ using ICSharpCode.SharpDevelop.Services; namespace Debugger.AddIn.Visualizers.Graph { - /// - /// Interaction logic for VisualizerWPFWindow.xaml - /// - public partial class VisualizerWPFWindow : Window - { - private WindowsDebugger debuggerService; - private EnumViewModel layoutViewModel; - private ObjectGraph objectGraph; - private ObjectGraphBuilder objectGraphBuilder; - - private PositionedGraph oldPosGraph; - private PositionedGraph currentPosGraph; - private GraphDrawer graphDrawer; - - /// - /// Long-lived map telling which nodes the user expanded. - /// - private ExpandedNodes expandedNodes; + /// + /// Interaction logic for VisualizerWPFWindow.xaml + /// + public partial class VisualizerWPFWindow : Window + { + private WindowsDebugger debuggerService; + private EnumViewModel layoutViewModel; + private ObjectGraph objectGraph; + private ObjectGraphBuilder objectGraphBuilder; + + private PositionedGraph oldPosGraph; + private PositionedGraph currentPosGraph; + private GraphDrawer graphDrawer; + + /// + /// Long-lived map telling which nodes the user expanded. + /// + private ExpandedNodes expandedNodes; - public VisualizerWPFWindow() - { - InitializeComponent(); - - debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; + public VisualizerWPFWindow() + { + InitializeComponent(); + + debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; if (debuggerService == null) throw new ApplicationException("Only windows debugger is currently supported"); - + debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped); this.layoutViewModel = new EnumViewModel(); - this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); - this.DataContext = this.layoutViewModel; - - this.graphDrawer = new GraphDrawer(this.canvas); - - this.expandedNodes = new ExpandedNodes(); + this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); + this.DataContext = this.layoutViewModel; + + this.graphDrawer = new GraphDrawer(this.canvas); + + this.expandedNodes = new ExpandedNodes(); } public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) @@ -76,33 +76,33 @@ namespace Debugger.AddIn.Visualizers.Graph debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged); this.Close(); } - - private void Inspect_Button_Click(object sender, RoutedEventArgs e) - { + + private void Inspect_Button_Click(object sender, RoutedEventArgs e) + { refreshGraph(); - } - - void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "SelectedEnumValue") // TODO should be special event for enum value change - { - layoutGraph(this.objectGraph); - } - } - - void refreshGraph() + } + + void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { - this.objectGraph = rebuildGraph(); - layoutGraph(this.objectGraph); + if (e.PropertyName == "SelectedEnumValue") // TODO should be special event for enum value change + { + layoutGraph(this.objectGraph); + } + } + + void refreshGraph() + { + this.objectGraph = rebuildGraph(); + layoutGraph(this.objectGraph); //GraphDrawer drawer = new GraphDrawer(graph); //drawer.Draw(canvas); } - - ObjectGraph rebuildGraph() - { - this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService); + + ObjectGraph rebuildGraph() + { + this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService); try - { + { ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); return this.objectGraphBuilder.BuildGraphForExpression(txtExpression.Text, this.expandedNodes); } @@ -116,21 +116,21 @@ namespace Debugger.AddIn.Visualizers.Graph guiHandleException(ex); return null; } - } - - void layoutGraph(ObjectGraph graph) - { - ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); - Layout.TreeLayouter layouter = new Layout.TreeLayouter(); - - this.oldPosGraph = this.currentPosGraph; + } + + void layoutGraph(ObjectGraph graph) + { + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); + Layout.TreeLayouter layouter = new Layout.TreeLayouter(); + + this.oldPosGraph = this.currentPosGraph; this.currentPosGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes); registerExpandCollapseEvents(this.currentPosGraph); var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph); this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff); //this.graphDrawer.Draw(currentGraph); - } + } void guiHandleException(System.Exception ex) { @@ -165,5 +165,5 @@ namespace Debugger.AddIn.Visualizers.Graph e.Property.ObjectProperty.TargetNode = null; layoutGraph(this.objectGraph); } - } + } } \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml index a4e01adea4..e53dc3a9ad 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml @@ -20,7 +20,7 @@ Expression: - + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs index 49e1719aef..949c509dbe 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs @@ -4,7 +4,9 @@ // // $Revision$ // +using Debugger.MetaData; using System; +using System.Collections; using System.Collections.Generic; using System.Text; using System.Windows; @@ -13,6 +15,8 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; namespace Debugger.AddIn.Visualizers.GridVisualizer { @@ -21,9 +25,55 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer /// public partial class GridVisualizerWindow : Window { + private WindowsDebugger debuggerService; + public GridVisualizerWindow() { InitializeComponent(); } + + private void btnInspect_Click(object sender, RoutedEventArgs e) + { + this.debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; + if (debuggerService == null) + throw new ApplicationException("Only windows debugger is currently supported"); + + Value val = debuggerService.GetValueFromName(txtExpression.Text).GetPermanentReference(); + if (val != null && !val.IsNull) + { + // search val.Type.Interfaces for IList, from it, get T + DebugType iListType = val.Type.GetInterface(typeof(IList).FullName); + if (iListType != null) + { + List genericArguments = val.Type.GenericArguments; + if (genericArguments.Count == 1) + { + DebugType listItemType = genericArguments[0]; + + var valuesProvider = new ListValuesProvider(val.Expression, iListType, listItemType); + var virtCollection = new VirtualizingCollection(valuesProvider); + + IList listItemTypeMembers = valuesProvider.GetItemTypeMembers(); + createListViewColumns(listItemTypeMembers); + + this.listView.ItemsSource = virtCollection; + } + } + } + } + + private void createListViewColumns(IList itemTypeMembers) + { + var gridView = (GridView)listView.View; + gridView.Columns.Clear(); + foreach (var member in itemTypeMembers) + { + GridViewColumn column = new GridViewColumn(); + column.Header = member.Name; + // "{Binding Path=[Name]}" + column.DisplayMemberBinding = new Binding("[" + member.Name + "].Value"); + gridView.Columns.Add(column); + } + } } } \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs index f523b2d270..91118884f7 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ObjectValue.cs @@ -4,29 +4,46 @@ // // $Revision$ // +using ICSharpCode.SharpDevelop.Services; using System; using System.Collections.Generic; using Debugger.AddIn.Visualizers.Utils; +using Debugger.Expressions; +using Debugger.MetaData; namespace Debugger.AddIn.Visualizers.GridVisualizer { /// - /// Description of ObjectValue. + /// Object in the debugee, with its properties loaded. /// public class ObjectValue { private Dictionary properties = new Dictionary(); + /// + /// Returns property with given name. + /// public ObjectProperty this[string key] { - get - { - return properties.GetValue(key); - } - set + get { return properties.GetValue(key); } + set { properties[key] = value; } + } + + public static ObjectValue Create(Expression expr, DebugType type, BindingFlags bindingFlags) + { + ObjectValue result = new ObjectValue(); + foreach(Expression memberExpr in expr.AppendObjectMembers(type, bindingFlags)) { - properties[key] = value; + ObjectProperty property = new ObjectProperty(); + property.IsAtomic = memberExpr.Evaluate(WindowsDebugger.CurrentProcess).Type.IsPrimitive; + property.Name = memberExpr.CodeTail; + property.Expression = memberExpr; + property.IsNull = memberExpr.Evaluate(WindowsDebugger.CurrentProcess).IsNull; + property.Value = memberExpr.Evaluate(WindowsDebugger.CurrentProcess).AsString; + + result.properties.Add(property.Name, property); } + return result; } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs index 5b8b6a3c5b..0402baec49 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs @@ -9,7 +9,7 @@ using Debugger.Expressions; using ICSharpCode.SharpDevelop.Services; using System.Collections.Generic; -namespace Debugger.AddIn.Visualizers.GridVisualizer.ValueProviders +namespace Debugger.AddIn.Visualizers.GridVisualizer { /// /// Provides s for debugee objects implementing IEnumerable. diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/IListValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/IListValuesProvider.cs new file mode 100644 index 0000000000..e792121b8c --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/IListValuesProvider.cs @@ -0,0 +1,19 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.GridVisualizer +{ + /// + /// Can provide individial items for a lazy collection, as well as total count of items. + /// + public interface IListValuesProvider + { + int GetCount(); + T GetItemAt(int index); + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs index c515302594..0b2bcf48fd 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs @@ -5,33 +5,70 @@ // $Revision$ // using System; -using ICSharpCode.SharpDevelop.Services; using Debugger.Expressions; +using Debugger.MetaData; +using ICSharpCode.SharpDevelop.Services; +using System.Collections; +using System.Collections.Generic; -namespace Debugger.AddIn.Visualizers.GridVisualizer.ValueProviders +namespace Debugger.AddIn.Visualizers.GridVisualizer { /// /// Provides s for debugee objects implementing IList. /// - public class ListValuesProvider + public class ListValuesProvider : IListValuesProvider { - private WindowsDebugger debugger; + private readonly Debugger.MetaData.BindingFlags bindingFlags = + BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; + private Expression targetObject; + private DebugType iListType; + private DebugType listItemType; - public ListValuesProvider(WindowsDebugger debuggerService, Expression targetObject) + private bool countEvaluated = false; + private int count = -1; + + public ListValuesProvider(Expression targetObject, DebugType iListType, DebugType listItemType) { - this.debugger = debuggerService; this.targetObject = targetObject; + this.iListType = iListType; + this.listItemType = listItemType; } public int GetCount() { - return -1; + if (!countEvaluated) + { + this.count = evaluateCount(); + } + return this.count; } public ObjectValue GetItemAt(int index) { - return null; + PropertyInfo itemProperty = iListType.GetProperty("Item"); + Expression itemExpr = targetObject.AppendMemberReference(itemProperty, new PrimitiveExpression(index)); + return ObjectValue.Create(itemExpr, this.listItemType, this.bindingFlags); + } + + public IList GetItemTypeMembers() + { + return listItemType.GetMembers(this.bindingFlags); + } + + private int evaluateCount() + { + PropertyInfo countProperty = iListType.GetInterface(typeof(ICollection).FullName).GetProperty("Count"); + Expression countExpr = targetObject.AppendPropertyReference(countProperty); + int count = 0; + try { + // Do not get string representation since it can be printed in hex later + Value countValue = countExpr.Evaluate(WindowsDebugger.CurrentProcess); + count = (int)countValue.PrimitiveValue; + } catch (GetValueException) { + count = -1; + } + return count; } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/VirtualizingCollection.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/VirtualizingCollection.cs new file mode 100644 index 0000000000..a2e9544423 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/GridVisualizer/VirtualizingCollection.cs @@ -0,0 +1,192 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.GridVisualizer +{ + /// + /// IList<T> with data vitualization - the indexer is lazy, uses IListValuesProvider to obtain values. + /// + public class VirtualizingCollection : IList, IList + { + private IListValuesProvider valueProvider; + private Dictionary itemCache = new Dictionary(); + + public VirtualizingCollection(IListValuesProvider valueProvider) + { + this.valueProvider = valueProvider; + } + + public int Count + { + get { return this.valueProvider.GetCount(); } + } + + public T this[int index] + { + get + { + T cachedItem; + if (!itemCache.TryGetValue(index, out cachedItem)) + { + cachedItem = this.valueProvider.GetItemAt(index); + this.itemCache[index] = cachedItem; + } + return cachedItem; + } + set + { + throw new NotImplementedException(); + } + } + + #region IList Members + + public int IndexOf(T item) + { + return -1; + } + + public void Insert(int index, T item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + #endregion + + #region ICollection Members + + public void Add(T item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + /// + /// Should be avoided on large collections due to performance. + /// + /// + public IEnumerator GetEnumerator() + { + for (int i = 0; i < this.Count; i++) + { + yield return this[i]; + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + #endregion + + #region IList Members + + public int Add(object value) + { + throw new NotImplementedException(); + } + + public bool Contains(object value) + { + throw new NotImplementedException(); + } + + public int IndexOf(object value) + { + return IndexOf((T)value); + } + + public void Insert(int index, object value) + { + throw new NotImplementedException(); + } + + public bool IsFixedSize + { + get { return false; } + } + + public void Remove(object value) + { + throw new NotImplementedException(); + } + + object IList.this[int index] + { + get + { + return this[index]; + } + set + { + throw new NotImplementedException(); + } + } + + #endregion + + #region ICollection Members + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + public bool IsSynchronized + { + get { return false; } + } + + public object SyncRoot + { + get { return this; } + } + + #endregion + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Metadata/DebugType.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Metadata/DebugType.cs index ea0148f7f4..145cc495af 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Metadata/DebugType.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Metadata/DebugType.cs @@ -144,7 +144,7 @@ namespace Debugger.MetaData } } - /// Gets generics arguments for a type or an emtpy array for non-generic types. + /// Gets generics arguments for a type or an empty List for non-generic types. public List GenericArguments { get { if (this.IsArray || this.IsPointer) {