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. 32
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
  9. 49
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs
  10. 25
      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. 76
      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. 115
      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. 199
      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. 48
      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. 14
      src/AddIns/Debugger/Debugger.Core/Eval.cs
  47. 34
      src/AddIns/Debugger/Debugger.Core/GetValueException.cs
  48. 4
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  49. 18
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
  50. 1
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugType.cs
  51. 17
      src/AddIns/Debugger/Debugger.Core/Process.cs
  52. 10
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  53. 1
      src/AddIns/Debugger/Debugger.Core/Value.cs
  54. 4
      src/AddIns/Debugger/Debugger.Tests/Debugger.Tests.csproj
  55. 2
      src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs
  56. 12
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/ITreeNode.cs
  57. 4
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/PinBookmark.cs

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

@ -97,6 +97,8 @@ @@ -97,6 +97,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="NRefactory\ExpressionEvaluator.cs" />
<Compile Include="NRefactory\ExpressionExtensionMethods.cs" />
<Compile Include="Options\DebuggingOptionsPanel.xaml.cs">
<DependentUpon>DebuggingOptionsPanel.xaml</DependentUpon>
<SubType>Code</SubType>
@ -161,7 +163,6 @@ @@ -161,7 +163,6 @@
<Compile Include="Pads\WatchPad.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Pads\WatchPadModel.cs" />
<Compile Include="Service\AttachToProcessForm.cs">
<SubType>Form</SubType>
</Compile>
@ -192,11 +193,6 @@ @@ -192,11 +193,6 @@
<Compile Include="Tooltips\VisualizerPicker.cs">
<DependentUpon>VisualizerPicker.xaml</DependentUpon>
</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="Visualizers\Commands\ExpressionVisualizerCommand.cs" />
<Compile Include="Visualizers\Commands\GridVisualizerCommand.cs" />
@ -209,7 +205,6 @@ @@ -209,7 +205,6 @@
<Compile Include="Visualizers\Commands\XmlVisualizerCommand.cs" />
<Compile Include="Visualizers\Common\DebuggerVisualizerException.cs" />
<Compile Include="Visualizers\Common\IEvaluate.cs" />
<Compile Include="Visualizers\Common\IListValuesProvider.cs" />
<Compile Include="Visualizers\Common\ObjectProperty.cs" />
<Compile Include="Visualizers\Common\ObjectPropertyComparer.cs" />
<Compile Include="Visualizers\Common\VirtualizingCollection.cs" />
@ -253,12 +248,10 @@ @@ -253,12 +248,10 @@
<Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\ThisNode.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\TreeModel\NonPublicMembersNode.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\GridViewColumnHider.cs" />
<Compile Include="Visualizers\PresentationBindings\GridViewHideableColumn.cs" />
<Compile Include="Visualizers\PresentationBindings\TooltipVisibilityConverter.cs" />
<Compile Include="Visualizers\TextVisualizer\TextVisualizerMode.cs" />
<Compile Include="Visualizers\TextVisualizer\TextVisualizerWindow.xaml.cs">
<DependentUpon>TextVisualizerWindow.xaml</DependentUpon>
<SubType>Code</SubType>
@ -288,8 +281,6 @@ @@ -288,8 +281,6 @@
<Compile Include="Pads\RunningThreadsPad.Menu.cs" />
<Compile Include="TreeModel\ArrayRangeNode.cs" />
<Compile Include="TreeModel\ExpressionNode.cs" />
<Compile Include="TreeModel\IContextMenu.cs" />
<Compile Include="TreeModel\ISetText.cs" />
<Compile Include="TreeModel\ChildNodesOfObject.cs" />
<Compile Include="TreeModel\StackFrameNode.cs" />
<Compile Include="TreeModel\Utils.cs" />
@ -312,9 +303,6 @@ @@ -312,9 +303,6 @@
<DependentUpon>GridVisualizerWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</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\DictionaryExtensions.cs" />
<Compile Include="Visualizers\Utils\Lookup.cs" />
@ -324,7 +312,6 @@ @@ -324,7 +312,6 @@
</Compile>
<Compile Include="Visualizers\PresentationBindings\DisplayAttribute.cs" />
<Compile Include="Visualizers\PresentationBindings\EnumViewModel.cs" />
<None Include="COPYING" />
</ItemGroup>
<ItemGroup>
<Content Include="Debugger.AddIn.addin">
@ -408,6 +395,7 @@ @@ -408,6 +395,7 @@
<Name>ICSharpCode.Core.WinForms</Name>
<Private>False</Private>
</ProjectReference>
<Folder Include="NRefactory" />
<Folder Include="Pads\Commands" />
<Folder Include="Pads\Controls" />
<Folder Include="Pads\ParallelPad" />
@ -423,7 +411,6 @@ @@ -423,7 +411,6 @@
<Folder Include="Visualizers\Graph\ExpandedPaths" />
<Folder Include="Visualizers\Graph\ObjectGraph\TreeModel" />
<Folder Include="Visualizers\Graph\ObjectGraph" />
<Folder Include="Visualizers\GridVisualizer\ValueProviders" />
<Folder Include="Visualizers\Common" />
<Folder Include="Visualizers\Commands" />
<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 @@ -15,8 +15,8 @@ namespace ICSharpCode.NRefactory.Visitors
{
public class EvaluateException: GetValueException
{
public EvaluateException(INode code, string msg):base(code, msg) {}
public EvaluateException(INode code, string msgFmt, params string[] msgArgs):base(code, string.Format(msgFmt, msgArgs)) {}
public EvaluateException(INode code, string msg):base(msg) {}
public EvaluateException(INode code, string msgFmt, params string[] msgArgs):base(string.Format(msgFmt, msgArgs)) {}
}
class TypedValue
@ -163,13 +163,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -163,13 +163,7 @@ namespace ICSharpCode.NRefactory.Visitors
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;
if (context.Process.ExpressionsCache.TryGetValue(expression, out val)) {
if (val == null || !val.Value.IsInvalid)
return val;
}
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
@ -177,11 +171,8 @@ namespace ICSharpCode.NRefactory.Visitors @@ -177,11 +171,8 @@ namespace ICSharpCode.NRefactory.Visitors
val = (TypedValue)expression.AcceptVisitor(this, data);
if (val != null && permRef)
val = new TypedValue(val.Value.GetPermanentReference(), val.Type);
} catch (GetValueException e) {
e.Expression = expression;
throw;
} catch (NotImplementedException e) {
throw new GetValueException(expression, "Language feature not implemented: " + e.Message);
throw new EvaluateException(expression, "Language feature not implemented: " + e.Message);
} finally {
watch.Stop();
context.Process.TraceMessage("Evaluated: {0} in {1} ms total", expression.PrettyPrint(), watch.ElapsedMilliseconds);
@ -190,9 +181,6 @@ namespace ICSharpCode.NRefactory.Visitors @@ -190,9 +181,6 @@ namespace ICSharpCode.NRefactory.Visitors
if (val != null && val.Value.IsInvalid)
throw new DebuggerException("Expression \"" + expression.PrettyPrint() + "\" is invalid right after evaluation");
// Add the result to cache
context.Process.ExpressionsCache[expression] = 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 @@ -28,7 +28,6 @@ namespace ICSharpCode.SharpDevelop.Services
DebuggeeExceptionWindowState = FormWindowState.Normal;
}
public bool ICorDebugVisualizerEnabled { get; set; }
public ShowIntegersAs ShowIntegersAs { get; set; }
public bool ShowArgumentNames { get; set; }
public bool ShowArgumentValues { get; set; }

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

