Browse Source

Completely changed how debugger keeps state for nodes in local variables pad and in tooltips.

The old method was to keep a NRefactory expression for each node.
The new method is create a lambda expression for each node.

The main motivation for this change is to get ready for NR5 which does not allow any sharing of AST nodes.  This would mean that we would have to create a complete AST for each node, which might get expensive for deeply nested nodes.  Caching of already evaluated expression would also be more difficult with separate ASTs.  ILSpy is based on NR5 so we need this solution for it right now.

Another disadvantage was that every operation had to go though AST so we had to support it in the evaluator, we had to generate the AST, and we had to hope that nothing breaks on the way.  This is particularly complex for types - with lambda expression we simply keep around the reference to the type or to whatever we need.  Some things like "current exception object" do not exist in the AST so we had to hack around it.

On the other had, it was nice to have accurate C# expression for all nodes - for pretty printing, editing, or saving/loading it.
newNRvisualizers
David Srbecký 14 years ago
parent
commit
0a07af2788
  1. 19
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  2. 18
      src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluator.cs
  3. 0
      src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionExtensionMethods.cs
  4. 1
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs
  5. 73
      src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs
  6. 18
      src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/TreeNodeWrapper.cs
  7. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
  8. 30
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
  9. 49
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs
  10. 23
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  11. 5
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
  12. 10
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs
  13. 13
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinningBinding.cs
  14. 66
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ArrayRangeNode.cs
  15. 107
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ChildNodesOfObject.cs
  16. 25
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/DebuggerResourceService.cs
  17. 135
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
  18. 12
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/IContextMenu.cs
  19. 158
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ICorDebug.cs
  20. 32
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/IEnumerableNode.cs
  21. 33
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/IListNode.cs
  22. 12
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ISetText.cs
  23. 31
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/SavedTreeNode.cs
  24. 50
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/StackFrameNode.cs
  25. 111
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/TreeNode.cs
  26. 10
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/ExpressionVisualizerCommand.cs
  27. 13
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/GridVisualizerCommand.cs
  28. 4
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/GridVisualizerMenuCommand.cs
  29. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/IVisualizerDescriptor.cs
  30. 12
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/ObjectGraphVisualizerCommand.cs
  31. 12
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/TextVisualizerCommand.cs
  32. 13
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/XmlVisualizerCommand.cs
  33. 16
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/IListValuesProvider.cs
  34. 16
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/VirtualizingCollection.cs
  35. 4
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/TreeModel/ContentPropertyNode.cs
  36. 3
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml
  37. 173
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs
  38. 85
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ObjectValue.cs
  39. 50
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs
  40. 42
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/GridValuesProvider.cs
  41. 59
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs
  42. 12
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/TextVisualizer/TextVisualizerMode.cs
  43. 46
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/TextVisualizer/TextVisualizerWindow.xaml.cs
  44. 13
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Utils/DebuggerHelpers.cs
  45. 12
      src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj
  46. 34
      src/AddIns/Debugger/Debugger.Core/GetValueException.cs
  47. 4
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  48. 18
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
  49. 1
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugType.cs
  50. 17
      src/AddIns/Debugger/Debugger.Core/Process.cs
  51. 10
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  52. 1
      src/AddIns/Debugger/Debugger.Core/Value.cs
  53. 4
      src/AddIns/Debugger/Debugger.Tests/Debugger.Tests.csproj
  54. 2
      src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs
  55. 12
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/ITreeNode.cs
  56. 4
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/PinBookmark.cs

19
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -97,6 +97,8 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="NRefactory\ExpressionEvaluator.cs" />
<Compile Include="NRefactory\ExpressionExtensionMethods.cs" />
<Compile Include="Options\DebuggingOptionsPanel.xaml.cs"> <Compile Include="Options\DebuggingOptionsPanel.xaml.cs">
<DependentUpon>DebuggingOptionsPanel.xaml</DependentUpon> <DependentUpon>DebuggingOptionsPanel.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
@ -161,7 +163,6 @@
<Compile Include="Pads\WatchPad.cs"> <Compile Include="Pads\WatchPad.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Pads\WatchPadModel.cs" />
<Compile Include="Service\AttachToProcessForm.cs"> <Compile Include="Service\AttachToProcessForm.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@ -192,11 +193,6 @@
<Compile Include="Tooltips\VisualizerPicker.cs"> <Compile Include="Tooltips\VisualizerPicker.cs">
<DependentUpon>VisualizerPicker.xaml</DependentUpon> <DependentUpon>VisualizerPicker.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="TreeModel\DebuggerResourceService.cs" />
<Compile Include="TreeModel\ICorDebug.cs" />
<Compile Include="TreeModel\IEnumerableNode.cs" />
<Compile Include="TreeModel\IListNode.cs" />
<Compile Include="TreeModel\SavedTreeNode.cs" />
<Compile Include="TreeModel\TreeNode.cs" /> <Compile Include="TreeModel\TreeNode.cs" />
<Compile Include="Visualizers\Commands\ExpressionVisualizerCommand.cs" /> <Compile Include="Visualizers\Commands\ExpressionVisualizerCommand.cs" />
<Compile Include="Visualizers\Commands\GridVisualizerCommand.cs" /> <Compile Include="Visualizers\Commands\GridVisualizerCommand.cs" />
@ -209,7 +205,6 @@
<Compile Include="Visualizers\Commands\XmlVisualizerCommand.cs" /> <Compile Include="Visualizers\Commands\XmlVisualizerCommand.cs" />
<Compile Include="Visualizers\Common\DebuggerVisualizerException.cs" /> <Compile Include="Visualizers\Common\DebuggerVisualizerException.cs" />
<Compile Include="Visualizers\Common\IEvaluate.cs" /> <Compile Include="Visualizers\Common\IEvaluate.cs" />
<Compile Include="Visualizers\Common\IListValuesProvider.cs" />
<Compile Include="Visualizers\Common\ObjectProperty.cs" /> <Compile Include="Visualizers\Common\ObjectProperty.cs" />
<Compile Include="Visualizers\Common\ObjectPropertyComparer.cs" /> <Compile Include="Visualizers\Common\ObjectPropertyComparer.cs" />
<Compile Include="Visualizers\Common\VirtualizingCollection.cs" /> <Compile Include="Visualizers\Common\VirtualizingCollection.cs" />
@ -253,12 +248,10 @@
<Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\ThisNode.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\ThisNode.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\NonPublicMembersNode.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\NonPublicMembersNode.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\PropertyNode.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\PropertyNode.cs" />
<Compile Include="Visualizers\GridVisualizer\ValueProviders\GridValuesProvider.cs" />
<Compile Include="Visualizers\PresentationBindings\DragScrollViewer.cs" /> <Compile Include="Visualizers\PresentationBindings\DragScrollViewer.cs" />
<Compile Include="Visualizers\PresentationBindings\GridViewColumnHider.cs" /> <Compile Include="Visualizers\PresentationBindings\GridViewColumnHider.cs" />
<Compile Include="Visualizers\PresentationBindings\GridViewHideableColumn.cs" /> <Compile Include="Visualizers\PresentationBindings\GridViewHideableColumn.cs" />
<Compile Include="Visualizers\PresentationBindings\TooltipVisibilityConverter.cs" /> <Compile Include="Visualizers\PresentationBindings\TooltipVisibilityConverter.cs" />
<Compile Include="Visualizers\TextVisualizer\TextVisualizerMode.cs" />
<Compile Include="Visualizers\TextVisualizer\TextVisualizerWindow.xaml.cs"> <Compile Include="Visualizers\TextVisualizer\TextVisualizerWindow.xaml.cs">
<DependentUpon>TextVisualizerWindow.xaml</DependentUpon> <DependentUpon>TextVisualizerWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
@ -288,8 +281,6 @@
<Compile Include="Pads\RunningThreadsPad.Menu.cs" /> <Compile Include="Pads\RunningThreadsPad.Menu.cs" />
<Compile Include="TreeModel\ArrayRangeNode.cs" /> <Compile Include="TreeModel\ArrayRangeNode.cs" />
<Compile Include="TreeModel\ExpressionNode.cs" /> <Compile Include="TreeModel\ExpressionNode.cs" />
<Compile Include="TreeModel\IContextMenu.cs" />
<Compile Include="TreeModel\ISetText.cs" />
<Compile Include="TreeModel\ChildNodesOfObject.cs" /> <Compile Include="TreeModel\ChildNodesOfObject.cs" />
<Compile Include="TreeModel\StackFrameNode.cs" /> <Compile Include="TreeModel\StackFrameNode.cs" />
<Compile Include="TreeModel\Utils.cs" /> <Compile Include="TreeModel\Utils.cs" />
@ -312,9 +303,6 @@
<DependentUpon>GridVisualizerWindow.xaml</DependentUpon> <DependentUpon>GridVisualizerWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Visualizers\GridVisualizer\ObjectValue.cs" />
<Compile Include="Visualizers\GridVisualizer\ValueProviders\EnumerableValuesProvider.cs" />
<Compile Include="Visualizers\GridVisualizer\ValueProviders\ListValuesProvider.cs" />
<Compile Include="Visualizers\Utils\DebuggerHelpers.cs" /> <Compile Include="Visualizers\Utils\DebuggerHelpers.cs" />
<Compile Include="Visualizers\Utils\DictionaryExtensions.cs" /> <Compile Include="Visualizers\Utils\DictionaryExtensions.cs" />
<Compile Include="Visualizers\Utils\Lookup.cs" /> <Compile Include="Visualizers\Utils\Lookup.cs" />
@ -324,7 +312,6 @@
</Compile> </Compile>
<Compile Include="Visualizers\PresentationBindings\DisplayAttribute.cs" /> <Compile Include="Visualizers\PresentationBindings\DisplayAttribute.cs" />
<Compile Include="Visualizers\PresentationBindings\EnumViewModel.cs" /> <Compile Include="Visualizers\PresentationBindings\EnumViewModel.cs" />
<None Include="COPYING" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Debugger.AddIn.addin"> <Content Include="Debugger.AddIn.addin">
@ -408,6 +395,7 @@
<Name>ICSharpCode.Core.WinForms</Name> <Name>ICSharpCode.Core.WinForms</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
<Folder Include="NRefactory" />
<Folder Include="Pads\Commands" /> <Folder Include="Pads\Commands" />
<Folder Include="Pads\Controls" /> <Folder Include="Pads\Controls" />
<Folder Include="Pads\ParallelPad" /> <Folder Include="Pads\ParallelPad" />
@ -423,7 +411,6 @@
<Folder Include="Visualizers\Graph\ExpandedPaths" /> <Folder Include="Visualizers\Graph\ExpandedPaths" />
<Folder Include="Visualizers\Graph\ObjectGraph\TreeModel" /> <Folder Include="Visualizers\Graph\ObjectGraph\TreeModel" />
<Folder Include="Visualizers\Graph\ObjectGraph" /> <Folder Include="Visualizers\Graph\ObjectGraph" />
<Folder Include="Visualizers\GridVisualizer\ValueProviders" />
<Folder Include="Visualizers\Common" /> <Folder Include="Visualizers\Common" />
<Folder Include="Visualizers\Commands" /> <Folder Include="Visualizers\Commands" />
<Folder Include="Visualizers\TextVisualizer" /> <Folder Include="Visualizers\TextVisualizer" />

18
src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs → src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluator.cs

@ -15,8 +15,8 @@ namespace ICSharpCode.NRefactory.Visitors
{ {
public class EvaluateException: GetValueException public class EvaluateException: GetValueException
{ {
public EvaluateException(INode code, string msg):base(code, msg) {} public EvaluateException(INode code, string msg):base(msg) {}
public EvaluateException(INode code, string msgFmt, params string[] msgArgs):base(code, string.Format(msgFmt, msgArgs)) {} public EvaluateException(INode code, string msgFmt, params string[] msgArgs):base(string.Format(msgFmt, msgArgs)) {}
} }
class TypedValue class TypedValue
@ -163,13 +163,7 @@ namespace ICSharpCode.NRefactory.Visitors
TypedValue Evaluate(INode expression, bool permRef, object data = null) TypedValue Evaluate(INode expression, bool permRef, object data = null)
{ {
// Try to get the value from cache
// (the cache is cleared when the process is resumed)
TypedValue val; TypedValue val;
if (context.Process.ExpressionsCache.TryGetValue(expression, out val)) {
if (val == null || !val.Value.IsInvalid)
return val;
}
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start(); watch.Start();
@ -177,11 +171,8 @@ namespace ICSharpCode.NRefactory.Visitors
val = (TypedValue)expression.AcceptVisitor(this, data); val = (TypedValue)expression.AcceptVisitor(this, data);
if (val != null && permRef) if (val != null && permRef)
val = new TypedValue(val.Value.GetPermanentReference(), val.Type); val = new TypedValue(val.Value.GetPermanentReference(), val.Type);
} catch (GetValueException e) {
e.Expression = expression;
throw;
} catch (NotImplementedException e) { } catch (NotImplementedException e) {
throw new GetValueException(expression, "Language feature not implemented: " + e.Message); throw new EvaluateException(expression, "Language feature not implemented: " + e.Message);
} finally { } finally {
watch.Stop(); watch.Stop();
context.Process.TraceMessage("Evaluated: {0} in {1} ms total", expression.PrettyPrint(), watch.ElapsedMilliseconds); context.Process.TraceMessage("Evaluated: {0} in {1} ms total", expression.PrettyPrint(), watch.ElapsedMilliseconds);
@ -190,9 +181,6 @@ namespace ICSharpCode.NRefactory.Visitors
if (val != null && val.Value.IsInvalid) if (val != null && val.Value.IsInvalid)
throw new DebuggerException("Expression \"" + expression.PrettyPrint() + "\" is invalid right after evaluation"); throw new DebuggerException("Expression \"" + expression.PrettyPrint() + "\" is invalid right after evaluation");
// Add the result to cache
context.Process.ExpressionsCache[expression] = val;
return val; return val;
} }

0
src/AddIns/Debugger/Debugger.Core/NRefactory/Ast/ExpressionExtensionMethods.cs → src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionExtensionMethods.cs

1
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs

@ -28,7 +28,6 @@ namespace ICSharpCode.SharpDevelop.Services
DebuggeeExceptionWindowState = FormWindowState.Normal; DebuggeeExceptionWindowState = FormWindowState.Normal;
} }
public bool ICorDebugVisualizerEnabled { get; set; }
public ShowIntegersAs ShowIntegersAs { get; set; } public ShowIntegersAs ShowIntegersAs { get; set; }
public bool ShowArgumentNames { get; set; } public bool ShowArgumentNames { get; set; }
public bool ShowArgumentValues { get; set; } public bool ShowArgumentValues { get; set; }

