diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs index 583a1322e0..1675938b37 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/LocalVarPad.cs @@ -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 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 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 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 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 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); + } + } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs index a6fa27a7f6..4875910d21 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs @@ -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 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 { 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 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 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); - } - } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs index e3876042da..e590de8efe 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DebuggerIcons.cs @@ -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 diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs index 1a2d1f15b9..1776cb0051 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Service/DynamicTreeDebuggerRow.cs @@ -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 { } - bool skipUpdate = true; - public DynamicTreeDebuggerRow(Variable variable) { if (variable == null) throw new ArgumentNullException("variable"); @@ -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 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 } 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 List privateInstance = new List(); 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); } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj index 2c329ed30f..f864a45da3 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj @@ -190,18 +190,14 @@ - - - - @@ -381,6 +377,8 @@ + + diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs index 9cec04ccf2..b679baac69 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/DebugeeState.cs @@ -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. /// - 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); + } + } + } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/IExpirable.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/IExpirable.cs new file mode 100644 index 0000000000..d3ccdd15e1 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/IExpirable.cs @@ -0,0 +1,18 @@ +// +// +// +// +// $Revision: 1551 $ +// + +using System; + +namespace Debugger +{ + public interface IExpirable + { + event EventHandler Expired; + + bool HasExpired { get ; } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs index 0a3dccc910..6a4d2a5060 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs @@ -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) { diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs index 47477bbed9..c0123ea107 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger-StateControl.cs @@ -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 throw new DebuggerException("Already resumed"); } + pauseSession.NotifyHasExpired(); pauseSession = null; OnDebuggingResumed(); pausedHandle.Reset(); diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs index f7cb6164ea..de8ea978c9 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/NDebugger.cs @@ -24,8 +24,6 @@ namespace Debugger MTA2STA mta2sta = new MTA2STA(); - VariableCollection localVariables; - string debuggeeVersion; public MTA2STA MTA2STA { @@ -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 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 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; + } } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs index 1b415010fd..128220b693 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/PauseSession.cs @@ -15,10 +15,30 @@ namespace Debugger /// Holds information about the state of paused debugger. /// Expires when when Continue is called on debugger. /// - 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; diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs index d0b5af3da3..54620c3882 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Exception.cs @@ -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") { diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs index 3381203cc0..e65c530b4f 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Function.cs @@ -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 } } - 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 } } - public IEnumerable 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 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 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 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 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) diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs index b39f647906..87608ff910 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/NDebugger-Processes.cs @@ -49,6 +49,7 @@ namespace Debugger internal void RemoveProcess(Process process) { processCollection.Remove(process); + process.NotifyHasExpired(); OnProcessExited(process); // noProcessesHandle is set in NDebugger.TerminateDebugger } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs index aa93ae6f4e..bed55c311a 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs @@ -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 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; diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs index e1f1418185..f25d990061 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs @@ -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; } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs index c38f89f2bc..c112297dd8 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ArrayValue.cs @@ -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 } } - public override IEnumerable GetSubVariables() + protected override IEnumerable GetSubVariables() { uint[] indices = new uint[rank]; diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ClassVariable.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ClassVariable.cs deleted file mode 100644 index 19094c4290..0000000000 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ClassVariable.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// -// -// -// $Revision$ -// - -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; - } - - } -} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs index 8afda9cfb0..c369936313 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.cs @@ -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}; /// /// This class holds information about function evaluation. @@ -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 EvalStarted; public event EventHandler EvalComplete; @@ -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(); } } - /// - /// True if the evaluation has been completed. - /// 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; - } - } - - /// - /// The result of the evaluation. Always non-null, but it may be UnavailableValue. - /// - 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 } } - 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 dependencies = new List(); + 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; } - /// True is setup was successful + /// True if setup was successful 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 corArgs = new List(); + 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 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 } } - 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)); - } } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs index 724a9e088d..5ee64f3ec2 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/EvalEventArgs.cs @@ -20,12 +20,6 @@ namespace Debugger } } - public Value Result { - get { - return eval.Result; - } - } - public EvalEventArgs(Eval eval): base(eval.Debugger) { this.eval = eval; diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs index c1a6570974..33a254da71 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValue.cs @@ -55,11 +55,29 @@ namespace Debugger } } + public IEnumerable 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 } } - public override IEnumerable GetSubVariables() + protected override IEnumerable GetSubVariables() { if (HasBaseClass) { yield return GetBaseClassVariable(); @@ -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 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()}; - } 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 public Variable GetBaseClassVariable() { if (HasBaseClass) { - return new Variable(debugger, - "", - new PersistentValue(debugger, delegate { return GetBaseClassValue(); })); + return new Variable("", + new PersistentValue(debugger, + new IExpirable[] {this.PersistentValue}, + delegate { return GetBaseClassValue(); })); } else { return null; } @@ -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; } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs index 1b98030d45..aaece7d832 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs @@ -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. /// - public class PersistentValue + public class PersistentValue: IExpirable { /// /// Delegate that is used to get value. This delegate may be called at any time and should never return null. /// 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 ValueChanged; public NDebugger Debugger { get { @@ -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 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()) { - return corValue.As(); + return corValue; } corValue = PersistentValue.DereferenceUnbox(corValue); if (corValue != null && corValue.Is()) { - return corValue.As().CreateHandle(CorDebugHandleType.HANDLE_WEAK_TRACK_RESURRECTION); + return corValue.As().CreateHandle(CorDebugHandleType.HANDLE_WEAK_TRACK_RESURRECTION).CastTo(); } 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()) { - 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()) { @@ -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 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"); } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValueEventArgs.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValueEventArgs.cs new file mode 100644 index 0000000000..97b80606dc --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValueEventArgs.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace Debugger +{ + public class PersistentValueEventArgs: EventArgs + { + PersistentValue persistentValue; + + public PersistentValue PersistentValue { + get { + return persistentValue; + } + } + + public PersistentValueEventArgs(PersistentValue persistentValue) + { + this.persistentValue = persistentValue; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs index 74d0c3d48f..824dedbc24 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PrimitiveValue.cs @@ -47,7 +47,7 @@ namespace Debugger } else { (CorValue.CastTo()).Value = newValue; } - OnValueChanged(); + PersistentValue.NotifyValueChange(); } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PropertyVariable.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PropertyVariable.cs deleted file mode 100644 index b53b929deb..0000000000 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PropertyVariable.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// -// -// -// $Revision$ -// - -using System; - -using Debugger.Wrappers.CorDebug; - -namespace Debugger -{ - /// - /// Delegate that is used to get eval. This delegate may be called at any time and may return null. - /// - 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; - } - } - } -} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs index 7b9cb0ab12..c303b1048e 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/UnavailableValue.cs @@ -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; } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs index a27d090526..589708eb1b 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Value.cs @@ -16,9 +16,7 @@ namespace Debugger public abstract class Value: RemotingObjectBase { protected NDebugger debugger; - protected PersistentValue pValue; - - public event EventHandler ValueChanged; + PersistentValue pValue; public NDebugger Debugger { get { @@ -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 } } - /// - /// If true than the value is no longer valid and you should obtain updated copy - /// - public bool IsExpired { - get { - return pValue.IsExpired; - } - } - internal CorElementType CorType { get { return GetCorType(CorValue); @@ -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 get; } - /// - /// Gets the subvariables of this value - /// - public virtual IEnumerable GetSubVariables() + public VariableCollection SubVariables { + get { + return new VariableCollection(GetSubVariables()); + } + } + + protected virtual IEnumerable 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"); diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs index 9e647d4b7b..36aa0a962d 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs @@ -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 valueChanged; - public event EventHandler ValueRemovedFromCollection; + public event EventHandler ValueChanged { + add { + pValue.ValueChanged += value; + } + remove { + pValue.ValueChanged -= value; + } + } - public event EventHandler 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 } } - /// - /// Gets value of variable which is safe to use (it is not null and it is not expired) - /// - public Value Value { + public PersistentValue PersistentValue { get { - Value val = pValue.Value; - val.ValueChanged -= OnValueChanged; - val.ValueChanged += OnValueChanged; - return val; + return pValue; } } - /// - /// 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 - /// - 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 != "") { string middle = name.TrimStart('<').Split('>')[0]; // Get text between '<' and '>' @@ -106,10 +109,5 @@ namespace Debugger } } } - - void OnSubVariablesUpdating(object sender, VariableCollectionEventArgs e) - { - subVariables.UpdateTo(Value.GetSubVariables()); - } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs index 1469048c8b..6e6fa6ea76 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollection.cs @@ -14,106 +14,27 @@ using Debugger.Wrappers.CorDebug; namespace Debugger { [Serializable] - public class VariableCollection: ReadOnlyCollectionBase + public class VariableCollection: RemotingObjectBase, IEnumerable { - NDebugger debugger; + public static VariableCollection Empty = new VariableCollection(new Variable[] {}); - public static VariableCollection Empty; + IEnumerable collectionEnum; - bool readOnly = false; - - public event EventHandler VariableAdded; - public event EventHandler VariableRemoved; - internal event EventHandler Updating; - - public NDebugger Debugger { - get { - return debugger; - } - } - - internal VariableCollection(NDebugger debugger) + IEnumerator IEnumerable.GetEnumerator() { - this.debugger = debugger; + return GetEnumerator(); } - /// - /// Creates new collection and fills it by calling 'updating' delegate - /// - /// - /// - internal VariableCollection(NDebugger debugger, EventHandler updating): this(debugger) + public IEnumerator 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(); } - /// - /// Removes all variables from collection and resets the updating function - /// - internal void Clear() + internal VariableCollection(IEnumerable 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 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 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; } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollectionEventArgs.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollectionEventArgs.cs deleted file mode 100644 index b94f22db57..0000000000 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableCollectionEventArgs.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// -// -// -// $Revision$ -// - -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; - } - } -} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableEventArgs.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableEventArgs.cs deleted file mode 100644 index 961ea9255d..0000000000 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/VariableEventArgs.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// -// -// -// $Revision$ -// - -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; - } - } -} diff --git a/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs b/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs index d0ffe2cb9b..f4153e4745 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs @@ -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 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);