@ -2,18 +2,12 @@ @@ -2,18 +2,12 @@
// 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.Windows.Forms;
using Debugger.AddIn.Pads;
using Debugger.AddIn.Pads.Controls;
using Debugger.AddIn.TreeModel;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.Core.WinForms;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui.Pads;
using ICSharpCode.SharpDevelop.Project;
@ -41,11 +35,10 @@ namespace Debugger.AddIn @@ -41,11 +35,10 @@ namespace Debugger.AddIn
string language = ProjectService.CurrentProject.Language;
var text = new TextNode(null, input,
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp).ToSharpTreeNode();
var text = new TreeNode(input, null).ToSharpTreeNode();
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);
}
@ -108,66 +101,4 @@ namespace Debugger.AddIn @@ -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 @@ -28,22 +28,22 @@ namespace Debugger.AddIn.Pads.Controls
public TreeNode Node { get; private set; }
public override object Text {
get { return Node.Name; }
get { return this.Node.Name; }
}
public override object Icon {
get { return Node.ImageSource; }
get { return this.Node.ImageSource; }
}
public override bool ShowExpander {
get { return Node.HasChildNodes; }
get { return this.Node.GetChildren != null; }
}
protected override void LoadChildren()
{
if (Node.HasChildNodes) {
((WindowsDebugger)DebuggerService.CurrentDebugger).DebuggedProcess
.EnqueueWork(Dispatcher.CurrentDispatcher, () => Children.AddRange(Node.ChildNodes.Select(node => node.ToSharpTreeNode())));
if (this.Node.GetChildren != null) {
var process = ((WindowsDebugger)DebuggerService.CurrentDebugger).DebuggedProcess;
process.EnqueueWork(Dispatcher.CurrentDispatcher, () => Children.AddRange(this.Node.GetChildren().Select(node => node.ToSharpTreeNode())));
}
}
}
@ -69,12 +69,10 @@ namespace Debugger.AddIn.Pads.Controls @@ -69,12 +69,10 @@ namespace Debugger.AddIn.Pads.Controls
string language = ProjectService.CurrentProject.Language;
// FIXME languages
TextNode text = new TextNode(null, e.Data.GetData(DataFormats.StringFormat).ToString(),
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp);
var text = new TreeNode(e.Data.GetData(DataFormats.StringFormat).ToString(), null);
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.InvalidatePad();

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

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

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