73
src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs

@ -2,18 +2,12 @@
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) // This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows.Forms;
using Debugger.AddIn.Pads; using Debugger.AddIn.Pads;
using Debugger.AddIn.Pads.Controls; using Debugger.AddIn.Pads.Controls;
using Debugger.AddIn.TreeModel; using Debugger.AddIn.TreeModel;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.Core.WinForms; using ICSharpCode.Core.WinForms;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui.Pads; using ICSharpCode.SharpDevelop.Gui.Pads;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
@ -41,11 +35,10 @@ namespace Debugger.AddIn
string language = ProjectService.CurrentProject.Language; string language = ProjectService.CurrentProject.Language;
var text = new TextNode(null, input, var text = new TreeNode(input, null).ToSharpTreeNode();
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp).ToSharpTreeNode();
var list = pad.WatchList; var list = pad.WatchList;
if(!list.WatchItems.Any(n => text.Node.FullName == ((TreeNodeWrapper)n).Node.FullName)) if(!list.WatchItems.Any(n => text.Node.Name == ((TreeNodeWrapper)n).Node.Name))
list.WatchItems.Add(text); list.WatchItems.Add(text);
} }
@ -108,66 +101,4 @@ namespace Debugger.AddIn
} }
} }
} }
public class WatchScriptingLanguageMenuBuilder : ISubmenuBuilder, IMenuItemBuilder
{
public ToolStripItem[] BuildSubmenu(Codon codon, object owner)
{
List<ToolStripItem> items = new List<ToolStripItem>();
if (owner is WatchPad) {
WatchPad pad = (WatchPad)owner;
if (pad.WatchList.SelectedNode == null)
return items.ToArray();
var node = pad.WatchList.SelectedNode.Node;
while (node.Parent != null && node.Parent.Parent != null)
{
node = node.Parent;
}
if (!(node is TextNode))
return items.ToArray();
foreach (string item in SupportedLanguage.GetNames(typeof(SupportedLanguage))) {
items.Add(MakeItem(item, item, node as TextNode, (sender, e) => HandleItem(sender)));
}
}
return items.ToArray();
}
ToolStripMenuItem MakeItem(string title, string name, TextNode tag, EventHandler onClick)
{
ToolStripMenuItem menuItem = new ToolStripMenuItem(StringParser.Parse(title));
menuItem.Click += onClick;
menuItem.Name = name;
menuItem.Tag = tag;
if (name == tag.Language.ToString())
menuItem.Checked = true;
return menuItem;
}
void HandleItem(object sender)
{
ToolStripMenuItem item = null;
if (sender is ToolStripMenuItem)
item = (ToolStripMenuItem)sender;
if (item != null) {
TextNode node = (TextNode)item.Tag;
node.Language = (SupportedLanguage)SupportedLanguage.Parse(typeof(SupportedLanguage), item.Text);
}
}
public System.Collections.ICollection BuildItems(Codon codon, object owner)
{
return BuildSubmenu(codon, owner).TranslateToWpf();
}
}
} }

18
src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/TreeNodeWrapper.cs

@ -28,22 +28,22 @@ namespace Debugger.AddIn.Pads.Controls
public TreeNode Node { get; private set; } public TreeNode Node { get; private set; }
public override object Text { public override object Text {
get { return Node.Name; } get { return this.Node.Name; }
} }
public override object Icon { public override object Icon {
get { return Node.ImageSource; } get { return this.Node.ImageSource; }
} }
public override bool ShowExpander { public override bool ShowExpander {
get { return Node.HasChildNodes; } get { return this.Node.GetChildren != null; }
} }
protected override void LoadChildren() protected override void LoadChildren()
{ {
if (Node.HasChildNodes) { if (this.Node.GetChildren != null) {
((WindowsDebugger)DebuggerService.CurrentDebugger).DebuggedProcess var process = ((WindowsDebugger)DebuggerService.CurrentDebugger).DebuggedProcess;
.EnqueueWork(Dispatcher.CurrentDispatcher, () => Children.AddRange(Node.ChildNodes.Select(node => node.ToSharpTreeNode()))); process.EnqueueWork(Dispatcher.CurrentDispatcher, () => Children.AddRange(this.Node.GetChildren().Select(node => node.ToSharpTreeNode())));
} }
} }
} }
@ -69,12 +69,10 @@ namespace Debugger.AddIn.Pads.Controls
string language = ProjectService.CurrentProject.Language; string language = ProjectService.CurrentProject.Language;
// FIXME languages var text = new TreeNode(e.Data.GetData(DataFormats.StringFormat).ToString(), null);
TextNode text = new TextNode(null, e.Data.GetData(DataFormats.StringFormat).ToString(),
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp);
var node = text.ToSharpTreeNode(); var node = text.ToSharpTreeNode();
if (!WatchPad.Instance.WatchList.WatchItems.Any(n => text.FullName == ((TreeNodeWrapper)n).Node.FullName)) if (!WatchPad.Instance.WatchList.WatchItems.Any(n => text.Name == ((TreeNodeWrapper)n).Node.Name))
WatchPad.Instance.WatchList.WatchItems.Add(node); WatchPad.Instance.WatchList.WatchItems.Add(node);
WatchPad.Instance.InvalidatePad(); WatchPad.Instance.InvalidatePad();

2
src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs

@ -71,7 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
debuggedProcess.EnqueueForEach( debuggedProcess.EnqueueForEach(
Dispatcher.CurrentDispatcher, Dispatcher.CurrentDispatcher,
new StackFrameNode(frame).ChildNodes.ToList(), Utils.GetLocalVariableNodes(frame).ToList(),
n => localVarList.WatchItems.Add(n.ToSharpTreeNode()) n => localVarList.WatchItems.Add(n.ToSharpTreeNode())
); );
} catch (Exception ex) { } catch (Exception ex) {

30
src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs

@ -17,6 +17,7 @@ using Debugger.AddIn.TreeModel;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Core.Presentation; using ICSharpCode.Core.Presentation;
using ICSharpCode.NRefactory; using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using Exception = System.Exception; using Exception = System.Exception;
@ -78,29 +79,26 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
return; return;
foreach (var element in props.Elements) { foreach (var element in props.Elements) {
watchList.WatchItems.Add(new TextNode(null, element, (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), props[element])).ToSharpTreeNode()); watchList.WatchItems.Add(new TreeNode(element, null).ToSharpTreeNode());
} }
} }
void OnWatchItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) void OnWatchItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 0) {
// add to saved data
var data = e.NewItems.OfType<TextNode>().FirstOrDefault();
if (data != null) {
var props = GetSavedVariablesProperties(); var props = GetSavedVariablesProperties();
if (props == null) return; if (props == null) return;
props.Set(data.FullName, data.Language.ToString());
if (e.Action == NotifyCollectionChangedAction.Add) {
// add to saved data
foreach(var data in e.NewItems.OfType<TreeNode>()) {
props.Set(data.Name, (object)null);
} }
} }
if (e.Action == NotifyCollectionChangedAction.Remove) { if (e.Action == NotifyCollectionChangedAction.Remove) {
// remove from saved data // remove from saved data
var data = e.OldItems.OfType<TextNode>().FirstOrDefault(); foreach(var data in e.OldItems.OfType<TreeNode>()) {
if (data != null) { props.Remove(data.Name);
var props = GetSavedVariablesProperties();
if (props == null) return;
props.Remove(data.FullName);
} }
} }
} }
@ -162,9 +160,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
// rebuild list // rebuild list
var nodes = new List<TreeNodeWrapper>(); var nodes = new List<TreeNodeWrapper>();
foreach (var nod in watchList.WatchItems.OfType<TreeNodeWrapper>()) foreach (var nod in watchList.WatchItems.OfType<TreeNodeWrapper>())
nodes.Add(new TextNode(null, nod.Node.Name, nodes.Add(new TreeNode(nod.Node.Name, null).ToSharpTreeNode());
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp)
.ToSharpTreeNode());
watchList.WatchItems.Clear(); watchList.WatchItems.Clear();
foreach (var nod in nodes) foreach (var nod in nodes)
@ -194,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{ {
try { try {
LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(node.Node.Name) ? "is null or empty!" : node.Node.Name)); LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(node.Node.Name) ? "is null or empty!" : node.Node.Name));
var nodExpression = debugger.GetExpression(node.Node.Name);
//Value val = ExpressionEvaluator.Evaluate(nod.Name, nod.Language, debuggedProcess.SelectedStackFrame); //Value val = ExpressionEvaluator.Evaluate(nod.Name, nod.Language, debuggedProcess.SelectedStackFrame);
ExpressionNode valNode = new ExpressionNode(null, null, node.Node.Name, nodExpression); ExpressionNode valNode = new ExpressionNode(null, node.Node.Name, () => debugger.GetExpression(node.Node.Name).Evaluate(debuggedProcess));
return valNode.ToSharpTreeNode(); return valNode.ToSharpTreeNode();
} catch (GetValueException) { } catch (GetValueException) {
string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), node.Node.Name); string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), node.Node.Name);
ErrorInfoNode infoNode = new ErrorInfoNode(node.Node.Name, error); TreeNode infoNode = new TreeNode("Icons.16x16.Error", node.Node.Name, error, string.Empty, null);
return infoNode.ToSharpTreeNode(); return infoNode.ToSharpTreeNode();
} }
} }

49
src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs

@ -1,49 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Text;
using Debugger.AddIn.TreeModel;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Gui.Pads
{
public class TextNode : TreeNode, ISetText
{
public TextNode(TreeNode parent, string text, SupportedLanguage language)
: base(parent)
{
this.Name = text;
this.Language = language;
}
public override bool CanSetText {
get {
return true;
}
}
public override bool SetText(string text)
{
this.Text = text;
return true;
}
public bool SetName(string name)
{
this.Name = name;
return true;
}
public SupportedLanguage Language { get; set; }
}
public class ErrorInfoNode : ICorDebug.InfoNode
{
public ErrorInfoNode(string name, string text) : base(null, name, text)
{
IconImage = DebuggerResourceService.GetImage("Icons.16x16.Error");
}
}
}

23
src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs

@ -491,10 +491,7 @@ namespace ICSharpCode.SharpDevelop.Services
{ {
try { try {
var tooltipExpression = GetExpression(variableName); var tooltipExpression = GetExpression(variableName);
string imageName; ExpressionNode expressionNode = new ExpressionNode("Icons.16x16.Local", variableName, () => tooltipExpression.Evaluate(this.DebuggedProcess));
var image = ExpressionNode.GetImageForLocalVariable(out imageName);
ExpressionNode expressionNode = new ExpressionNode(null, image, variableName, tooltipExpression);
expressionNode.ImageName = imageName;
return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = debuggedProcess.GetCurrentExecutingFrame().HasSymbols }; return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = debuggedProcess.GetCurrentExecutingFrame().HasSymbols };
} catch (System.Exception ex) { } catch (System.Exception ex) {
LoggingService.Error("Error on GetTooltipControl: " + ex.Message); LoggingService.Error("Error on GetTooltipControl: " + ex.Message);
@ -505,19 +502,7 @@ namespace ICSharpCode.SharpDevelop.Services
public ITreeNode GetNode(string variable, string currentImageName = null) public ITreeNode GetNode(string variable, string currentImageName = null)
{ {
try { try {
var expression = GetExpression(variable); return new ExpressionNode(currentImageName ?? "Icons.16x16.Local", variable, () => GetExpression(variable).Evaluate(this.DebuggedProcess));
string imageName;
IImage image;
if (string.IsNullOrEmpty(currentImageName)) {
image = ExpressionNode.GetImageForLocalVariable(out imageName);
}
else {
image = new ResourceServiceImage(currentImageName);
imageName = currentImageName;
}
ExpressionNode expressionNode = new ExpressionNode(null, image, variable, expression);
expressionNode.ImageName = imageName;
return expressionNode;
} catch (GetValueException) { } catch (GetValueException) {
return null; return null;
} }
@ -827,9 +812,9 @@ namespace ICSharpCode.SharpDevelop.Services
{ {
OnIsProcessRunningChanged(EventArgs.Empty); OnIsProcessRunningChanged(EventArgs.Empty);
using(new PrintTimes("Jump to current line")) { LoggingService.Info("Jump to current line");
JumpToCurrentLine(); JumpToCurrentLine();
}
// TODO update tooltip // TODO update tooltip
/*if (currentTooltipRow != null && currentTooltipRow.IsShown) { /*if (currentTooltipRow != null && currentTooltipRow.IsShown) {
using(new PrintTimes("Update tooltip")) { using(new PrintTimes("Update tooltip")) {

5
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
@ -228,8 +229,8 @@ namespace Debugger.AddIn.Tooltips
this.childPopup.IsLeaf = true; this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet; this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet; this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
if (clickedNode.ChildNodes != null) { if (clickedNode.GetChildren != null) {
this.childPopup.ItemsSource = clickedNode.ChildNodes; this.childPopup.ItemsSource = clickedNode.GetChildren().ToList();
this.childPopup.Open(); this.childPopup.Open();
} }
} else { } else {

10
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs

@ -4,13 +4,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Shapes; using System.Windows.Shapes;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
@ -207,8 +207,8 @@ namespace Debugger.AddIn.Tooltips
this.childPopup.IsLeaf = true; this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet; this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet; this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
if (clickedNode.ChildNodes != null) { if (clickedNode.GetChildren != null) {
this.childPopup.ItemsSource = clickedNode.ChildNodes; this.childPopup.ItemsSource = clickedNode.GetChildren().ToList();
this.childPopup.Open(); this.childPopup.Open();
} }
} else { } else {
@ -300,14 +300,14 @@ namespace Debugger.AddIn.Tooltips
// refresh content // refresh content
ITreeNode node = ((FrameworkElement)e.OriginalSource).DataContext as ITreeNode; ITreeNode node = ((FrameworkElement)e.OriginalSource).DataContext as ITreeNode;
var resultNode = currentDebugger.GetNode(node.FullName, node.ImageName); var resultNode = currentDebugger.GetNode(node.Name, node.ImageName);
if (resultNode == null) if (resultNode == null)
return; return;
// HACK for updating the pins in tooltip // HACK for updating the pins in tooltip
var observable = new ObservableCollection<ITreeNode>(); var observable = new ObservableCollection<ITreeNode>();
var source = lazyGrid.ItemsSource; var source = lazyGrid.ItemsSource;
source.ForEach(item => { source.ForEach(item => {
if (item.CompareTo(node) == 0) if (item.Name == node.Name)
observable.Add(resultNode); observable.Add(resultNode);
else else
observable.Add(item); observable.Add(item);

13
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinningBinding.cs

@ -60,12 +60,13 @@ namespace Debugger.AddIn.Tooltips
var nodes = new ObservableCollection<ITreeNode>(); var nodes = new ObservableCollection<ITreeNode>();
foreach (var tuple in pin.SavedNodes) { foreach (var tuple in pin.SavedNodes) {
string imageName = !string.IsNullOrEmpty(tuple.Item1) ? tuple.Item1 : "Icons.16x16.Field"; var node = new Debugger.AddIn.TreeModel.TreeNode(
var node = new Debugger.AddIn.TreeModel.SavedTreeNode( !string.IsNullOrEmpty(tuple.Item1) ? tuple.Item1 : "Icons.16x16.Field",
new ResourceServiceImage(imageName),
tuple.Item2, tuple.Item2,
tuple.Item3); tuple.Item3,
node.ImageName = imageName; string.Empty,
null
);
nodes.Add(node); nodes.Add(node);
} }
@ -96,7 +97,7 @@ namespace Debugger.AddIn.Tooltips
pin.SavedNodes.Add( pin.SavedNodes.Add(
new Tuple<string, string, string>( new Tuple<string, string, string>(
"Icons.16x16.Field", "Icons.16x16.Field",
node.FullName, node.Name,
node.Text)); node.Text));
} }

66
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ArrayRangeNode.cs

@ -10,41 +10,10 @@ using ICSharpCode.SharpDevelop.Debugging;
namespace Debugger.AddIn.TreeModel namespace Debugger.AddIn.TreeModel
{ {
public partial class Utils public partial class Utils
{
public static IEnumerable<TreeNode> LazyGetChildNodesOfArray(TreeNode parent, Expression expression, ArrayDimensions dimensions)
{
if (dimensions.TotalElementCount == 0)
return new TreeNode[] { new TreeNode(null, "(empty)", null, null, parent, _ => null) };
return new ArrayRangeNode(parent, expression, dimensions, dimensions).ChildNodes;
}
}
/// <summary> This is a partent node for all elements within a given bounds </summary>
public class ArrayRangeNode: TreeNode
{ {
const int MaxElementCount = 100; const int MaxElementCount = 100;
Expression arrayTarget; public static TreeNode GetArrayRangeNode(ExpressionNode expr, ArrayDimensions bounds, ArrayDimensions originalBounds)
ArrayDimensions bounds;
ArrayDimensions originalBounds;
public ArrayRangeNode(TreeNode parent, Expression arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds)
: base(parent)
{
this.arrayTarget = arrayTarget;
this.bounds = bounds;
this.originalBounds = originalBounds;
this.Name = GetName();
this.childNodes = LazyGetChildren();
}
public override IEnumerable<TreeNode> ChildNodes {
get { return base.ChildNodes; }
}
string GetName()
{ {
StringBuilder name = new StringBuilder(); StringBuilder name = new StringBuilder();
bool isFirst = true; bool isFirst = true;
@ -56,7 +25,7 @@ namespace Debugger.AddIn.TreeModel
ArrayDimension originalDim = originalBounds[i]; ArrayDimension originalDim = originalBounds[i];
if (dim.Count == 0) { if (dim.Count == 0) {
throw new DebuggerException("Empty dimension"); name.Append("-");
} else if (dim.Count == 1) { } else if (dim.Count == 1) {
name.Append(dim.LowerBound.ToString()); name.Append(dim.LowerBound.ToString());
} else if (dim.Equals(originalDim)) { } else if (dim.Equals(originalDim)) {
@ -68,11 +37,21 @@ namespace Debugger.AddIn.TreeModel
} }
} }
name.Append("]"); name.Append("]");
return name.ToString();
return new TreeNode(name.ToString(), () => GetChildNodesOfArray(expr, bounds, originalBounds));
} }
static string GetName(int[] indices) public static IEnumerable<TreeNode> GetChildNodesOfArray(ExpressionNode arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds)
{ {
if (bounds.TotalElementCount == 0)
{
yield return new TreeNode("(empty)", null);
yield break;
}
// The whole array is small - just add all elements as childs
if (bounds.TotalElementCount <= MaxElementCount) {
foreach(int[] indices in bounds.Indices) {
StringBuilder sb = new StringBuilder(indices.Length * 4); StringBuilder sb = new StringBuilder(indices.Length * 4);
sb.Append("["); sb.Append("[");
bool isFirst = true; bool isFirst = true;
@ -82,19 +61,8 @@ namespace Debugger.AddIn.TreeModel
isFirst = false; isFirst = false;
} }
sb.Append("]"); sb.Append("]");
return sb.ToString(); int[] indicesCopy = indices;
} yield return new ExpressionNode("Icons.16x16.Field", sb.ToString(), () => arrayTarget.Evaluate().GetArrayElement(indicesCopy));
IEnumerable<TreeNode> LazyGetChildren()
{
// The whole array is small - just add all elements as childs
if (bounds.TotalElementCount <= MaxElementCount) {
foreach(int[] indices in bounds.Indices) {
string imageName;
var image = ExpressionNode.GetImageForArrayIndexer(out imageName);
var expression = new ExpressionNode(this, image, GetName(indices), arrayTarget.AppendIndexer(indices));
expression.ImageName = imageName;
yield return expression;
} }
yield break; yield break;
} }
@ -117,7 +85,7 @@ namespace Debugger.AddIn.TreeModel
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) { for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) {
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds); List<ArrayDimension> newDims = new List<ArrayDimension>(bounds);
newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound)); newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound));
yield return new ArrayRangeNode(this, arrayTarget, new ArrayDimensions(newDims), originalBounds); yield return GetArrayRangeNode(arrayTarget, new ArrayDimensions(newDims), originalBounds);
} }
yield break; yield break;
} }

107
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ChildNodesOfObject.cs

@ -1,10 +1,11 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) // This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Linq;
using Debugger.AddIn.Visualizers.Utils; using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData; using Debugger.MetaData;
using ICSharpCode.Core; using ICSharpCode.Core;
@ -17,7 +18,7 @@ namespace Debugger.AddIn.TreeModel
{ {
public partial class Utils public partial class Utils
{ {
public static IEnumerable<TreeNode> LazyGetChildNodesOfObject(TreeNode current, Expression targetObject, DebugType shownType) public static IEnumerable<TreeNode> GetChildNodesOfObject(ExpressionNode expr, DebugType shownType)
{ {
MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
@ -27,116 +28,94 @@ namespace Debugger.AddIn.TreeModel
DebugType baseType = (DebugType)shownType.BaseType; DebugType baseType = (DebugType)shownType.BaseType;
if (baseType != null) { if (baseType != null) {
yield return new TreeNode( yield return new TreeNode(
DebuggerResourceService.GetImage("Icons.16x16.Class"), "Icons.16x16.Class",
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.BaseClass}"), StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.BaseClass}"),
baseType.Name, baseType.Name,
baseType.FullName, baseType.FullName,
current, baseType.FullName == "System.Object" ? (Func<IEnumerable<TreeNode>>) null : () => Utils.GetChildNodesOfObject(expr, baseType)
newNode => baseType.FullName == "System.Object" ? null : Utils.LazyGetChildNodesOfObject(newNode, targetObject, baseType)
); );
} }
if (nonPublicInstance.Length > 0) { if (nonPublicInstance.Length > 0) {
yield return new TreeNode( yield return new TreeNode(
null,
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicMembers}"), StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicMembers}"),
string.Empty, () => GetMembersOfObject(expr, nonPublicInstance)
string.Empty,
current,
newNode => Utils.LazyGetMembersOfObject(newNode, targetObject, nonPublicInstance)
); );
} }
if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) { if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) {
yield return new TreeNode( yield return new TreeNode(
null,
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.StaticMembers}"), StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.StaticMembers}"),
string.Empty, () => {
string.Empty, var children = GetMembersOfObject(expr, publicStatic).ToList();
current,
p => {
var children = Utils.LazyGetMembersOfObject(p, targetObject, publicStatic);
if (nonPublicStatic.Length > 0) { if (nonPublicStatic.Length > 0) {
TreeNode nonPublicStaticNode = new TreeNode( children.Insert(0, new TreeNode(
null,
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicStaticMembers}"), StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicStaticMembers}"),
string.Empty, () => GetMembersOfObject(expr, nonPublicStatic)
string.Empty, ));
p,
newNode => Utils.LazyGetMembersOfObject(newNode, targetObject, nonPublicStatic)
);
children = Utils.PrependNode(nonPublicStaticNode, children);
} }
return children; return children;
} }
); );
} }
DebugType iListType = (DebugType)shownType.GetInterface(typeof(IList).FullName); if (shownType.GetInterface(typeof(IList).FullName) != null) {
if (iListType != null) { yield return new TreeNode(
yield return new IListNode(current, targetObject); "IList",
() => GetItemsOfIList(() => expr.Evaluate())
);
} else { } else {
DebugType iEnumerableType, itemType; DebugType listType, iEnumerableType, itemType;
if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) { if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
yield return new IEnumerableNode(current, targetObject, itemType); yield return new TreeNode(
null,
"IEnumerable",
"Expanding will enumerate the IEnumerable",
string.Empty,
() => GetItemsOfIList(() => DebuggerHelpers.CreateListFromIEnumeralbe(expr.Evaluate(), itemType, out listType))
);
} }
} }
foreach(TreeNode node in LazyGetMembersOfObject(current, targetObject, publicInstance)) { foreach(TreeNode node in GetMembersOfObject(expr, publicInstance)) {
yield return node; yield return node;
} }
} }
public static IEnumerable<TreeNode> LazyGetMembersOfObject(TreeNode parent, Expression expression, MemberInfo[] members) public static IEnumerable<TreeNode> GetMembersOfObject(ExpressionNode expr, MemberInfo[] members)
{ {
List<TreeNode> nodes = new List<TreeNode>(); foreach(MemberInfo memberInfo in members.OrderBy(m => m.Name)) {
foreach(MemberInfo memberInfo in members) { var memberInfoCopy = memberInfo;
string imageName; string imageName = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo);
var image = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo, out imageName); yield return new ExpressionNode(imageName, memberInfo.Name, () => expr.Evaluate().GetMemberValue(memberInfoCopy));
var exp = new ExpressionNode(parent, image, memberInfo.Name, expression.AppendMemberReference((IDebugMemberInfo)memberInfo));
exp.ImageName = imageName;
nodes.Add(exp);
} }
nodes.Sort();
return nodes;
} }
public static IEnumerable<TreeNode> GetItemsOfIList(Func<Value> getValue)
public static IEnumerable<TreeNode> LazyGetItemsOfIList(TreeNode parent, Expression targetObject)
{ {
// Add a cast, so that we are sure the expression has an indexer. Value list = null;
// (The expression can be e.g. of type 'object' but its value is a List. DebugType iListType = null;
// Without the cast, evaluating "expr[i]" would fail, because object does not have an indexer).
targetObject = targetObject.CastToIList();
int count = 0; int count = 0;
GetValueException error = null; GetValueException error = null;
try { try {
count = targetObject.GetIListCount(); // We use lambda for the value just so that we can get it in this try-catch block
list = getValue().GetPermanentReference();
iListType = (DebugType)list.Type.GetInterface(typeof(IList).FullName);
// Do not get string representation since it can be printed in hex
count = (int)list.GetPropertyValue(iListType.GetProperty("Count")).PrimitiveValue;
} catch (GetValueException e) { } catch (GetValueException e) {
// Cannot yield a value in the body of a catch clause (CS1631) // Cannot yield a value in the body of a catch clause (CS1631)
error = e; error = e;
} }
if (error != null) { if (error != null) {
yield return new TreeNode(null, "(error)", error.Message, null, null, _ => null); yield return new TreeNode(null, "(error)", error.Message, string.Empty, null);
} else if (count == 0) { } else if (count == 0) {
yield return new TreeNode(null, "(empty)", null, null, null, _ => null); yield return new TreeNode("(empty)", null);
} else { } else {
PropertyInfo pi = iListType.GetProperty("Item");
for(int i = 0; i < count; i++) { for(int i = 0; i < count; i++) {
string imageName; int iCopy = i;
var image = ExpressionNode.GetImageForArrayIndexer(out imageName); yield return new ExpressionNode("Icons.16x16.Field", "[" + i + "]", () => list.GetPropertyValue(pi, Eval.CreateValue(list.AppDomain, iCopy)));
var itemNode = new ExpressionNode(parent, image, "[" + i + "]", targetObject.AppendIndexer(i));
itemNode.ImageName = imageName;
yield return itemNode;
}
}
}
public static IEnumerable<TreeNode> PrependNode(TreeNode node, IEnumerable<TreeNode> rest)
{
yield return node;
if (rest != null) {
foreach(TreeNode absNode in rest) {
yield return absNode;
} }
} }
} }

25
src/AddIns/Debugger/Debugger.AddIn/TreeModel/DebuggerResourceService.cs

@ -1,25 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using ICSharpCode.SharpDevelop;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Debugger.AddIn.TreeModel
{
/// <summary>
/// Gets resources the way suitable for Debugger.AddIn.
/// </summary>
public static class DebuggerResourceService
{
/// <summary>
/// Gets image with given name from resources.
/// </summary>
/// <param name="resourceName">Resource name of the image.</param>
public static IImage GetImage(string resourceName)
{
return new ResourceServiceImage(resourceName);
}
}
}

135
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs

@ -4,17 +4,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Debugger.AddIn.Visualizers; using Debugger.AddIn.Visualizers;
using Debugger.MetaData; using Debugger.MetaData;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services; using ICSharpCode.SharpDevelop.Services;
@ -26,11 +27,12 @@ namespace Debugger.AddIn.TreeModel
/// Node in the tree which can be defined by a debugger expression. /// Node in the tree which can be defined by a debugger expression.
/// The expression will be lazily evaluated when needed. /// The expression will be lazily evaluated when needed.
/// </summary> /// </summary>
public class ExpressionNode: TreeNode, ISetText, INotifyPropertyChanged public class ExpressionNode: TreeNode, INotifyPropertyChanged
{ {
bool evaluated; bool evaluated;
Expression expression; Func<Value> valueGetter;
Value permanentValue;
bool canSetText; bool canSetText;
GetValueException error; GetValueException error;
string fullText; string fullText;
@ -40,10 +42,6 @@ namespace Debugger.AddIn.TreeModel
set { evaluated = value; } set { evaluated = value; }
} }
public Expression Expression {
get { return expression; }
}
public override bool CanSetText { public override bool CanSetText {
get { get {
if (!evaluated) EvaluateExpression(); if (!evaluated) EvaluateExpression();
@ -75,12 +73,6 @@ namespace Debugger.AddIn.TreeModel
} }
} }
public override string FullName {
get {
return this.expression.PrettyPrint() ?? Name.Trim();
}
}
public override string Type { public override string Type {
get { get {
if (!evaluated) EvaluateExpression(); if (!evaluated) EvaluateExpression();
@ -88,17 +80,13 @@ namespace Debugger.AddIn.TreeModel
} }
} }
public override IEnumerable<TreeNode> ChildNodes { public override Func<IEnumerable<TreeNode>> GetChildren {
get { get {
if (!evaluated) EvaluateExpression(); if (!evaluated) EvaluateExpression();
return base.ChildNodes; return base.GetChildren;
}
} }
protected set {
public override bool HasChildNodes { base.GetChildren = value;
get {
if (!evaluated) EvaluateExpression();
return base.HasChildNodes;
} }
} }
@ -136,42 +124,47 @@ namespace Debugger.AddIn.TreeModel
foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) { foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) {
if (descriptor.IsVisualizerAvailable(this.expressionType)) { if (descriptor.IsVisualizerAvailable(this.expressionType)) {
yield return descriptor.CreateVisualizerCommand(this.Expression); yield return descriptor.CreateVisualizerCommand(this.Name, () => this.Evaluate());
} }
} }
} }
public ExpressionNode(TreeNode parent, IImage image, string name, Expression expression) public ExpressionNode(string imageName, string name, Func<Value> valueGetter)
: base(parent) : base(imageName, name, string.Empty, string.Empty, null)
{ {
this.IconImage = image; this.valueGetter = valueGetter;
this.Name = name;
this.expression = expression;
} }
/// <summary>
/// Get the value of the node and cache it as long-lived reference.
/// We assume that the user will need this value a lot.
/// </summary>
public Value Evaluate()
{
if (permanentValue == null)
{
Stopwatch watch = new Stopwatch();
watch.Start();
permanentValue = valueGetter().GetPermanentReference();
LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms (result cached for future use)", this.Name, watch.ElapsedMilliseconds);
}
return permanentValue;
}
/// <summary>
/// Get the value of the node and update the UI text fields.
/// </summary>
void EvaluateExpression() void EvaluateExpression()
{ {
evaluated = true; evaluated = true;
Stopwatch watch = new Stopwatch();
watch.Start();
Value val; Value val;
try { try {
var process = WindowsDebugger.DebuggedProcess; // Do not keep permanent reference
if (process == null) return; val = valueGetter();
StackFrame frame = process.GetCurrentExecutingFrame();
if (frame == null) return;
var debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
object data = debugger.debuggerDecompilerService.GetLocalVariableIndex(frame.MethodInfo.DeclaringType.MetadataToken,
frame.MethodInfo.MetadataToken,
Name);
if (expression is MemberReferenceExpression) {
var memberExpression = (MemberReferenceExpression)expression;
memberExpression.TargetObject.UserData = data;
} else {
expression.UserData = data;
}
// evaluate expression
val = expression.Evaluate(process);
} catch (GetValueException e) { } catch (GetValueException e) {
error = e; error = e;
this.Text = e.Message; this.Text = e.Message;
@ -188,27 +181,24 @@ namespace Debugger.AddIn.TreeModel
if (val.IsNull) { if (val.IsNull) {
} else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass } else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass
} else if (val.Type.IsArray) { // Must be before IsClass } else if (val.Type.IsArray) { // Must be before IsClass
if (val.ArrayLength > 0) if (val.ArrayLength > 0) {
this.childNodes = Utils.LazyGetChildNodesOfArray(this, this.Expression, val.ArrayDimensions); var dims = val.ArrayDimensions; // Eval now
this.GetChildren = () => Utils.GetChildNodesOfArray(this, dims, dims);
}
} else if (val.Type.IsClass || val.Type.IsValueType) { } else if (val.Type.IsClass || val.Type.IsValueType) {
if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) { if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) {
if ((int)val.GetMemberValue("_size").PrimitiveValue > 0) if ((int)val.GetMemberValue("_size").PrimitiveValue > 0)
this.childNodes = Utils.LazyGetItemsOfIList(this, this.expression); this.GetChildren = () => Utils.GetItemsOfIList(() => this.Evaluate());
} else { } else {
this.childNodes = Utils.LazyGetChildNodesOfObject(this, this.Expression, val.Type); this.GetChildren = () => Utils.GetChildNodesOfObject(this, val.Type);
} }
} else if (val.Type.IsPointer) { } else if (val.Type.IsPointer) {
Value deRef = val.Dereference(); Value deRef = val.Dereference();
if (deRef != null) { if (deRef != null) {
this.childNodes = new ExpressionNode [] { new ExpressionNode(this, this.IconImage, "*" + this.Name, this.Expression.AppendDereference()) }; this.GetChildren = () => new ExpressionNode [] { new ExpressionNode(this.ImageName, "*" + this.Name, () => this.Evaluate().Dereference()) };
} }
} }
if (DebuggingOptions.Instance.ICorDebugVisualizerEnabled) {
TreeNode info = ICorDebug.GetDebugInfoRoot(val.AppDomain, val.CorValue);
this.childNodes = Utils.PrependNode(info, this.ChildNodes);
}
// Do last since it may expire the object // Do last since it may expire the object
if (val.Type.IsInteger) { if (val.Type.IsInteger) {
fullText = FormatInteger(val.PrimitiveValue); fullText = FormatInteger(val.PrimitiveValue);
@ -236,6 +226,8 @@ namespace Debugger.AddIn.TreeModel
} }
this.Text = (fullText.Length > 256) ? fullText.Substring(0, 256) + "..." : fullText; this.Text = (fullText.Length > 256) ? fullText.Substring(0, 256) + "..." : fullText;
LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms", this.Name, watch.ElapsedMilliseconds);
} }
string Escape(string source) string Escape(string source)
@ -308,11 +300,9 @@ namespace Debugger.AddIn.TreeModel
public override bool SetText(string newText) public override bool SetText(string newText)
{ {
string fullName = FullName;
Value val = null; Value val = null;
try { try {
val = this.Expression.Evaluate(WindowsDebugger.DebuggedProcess); val = this.Evaluate();
if (val.Type.IsInteger && newText.StartsWith("0x")) { if (val.Type.IsInteger && newText.StartsWith("0x")) {
try { try {
val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber); val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber);
@ -339,33 +329,10 @@ namespace Debugger.AddIn.TreeModel
return false; return false;
} }
public static IImage GetImageForThis(out string imageName) public static string GetImageForMember(IDebugMemberInfo memberInfo)
{
imageName = "Icons.16x16.Parameter";
return DebuggerResourceService.GetImage(imageName);
}
public static IImage GetImageForParameter(out string imageName)
{
imageName = "Icons.16x16.Parameter";
return DebuggerResourceService.GetImage(imageName);
}
public static IImage GetImageForLocalVariable(out string imageName)
{
imageName = "Icons.16x16.Local";
return DebuggerResourceService.GetImage(imageName);
}
public static IImage GetImageForArrayIndexer(out string imageName)
{
imageName = "Icons.16x16.Field";
return DebuggerResourceService.GetImage(imageName);
}
public static IImage GetImageForMember(IDebugMemberInfo memberInfo, out string imageName)
{ {
string name = string.Empty; string name = string.Empty;
if (memberInfo.IsPublic) { if (memberInfo.IsPublic) {
} else if (memberInfo.IsAssembly) { } else if (memberInfo.IsAssembly) {
name += "Internal"; name += "Internal";
@ -374,6 +341,7 @@ namespace Debugger.AddIn.TreeModel
} else if (memberInfo.IsPrivate) { } else if (memberInfo.IsPrivate) {
name += "Private"; name += "Private";
} }
if (memberInfo is FieldInfo) { if (memberInfo is FieldInfo) {
name += "Field"; name += "Field";
} else if (memberInfo is PropertyInfo) { } else if (memberInfo is PropertyInfo) {
@ -384,8 +352,7 @@ namespace Debugger.AddIn.TreeModel
throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName); throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName);
} }
imageName = "Icons.16x16." + name; return "Icons.16x16." + name;
return DebuggerResourceService.GetImage(imageName);
} }
// public ContextMenuStrip GetContextMenu() // public ContextMenuStrip GetContextMenu()

12
src/AddIns/Debugger/Debugger.AddIn/TreeModel/IContextMenu.cs

@ -1,12 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System.Windows.Forms;
namespace Debugger.AddIn.TreeModel
{
public interface IContextMenu
{
ContextMenuStrip GetContextMenu();
}
}

158
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ICorDebug.cs

@ -1,158 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using Debugger.Interop.CorDebug;
using System;
using System.Collections.Generic;
using Debugger.MetaData;
using ICSharpCode.SharpDevelop.Debugging;
namespace Debugger.AddIn.TreeModel
{
public class ICorDebug
{
public class InfoNode: TreeNode
{
List<TreeNode> children;
public InfoNode(TreeNode parent, string name, string text)
: this(parent, name, text, _ => null)
{
}
public InfoNode(TreeNode parent, string name, string text, Func<TreeNode, List<TreeNode>> children)
: base(parent)
{
this.Name = name;
this.Text = text;
this.children = children(this);
}
public override IEnumerable<TreeNode> ChildNodes {
get { return children; }
}
public void AddChild(string name, string text)
{
if (children == null) {
children = new List<TreeNode>();
}
children.Add(new InfoNode(this, name, text));
}
public void AddChild(string name, string text, Func<TreeNode, List<TreeNode>> subChildren)
{
if (children == null) {
children = new List<TreeNode>();
}
children.Add(new InfoNode(this, name, text, p => subChildren(p)));
}
}
public static InfoNode GetDebugInfoRoot(AppDomain appDomain, ICorDebugValue corValue)
{
return new InfoNode(null, "ICorDebug", "", p => GetDebugInfo(p, appDomain, corValue));
}
public static List<TreeNode> GetDebugInfo(TreeNode parent, AppDomain appDomain, ICorDebugValue corValue)
{
List<TreeNode> items = new List<TreeNode>();
if (corValue is ICorDebugValue) {
InfoNode info = new InfoNode(parent, "ICorDebugValue", "");
info.AddChild("Address", corValue.GetAddress().ToString("X8"));
info.AddChild("Type", ((CorElementType)corValue.GetTheType()).ToString());
info.AddChild("Size", corValue.GetSize().ToString());
items.Add(info);
}
if (corValue is ICorDebugValue2) {
InfoNode info = new InfoNode(parent, "ICorDebugValue2", "");
ICorDebugValue2 corValue2 = (ICorDebugValue2)corValue;
string fullname;
try {
fullname = DebugType.CreateFromCorType(appDomain, corValue2.GetExactType()).FullName;
} catch (DebuggerException e) {
fullname = e.Message;
}
info.AddChild("ExactType", fullname);
items.Add(info);
}
if (corValue is ICorDebugGenericValue) {
InfoNode info = new InfoNode(parent, "ICorDebugGenericValue", "");
try {
byte[] bytes = ((ICorDebugGenericValue)corValue).GetRawValue();
for(int i = 0; i < bytes.Length; i += 8) {
string val = "";
for(int j = i; j < bytes.Length && j < i + 8; j++) {
val += bytes[j].ToString("X2") + " ";
}
info.AddChild("Value" + i.ToString("X2"), val);
}
} catch (ArgumentException) {
info.AddChild("Value", "N/A");
}
items.Add(info);
}
if (corValue is ICorDebugReferenceValue) {
InfoNode info = new InfoNode(parent, "ICorDebugReferenceValue", "");
ICorDebugReferenceValue refValue = (ICorDebugReferenceValue)corValue;
info.AddChild("IsNull", (refValue.IsNull() != 0).ToString());
if (refValue.IsNull() == 0) {
info.AddChild("Value", refValue.GetValue().ToString("X8"));
if (refValue.Dereference() != null) {
info.AddChild("Dereference", "", p => GetDebugInfo(p, appDomain, refValue.Dereference()));
} else {
info.AddChild("Dereference", "N/A");
}
}
items.Add(info);
}
if (corValue is ICorDebugHeapValue) {
InfoNode info = new InfoNode(parent, "ICorDebugHeapValue", "");
items.Add(info);
}
if (corValue is ICorDebugHeapValue2) {
InfoNode info = new InfoNode(parent, "ICorDebugHeapValue2", "");
items.Add(info);
}
if (corValue is ICorDebugObjectValue) {
InfoNode info = new InfoNode(parent, "ICorDebugObjectValue", "");
ICorDebugObjectValue objValue = (ICorDebugObjectValue)corValue;
info.AddChild("Class", objValue.GetClass().GetToken().ToString("X8"));
info.AddChild("IsValueClass", (objValue.IsValueClass() != 0).ToString());
items.Add(info);
}
if (corValue is ICorDebugObjectValue2) {
InfoNode info = new InfoNode(parent, "ICorDebugObjectValue2", "");
items.Add(info);
}
if (corValue is ICorDebugBoxValue) {
InfoNode info = new InfoNode(parent, "ICorDebugBoxValue", "");
ICorDebugBoxValue boxValue = (ICorDebugBoxValue)corValue;
info.AddChild("Object", "", p => GetDebugInfo(p, appDomain, boxValue.GetObject()));
items.Add(info);
}
if (corValue is ICorDebugStringValue) {
InfoNode info = new InfoNode(parent, "ICorDebugStringValue", "");
ICorDebugStringValue stringValue = (ICorDebugStringValue)corValue;
info.AddChild("Length", stringValue.GetLength().ToString());
info.AddChild("String", stringValue.GetString());
items.Add(info);
}
if (corValue is ICorDebugArrayValue) {
InfoNode info = new InfoNode(parent, "ICorDebugArrayValue", "");
info.AddChild("...", "...");
items.Add(info);
}
if (corValue is ICorDebugHandleValue) {
InfoNode info = new InfoNode(parent, "ICorDebugHandleValue", "");
ICorDebugHandleValue handleValue = (ICorDebugHandleValue)corValue;
info.AddChild("HandleType", handleValue.GetHandleType().ToString());
items.Add(info);
}
return items;
}
}
}

32
src/AddIns/Debugger/Debugger.AddIn/TreeModel/IEnumerableNode.cs

@ -1,32 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Debugging;
namespace Debugger.AddIn.TreeModel
{
/// <summary>
/// IEnumerable node in the variable tree.
/// </summary>
public class IEnumerableNode : TreeNode
{
Expression targetObject;
Expression debugListExpression;
public IEnumerableNode(TreeNode parent, Expression targetObject, DebugType itemType)
: base(parent)
{
this.targetObject = targetObject;
this.Name = "IEnumerable";
this.Text = "Expanding will enumerate the IEnumerable";
DebugType debugListType;
this.debugListExpression = DebuggerHelpers.CreateDebugListExpression(targetObject, itemType, out debugListType);
this.childNodes = Utils.LazyGetItemsOfIList(this, this.debugListExpression);
}
}
}

33
src/AddIns/Debugger/Debugger.AddIn/TreeModel/IListNode.cs

@ -1,33 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using Debugger.AddIn.Visualizers.Utils;
using ICSharpCode.NRefactory.Ast;
using System.Collections;
using System.Collections.Generic;
using Debugger.MetaData;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.TreeModel
{
public class IListNode : TreeNode
{
Expression targetList;
int listCount;
public IListNode(TreeNode parent, Expression targetListObject)
: base(parent)
{
this.targetList = targetListObject;
this.Name = "IList";
this.listCount = this.targetList.GetIListCount();
this.childNodes = Utils.LazyGetItemsOfIList(this, this.targetList);
}
public override bool HasChildNodes {
get { return this.listCount > 0; }
}
}
}

12
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ISetText.cs

@ -1,12 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
namespace Debugger.AddIn.TreeModel
{
public interface ISetText
{
bool CanSetText { get; }
bool SetText(string text);
}
}

31
src/AddIns/Debugger/Debugger.AddIn/TreeModel/SavedTreeNode.cs

@ -1,31 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.SharpDevelop;
namespace Debugger.AddIn.TreeModel
{
/// <summary>
/// Description of SavedTreeNode.
/// </summary>
public class SavedTreeNode : TreeNode
{
public override bool CanSetText {
get { return true; }
}
public SavedTreeNode(IImage image, string fullname, string text)
: base(null)
{
base.IconImage = image;
FullName = fullname;
Text = text;
}
public override bool SetText(string newValue) {
Text = newValue;
return false;
}
}
}

50
src/AddIns/Debugger/Debugger.AddIn/TreeModel/StackFrameNode.cs

@ -11,56 +11,32 @@ using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.TreeModel namespace Debugger.AddIn.TreeModel
{ {
public class StackFrameNode: TreeNode public partial class Utils
{ {
StackFrame stackFrame; public static IEnumerable<TreeNode> GetLocalVariableNodes(StackFrame stackFrame)
public StackFrame StackFrame {
get { return stackFrame; }
}
public StackFrameNode(StackFrame stackFrame)
: base(null)
{
this.stackFrame = stackFrame;
this.Name = stackFrame.MethodInfo.Name;
this.childNodes = LazyGetChildNodes();
}
IEnumerable<TreeNode> LazyGetChildNodes()
{ {
foreach(DebugParameterInfo par in stackFrame.MethodInfo.GetParameters()) { foreach(DebugParameterInfo par in stackFrame.MethodInfo.GetParameters()) {
string imageName; var parCopy = par;
var image = ExpressionNode.GetImageForParameter(out imageName); yield return new ExpressionNode("Icons.16x16.Parameter", par.Name, () => parCopy.GetValue(stackFrame));
var expression = new ExpressionNode(this, image, par.Name, par.GetExpression());
expression.ImageName = imageName;
yield return expression;
} }
if (this.stackFrame.HasSymbols) { if (stackFrame.HasSymbols) {
foreach(DebugLocalVariableInfo locVar in stackFrame.MethodInfo.GetLocalVariables(this.StackFrame.IP)) { foreach(DebugLocalVariableInfo locVar in stackFrame.MethodInfo.GetLocalVariables(stackFrame.IP)) {
string imageName; var locVarCopy = locVar;
var image = ExpressionNode.GetImageForLocalVariable(out imageName); yield return new ExpressionNode("Icons.16x16.Local", locVar.Name, () => locVarCopy.GetValue(stackFrame));
var expression = new ExpressionNode(this, image, locVar.Name, locVar.GetExpression());
expression.ImageName = imageName;
yield return expression;
} }
} else { } else {
WindowsDebugger debugger = (WindowsDebugger)DebuggerService.CurrentDebugger; WindowsDebugger debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
if (debugger.debuggerDecompilerService != null) { if (debugger.debuggerDecompilerService != null) {
int typeToken = this.stackFrame.MethodInfo.DeclaringType.MetadataToken; int typeToken = stackFrame.MethodInfo.DeclaringType.MetadataToken;
int methodToken = this.stackFrame.MethodInfo.MetadataToken; int methodToken = stackFrame.MethodInfo.MetadataToken;
foreach (var localVar in debugger.debuggerDecompilerService.GetLocalVariables(typeToken, methodToken)) { foreach (var localVar in debugger.debuggerDecompilerService.GetLocalVariables(typeToken, methodToken)) {
string imageName; int index = ((int[])debugger.debuggerDecompilerService.GetLocalVariableIndex(typeToken, methodToken, localVar))[0];
var image = ExpressionNode.GetImageForLocalVariable(out imageName); yield return new ExpressionNode("Icons.16x16.Local", localVar, () => stackFrame.GetLocalVariableValue((uint)index));
var expression = new ExpressionNode(this, image, localVar, ExpressionEvaluator.ParseExpression(localVar, SupportedLanguage.CSharp));
expression.ImageName = imageName;
yield return expression;
} }
} }
} }
if (stackFrame.Thread.CurrentException != null) { if (stackFrame.Thread.CurrentException != null) {
yield return new ExpressionNode(this, null, "__exception", new IdentifierExpression("__exception")); yield return new ExpressionNode(null, "$exception", () => stackFrame.Thread.CurrentException.Value);
} }
} }
} }

111
src/AddIns/Debugger/Debugger.AddIn/TreeModel/TreeNode.cs

@ -15,31 +15,22 @@ namespace Debugger.AddIn.TreeModel
{ {
/// <summary> /// <summary>
/// A node in the variable tree. /// A node in the variable tree.
/// The node is immutable.
/// </summary> /// </summary>
public class TreeNode : ITreeNode public class TreeNode : ITreeNode
{ {
IImage iconImage = null; public IImage IconImage { get; protected set; }
string name = string.Empty; public string ImageName { get; set; }
string imageName = string.Empty; public string Name { get; set; }
string text = string.Empty; public virtual string Text { get; set; }
string type = string.Empty; public virtual string Type { get; protected set; }
protected IEnumerable<TreeNode> childNodes = null; public virtual Func<IEnumerable<TreeNode>> GetChildren { get; protected set; }
/// <summary>
/// The image displayed for this node.
/// </summary>
public IImage IconImage {
get { return iconImage; }
protected set { iconImage = value; }
}
/// <summary> /// <summary>
/// System.Windows.Media.ImageSource version of <see cref="IconImage"/>. /// System.Windows.Media.ImageSource version of <see cref="IconImage"/>.
/// </summary> /// </summary>
public ImageSource ImageSource { public ImageSource ImageSource {
get { get {
return iconImage == null ? null : iconImage.ImageSource; return this.IconImage == null ? null : this.IconImage.ImageSource;
} }
} }
@ -48,52 +39,24 @@ namespace Debugger.AddIn.TreeModel
/// </summary> /// </summary>
public Image Image { public Image Image {
get { get {
return iconImage == null ? null : iconImage.Bitmap; return this.IconImage == null ? null : this.IconImage.Bitmap;
} }
} }
public string Name { public virtual bool CanSetText {
get { return name; } get { return false; }
set { name = value; }
}
public string ImageName {
get { return imageName; }
set { imageName = value; }
}
public virtual string FullName {
get { return name; }
set { name = value; }
}
public virtual string Text
{
get { return text; }
set { text = value; }
}
public virtual string Type {
get { return type; }
protected set { type = value; }
} }
public virtual TreeNode Parent { get; protected set; } Func<IEnumerable<ITreeNode>> ITreeNode.GetChildren {
get {
public virtual IEnumerable<TreeNode> ChildNodes { if (this.GetChildren == null)
get { return childNodes; } return null;
return () => this.GetChildren();
} }
IEnumerable<ITreeNode> ITreeNode.ChildNodes {
get { return childNodes; }
} }
public virtual bool HasChildNodes { public virtual bool HasChildNodes {
get { return childNodes != null; } get { return this.GetChildren != null; }
}
public virtual bool CanSetText {
get { return false; }
} }
public virtual IEnumerable<IVisualizerCommand> VisualizerCommands { public virtual IEnumerable<IVisualizerCommand> VisualizerCommands {
@ -110,41 +73,21 @@ namespace Debugger.AddIn.TreeModel
public bool IsPinned { get; set; } public bool IsPinned { get; set; }
public TreeNode(TreeNode parent) public TreeNode(string name, Func<IEnumerable<TreeNode>> getChildren)
{
this.Parent = parent;
}
public TreeNode(IImage iconImage, string name, string text, string type, TreeNode parent, Func<TreeNode, IEnumerable<TreeNode>> childNodes)
: this(parent)
{
if (childNodes == null)
throw new ArgumentNullException("childNodes");
this.iconImage = iconImage;
this.name = name;
this.text = text;
this.type = type;
this.childNodes = childNodes(this);
}
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
TreeNode other = obj as TreeNode;
if (other == null)
return false;
return this.FullName == other.FullName;
}
public override int GetHashCode()
{ {
return this.FullName.GetHashCode(); this.Name = name;
this.GetChildren = getChildren;
} }
#endregion
public int CompareTo(ITreeNode other) public TreeNode(string imageName, string name, string text, string type, Func<IEnumerable<TreeNode>> getChildren)
{ {
return this.FullName.CompareTo(other.FullName); this.ImageName = imageName;
if (imageName != null)
this.IconImage = new ResourceServiceImage(imageName);
this.Name = name;
this.Text = text;
this.Type = type;
this.GetChildren = getChildren;
} }
public virtual bool SetText(string newValue) { public virtual bool SetText(string newValue) {

10
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/ExpressionVisualizerCommand.cs

@ -16,11 +16,15 @@ namespace Debugger.AddIn.Visualizers
// should we make visualizer command available for Expression, or any TreeNode? // should we make visualizer command available for Expression, or any TreeNode?
public abstract class ExpressionVisualizerCommand : IVisualizerCommand public abstract class ExpressionVisualizerCommand : IVisualizerCommand
{ {
public Expression Expression { get; private set; } public string ValueName { get; private set; }
public Func<Value> GetValue { get; private set; }
public ExpressionVisualizerCommand(Expression expression) public ExpressionVisualizerCommand(string valueName, Func<Value> getValue)
{ {
this.Expression = expression; if (getValue == null)
throw new ArgumentNullException("getValue");
this.ValueName = valueName;
this.GetValue = getValue;
} }
public abstract void Execute(); public abstract void Execute();

13
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/GridVisualizerCommand.cs

@ -25,9 +25,9 @@ namespace Debugger.AddIn.Visualizers
return type.ResolveIEnumerableImplementation(out collectionType, out itemType); return type.ResolveIEnumerableImplementation(out collectionType, out itemType);
} }
public IVisualizerCommand CreateVisualizerCommand(Expression expression) public IVisualizerCommand CreateVisualizerCommand(string valueName, Func<Value> getValue)
{ {
return new GridVisualizerCommand(expression); return new GridVisualizerCommand(valueName, getValue);
} }
} }
@ -36,8 +36,7 @@ namespace Debugger.AddIn.Visualizers
/// </summary> /// </summary>
public class GridVisualizerCommand : ExpressionVisualizerCommand public class GridVisualizerCommand : ExpressionVisualizerCommand
{ {
public GridVisualizerCommand(Expression expression) public GridVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
:base(expression)
{ {
} }
@ -48,10 +47,8 @@ namespace Debugger.AddIn.Visualizers
public override void Execute() public override void Execute()
{ {
if (this.Expression == null) GridVisualizerWindow window = new GridVisualizerWindow(this.ValueName, this.GetValue);
return; window.ShowDialog();
var gridVisualizerWindow = GridVisualizerWindow.EnsureShown();
gridVisualizerWindow.ShownExpression = this.Expression;
} }
} }
} }

4
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/GridVisualizerMenuCommand.cs

@ -14,9 +14,7 @@ namespace Debugger.AddIn.Visualizers
{ {
public override void Run() public override void Run()
{ {
GridVisualizerWindow window = new GridVisualizerWindow();
window.Topmost = true;
window.Show();
} }
} }
} }

2
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/IVisualizerDescriptor.cs

@ -16,6 +16,6 @@ namespace Debugger.AddIn.Visualizers
public interface IVisualizerDescriptor public interface IVisualizerDescriptor
{ {
bool IsVisualizerAvailable(DebugType type); bool IsVisualizerAvailable(DebugType type);
IVisualizerCommand CreateVisualizerCommand(Expression expression); IVisualizerCommand CreateVisualizerCommand(string valueName, Func<Value> getValue);
} }
} }

12
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/ObjectGraphVisualizerCommand.cs

@ -21,9 +21,9 @@ namespace Debugger.AddIn.Visualizers
return !type.IsAtomic() && !type.IsSystemDotObject(); return !type.IsAtomic() && !type.IsSystemDotObject();
} }
public IVisualizerCommand CreateVisualizerCommand(Expression expression) public IVisualizerCommand CreateVisualizerCommand(string valueName, Func<Value> getValue)
{ {
return new ObjectGraphVisualizerCommand(expression); return new ObjectGraphVisualizerCommand(valueName, getValue);
} }
} }
@ -32,8 +32,7 @@ namespace Debugger.AddIn.Visualizers
/// </summary> /// </summary>
public class ObjectGraphVisualizerCommand : ExpressionVisualizerCommand public class ObjectGraphVisualizerCommand : ExpressionVisualizerCommand
{ {
public ObjectGraphVisualizerCommand(Expression expression) public ObjectGraphVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
:base(expression)
{ {
} }
@ -44,10 +43,9 @@ namespace Debugger.AddIn.Visualizers
public override void Execute() public override void Execute()
{ {
if (this.Expression == null)
return;
var objectGraphWindow = ObjectGraphWindow.EnsureShown(); var objectGraphWindow = ObjectGraphWindow.EnsureShown();
objectGraphWindow.ShownExpression = this.Expression; // TODO: This only works on the root level
objectGraphWindow.ShownExpression = new IdentifierExpression(this.ValueName);
} }
} }
} }

12
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/TextVisualizerCommand.cs

@ -20,16 +20,15 @@ namespace Debugger.AddIn.Visualizers
return type.FullName == typeof(string).FullName; return type.FullName == typeof(string).FullName;
} }
public IVisualizerCommand CreateVisualizerCommand(Expression expression) public IVisualizerCommand CreateVisualizerCommand(string valueName, Func<Value> getValue)
{ {
return new TextVisualizerCommand(expression); return new TextVisualizerCommand(valueName, getValue);
} }
} }
public class TextVisualizerCommand : ExpressionVisualizerCommand public class TextVisualizerCommand : ExpressionVisualizerCommand
{ {
public TextVisualizerCommand(Expression expression) public TextVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
:base(expression)
{ {
} }
@ -40,10 +39,7 @@ namespace Debugger.AddIn.Visualizers
public override void Execute() public override void Execute()
{ {
if (this.Expression == null) var textVisualizerWindow = new TextVisualizerWindow(this.ValueName, this.GetValue().AsString());
return;
string expressionValue = this.Expression.Evaluate(WindowsDebugger.CurrentProcess).AsString();
var textVisualizerWindow = new TextVisualizerWindow(this.Expression.PrettyPrint(), expressionValue);
textVisualizerWindow.ShowDialog(); textVisualizerWindow.ShowDialog();
} }
} }

13
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Commands/XmlVisualizerCommand.cs

@ -20,9 +20,9 @@ namespace Debugger.AddIn.Visualizers
return type.FullName == typeof(string).FullName; return type.FullName == typeof(string).FullName;
} }
public IVisualizerCommand CreateVisualizerCommand(Expression expression) public IVisualizerCommand CreateVisualizerCommand(string valueName, Func<Value> getValue)
{ {
return new XmlVisualizerCommand(expression); return new XmlVisualizerCommand(valueName, getValue);
} }
} }
@ -31,8 +31,7 @@ namespace Debugger.AddIn.Visualizers
/// </summary> /// </summary>
public class XmlVisualizerCommand : ExpressionVisualizerCommand public class XmlVisualizerCommand : ExpressionVisualizerCommand
{ {
public XmlVisualizerCommand(Expression expression) public XmlVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
:base(expression)
{ {
} }
@ -43,11 +42,7 @@ namespace Debugger.AddIn.Visualizers
public override void Execute() public override void Execute()
{ {
if (this.Expression == null) var textVisualizerWindow = new TextVisualizerWindow(this.ValueName, this.GetValue().AsString(), ".xml");
return;
var textVisualizerWindow = new TextVisualizerWindow(
this.Expression.PrettyPrint(), this.Expression.Evaluate(WindowsDebugger.CurrentProcess).AsString());
textVisualizerWindow.Mode = TextVisualizerMode.Xml;
textVisualizerWindow.ShowDialog(); textVisualizerWindow.ShowDialog();
} }
} }

16
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/IListValuesProvider.cs

@ -1,16 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
namespace Debugger.AddIn.Visualizers
{
/// <summary>
/// Can provide individial items for a lazy collection, as well as total count of items.
/// </summary>
public interface IListValuesProvider<T>
{
int GetCount();
T GetItemAt(int index);
}
}

16
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/VirtualizingCollection.cs

@ -8,21 +8,23 @@ using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers namespace Debugger.AddIn.Visualizers
{ {
/// <summary> /// <summary>
/// IList&lt;T&gt; with data vitualization - the indexer is lazy, uses IListValuesProvider to obtain values when needed. /// IList&lt;T&gt; with data vitualization - the indexer is lazy, uses lamda function to obtain values when needed.
/// </summary> /// </summary>
public class VirtualizingCollection<T> : IList<T>, IList public class VirtualizingCollection<T> : IList<T>, IList
{ {
private IListValuesProvider<T> valueProvider; int count;
private Dictionary<int, T> itemCache = new Dictionary<int, T>(); Func<int, T> getItem;
Dictionary<int, T> itemCache = new Dictionary<int, T>();
public VirtualizingCollection(IListValuesProvider<T> valueProvider) public VirtualizingCollection(int count, Func<int, T> getItem)
{ {
this.valueProvider = valueProvider; this.count = count;
this.getItem = getItem;
} }
public int Count public int Count
{ {
get { return this.valueProvider.GetCount(); } get { return count; }
} }
public T this[int index] public T this[int index]
@ -32,7 +34,7 @@ namespace Debugger.AddIn.Visualizers
T cachedItem; T cachedItem;
if (!itemCache.TryGetValue(index, out cachedItem)) if (!itemCache.TryGetValue(index, out cachedItem))
{ {
cachedItem = this.valueProvider.GetItemAt(index); cachedItem = getItem(index);
this.itemCache[index] = cachedItem; this.itemCache[index] = cachedItem;
} }
return cachedItem; return cachedItem;

4
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/TreeModel/ContentPropertyNode.cs

@ -6,6 +6,7 @@ using System.ComponentModel;
using Debugger.AddIn.TreeModel; using Debugger.AddIn.TreeModel;
using Debugger.AddIn.Visualizers.Graph.Drawing; using Debugger.AddIn.Visualizers.Graph.Drawing;
using Debugger.MetaData; using Debugger.MetaData;
using ICSharpCode.SharpDevelop;
namespace Debugger.AddIn.Visualizers.Graph.Layout namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
@ -77,8 +78,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
if ((this.Property != null) && (this.Property.ObjectGraphProperty != null)) { if ((this.Property != null) && (this.Property.ObjectGraphProperty != null)) {
var memberInfo = (IDebugMemberInfo)this.Property.ObjectGraphProperty.MemberInfo; var memberInfo = (IDebugMemberInfo)this.Property.ObjectGraphProperty.MemberInfo;
if (memberInfo != null) { if (memberInfo != null) {
string imageName; var image = new ResourceServiceImage(ExpressionNode.GetImageForMember(memberInfo));
var image = ExpressionNode.GetImageForMember(memberInfo, out imageName);
this.MemberIcon = image.ImageSource; this.MemberIcon = image.ImageSource;
} }
} }

3
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml

@ -27,9 +27,6 @@
<Border Margin="3" Padding="3"> <Border Margin="3" Padding="3">
<DockPanel> <DockPanel>
<TextBlock VerticalAlignment="Center">Expression:</TextBlock>
<TextBox Name="txtExpression" VerticalAlignment="Center" Width="100"></TextBox>
<Button Name="btnInspect" Click="btnInspect_Click">Inspect</Button>
<ComboBox Name="cmbColumns" Style="{StaticResource styleColumnsComboBox}" Width="74" Height="20" HorizontalAlignment="Right"> <ComboBox Name="cmbColumns" Style="{StaticResource styleColumnsComboBox}" Width="74" Height="20" HorizontalAlignment="Right">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate> <DataTemplate>

173
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs

@ -2,26 +2,16 @@
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) // This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Debugger.AddIn.TreeModel;
using Debugger.AddIn.Visualizers.PresentationBindings; using Debugger.AddIn.Visualizers.PresentationBindings;
using Debugger.AddIn.Visualizers.Utils; using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData; using Debugger.MetaData;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Visualizers.GridVisualizer namespace Debugger.AddIn.Visualizers.GridVisualizer
{ {
@ -30,76 +20,15 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer
/// </summary> /// </summary>
public partial class GridVisualizerWindow : Window public partial class GridVisualizerWindow : Window
{ {
WindowsDebugger debuggerService; Func<Value> getValue;
GridViewColumnHider columnHider;
public GridVisualizerWindow() public GridVisualizerWindow(string valueName, Func<Value> getValue)
{ {
InitializeComponent(); InitializeComponent();
this.debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; this.Title = valueName;
if (debuggerService == null) this.getValue = getValue;
throw new ApplicationException("Only windows debugger is currently supported");
instance = this;
this.Deactivated += GridVisualizerWindow_Deactivated;
}
void GridVisualizerWindow_Deactivated(object sender, EventArgs e)
{
this.Close();
}
private ICSharpCode.NRefactory.Ast.Expression shownExpression;
public ICSharpCode.NRefactory.Ast.Expression ShownExpression
{
get {
return shownExpression;
}
set {
if (value == null) {
shownExpression = null;
txtExpression.Text = null;
Refresh();
return;
}
if (shownExpression == null || value.PrettyPrint() != shownExpression.PrettyPrint()) {
txtExpression.Text = value.PrettyPrint();
Refresh();
}
}
}
static GridVisualizerWindow instance;
/// <summary> When Window is visible, returns reference to the Window. Otherwise returns null. </summary>
public static GridVisualizerWindow Instance
{
get { return instance; }
}
public static GridVisualizerWindow EnsureShown()
{
var window = GridVisualizerWindow.Instance ?? new GridVisualizerWindow();
window.Topmost = true;
window.Show();
return window;
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
this.Deactivated -= GridVisualizerWindow_Deactivated;
base.OnClosing(e);
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
instance = null;
}
private void btnInspect_Click(object sender, RoutedEventArgs e)
{
Refresh(); Refresh();
} }
@ -108,82 +37,76 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer
try { try {
// clear ListView // clear ListView
listView.ItemsSource = null; listView.ItemsSource = null;
ScrollViewer listViewScroller = listView.GetScrollViewer();
if (listViewScroller != null) { Value shownValue = getValue();
listViewScroller.ScrollToVerticalOffset(0);
} DebugType iListType, iEnumerableType, itemType;
Value shownValue = null;
ICSharpCode.NRefactory.Ast.Expression shownExpr = null;
try {
shownExpr = debuggerService.GetExpression(txtExpression.Text);
shownValue = shownExpr.Evaluate(debuggerService.DebuggedProcess);
} catch(GetValueException e) {
MessageService.ShowMessage(e.Message);
}
if (shownValue != null && !shownValue.IsNull) {
GridValuesProvider gridValuesProvider;
// Value is IList // Value is IList
DebugType iListType, listItemType; if (shownValue.Type.ResolveIListImplementation(out iListType, out itemType)) {
if (shownValue.Type.ResolveIListImplementation(out iListType, out listItemType)) { // Ok
gridValuesProvider = CreateListValuesProvider(shownExpr.CastToIList(), listItemType);
} else { } else {
// Value is IEnumerable // Value is IEnumerable
DebugType iEnumerableType, itemType;
if (shownValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) { if (shownValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
DebugType debugListType; shownValue = DebuggerHelpers.CreateListFromIEnumeralbe(shownValue, itemType, out iListType);
var debugListExpression = DebuggerHelpers.CreateDebugListExpression(shownExpr, itemType, out debugListType);
gridValuesProvider = CreateListValuesProvider(debugListExpression, itemType);
} else { } else {
// Not IList or IEnumerable<T> - can't be displayed in GridVisualizer // Not IList or IEnumerable<T> - can't be displayed in GridVisualizer
return; return;
} }
} }
shownValue = shownValue.GetPermanentReference();
IList<MemberInfo> itemTypeMembers = gridValuesProvider.GetItemTypeMembers(); MemberInfo[] members = itemType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance);
InitializeColumns((GridView)this.listView.View, itemTypeMembers); PropertyInfo itemGetter = iListType.GetProperty("Item");
this.columnHider = new GridViewColumnHider((GridView)this.listView.View); int rowCount = (int)shownValue.GetPropertyValue(iListType.GetProperty("Count")).PrimitiveValue;
cmbColumns.ItemsSource = this.columnHider.HideableColumns; int columnCount = members.Length + 1;
}
var rowCollection = new VirtualizingCollection<VirtualizingCollection<string>>(
rowCount,
(rowIndex) => new VirtualizingCollection<string>(
columnCount,
(columnIndex) => {
if (columnIndex == members.Length)
return "[" + rowIndex + "]";
try {
var rowValue = shownValue.GetPropertyValue(itemGetter, Eval.CreateValue(shownValue.AppDomain, rowIndex));
return rowValue.GetMemberValue(members[columnIndex]).InvokeToString();
} catch (GetValueException e) { } catch (GetValueException e) {
MessageService.ShowMessage(e.Message); return "Exception: " + e.Message;
} catch (DebuggerVisualizerException e) {
MessageService.ShowMessage(e.Message);
} }
} }
)
);
this.listView.ItemsSource = rowCollection;
ListValuesProvider CreateListValuesProvider(ICSharpCode.NRefactory.Ast.Expression targetExpression, DebugType listItemType) InitializeColumns((GridView)this.listView.View, members);
{
var listValuesProvider = new ListValuesProvider(targetExpression, listItemType); GridViewColumnHider columnHider = new GridViewColumnHider((GridView)this.listView.View);
var virtCollection = new VirtualizingCollection<ObjectValue>(listValuesProvider); cmbColumns.ItemsSource = columnHider.HideableColumns;
this.listView.ItemsSource = virtCollection;
return listValuesProvider; } catch(GetValueException e) {
MessageService.ShowMessage(e.Message);
}
} }
void InitializeColumns(GridView gridView, IList<MemberInfo> itemTypeMembers) void InitializeColumns(GridView gridView, MemberInfo[] members)
{ {
gridView.Columns.Clear(); gridView.Columns.Clear();
AddIndexColumn(gridView);
AddMembersColumns(gridView, itemTypeMembers);
}
void AddIndexColumn(GridView gridView) // Index column
{
var indexColumn = new GridViewHideableColumn(); var indexColumn = new GridViewHideableColumn();
indexColumn.CanBeHidden = false; indexColumn.CanBeHidden = false;
indexColumn.Width = 36; indexColumn.Width = 36;
indexColumn.Header = string.Empty; indexColumn.Header = string.Empty;
indexColumn.DisplayMemberBinding = new Binding("Index"); indexColumn.DisplayMemberBinding = new Binding("[" + members.Length + "]");
gridView.Columns.Add(indexColumn); gridView.Columns.Add(indexColumn);
}
void AddMembersColumns(GridView gridView, IList<MemberInfo> itemTypeMembers) // Member columns
{ for (int i = 0; i < members.Length; i++) {
foreach (var member in itemTypeMembers) {
var memberColumn = new GridViewHideableColumn(); var memberColumn = new GridViewHideableColumn();
memberColumn.CanBeHidden = true; memberColumn.CanBeHidden = true;
memberColumn.Header = member.Name; memberColumn.Header = members[i].Name;
// "{Binding Path=[Name].Value}" memberColumn.IsVisibleDefault = ((IDebugMemberInfo)members[i]).IsPublic;
memberColumn.DisplayMemberBinding = new Binding("[" + member.Name + "].Value"); memberColumn.DisplayMemberBinding = new Binding("[" + i + "]");
gridView.Columns.Add(memberColumn); gridView.Columns.Add(memberColumn);
} }
} }

85
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ObjectValue.cs

@ -1,85 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Services;
using System.Reflection;
namespace Debugger.AddIn.Visualizers.GridVisualizer
{
/// <summary>
/// Item of a collection in the debugee, with lazy evaluated properties.
/// </summary>
public class ObjectValue
{
/// <summary> Index of this item in the collection. </summary>
public int Index { get; private set; }
// PermanentReference for one row.
public Value PermanentReference { get; private set; }
private Dictionary<string, ObjectProperty> properties = new Dictionary<string, ObjectProperty>();
/// <summary> Used to quickly find MemberInfo by member name - DebugType.GetMember(name) uses a loop to search members </summary>
private Dictionary<string, MemberInfo> memberForNameMap;
internal ObjectValue(int index, Dictionary<string, MemberInfo> memberFromNameMap)
{
this.Index = index;
this.memberForNameMap = memberFromNameMap;
}
/// <summary>
/// Returns property with given name.
/// </summary>
public ObjectProperty this[string propertyName]
{
get
{
ObjectProperty property;
// has property with name 'propertyName' already been evaluated?
if(!this.properties.TryGetValue(propertyName, out property))
{
if (this.PermanentReference == null) {
throw new DebuggerVisualizerException("Cannot get member of this ObjectValue - ObjectValue.PermanentReference is null");
}
MemberInfo memberInfo = this.memberForNameMap.GetValue(propertyName);
if (memberInfo == null) {
throw new DebuggerVisualizerException("Cannot get member value - no member found with name " + propertyName);
}
property = CreatePropertyFromValue(propertyName, this.PermanentReference.GetMemberValue(memberInfo));
this.properties.Add(propertyName, property);
}
return property;
}
}
public static ObjectValue Create(Debugger.Value value, int index, Dictionary<string, MemberInfo> memberFromName)
{
ObjectValue result = new ObjectValue(index, memberFromName);
// remember PermanentReference for expanding IEnumerable
Value permanentReference = value.GetPermanentReference();
result.PermanentReference = permanentReference;
return result;
}
private static ObjectProperty CreatePropertyFromValue(string propertyName, Value value)
{
ObjectProperty property = new ObjectProperty();
property.Name = propertyName;
// property.Expression = ?.Age
// - cannot use expression,
// if the value is returned from an enumerator, it has no meaningful expression
property.IsAtomic = value.Type.IsPrimitive;
property.IsNull = value.IsNull;
property.Value = value.IsNull ? "" : value.InvokeToString();
return property;
}
}
}

50
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/EnumerableValuesProvider.cs

@ -1,50 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Services;
using System.Reflection;
namespace Debugger.AddIn.Visualizers.GridVisualizer
{
/// <summary>
/// Provides <see cref="ObjectValue"/>s for debugee objects implementing IEnumerable.
/// </summary>
/*public class EnumerableValuesProvider : GridValuesProvider
{
public EnumerableValuesProvider(Expression targetObject, DebugType iEnumerableType, DebugType itemType)
:base(targetObject, iEnumerableType, itemType)
{
this.itemsSource = enumerateItems();
}
private IEnumerable<ObjectValue> itemsSource;
public IEnumerable<ObjectValue> ItemsSource
{
get { return this.itemsSource; }
}
private IEnumerable<ObjectValue> enumerateItems()
{
MethodInfo enumeratorMethod = collectionType.GetMethod("GetEnumerator");
Value enumerator = targetObject.Evaluate(WindowsDebugger.CurrentProcess).InvokeMethod(enumeratorMethod, null).GetPermanentReference();
MethodInfo moveNextMethod = enumerator.Type.GetMethod("MoveNext");
PropertyInfo currentproperty = enumerator.Type.GetInterface(typeof(IEnumerator).FullName).GetProperty("Current");
int index = 0;
while ((bool)enumerator.InvokeMethod(moveNextMethod, null).PrimitiveValue)
{
Value currentValue = enumerator.GetPropertyValue(currentproperty);
yield return ObjectValue.Create(currentValue, index++, this.memberFromNameMap);
}
}
}*/
}

42
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/GridValuesProvider.cs

@ -1,42 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.NRefactory.Ast;
using System.Reflection;
namespace Debugger.AddIn.Visualizers.GridVisualizer
{
/// <summary>
/// Provides <see cref="ObjectValue"/>s to be displayed in Grid visualizer.
/// Descandants implement getting values for IList and IEnumerable.
/// </summary>
public class GridValuesProvider
{
/// <summary> Used to quickly find MemberInfo by member name - DebugType.GetMember(name) uses a loop to search members </summary>
protected Dictionary<string, MemberInfo> memberFromNameMap;
protected Expression targetObject;
protected DebugType itemType;
public GridValuesProvider(Expression targetObject, DebugType itemType)
{
this.targetObject = targetObject;
this.itemType = itemType;
this.memberFromNameMap = this.GetItemTypeMembers().MakeDictionary(memberInfo => memberInfo.Name);
}
/// <summary>
/// Gets members that will be displayed as columns.
/// </summary>
public IList<MemberInfo> GetItemTypeMembers()
{
var publicPropetiesAndFields = itemType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance);
return publicPropetiesAndFields;
}
}
}

59
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/ValueProviders/ListValuesProvider.cs

@ -1,59 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Visualizers.GridVisualizer
{
/// <summary>
/// Provides <see cref="ObjectValue"/>s for debugee objects implementing IList.
/// </summary>
public class ListValuesProvider : GridValuesProvider, IListValuesProvider<ObjectValue>
{
int? listCount = null;
/// <summary>
/// After evaluating how many items to clear debugger Expression cache,
/// so that the cache does not keep too many PermanentReferences.
/// </summary>
static readonly int ClearCacheThreshold = 50;
public ListValuesProvider(Expression targetObject, DebugType listItemType)
:base(targetObject, listItemType)
{
}
public int GetCount()
{
if (this.listCount == null) {
this.listCount = this.targetObject.GetIListCount();
}
return this.listCount.Value;
}
/// <summary>When this reaches ClearCacheThreshold, the debugger Expression cache is cleared.</summary>
int itemClearCacheCounter = 0;
public ObjectValue GetItemAt(int index)
{
if (itemClearCacheCounter++ > ClearCacheThreshold) {
// clear debugger Expression cache to avoid holding too many PermanentReferences
WindowsDebugger.CurrentProcess.ClearExpressionCache();
LoggingService.Info("Cleared debugger Expression cache.");
itemClearCacheCounter = 0;
}
return ObjectValue.Create(
targetObject.AppendIndexer(index).Evaluate(WindowsDebugger.CurrentProcess),
//targetObject.AppendIndexer(index), // use Expression instead of value - possible only for IList though
index,
this.memberFromNameMap);
}
}
}

12
src/AddIns/Debugger/Debugger.AddIn/Visualizers/TextVisualizer/TextVisualizerMode.cs

@ -1,12 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System.Collections.Generic;
using System.Linq;
using System;
public enum TextVisualizerMode
{
PlainText,
Xml
}

46
src/AddIns/Debugger/Debugger.AddIn/Visualizers/TextVisualizer/TextVisualizerWindow.xaml.cs

@ -1,17 +1,9 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) // This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using ICSharpCode.AvalonEdit.Highlighting;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using ICSharpCode.AvalonEdit.Highlighting;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Debugger.AddIn.Visualizers.TextVisualizer namespace Debugger.AddIn.Visualizers.TextVisualizer
{ {
@ -20,42 +12,14 @@ namespace Debugger.AddIn.Visualizers.TextVisualizer
/// </summary> /// </summary>
public partial class TextVisualizerWindow : Window public partial class TextVisualizerWindow : Window
{ {
public TextVisualizerWindow() public TextVisualizerWindow(string title, string text, string highlighting = null)
{
InitializeComponent();
Mode = TextVisualizerMode.PlainText;
textEditor.IsReadOnly = true;
}
public TextVisualizerWindow(string title, string text)
{ {
InitializeComponent(); InitializeComponent();
Title = title; Title = title;
Text = text; this.textEditor.Text = text;
} if (highlighting != null)
this.textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(highlighting);
public string Text
{
get { return this.textEditor.Text; }
set { this.textEditor.Text = value; }
}
private TextVisualizerMode mode;
public TextVisualizerMode Mode
{
get { return mode; }
set {
mode = value;
switch (mode) {
case TextVisualizerMode.PlainText:
textEditor.SyntaxHighlighting = null;
break;
case TextVisualizerMode.Xml:
textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
break;
}
}
} }
void CheckBox_CheckedChanged(object sender, RoutedEventArgs e) void CheckBox_CheckedChanged(object sender, RoutedEventArgs e)

13
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Utils/DebuggerHelpers.cs

@ -33,6 +33,17 @@ namespace Debugger.AddIn.Visualizers.Utils
return new ObjectCreateExpression(listType.GetTypeReference(), iEnumerableVariableExplicitCast.SingleItemList()); return new ObjectCreateExpression(listType.GetTypeReference(), iEnumerableVariableExplicitCast.SingleItemList());
} }
public static Value CreateListFromIEnumeralbe(Value iEnumerableValue, DebugType itemType, out DebugType listType)
{
listType = DebugType.CreateFromType(iEnumerableValue.AppDomain, typeof(System.Collections.Generic.List<>), itemType);
DebugType iEnumerableType = DebugType.CreateFromType(itemType.AppDomain, typeof(IEnumerable<>), itemType);
ConstructorInfo ctor = listType.GetConstructor(BindingFlags.Default, null, CallingConventions.Any, new System.Type[] { iEnumerableType }, null);
if (ctor == null)
throw new DebuggerException("List<T> constructor not found");
return (Value)ctor.Invoke(new object[] { iEnumerableValue });
}
/// <summary> /// <summary>
/// Gets underlying address of object in the debuggee. /// Gets underlying address of object in the debuggee.
/// </summary> /// </summary>
@ -120,7 +131,7 @@ namespace Debugger.AddIn.Visualizers.Utils
Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess); Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess);
var iCollectionType = list.Type.GetInterface(typeof(System.Collections.ICollection).FullName); var iCollectionType = list.Type.GetInterface(typeof(System.Collections.ICollection).FullName);
if (iCollectionType == null) if (iCollectionType == null)
throw new GetValueException(targetObject, targetObject.PrettyPrint() + " does not implement System.Collections.ICollection"); throw new GetValueException("Object does not implement System.Collections.ICollection");
// Do not get string representation since it can be printed in hex // Do not get string representation since it can be printed in hex
return (int)list.GetPropertyValue(iCollectionType.GetProperty("Count")).PrimitiveValue; return (int)list.GetPropertyValue(iCollectionType.GetProperty("Count")).PrimitiveValue;
} }

12
src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj

@ -144,8 +144,6 @@
<Compile Include="Mono.Cecil\Mono.Cecil\MethodCallingConvention.cs" /> <Compile Include="Mono.Cecil\Mono.Cecil\MethodCallingConvention.cs" />
<Compile Include="Mono.Cecil\Mono.Cecil\ReflectionException.cs" /> <Compile Include="Mono.Cecil\Mono.Cecil\ReflectionException.cs" />
<Compile Include="NDebugger.cs" /> <Compile Include="NDebugger.cs" />
<Compile Include="NRefactory\Ast\ExpressionExtensionMethods.cs" />
<Compile Include="NRefactory\Visitors\ExpressionEvaluator.cs" />
<Compile Include="Options.cs" /> <Compile Include="Options.cs" />
<Compile Include="PausedReason.cs" /> <Compile Include="PausedReason.cs" />
<Compile Include="PauseSession.cs" /> <Compile Include="PauseSession.cs" />
@ -198,17 +196,7 @@
<Folder Include="Mono.Cecil\Mono.Cecil.Binary" /> <Folder Include="Mono.Cecil\Mono.Cecil.Binary" />
<Folder Include="Mono.Cecil\Mono.Cecil.Metadata" /> <Folder Include="Mono.Cecil\Mono.Cecil.Metadata" />
<Folder Include="Mono.Cecil\Mono.Cecil.Signatures" /> <Folder Include="Mono.Cecil\Mono.Cecil.Signatures" />
<Folder Include="NRefactory" />
<Folder Include="NRefactory\Ast" />
<Folder Include="NRefactory\Visitors" />
<Folder Include="Tests" /> <Folder Include="Tests" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project> </Project>

34
src/AddIns/Debugger/Debugger.Core/GetValueException.cs

@ -2,55 +2,21 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using ICSharpCode.NRefactory.Ast;
namespace Debugger namespace Debugger
{ {
public class GetValueException: DebuggerException public class GetValueException: DebuggerException
{ {
INode expression;
string error;
/// <summary> Expression that has caused this exception to occur </summary>
public INode Expression {
get { return expression; }
set { expression = value; }
}
public string Error {
get { return error; }
}
public override string Message {
get {
if (expression == null) {
return error;
} else {
return error;
// return String.Format("Error evaluating \"{0}\": {1}", expression.PrettyPrint(), error);
}
}
}
public GetValueException(INode expression, string error):base(error)
{
this.expression = expression;
this.error = error;
}
public GetValueException(string error, System.Exception inner):base(error, inner) public GetValueException(string error, System.Exception inner):base(error, inner)
{ {
this.error = error;
} }
public GetValueException(string errorFmt, params object[] args):base(string.Format(errorFmt, args)) public GetValueException(string errorFmt, params object[] args):base(string.Format(errorFmt, args))
{ {
this.error = string.Format(errorFmt, args);
} }
public GetValueException(string error):base(error) public GetValueException(string error):base(error)
{ {
this.error = error;
} }
} }
} }

4
src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs

@ -246,14 +246,14 @@ namespace Debugger
public void EvalException(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval) public void EvalException(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval)
{ {
EnterCallback(PausedReason.EvalComplete, "EvalException", pThread); EnterCallback(PausedReason.EvalComplete, "EvalException (" + process.ActiveEvals[corEval].Description + ")", pThread);
HandleEvalComplete(pAppDomain, pThread, corEval, true); HandleEvalComplete(pAppDomain, pThread, corEval, true);
} }
public void EvalComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval) public void EvalComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval)
{ {
EnterCallback(PausedReason.EvalComplete, "EvalComplete", pThread); EnterCallback(PausedReason.EvalComplete, "EvalComplete (" + process.ActiveEvals[corEval].Description + ")", pThread);
HandleEvalComplete(pAppDomain, pThread, corEval, false); HandleEvalComplete(pAppDomain, pThread, corEval, false);
} }

