Browse Source

Completely rewritten variable life-time management and updating;

Rewritten property evaluation

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1556 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 19 years ago
parent
commit
9559aea032
  1. 38
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs
  2. 70
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs
  3. 4
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs
  4. 40
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs
  5. 6
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj
  6. 20
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs
  7. 18
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/IExpirable.cs
  8. 2
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs
  9. 5
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs
  10. 27
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs
  11. 22
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs
  12. 4
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs
  13. 86
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs
  14. 1
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs
  15. 23
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs
  16. 4
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs
  17. 9
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs
  18. 36
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ClassVariable.cs
  19. 182
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs
  20. 6
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs
  21. 79
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs
  22. 130
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs
  23. 27
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValueEventArgs.cs
  24. 2
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs
  25. 62
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PropertyVariable.cs
  26. 7
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs
  27. 40
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs
  28. 98
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs
  29. 163
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs
  30. 28
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollectionEventArgs.cs
  31. 28
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableEventArgs.cs
  32. 29
      src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs

38
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs

@ -52,6 +52,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -52,6 +52,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
localVarList.Visible = false;
localVarList.SizeChanged += new EventHandler(localVarList_SizeChanged);
localVarList.BeforeExpand += new TreeListViewCancelEventHandler(localVarList_BeforeExpand);
localVarList.AfterExpand += new TreeListViewEventHandler(localVarList_AfterExpand);
RedrawContent();
@ -79,7 +80,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -79,7 +80,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
protected override void RegisterDebuggerEvents()
{
debuggerCore.DebuggeeStateChanged += delegate { debuggerCore.LocalVariables.Update(); };
debuggerCore.DebuggeeStateChanged += delegate { RefreshPad(); };
}
public override void RefreshPad()
@ -98,11 +99,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -98,11 +99,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
TreeListViewItem staticMenu = new TreeListViewItem(staticMembersName, 0);
TreeListViewItem privateStaticMenu = new TreeListViewItem(privateStaticMembersName, 0);
AddVariableMethod addVariable = delegate(Variable variable) {
ClassVariable classVariable = variable as ClassVariable;
if (classVariable == null || classVariable.IsPublic) {
if (classVariable != null && classVariable.IsStatic) {
foreach(Variable variable in varCollection) {
if (variable.IsPublic) {
if (variable.IsStatic) {
// Public static
if (staticMenu.TreeListView == null) {
tree.Add(staticMenu);
@ -114,7 +113,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -114,7 +113,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
tree.Add(new TreeListViewDebuggerItem(variable));
}
} else {
if (classVariable.IsStatic) {
if (variable.IsStatic) {
// Private static
if (staticMenu.TreeListView == null) {
tree.Add(staticMenu);
@ -134,18 +133,10 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -134,18 +133,10 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
privateInstanceMenu.Items.Add(new TreeListViewDebuggerItem(variable));
}
}
};
varCollection.VariableAdded += delegate(object sender, VariableEventArgs e) {
addVariable(e.Variable);
};
foreach(Variable variable in varCollection) {
addVariable(variable);
}
}
private void localVarList_BeforeExpand(object sender, TreeListViewCancelEventArgs e)
void localVarList_BeforeExpand(object sender, TreeListViewCancelEventArgs e)
{
if (debuggerCore.IsPaused) {
if (e.Item is TreeListViewDebuggerItem) {
@ -156,5 +147,20 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -156,5 +147,20 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
e.Cancel = true;
}
}
void localVarList_AfterExpand(object sender, TreeListViewEventArgs e)
{
UpdateSubTree(e.Item);
}
static void UpdateSubTree(TreeListViewItem tree)
{
foreach(TreeListViewItem item in tree.Items) {
if (item is TreeListViewDebuggerItem) {
((TreeListViewDebuggerItem)item).Update();
}
if (item.IsExpanded) UpdateSubTree(item);
}
}
}
}

70
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -17,6 +17,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{
Variable variable;
bool populated = false;
bool dirty = true;
public Variable Variable {
get {
@ -42,10 +43,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -42,10 +43,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
bool IsVisible {
get {
if (this.Parent == null) return true;
foreach(TreeListViewItem parent in this.ParentsInHierarch) {
if (!parent.IsExpanded) {
return false;
}
if (!parent.IsExpanded) return false;
}
return true;
}
@ -55,41 +55,19 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -55,41 +55,19 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{
this.variable = variable;
variable.ValueChanged += Update;
variable.ValueRemovedFromCollection += delegate {
variable.ValueChanged -= Update;
this.Remove();
};
variable.ValueChanged += delegate { dirty = true; Update(); };
variable.Expired += delegate { this.Remove(); };
SubItems.Add("");
SubItems.Add("");
Update(this, null);
Update();
}
bool waitingForParentToExpand = false;
void Update(object sender, DebuggerEventArgs e)
public void Update()
{
if (waitingForParentToExpand) return;
if (this.Parent != null && !IsVisible) {
// Delay the update until the parent is expanded
TreeListViewItemHanlder update = null;
update = delegate {
waitingForParentToExpand = false;
Update(this, null);
foreach(TreeListViewItem parent in this.ParentsInHierarch) {
parent.AfterExpand -= update;
}
};
foreach(TreeListViewItem parent in this.ParentsInHierarch) {
parent.AfterExpand += update;
}
waitingForParentToExpand = true;
return;
}
if (!dirty) return;
if (!IsVisible) return;
if (this.TreeListView != null) {
((DebuggerTreeListView)this.TreeListView).DelayRefresh();
@ -102,9 +80,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -102,9 +80,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
this.ImageIndex = DebuggerIcons.GetImageListIndex(variable);
if (IsExpanded) {
variable.SubVariables.Update();
} else {
if (!IsExpanded) {
// Show plus sign
if (variable.Value.MayHaveSubVariables && Items.Count == 0) {
TreeListViewItem dummy = new TreeListViewItem();
@ -112,31 +88,19 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -112,31 +88,19 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
Items.Add(dummy);
}
}
dirty = false;
}
public void BeforeExpand()
{
if (populated) {
variable.SubVariables.Update();
} else {
Populate();
if (!populated) {
Items.Clear();
// Do not sort names of array items
this.Items.SortOrder = variable.Value is ArrayValue ? SortOrder.None : SortOrder.Ascending;
LocalVarPad.AddVariableCollectionToTree(variable.SubVariables, this.Items);
populated = true;
}
}
public void Populate()
{
Items.Clear();
// Do not sort names of array items
if (variable.Value is ArrayValue) {
this.Items.SortOrder = SortOrder.None;
} else {
this.Items.SortOrder = SortOrder.Ascending;
}
LocalVarPad.AddVariableCollectionToTree(variable.SubVariables, this.Items);
}
}
}

4
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs

@ -41,9 +41,7 @@ namespace Debugger @@ -41,9 +41,7 @@ namespace Debugger
public static int GetImageListIndex(Variable variable)
{
if (variable is PropertyVariable){
return 2; // Property
} else if (variable.Value is ObjectValue) {
if (variable.Value is ObjectValue) {
return 0; // Class
} else {
return 1; // Field

40
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs

@ -27,6 +27,7 @@ namespace ICSharpCode.SharpDevelop.Services @@ -27,6 +27,7 @@ namespace ICSharpCode.SharpDevelop.Services
Variable variable;
Image image;
bool populated = false;
bool dirty = true;
public Variable Variable {
get {
@ -50,8 +51,6 @@ namespace ICSharpCode.SharpDevelop.Services @@ -50,8 +51,6 @@ namespace ICSharpCode.SharpDevelop.Services
{
}
bool skipUpdate = true;
public DynamicTreeDebuggerRow(Variable variable)
{
if (variable == null) throw new ArgumentNullException("variable");
@ -59,10 +58,8 @@ namespace ICSharpCode.SharpDevelop.Services @@ -59,10 +58,8 @@ namespace ICSharpCode.SharpDevelop.Services
this.variable = variable;
this.Shown += delegate {
this.variable.ValueChanged += Update;
if (!skipUpdate) {
DoInPausedState( delegate { Update(); } );
}
skipUpdate = false;
dirty = true;
DoInPausedState( delegate { Update(); } );
};
this.Hidden += delegate {
this.variable.ValueChanged -= Update;
@ -77,13 +74,16 @@ namespace ICSharpCode.SharpDevelop.Services @@ -77,13 +74,16 @@ namespace ICSharpCode.SharpDevelop.Services
Update();
}
void Update(object sender, DebuggerEventArgs e)
void Update(object sender, PersistentValueEventArgs e)
{
dirty = true;
Update();
}
void Update()
{
if (!dirty) return;
image = DebuggerIcons.GetImage(variable);
this[1].Text = ""; // Icon
this[2].Text = variable.Name;
@ -94,11 +94,12 @@ namespace ICSharpCode.SharpDevelop.Services @@ -94,11 +94,12 @@ namespace ICSharpCode.SharpDevelop.Services
}
this[3].AllowLabelEdit = variable.Value is PrimitiveValue &&
variable.Value.ManagedType != typeof(string) &&
!(variable is PropertyVariable) &&
!ShowValuesInHexadecimal;
this.ShowPlus = variable.Value.MayHaveSubVariables;
this.ShowMinusWhileExpanded = true;
dirty = false;
}
void OnIconPaint(object sender, ItemPaintEventArgs e)
@ -182,22 +183,17 @@ namespace ICSharpCode.SharpDevelop.Services @@ -182,22 +183,17 @@ namespace ICSharpCode.SharpDevelop.Services
List<Variable> privateInstance = new List<Variable>();
foreach(Variable variable in this.Variable.SubVariables) {
ClassVariable classVariable = variable as ClassVariable;
if (classVariable == null) {
publicInstance.Add(variable);
if (variable.IsPublic) {
if (variable.IsStatic) {
publicStatic.Add(variable);
} else {
publicInstance.Add(variable);
}
} else {
if (classVariable.IsPublic) {
if (classVariable.IsStatic) {
publicStatic.Add(variable);
} else {
publicInstance.Add(variable);
}
if (variable.IsStatic) {
privateStatic.Add(variable);
} else {
if (classVariable.IsStatic) {
privateStatic.Add(variable);
} else {
privateInstance.Add(variable);
}
privateInstance.Add(variable);
}
}
}

6
src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj

@ -190,18 +190,14 @@ @@ -190,18 +190,14 @@
<Compile Include="Src\Variables\NullValue.cs" />
<Compile Include="Src\Variables\ObjectValue.cs" />
<Compile Include="Src\Variables\PrimitiveValue.cs" />
<Compile Include="Src\Variables\PropertyVariable.cs" />
<Compile Include="Src\Variables\SignatureStream.cs" />
<Compile Include="Src\Variables\UnavailableValue.cs" />
<Compile Include="Src\Variables\Value.cs" />
<Compile Include="Src\Variables\ValueEventArgs.cs" />
<Compile Include="Src\Variables\VariableCollection.cs" />
<Compile Include="Src\Variables\Variable.cs" />
<Compile Include="Src\Variables\VariableEventArgs.cs" />
<Compile Include="Src\Variables\VariableCollectionEventArgs.cs" />
<Compile Include="Src\Threads\Stepper.cs" />
<Compile Include="Src\Threads\StepperEventArgs.cs" />
<Compile Include="Src\Variables\ClassVariable.cs" />
<Compile Include="Src\Interop\CorSym\_FILETIME.cs" />
<Compile Include="Src\Interop\CorSym\_LARGE_INTEGER.cs" />
<Compile Include="Src\Interop\CorSym\_ULARGE_INTEGER.cs" />
@ -381,6 +377,8 @@ @@ -381,6 +377,8 @@
<Compile Include="Src\Wrappers\CorDebug\ICorDebugChain.cs" />
<Compile Include="Src\Wrappers\CorDebug\ICorDebugFrame.cs" />
<Compile Include="Src\Variables\PersistentValue.cs" />
<Compile Include="Src\Variables\PersistentValueEventArgs.cs" />
<Compile Include="Src\Debugger\IExpirable.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="README.TXT" />

20
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs

@ -13,8 +13,26 @@ namespace Debugger @@ -13,8 +13,26 @@ namespace Debugger
/// Unique identifier of the state of the debugee.
/// Changes when debuggee is stepped, but not when properity is evaluated.
/// </summary>
public class DebugeeState
public class DebugeeState: IExpirable
{
bool hasExpired = false;
public event EventHandler Expired;
public bool HasExpired {
get {
return hasExpired;
}
}
internal void NotifyHasExpired()
{
if(!hasExpired) {
hasExpired = true;
if (Expired != null) {
Expired(this, EventArgs.Empty);
}
}
}
}
}

18
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/IExpirable.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision: 1551 $</version>
// </file>
using System;
namespace Debugger
{
public interface IExpirable
{
event EventHandler Expired;
bool HasExpired { get ; }
}
}

2
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs

@ -237,7 +237,7 @@ namespace Debugger @@ -237,7 +237,7 @@ namespace Debugger
// this will also remove the eval form PendingEvals collection
Eval eval = debugger.GetEval(corEval);
if (eval != null) {
eval.OnEvalComplete(!exception);
eval.NotifyEvaluationComplete(!exception);
}
if (debugger.PendingEvals.Count > 0) {

5
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs

@ -117,8 +117,12 @@ namespace Debugger @@ -117,8 +117,12 @@ namespace Debugger
return debugeeState;
}
private set {
DebugeeState oldDebugeeState = debugeeState;
debugeeState = value;
OnDebuggeeStateChanged();
if (oldDebugeeState != null) {
oldDebugeeState.NotifyHasExpired();
}
}
}
@ -180,6 +184,7 @@ namespace Debugger @@ -180,6 +184,7 @@ namespace Debugger
throw new DebuggerException("Already resumed");
}
pauseSession.NotifyHasExpired();
pauseSession = null;
OnDebuggingResumed();
pausedHandle.Reset();

27
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs

@ -24,8 +24,6 @@ namespace Debugger @@ -24,8 +24,6 @@ namespace Debugger
MTA2STA mta2sta = new MTA2STA();
VariableCollection localVariables;
string debuggeeVersion;
public MTA2STA MTA2STA {
@ -62,8 +60,6 @@ namespace Debugger @@ -62,8 +60,6 @@ namespace Debugger
this.ModuleLoaded += SetBreakpointsInModule;
localVariables = new VariableCollection(this);
Wrappers.ResourceManager.TraceMessagesEnabled = false;
Wrappers.ResourceManager.TraceMessage += delegate (object s, MessageEventArgs e) {
TraceMessage(e.Message);
@ -115,15 +111,11 @@ namespace Debugger @@ -115,15 +111,11 @@ namespace Debugger
corDebug.Initialize();
corDebug.SetManagedHandler(new ICorDebugManagedCallback(managedCallbackProxy));
localVariables.Updating += OnUpdatingLocalVariables;
TraceMessage("ICorDebug initialized, debugee version " + debuggeeVersion);
}
internal void TerminateDebugger()
{
localVariables.Clear();
ClearModules();
ResetBreakpoints();
@ -212,20 +204,11 @@ namespace Debugger @@ -212,20 +204,11 @@ namespace Debugger
public VariableCollection LocalVariables {
get {
localVariables.Update();
return localVariables;
}
}
void OnUpdatingLocalVariables(object sender, VariableCollectionEventArgs e)
{
if (SelectedFunction == null || IsRunning) {
e.VariableCollection.UpdateTo(new Variable[] {}); // Make it empty
} else {
e.VariableCollection.UpdateTo(SelectedFunction.Variables);
SelectedFunction.Expired += delegate {
e.VariableCollection.UpdateTo(new Variable[] {});
};
if (SelectedFunction == null || IsRunning) {
return VariableCollection.Empty;
} else {
return SelectedFunction.Variables;
}
}
}
}

22
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs

@ -15,10 +15,30 @@ namespace Debugger @@ -15,10 +15,30 @@ namespace Debugger
/// Holds information about the state of paused debugger.
/// Expires when when Continue is called on debugger.
/// </summary>
public class PauseSession
public class PauseSession: IExpirable
{
PausedReason pausedReason;
bool hasExpired = false;
public event EventHandler Expired;
public bool HasExpired {
get {
return hasExpired;
}
}
internal void NotifyHasExpired()
{
if(!hasExpired) {
hasExpired = true;
if (Expired != null) {
Expired(this, EventArgs.Empty);
}
}
}
public PausedReason PausedReason {
get {
return pausedReason;

4
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs

@ -41,7 +41,9 @@ namespace Debugger @@ -41,7 +41,9 @@ namespace Debugger
this.thread = thread;
corValue = thread.CorThread.CurrentException;
exceptionType = thread.CurrentExceptionType;
runtimeValue = new PersistentValue(debugger, corValue).Value;
runtimeValue = new PersistentValue(debugger,
new IExpirable[] {debugger.PauseSession},
corValue).Value;
runtimeValueException = runtimeValue as ObjectValue;
if (runtimeValueException != null) {
while (runtimeValueException.Type != "System.Exception") {

86
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs

@ -16,7 +16,7 @@ using Debugger.Wrappers.MetaData; @@ -16,7 +16,7 @@ using Debugger.Wrappers.MetaData;
namespace Debugger
{
public class Function: RemotingObjectBase
public class Function: RemotingObjectBase, IExpirable
{
NDebugger debugger;
@ -106,20 +106,6 @@ namespace Debugger @@ -106,20 +106,6 @@ namespace Debugger
}
}
ICorDebugValue ThisCorValue {
get {
if (IsStatic) throw new DebuggerException("Static method does not have 'this'.");
if (this.HasExpired) throw new CannotGetValueException("Function has expired");
try {
return CorILFrame.GetArgument(0);
} catch (COMException e) {
// System.Runtime.InteropServices.COMException (0x80131304): An IL variable is not available at the current native IP. (See Forum-8640)
if ((uint)e.ErrorCode == 0x80131304) throw new CannotGetValueException("Not available in the current state");
throw;
}
}
}
internal Function(Thread thread, FrameID frameID, ICorDebugILFrame corILFrame)
{
this.debugger = thread.Debugger;
@ -368,21 +354,47 @@ namespace Debugger @@ -368,21 +354,47 @@ namespace Debugger
}
}
public IEnumerable<Variable> Variables {
public VariableCollection Variables {
get {
if (!IsStatic) {
yield return new Variable(debugger,
"this",
new PersistentValue(debugger, delegate { return ThisCorValue; }));
}
foreach(Variable var in ArgumentVariables) {
yield return var;
}
foreach(Variable var in LocalVariables) {
yield return var;
}
foreach(Variable var in ContaingClassVariables) {
yield return var;
return new VariableCollection(GetVariables());
}
}
IEnumerable<Variable> GetVariables()
{
if (!IsStatic) {
yield return new Variable("this",
ThisValue);
}
foreach(Variable var in ArgumentVariables) {
yield return var;
}
foreach(Variable var in LocalVariables) {
yield return var;
}
foreach(Variable var in ContaingClassVariables) {
yield return var;
}
}
public PersistentValue ThisValue {
get {
return new PersistentValue(debugger,
new IExpirable[] {this},
delegate { return ThisCorValue; });
}
}
ICorDebugValue ThisCorValue {
get {
if (IsStatic) throw new DebuggerException("Static method does not have 'this'.");
if (this.HasExpired) throw new CannotGetValueException("Function has expired");
try {
return CorILFrame.GetArgument(0);
} catch (COMException e) {
// System.Runtime.InteropServices.COMException (0x80131304): An IL variable is not available at the current native IP. (See Forum-8640)
if ((uint)e.ErrorCode == 0x80131304) throw new CannotGetValueException("Not available in the current state");
throw;
}
}
}
@ -391,7 +403,7 @@ namespace Debugger @@ -391,7 +403,7 @@ namespace Debugger
get {
// TODO: Should work for static
if (!IsStatic) {
foreach(Variable var in new PersistentValue(debugger, delegate{ return ThisCorValue; }).Value.GetSubVariables()) {
foreach(Variable var in ThisValue.Value.SubVariables) {
yield return var;
}
}
@ -421,9 +433,10 @@ namespace Debugger @@ -421,9 +433,10 @@ namespace Debugger
public Variable GetArgumentVariable(int index)
{
return new Variable(debugger,
GetParameterName(index),
new PersistentValue(debugger, delegate { return GetArgumentCorValue(index); } ));
return new Variable(GetParameterName(index),
new PersistentValue(debugger,
new IExpirable[] {this},
delegate { return GetArgumentCorValue(index); } ));
}
ICorDebugValue GetArgumentCorValue(int index)
@ -474,9 +487,10 @@ namespace Debugger @@ -474,9 +487,10 @@ namespace Debugger
Variable GetLocalVariable(ISymUnmanagedVariable symVar)
{
return new Variable(debugger,
symVar.Name,
new PersistentValue(debugger, delegate { return GetCorValueOfLocalVariable(symVar); }));
return new Variable(symVar.Name,
new PersistentValue(debugger,
new IExpirable[] {this},
delegate { return GetCorValueOfLocalVariable(symVar); }));
}
ICorDebugValue GetCorValueOfLocalVariable(ISymUnmanagedVariable symVar)

1
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs

@ -49,6 +49,7 @@ namespace Debugger @@ -49,6 +49,7 @@ namespace Debugger
internal void RemoveProcess(Process process)
{
processCollection.Remove(process);
process.NotifyHasExpired();
OnProcessExited(process);
// noProcessesHandle is set in NDebugger.TerminateDebugger
}

23
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs

@ -14,7 +14,7 @@ using Debugger.Wrappers.CorDebug; @@ -14,7 +14,7 @@ using Debugger.Wrappers.CorDebug;
namespace Debugger
{
public class Process: RemotingObjectBase
public class Process: RemotingObjectBase, IExpirable
{
NDebugger debugger;
@ -23,6 +23,27 @@ namespace Debugger @@ -23,6 +23,27 @@ namespace Debugger
Thread selectedThread;
bool isProcessRunning = true;
bool hasExpired = false;
public event EventHandler Expired;
public bool HasExpired {
get {
return hasExpired;
}
}
internal void NotifyHasExpired()
{
if(!hasExpired) {
hasExpired = true;
if (Expired != null) {
Expired(this, new DebuggerEventArgs(debugger));
}
}
}
public NDebugger Debugger {
get {
return debugger;

4
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs

@ -114,7 +114,9 @@ namespace Debugger @@ -114,7 +114,9 @@ namespace Debugger
if (!HasBeenLoaded) throw new DebuggerException("Thread has not started jet");
process.AssertPaused();
return new PersistentValue(debugger, corThread.Object).Value;
return new PersistentValue(debugger,
new IExpirable[] {debugger.PauseSession},
corThread.Object).Value;
}
}

9
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs

@ -118,9 +118,10 @@ namespace Debugger @@ -118,9 +118,10 @@ namespace Debugger
elementName += indices[i].ToString() + ",";
elementName = elementName.TrimEnd(new char[] {','}) + "]";
return new Variable(debugger,
elementName,
new PersistentValue(debugger, delegate { return GetCorValueOfItem(indices); }));
return new Variable(elementName,
new PersistentValue(debugger,
new IExpirable[] {this.PersistentValue},
delegate { return GetCorValueOfItem(indices); }));
}
unsafe ICorDebugValue GetCorValueOfItem(uint[] indices)
@ -137,7 +138,7 @@ namespace Debugger @@ -137,7 +138,7 @@ namespace Debugger
}
}
public override IEnumerable<Variable> GetSubVariables()
protected override IEnumerable<Variable> GetSubVariables()
{
uint[] indices = new uint[rank];

36
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ClassVariable.cs

@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger
{
public class ClassVariable: Variable
{
internal bool isStatic;
internal bool isPublic;
public bool IsStatic {
get {
return isStatic;
}
}
public bool IsPublic {
get {
return isPublic;
}
}
public ClassVariable(NDebugger debugger, string name, bool isStatic, bool isPublic, PersistentValue pValue): base(debugger, name, pValue)
{
this.isStatic = isStatic;
this.isPublic = isPublic;
}
}
}

182
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs

@ -7,15 +7,14 @@ @@ -7,15 +7,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Debugger.Wrappers.CorDebug;
namespace Debugger
{
delegate ICorDebugValue[] CorValuesGetter();
public enum EvalState {Pending, Evaluating, EvaluatedSuccessfully, EvaluatedException, EvaluatedNoResult, Error, Expired};
public enum EvalState {WaitingForRequest, EvaluationScheduled, Evaluating, EvaluatedSuccessfully, EvaluatedException, EvaluatedNoResult, EvaluatedError};
/// <summary>
/// This class holds information about function evaluation.
@ -24,16 +23,19 @@ namespace Debugger @@ -24,16 +23,19 @@ namespace Debugger
{
NDebugger debugger;
PersistentValue pValue;
ICorDebugEval corEval;
ICorDebugFunction corFunction;
CorValuesGetter getArgs;
bool reevaluateAfterDebuggeeStateChange;
PersistentValue thisValue;
PersistentValue[] args;
EvalState evalState = EvalState.Pending;
Value result;
EvalState evalState = EvalState.WaitingForRequest;
ICorDebugValue result;
DebugeeState debugeeStateOfResult;
string error;
DebugeeState debugeeStateWhenEvaluated;
public event EventHandler<EvalEventArgs> EvalStarted;
public event EventHandler<EvalEventArgs> EvalComplete;
@ -45,48 +47,30 @@ namespace Debugger @@ -45,48 +47,30 @@ namespace Debugger
public EvalState EvalState {
get {
if (result != null && (debugeeStateWhenEvaluated != debugger.DebugeeState || result.IsExpired)) {
return EvalState.Expired;
} else {
return evalState;
return evalState;
}
set {
evalState = value;
if (Evaluated) {
debugeeStateOfResult = debugger.DebugeeState;
OnEvalComplete(new EvalEventArgs(this));
}
pValue.NotifyValueChange();
}
}
/// <summary>
/// True if the evaluation has been completed.
/// </summary>
public bool Evaluated {
get {
return this.EvalState != EvalState.Pending &&
this.EvalState != EvalState.Evaluating;
return evalState == EvalState.EvaluatedSuccessfully ||
evalState == EvalState.EvaluatedException ||
evalState == EvalState.EvaluatedNoResult ||
evalState == EvalState.EvaluatedError;
}
}
public bool HasExpired {
public PersistentValue PersistentValue {
get {
return this.EvalState == EvalState.Expired;
}
}
/// <summary>
/// The result of the evaluation. Always non-null, but it may be UnavailableValue.
/// </summary>
public Value Result {
get {
switch(this.EvalState) {
case EvalState.Pending: return new UnavailableValue(debugger, "Evaluation pending");
case EvalState.Evaluating: return new UnavailableValue(debugger, "Evaluating...");
case EvalState.EvaluatedSuccessfully: return result;
case EvalState.EvaluatedException:
ObjectValue exception = (ObjectValue)result;
while (exception.Type != "System.Exception") exception = exception.BaseClass;
return new UnavailableValue(debugger, result.Type + ": " + exception["_message"].Value.AsString);
case EvalState.EvaluatedNoResult: return new UnavailableValue(debugger, "No return value");
case EvalState.Error: return new UnavailableValue(debugger, error);
case EvalState.Expired: return new UnavailableValue(debugger, "Result has expired");
default: throw new DebuggerException("Unknown state");
}
return pValue;
}
}
@ -96,43 +80,83 @@ namespace Debugger @@ -96,43 +80,83 @@ namespace Debugger
}
}
internal Eval(NDebugger debugger, ICorDebugFunction corFunction, CorValuesGetter getArgs)
internal Eval(NDebugger debugger, ICorDebugFunction corFunction, bool reevaluateAfterDebuggeeStateChange, PersistentValue thisValue, PersistentValue[] args)
{
this.debugger = debugger;
this.corFunction = corFunction;
this.getArgs = getArgs;
this.reevaluateAfterDebuggeeStateChange = reevaluateAfterDebuggeeStateChange;
this.thisValue = thisValue;
this.args = args;
List<PersistentValue> dependencies = new List<PersistentValue>();
if (thisValue != null) dependencies.Add(thisValue);
dependencies.AddRange(args);
// Schedule the eval for evaluation
pValue = new PersistentValue(debugger,
dependencies.ToArray(),
delegate { return GetCorValue(); });
foreach(PersistentValue dependency in dependencies) {
dependency.ValueChanged += delegate { EvalState = EvalState.WaitingForRequest; };
}
}
ICorDebugValue GetCorValue()
{
if (Evaluated && reevaluateAfterDebuggeeStateChange && debugger.DebugeeState != debugeeStateOfResult) {
ScheduleEvaluation();
}
switch(this.EvalState) {
case EvalState.WaitingForRequest: ScheduleEvaluation(); goto case EvalState.EvaluationScheduled;
case EvalState.EvaluationScheduled: throw new CannotGetValueException("Evaluation pending");
case EvalState.Evaluating: throw new CannotGetValueException("Evaluating...");
case EvalState.EvaluatedSuccessfully: return result;
case EvalState.EvaluatedException: return result;
case EvalState.EvaluatedNoResult: throw new CannotGetValueException("No return value");
case EvalState.EvaluatedError: throw new CannotGetValueException(error);
default: throw new DebuggerException("Unknown state");
}
}
void ScheduleEvaluation()
{
debugger.AddEval(this);
debugger.MTA2STA.AsyncCall(delegate {
if (debugger.IsPaused && !this.HasExpired) {
debugger.StartEvaluation();
}
if (debugger.IsPaused) debugger.StartEvaluation();
});
EvalState = EvalState.EvaluationScheduled;
}
/// <returns>True is setup was successful</returns>
/// <returns>True if setup was successful</returns>
internal bool SetupEvaluation(Thread targetThread)
{
debugger.AssertPaused();
if (targetThread.IsLastFunctionNative) {
error = "Can not evaluate because native frame is on top of stack";
evalState = EvalState.Error;
if (EvalComplete != null) {
EvalComplete(this, new EvalEventArgs(this));
}
OnError("Can not evaluate because native frame is on top of stack");
return false;
}
ICorDebugValue[] args = getArgs();
if (args == null) {
error = "Can not get args for eval";
evalState = EvalState.Error;
if (EvalComplete != null) {
EvalComplete(this, new EvalEventArgs(this));
List<ICorDebugValue> corArgs = new List<ICorDebugValue>();
try {
if (thisValue != null) {
Value val = thisValue.Value;
if (!(val is ObjectValue)) {
OnError("Can not evaluate on a value which is not an object");
return false;
}
if (!((ObjectValue)val).IsSuperClass(corFunction.Class)) {
OnError("Can not evaluate because the object does not contain specified function");
return false;
}
corArgs.Add(thisValue.SoftReference);
}
foreach(PersistentValue arg in args) {
corArgs.Add(arg.SoftReference);
}
} catch (CannotGetValueException e) {
OnError(e.Message);
return false;
}
@ -140,24 +164,27 @@ namespace Debugger @@ -140,24 +164,27 @@ namespace Debugger
corEval = targetThread.CorThread.CreateEval();
try {
corEval.CallFunction(corFunction, (uint)args.Length, args);
corEval.CallFunction(corFunction, (uint)corArgs.Count, corArgs.ToArray());
} catch (COMException e) {
if ((uint)e.ErrorCode == 0x80131C26) {
error = "Can not evaluate in optimized code";
evalState = EvalState.Error;
if (EvalComplete != null) {
EvalComplete(this, new EvalEventArgs(this));
}
OnError("Can not evaluate in optimized code");
return false;
}
}
EvalState = EvalState.Evaluating;
OnEvalStarted(new EvalEventArgs(this));
evalState = EvalState.Evaluating;
return true;
}
void OnError(string msg)
{
error = msg;
EvalState = EvalState.EvaluatedError;
}
protected virtual void OnEvalStarted(EvalEventArgs e)
{
if (EvalStarted != null) {
@ -165,26 +192,27 @@ namespace Debugger @@ -165,26 +192,27 @@ namespace Debugger
}
}
protected internal virtual void OnEvalComplete(bool successful)
protected virtual void OnEvalComplete(EvalEventArgs e)
{
if (EvalComplete != null) {
EvalComplete(this, e);
}
}
internal void NotifyEvaluationComplete(bool successful)
{
// Eval result should be ICorDebugHandleValue so it should survive Continue()
result = new PersistentValue(debugger, corEval.Result).Value;
debugeeStateWhenEvaluated = debugger.DebugeeState;
result = corEval.Result;
if (result == null) {
evalState = EvalState.EvaluatedNoResult;
EvalState = EvalState.EvaluatedNoResult;
} else {
if (successful) {
evalState = EvalState.EvaluatedSuccessfully;
EvalState = EvalState.EvaluatedSuccessfully;
} else {
evalState = EvalState.EvaluatedException;
EvalState = EvalState.EvaluatedException;
}
}
if (EvalComplete != null) {
EvalComplete(this, new EvalEventArgs(this));
}
}
}
}

6
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs

@ -20,12 +20,6 @@ namespace Debugger @@ -20,12 +20,6 @@ namespace Debugger
}
}
public Value Result {
get {
return eval.Result;
}
}
public EvalEventArgs(Eval eval): base(eval.Debugger)
{
this.eval = eval;

79
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs

@ -55,11 +55,29 @@ namespace Debugger @@ -55,11 +55,29 @@ namespace Debugger
}
}
public IEnumerable<ObjectValue> SuperClasses {
get {
ICorDebugClass currentClass = corClass;
do {
yield return new ObjectValue(debugger, this.PersistentValue, currentClass);
currentClass = GetSuperClass(debugger, currentClass);
} while (currentClass != null);
}
}
internal bool IsSuperClass(ICorDebugClass corClass)
{
foreach(ObjectValue superClass in SuperClasses) {
if (superClass.corClass == corClass) return true;
}
return false;
}
public ObjectValue BaseClass {
get {
ICorDebugClass superClass = GetSuperClass(debugger, corClass);
if (superClass == null) throw new DebuggerException("Does not have a base class");
return new ObjectValue(debugger, pValue, superClass);
return new ObjectValue(debugger, this.PersistentValue, superClass);
}
}
@ -120,7 +138,7 @@ namespace Debugger @@ -120,7 +138,7 @@ namespace Debugger
}
}
public override IEnumerable<Variable> GetSubVariables()
protected override IEnumerable<Variable> GetSubVariables()
{
if (HasBaseClass) {
yield return GetBaseClassVariable();
@ -141,11 +159,12 @@ namespace Debugger @@ -141,11 +159,12 @@ namespace Debugger
FieldProps field = f; // One per scope/delegate
if (field.IsStatic && field.IsLiteral) continue; // Skip field
if (!field.IsStatic && CorValue == null) continue; // Skip field
yield return new ClassVariable(debugger,
field.Name,
field.IsStatic,
field.IsPublic,
new PersistentValue(debugger, delegate { return GetCorValueOfField(field); }));
yield return new Variable(field.Name,
field.IsStatic,
field.IsPublic,
new PersistentValue(debugger,
new IExpirable[] {this.PersistentValue},
delegate { return GetCorValueOfField(field); }));
}
}
@ -175,34 +194,15 @@ namespace Debugger @@ -175,34 +194,15 @@ namespace Debugger
foreach(MethodProps m in Methods) {
MethodProps method = m; // One per scope/delegate
if (method.HasSpecialName && method.Name.StartsWith("get_") && method.Name != "get_Item") {
yield return new PropertyVariable(debugger,
method.Name.Remove(0, 4),
method.IsStatic,
method.IsPublic,
delegate { return CreatePropertyEval(method); });
}
}
}
Eval CreatePropertyEval(MethodProps method)
{
if (!IsCorValueCompatible) return null;
ICorDebugFunction evalCorFunction = Module.CorModule.GetFunctionFromToken(method.Token);
return new Eval(debugger, evalCorFunction, delegate { return GetArgsForEval(method); });
}
ICorDebugValue[] GetArgsForEval(MethodProps method)
{
if (!IsCorValueCompatible) return null;
if (method.IsStatic) {
return new ICorDebugValue[] {};
} else {
if (this.SoftReference != null) {
return new ICorDebugValue[] {this.SoftReference.CastTo<ICorDebugValue>()};
} else {
return new ICorDebugValue[] {this.CorValue};
Eval eval = new Eval(debugger,
Module.CorModule.GetFunctionFromToken(method.Token),
true, // reevaluateAfterDebuggeeStateChange
method.IsStatic? null : this.PersistentValue,
new PersistentValue[] {});
yield return new Variable(method.Name.Remove(0, 4),
method.IsStatic,
method.IsPublic,
eval.PersistentValue);
}
}
}
@ -210,9 +210,10 @@ namespace Debugger @@ -210,9 +210,10 @@ namespace Debugger
public Variable GetBaseClassVariable()
{
if (HasBaseClass) {
return new Variable(debugger,
"<Base class>",
new PersistentValue(debugger, delegate { return GetBaseClassValue(); }));
return new Variable("<Base class>",
new PersistentValue(debugger,
new IExpirable[] {this.PersistentValue},
delegate { return GetBaseClassValue(); }));
} else {
return null;
}
@ -220,7 +221,7 @@ namespace Debugger @@ -220,7 +221,7 @@ namespace Debugger
Value GetBaseClassValue()
{
if (!IsCorValueCompatible) return new UnavailableValue(debugger, "Object type changed");
if (!IsCorValueCompatible) throw new CannotGetValueException("Object type changed");
return this.BaseClass;
}

130
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs

@ -16,22 +16,32 @@ namespace Debugger @@ -16,22 +16,32 @@ namespace Debugger
/// the value of a given object even after continue. This level of
/// abstraction is necessary because the type of a value can change
/// (eg for local variable of type object)
///
/// Expiration: Once value expires it can not be used anymore. Expiration
/// is permanet - once value expires it stays expired. Value expires when
/// any object specified in constructor expires of when process exits.
///
/// ValueChange: ValueChange event is called whenever DebugeeState changes
/// or when NotifyValueChange() is called.
/// </summary>
public class PersistentValue
public class PersistentValue: IExpirable
{
/// <summary>
/// Delegate that is used to get value. This delegate may be called at any time and should never return null.
/// </summary>
public delegate Value ValueGetter();
public delegate ICorDebugValue CorValueGetter();
public delegate bool IsExpiredDelegate();
NDebugger debugger;
CorValueGetter corValueGetter;
ValueGetter valueGetter;
IsExpiredDelegate isExpired;
bool isExpired = false;
public event EventHandler Expired;
public event EventHandler<PersistentValueEventArgs> ValueChanged;
public NDebugger Debugger {
get {
@ -47,6 +57,7 @@ namespace Debugger @@ -47,6 +57,7 @@ namespace Debugger
ICorDebugValue RawCorValue {
get {
if (this.HasExpired) throw new CannotGetValueException("CorValue has expired");
return corValueGetter();
}
}
@ -56,70 +67,106 @@ namespace Debugger @@ -56,70 +67,106 @@ namespace Debugger
try {
return valueGetter();
} catch (CannotGetValueException e) {
return new UnavailableValue(debugger, e.Message);
return new UnavailableValue(debugger, this, e.Message);
}
}
}
public bool IsExpired {
public bool HasExpired {
get {
return isExpired();
return isExpired;
}
}
public ICorDebugHandleValue SoftReference {
public ICorDebugValue SoftReference {
get {
if (this.IsExpired) throw new DebuggerException("CorValue has expired");
if (this.HasExpired) throw new DebuggerException("CorValue has expired");
ICorDebugValue corValue = RawCorValue;
if (corValue != null && corValue.Is<ICorDebugHandleValue>()) {
return corValue.As<ICorDebugHandleValue>();
return corValue;
}
corValue = PersistentValue.DereferenceUnbox(corValue);
if (corValue != null && corValue.Is<ICorDebugHeapValue2>()) {
return corValue.As<ICorDebugHeapValue2>().CreateHandle(CorDebugHandleType.HANDLE_WEAK_TRACK_RESURRECTION);
return corValue.As<ICorDebugHeapValue2>().CreateHandle(CorDebugHandleType.HANDLE_WEAK_TRACK_RESURRECTION).CastTo<ICorDebugValue>();
} else {
return null; // Value type
return corValue; // Value type - return value type
}
}
}
public PersistentValue(NDebugger debugger, ValueGetter valueGetter)
PersistentValue(NDebugger debugger, IExpirable[] dependencies)
{
this.debugger = debugger;
foreach(IExpirable exp in dependencies) {
AddDependency(exp);
}
AddDependency(debugger.SelectedProcess);
debugger.DebuggeeStateChanged += NotifyValueChange;
}
public PersistentValue(NDebugger debugger, IExpirable[] dependencies, ValueGetter valueGetter):this(debugger, dependencies)
{
this.corValueGetter = delegate { throw new CannotGetValueException("CorValue not available for custom value"); };
this.isExpired = delegate { return false; };
this.valueGetter = valueGetter;
}
public PersistentValue(NDebugger debugger, ICorDebugValue corValue)
public PersistentValue(NDebugger debugger, IExpirable[] dependencies, ICorDebugValue corValue):this(debugger, dependencies)
{
PauseSession pauseSessionAtCreation = debugger.PauseSession;
DebugeeState debugeeStateAtCreation = debugger.DebugeeState;
this.debugger = debugger;
this.corValueGetter = delegate {
if (this.IsExpired) throw new CannotGetValueException("CorValue has expired");
return corValue;
};
this.isExpired = delegate {
if (corValue != null && corValue.Is<ICorDebugHandleValue>()) {
return debugeeStateAtCreation != debugger.DebugeeState;
} else {
return pauseSessionAtCreation != debugger.PauseSession;
}
};
this.corValueGetter = delegate { return corValue; };
this.valueGetter = delegate { return CreateValue(); };
}
public PersistentValue(NDebugger debugger, CorValueGetter corValueGetter)
public PersistentValue(NDebugger debugger, IExpirable[] dependencies, CorValueGetter corValueGetter):this(debugger, dependencies)
{
this.debugger = debugger;
this.corValueGetter = corValueGetter;
this.isExpired = delegate { return false; };
this.valueGetter = delegate { return CreateValue(); };
}
void AddDependency(IExpirable dependency)
{
if (dependency.HasExpired) {
MakeExpired();
} else {
dependency.Expired += delegate { MakeExpired(); };
}
}
void MakeExpired()
{
if (!isExpired) {
isExpired = true;
OnExpired(new PersistentValueEventArgs(this));
debugger.DebuggeeStateChanged -= NotifyValueChange;
}
}
void NotifyValueChange(object sender, DebuggerEventArgs e)
{
NotifyValueChange();
}
internal void NotifyValueChange()
{
if (!isExpired) {
OnValueChanged(new PersistentValueEventArgs(this));
}
}
protected virtual void OnValueChanged(PersistentValueEventArgs e)
{
if (ValueChanged != null) {
ValueChanged(this, e);
}
}
protected virtual void OnExpired(EventArgs e)
{
if (Expired != null) {
Expired(this, e);
}
}
internal static ICorDebugValue DereferenceUnbox(ICorDebugValue corValue)
{
if (corValue.Is<ICorDebugReferenceValue>()) {
@ -147,16 +194,15 @@ namespace Debugger @@ -147,16 +194,15 @@ namespace Debugger
Value CreateValue()
{
ICorDebugValue corValue = RawCorValue;
ICorDebugValue derefed = DereferenceUnbox(corValue);
if (derefed == null) {
ICorDebugValue corValue = this.CorValue;
if (corValue == null) {
return new NullValue(debugger, this);
}
CorElementType type = Value.GetCorType(derefed);
CorElementType type = Value.GetCorType(corValue);
switch(type)
{
switch(type) {
case CorElementType.BOOLEAN:
case CorElementType.CHAR:
case CorElementType.I1:
@ -173,18 +219,18 @@ namespace Debugger @@ -173,18 +219,18 @@ namespace Debugger
case CorElementType.U:
case CorElementType.STRING:
return new PrimitiveValue(debugger, this);
case CorElementType.ARRAY:
case CorElementType.SZARRAY: // Short-cut for single dimension zero lower bound array
return new ArrayValue(debugger, this);
case CorElementType.VALUETYPE:
case CorElementType.CLASS:
case CorElementType.OBJECT: // Short-cut for Class "System.Object"
return new ObjectValue(debugger, this);
default: // Unknown type
return new UnavailableValue(debugger, "Unknown value type");
throw new CannotGetValueException("Unknown value type");
}
}
}

27
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValueEventArgs.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger
{
public class PersistentValueEventArgs: EventArgs
{
PersistentValue persistentValue;
public PersistentValue PersistentValue {
get {
return persistentValue;
}
}
public PersistentValueEventArgs(PersistentValue persistentValue)
{
this.persistentValue = persistentValue;
}
}
}

2
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs

@ -47,7 +47,7 @@ namespace Debugger @@ -47,7 +47,7 @@ namespace Debugger
} else {
(CorValue.CastTo<ICorDebugGenericValue>()).Value = newValue;
}
OnValueChanged();
PersistentValue.NotifyValueChange();
}
}

62
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PropertyVariable.cs

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using Debugger.Wrappers.CorDebug;
namespace Debugger
{
/// <summary>
/// Delegate that is used to get eval. This delegate may be called at any time and may return null.
/// </summary>
public delegate Eval EvalCreator();
public class PropertyVariable: ClassVariable
{
EvalCreator evalCreator;
Eval cachedEval;
internal PropertyVariable(NDebugger debugger, string name, bool isStatic, bool isPublic, EvalCreator evalCreator):base(debugger, name, isStatic, isPublic, null)
{
this.evalCreator = evalCreator;
this.pValue = new PersistentValue(debugger, delegate { return GetValueOfResult(); });
}
Value GetValueOfResult()
{
if (Eval != null) {
return Eval.Result;
} else {
return new UnavailableValue(debugger, "Property unavailable");
}
}
bool IsEvaluated {
get {
if (Eval != null) {
return Eval.Evaluated;
} else {
return true;
}
}
}
Eval Eval {
get {
if (cachedEval == null || cachedEval.HasExpired) {
cachedEval = evalCreator();
if (cachedEval != null) {
cachedEval.EvalStarted += delegate { OnValueChanged(this, null); };
cachedEval.EvalComplete += delegate { OnValueChanged(this, null); };
}
}
return cachedEval;
}
}
}
}

7
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs

@ -28,12 +28,7 @@ namespace Debugger @@ -28,12 +28,7 @@ namespace Debugger
}
}
internal UnavailableValue(NDebugger debugger): this(debugger, "Value is not available")
{
}
internal UnavailableValue(NDebugger debugger, string message):base(debugger, new PersistentValue(debugger, (ICorDebugValue)null))
internal UnavailableValue(NDebugger debugger, PersistentValue pValue, string message):base(debugger, pValue)
{
this.message = message;
}

40
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs

@ -16,9 +16,7 @@ namespace Debugger @@ -16,9 +16,7 @@ namespace Debugger
public abstract class Value: RemotingObjectBase
{
protected NDebugger debugger;
protected PersistentValue pValue;
public event EventHandler<ValueEventArgs> ValueChanged;
PersistentValue pValue;
public NDebugger Debugger {
get {
@ -26,15 +24,15 @@ namespace Debugger @@ -26,15 +24,15 @@ namespace Debugger
}
}
internal ICorDebugValue CorValue {
public PersistentValue PersistentValue {
get {
return pValue.CorValue;
return pValue;
}
}
protected ICorDebugHandleValue SoftReference {
internal ICorDebugValue CorValue {
get {
return pValue.SoftReference;
return pValue.CorValue;
}
}
@ -44,15 +42,6 @@ namespace Debugger @@ -44,15 +42,6 @@ namespace Debugger
}
}
/// <summary>
/// If true than the value is no longer valid and you should obtain updated copy
/// </summary>
public bool IsExpired {
get {
return pValue.IsExpired;
}
}
internal CorElementType CorType {
get {
return GetCorType(CorValue);
@ -69,12 +58,6 @@ namespace Debugger @@ -69,12 +58,6 @@ namespace Debugger
}
}
protected virtual void OnValueChanged() {
if (ValueChanged != null) {
ValueChanged(this, new ValueEventArgs(this));
}
}
public virtual Type ManagedType {
get {
return CorTypeToManagedType(CorType);
@ -85,17 +68,20 @@ namespace Debugger @@ -85,17 +68,20 @@ namespace Debugger
get;
}
/// <summary>
/// Gets the subvariables of this value
/// </summary>
public virtual IEnumerable<Variable> GetSubVariables()
public VariableCollection SubVariables {
get {
return new VariableCollection(GetSubVariables());
}
}
protected virtual IEnumerable<Variable> GetSubVariables()
{
yield break;
}
public Variable this[string variableName] {
get {
foreach(Variable v in GetSubVariables()) {
foreach(Variable v in SubVariables) {
if (v.Name == variableName) return v;
}
throw new DebuggerException("Subvariable " + variableName + " does not exist");

98
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs

@ -11,35 +11,40 @@ using Debugger.Wrappers.CorDebug; @@ -11,35 +11,40 @@ using Debugger.Wrappers.CorDebug;
namespace Debugger
{
public class Variable: RemotingObjectBase
public class Variable: RemotingObjectBase, IExpirable
{
protected NDebugger debugger;
string name;
VariableCollection subVariables;
internal protected PersistentValue pValue;
PersistentValue pValue;
bool isStatic;
bool isPublic;
event EventHandler<DebuggerEventArgs> valueChanged;
public event EventHandler<VariableCollectionEventArgs> ValueRemovedFromCollection;
public event EventHandler<PersistentValueEventArgs> ValueChanged {
add {
pValue.ValueChanged += value;
}
remove {
pValue.ValueChanged -= value;
}
}
public event EventHandler<DebuggerEventArgs> ValueChanged {
public event EventHandler Expired {
add {
valueChanged += value;
debugger.DebuggeeStateChanged += value;
debugger.ProcessExited += delegate {
debugger.DebuggeeStateChanged -= value;
};
pValue.Expired += value;
}
remove {
valueChanged -= value;
debugger.DebuggeeStateChanged -= value;
pValue.Expired -= value;
}
}
public bool HasExpired {
get {
return pValue.HasExpired;
}
}
public NDebugger Debugger {
get {
return debugger;
return pValue.Debugger;
}
}
@ -49,55 +54,53 @@ namespace Debugger @@ -49,55 +54,53 @@ namespace Debugger
}
}
/// <summary>
/// Gets value of variable which is safe to use (it is not null and it is not expired)
/// </summary>
public Value Value {
public PersistentValue PersistentValue {
get {
Value val = pValue.Value;
val.ValueChanged -= OnValueChanged;
val.ValueChanged += OnValueChanged;
return val;
return pValue;
}
}
/// <summary>
/// Return up-to-date collection of subvariables.
/// This collection is lazy - you need to call its method Update if you want to use it later
/// </summary>
public VariableCollection SubVariables {
public bool IsStatic {
get {
subVariables.Update();
return subVariables;
return isStatic;
}
}
public bool MayHaveSubVariables {
public bool IsPublic {
get {
return Value.MayHaveSubVariables;
return isPublic;
}
}
protected internal virtual void OnValueChanged(object sender, ValueEventArgs e)
{
if (valueChanged != null) {
valueChanged(this, new VariableEventArgs(this));
public Value Value {
get {
return pValue.Value;
}
}
public bool MayHaveSubVariables {
get {
return pValue.Value.MayHaveSubVariables;
}
}
protected internal virtual void OnValueRemovedFromCollection(VariableCollectionEventArgs e) {
if (ValueRemovedFromCollection != null) {
ValueRemovedFromCollection(this, e);
public VariableCollection SubVariables {
get {
return pValue.Value.SubVariables;
}
}
public Variable(NDebugger debugger, string name, PersistentValue pValue)
public Variable(string name, PersistentValue pValue):this(name, false, true, pValue)
{
}
public Variable(string name, bool isStatic, bool isPublic, PersistentValue pValue)
{
this.debugger = debugger;
this.name = name;
this.isStatic = isStatic;
this.isPublic = isPublic;
this.pValue = pValue;
this.subVariables = new VariableCollection(debugger);
this.subVariables.Updating += OnSubVariablesUpdating;
if (name.StartsWith("<") && name.Contains(">") && name != "<Base class>") {
string middle = name.TrimStart('<').Split('>')[0]; // Get text between '<' and '>'
@ -106,10 +109,5 @@ namespace Debugger @@ -106,10 +109,5 @@ namespace Debugger
}
}
}
void OnSubVariablesUpdating(object sender, VariableCollectionEventArgs e)
{
subVariables.UpdateTo(Value.GetSubVariables());
}
}
}

163
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs

@ -14,106 +14,27 @@ using Debugger.Wrappers.CorDebug; @@ -14,106 +14,27 @@ using Debugger.Wrappers.CorDebug;
namespace Debugger
{
[Serializable]
public class VariableCollection: ReadOnlyCollectionBase
public class VariableCollection: RemotingObjectBase, IEnumerable<Variable>
{
NDebugger debugger;
public static VariableCollection Empty = new VariableCollection(new Variable[] {});
public static VariableCollection Empty;
IEnumerable<Variable> collectionEnum;
bool readOnly = false;
public event EventHandler<VariableEventArgs> VariableAdded;
public event EventHandler<VariableEventArgs> VariableRemoved;
internal event EventHandler<VariableCollectionEventArgs> Updating;
public NDebugger Debugger {
get {
return debugger;
}
}
internal VariableCollection(NDebugger debugger)
IEnumerator IEnumerable.GetEnumerator()
{
this.debugger = debugger;
return GetEnumerator();
}
/// <summary>
/// Creates new collection and fills it by calling 'updating' delegate
/// </summary>
/// <param name="updating"></param>
/// <returns></returns>
internal VariableCollection(NDebugger debugger, EventHandler<VariableCollectionEventArgs> updating): this(debugger)
public IEnumerator<Variable> GetEnumerator()
{
this.Updating += updating;
this.Update();
}
static VariableCollection()
{
Empty = new VariableCollection(null);
Empty.readOnly = true;
}
public bool Contains(Variable variable)
{
foreach (Variable v in InnerList) {
if (v == variable) {
return true;
}
}
return false;
}
public bool Contains(string variableName)
{
foreach (Variable v in InnerList) {
if (v.Name == variableName) {
return true;
}
}
return false;
}
internal void Add(Variable variable)
{
if (readOnly) {
throw new DebuggerException("VariableCollection is marked as read only");
}
if (variable != null) {
InnerList.Add(variable);
OnVariableAdded(new VariableEventArgs(variable));
}
}
internal void Remove(Variable variable)
{
if (readOnly) {
throw new DebuggerException("VariableCollection is marked as read only");
}
if (variable != null) {
InnerList.Remove(variable);
OnVariableRemoved(new VariableEventArgs(variable));
variable.OnValueRemovedFromCollection(new VariableCollectionEventArgs(this));
}
return collectionEnum.GetEnumerator();
}
/// <summary>
/// Removes all variables from collection and resets the updating function
/// </summary>
internal void Clear()
internal VariableCollection(IEnumerable<Variable> collectionEnum)
{
while(this.Count > 0) {
this.Remove(this[0]);
}
Updating = null;
this.collectionEnum = collectionEnum;
}
public Variable this[int index] {
get {
return (Variable) InnerList[index];
}
}
public Variable this[string variableName] {
get {
int index = variableName.IndexOf('.');
@ -122,81 +43,19 @@ namespace Debugger @@ -122,81 +43,19 @@ namespace Debugger
string subVariable = variableName.Substring(index + 1);
return this[rootVariable].SubVariables[subVariable];
} else {
foreach (Variable v in InnerList) {
if (v.Name == variableName) {
return v;
}
foreach (Variable v in this) {
if (v.Name == variableName) return v;
}
}
throw new DebuggerException("Variable \"" + variableName + "\" is not in collection");
}
}
public void Update()
{
OnUpdating();
}
public void UpdateTo(IEnumerable<Variable> newVariables)
{
ArrayList toBeRemoved = (ArrayList)this.InnerList.Clone();
foreach(Variable newVariable in newVariables) {
if (this.Contains(newVariable.Name)) {
Variable oldVariable = this[newVariable.Name];
// HACK: Realy bad object-oriented design!!!
// Trasfer the new variable into the old one
if (oldVariable != newVariable) {
oldVariable.pValue = newVariable.pValue;
if (newVariable is ClassVariable && oldVariable is ClassVariable) {
((ClassVariable)oldVariable).isPublic = ((ClassVariable)oldVariable).isPublic;
((ClassVariable)oldVariable).isStatic = ((ClassVariable)oldVariable).isStatic;
}
if (newVariable is PropertyVariable) {
newVariable.ValueChanged += delegate { oldVariable.OnValueChanged(this, null); };
}
}
// Keep the variable in the list
toBeRemoved.Remove(this[newVariable.Name]);
} else {
// Add new variable
this.Add(newVariable);
}
}
foreach(Variable variable in toBeRemoved) {
this.Remove(variable);
}
}
protected virtual void OnVariableAdded(VariableEventArgs e)
{
if (VariableAdded != null) {
VariableAdded(this, e);
}
}
protected virtual void OnVariableRemoved(VariableEventArgs e)
{
if (VariableRemoved != null) {
VariableRemoved(this, e);
}
}
protected virtual void OnUpdating()
{
if (Updating != null) {
Updating(this, new VariableCollectionEventArgs(this));
}
}
public override string ToString() {
string txt = "";
foreach(Variable v in this) {
txt += v.ToString() + "\n";
}
return txt;
}
}

28
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollectionEventArgs.cs

@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger
{
[Serializable]
public class VariableCollectionEventArgs: DebuggerEventArgs
{
VariableCollection variableCollection;
public VariableCollection VariableCollection {
get {
return variableCollection;
}
}
public VariableCollectionEventArgs(VariableCollection variableCollection): base(variableCollection.Debugger)
{
this.variableCollection = variableCollection;
}
}
}

28
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableEventArgs.cs

@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger
{
[Serializable]
public class VariableEventArgs : DebuggerEventArgs
{
Variable variable;
public Variable Variable {
get {
return variable;
}
}
public VariableEventArgs(Variable variable): base(variable.Debugger)
{
this.variable = variable;
}
}
}

29
src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs

@ -484,22 +484,20 @@ namespace Debugger.Tests @@ -484,22 +484,20 @@ namespace Debugger.Tests
Assert.AreEqual("privateField", subVars[1].Name);
Assert.AreEqual("publicFiled", subVars[2].Name);
Assert.AreEqual("PublicProperty", subVars[3].Name);
Assert.AreEqual(typeof(ClassVariable), subVars[1].GetType());
Assert.AreEqual(typeof(ClassVariable), subVars[2].GetType());
Assert.AreEqual(typeof(PropertyVariable), subVars[3].GetType());
Assert.AreEqual(false, ((ClassVariable)subVars[1]).IsPublic);
Assert.AreEqual(true, ((ClassVariable)subVars[2]).IsPublic);
Assert.AreEqual(true, ((ClassVariable)subVars[3]).IsPublic);
Assert.AreEqual(typeof(Variable), subVars[1].GetType());
Assert.AreEqual(typeof(Variable), subVars[2].GetType());
Assert.AreEqual(typeof(Variable), subVars[3].GetType());
Assert.AreEqual(false, ((Variable)subVars[1]).IsPublic);
Assert.AreEqual(true, ((Variable)subVars[2]).IsPublic);
Assert.AreEqual(true, ((Variable)subVars[3]).IsPublic);
Assert.AreEqual(true, ((ObjectValue)local.Value).HasBaseClass);
baseClass = subVars[0];
Assert.AreEqual(typeof(ObjectValue), baseClass.Value.GetType());
Assert.AreEqual(false, baseClass.Value.IsExpired);
Assert.AreEqual("{Debugger.Tests.TestPrograms.BaseClass}", baseClass.Value.AsString);
debugger.Continue();
WaitForPause(PausedReason.Break, null);
Assert.AreEqual(typeof(ObjectValue), baseClass.Value.GetType());
Assert.AreEqual(false, baseClass.Value.IsExpired);
Assert.AreEqual("{Debugger.Tests.TestPrograms.BaseClass}", baseClass.Value.AsString);
debugger.Continue();
@ -566,22 +564,17 @@ namespace Debugger.Tests @@ -566,22 +564,17 @@ namespace Debugger.Tests
Assert.AreEqual(typeof(Variable), local.GetType());
foreach(Variable var in local.SubVariables) {
if (var is PropertyVariable) {
Assert.AreEqual(typeof(UnavailableValue), var.Value.GetType(), "Variable name: " + var.Name);
debugger.StartEvaluation();
WaitForPause(PausedReason.EvalComplete, null);
Assert.AreEqual(false, var.Value.IsExpired, "Variable name: " + var.Name);
Assert.AreNotEqual(null, var.Value.AsString, "Variable name: " + var.Name);
}
Assert.AreEqual(typeof(UnavailableValue), var.Value.GetType(), "Variable name: " + var.Name);
debugger.StartEvaluation();
WaitForPause(PausedReason.EvalComplete, null);
Assert.AreNotEqual(null, var.Value.AsString, "Variable name: " + var.Name);
}
debugger.Continue();
WaitForPause(PausedReason.Break, null);
foreach(Variable var in local.SubVariables) {
if (var is PropertyVariable) {
Assert.AreEqual(typeof(UnavailableValue), var.Value.GetType(), "Variable name: " + var.Name);
}
Assert.AreEqual(typeof(UnavailableValue), var.Value.GetType(), "Variable name: " + var.Name);
}
debugger.StartEvaluation();
WaitForPause(PausedReason.EvalComplete, null);

Loading…
Cancel
Save