From ac858413e9d9c283c7d906538089faa3e17bd954 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Jul 2023 11:17:54 +0200 Subject: [PATCH 01/13] Unify type formatter state struct --- .../Formatters/AdditionalTypeInfoState.cs | 11 ++++ .../Formatters/CSharp/CSharpTypeFormatter.cs | 50 ++++++++----------- .../VisualBasic/VisualBasicTypeFormatter.cs | 40 +++++++-------- 3 files changed, 51 insertions(+), 50 deletions(-) create mode 100644 dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs new file mode 100644 index 0000000000..81df60da62 --- /dev/null +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs @@ -0,0 +1,11 @@ +namespace dnSpy.Roslyn.Debugger.Formatters { + struct AdditionalTypeInfoState { + internal readonly IAdditionalTypeInfoProvider? TypeInfoProvider; + + internal int DynamicTypeIndex; + internal int NativeIntTypeIndex; + internal int TupleNameIndex; + + public AdditionalTypeInfoState(IAdditionalTypeInfoProvider? typeInfoProvider) => TypeInfoProvider = typeInfoProvider; + } +} diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs index 67847d5fb3..0a26a3aca7 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs @@ -115,23 +115,17 @@ internal static string GetFormattedIdentifier(string? id) { void WriteIdentifier(string? id, DbgTextColor color) => OutputWrite(GetFormattedIdentifier(id), color); - struct TypeFormatterState { - public int DynamicTypeIndex; - public int NativeIntTypeIndex; - public int TupleNameIndex; - } - public void Format(DmdType type, DbgDotNetValue? value = null, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null, DmdParameterInfo? pd = null, bool forceReadOnly = false) { WriteRefIfByRef(type, pd, forceReadOnly); - TypeFormatterState state = default; + var state = new AdditionalTypeInfoState(additionalTypeInfoProvider); if (type.IsByRef) { type = type.GetElementType()!; state.DynamicTypeIndex++; } - FormatCore(type, value, additionalTypeInfoProvider, ref state); + FormatCore(type, value, ref state); } - void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + void FormatCore(DmdType type, DbgDotNetValue? value, ref AdditionalTypeInfoState state) { if (type is null) throw new ArgumentNullException(nameof(type)); @@ -154,7 +148,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider state.DynamicTypeIndex++; } while (type.IsArray); var t = arrayTypesList[arrayTypesList.Count - 1]; - FormatCore(t.type.GetElementType()!, null, additionalTypeInfoProvider, ref state); + FormatCore(t.type.GetElementType()!, null, ref state); foreach (var tuple in arrayTypesList) { var aryType = tuple.type; var aryValue = tuple.value; @@ -204,7 +198,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider case DmdTypeSignatureKind.Pointer: state.DynamicTypeIndex++; - FormatCore(type.GetElementType()!, null, additionalTypeInfoProvider, ref state); + FormatCore(type.GetElementType()!, null, ref state); OutputWrite("*", DbgTextColor.Operator); break; @@ -212,7 +206,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider state.DynamicTypeIndex++; OutputWrite(BYREF_KEYWORD, DbgTextColor.Keyword); WriteSpace(); - FormatCore(type.GetElementType()!, disposeThisValue = value?.LoadIndirect().Value, additionalTypeInfoProvider, ref state); + FormatCore(type.GetElementType()!, disposeThisValue = value?.LoadIndirect().Value, ref state); break; case DmdTypeSignatureKind.TypeGenericParameter: @@ -227,7 +221,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider case DmdTypeSignatureKind.GenericInstance: if (type.IsNullable) { state.DynamicTypeIndex++; - FormatCore(type.GetNullableElementType(), null, additionalTypeInfoProvider, ref state); + FormatCore(type.GetNullableElementType(), null, ref state); OutputWrite("?", DbgTextColor.Operator); break; } @@ -238,7 +232,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider OutputWrite(TUPLE_OPEN_PAREN, DbgTextColor.Punctuation); var tupleType = type; for (;;) { - tupleType = WriteTupleFields(tupleType, ref tupleIndex, additionalTypeInfoProvider, ref state); + tupleType = WriteTupleFields(tupleType, ref tupleIndex, ref state); if (tupleType is not null) { WriteCommaSpace(); state.DynamicTypeIndex++; @@ -256,16 +250,16 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider KeywordType keywordType; if (type.DeclaringType is null) { keywordType = GetKeywordType(type); - if (additionalTypeInfoProvider is not null) { - if (keywordType == KeywordType.Object && additionalTypeInfoProvider.IsDynamicType(state.DynamicTypeIndex)) { + if (state.TypeInfoProvider is not null) { + if (keywordType == KeywordType.Object && state.TypeInfoProvider.IsDynamicType(state.DynamicTypeIndex)) { OutputWrite("dynamic", DbgTextColor.Keyword); break; } - if (type == type.AppDomain.System_IntPtr && additionalTypeInfoProvider.IsNativeIntegerType(state.NativeIntTypeIndex++)) { + if (type == type.AppDomain.System_IntPtr && state.TypeInfoProvider.IsNativeIntegerType(state.NativeIntTypeIndex++)) { OutputWrite("nint", DbgTextColor.Keyword); break; } - if (type == type.AppDomain.System_UIntPtr && additionalTypeInfoProvider.IsNativeIntegerType(state.NativeIntTypeIndex++)) { + if (type == type.AppDomain.System_UIntPtr && state.TypeInfoProvider.IsNativeIntegerType(state.NativeIntTypeIndex++)) { OutputWrite("nuint", DbgTextColor.Keyword); break; } @@ -273,7 +267,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider if (keywordType == KeywordType.NoKeyword) WriteNamespace(type); WriteTypeName(type, keywordType); - WriteGenericArguments(type, genericArgs, ref genericArgsIndex, additionalTypeInfoProvider, ref state); + WriteGenericArguments(type, genericArgs, ref genericArgsIndex, ref state); } else { var typesList = new List(); @@ -287,7 +281,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider WriteNamespace(type); for (int i = typesList.Count - 1; i >= 0; i--) { WriteTypeName(typesList[i], i == 0 ? keywordType : KeywordType.NoKeyword); - WriteGenericArguments(typesList[i], genericArgs, ref genericArgsIndex, additionalTypeInfoProvider, ref state); + WriteGenericArguments(typesList[i], genericArgs, ref genericArgsIndex, ref state); if (i != 0) OutputWrite(".", DbgTextColor.Operator); } @@ -297,7 +291,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider case DmdTypeSignatureKind.FunctionPointer: var sig = type.GetFunctionPointerMethodSignature(); state.DynamicTypeIndex++; - FormatCore(sig.ReturnType, null, additionalTypeInfoProvider, ref state); + FormatCore(sig.ReturnType, null, ref state); WriteSpace(); OutputWrite(METHOD_OPEN_PAREN, DbgTextColor.Punctuation); var types = sig.GetParameterTypes(); @@ -305,7 +299,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider if (i > 0) WriteCommaSpace(); state.DynamicTypeIndex++; - FormatCore(types[i], null, additionalTypeInfoProvider, ref state); + FormatCore(types[i], null, ref state); } types = sig.GetVarArgsParameterTypes(); if (types.Count > 0) { @@ -314,7 +308,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider OutputWrite("...", DbgTextColor.Punctuation); for (int i = 0; i < types.Count; i++) { WriteCommaSpace(); - FormatCore(types[i], null, additionalTypeInfoProvider, ref state); + FormatCore(types[i], null, ref state); } } OutputWrite(METHOD_CLOSE_PAREN, DbgTextColor.Punctuation); @@ -336,7 +330,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider } } - void WriteGenericArguments(DmdType type, IList genericArgs, ref int genericArgsIndex, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + void WriteGenericArguments(DmdType type, IList genericArgs, ref int genericArgsIndex, ref AdditionalTypeInfoState state) { var gas = type.GetGenericArguments(); if (genericArgsIndex < genericArgs.Count && genericArgsIndex < gas.Count) { OutputWrite(GENERICS_OPEN_PAREN, DbgTextColor.Punctuation); @@ -345,13 +339,13 @@ void WriteGenericArguments(DmdType type, IList genericArgs, ref int gen if (j > startIndex) WriteCommaSpace(); state.DynamicTypeIndex++; - FormatCore(genericArgs[j], null, additionalTypeInfoProvider, ref state); + FormatCore(genericArgs[j], null, ref state); } OutputWrite(GENERICS_CLOSE_PAREN, DbgTextColor.Punctuation); } } - DmdType? WriteTupleFields(DmdType type, ref int index, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + DmdType? WriteTupleFields(DmdType type, ref int index, ref AdditionalTypeInfoState state) { var args = type.GetGenericArguments(); Debug.Assert(0 < args.Count && args.Count <= TypeFormatterUtils.MAX_TUPLE_ARITY); if (args.Count > TypeFormatterUtils.MAX_TUPLE_ARITY) { @@ -362,8 +356,8 @@ void WriteGenericArguments(DmdType type, IList genericArgs, ref int gen if (i > 0) WriteCommaSpace(); state.DynamicTypeIndex++; - FormatCore(args[i], null, additionalTypeInfoProvider, ref state); - string? fieldName = additionalTypeInfoProvider?.GetTupleElementName(index++); + FormatCore(args[i], null, ref state); + string? fieldName = state.TypeInfoProvider?.GetTupleElementName(index++); if (fieldName is not null) { WriteSpace(); OutputWrite(fieldName, DbgTextColor.InstanceField); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs index f6351c6480..b260ccc1b6 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs @@ -124,16 +124,12 @@ internal static string GetFormattedIdentifier(string? id) { void WriteIdentifier(string? id, DbgTextColor color) => OutputWrite(GetFormattedIdentifier(id), color); - struct TypeFormatterState { - public int TupleNameIndex; - } - public void Format(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null) { - TypeFormatterState state = default; - FormatCore(type, value, additionalTypeInfoProvider, ref state); + var state = new AdditionalTypeInfoState(additionalTypeInfoProvider); + FormatCore(type, value, ref state); } - void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + void FormatCore(DmdType type, DbgDotNetValue? value, ref AdditionalTypeInfoState state) { if (type is null) throw new ArgumentNullException(nameof(type)); @@ -153,7 +149,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider type = type.GetElementType()!; } while (type.IsArray); var t = arrayTypesList[arrayTypesList.Count - 1]; - FormatCore(t.type.GetElementType()!, null, additionalTypeInfoProvider, ref state); + FormatCore(t.type.GetElementType()!, null, ref state); foreach (var tuple in arrayTypesList) { var aryType = tuple.type; var aryValue = tuple.value; @@ -202,14 +198,14 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider break; case DmdTypeSignatureKind.Pointer: - FormatCore(type.GetElementType()!, null, additionalTypeInfoProvider, ref state); + FormatCore(type.GetElementType()!, null, ref state); OutputWrite("*", DbgTextColor.Operator); break; case DmdTypeSignatureKind.ByRef: OutputWrite(BYREF_KEYWORD, DbgTextColor.Keyword); WriteSpace(); - FormatCore(type.GetElementType()!, disposeThisValue = value?.LoadIndirect().Value, additionalTypeInfoProvider, ref state); + FormatCore(type.GetElementType()!, disposeThisValue = value?.LoadIndirect().Value, ref state); break; case DmdTypeSignatureKind.TypeGenericParameter: @@ -223,7 +219,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider case DmdTypeSignatureKind.Type: case DmdTypeSignatureKind.GenericInstance: if (type.IsNullable) { - FormatCore(type.GetNullableElementType(), null, additionalTypeInfoProvider, ref state); + FormatCore(type.GetNullableElementType(), null, ref state); OutputWrite("?", DbgTextColor.Operator); break; } @@ -234,7 +230,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider OutputWrite(TUPLE_OPEN_PAREN, DbgTextColor.Punctuation); var tupleType = type; for (;;) { - tupleType = WriteTupleFields(tupleType, ref tupleIndex, additionalTypeInfoProvider, ref state); + tupleType = WriteTupleFields(tupleType, ref tupleIndex, ref state); if (tupleType is not null) { WriteCommaSpace(); state.TupleNameIndex += TypeFormatterUtils.GetTupleArity(tupleType); @@ -254,7 +250,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider if (keywordType == KeywordType.NoKeyword) WriteNamespace(type); WriteTypeName(type, keywordType); - WriteGenericArguments(type, genericArgs, ref genericArgsIndex, additionalTypeInfoProvider, ref state); + WriteGenericArguments(type, genericArgs, ref genericArgsIndex, ref state); } else { var typesList = new List(); @@ -268,7 +264,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider WriteNamespace(type); for (int i = typesList.Count - 1; i >= 0; i--) { WriteTypeName(typesList[i], i == 0 ? keywordType : KeywordType.NoKeyword); - WriteGenericArguments(typesList[i], genericArgs, ref genericArgsIndex, additionalTypeInfoProvider, ref state); + WriteGenericArguments(typesList[i], genericArgs, ref genericArgsIndex, ref state); if (i != 0) OutputWrite(".", DbgTextColor.Operator); } @@ -277,14 +273,14 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider case DmdTypeSignatureKind.FunctionPointer: var sig = type.GetFunctionPointerMethodSignature(); - FormatCore(sig.ReturnType, null, additionalTypeInfoProvider, ref state); + FormatCore(sig.ReturnType, null, ref state); WriteSpace(); OutputWrite(METHOD_OPEN_PAREN, DbgTextColor.Punctuation); var types = sig.GetParameterTypes(); for (int i = 0; i < types.Count; i++) { if (i > 0) WriteCommaSpace(); - FormatCore(types[i], null, additionalTypeInfoProvider, ref state); + FormatCore(types[i], null, ref state); } types = sig.GetVarArgsParameterTypes(); if (types.Count > 0) { @@ -293,7 +289,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider OutputWrite("...", DbgTextColor.Punctuation); for (int i = 0; i < types.Count; i++) { WriteCommaSpace(); - FormatCore(types[i], null, additionalTypeInfoProvider, ref state); + FormatCore(types[i], null, ref state); } } OutputWrite(METHOD_CLOSE_PAREN, DbgTextColor.Punctuation); @@ -315,7 +311,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider } } - void WriteGenericArguments(DmdType type, IList genericArgs, ref int genericArgsIndex, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + void WriteGenericArguments(DmdType type, IList genericArgs, ref int genericArgsIndex, ref AdditionalTypeInfoState state) { var gas = type.GetGenericArguments(); if (genericArgsIndex < genericArgs.Count && genericArgsIndex < gas.Count) { OutputWrite(GENERICS_OPEN_PAREN, DbgTextColor.Punctuation); @@ -325,13 +321,13 @@ void WriteGenericArguments(DmdType type, IList genericArgs, ref int gen for (int j = startIndex; j < genericArgs.Count && j < gas.Count; j++, genericArgsIndex++) { if (j > startIndex) WriteCommaSpace(); - FormatCore(genericArgs[j], null, additionalTypeInfoProvider, ref state); + FormatCore(genericArgs[j], null, ref state); } OutputWrite(GENERICS_CLOSE_PAREN, DbgTextColor.Punctuation); } } - DmdType? WriteTupleFields(DmdType type, ref int index, IAdditionalTypeInfoProvider? additionalTypeInfoProvider, ref TypeFormatterState state) { + DmdType? WriteTupleFields(DmdType type, ref int index, ref AdditionalTypeInfoState state) { var args = type.GetGenericArguments(); Debug.Assert(0 < args.Count && args.Count <= TypeFormatterUtils.MAX_TUPLE_ARITY); if (args.Count > TypeFormatterUtils.MAX_TUPLE_ARITY) { @@ -341,14 +337,14 @@ void WriteGenericArguments(DmdType type, IList genericArgs, ref int gen for (int i = 0; i < args.Count && i < TypeFormatterUtils.MAX_TUPLE_ARITY - 1; i++) { if (i > 0) WriteCommaSpace(); - string? fieldName = additionalTypeInfoProvider?.GetTupleElementName(index++); + string? fieldName = state.TypeInfoProvider?.GetTupleElementName(index++); if (fieldName is not null) { OutputWrite(fieldName, DbgTextColor.InstanceField); WriteSpace(); OutputWrite("As", DbgTextColor.Keyword); WriteSpace(); } - FormatCore(args[i], null, additionalTypeInfoProvider, ref state); + FormatCore(args[i], null, ref state); } if (args.Count == TypeFormatterUtils.MAX_TUPLE_ARITY) return args[TypeFormatterUtils.MAX_TUPLE_ARITY - 1]; From b27e546db19055c787876faf78f27795ea3a82dc Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Jul 2023 11:18:20 +0200 Subject: [PATCH 02/13] Introduce unused `TupleField.CustomName` property --- .../ValueNodes/DbgDotNetValueNodeProviderFactory.cs | 2 +- .../Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleField.cs | 8 +++++++- .../Debugger/ValueNodes/TupleValueNodeProvider.cs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs index 84153a4a63..90f2588217 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs @@ -288,7 +288,7 @@ string GetTypeExpression(DmdType type) { if (info.tupleIndex < 0) return null; var defaultName = GetDefaultTupleName(info.tupleIndex); - tupleFields[info.tupleIndex] = new TupleField(defaultName, info.fields!.ToArray()); + tupleFields[info.tupleIndex] = new TupleField(defaultName, null, info.fields!.ToArray()); } return tupleFields; } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleField.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleField.cs index 1969c836bd..118aed8f1a 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleField.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleField.cs @@ -26,13 +26,19 @@ readonly struct TupleField { /// public readonly string DefaultName; + /// + /// User defined name, if any. + /// + public readonly string? CustomName; + /// /// All fields that must be accessed in order to get the value shown in the UI, eg. Rest.Rest.Item3 /// public readonly DmdFieldInfo[] Fields; - public TupleField(string defaultName, DmdFieldInfo[] fields) { + public TupleField(string defaultName, string? customName, DmdFieldInfo[] fields) { DefaultName = defaultName; + CustomName = customName; Fields = fields; } } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs index 28ce2949d2..353cac3745 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs @@ -89,7 +89,7 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN valueResult = default; } - var name = new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.InstanceField, info.DefaultName)); + var name = new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.InstanceField, info.CustomName ?? info.DefaultName)); DbgDotNetValueNode newNode; if (errorMessage is not null) newNode = valueNodeFactory.CreateError(evalInfo, name, errorMessage, expression, false); From 460e450e563ef425ee5ab2fa9586d4f7badb4a15 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Jul 2023 11:19:20 +0200 Subject: [PATCH 03/13] Implement IAdditionalTypeInfoProvider for Roslyn CustomTypeInfo --- ...ustomTypeInfoAdditionalTypeInfoProvider.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs new file mode 100644 index 0000000000..3a5c3127bb --- /dev/null +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.ObjectModel; +using dnSpy.Contracts.Debugger.DotNet.Evaluation; +using Microsoft.CodeAnalysis.ExpressionEvaluator; + +namespace dnSpy.Roslyn.Debugger.Formatters { + sealed class CustomTypeInfoAdditionalTypeInfoProvider : IAdditionalTypeInfoProvider { + readonly ReadOnlyCollection? dynamicFlags; + readonly ReadOnlyCollection? tupleElementNames; + + CustomTypeInfoAdditionalTypeInfoProvider(DbgDotNetCustomTypeInfo customTypeInfo) => CustomTypeInfo.Decode(customTypeInfo.CustomTypeInfoId, customTypeInfo.CustomTypeInfo, out dynamicFlags, out tupleElementNames); + + public static CustomTypeInfoAdditionalTypeInfoProvider? TryCreate(DbgDotNetCustomTypeInfo customTypeInfo) { + if (customTypeInfo.CustomTypeInfoId != CustomTypeInfo.PayloadTypeId) + return null; + return new CustomTypeInfoAdditionalTypeInfoProvider(customTypeInfo); + } + + public bool IsDynamicType(int typeIndex) => DynamicFlagsCustomTypeInfo.GetFlag(dynamicFlags, typeIndex); + + public string? GetTupleElementName(int typeIndex) => CustomTypeInfo.GetTupleElementNameIfAny(tupleElementNames, typeIndex); + + public bool IsNativeIntegerType(int typeIndex) => false; + } +} From 83239cda5d15589387b9db7788356ea3e8117e4d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 14 Jul 2023 19:03:41 +0200 Subject: [PATCH 04/13] Initial implementation of named tuple support in debugger value nodes --- .../Engine/DbgDotNetEngineValueNodeFactory.cs | 6 +- .../Engine/DbgDotNetValueCreator.cs | 2 +- .../DbgEngineStaticFieldsProviderImpl.cs | 2 +- .../Engine/DbgEngineValueNodeFactoryImpl.cs | 4 +- .../Formatters/AdditionalTypeInfoState.cs | 24 +++++++ ...ustomTypeInfoAdditionalTypeInfoProvider.cs | 18 +++++- .../dnSpy.Roslyn/Debugger/StateWithKey.cs | 4 +- .../DbgDotNetValueNodeProviderFactory.cs | 63 +++++++++++-------- .../ValueNodes/LanguageValueNodeFactory.cs | 35 ++++++++--- .../ValueNodes/TupleValueNodeProvider.cs | 24 ++++++- .../ValueNodes/DbgDotNetValueNodeFactory.cs | 3 +- 11 files changed, 135 insertions(+), 50 deletions(-) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs index ed247ed1f9..b8e28f71d5 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs @@ -30,7 +30,7 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Debugger.DotNet.Evaluation.Engine { abstract class DbgDotNetEngineValueNodeFactory { - public abstract DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType); + public abstract DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo? customTypeInfo); public abstract DbgEngineValueNode CreateException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options); public abstract DbgEngineValueNode CreateStowedException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options); public abstract DbgEngineValueNode CreateReturnValue(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, DmdMethodBase method); @@ -53,8 +53,8 @@ public DbgDotNetEngineValueNodeFactoryImpl(DbgDotNetFormatter formatter, DbgDotN internal DbgEngineValueNode Create(DbgDotNetValueNode node) => new DbgEngineValueNodeImpl(this, node); - public override DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType) => - new DbgEngineValueNodeImpl(this, factory.Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType)); + public override DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo customTypeInfo) => + new DbgEngineValueNodeImpl(this, factory.Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, customTypeInfo)); public override DbgEngineValueNode CreateException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options) => new DbgEngineValueNodeImpl(this, factory.CreateException(evalInfo, id, value, formatSpecifiers, options)); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetValueCreator.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetValueCreator.cs index bd348a25a7..7b567d65c7 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetValueCreator.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetValueCreator.cs @@ -51,7 +51,7 @@ public DbgEngineValueNode CreateValueNode(ref DbgDotNetILInterpreterState? ilInt if (res.ErrorMessage is not null) return valueNodeFactory.CreateError(evalInfo, compExprInfo.Name, res.ErrorMessage, compExprInfo.Expression, (compExprInfo.Flags & DbgEvaluationResultFlags.SideEffects) != 0); //TODO: Pass in compExprInfo.CustomTypeInfo, or attach it to the DbgDotNetValueNode - return valueNodeFactory.Create(evalInfo, compExprInfo.Name, res.Value!, compExprInfo.FormatSpecifiers, nodeOptions, compExprInfo.Expression, compExprInfo.ImageName, (compExprInfo.Flags & DbgEvaluationResultFlags.ReadOnly) != 0, (compExprInfo.Flags & DbgEvaluationResultFlags.SideEffects) != 0, expectedType); + return valueNodeFactory.Create(evalInfo, compExprInfo.Name, res.Value!, compExprInfo.FormatSpecifiers, nodeOptions, compExprInfo.Expression, compExprInfo.ImageName, (compExprInfo.Flags & DbgEvaluationResultFlags.ReadOnly) != 0, (compExprInfo.Flags & DbgEvaluationResultFlags.SideEffects) != 0, expectedType, compExprInfo.CustomTypeInfo); } catch { res.Value?.Dispose(); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs index 8a8239ed1d..6dbed142b9 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs @@ -89,7 +89,7 @@ DbgEngineValueNode[] GetNodesCore(DbgEvaluationInfo evalInfo, DbgValueNodeEvalua if (fieldVal.HasError) valueNodes[j++] = valueNodeFactory.CreateError(evalInfo, fieldExpression, fieldVal.ErrorMessage!, fieldExpression.ToString(), false); else - valueNodes[j++] = valueNodeFactory.Create(evalInfo, fieldExpression, fieldVal.Value!, null, options, fieldExpression.ToString(), GetFieldImageName(field), false, false, field.FieldType); + valueNodes[j++] = valueNodeFactory.Create(evalInfo, fieldExpression, fieldVal.Value!, null, options, fieldExpression.ToString(), GetFieldImageName(field), false, false, field.FieldType, null); // TODO: } ObjectCache.Free(ref output); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineValueNodeFactoryImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineValueNodeFactoryImpl.cs index a6c20ee7ea..ba28282e72 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineValueNodeFactoryImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineValueNodeFactoryImpl.cs @@ -64,7 +64,7 @@ DbgEngineValueNode[] CreateCore(DbgEvaluationInfo evalInfo, DbgExpressionEvaluat newNode = valueNodeFactory.CreateError(evalInfo, evalRes.Name, evalRes.Error, info.Expression, causesSideEffects); else { bool isReadOnly = (evalRes.Flags & DbgEvaluationResultFlags.ReadOnly) != 0; - newNode = valueNodeFactory.Create(evalInfo, evalRes.Name, evalRes.Value!, evalRes.FormatSpecifiers, info.NodeOptions, info.Expression, evalRes.ImageName, isReadOnly, causesSideEffects, evalRes.Type!); + newNode = valueNodeFactory.Create(evalInfo, evalRes.Name, evalRes.Value!, evalRes.FormatSpecifiers, info.NodeOptions, info.Expression, evalRes.ImageName, isReadOnly, causesSideEffects, evalRes.Type!, evalRes.CustomTypeInfo); } res[i] = newNode; } @@ -109,7 +109,7 @@ DbgEngineValueNode[] CreateCore(DbgEvaluationInfo evalInfo, DbgEngineObjectId[] if (objectIdValue is null) res[i] = valueNodeFactory.CreateError(evalInfo, name, "Could not get Object ID value", expression, false); else - res[i] = valueNodeFactory.Create(evalInfo, name, objectIdValue, null, options, expression, PredefinedDbgValueNodeImageNames.ObjectId, true, false, objectIdValue.Type); + res[i] = valueNodeFactory.Create(evalInfo, name, objectIdValue, null, options, expression, PredefinedDbgValueNodeImageNames.ObjectId, true, false, objectIdValue.Type, null); } ObjectCache.Free(ref output); return res; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs index 81df60da62..2229ee472f 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs @@ -7,5 +7,29 @@ struct AdditionalTypeInfoState { internal int TupleNameIndex; public AdditionalTypeInfoState(IAdditionalTypeInfoProvider? typeInfoProvider) => TypeInfoProvider = typeInfoProvider; + + public override bool Equals(object? obj) { + if (obj is not AdditionalTypeInfoState other) + return false; + if (!Equals(TypeInfoProvider, other.TypeInfoProvider)) + return false; + if (DynamicTypeIndex != other.DynamicTypeIndex) + return false; + if (NativeIntTypeIndex != other.NativeIntTypeIndex) + return false; + if (TupleNameIndex != other.TupleNameIndex) + return false; + return true; + } + + public override int GetHashCode() { + unchecked { + int hashCode = TypeInfoProvider is not null ? TypeInfoProvider.GetHashCode() : 0; + hashCode = hashCode * 397 ^ DynamicTypeIndex; + hashCode = hashCode * 397 ^ NativeIntTypeIndex; + hashCode = hashCode * 397 ^ TupleNameIndex; + return hashCode; + } + } } } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs index 3a5c3127bb..251f2eb478 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs @@ -4,13 +4,17 @@ namespace dnSpy.Roslyn.Debugger.Formatters { sealed class CustomTypeInfoAdditionalTypeInfoProvider : IAdditionalTypeInfoProvider { + readonly DbgDotNetCustomTypeInfo customTypeInfo; readonly ReadOnlyCollection? dynamicFlags; readonly ReadOnlyCollection? tupleElementNames; - CustomTypeInfoAdditionalTypeInfoProvider(DbgDotNetCustomTypeInfo customTypeInfo) => CustomTypeInfo.Decode(customTypeInfo.CustomTypeInfoId, customTypeInfo.CustomTypeInfo, out dynamicFlags, out tupleElementNames); + CustomTypeInfoAdditionalTypeInfoProvider(DbgDotNetCustomTypeInfo customTypeInfo) { + this.customTypeInfo = customTypeInfo; + CustomTypeInfo.Decode(customTypeInfo.CustomTypeInfoId, customTypeInfo.CustomTypeInfo, out dynamicFlags, out tupleElementNames); + } - public static CustomTypeInfoAdditionalTypeInfoProvider? TryCreate(DbgDotNetCustomTypeInfo customTypeInfo) { - if (customTypeInfo.CustomTypeInfoId != CustomTypeInfo.PayloadTypeId) + public static CustomTypeInfoAdditionalTypeInfoProvider? TryCreate(DbgDotNetCustomTypeInfo? customTypeInfo) { + if (customTypeInfo?.CustomTypeInfoId != CustomTypeInfo.PayloadTypeId) return null; return new CustomTypeInfoAdditionalTypeInfoProvider(customTypeInfo); } @@ -20,5 +24,13 @@ sealed class CustomTypeInfoAdditionalTypeInfoProvider : IAdditionalTypeInfoProvi public string? GetTupleElementName(int typeIndex) => CustomTypeInfo.GetTupleElementNameIfAny(tupleElementNames, typeIndex); public bool IsNativeIntegerType(int typeIndex) => false; + + public override bool Equals(object? obj) { + if (ReferenceEquals(this, obj)) + return true; + return obj is CustomTypeInfoAdditionalTypeInfoProvider other && customTypeInfo.Equals(other.customTypeInfo); + } + + public override int GetHashCode() => customTypeInfo.GetHashCode(); } } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/StateWithKey.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/StateWithKey.cs index 40722632a9..debb6aa37b 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/StateWithKey.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/StateWithKey.cs @@ -35,7 +35,7 @@ sealed class StateWithKey where T : class { var list = state.list; for (int i = 0; i < list.Count; i++) { var info = list[i]; - if (info.key == key) + if (Equals(info.key, key)) return info.data; } return null; @@ -50,7 +50,7 @@ public static T GetOrCreate(DmdObject obj, object key, Func create) { var list = state.list; for (int i = 0; i < list.Count; i++) { var info = list[i]; - if (info.key == key) + if (Equals(info.key, key)) return info.data; } var data = create(); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs index 90f2588217..bb449fde49 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs @@ -28,6 +28,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; using dnSpy.Roslyn.Properties; namespace dnSpy.Roslyn.Debugger.ValueNodes { @@ -42,6 +43,7 @@ enum TypeStateFlags { sealed class TypeState { public readonly DmdType Type; + public readonly AdditionalTypeInfoState AdditionalTypeInfo; public readonly DmdType? EnumerableType; public readonly TypeStateFlags Flags; public readonly string TypeExpression; @@ -64,8 +66,9 @@ sealed class TypeState { public MemberValueNodeInfoCollection CachedRawViewInstanceMembers; public MemberValueNodeInfoCollection CachedRawViewStaticMembers; - public TypeState(DmdType type, string typeExpression) { + public TypeState(DmdType type, string typeExpression, AdditionalTypeInfoState typeInfo) { Type = type; + AdditionalTypeInfo = typeInfo; Flags = TypeStateFlags.None; TypeExpression = typeExpression; HasNoChildren = true; @@ -74,8 +77,9 @@ public TypeState(DmdType type, string typeExpression) { TupleFields = Array.Empty(); } - public TypeState(DmdType type, string typeExpression, MemberValueNodeInfoCollection instanceMembers, MemberValueNodeInfoCollection staticMembers, TupleField[] tupleFields) { + public TypeState(DmdType type, string typeExpression, AdditionalTypeInfoState typeInfo, MemberValueNodeInfoCollection instanceMembers, MemberValueNodeInfoCollection staticMembers, TupleField[] tupleFields) { Type = type; + AdditionalTypeInfo = typeInfo; EnumerableType = GetEnumerableType(type); Flags = GetFlags(type, tupleFields); TypeExpression = typeExpression; @@ -200,14 +204,17 @@ enum CreationOptions { NoExtraRawView = 4, } - public DbgDotNetValueNodeProviderResult Create(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { + public DbgDotNetValueNodeProviderResult Create(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) => + Create(evalInfo, addParens, slotType, default, nodeInfo, options); + + public DbgDotNetValueNodeProviderResult Create(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, AdditionalTypeInfoState typeInfoState, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var providers = new List(2); - Create(evalInfo, providers, addParens, slotType, nodeInfo, options, CreationOptions.None); + Create(evalInfo, providers, addParens, slotType, typeInfoState, nodeInfo, options, CreationOptions.None); return new DbgDotNetValueNodeProviderResult(DbgDotNetValueNodeProvider.Create(providers)); } public DbgDotNetValueNodeProviderResult CreateDynamicView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { - var state = GetTypeState(nodeInfo); + var state = GetTypeState(nodeInfo, slotType, default); var provider = TryCreateDynamicView(state, nodeInfo.Expression, addParens, nodeInfo.Value, slotType, options); if (provider is not null) return new DbgDotNetValueNodeProviderResult(provider); @@ -215,32 +222,35 @@ public DbgDotNetValueNodeProviderResult CreateDynamicView(DbgEvaluationInfo eval } public DbgDotNetValueNodeProviderResult CreateResultsView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { - var state = GetTypeState(nodeInfo); + var state = GetTypeState(nodeInfo, slotType, default); var provider = TryCreateResultsView(state, nodeInfo.Expression, addParens, nodeInfo.Value, slotType, options); if (provider is not null) return new DbgDotNetValueNodeProviderResult(provider); return new DbgDotNetValueNodeProviderResult(dnSpy_Roslyn_Resources.ResultsView_MustBeEnumerableType); } - TypeState GetTypeState(DbgDotNetValueNodeInfo nodeInfo) { + void Create(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options, CreationOptions creationOptions) => + CreateCore(evalInfo, providers, addParens, slotType, nodeInfo, GetTypeState(nodeInfo, slotType, default), options, creationOptions); + + void Create(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, AdditionalTypeInfoState typeInfoState, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options, CreationOptions creationOptions) => + CreateCore(evalInfo, providers, addParens, slotType, nodeInfo, GetTypeState(nodeInfo, slotType, typeInfoState), options, creationOptions); + + TypeState GetTypeState(DbgDotNetValueNodeInfo nodeInfo, DmdType slotType, AdditionalTypeInfoState typeInfoState) { var type = nodeInfo.Value.Type; if (type.IsByRef) type = type.GetElementType()!; - return GetOrCreateTypeState(type); + return GetOrCreateTypeState(type, nodeInfo.Value.Type == slotType ? typeInfoState : default); } - void Create(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options, CreationOptions creationOptions) => - CreateCore(evalInfo, providers, addParens, slotType, nodeInfo, GetTypeState(nodeInfo), options, creationOptions); - - TypeState GetOrCreateTypeState(DmdType type) { - var state = StateWithKey.TryGet(type, this); + TypeState GetOrCreateTypeState(DmdType type, AdditionalTypeInfoState typeInfoState) { + var state = StateWithKey.TryGet(type, (this, typeInfoState)); if (state is not null) return state; - return CreateTypeState(type); + return CreateTypeState(type, typeInfoState); - TypeState CreateTypeState(DmdType type2) { - var state2 = CreateTypeStateCore(type2); - return StateWithKey.GetOrCreate(type2, this, () => state2); + TypeState CreateTypeState(DmdType type2, AdditionalTypeInfoState typeInfoState2) { + var state2 = CreateTypeStateCore(type2, typeInfoState2); + return StateWithKey.GetOrCreate(type2, (this, typeInfoState2), () => state2); } } @@ -278,7 +288,7 @@ string GetTypeExpression(DmdType type) { return output.ToString(); } - TupleField[]? TryCreateTupleFields(DmdType type) { + TupleField[]? TryCreateTupleFields(DmdType type, AdditionalTypeInfoState typeInfoState) { var tupleArity = Formatters.TypeFormatterUtils.GetTupleArity(type); if (tupleArity <= 0) return null; @@ -288,24 +298,25 @@ string GetTypeExpression(DmdType type) { if (info.tupleIndex < 0) return null; var defaultName = GetDefaultTupleName(info.tupleIndex); - tupleFields[info.tupleIndex] = new TupleField(defaultName, null, info.fields!.ToArray()); + var customName = typeInfoState.TypeInfoProvider?.GetTupleElementName(typeInfoState.TupleNameIndex + info.tupleIndex); + tupleFields[info.tupleIndex] = new TupleField(defaultName, customName, info.fields!.ToArray()); } return tupleFields; } static string GetDefaultTupleName(int tupleIndex) => "Item" + (tupleIndex + 1).ToString(); - TypeState CreateTypeStateCore(DmdType type) { + TypeState CreateTypeStateCore(DmdType type, AdditionalTypeInfoState typeInfoState) { var typeExpression = GetTypeExpression(type); if (HasNoChildren(type) || type.IsFunctionPointer) - return new TypeState(type, typeExpression); + return new TypeState(type, typeExpression, typeInfoState); MemberValueNodeInfoCollection instanceMembers, staticMembers; TupleField[] tupleFields; Debug.Assert(!type.IsByRef); if (type.TypeSignatureKind == DmdTypeSignatureKind.Type || type.TypeSignatureKind == DmdTypeSignatureKind.GenericInstance) { - tupleFields = TryCreateTupleFields(type) ?? Array.Empty(); + tupleFields = TryCreateTupleFields(type, typeInfoState) ?? Array.Empty(); var instanceMembersList = new List(); var staticMembersList = new List(); @@ -387,7 +398,7 @@ TypeState CreateTypeStateCore(DmdType type) { tupleFields = Array.Empty(); } - return new TypeState(type, typeExpression, instanceMembers, staticMembers, tupleFields); + return new TypeState(type, typeExpression, typeInfoState, instanceMembers, staticMembers, tupleFields); } MemberValueNodeInfo[] InitializeOverloadedMembers(MemberValueNodeInfo[] memberInfos) { @@ -487,7 +498,7 @@ bool TryCreateNullable(DbgEvaluationInfo evalInfo, List pro bool funcEval = (evalOptions & DbgValueNodeEvaluationOptions.NoFuncEval) == 0; if (state.IsTupleType && !forceRawView) { - providers.Add(new TupleValueNodeProvider(addParens, slotType, nodeInfo, state.TupleFields)); + providers.Add(new TupleValueNodeProvider(addParens, slotType, state.AdditionalTypeInfo, nodeInfo, state.TupleFields)); AddProvidersOneChildNode(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, isRawView: true); return; } @@ -571,7 +582,7 @@ void AddProvidersOneChildNode(List providers, TypeSt } internal void GetMemberCollections(DmdType type, DbgValueNodeEvaluationOptions evalOptions, out MemberValueNodeInfoCollection instanceMembersInfos, out MemberValueNodeInfoCollection staticMembersInfos) { - var state = GetOrCreateTypeState(type); + var state = GetOrCreateTypeState(type, default); GetMemberCollections(state, evalOptions, out instanceMembersInfos, out staticMembersInfos); } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs index 1260497776..f7fee68cf0 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs @@ -28,6 +28,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; namespace dnSpy.Roslyn.Debugger.ValueNodes { abstract class LanguageValueNodeFactory : DbgDotNetValueNodeFactory { @@ -60,7 +61,7 @@ internal DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText nam internal DbgDotNetValueNode Create(DbgDotNetValueNodeProvider provider, DbgDotNetText name, DbgDotNetValueNodeInfo nodeInfo, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DmdType actualType, string? errorMessage, DbgDotNetText valueText, ReadOnlyCollection? formatSpecifiers) => new DbgDotNetValueNodeImpl(this, provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, actualType, errorMessage, valueText, formatSpecifiers, null); - DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, bool isRootExpression, ColumnFormatter? columnFormatter) { + DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, AdditionalTypeInfoState typeInfoState, bool isRootExpression, ColumnFormatter? columnFormatter) { // Could be a by-ref property, local, or parameter. if (expectedType.IsByRef) { if (value.Type.IsByRef) { @@ -71,6 +72,7 @@ DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, D value = newValue.Value!; } expectedType = expectedType.GetElementType()!; + typeInfoState.DynamicTypeIndex++; } options = PredefinedFormatSpecifiers.GetValueNodeEvaluationOptions(formatSpecifiers, options); @@ -88,7 +90,7 @@ DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, D useProvider = true; } else - info = valueNodeProviderFactory.Create(evalInfo, addParens, expectedType, nodeInfo, options); + info = valueNodeProviderFactory.Create(evalInfo, addParens, expectedType, typeInfoState, nodeInfo, options); if (useProvider) { if (info.ErrorMessage is not null) return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, PredefinedDbgValueNodeImageNames.Error, true, false, null, null, info.ErrorMessage, new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.Error, info.ErrorMessage)), formatSpecifiers, columnFormatter); @@ -98,11 +100,17 @@ DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, D return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, value.Type, info.ErrorMessage, default, formatSpecifiers, columnFormatter); } - public sealed override DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType) => - Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, true); + public sealed override DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo? customTypeInfo) => + Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, CustomTypeInfoAdditionalTypeInfoProvider.TryCreate(customTypeInfo), true); + + internal DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, IAdditionalTypeInfoProvider? typeInfoProvider, bool isRootExpression) => + CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, new AdditionalTypeInfoState(typeInfoProvider), isRootExpression, null); + + internal DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, AdditionalTypeInfoState typeInfoState, bool isRootExpression) => + CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, typeInfoState, isRootExpression, null); internal DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, bool isRootExpression) => - CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, isRootExpression, null); + CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, default, isRootExpression, null); public sealed override DbgDotNetValueNode CreateException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options) { var output = ObjectCache.AllocDotNetTextOutput(); @@ -112,7 +120,7 @@ public sealed override DbgDotNetValueNode CreateException(DbgEvaluationInfo eval const bool isReadOnly = true; const bool causesSideEffects = false; const string imageName = PredefinedDbgValueNodeImageNames.Exception; - return CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, value.Type, false, null); + return Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, value.Type, false); } public sealed override DbgDotNetValueNode CreateStowedException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options) { @@ -123,7 +131,7 @@ public sealed override DbgDotNetValueNode CreateStowedException(DbgEvaluationInf const bool isReadOnly = true; const bool causesSideEffects = false; const string imageName = PredefinedDbgValueNodeImageNames.StowedException; - return CreateValue(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, value.Type, false, null); + return Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, value.Type, false); } public sealed override DbgDotNetValueNode CreateReturnValue(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, DmdMethodBase method) { @@ -135,8 +143,17 @@ public sealed override DbgDotNetValueNode CreateReturnValue(DbgEvaluationInfo ev const bool causesSideEffects = false; var property = PropertyState.TryGetProperty(method); var imageName = property is not null ? ImageNameUtils.GetImageName(property) : ImageNameUtils.GetImageName(method, SupportsModuleTypes); - var expectedType = method is DmdMethodInfo mi ? mi.ReturnType : value.Type; - return CreateValue(evalInfo, default, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, false, columnFormatter); + DmdType expectedType; + IAdditionalTypeInfoProvider? typeInfoProvider; + if (method is DmdMethodInfo mi) { + expectedType = mi.ReturnType; + typeInfoProvider = CustomAttributeAdditionalTypeInfoProvider.Create(mi.ReturnParameter); + } + else { + expectedType = value.Type; + typeInfoProvider = null; + } + return CreateValue(evalInfo, default, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, new AdditionalTypeInfoState(typeInfoProvider), false, columnFormatter); } internal void FormatReturnValueMethodName(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgValueFormatterOptions options, CultureInfo? cultureInfo, DmdMethodBase method) => diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs index 353cac3745..9ea61ac27e 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs @@ -28,6 +28,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; namespace dnSpy.Roslyn.Debugger.ValueNodes { sealed class TupleValueNodeProvider : DbgDotNetValueNodeProvider { @@ -39,12 +40,14 @@ sealed class TupleValueNodeProvider : DbgDotNetValueNodeProvider { readonly bool addParens; readonly DmdType slotType; + readonly AdditionalTypeInfoState typeInfo; readonly DbgDotNetValueNodeInfo nodeInfo; readonly TupleField[] tupleFields; - public TupleValueNodeProvider(bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, TupleField[] tupleFields) { + public TupleValueNodeProvider(bool addParens, DmdType slotType, AdditionalTypeInfoState typeInfo, DbgDotNetValueNodeInfo nodeInfo, TupleField[] tupleFields) { this.addParens = addParens; this.slotType = slotType; + this.typeInfo = typeInfo; this.nodeInfo = nodeInfo; this.tupleFields = tupleFields; } @@ -57,6 +60,18 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN var valueResults = new List(); DbgDotNetValueResult valueResult = default; try { + var additionalTypeInfo = typeInfo; + additionalTypeInfo.TupleNameIndex += tupleFields.Length; + + var l = Math.Min((int)index, tupleFields.Length); + for (var i = 0; i < l; i++) { + ref readonly var info = ref tupleFields[i]; + for (int j = 0; j < info.Fields.Length; j++) { + if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[j].FieldType, out int cardinality)) + additionalTypeInfo.TupleNameIndex += cardinality; + } + } + for (int i = 0; i < res.Length; i++) { evalInfo.CancellationToken.ThrowIfCancellationRequested(); ref readonly var info = ref tupleFields[(int)index + i]; @@ -96,12 +111,17 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN else if (valueIsException) newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.Error, true, false, expectedType, false); else - newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, false); + newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, additionalTypeInfo, false); foreach (var vr in valueResults) vr.Value?.Dispose(); valueResults.Clear(); res[i] = newNode; + + for (int j = 0; j < info.Fields.Length; j++) { + if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[j].FieldType, out int cardinality)) + additionalTypeInfo.TupleNameIndex += cardinality; + } } } catch { diff --git a/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ValueNodes/DbgDotNetValueNodeFactory.cs b/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ValueNodes/DbgDotNetValueNodeFactory.cs index 026bb56e1f..c9c4d1f905 100644 --- a/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ValueNodes/DbgDotNetValueNodeFactory.cs +++ b/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ValueNodes/DbgDotNetValueNodeFactory.cs @@ -43,8 +43,9 @@ public abstract class DbgDotNetValueNodeFactory { /// true if it's a read-only value /// true if the expression causes side effects /// Expected type + /// Custom type info /// - public abstract DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType); + public abstract DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo? customTypeInfo); /// /// Creates an exception value node From 1521cc620da098dc9e0dc1e5ce5d665729eecdf0 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 19 Jul 2023 15:13:36 +0200 Subject: [PATCH 05/13] Display tuple element names in array elements and pointer dereference --- .../Debugger/ValueNodes/ArrayValueNodeProvider.cs | 8 ++++++-- .../ValueNodes/DbgDotNetValueNodeProviderFactory.cs | 4 ++-- .../Debugger/ValueNodes/PointerValueNodeProvider.cs | 8 ++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ArrayValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ArrayValueNodeProvider.cs index 37452c59cb..85668730c1 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ArrayValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ArrayValueNodeProvider.cs @@ -27,6 +27,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; namespace dnSpy.Roslyn.Debugger.ValueNodes { sealed class ArrayValueNodeProvider : DbgDotNetValueNodeProvider { @@ -39,16 +40,19 @@ sealed class ArrayValueNodeProvider : DbgDotNetValueNodeProvider { readonly DbgDotNetValueNodeProviderFactory owner; readonly bool addParens; readonly DmdType slotType; + readonly AdditionalTypeInfoState typeInfo; readonly DbgDotNetValueNodeInfo valueInfo; readonly uint arrayCount; readonly DbgDotNetArrayDimensionInfo[] dimensionInfos; // This one's only non-null if this is an array with 2 or more dimensions readonly int[]? indexes; - public ArrayValueNodeProvider(DbgDotNetValueNodeProviderFactory owner, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo valueInfo) { + public ArrayValueNodeProvider(DbgDotNetValueNodeProviderFactory owner, bool addParens, DmdType slotType, AdditionalTypeInfoState typeInfo, DbgDotNetValueNodeInfo valueInfo) { this.owner = owner; this.addParens = addParens; this.slotType = slotType; + typeInfo.DynamicTypeIndex++; + this.typeInfo = typeInfo; this.valueInfo = valueInfo; bool b = valueInfo.Value.GetArrayInfo(out arrayCount, out dimensionInfos!) && dimensionInfos.Length != 0; @@ -107,7 +111,7 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN } } if (newNode is null) - newNode = valueNodeFactory.Create(evalInfo, name, newValue.Value, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.ArrayElement, false, false, elementType, false); + newNode = valueNodeFactory.Create(evalInfo, name, newValue.Value, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.ArrayElement, false, false, elementType, typeInfo, false); } newValue = default; res[i] = newNode; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs index bb449fde49..a841583777 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs @@ -519,7 +519,7 @@ void CreateCore(DbgEvaluationInfo evalInfo, List pro } if (state.Type.IsArray && !nodeInfo.Value.IsNull) { - providers.Add(new ArrayValueNodeProvider(this, addParens, slotType, nodeInfo)); + providers.Add(new ArrayValueNodeProvider(this, addParens, slotType, state.AdditionalTypeInfo, nodeInfo)); return; } @@ -618,7 +618,7 @@ void AddProviders(List providers, TypeState state, s if (value.IsNull) instanceMembersInfos = MemberValueNodeInfoCollection.Empty; if (PointerValueNodeProvider.IsSupported(value)) - providers.Add(new PointerValueNodeProvider(this, expression, value)); + providers.Add(new PointerValueNodeProvider(this, expression, value, state.AdditionalTypeInfo)); else { providers.Add(new InstanceMembersValueNodeProvider(valueNodeFactory, isRawView ? rawViewName : InstanceMembersName, expression, addParens, slotType, value, instanceMembersInfos, membersEvalOptions, diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/PointerValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/PointerValueNodeProvider.cs index eaef802730..6c68cc2b11 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/PointerValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/PointerValueNodeProvider.cs @@ -25,6 +25,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.DotNet.Evaluation.ValueNodes; using dnSpy.Contracts.Debugger.DotNet.Text; using dnSpy.Contracts.Debugger.Evaluation; +using dnSpy.Roslyn.Debugger.Formatters; namespace dnSpy.Roslyn.Debugger.ValueNodes { sealed class PointerValueNodeProvider : DbgDotNetValueNodeProvider { @@ -35,13 +36,16 @@ sealed class PointerValueNodeProvider : DbgDotNetValueNodeProvider { readonly DbgDotNetValueNodeProviderFactory valueNodeProviderFactory; readonly DbgDotNetValue value; + readonly AdditionalTypeInfoState typeInfo; DbgDotNetValue? derefValue; bool initialized; - public PointerValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, string expression, DbgDotNetValue value) { + public PointerValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, string expression, DbgDotNetValue value, AdditionalTypeInfoState typeInfo) { Debug.Assert(IsSupported(value)); this.valueNodeProviderFactory = valueNodeProviderFactory; this.value = value; + typeInfo.DynamicTypeIndex++; + this.typeInfo = typeInfo; Expression = expression; } @@ -64,7 +68,7 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN var derefExpr = valueNodeProviderFactory.GetDereferenceExpression(Expression); ref readonly var derefName = ref valueNodeProviderFactory.GetDereferencedName(); var nodeInfo = new DbgDotNetValueNodeInfo(derefValue, derefExpr); - var res = valueNodeProviderFactory.Create(evalInfo, true, derefValue.Type, nodeInfo, options); + var res = valueNodeProviderFactory.Create(evalInfo, true, derefValue.Type, typeInfo, nodeInfo, options); DbgDotNetValueNode valueNode; if (res.ErrorMessage is not null) valueNode = valueNodeFactory.CreateError(evalInfo, DbgDotNetText.Empty, res.ErrorMessage, derefExpr, false); From 29a65a04fc564f15f4c2baadf8104af10d0eeb2d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 1 Aug 2023 11:01:46 +0200 Subject: [PATCH 06/13] Add missing license headers --- .../Formatters/AdditionalTypeInfoState.cs | 19 ++++++++++++++++ ...stomAttributeAdditionalTypeInfoProvider.cs | 22 ++++++++++++++++++- ...ustomTypeInfoAdditionalTypeInfoProvider.cs | 20 +++++++++++++++++ .../Formatters/IAdditionalTypeInfoProvider.cs | 21 +++++++++++++++++- 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs index 2229ee472f..c2df296cd7 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/AdditionalTypeInfoState.cs @@ -1,3 +1,22 @@ +/* + Copyright (C) 2023 ElektroKill + + This file is part of dnSpy + + dnSpy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + dnSpy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with dnSpy. If not, see . +*/ + namespace dnSpy.Roslyn.Debugger.Formatters { struct AdditionalTypeInfoState { internal readonly IAdditionalTypeInfoProvider? TypeInfoProvider; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs index aa99170146..3ccbf1caea 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs @@ -1,4 +1,24 @@ -using System.Collections.Generic; +/* + Copyright (C) 2023 ElektroKill + + This file is part of dnSpy + + dnSpy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + dnSpy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with dnSpy. If not, see . +*/ + + +using System.Collections.Generic; using System.Collections.ObjectModel; using dnSpy.Debugger.DotNet.Metadata; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs index 251f2eb478..ef9d1d6de5 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomTypeInfoAdditionalTypeInfoProvider.cs @@ -1,3 +1,23 @@ +/* + Copyright (C) 2023 ElektroKill + + This file is part of dnSpy + + dnSpy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + dnSpy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with dnSpy. If not, see . +*/ + + using System.Collections.ObjectModel; using dnSpy.Contracts.Debugger.DotNet.Evaluation; using Microsoft.CodeAnalysis.ExpressionEvaluator; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/IAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/IAdditionalTypeInfoProvider.cs index 68a6d1b7b2..0e6d9c262a 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/IAdditionalTypeInfoProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/IAdditionalTypeInfoProvider.cs @@ -1,4 +1,23 @@ -namespace dnSpy.Roslyn.Debugger.Formatters { +/* + Copyright (C) 2023 ElektroKill + + This file is part of dnSpy + + dnSpy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + dnSpy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with dnSpy. If not, see . +*/ + +namespace dnSpy.Roslyn.Debugger.Formatters { interface IAdditionalTypeInfoProvider { bool IsDynamicType(int typeIndex); string? GetTupleElementName(int typeIndex); From 6e9a6821906d83d3320ce4cbab6fab6bbe5c0ac7 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 1 Aug 2023 13:47:15 +0200 Subject: [PATCH 07/13] Cache additional type info in `TupleValueNodeProvider` --- .../ValueNodes/TupleValueNodeProvider.cs | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs index 9ea61ac27e..464c2f80c2 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs @@ -40,41 +40,54 @@ sealed class TupleValueNodeProvider : DbgDotNetValueNodeProvider { readonly bool addParens; readonly DmdType slotType; - readonly AdditionalTypeInfoState typeInfo; readonly DbgDotNetValueNodeInfo nodeInfo; readonly TupleField[] tupleFields; + readonly AdditionalTypeInfoState[] cachedTypeInfoStates; + int cachedIndex; public TupleValueNodeProvider(bool addParens, DmdType slotType, AdditionalTypeInfoState typeInfo, DbgDotNetValueNodeInfo nodeInfo, TupleField[] tupleFields) { this.addParens = addParens; this.slotType = slotType; - this.typeInfo = typeInfo; this.nodeInfo = nodeInfo; this.tupleFields = tupleFields; + cachedTypeInfoStates = new AdditionalTypeInfoState[tupleFields.Length]; + typeInfo.TupleNameIndex += tupleFields.Length; + cachedTypeInfoStates[0] = typeInfo; + cachedIndex = 0; } public override ulong GetChildCount(DbgEvaluationInfo evalInfo) => (uint)tupleFields.Length; + AdditionalTypeInfoState GetCachedTypeInfoState(int tupleFieldIndex) { + if (cachedIndex >= tupleFieldIndex) + return cachedTypeInfoStates[tupleFieldIndex]; + + var typeInfo = cachedTypeInfoStates[cachedIndex]; + while (cachedIndex < tupleFieldIndex) { + ref readonly var info = ref tupleFields[cachedIndex]; + for (int k = 0; k < info.Fields.Length; k++) { + typeInfo.DynamicTypeIndex++; + if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[k].FieldType, out int cardinality)) { + typeInfo.TupleNameIndex += cardinality; + typeInfo.DynamicTypeIndex++; + } + } + cachedTypeInfoStates[++cachedIndex] = typeInfo; + } + + return typeInfo; + } + public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueNodeFactory, DbgEvaluationInfo evalInfo, ulong index, int count, DbgValueNodeEvaluationOptions options, ReadOnlyCollection? formatSpecifiers) { var runtime = evalInfo.Runtime.GetDotNetRuntime(); var res = count == 0 ? Array.Empty() : new DbgDotNetValueNode[count]; var valueResults = new List(); DbgDotNetValueResult valueResult = default; try { - var additionalTypeInfo = typeInfo; - additionalTypeInfo.TupleNameIndex += tupleFields.Length; - - var l = Math.Min((int)index, tupleFields.Length); - for (var i = 0; i < l; i++) { - ref readonly var info = ref tupleFields[i]; - for (int j = 0; j < info.Fields.Length; j++) { - if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[j].FieldType, out int cardinality)) - additionalTypeInfo.TupleNameIndex += cardinality; - } - } - for (int i = 0; i < res.Length; i++) { evalInfo.CancellationToken.ThrowIfCancellationRequested(); - ref readonly var info = ref tupleFields[(int)index + i]; + int tupleFieldIndex = (int)index + i; + ref readonly var info = ref tupleFields[tupleFieldIndex]; var castType = NeedCast(slotType, nodeInfo.Value.Type) ? nodeInfo.Value.Type : null; var expression = valueNodeFactory.GetFieldExpression(nodeInfo.Expression, info.DefaultName, castType, addParens); const string imageName = PredefinedDbgValueNodeImageNames.FieldPublic; @@ -111,17 +124,12 @@ public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueN else if (valueIsException) newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.Error, true, false, expectedType, false); else - newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, additionalTypeInfo, false); + newNode = valueNodeFactory.Create(evalInfo, name, objValue, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, GetCachedTypeInfoState(tupleFieldIndex), false); foreach (var vr in valueResults) vr.Value?.Dispose(); valueResults.Clear(); res[i] = newNode; - - for (int j = 0; j < info.Fields.Length; j++) { - if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[j].FieldType, out int cardinality)) - additionalTypeInfo.TupleNameIndex += cardinality; - } } } catch { From 2fd18f158c2eb9028245536e2084ca8c04de5b7c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 3 Aug 2023 21:03:06 +0200 Subject: [PATCH 08/13] Read custom type info from instance and static fields of objects --- .../Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs | 2 +- .../Debugger/ValueNodes/MembersValueNodeProvider.cs | 7 +++++-- .../Debugger/ValueNodes/StaticMembersValueNodeProvider.cs | 7 +++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs index b8e28f71d5..01284a16d3 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgDotNetEngineValueNodeFactory.cs @@ -53,7 +53,7 @@ public DbgDotNetEngineValueNodeFactoryImpl(DbgDotNetFormatter formatter, DbgDotN internal DbgEngineValueNode Create(DbgDotNetValueNode node) => new DbgEngineValueNodeImpl(this, node); - public override DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo customTypeInfo) => + public override DbgEngineValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo? customTypeInfo) => new DbgEngineValueNodeImpl(this, factory.Create(evalInfo, name, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, customTypeInfo)); public override DbgEngineValueNode CreateException(DbgEvaluationInfo evalInfo, uint id, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options) => diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/MembersValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/MembersValueNodeProvider.cs index d589d16517..0f45d6c12a 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/MembersValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/MembersValueNodeProvider.cs @@ -31,6 +31,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; using dnSpy.Roslyn.Properties; namespace dnSpy.Roslyn.Debugger.ValueNodes { @@ -215,8 +216,10 @@ void Initialize(DbgEvaluationInfo evalInfo) { } else if (valueResult.ValueIsException) newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.Error, true, false, expectedType, false); - else - newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, false); + else { + var customTypeInfoProvider = CustomAttributeAdditionalTypeInfoProvider.Create(info.Member); + newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, customTypeInfoProvider, false); + } valueResult = default; return (newNode, canHide); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/StaticMembersValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/StaticMembersValueNodeProvider.cs index 39f147058e..303b245b6b 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/StaticMembersValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/StaticMembersValueNodeProvider.cs @@ -27,6 +27,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; +using dnSpy.Roslyn.Debugger.Formatters; namespace dnSpy.Roslyn.Debugger.ValueNodes { sealed class StaticMembersValueNodeProvider : MembersValueNodeProvider { @@ -92,8 +93,10 @@ protected override (DbgDotNetValueNode node, bool canHide) CreateValueNode(DbgEv newNode = valueNodeFactory.CreateError(evalInfo, info.Name, valueResult.ErrorMessage!, expression, false); else if (valueResult.ValueIsException) newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.Error, true, false, expectedType, false); - else - newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, false); + else { + var customTypeInfoProvider = CustomAttributeAdditionalTypeInfoProvider.Create(info.Member); + newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value!, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, customTypeInfoProvider, false); + } valueResult = default; return (newNode, true); From e5b2a52d05b884ba94edfa64c75607cbc87a12fb Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 4 Aug 2023 14:37:39 +0200 Subject: [PATCH 09/13] Add custom type info to `Static Fields` tool window --- .../Evaluation/Engine/DbgEngineLanguageImpl.cs | 2 +- .../Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineLanguageImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineLanguageImpl.cs index 32d46c7e93..779ca65563 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineLanguageImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineLanguageImpl.cs @@ -123,7 +123,7 @@ public override void InitializeContext(DbgEvaluationContext context, DbgCodeLoca Debug2.Assert(context.Runtime.GetDotNetRuntime() is not null); IDebuggerDisplayAttributeEvaluatorUtils.Initialize(context, debuggerDisplayAttributeEvaluator); - // Needed by DebuggerRuntimeImpl (calls expressionCompiler.TryGetAliasInfo()) and DbgCorDebugInternalRuntimeImpl (calls expressionCompiler.CreateCustomTypeInfo()) + // Needed by DebuggerRuntimeImpl (calls expressionCompiler.TryGetAliasInfo()), DbgCorDebugInternalRuntimeImpl and DbgEngineStaticFieldsProviderImpl (calls expressionCompiler.CreateCustomTypeInfo()) context.GetOrCreateData(() => expressionCompiler); if ((context.Options & DbgEvaluationContextOptions.NoMethodBody) == 0 && location is IDbgDotNetCodeLocation loc) { diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs index 6dbed142b9..f1dd91f6e0 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet/Evaluation/Engine/DbgEngineStaticFieldsProviderImpl.cs @@ -23,6 +23,7 @@ You should have received a copy of the GNU General Public License using dnlib.DotNet.Emit; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.DotNet.Evaluation; +using dnSpy.Contracts.Debugger.DotNet.Evaluation.ExpressionCompiler; using dnSpy.Contracts.Debugger.DotNet.Evaluation.Formatters; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Evaluation; @@ -88,8 +89,10 @@ DbgEngineValueNode[] GetNodesCore(DbgEvaluationInfo evalInfo, DbgValueNodeEvalua if (fieldVal.HasError) valueNodes[j++] = valueNodeFactory.CreateError(evalInfo, fieldExpression, fieldVal.ErrorMessage!, fieldExpression.ToString(), false); - else - valueNodes[j++] = valueNodeFactory.Create(evalInfo, fieldExpression, fieldVal.Value!, null, options, fieldExpression.ToString(), GetFieldImageName(field), false, false, field.FieldType, null); // TODO: + else { + evalInfo.Context.TryGetData(out DbgDotNetExpressionCompiler? expressionCompiler); + valueNodes[j++] = valueNodeFactory.Create(evalInfo, fieldExpression, fieldVal.Value!, null, options, fieldExpression.ToString(), GetFieldImageName(field), false, false, field.FieldType, expressionCompiler?.CreateCustomTypeInfo(field)); + } } ObjectCache.Free(ref output); From cb5d074639b3b0933ee4815d9589a321b4741fb7 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 4 Aug 2023 14:38:13 +0200 Subject: [PATCH 10/13] Fix custom type info generation for `Show Raw Locals` option --- .../dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs index 67a7ade9d3..b9da350824 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs @@ -194,9 +194,10 @@ public byte[] Compile(out DSEELocalAndMethod[] locals, out string typeName, out if (dynamicAttr.ConstructorArguments.Count == 0) dynamicFlags = new ReadOnlyCollection(new byte[] { 1 }); else if (dynamicAttr.ConstructorArguments.Count == 1 && dynamicAttr.ConstructorArguments[0].Value is IList flags) { - bool[]? array = new bool[flags.Count]; - for (var i = 0; i < flags.Count; i++) { - var argValue = flags[i].Value; + int offset = parameter.Type.RemovePinnedAndModifiers().IsByRef ? 1 : 0; + bool[]? array = new bool[flags.Count - offset]; + for (var i = 0; i < array.Length; i++) { + var argValue = flags[i + offset].Value; if (argValue is bool b) array[i] = b; else { From e014c05adcf58645b8a6b37fc23fed3ac7a3893f Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 8 Aug 2023 14:09:41 +0200 Subject: [PATCH 11/13] Fix custom type info computation for tuple value nodes --- .../ValueNodes/TupleValueNodeProvider.cs | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs index 464c2f80c2..4ff8d34987 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs @@ -52,6 +52,7 @@ public TupleValueNodeProvider(bool addParens, DmdType slotType, AdditionalTypeIn this.tupleFields = tupleFields; cachedTypeInfoStates = new AdditionalTypeInfoState[tupleFields.Length]; typeInfo.TupleNameIndex += tupleFields.Length; + typeInfo.DynamicTypeIndex++; cachedTypeInfoStates[0] = typeInfo; cachedIndex = 0; } @@ -64,20 +65,64 @@ AdditionalTypeInfoState GetCachedTypeInfoState(int tupleFieldIndex) { var typeInfo = cachedTypeInfoStates[cachedIndex]; while (cachedIndex < tupleFieldIndex) { - ref readonly var info = ref tupleFields[cachedIndex]; - for (int k = 0; k < info.Fields.Length; k++) { - typeInfo.DynamicTypeIndex++; - if (TypeFormatterUtils.IsSystemValueTuple(info.Fields[k].FieldType, out int cardinality)) { - typeInfo.TupleNameIndex += cardinality; + ref readonly var previousField = ref tupleFields[cachedIndex]; + UpdateTypeState(previousField.Fields[previousField.Fields.Length - 1].FieldType, ref typeInfo); + + typeInfo.DynamicTypeIndex++; + + ref readonly var field = ref tupleFields[cachedIndex + 1]; + if (field.Fields.Length > 1) { + var item1 = field.Fields[field.Fields.Length - 1]; + var rest = field.Fields[field.Fields.Length - 2]; + if (item1.Name == "Item1" && rest.Name == "Rest") { typeInfo.DynamicTypeIndex++; + typeInfo.TupleNameIndex += TypeFormatterUtils.GetTupleArity(rest.FieldType); } } + cachedTypeInfoStates[++cachedIndex] = typeInfo; } return typeInfo; } + static void UpdateTypeState(DmdType type, ref AdditionalTypeInfoState state) { + state.DynamicTypeIndex += type.GetCustomModifiers().Count; + + while (type.HasElementType) { + state.DynamicTypeIndex++; + type = type.GetElementType()!; + } + + switch (type.TypeSignatureKind) { + case DmdTypeSignatureKind.Type: + case DmdTypeSignatureKind.TypeGenericParameter: + case DmdTypeSignatureKind.MethodGenericParameter: + return; + case DmdTypeSignatureKind.GenericInstance: + if (TypeFormatterUtils.IsSystemValueTuple(type, out int cardinality)) + state.TupleNameIndex += cardinality; + + var gen = type.GetGenericArguments(); + for (int i = 0; i < gen.Count; i++) { + state.DynamicTypeIndex++; + UpdateTypeState(gen[i], ref state); + } + break; + case DmdTypeSignatureKind.FunctionPointer: + var sig = type.GetFunctionPointerMethodSignature(); + state.DynamicTypeIndex++; + UpdateTypeState(sig.ReturnType, ref state); + + var types = sig.GetParameterTypes(); + for (int i = 0; i < types.Count; i++) { + state.DynamicTypeIndex++; + UpdateTypeState(types[i], ref state); + } + break; + } + } + public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueNodeFactory, DbgEvaluationInfo evalInfo, ulong index, int count, DbgValueNodeEvaluationOptions options, ReadOnlyCollection? formatSpecifiers) { var runtime = evalInfo.Runtime.GetDotNetRuntime(); var res = count == 0 ? Array.Empty() : new DbgDotNetValueNode[count]; From f384bd494c4ad8003a33a82c1dd48ab2f4f55f7c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 9 Aug 2023 21:30:14 +0200 Subject: [PATCH 12/13] Display custom type info in `Type` column for value nodes --- .../Formatters/CSharp/CSharpFormatter.cs | 4 +-- .../Formatters/CSharp/CSharpTypeFormatter.cs | 6 ++-- .../Debugger/Formatters/LanguageFormatter.cs | 32 ++++++++++++------- .../VisualBasic/VisualBasicFormatter.cs | 4 +-- .../VisualBasic/VisualBasicTypeFormatter.cs | 8 ++--- .../ValueNodes/DbgDotNetValueNodeImpl.cs | 32 +++++++++++++++++-- .../ValueNodes/LanguageValueNodeFactory.cs | 12 +++---- 7 files changed, 67 insertions(+), 31 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpFormatter.cs index 8d63ff87bf..0afe4b5da4 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpFormatter.cs @@ -27,8 +27,8 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Roslyn.Debugger.Formatters.CSharp { [ExportDbgDotNetFormatter(DbgDotNetLanguageGuids.CSharp)] sealed class CSharpFormatter : LanguageFormatter { - public override void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type, DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) => - new CSharpTypeFormatter(output, options.ToTypeFormatterOptions(), cultureInfo).Format(type, value); + public override void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type, AdditionalTypeInfoState additionalTypeInfo, DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) => + new CSharpTypeFormatter(output, options.ToTypeFormatterOptions(), cultureInfo).Format(type, additionalTypeInfo, value); public override void FormatValue(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetValue value, DbgValueFormatterOptions options, CultureInfo? cultureInfo) => new CSharpValueFormatter(output, evalInfo, this, options.ToValueFormatterOptions(), cultureInfo).Format(value); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs index 0a26a3aca7..0ffd98ae9a 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs @@ -115,9 +115,11 @@ internal static string GetFormattedIdentifier(string? id) { void WriteIdentifier(string? id, DbgTextColor color) => OutputWrite(GetFormattedIdentifier(id), color); - public void Format(DmdType type, DbgDotNetValue? value = null, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null, DmdParameterInfo? pd = null, bool forceReadOnly = false) { + public void Format(DmdType type, DbgDotNetValue? value = null, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null, DmdParameterInfo? pd = null, bool forceReadOnly = false) => + Format(type, new AdditionalTypeInfoState(additionalTypeInfoProvider), value, pd, forceReadOnly); + + public void Format(DmdType type, AdditionalTypeInfoState state, DbgDotNetValue? value = null, DmdParameterInfo? pd = null, bool forceReadOnly = false) { WriteRefIfByRef(type, pd, forceReadOnly); - var state = new AdditionalTypeInfoState(additionalTypeInfoProvider); if (type.IsByRef) { type = type.GetElementType()!; state.DynamicTypeIndex++; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/LanguageFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/LanguageFormatter.cs index 5e2b539cf0..9f6ef6a10f 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/LanguageFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/LanguageFormatter.cs @@ -1,28 +1,36 @@ /* - Copyright (C) 2014-2019 de4dot@gmail.com + Copyright (C) 2014-2019 de4dot@gmail.com - This file is part of dnSpy + This file is part of dnSpy - dnSpy is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + dnSpy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - dnSpy is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + dnSpy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with dnSpy. If not, see . + You should have received a copy of the GNU General Public License + along with dnSpy. If not, see . */ +using System.Globalization; +using dnSpy.Contracts.Debugger.DotNet.Evaluation; using dnSpy.Contracts.Debugger.DotNet.Evaluation.Formatters; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; +using dnSpy.Debugger.DotNet.Metadata; namespace dnSpy.Roslyn.Debugger.Formatters { abstract class LanguageFormatter : DbgDotNetFormatter { + public override void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type,DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) => + FormatType(evalInfo, output, type, default, value, options, cultureInfo); + + public abstract void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type, AdditionalTypeInfoState additionalTypeInfo, DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo); + public override void FormatExceptionName(DbgEvaluationContext context, IDbgTextWriter output, uint id) => output.Write(DbgTextColor.ExceptionName, AliasConstants.ExceptionName); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicFormatter.cs index 4dccbd25c1..d896daed1b 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicFormatter.cs @@ -27,8 +27,8 @@ You should have received a copy of the GNU General Public License namespace dnSpy.Roslyn.Debugger.Formatters.VisualBasic { [ExportDbgDotNetFormatter(DbgDotNetLanguageGuids.VisualBasic)] sealed class VisualBasicFormatter : LanguageFormatter { - public override void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type, DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) => - new VisualBasicTypeFormatter(output, options.ToTypeFormatterOptions(), cultureInfo).Format(type, value); + public override void FormatType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DmdType type, AdditionalTypeInfoState additionalTypeInfo, DbgDotNetValue? value, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) => + new VisualBasicTypeFormatter(output, options.ToTypeFormatterOptions(), cultureInfo).Format(type, additionalTypeInfo, value); public override void FormatValue(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetValue value, DbgValueFormatterOptions options, CultureInfo? cultureInfo) => new VisualBasicValueFormatter(output, evalInfo, this, options.ToValueFormatterOptions(), cultureInfo).Format(value); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs index b260ccc1b6..8af63cfdcb 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs @@ -124,10 +124,10 @@ internal static string GetFormattedIdentifier(string? id) { void WriteIdentifier(string? id, DbgTextColor color) => OutputWrite(GetFormattedIdentifier(id), color); - public void Format(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null) { - var state = new AdditionalTypeInfoState(additionalTypeInfoProvider); - FormatCore(type, value, ref state); - } + public void Format(DmdType type, DbgDotNetValue? value = null, IAdditionalTypeInfoProvider? additionalTypeInfoProvider = null) => + Format(type,new AdditionalTypeInfoState(additionalTypeInfoProvider), value); + + public void Format(DmdType type, AdditionalTypeInfoState state, DbgDotNetValue? value = null) => FormatCore(type, value, ref state); void FormatCore(DmdType type, DbgDotNetValue? value, ref AdditionalTypeInfoState state) { if (type is null) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs index 5d33dc7ea4..bf186884b0 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs @@ -51,8 +51,9 @@ sealed class DbgDotNetValueNodeImpl : DbgDotNetValueNode { readonly DbgDotNetText valueText; ReadOnlyCollection? formatSpecifiers; readonly ColumnFormatter? columnFormatter; + readonly AdditionalTypeInfoState additionalTypeInfo; - public DbgDotNetValueNodeImpl(LanguageValueNodeFactory valueNodeFactory, DbgDotNetValueNodeProvider? childNodeProvider, DbgDotNetText name, DbgDotNetValueNodeInfo? nodeInfo, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType? expectedType, DmdType? actualType, string? errorMessage, DbgDotNetText valueText, ReadOnlyCollection? formatSpecifiers, ColumnFormatter? columnFormatter) { + public DbgDotNetValueNodeImpl(LanguageValueNodeFactory valueNodeFactory, DbgDotNetValueNodeProvider? childNodeProvider, DbgDotNetText name, DbgDotNetValueNodeInfo? nodeInfo, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType? expectedType, AdditionalTypeInfoState additionalTypeInfo, DmdType? actualType, string? errorMessage, DbgDotNetText valueText, ReadOnlyCollection? formatSpecifiers, ColumnFormatter? columnFormatter) { if (name.Parts is null && columnFormatter is null) throw new ArgumentException(); this.valueNodeFactory = valueNodeFactory ?? throw new ArgumentNullException(nameof(valueNodeFactory)); @@ -67,6 +68,7 @@ public DbgDotNetValueNodeImpl(LanguageValueNodeFactory valueNodeFactory, DbgDotN ExpectedType = expectedType; ActualType = actualType; ErrorMessage = errorMessage; + this.additionalTypeInfo = additionalTypeInfo; this.valueText = valueText; this.formatSpecifiers = formatSpecifiers; this.columnFormatter = columnFormatter; @@ -102,11 +104,13 @@ public override bool FormatValue(DbgEvaluationInfo evalInfo, IDbgTextWriter outp public override bool FormatActualType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterTypeOptions options, DbgValueFormatterOptions valueOptions, CultureInfo? cultureInfo) => columnFormatter?.FormatActualType(evalInfo, output, formatter, options, valueOptions, cultureInfo) == true || - FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, cultureInfo); + FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, cultureInfo) || + FormatActualTypeLanguageFormatter(evalInfo, output, formatter, options, cultureInfo); public override bool FormatExpectedType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterTypeOptions options, DbgValueFormatterOptions valueOptions, CultureInfo? cultureInfo) => columnFormatter?.FormatExpectedType(evalInfo, output, formatter, options, valueOptions, cultureInfo) == true || - FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, cultureInfo); + FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, cultureInfo) || + FormatExpectedTypeLanguageFormatter(evalInfo, output, formatter, options, cultureInfo); bool FormatDebuggerDisplayAttributeType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterOptions options, CultureInfo? cultureInfo) { if (Value is null) @@ -121,6 +125,28 @@ bool FormatDebuggerDisplayAttributeType(DbgEvaluationInfo evalInfo, IDbgTextWrit return displayAttrFormatter.FormatType(Value); } + bool FormatActualTypeLanguageFormatter(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) { + if (formatter is not LanguageFormatter languageFormatter) + return false; + if (ActualType is null || ExpectedType is null) + return false; + if (ActualType != ExpectedType) + return false; + + languageFormatter.FormatType(evalInfo, output, ActualType, additionalTypeInfo, Value, options, cultureInfo); + return true; + } + + bool FormatExpectedTypeLanguageFormatter(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterTypeOptions options, CultureInfo? cultureInfo) { + if (formatter is not LanguageFormatter languageFormatter) + return false; + if (ExpectedType is null) + return false; + + languageFormatter.FormatType(evalInfo, output, ExpectedType, additionalTypeInfo, Value, options, cultureInfo); + return true; + } + public override ulong GetChildCount(DbgEvaluationInfo evalInfo) => childNodeProvider?.GetChildCount(evalInfo) ?? 0; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs index f7fee68cf0..5a1747ca5e 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs @@ -56,10 +56,10 @@ public DbgDotNetText GetTypeParameterName(DmdType typeParameter) { } internal DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValueNodeProvider provider, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, DbgDotNetText valueText) => - new DbgDotNetValueNodeImpl(this, provider, name, null, expression, imageName, true, false, null, null, null, valueText, formatSpecifiers, null); + new DbgDotNetValueNodeImpl(this, provider, name, null, expression, imageName, true, false, null, default, null, null, valueText, formatSpecifiers, null); internal DbgDotNetValueNode Create(DbgDotNetValueNodeProvider provider, DbgDotNetText name, DbgDotNetValueNodeInfo nodeInfo, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DmdType actualType, string? errorMessage, DbgDotNetText valueText, ReadOnlyCollection? formatSpecifiers) => - new DbgDotNetValueNodeImpl(this, provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, actualType, errorMessage, valueText, formatSpecifiers, null); + new DbgDotNetValueNodeImpl(this, provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, default, actualType, errorMessage, valueText, formatSpecifiers, null); DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, AdditionalTypeInfoState typeInfoState, bool isRootExpression, ColumnFormatter? columnFormatter) { // Could be a by-ref property, local, or parameter. @@ -93,11 +93,11 @@ DbgDotNetValueNode CreateValue(DbgEvaluationInfo evalInfo, DbgDotNetText name, D info = valueNodeProviderFactory.Create(evalInfo, addParens, expectedType, typeInfoState, nodeInfo, options); if (useProvider) { if (info.ErrorMessage is not null) - return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, PredefinedDbgValueNodeImageNames.Error, true, false, null, null, info.ErrorMessage, new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.Error, info.ErrorMessage)), formatSpecifiers, columnFormatter); + return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, PredefinedDbgValueNodeImageNames.Error, true, false, null, default, null, info.ErrorMessage, new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.Error, info.ErrorMessage)), formatSpecifiers, columnFormatter); Debug2.Assert(info.Provider is not null); - return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, info.Provider?.ImageName ?? imageName, true, false, null, null, info.ErrorMessage, info.Provider?.ValueText ?? default, formatSpecifiers, columnFormatter); + return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, info.Provider?.ImageName ?? imageName, true, false, null, default, null, info.ErrorMessage, info.Provider?.ValueText ?? default, formatSpecifiers, columnFormatter); } - return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, value.Type, info.ErrorMessage, default, formatSpecifiers, columnFormatter); + return new DbgDotNetValueNodeImpl(this, info.Provider, name, nodeInfo, expression, imageName, isReadOnly, causesSideEffects, expectedType, typeInfoState, value.Type, info.ErrorMessage, default, formatSpecifiers, columnFormatter); } public sealed override DbgDotNetValueNode Create(DbgEvaluationInfo evalInfo, DbgDotNetText name, DbgDotNetValue value, ReadOnlyCollection? formatSpecifiers, DbgValueNodeEvaluationOptions options, string expression, string imageName, bool isReadOnly, bool causesSideEffects, DmdType expectedType, DbgDotNetCustomTypeInfo? customTypeInfo) => @@ -177,7 +177,7 @@ static DbgValueFormatterTypeOptions ToDbgValueFormatterTypeOptions(DbgValueForma protected abstract void FormatReturnValueMethodName(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgValueFormatterTypeOptions typeOptions, DbgValueFormatterOptions valueOptions, CultureInfo? cultureInfo, DmdMethodBase method, DmdPropertyInfo? property); public sealed override DbgDotNetValueNode CreateError(DbgEvaluationInfo evalInfo, DbgDotNetText name, string errorMessage, string expression, bool causesSideEffects) => - new DbgDotNetValueNodeImpl(this, null, name, null, expression, PredefinedDbgValueNodeImageNames.Error, true, causesSideEffects, null, null, errorMessage, default, null, null); + new DbgDotNetValueNodeImpl(this, null, name, null, expression, PredefinedDbgValueNodeImageNames.Error, true, causesSideEffects, null, default, null, errorMessage, default, null, null); public sealed override DbgDotNetValueNode CreateTypeVariables(DbgEvaluationInfo evalInfo, DbgDotNetTypeVariableInfo[] typeVariableInfos) => new DbgDotNetTypeVariablesNode(this, typeVariableInfos); From 99c18d613890087e32a157e7e0bbd5acfc20c30f Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 10 Aug 2023 14:22:28 +0200 Subject: [PATCH 13/13] Improved handling of modifier signatures for `Raw locals` display --- .../dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs index b9da350824..98331d1697 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/GetLocalsAssemblyBuilder.cs @@ -194,7 +194,14 @@ public byte[] Compile(out DSEELocalAndMethod[] locals, out string typeName, out if (dynamicAttr.ConstructorArguments.Count == 0) dynamicFlags = new ReadOnlyCollection(new byte[] { 1 }); else if (dynamicAttr.ConstructorArguments.Count == 1 && dynamicAttr.ConstructorArguments[0].Value is IList flags) { - int offset = parameter.Type.RemovePinnedAndModifiers().IsByRef ? 1 : 0; + int offset = 0; + var type = parameter.Type.RemovePinned(); + while (type is ModifierSig) { + type = type.Next.RemovePinned(); + offset++; + } + if (type.IsByRef) + offset++; bool[]? array = new bool[flags.Count - offset]; for (var i = 0; i < array.Length; i++) { var argValue = flags[i + offset].Value;