@ -17,6 +17,7 @@ using Debugger.AddIn.TreeModel; @@ -17,6 +17,7 @@ using Debugger.AddIn.TreeModel;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Project;
using Exception = System.Exception;
@ -78,29 +79,26 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -78,29 +79,26 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
return;
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)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 0) {
var props = GetSavedVariablesProperties();
if (props == null) return;
if (e.Action == NotifyCollectionChangedAction.Add) {
// add to saved data
var data = e.NewItems.OfType<TextNode>().FirstOrDefault();
if (data != null) {
var props = GetSavedVariablesProperties();
if (props == null) return;
props.Set(data.FullName, data.Language.ToString());
foreach(var data in e.NewItems.OfType<TreeNode>()) {
props.Set(data.Name, (object)null);
}
}
if (e.Action == NotifyCollectionChangedAction.Remove) {
// remove from saved data
var data = e.OldItems.OfType<TextNode>().FirstOrDefault();
if (data != null) {
var props = GetSavedVariablesProperties();
if (props == null) return;
props.Remove(data.FullName);
foreach(var data in e.OldItems.OfType<TreeNode>()) {
props.Remove(data.Name);
}
}
}
@ -162,9 +160,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -162,9 +160,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
// rebuild list
var nodes = new List<TreeNodeWrapper>();
foreach (var nod in watchList.WatchItems.OfType<TreeNodeWrapper>())
nodes.Add(new TextNode(null, nod.Node.Name,
language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp)
.ToSharpTreeNode());
nodes.Add(new TreeNode(nod.Node.Name, null).ToSharpTreeNode());
watchList.WatchItems.Clear();
foreach (var nod in nodes)
@ -194,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -194,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{
try {
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);
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();
} catch (GetValueException) {
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();
}
}

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

@ -1,49 +0,0 @@ @@ -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");
}
}
}

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

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

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

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

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

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

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

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

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

@ -10,41 +10,10 @@ using ICSharpCode.SharpDevelop.Debugging; @@ -10,41 +10,10 @@ using ICSharpCode.SharpDevelop.Debugging;
namespace Debugger.AddIn.TreeModel
{
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;
Expression arrayTarget;
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()
public static TreeNode GetArrayRangeNode(ExpressionNode expr, ArrayDimensions bounds, ArrayDimensions originalBounds)
{
StringBuilder name = new StringBuilder();
bool isFirst = true;
@ -56,7 +25,7 @@ namespace Debugger.AddIn.TreeModel @@ -56,7 +25,7 @@ namespace Debugger.AddIn.TreeModel
ArrayDimension originalDim = originalBounds[i];
if (dim.Count == 0) {
throw new DebuggerException("Empty dimension");
name.Append("-");
} else if (dim.Count == 1) {
name.Append(dim.LowerBound.ToString());
} else if (dim.Equals(originalDim)) {
@ -68,33 +37,32 @@ namespace Debugger.AddIn.TreeModel @@ -68,33 +37,32 @@ namespace Debugger.AddIn.TreeModel
}
}
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)
{
StringBuilder sb = new StringBuilder(indices.Length * 4);
sb.Append("[");
bool isFirst = true;
foreach(int index in indices) {
if (!isFirst) sb.Append(", ");
sb.Append(index.ToString());
isFirst = false;
if (bounds.TotalElementCount == 0)
{
yield return new TreeNode("(empty)", null);
yield break;
}
sb.Append("]");
return sb.ToString();
}
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;
StringBuilder sb = new StringBuilder(indices.Length * 4);
sb.Append("[");
bool isFirst = true;
foreach(int index in indices) {
if (!isFirst) sb.Append(", ");
sb.Append(index.ToString());
isFirst = false;
}
sb.Append("]");
int[] indicesCopy = indices;
yield return new ExpressionNode("Icons.16x16.Field", sb.ToString(), () => arrayTarget.Evaluate().GetArrayElement(indicesCopy));
}
yield break;
}
@ -117,7 +85,7 @@ namespace Debugger.AddIn.TreeModel @@ -117,7 +85,7 @@ namespace Debugger.AddIn.TreeModel
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) {
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds);
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;
}

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

