Browse Source

implement lazy evaluation for IEnumerable<T> and lists in debugger. Makes possible to evaluate infinite iterators and really big lists faster.

pull/517/head
Siegfried Pammer 11 years ago
parent
commit
9499b7e161
  1. 1
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml
  2. 43
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
  3. 60
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs
  4. 9
      src/AddIns/Debugger/Debugger.Core/Process.cs

1
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml

@ -131,6 +131,7 @@ @@ -131,6 +131,7 @@
SelectionUnit="FullRow"
x:Name="dataGrid"
AutoGenerateColumns="False"
ScrollViewer.ScrollChanged="DataGrid_ScrollChanged"
CanUserAddRows="False"
HeadersVisibility="None"
BorderBrush="Gray"

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

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@ -36,13 +37,16 @@ namespace Debugger.AddIn.Tooltips @@ -36,13 +37,16 @@ namespace Debugger.AddIn.Tooltips
static Point ChildPopupOffset = new Point(16, 15);
public DebuggerTooltipControl ChildTooltip { get; private set; }
public IEnumerable<TreeNode> TreeNodes { get; set; }
readonly IEnumerator<TreeNode> treeNodesGenerator;
readonly ObservableCollection<TreeNode> treeNodes;
public DebuggerTooltipControl(IEnumerable<TreeNode> treeNodes)
public DebuggerTooltipControl(IEnumerable<TreeNode> treeNodesGenerator)
{
InitializeComponent();
this.TreeNodes = treeNodes;
this.treeNodesGenerator = treeNodesGenerator.GetEnumerator();
treeNodes = new ObservableCollection<TreeNode>();
GetNextItems();
this.dataGrid.ItemsSource = treeNodes;
// Only the leaf of the tooltip has this set to false
@ -63,7 +67,7 @@ namespace Debugger.AddIn.Tooltips @@ -63,7 +67,7 @@ namespace Debugger.AddIn.Tooltips
if (clickedButton.IsChecked == true && clickedNode.GetChildren != null) {
Point popupPos = clickedButton.PointToScreen(ChildPopupOffset).TransformFromDevice(clickedButton);
this.ChildTooltip = new DebuggerTooltipControl(clickedNode.GetChildren().ToList()) {
this.ChildTooltip = new DebuggerTooltipControl(clickedNode.GetChildren()) {
// We can not use placement target otherwise we would get too deep logical tree
Placement = PlacementMode.Absolute,
HorizontalOffset = popupPos.X,
@ -118,22 +122,23 @@ namespace Debugger.AddIn.Tooltips @@ -118,22 +122,23 @@ namespace Debugger.AddIn.Tooltips
}
}
/*
void AnimateCloseControl(bool show)
void DataGrid_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = show ? 0 : 1;
animation.To = show ? 1 : 0;
animation.BeginTime = new TimeSpan(0, 0, show ? 0 : 1);
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
animation.SetValue(Storyboard.TargetProperty, this.PinCloseControl);
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty));
Storyboard board = new Storyboard();
board.Children.Add(animation);
board.Begin(this);
if (e.ExtentHeight > e.ViewportHeight) {
if (Math.Abs(e.ExtentHeight - e.VerticalOffset - e.ViewportHeight) < 2)
GetNextItems();
}
}
void GetNextItems(int max = 25)
{
int count = 0;
while (treeNodesGenerator.MoveNext()) {
treeNodes.Add(treeNodesGenerator.Current);
count++;
if (count >= max)
return;
}
}
*/
}
}

60
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs

@ -24,6 +24,7 @@ using System.Linq; @@ -24,6 +24,7 @@ using System.Linq;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using Debugger.AddIn.Visualizers;
@ -320,11 +321,7 @@ namespace Debugger.AddIn.TreeModel @@ -320,11 +321,7 @@ namespace Debugger.AddIn.TreeModel
ienumerableType.Name,
ienumerableType.ReflectionName,
string.Empty,
() => {
// Note that this will bind to the current content forever and it will not reeveluate
Value list = CreateListFromIEnumerable(ienumerableTypeCopy, GetValue()).GetPermanentReferenceOfHeapValue();
return GetIListChildren(() => list);
}
() => LazyEvaluateIEnumerableOfT(ienumerableType, GetValue())
);
}
@ -333,6 +330,59 @@ namespace Debugger.AddIn.TreeModel @@ -333,6 +330,59 @@ namespace Debugger.AddIn.TreeModel
}
}
IEnumerable<TreeNode> LazyEvaluateIEnumerableOfT(ParameterizedType targetType, Value value)
{
var enumerableType = targetType.Compilation.FindType(KnownTypeCode.IEnumerableOfT).GetDefinition();
var enumeratorType = targetType.Compilation.FindType(KnownTypeCode.IEnumeratorOfT).GetDefinition();
var substitution = new TypeParameterSubstitution(targetType.TypeArguments, EmptyList<IType>.Instance);
IMethod getEnumerator = enumerableType.GetMethods(m => m.Name == "GetEnumerator", GetMemberOptions.IgnoreInheritedMembers)
.First(m => m.ReturnType.GetDefinition().Equals(enumeratorType))
.Specialize(substitution);
IMethod moveNext = enumeratorType.GetMethods(m => m.Name == "MoveNext").First();
IProperty current = enumeratorType.GetProperties(p => p.Name == "Current").First();
Value enumerator;
GetValueException lastError;
enumerator = EvaluateOrError(() => Debugger.Value.InvokeMethod(WindowsDebugger.EvalThread, value, getEnumerator), out lastError);
if (lastError != null) {
yield return new TreeNode(null, "(error)", lastError.Message, string.Empty, null);
yield break;
}
int currentIndex = -1;
var moveNextResult = EvaluateOrError(() => Debugger.Value.InvokeMethod(WindowsDebugger.EvalThread, enumerator, moveNext), out lastError);
while (lastError == null && (bool)moveNextResult.PrimitiveValue) {
currentIndex++;
var currentValue = EvaluateOrError(() => enumerator.GetPropertyValue(WindowsDebugger.EvalThread, current), out lastError);
yield return new ValueNode(ClassBrowserIconService.Field, "[" + currentIndex + "]", () => currentValue);
moveNextResult = EvaluateOrError(() => Debugger.Value.InvokeMethod(WindowsDebugger.EvalThread, enumerator, moveNext), out lastError);
}
if (lastError != null) {
yield return new TreeNode(null, "(error)", lastError.Message, string.Empty, null);
yield break;
}
if (currentIndex < 0)
yield return new TreeNode("(empty)", null);
}
Value EvaluateOrError(Func<Value> expression, out GetValueException error)
{
error = null;
Debugger.Value value;
try {
value = expression();
} catch (GetValueException ex) {
error = ex;
return null;
}
if (value.Type.GetNonInterfaceBaseTypes().Any(t => t.IsKnownType(KnownTypeCode.Exception))) {
error = new GetValueException(value.GetPropertyValue(WindowsDebugger.EvalThread, "Message").AsString());
return null;
}
return value;
}
IEnumerable<TreeNode> GetMembers(IEnumerable<IMember> members)
{
foreach(var memberInfo in members.OrderBy(m => m.Name)) {

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

@ -315,7 +315,8 @@ namespace Debugger @@ -315,7 +315,8 @@ namespace Debugger
}
/// <summary>
/// Indentification of the state of the debugee. This value changes whenever the state of the debugee significatntly changes
/// Numeric value to identify the debuggee state during a pause session.
/// This value changes whenever the state of the debuggee significantly changes.
/// </summary>
public long DebuggeeState {
get { return debuggeeState; }
@ -337,7 +338,7 @@ namespace Debugger @@ -337,7 +338,7 @@ namespace Debugger
AssertPaused();
pauseSession = 0;
if (action == DebuggeeStateAction.Clear) {
if (debuggeeState == 0) throw new DebuggerException("Debugee state already cleared");
if (debuggeeState == 0) throw new DebuggerException("Debuggee state already cleared");
debuggeeState = 0;
}
}
@ -385,7 +386,7 @@ namespace Debugger @@ -385,7 +386,7 @@ namespace Debugger
if (Exited != null) {
Exited(this, new DebuggerEventArgs() { Process = this });
}
// Expire pause seesion first
// Expire pause session first
if (IsPaused) {
NotifyResumed(DebuggeeStateAction.Clear);
}
@ -469,7 +470,7 @@ namespace Debugger @@ -469,7 +470,7 @@ namespace Debugger
}
/// <summary>
/// Resume execution and run all threads not marked by the user as susspended.
/// Resume execution and run all threads not marked by the user as suspended.
/// </summary>
public void AsyncContinue()
{

Loading…
Cancel
Save