18
src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs

@ -639,7 +639,7 @@ namespace Debugger.MetaData
{ {
List<DebugLocalVariableInfo> vars = new List<DebugLocalVariableInfo>(); List<DebugLocalVariableInfo> vars = new List<DebugLocalVariableInfo>();
foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) { foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) {
ISymUnmanagedVariable symVarCopy = symVar; uint address = (uint)symVar.GetAddressField1();
int start; int start;
SignatureReader sigReader = new SignatureReader(symVar.GetSignature()); SignatureReader sigReader = new SignatureReader(symVar.GetSignature());
LocalVarSig.LocalVariable locVarSig = sigReader.ReadLocalVariable(sigReader.Blob, 0, out start); LocalVarSig.LocalVariable locVarSig = sigReader.ReadLocalVariable(sigReader.Blob, 0, out start);
@ -654,7 +654,7 @@ namespace Debugger.MetaData
(int)symScope.GetStartOffset(), (int)symScope.GetStartOffset(),
(int)symScope.GetEndOffset(), (int)symScope.GetEndOffset(),
delegate(StackFrame context) { delegate(StackFrame context) {
return GetLocalVariableValue(context, symVarCopy); return context.GetLocalVariableValue(address);
}, },
locVarType locVarType
); );
@ -668,7 +668,7 @@ namespace Debugger.MetaData
(int)symScope.GetEndOffset(), (int)symScope.GetEndOffset(),
locVarType, locVarType,
delegate(StackFrame context) { delegate(StackFrame context) {
return GetLocalVariableValue(context, symVarCopy); return context.GetLocalVariableValue(address);
} }
); );
vars.Add(locVar); vars.Add(locVar);
@ -680,18 +680,6 @@ namespace Debugger.MetaData
return vars; return vars;
} }
static Value GetLocalVariableValue(StackFrame context, ISymUnmanagedVariable symVar)
{
ICorDebugValue corVal;
try {
corVal = context.CorILFrame.GetLocalVariable((uint)symVar.GetAddressField1());
} catch (COMException e) {
if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
throw;
}
return new Value(context.AppDomain, corVal);
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ {

1
src/AddIns/Debugger/Debugger.Core/MetaData/DebugType.cs

@ -9,7 +9,6 @@ using System.Text;
using Debugger.Interop.CorDebug; using Debugger.Interop.CorDebug;
using Debugger.Interop.MetaData; using Debugger.Interop.MetaData;
using ICSharpCode.NRefactory.Ast;
using Mono.Cecil.Signatures; using Mono.Cecil.Signatures;
namespace Debugger.MetaData namespace Debugger.MetaData

17
src/AddIns/Debugger/Debugger.Core/Process.cs

@ -7,8 +7,6 @@ using System.Runtime.InteropServices;
using Debugger.Interop.CorDebug; using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym; using Debugger.Interop.CorSym;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
namespace Debugger namespace Debugger
{ {
@ -251,7 +249,6 @@ namespace Debugger
internal bool TerminateCommandIssued = false; internal bool TerminateCommandIssued = false;
internal Queue<Breakpoint> BreakpointHitEventQueue = new Queue<Breakpoint>(); internal Queue<Breakpoint> BreakpointHitEventQueue = new Queue<Breakpoint>();
internal Dictionary<INode, TypedValue> ExpressionsCache = new Dictionary<INode, TypedValue>();
#region Events #region Events
@ -330,7 +327,6 @@ namespace Debugger
if (action == DebuggeeStateAction.Clear) { if (action == DebuggeeStateAction.Clear) {
if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared"); if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared");
debuggeeState = null; debuggeeState = null;
this.ExpressionsCache.Clear();
} }
} }
@ -557,19 +553,6 @@ namespace Debugger
// This is done once ExitProcess callback is received // This is done once ExitProcess callback is received
} }
/// <summary>
/// Clears the internal Expression cache used too speed up Expression evaluation.
/// Use this if your code evaluates expressions in a way which would cause
/// the cache to grow too large. The cache holds PermanentReferences so it
/// shouldn't grow larger than a few hundred items.
/// </summary>
public void ClearExpressionCache()
{
if (this.ExpressionsCache != null ){
this.ExpressionsCache.Clear();
}
}
void SelectSomeThread() void SelectSomeThread()
{ {
if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) { if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) {

10
src/AddIns/Debugger/Debugger.Core/StackFrame.cs

@ -380,6 +380,16 @@ namespace Debugger
return loc.GetValue(this); return loc.GetValue(this);
} }
public Value GetLocalVariableValue(uint address)
{
try {
return new Value(this.AppDomain, this.CorILFrame.GetLocalVariable(address));
} catch (COMException e) {
if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
throw;
}
}
/// <summary> Get instance of 'this'. It works well with delegates and enumerators. </summary> /// <summary> Get instance of 'this'. It works well with delegates and enumerators. </summary>
[Debugger.Tests.Ignore] [Debugger.Tests.Ignore]
public Value GetLocalVariableThis() public Value GetLocalVariableThis()

