From a57ab03b7fe1cb340a2c3a330afa9c0748e29523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 15 Jul 2006 20:48:28 +0000 Subject: [PATCH] ObjectValue split into ObjectValue and ObjectValueClass; Variable submenus implemented in Debugger.Core git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1582 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Pads/LocalVarPad.cs | 52 ++-- .../Src/Service/DynamicTreeDebuggerRow.cs | 74 ++---- .../Project/Debugger.Core.csproj | 5 +- .../Project/Src/Threads/Exception.cs | 19 +- .../Project/Src/Threads/Thread.cs | 2 +- .../Project/Src/Variables/ArrayValue.cs | 12 +- .../Variables/Evals/Eval.CallFunctionEval.cs | 2 +- .../Project/Src/Variables/NullValue.cs | 2 +- .../Project/Src/Variables/ObjectValue.cs | 237 ++---------------- .../Project/Src/Variables/ObjectValueClass.cs | 227 +++++++++++++++++ .../Project/Src/Variables/PrimitiveValue.cs | 2 +- .../Project/Src/Variables/UnavailableValue.cs | 4 +- .../Project/Src/Variables/Value.cs | 16 +- .../{PersistentValue.cs => Variable.cs} | 40 +-- .../Src/Variables/VariableCollection.cs | 51 +++- .../Project/Src/DebuggerTests.cs | 1 - 16 files changed, 375 insertions(+), 371 deletions(-) create mode 100644 src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValueClass.cs rename src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/{PersistentValue.cs => Variable.cs} (84%) 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 1675938b37..a86e6c43a7 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 @@ -95,44 +95,20 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads public static void AddVariableCollectionToTree(VariableCollection varCollection, TreeListViewItemCollection tree) { - TreeListViewItem privateInstanceMenu = new TreeListViewItem(privateMembersName, 0); - TreeListViewItem staticMenu = new TreeListViewItem(staticMembersName, 0); - TreeListViewItem privateStaticMenu = new TreeListViewItem(privateStaticMembersName, 0); - - foreach(Variable variable in varCollection) { - if (variable.IsPublic) { - if (variable.IsStatic) { - // Public static - if (staticMenu.TreeListView == null) { - tree.Add(staticMenu); - tree.Sort(false); - } - staticMenu.Items.Add(new TreeListViewDebuggerItem(variable)); - } else { - // Public instance - tree.Add(new TreeListViewDebuggerItem(variable)); - } - } else { - if (variable.IsStatic) { - // Private static - if (staticMenu.TreeListView == null) { - tree.Add(staticMenu); - tree.Sort(false); - } - if (privateStaticMenu.TreeListView == null) { - staticMenu.Items.Add(privateStaticMenu); - staticMenu.Items.Sort(false); - } - privateStaticMenu.Items.Add(new TreeListViewDebuggerItem(variable)); - } else { - // Private instance - if (privateInstanceMenu.TreeListView == null) { - tree.Add(privateInstanceMenu); - tree.Sort(false); - } - privateInstanceMenu.Items.Add(new TreeListViewDebuggerItem(variable)); - } - } + foreach(VariableCollection sub in varCollection.SubCollections) { + VariableCollection subCollection = sub; + TreeListViewItem subMenu = new TreeListViewItem("<" + subCollection.Name + ">", 0); + subMenu.SubItems.Add(subCollection.Value); + tree.Add(subMenu); + TreeListViewItem.TreeListViewItemHanlder populate = null; + populate = delegate { + AddVariableCollectionToTree(subCollection, subMenu.Items); + subMenu.AfterExpand -= populate; + }; + subMenu.AfterExpand += populate; + } + foreach(Variable variable in varCollection.Items) { + tree.Add(new TreeListViewDebuggerItem(variable)); } } 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 dcb9d5a68e..97c98330ec 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 @@ -177,64 +177,34 @@ namespace ICSharpCode.SharpDevelop.Services void Populate() { - List publicStatic = new List(); - List publicInstance = new List(); - List privateStatic = new List(); - List privateInstance = new List(); - - foreach(Variable variable in this.Variable.Value.SubVariables) { - if (variable.IsPublic) { - if (variable.IsStatic) { - publicStatic.Add(variable); - } else { - publicInstance.Add(variable); - } - } else { - if (variable.IsStatic) { - privateStatic.Add(variable); - } else { - privateInstance.Add(variable); - } - } - } - - this.ChildRows.Clear(); - // Private Members - if (privateInstance.Count > 0) { - this.ChildRows.Add(MakeSubMenu("Private Members", - RowsFromVariables(privateInstance))); - } - // Static Members - if (publicStatic.Count > 0 || privateStatic.Count > 0) { - DynamicTreeRow privateStaticSubMenu = MakeSubMenu("Private Static Members", - RowsFromVariables(privateStatic)); - this.ChildRows.Add(MakeSubMenu("Static Members", - privateStatic.Count > 0 ? new DynamicListRow[]{privateStaticSubMenu} : new DynamicListRow[]{}, - RowsFromVariables(publicStatic))); - } - // Public Members - this.ChildRows.AddRange(RowsFromVariables(publicInstance)); - + Fill(this, Variable.Value.SubVariables); populated = true; } - IEnumerable RowsFromVariables(IEnumerable variables) + static void Fill(DynamicTreeRow row, VariableCollection collection) { - foreach(Variable variable in variables) { - yield return new DynamicTreeDebuggerRow(variable); + row.ChildRows.Clear(); + foreach(VariableCollection sub in collection.SubCollections) { + VariableCollection subCollection = sub; + + DynamicTreeRow subMenu = new DynamicTreeRow(); + DebuggerGridControl.AddColumns(subMenu.ChildColumns); + subMenu[2].Text = subCollection.Name; + subMenu[3].Text = subCollection.Value; + subMenu.ShowMinusWhileExpanded = true; + + EventHandler populate = null; + populate = delegate { + Fill(subMenu, subCollection); + subMenu.Expanding -= populate; + }; + subMenu.Expanding += populate; + + row.ChildRows.Add(subMenu); } - } - - DynamicTreeRow MakeSubMenu(string name, params IEnumerable[] elements) - { - DynamicTreeRow rootRow = new DynamicTreeRow(); - rootRow.ShowMinusWhileExpanded = true; - DebuggerGridControl.AddColumns(rootRow.ChildColumns); - rootRow[2].Text = name; - foreach(IEnumerable rows in elements) { - rootRow.ChildRows.AddRange(rows); + foreach(Variable variable in collection.Items) { + row.ChildRows.Add(new DynamicTreeDebuggerRow(variable)); } - return rootRow; } } } 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 7156de988d..99806f593b 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj @@ -188,7 +188,7 @@ - + @@ -380,7 +380,8 @@ - + + 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 6d83322dc2..f2e865e021 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 @@ -20,7 +20,7 @@ namespace Debugger Thread thread; ICorDebugValue corValue; Value runtimeValue; - ObjectValue runtimeValueException; + ObjectValueClass runtimeValueException; ExceptionType exceptionType; SourcecodeSegment location; DateTime creationTime; @@ -45,19 +45,10 @@ namespace Debugger "$exception", Variable.Flags.Default, new IExpirable[] {debugger.PauseSession}, - corValue).Value; - runtimeValueException = runtimeValue as ObjectValue; - if (runtimeValueException != null) { - while (runtimeValueException.Type != "System.Exception") { - if (runtimeValueException.HasBaseClass == false) { - runtimeValueException = null; - break; - } - runtimeValueException = runtimeValueException.BaseClass; - } - message = runtimeValueException["_message"].Value.AsString; - } - + delegate { return corValue; } ).Value; + runtimeValueException = ((ObjectValue)runtimeValue).GetClass("System.Exception"); + message = runtimeValueException.SubVariables["_message"].Value.AsString; + if (thread.LastFunctionWithLoadedSymbols != null) { location = thread.LastFunctionWithLoadedSymbols.NextStatement; } 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 b22230eb08..cf74b88b3f 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 @@ -118,7 +118,7 @@ namespace Debugger "thread" + id, Variable.Flags.Default, new IExpirable[] {debugger.PauseSession}, - corThread.Object).Value; + delegate { return 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 0579d98371..5111515ab7 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 @@ -60,7 +60,7 @@ namespace Debugger } - internal unsafe ArrayValue(NDebugger debugger, Variable variable):base(debugger, variable) + internal unsafe ArrayValue(Variable variable):base(variable) { corElementType = (CorElementType)CorArrayValue.ElementType; @@ -118,7 +118,7 @@ namespace Debugger elementName += indices[i].ToString() + ","; elementName = elementName.TrimEnd(new char[] {','}) + "]"; - return new Variable(debugger, + return new Variable(Debugger, elementName, Variable.Flags.Default, new IExpirable[] {this.Variable}, @@ -139,7 +139,13 @@ namespace Debugger } } - protected override IEnumerable GetSubVariables() + public override VariableCollection SubVariables { + get { + return new VariableCollection(GetSubVariables()); + } + } + + IEnumerable GetSubVariables() { uint[] indices = new uint[rank]; diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.CallFunctionEval.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.CallFunctionEval.cs index bfc1e3f8cf..ee7424919d 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.CallFunctionEval.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Evals/Eval.CallFunctionEval.cs @@ -75,7 +75,7 @@ namespace Debugger OnError("Can not evaluate on a value which is not an object"); return false; } - if (corFunction != null && !((ObjectValue)val).IsSuperClass(corFunction.Class)) { + if (corFunction != null && !((ObjectValue)val).IsInClassHierarchy(corFunction.Class)) { OnError("Can not evaluate because the object does not contain specified function"); return false; } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/NullValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/NullValue.cs index a832294232..217ad561b8 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/NullValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/NullValue.cs @@ -36,7 +36,7 @@ namespace Debugger } } - internal unsafe NullValue(NDebugger debugger, Variable variable):base(debugger, variable) + internal unsafe NullValue(Variable variable):base(variable) { } 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 08f1c5e267..1342948d6d 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 @@ -18,18 +18,7 @@ namespace Debugger { public class ObjectValue: Value { - Module finalCorClassModule; - uint finalCorClassToken; - - Module module; - ICorDebugClass corClass; - TypeDefProps classProps; - - protected ICorDebugObjectValue CorObjectValue { - get { - return this.CorValue.CastTo(); - } - } + ObjectValueClass topClass; public override string AsString { get { @@ -39,244 +28,66 @@ namespace Debugger public override string Type { get{ - return classProps.Name; + return topClass.Type; } } - public Module Module { - get { - return module; - } - } - - public uint ClassToken { + public ObjectValueClass TopClass { get { - return classProps.Token; + return topClass; } } - public IEnumerable SuperClasses { + public IEnumerable Classes { get { - ICorDebugClass currentClass = corClass; + ObjectValueClass currentClass = topClass; do { - yield return new ObjectValue(debugger, this.Variable, currentClass); - currentClass = GetSuperClass(debugger, currentClass); + yield return currentClass; + currentClass = currentClass.BaseClass; } while (currentClass != null); } } - internal bool IsSuperClass(ICorDebugClass corClass) + public ObjectValueClass GetClass(string type) { - 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, this.Variable, superClass); + foreach(ObjectValueClass cls in Classes) { + if (cls.Type == type) return cls; } + return null; } - public bool HasBaseClass { - get { - return GetSuperClass(debugger, corClass) != null; + internal bool IsInClassHierarchy(ICorDebugClass corClass) + { + foreach(ObjectValueClass cls in Classes) { + if (cls.CorClass == corClass) return true; } + return false; } - internal ObjectValue(NDebugger debugger, Variable variable):base(debugger, variable) + internal ObjectValue(Variable variable):base(variable) { - InitObjectValue(this.CorObjectValue.Class); - } - - internal ObjectValue(NDebugger debugger, Variable variable, ICorDebugClass corClass):base(debugger, variable) - { - InitObjectValue(corClass); - } - - void InitObjectValue(ICorDebugClass corClass) - { - this.finalCorClassModule = debugger.GetModule(this.CorObjectValue.Class.Module); - this.finalCorClassToken = this.CorObjectValue.Class.Token; - - this.module = debugger.GetModule(corClass.Module); - this.corClass = corClass; - this.classProps = Module.MetaData.GetTypeDefProps(corClass.Token); + topClass = new ObjectValueClass(this, this.CorValue.As().Class); } - bool IsCorValueCompatible { + internal bool IsCorValueCompatible { get { ObjectValue freshValue = this.FreshValue as ObjectValue; return freshValue != null && - this.finalCorClassModule == freshValue.Module && - this.finalCorClassToken == freshValue.ClassToken; + topClass.Module == freshValue.TopClass.Module && + topClass.ClassToken == freshValue.TopClass.ClassToken; } } - public ObjectValue ObjectClass { - get { - ObjectValue objectClass = this; - while (objectClass.HasBaseClass) { - objectClass = objectClass.BaseClass; - } - return objectClass; - } - } - - IEnumerable Methods { - get { - return this.Module.MetaData.EnumMethods(this.ClassToken); - } - } - public override bool MayHaveSubVariables { get { return true; } } - protected override IEnumerable GetSubVariables() - { - if (HasBaseClass) { - yield return GetBaseClassVariable(); - } - - foreach(Variable var in GetFieldVariables()) { - yield return var; - } - - foreach(Variable var in GetPropertyVariables()) { - yield return var; - } - } - - public IEnumerable GetFieldVariables() - { - foreach(FieldProps f in Module.MetaData.EnumFields(ClassToken)) { - 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 Variable(debugger, - field.Name, - (field.IsStatic ? Variable.Flags.Static : Variable.Flags.None) | - (field.IsPublic ? Variable.Flags.Public : Variable.Flags.None), - new IExpirable[] {this.Variable}, - delegate { return GetCorValueOfField(field); }); - } - } - - ICorDebugValue GetCorValueOfField(FieldProps field) - { - if (!IsCorValueCompatible) throw new CannotGetValueException("Object type changed"); - - // Current frame is used to resolve context specific static values (eg. ThreadStatic) - ICorDebugFrame curFrame = null; - if (debugger.IsPaused && debugger.SelectedThread != null && debugger.SelectedThread.LastFunction != null && debugger.SelectedThread.LastFunction.CorILFrame != null) { - curFrame = debugger.SelectedThread.LastFunction.CorILFrame.CastTo(); - } - - try { - if (field.IsStatic) { - return corClass.GetStaticFieldValue(field.Token, curFrame); - } else { - return CorObjectValue.GetFieldValue(corClass, field.Token); - } - } catch { - throw new CannotGetValueException(); - } - } - - public IEnumerable GetPropertyVariables() - { - foreach(MethodProps m in Methods) { - MethodProps method = m; // One per scope/delegate - if (method.HasSpecialName && method.Name.StartsWith("get_") && method.Name != "get_Item") { - Eval eval = Eval.CallFunction(debugger, - Module.CorModule.GetFunctionFromToken(method.Token), - true, // reevaluateAfterDebuggeeStateChange - method.IsStatic? null : this.Variable, - new Variable[] {}); - Variable var = eval.Result; - var.Name = method.Name.Remove(0, 4); - var.VariableFlags = (method.IsStatic ? Variable.Flags.Static : Variable.Flags.None) | - (method.IsPublic ? Variable.Flags.Public : Variable.Flags.None); - yield return var; - } - } - } - - public Variable GetBaseClassVariable() - { - if (HasBaseClass) { - return new Variable(debugger, - "", - Variable.Flags.Default, - new IExpirable[] {this.Variable}, - delegate { return GetBaseClassValue(); }); - } else { - return null; - } - } - - Value GetBaseClassValue() - { - if (!IsCorValueCompatible) throw new CannotGetValueException("Object type changed"); - - return this.BaseClass; - } - - protected static ICorDebugClass GetSuperClass(NDebugger debugger, ICorDebugClass currClass) - { - Module currModule = debugger.GetModule(currClass.Module); - uint superToken = currModule.MetaData.GetTypeDefProps(currClass.Token).SuperClassToken; - - // It has no base class - if ((superToken & 0x00FFFFFF) == 0x00000000) return null; - - // TypeDef - Localy defined - if ((superToken & 0xFF000000) == 0x02000000) { - return currModule.CorModule.GetClassFromToken(superToken); - } - - // TypeRef - Referencing to external assembly - if ((superToken & 0xFF000000) == 0x01000000) { - string fullTypeName = currModule.MetaData.GetTypeRefProps(superToken).Name; - - foreach (Module superModule in debugger.Modules) { - // TODO: Does not work for nested - // TODO: preservesig - try { - uint token = superModule.MetaData.FindTypeDefByName(fullTypeName, 0).Token; - return superModule.CorModule.GetClassFromToken(token); - } catch { - continue; - } - } - } - - throw new DebuggerException("Superclass not found"); - } - - /* - // May return null - public Eval ToStringEval { + public override VariableCollection SubVariables { get { - ObjectValue baseClassObject = this.BaseClassObject; - foreach(MethodProps method in baseClassObject.Methods) { - if (method.Name == "ToString") { - ICorDebugValue[] evalArgs; - ICorDebugFunction evalCorFunction; - baseClassObject.Module.CorModule.GetFunctionFromToken(method.Token, out evalCorFunction); - evalArgs = new ICorDebugValue[] {this.SoftReference}; - return new Eval(debugger, evalCorFunction, evalArgs); - } - } - throw new DebuggerException("ToString method not found"); + return topClass.SubVariables; } } - */ } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValueClass.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValueClass.cs new file mode 100644 index 0000000000..707e74db15 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/ObjectValueClass.cs @@ -0,0 +1,227 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Runtime.InteropServices; + +using Debugger.Wrappers.CorDebug; +using Debugger.Wrappers.MetaData; + +namespace Debugger +{ + public class ObjectValueClass: RemotingObjectBase + { + NDebugger debugger; + + ObjectValue objectValue; + + Module module; + ICorDebugClass corClass; + TypeDefProps classProps; + + ICorDebugObjectValue CorObjectValue { + get { + return objectValue.CorValue.As(); + } + } + + bool IsCorValueCompatible { + get { + return objectValue.IsCorValueCompatible; + } + } + + public Module Module { + get { + return module; + } + } + + public string Type { + get{ + return classProps.Name; + } + } + + internal ICorDebugClass CorClass { + get { + return corClass; + } + } + + public uint ClassToken { + get { + return classProps.Token; + } + } + + public ObjectValueClass(ObjectValue objectValue, ICorDebugClass corClass) + { + this.debugger = objectValue.Debugger; + this.objectValue = objectValue; + this.module = debugger.GetModule(corClass.Module); + this.corClass = corClass; + this.classProps = Module.MetaData.GetTypeDefProps(corClass.Token); + } + + public VariableCollection SubVariables { + get { + return new VariableCollection("Base class", + "{" + Type + "}", + SubCollections, + GetSubVariables(Variable.Flags.Public, Variable.Flags.PublicStatic)); + } + } + + IEnumerable SubCollections { + get { + ObjectValueClass baseClass = BaseClass; + VariableCollection privateStatic = new VariableCollection("Private static members", + String.Empty, + new VariableCollection[0], + GetSubVariables(Variable.Flags.Static, Variable.Flags.PublicStatic)); + VariableCollection privateInstance = new VariableCollection("Private members", + String.Empty, + privateStatic.IsEmpty? new VariableCollection[0] : new VariableCollection[] {privateStatic}, + GetSubVariables(Variable.Flags.None, Variable.Flags.PublicStatic)); + VariableCollection publicStatic = new VariableCollection("Static members", + String.Empty, + new VariableCollection[0], + GetSubVariables(Variable.Flags.PublicStatic, Variable.Flags.PublicStatic)); + if (baseClass != null) { + yield return baseClass.SubVariables; + } + if (!privateInstance.IsEmpty) { + yield return privateInstance; + } + if (!publicStatic.IsEmpty) { + yield return publicStatic; + } + } + } + + IEnumerable GetSubVariables(Variable.Flags requiredFlags, Variable.Flags mask) { + foreach(Variable var in GetFieldVariables()) { + if ((var.VariableFlags & mask) == requiredFlags) { + yield return var; + } + } + + foreach(Variable var in GetPropertyVariables()) { + if ((var.VariableFlags & mask) == requiredFlags) { + yield return var; + } + } + } + + public ObjectValueClass BaseClass { + get { + ICorDebugClass superClass = GetSuperClass(debugger, corClass); + if (superClass != null) { + return new ObjectValueClass(objectValue, superClass); + } else { + return null; + } + } + } + + public IEnumerable GetFieldVariables() + { + foreach(FieldProps f in Module.MetaData.EnumFields(ClassToken)) { + FieldProps field = f; // One per scope/delegate + if (field.IsStatic && field.IsLiteral) continue; // Skip field + yield return new Variable(debugger, + field.Name, + (field.IsStatic ? Variable.Flags.Static : Variable.Flags.None) | + (field.IsPublic ? Variable.Flags.Public : Variable.Flags.None), + new IExpirable[] {this.objectValue.Variable}, + delegate { return GetCorValueOfField(field); }); + } + } + + ICorDebugValue GetCorValueOfField(FieldProps field) + { + if (!IsCorValueCompatible) throw new CannotGetValueException("Object type changed"); + + // Current frame is used to resolve context specific static values (eg. ThreadStatic) + ICorDebugFrame curFrame = null; + if (debugger.IsPaused && debugger.SelectedThread != null && debugger.SelectedThread.LastFunction != null && debugger.SelectedThread.LastFunction.CorILFrame != null) { + curFrame = debugger.SelectedThread.LastFunction.CorILFrame.CastTo(); + } + + try { + if (field.IsStatic) { + return corClass.GetStaticFieldValue(field.Token, curFrame); + } else { + return CorObjectValue.GetFieldValue(corClass, field.Token); + } + } catch { + throw new CannotGetValueException("Can not get value of field"); + } + } + + IEnumerable Methods { + get { + return this.Module.MetaData.EnumMethods(this.ClassToken); + } + } + + public IEnumerable GetPropertyVariables() + { + foreach(MethodProps m in Methods) { + MethodProps method = m; // One per scope/delegate + if (method.HasSpecialName && method.Name.StartsWith("get_") && method.Name != "get_Item") { + Eval eval = Eval.CallFunction(debugger, + Module.CorModule.GetFunctionFromToken(method.Token), + true, // reevaluateAfterDebuggeeStateChange + method.IsStatic? null : this.objectValue.Variable, + new Variable[] {}); + Variable var = eval.Result; + var.Name = method.Name.Remove(0, 4); + var.VariableFlags = (method.IsStatic ? Variable.Flags.Static : Variable.Flags.None) | + (method.IsPublic ? Variable.Flags.Public : Variable.Flags.None); + yield return var; + } + } + } + + protected static ICorDebugClass GetSuperClass(NDebugger debugger, ICorDebugClass currClass) + { + Module currModule = debugger.GetModule(currClass.Module); + uint superToken = currModule.MetaData.GetTypeDefProps(currClass.Token).SuperClassToken; + + // It has no base class + if ((superToken & 0x00FFFFFF) == 0x00000000) return null; + + // TypeDef - Localy defined + if ((superToken & 0xFF000000) == 0x02000000) { + return currModule.CorModule.GetClassFromToken(superToken); + } + + // TypeRef - Referencing to external assembly + if ((superToken & 0xFF000000) == 0x01000000) { + string fullTypeName = currModule.MetaData.GetTypeRefProps(superToken).Name; + + foreach (Module superModule in debugger.Modules) { + // TODO: Does not work for nested + // TODO: preservesig + try { + uint token = superModule.MetaData.FindTypeDefByName(fullTypeName, 0).Token; + return superModule.CorModule.GetClassFromToken(token); + } catch { + continue; + } + } + } + + throw new DebuggerException("Superclass not found"); + } + } +} 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 c7795b7514..11fe6d4679 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 @@ -51,7 +51,7 @@ namespace Debugger } } - internal PrimitiveValue(NDebugger debugger, Variable variable):base(debugger, variable) + internal PrimitiveValue(Variable variable):base(variable) { } 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 5d55fc5927..fcaf9eed73 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 @@ -19,7 +19,7 @@ namespace Debugger public override string AsString { get { return message; - } + } } public override string Type { @@ -28,7 +28,7 @@ namespace Debugger } } - internal UnavailableValue(NDebugger debugger, Variable variable, string message):base(debugger, variable) + internal UnavailableValue(Variable variable, string message):base(variable) { 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 fd8772c6bc..92ebc77d34 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 @@ -15,12 +15,11 @@ namespace Debugger { public abstract class Value: RemotingObjectBase { - protected NDebugger debugger; Variable variable; public NDebugger Debugger { get { - return debugger; + return variable.Debugger; } } @@ -68,17 +67,12 @@ namespace Debugger get; } - public VariableCollection SubVariables { + public virtual VariableCollection SubVariables { get { - return new VariableCollection(GetSubVariables()); + return new VariableCollection(new Variable[] {}); } } - protected virtual IEnumerable GetSubVariables() - { - yield break; - } - public Variable this[string variableName] { get { foreach(Variable v in SubVariables) { @@ -88,9 +82,9 @@ namespace Debugger } } - protected Value(NDebugger debugger, Variable variable) + protected Value(Variable variable) { - this.debugger = debugger; + if (variable == null) throw new ArgumentNullException("variable"); this.variable = variable; } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs similarity index 84% rename from src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs rename to src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs index 1053ab8e48..c7bac653a6 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/PersistentValue.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Variable.cs @@ -17,12 +17,13 @@ namespace Debugger /// 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. + /// 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 + /// or when process exits. /// - /// ValueChange: ValueChange event is called whenever DebugeeState changes - /// or when NotifyValueChange() is called. + /// ValueChange: ValueChange event is called whenever DebugeeState + /// changes or when NotifyValueChange() is called. /// public class Variable: IExpirable { @@ -33,7 +34,7 @@ namespace Debugger public delegate ICorDebugValue CorValueGetter(); [Flags] - public enum Flags { Default = Public, None = 0, Public = 1, Static = 2}; + public enum Flags { Default = Public, None = 0, Public = 1, Static = 2, PublicStatic = Public | Static}; NDebugger debugger; @@ -81,7 +82,7 @@ namespace Debugger try { return valueGetter(); } catch (CannotGetValueException e) { - return new UnavailableValue(debugger, this, e.Message); + return new UnavailableValue(this, e.Message); } } } @@ -150,18 +151,6 @@ namespace Debugger debugger.DebuggeeStateChanged += NotifyValueChange; } - public Variable(NDebugger debugger, string name, Flags flags, IExpirable[] dependencies, ValueGetter valueGetter):this(debugger, name, flags, dependencies) - { - this.corValueGetter = delegate { throw new CannotGetValueException("CorValue not available for custom value"); }; - this.valueGetter = valueGetter; - } - - public Variable(NDebugger debugger, string name, Flags flags, IExpirable[] dependencies, ICorDebugValue corValue):this(debugger, name, flags, dependencies) - { - this.corValueGetter = delegate { return corValue; }; - this.valueGetter = delegate { return CreateValue(); }; - } - public Variable(NDebugger debugger, string name, Flags flags, IExpirable[] dependencies, CorValueGetter corValueGetter):this(debugger, name, flags, dependencies) { this.corValueGetter = corValueGetter; @@ -242,7 +231,7 @@ namespace Debugger ICorDebugValue corValue = this.CorValue; if (corValue == null) { - return new NullValue(debugger, this); + return new NullValue(this); } CorElementType type = Value.GetCorType(corValue); @@ -263,16 +252,16 @@ namespace Debugger case CorElementType.I: case CorElementType.U: case CorElementType.STRING: - return new PrimitiveValue(debugger, this); + return new PrimitiveValue(this); case CorElementType.ARRAY: case CorElementType.SZARRAY: // Short-cut for single dimension zero lower bound array - return new ArrayValue(debugger, this); + return new ArrayValue(this); case CorElementType.VALUETYPE: case CorElementType.CLASS: case CorElementType.OBJECT: // Short-cut for Class "System.Object" - return new ObjectValue(debugger, this); + return new ObjectValue(this); default: // Unknown type throw new CannotGetValueException("Unknown value type"); @@ -282,11 +271,6 @@ namespace Debugger class CannotGetValueException: System.Exception { - public CannotGetValueException():this("Unable to get value") - { - - } - public CannotGetValueException(string message):base(message) { 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 ee7b71e56a..2b1c2df436 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 @@ -18,6 +18,9 @@ namespace Debugger { public static VariableCollection Empty = new VariableCollection(new Variable[] {}); + string name; + string val; + IEnumerable subCollectionsEnum; IEnumerable collectionEnum; IEnumerator IEnumerable.GetEnumerator() @@ -27,15 +30,57 @@ namespace Debugger public IEnumerator GetEnumerator() { - return collectionEnum.GetEnumerator(); + return Items.GetEnumerator(); + } + + public string Name { + get { + return name; + } + } + + public string Value { + get { + return val; + } + } + + public IEnumerable SubCollections { + get { + return subCollectionsEnum; + } + } + + public IEnumerable Items { + get { + return collectionEnum; + } + } + + public bool IsEmpty { + get { + foreach(VariableCollection col in SubCollections) { + if (!col.IsEmpty) return false; + } + return !Items.GetEnumerator().MoveNext(); + } } internal VariableCollection(IEnumerable collectionEnum) + :this(String.Empty, String.Empty, new VariableCollection[0], collectionEnum) + { + } + + public VariableCollection(string name, string val, IEnumerable subCollectionsEnum, IEnumerable collectionEnum) { + this.name = name; + this.val = val; + this.subCollectionsEnum = subCollectionsEnum; this.collectionEnum = collectionEnum; } - public Variable this[string variableName] { + + public virtual Variable this[string variableName] { get { int index = variableName.IndexOf('.'); if (index != -1) { @@ -47,7 +92,7 @@ namespace Debugger if (v.Name == variableName) return v; } } - throw new DebuggerException("Variable \"" + variableName + "\" is not in collection"); + return null; } } 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 bd1169e65c..a30a05ff58 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs @@ -490,7 +490,6 @@ namespace Debugger.Tests 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("{Debugger.Tests.TestPrograms.BaseClass}", baseClass.Value.AsString);