@ -1,10 +1,11 @@ @@ -1,10 +1,11 @@
// 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 System.Reflection;
using System.Linq;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.Core;
@ -17,7 +18,7 @@ namespace Debugger.AddIn.TreeModel @@ -17,7 +18,7 @@ namespace Debugger.AddIn.TreeModel
{
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[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
@ -27,116 +28,94 @@ namespace Debugger.AddIn.TreeModel @@ -27,116 +28,94 @@ namespace Debugger.AddIn.TreeModel
DebugType baseType = (DebugType)shownType.BaseType;
if (baseType != null) {
yield return new TreeNode(
DebuggerResourceService.GetImage("Icons.16x16.Class"),
"Icons.16x16.Class",
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.BaseClass}"),
baseType.Name,
baseType.FullName,
current,
newNode => baseType.FullName == "System.Object" ? null : Utils.LazyGetChildNodesOfObject(newNode, targetObject, baseType)
baseType.FullName == "System.Object" ? (Func<IEnumerable<TreeNode>>) null : () => Utils.GetChildNodesOfObject(expr, baseType)
);
}
if (nonPublicInstance.Length > 0) {
yield return new TreeNode(
null,
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicMembers}"),
string.Empty,
string.Empty,
current,
newNode => Utils.LazyGetMembersOfObject(newNode, targetObject, nonPublicInstance)
() => GetMembersOfObject(expr, nonPublicInstance)
);
}
if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) {
yield return new TreeNode(
null,
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.StaticMembers}"),
string.Empty,
string.Empty,
current,
p => {
var children = Utils.LazyGetMembersOfObject(p, targetObject, publicStatic);
() => {
var children = GetMembersOfObject(expr, publicStatic).ToList();
if (nonPublicStatic.Length > 0) {
TreeNode nonPublicStaticNode = new TreeNode(
null,
children.Insert(0, new TreeNode(
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicStaticMembers}"),
string.Empty,
string.Empty,
p,
newNode => Utils.LazyGetMembersOfObject(newNode, targetObject, nonPublicStatic)
);
children = Utils.PrependNode(nonPublicStaticNode, children);
() => GetMembersOfObject(expr, nonPublicStatic)
));
}
return children;
}
);
}
DebugType iListType = (DebugType)shownType.GetInterface(typeof(IList).FullName);
if (iListType != null) {
yield return new IListNode(current, targetObject);
if (shownType.GetInterface(typeof(IList).FullName) != null) {
yield return new TreeNode(
"IList",
() => GetItemsOfIList(() => expr.Evaluate())
);
} else {
DebugType iEnumerableType, itemType;
DebugType listType, iEnumerableType, 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;
}
}
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) {
string imageName;
var image = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo, out imageName);
var exp = new ExpressionNode(parent, image, memberInfo.Name, expression.AppendMemberReference((IDebugMemberInfo)memberInfo));
exp.ImageName = imageName;
nodes.Add(exp);
foreach(MemberInfo memberInfo in members.OrderBy(m => m.Name)) {
var memberInfoCopy = memberInfo;
string imageName = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo);
yield return new ExpressionNode(imageName, memberInfo.Name, () => expr.Evaluate().GetMemberValue(memberInfoCopy));
}
nodes.Sort();
return nodes;
}
public static IEnumerable<TreeNode> LazyGetItemsOfIList(TreeNode parent, Expression targetObject)
public static IEnumerable<TreeNode> GetItemsOfIList(Func<Value> getValue)
{
// Add a cast, so that we are sure the expression has an indexer.
// (The expression can be e.g. of type 'object' but its value is a List.
// Without the cast, evaluating "expr[i]" would fail, because object does not have an indexer).
targetObject = targetObject.CastToIList();
Value list = null;
DebugType iListType = null;
int count = 0;
GetValueException error = null;
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) {
// Cannot yield a value in the body of a catch clause (CS1631)
error = e;
}
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) {
yield return new TreeNode(null, "(empty)", null, null, null, _ => null);
yield return new TreeNode("(empty)", null);
} else {
PropertyInfo pi = iListType.GetProperty("Item");
for(int i = 0; i < count; i++) {
string imageName;
var image = ExpressionNode.GetImageForArrayIndexer(out imageName);
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;
int iCopy = i;
yield return new ExpressionNode("Icons.16x16.Field", "[" + i + "]", () => list.GetPropertyValue(pi, Eval.CreateValue(list.AppDomain, iCopy)));
}
}
}

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