1
src/AddIns/Debugger/Debugger.Core/Value.cs

@ -613,6 +613,7 @@ namespace Debugger
/// <summary> Invoke the ToString() method </summary> /// <summary> Invoke the ToString() method </summary>
public string InvokeToString(int maxLength = int.MaxValue) public string InvokeToString(int maxLength = int.MaxValue)
{ {
if (this.IsNull) return AsString(maxLength);
if (this.Type.IsPrimitive) return AsString(maxLength); if (this.Type.IsPrimitive) return AsString(maxLength);
if (this.Type.FullName == typeof(string).FullName) return AsString(maxLength); if (this.Type.FullName == typeof(string).FullName) return AsString(maxLength);
if (this.Type.IsPointer) return "0x" + this.PointerAddress.ToString("X"); if (this.Type.IsPointer) return "0x" + this.PointerAddress.ToString("X");

4
src/AddIns/Debugger/Debugger.Tests/Debugger.Tests.csproj

@ -77,6 +77,10 @@
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project> <Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name> <Name>NRefactory</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Debugger.AddIn\Debugger.AddIn.csproj">
<Project>{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C}</Project>
<Name>Debugger.AddIn</Name>
</ProjectReference>
<ProjectReference Include="..\Debugger.Core\Debugger.Core.csproj"> <ProjectReference Include="..\Debugger.Core\Debugger.Core.csproj">
<Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project> <Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project>
<Name>Debugger.Core</Name> <Name>Debugger.Core</Name>

2
src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs

@ -152,7 +152,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
b.Append('|'); b.Append('|');
b.Append(node.ImageName); b.Append(node.ImageName);
b.Append('|'); b.Append('|');
b.Append(node.FullName); b.Append(node.Name);
b.Append('|'); b.Append('|');
b.Append(node.Text); b.Append(node.Text);
} }

12
src/Main/Base/Project/Src/Services/Debugger/Tooltips/ITreeNode.cs

@ -10,14 +10,12 @@ namespace ICSharpCode.SharpDevelop.Debugging
/// <summary> /// <summary>
/// Node that can be bound to <see cref="DebuggerTooltipControl" />. /// Node that can be bound to <see cref="DebuggerTooltipControl" />.
/// </summary> /// </summary>
public interface ITreeNode : IComparable<ITreeNode> public interface ITreeNode
{ {
string Name { get; }
string FullName { get; }
string ImageName { get; } string ImageName { get; }
string Name { get; }
string Text { get; } string Text { get; }
bool CanSetText { get; } bool CanSetText { get; }
@ -26,9 +24,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
ImageSource ImageSource { get; } ImageSource ImageSource { get; }
IEnumerable<ITreeNode> ChildNodes { get; } Func<IEnumerable<ITreeNode>> GetChildren { get; }
bool HasChildNodes { get; }
IEnumerable<IVisualizerCommand> VisualizerCommands { get; } IEnumerable<IVisualizerCommand> VisualizerCommands { get; }

4
src/Main/Base/Project/Src/Services/Debugger/Tooltips/PinBookmark.cs

@ -77,7 +77,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
throw new ArgumentNullException("Node is null"); throw new ArgumentNullException("Node is null");
foreach (var currentNode in mark.Nodes) { foreach (var currentNode in mark.Nodes) {
if (node.FullName == currentNode.FullName) if (node.Name == currentNode.Name)
return true; return true;
} }
@ -92,7 +92,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
throw new ArgumentNullException("Node is null"); throw new ArgumentNullException("Node is null");
foreach (var currentNode in mark.Nodes) { foreach (var currentNode in mark.Nodes) {
if (node.FullName == currentNode.FullName) { if (node.Name == currentNode.Name) {
mark.Nodes.Remove(currentNode); mark.Nodes.Remove(currentNode);
return; return;
} }

Loading…
Cancel
Save