@ -1,25 +0,0 @@ @@ -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 @@ @@ -4,17 +4,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Debugger.AddIn.Visualizers;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
@ -26,11 +27,12 @@ namespace Debugger.AddIn.TreeModel @@ -26,11 +27,12 @@ namespace Debugger.AddIn.TreeModel
/// Node in the tree which can be defined by a debugger expression.
/// The expression will be lazily evaluated when needed.
/// </summary>
public class ExpressionNode: TreeNode, ISetText, INotifyPropertyChanged
public class ExpressionNode: TreeNode, INotifyPropertyChanged
{
bool evaluated;
Expression expression;
Func<Value> valueGetter;
Value permanentValue;
bool canSetText;
GetValueException error;
string fullText;
@ -40,10 +42,6 @@ namespace Debugger.AddIn.TreeModel @@ -40,10 +42,6 @@ namespace Debugger.AddIn.TreeModel
set { evaluated = value; }
}
public Expression Expression {
get { return expression; }
}
public override bool CanSetText {
get {
if (!evaluated) EvaluateExpression();
@ -75,12 +73,6 @@ namespace Debugger.AddIn.TreeModel @@ -75,12 +73,6 @@ namespace Debugger.AddIn.TreeModel
}
}
public override string FullName {
get {
return this.expression.PrettyPrint() ?? Name.Trim();
}
}
public override string Type {
get {
if (!evaluated) EvaluateExpression();
@ -88,17 +80,13 @@ namespace Debugger.AddIn.TreeModel @@ -88,17 +80,13 @@ namespace Debugger.AddIn.TreeModel
}
}
public override IEnumerable<TreeNode> ChildNodes {
public override Func<IEnumerable<TreeNode>> GetChildren {
get {
if (!evaluated) EvaluateExpression();
return base.ChildNodes;
return base.GetChildren;
}
}
public override bool HasChildNodes {
get {
if (!evaluated) EvaluateExpression();
return base.HasChildNodes;
protected set {
base.GetChildren = value;
}
}
@ -136,42 +124,47 @@ namespace Debugger.AddIn.TreeModel @@ -136,42 +124,47 @@ namespace Debugger.AddIn.TreeModel
foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) {
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)
: base(parent)
public ExpressionNode(string imageName, string name, Func<Value> valueGetter)
: base(imageName, name, string.Empty, string.Empty, null)
{
this.IconImage = image;
this.Name = name;
this.expression = expression;
this.valueGetter = valueGetter;
}
/// <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()
{
evaluated = true;
Stopwatch watch = new Stopwatch();
watch.Start();
Value val;
try {
var process = WindowsDebugger.DebuggedProcess;
if (process == null) return;
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);
// Do not keep permanent reference
val = valueGetter();
} catch (GetValueException e) {
error = e;
this.Text = e.Message;
@ -188,27 +181,24 @@ namespace Debugger.AddIn.TreeModel @@ -188,27 +181,24 @@ namespace Debugger.AddIn.TreeModel
if (val.IsNull) {
} else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass
} else if (val.Type.IsArray) { // Must be before IsClass
if (val.ArrayLength > 0)
this.childNodes = Utils.LazyGetChildNodesOfArray(this, this.Expression, val.ArrayDimensions);
if (val.ArrayLength > 0) {
var dims = val.ArrayDimensions; // Eval now
this.GetChildren = () => Utils.GetChildNodesOfArray(this, dims, dims);
}
} else if (val.Type.IsClass || val.Type.IsValueType) {
if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) {
if ((int)val.GetMemberValue("_size").PrimitiveValue > 0)
this.childNodes = Utils.LazyGetItemsOfIList(this, this.expression);
this.GetChildren = () => Utils.GetItemsOfIList(() => this.Evaluate());
} else {
this.childNodes = Utils.LazyGetChildNodesOfObject(this, this.Expression, val.Type);
this.GetChildren = () => Utils.GetChildNodesOfObject(this, val.Type);
}
} else if (val.Type.IsPointer) {
Value deRef = val.Dereference();
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
if (val.Type.IsInteger) {
fullText = FormatInteger(val.PrimitiveValue);
@ -236,6 +226,8 @@ namespace Debugger.AddIn.TreeModel @@ -236,6 +226,8 @@ namespace Debugger.AddIn.TreeModel
}
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)
@ -308,11 +300,9 @@ namespace Debugger.AddIn.TreeModel @@ -308,11 +300,9 @@ namespace Debugger.AddIn.TreeModel
public override bool SetText(string newText)
{
string fullName = FullName;
Value val = null;
try {
val = this.Expression.Evaluate(WindowsDebugger.DebuggedProcess);
val = this.Evaluate();
if (val.Type.IsInteger && newText.StartsWith("0x")) {
try {
val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber);
@ -339,33 +329,10 @@ namespace Debugger.AddIn.TreeModel @@ -339,33 +329,10 @@ namespace Debugger.AddIn.TreeModel
return false;
}
public static IImage GetImageForThis(out string imageName)
{
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)
public static string GetImageForMember(IDebugMemberInfo memberInfo)
{
string name = string.Empty;
if (memberInfo.IsPublic) {
} else if (memberInfo.IsAssembly) {
name += "Internal";
@ -374,6 +341,7 @@ namespace Debugger.AddIn.TreeModel @@ -374,6 +341,7 @@ namespace Debugger.AddIn.TreeModel
} else if (memberInfo.IsPrivate) {
name += "Private";
}
if (memberInfo is FieldInfo) {
name += "Field";
} else if (memberInfo is PropertyInfo) {
@ -384,8 +352,7 @@ namespace Debugger.AddIn.TreeModel @@ -384,8 +352,7 @@ namespace Debugger.AddIn.TreeModel
throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName);
}
imageName = "Icons.16x16." + name;
return DebuggerResourceService.GetImage(imageName);
return "Icons.16x16." + name;
}
// public ContextMenuStrip GetContextMenu()

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

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

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

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

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

@ -16,11 +16,15 @@ namespace Debugger.AddIn.Visualizers @@ -16,11 +16,15 @@ namespace Debugger.AddIn.Visualizers
// should we make visualizer command available for Expression, or any TreeNode?
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();

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

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

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

@ -14,9 +14,7 @@ namespace Debugger.AddIn.Visualizers @@ -14,9 +14,7 @@ namespace Debugger.AddIn.Visualizers
{
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 @@ -16,6 +16,6 @@ namespace Debugger.AddIn.Visualizers
public interface IVisualizerDescriptor
{
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 @@ -21,9 +21,9 @@ namespace Debugger.AddIn.Visualizers
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 @@ -32,8 +32,7 @@ namespace Debugger.AddIn.Visualizers
/// </summary>
public class ObjectGraphVisualizerCommand : ExpressionVisualizerCommand
{
public ObjectGraphVisualizerCommand(Expression expression)
:base(expression)
public ObjectGraphVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
{
}
@ -44,10 +43,9 @@ namespace Debugger.AddIn.Visualizers @@ -44,10 +43,9 @@ namespace Debugger.AddIn.Visualizers
public override void Execute()
{
if (this.Expression == null)
return;
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 @@ -20,16 +20,15 @@ namespace Debugger.AddIn.Visualizers
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 TextVisualizerCommand(Expression expression)
:base(expression)
public TextVisualizerCommand(string valueName, Func<Value> getValue) : base(valueName, getValue)
{
}
@ -40,10 +39,7 @@ namespace Debugger.AddIn.Visualizers @@ -40,10 +39,7 @@ namespace Debugger.AddIn.Visualizers
public override void Execute()
{
if (this.Expression == null)
return;
string expressionValue = this.Expression.Evaluate(WindowsDebugger.CurrentProcess).AsString();
var textVisualizerWindow = new TextVisualizerWindow(this.Expression.PrettyPrint(), expressionValue);
var textVisualizerWindow = new TextVisualizerWindow(this.ValueName, this.GetValue().AsString());
textVisualizerWindow.ShowDialog();
}
}

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

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

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

@ -1,16 +0,0 @@ @@ -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; @@ -8,21 +8,23 @@ using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers
{
/// <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>
public class VirtualizingCollection<T> : IList<T>, IList
{
private IListValuesProvider<T> valueProvider;
private Dictionary<int, T> itemCache = new Dictionary<int, T>();
int count;
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
{
get { return this.valueProvider.GetCount(); }
get { return count; }
}
public T this[int index]
@ -32,7 +34,7 @@ namespace Debugger.AddIn.Visualizers @@ -32,7 +34,7 @@ namespace Debugger.AddIn.Visualizers
T cachedItem;
if (!itemCache.TryGetValue(index, out cachedItem))
{
cachedItem = this.valueProvider.GetItemAt(index);
cachedItem = getItem(index);
this.itemCache[index] = cachedItem;
}
return cachedItem;

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

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

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

@ -27,9 +27,6 @@ @@ -27,9 +27,6 @@
<Border Margin="3" Padding="3">
<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.ItemTemplate>
<DataTemplate>

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

@ -2,26 +2,16 @@ @@ -2,26 +2,16 @@
// 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 System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
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.Utils;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Visualizers.GridVisualizer
{
@ -30,160 +20,93 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer @@ -30,160 +20,93 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer
/// </summary>
public partial class GridVisualizerWindow : Window
{
WindowsDebugger debuggerService;
GridViewColumnHider columnHider;
Func<Value> getValue;
public GridVisualizerWindow()
public GridVisualizerWindow(string valueName, Func<Value> getValue)
{
InitializeComponent();
this.debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger;
if (debuggerService == null)
throw new ApplicationException("Only windows debugger is currently supported");
this.Title = valueName;
this.getValue = getValue;
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();
}
public void Refresh()
{
try {
try {
// clear ListView
listView.ItemsSource = null;
ScrollViewer listViewScroller = listView.GetScrollViewer();
if (listViewScroller != null) {
listViewScroller.ScrollToVerticalOffset(0);
}
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
DebugType iListType, listItemType;
if (shownValue.Type.ResolveIListImplementation(out iListType, out listItemType)) {
gridValuesProvider = CreateListValuesProvider(shownExpr.CastToIList(), listItemType);
Value shownValue = getValue();
DebugType iListType, iEnumerableType, itemType;
// Value is IList
if (shownValue.Type.ResolveIListImplementation(out iListType, out itemType)) {
// Ok
} else {
// Value is IEnumerable
if (shownValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
shownValue = DebuggerHelpers.CreateListFromIEnumeralbe(shownValue, itemType, out iListType);
} else {
// Value is IEnumerable
DebugType iEnumerableType, itemType;
if (shownValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
DebugType debugListType;
var debugListExpression = DebuggerHelpers.CreateDebugListExpression(shownExpr, itemType, out debugListType);
gridValuesProvider = CreateListValuesProvider(debugListExpression, itemType);
} else {
// Not IList or IEnumerable<T> - can't be displayed in GridVisualizer
return;
}
// Not IList or IEnumerable<T> - can't be displayed in GridVisualizer
return;
}
IList<MemberInfo> itemTypeMembers = gridValuesProvider.GetItemTypeMembers();
InitializeColumns((GridView)this.listView.View, itemTypeMembers);
this.columnHider = new GridViewColumnHider((GridView)this.listView.View);
cmbColumns.ItemsSource = this.columnHider.HideableColumns;
}
} catch (GetValueException e) {
MessageService.ShowMessage(e.Message);
} catch (DebuggerVisualizerException e) {
shownValue = shownValue.GetPermanentReference();
MemberInfo[] members = itemType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo itemGetter = iListType.GetProperty("Item");
int rowCount = (int)shownValue.GetPropertyValue(iListType.GetProperty("Count")).PrimitiveValue;
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) {
return "Exception: " + e.Message;
}
}
)
);
this.listView.ItemsSource = rowCollection;
InitializeColumns((GridView)this.listView.View, members);
GridViewColumnHider columnHider = new GridViewColumnHider((GridView)this.listView.View);
cmbColumns.ItemsSource = columnHider.HideableColumns;
} catch(GetValueException e) {
MessageService.ShowMessage(e.Message);
}
}
ListValuesProvider CreateListValuesProvider(ICSharpCode.NRefactory.Ast.Expression targetExpression, DebugType listItemType)
{
var listValuesProvider = new ListValuesProvider(targetExpression, listItemType);
var virtCollection = new VirtualizingCollection<ObjectValue>(listValuesProvider);
this.listView.ItemsSource = virtCollection;
return listValuesProvider;
}
void InitializeColumns(GridView gridView, IList<MemberInfo> itemTypeMembers)
void InitializeColumns(GridView gridView, MemberInfo[] members)
{
gridView.Columns.Clear();
AddIndexColumn(gridView);
AddMembersColumns(gridView, itemTypeMembers);
}
void AddIndexColumn(GridView gridView)
{
// Index column
var indexColumn = new GridViewHideableColumn();
indexColumn.CanBeHidden = false;
indexColumn.Width = 36;
indexColumn.Header = string.Empty;
indexColumn.DisplayMemberBinding = new Binding("Index");
indexColumn.DisplayMemberBinding = new Binding("[" + members.Length + "]");
gridView.Columns.Add(indexColumn);
}
void AddMembersColumns(GridView gridView, IList<MemberInfo> itemTypeMembers)
{
foreach (var member in itemTypeMembers) {
// Member columns
for (int i = 0; i < members.Length; i++) {
var memberColumn = new GridViewHideableColumn();
memberColumn.CanBeHidden = true;
memberColumn.Header = member.Name;
// "{Binding Path=[Name].Value}"
memberColumn.DisplayMemberBinding = new Binding("[" + member.Name + "].Value");
memberColumn.Header = members[i].Name;
memberColumn.IsVisibleDefault = ((IDebugMemberInfo)members[i]).IsPublic;
memberColumn.DisplayMemberBinding = new Binding("[" + i + "]");
gridView.Columns.Add(memberColumn);
}
}

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

@ -1,85 +0,0 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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
}

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

@ -1,17 +1,9 @@ @@ -1,17 +1,9 @@
// 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.AvalonEdit.Highlighting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Highlighting;
namespace Debugger.AddIn.Visualizers.TextVisualizer
{
@ -20,42 +12,14 @@ namespace Debugger.AddIn.Visualizers.TextVisualizer @@ -20,42 +12,14 @@ namespace Debugger.AddIn.Visualizers.TextVisualizer
/// </summary>
public partial class TextVisualizerWindow : Window
{
public TextVisualizerWindow()
{
InitializeComponent();
Mode = TextVisualizerMode.PlainText;
textEditor.IsReadOnly = true;
}
public TextVisualizerWindow(string title, string text)
public TextVisualizerWindow(string title, string text, string highlighting = null)
{
InitializeComponent();
Title= title;
Text = text;
}
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;
}
}
Title = title;
this.textEditor.Text = text;
if (highlighting != null)
this.textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(highlighting);
}
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 @@ -33,6 +33,17 @@ namespace Debugger.AddIn.Visualizers.Utils
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>
/// Gets underlying address of object in the debuggee.
/// </summary>
@ -120,7 +131,7 @@ namespace Debugger.AddIn.Visualizers.Utils @@ -120,7 +131,7 @@ namespace Debugger.AddIn.Visualizers.Utils
Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess);
var iCollectionType = list.Type.GetInterface(typeof(System.Collections.ICollection).FullName);
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
return (int)list.GetPropertyValue(iCollectionType.GetProperty("Count")).PrimitiveValue;
}

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

@ -144,8 +144,6 @@ @@ -144,8 +144,6 @@
<Compile Include="Mono.Cecil\Mono.Cecil\MethodCallingConvention.cs" />
<Compile Include="Mono.Cecil\Mono.Cecil\ReflectionException.cs" />
<Compile Include="NDebugger.cs" />
<Compile Include="NRefactory\Ast\ExpressionExtensionMethods.cs" />
<Compile Include="NRefactory\Visitors\ExpressionEvaluator.cs" />
<Compile Include="Options.cs" />
<Compile Include="PausedReason.cs" />
<Compile Include="PauseSession.cs" />
@ -198,17 +196,7 @@ @@ -198,17 +196,7 @@
<Folder Include="Mono.Cecil\Mono.Cecil.Binary" />
<Folder Include="Mono.Cecil\Mono.Cecil.Metadata" />
<Folder Include="Mono.Cecil\Mono.Cecil.Signatures" />
<Folder Include="NRefactory" />
<Folder Include="NRefactory\Ast" />
<Folder Include="NRefactory\Visitors" />
<Folder Include="Tests" />
</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" />
</Project>

14
src/AddIns/Debugger/Debugger.Core/Eval.cs

@ -273,24 +273,24 @@ namespace Debugger @@ -273,24 +273,24 @@ namespace Debugger
);
}
public static Value CreateValue(AppDomain appDomain, object value)
{
if (value == null) {
public static Value CreateValue(AppDomain appDomain, object value)
{
if (value == null) {
ICorDebugClass corClass = appDomain.ObjectType.CorType.GetClass();
Thread thread = GetEvaluationThread(appDomain);
ICorDebugEval corEval = thread.CorThread.CreateEval();
ICorDebugValue corValue = corEval.CreateValue((uint)CorElementType.CLASS, corClass);
return new Value(appDomain, corValue);
} else if (value is string) {
return Eval.NewString(appDomain, (string)value);
return Eval.NewString(appDomain, (string)value);
} else {
if (!value.GetType().IsPrimitive)
throw new DebuggerException("Value must be primitve type. Seen " + value.GetType());
if (!value.GetType().IsPrimitive)
throw new DebuggerException("Value must be primitve type. Seen " + value.GetType());
Value val = Eval.NewObjectNoConstructor(DebugType.CreateFromType(appDomain.Mscorlib, value.GetType()));
val.PrimitiveValue = value;
return val;
}
}
}
/*
// The following function create values only for the purpuse of evalutaion

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

@ -2,55 +2,21 @@ @@ -2,55 +2,21 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.Ast;
namespace Debugger
{
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)
{
this.error = error;
}
public GetValueException(string errorFmt, params object[] args):base(string.Format(errorFmt, args))
{
this.error = string.Format(errorFmt, args);
}
public GetValueException(string error):base(error)
{
this.error = error;
}
}
}

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

@ -246,14 +246,14 @@ namespace Debugger @@ -246,14 +246,14 @@ namespace Debugger
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);
}
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);
}

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

@ -639,7 +639,7 @@ namespace Debugger.MetaData @@ -639,7 +639,7 @@ namespace Debugger.MetaData
{
List<DebugLocalVariableInfo> vars = new List<DebugLocalVariableInfo>();
foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) {
ISymUnmanagedVariable symVarCopy = symVar;
uint address = (uint)symVar.GetAddressField1();
int start;
SignatureReader sigReader = new SignatureReader(symVar.GetSignature());
LocalVarSig.LocalVariable locVarSig = sigReader.ReadLocalVariable(sigReader.Blob, 0, out start);
@ -654,7 +654,7 @@ namespace Debugger.MetaData @@ -654,7 +654,7 @@ namespace Debugger.MetaData
(int)symScope.GetStartOffset(),
(int)symScope.GetEndOffset(),
delegate(StackFrame context) {
return GetLocalVariableValue(context, symVarCopy);
return context.GetLocalVariableValue(address);
},
locVarType
);
@ -668,7 +668,7 @@ namespace Debugger.MetaData @@ -668,7 +668,7 @@ namespace Debugger.MetaData
(int)symScope.GetEndOffset(),
locVarType,
delegate(StackFrame context) {
return GetLocalVariableValue(context, symVarCopy);
return context.GetLocalVariableValue(address);
}
);
vars.Add(locVar);
@ -680,18 +680,6 @@ namespace Debugger.MetaData @@ -680,18 +680,6 @@ namespace Debugger.MetaData
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/>
public override string ToString()
{

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

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

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

@ -7,8 +7,6 @@ using System.Runtime.InteropServices; @@ -7,8 +7,6 @@ using System.Runtime.InteropServices;
using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
namespace Debugger
{
@ -251,7 +249,6 @@ namespace Debugger @@ -251,7 +249,6 @@ namespace Debugger
internal bool TerminateCommandIssued = false;
internal Queue<Breakpoint> BreakpointHitEventQueue = new Queue<Breakpoint>();
internal Dictionary<INode, TypedValue> ExpressionsCache = new Dictionary<INode, TypedValue>();
#region Events
@ -330,7 +327,6 @@ namespace Debugger @@ -330,7 +327,6 @@ namespace Debugger
if (action == DebuggeeStateAction.Clear) {
if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared");
debuggeeState = null;
this.ExpressionsCache.Clear();
}
}
@ -557,19 +553,6 @@ namespace Debugger @@ -557,19 +553,6 @@ namespace Debugger
// 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()
{
if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) {

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

@ -380,6 +380,16 @@ namespace Debugger @@ -380,6 +380,16 @@ namespace Debugger
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>
[Debugger.Tests.Ignore]
public Value GetLocalVariableThis()

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

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

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

@ -77,6 +77,10 @@ @@ -77,6 +77,10 @@
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
</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">
<Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project>
<Name>Debugger.Core</Name>

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

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

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

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

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

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

Loading…
Cancel
Save