From c6f6e2b304bc187e01362af72ef165ce3ea43a67 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 16 Jun 2023 20:27:04 +0200 Subject: [PATCH 001/122] Update NuGet.Configuration to 6.6.1 --- DnSpyCommon.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index d31cf60f50..4ae3002753 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -47,7 +47,7 @@ 5.0.1 4.6.0 7.0.0 - 6.6.0 + 6.6.1 From db1f4c9445fa9764de3352921bcd65da59bf4e85 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 16 Jun 2023 21:38:12 +0200 Subject: [PATCH 002/122] Improve BAML connection ID decompilation --- .../Rewrite/ConnectionIdRewritePass.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Extensions/dnSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/Extensions/dnSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index 8b29072b79..3e8caea034 100644 --- a/Extensions/dnSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/Extensions/dnSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -110,23 +110,21 @@ static void ProcessElement(XamlContext ctx, XElement elem, Dictionary> connIds) { - var connId = elem.Annotation(); - if (connId is null) - return; + foreach (var connId in elem.Annotations()) { + if (!connIds.TryGetValue((int)connId.Id, out var cb)) { + elem.AddBeforeSelf(new XComment(string.Format(dnSpy_BamlDecompiler_Resources.Error_UnknownConnectionId, connId.Id))); + return; + } - if (!connIds.TryGetValue((int)connId.Id, out var cb)) { - elem.AddBeforeSelf(new XComment(string.Format(dnSpy_BamlDecompiler_Resources.Error_UnknownConnectionId, connId.Id))); - return; + cb(ctx, elem); } - - cb(ctx, elem); } struct FieldAssignment { public string FieldName; public void Callback(XamlContext ctx, XElement elem) { - var xName = ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml); + var xName = ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml, elem); if (elem.Attribute("Name") is null && elem.Attribute(xName) is null) elem.Add(new XAttribute(xName, FieldName)); } From 56869a7792c0de52369add1af9c5ab10705de504 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 22 Jun 2023 14:59:27 +0200 Subject: [PATCH 003/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index bf75c94aa0..0e2c7b27fa 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit bf75c94aa07fc898a2b3533b6729a0a0ef9e9907 +Subproject commit 0e2c7b27fa4a0ffa4e9b7f69dfb3e3585ce08a8f From cb6c731c7c45fda87dbfb3bcf484f5d2e188749f Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 22 Jun 2023 21:43:04 +0200 Subject: [PATCH 004/122] Fixed loading XML documentation for the .NET core library --- .../Decompiler/XmlDoc/XmlDocLoader.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocLoader.cs b/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocLoader.cs index fc3fd3f33c..f1063b193d 100644 --- a/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocLoader.cs +++ b/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocLoader.cs @@ -190,6 +190,13 @@ static string[] GetDirectories(string path) { var found = LookupLocalizedXmlDoc(Path.Combine(directories[0], assemblyFileName)); if (found is not null) return found; + // System.Private.CoreLib.dll does not have a documentation file, but System.Runtime.dll does + // Since System.Runtime.dll contains just type forwarders to System.Private.CoreLib, we can use it's documentation file + if (assemblyFileName == "System.Private.CoreLib.dll") { + found = LookupLocalizedXmlDoc(Path.Combine(directories[0], "System.Runtime.dll")); + if (found is not null) + return found; + } } return null; From 81e9c2ee13925dc727538398009545420929a15a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 22 Jun 2023 21:50:39 +0200 Subject: [PATCH 005/122] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 25043ce142..923993e4a0 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,14 @@ To debug Unity games, you need this repo too: https://github.com/dnSpyEx/dnSpy-U - Break on module load - Tracepoints and conditional breakpoints - Export/import breakpoints and tracepoints +- Optional Just My Code (JMC) stepping filters for system libraries - Call stack, threads, modules, processes windows - Break on thrown exceptions (1st chance) - Variables windows support evaluating C# / Visual Basic expressions - Dynamic modules can be debugged (but not dynamic methods due to CLR limitations) - Output window logs various debugging events, and it shows timestamps by default :) - Assemblies that decrypt themselves at runtime can be debugged, dnSpy will use the in-memory image. You can also force dnSpy to always use in-memory images instead of disk files. +- Bypasses for common debugger detection techniques - Public API, you can write an extension or use the C# Interactive window to control the debugger # Assembly Editor @@ -69,7 +71,7 @@ To debug Unity games, you need this repo too: https://github.com/dnSpyEx/dnSpy-U # Other -- BAML decompiler +- BAML decompiler and disassembler - Blue, light and dark themes (and a dark high contrast theme) - Bookmarks - C# Interactive window can be used to script dnSpy @@ -90,6 +92,8 @@ To debug Unity games, you need this repo too: https://github.com/dnSpyEx/dnSpy-U - [VS MEF](https://github.com/microsoft/vs-mef) (Faster MEF equals faster startup) - [ClrMD](https://github.com/microsoft/clrmd) (Access to lower level debugging info not provided by the CorDebug API) - [Iced](https://github.com/icedland/iced) (x86/x64 disassembler) +- [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) (JSON serializer & deserializer) +- [NuGet.Configuration](https://github.com/NuGet/NuGet.Client) (NuGet configuration file reader) # Translating dnSpy From 985d53dd011d78ab0aee98a9bab40e4932b90630 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 23 Jun 2023 10:39:47 +0200 Subject: [PATCH 006/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 0e2c7b27fa..b456ca312e 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 0e2c7b27fa4a0ffa4e9b7f69dfb3e3585ce08a8f +Subproject commit b456ca312e82e99438e226018b555ac4cd294e33 From 06a1402cbd2fa02b059f0e020b8acd97e6cd1b94 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 23 Jun 2023 13:24:09 +0200 Subject: [PATCH 007/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index b456ca312e..3130225ef8 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit b456ca312e82e99438e226018b555ac4cd294e33 +Subproject commit 3130225ef8ff6a73890120efb56c58efeaf4f98d From 5076ea33a688b0e1c71e060a765d04bdf1864361 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 23 Jun 2023 19:41:47 +0200 Subject: [PATCH 008/122] Expose debugger value nodes to context menus --- .../Evaluation/ViewModel/Impl/ValueNodesVM.cs | 10 ++++++++++ dnSpy/dnSpy.Contracts.DnSpy/Menus/MenuConstants.cs | 3 +++ 2 files changed, 13 insertions(+) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger/Evaluation/ViewModel/Impl/ValueNodesVM.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger/Evaluation/ViewModel/Impl/ValueNodesVM.cs index cc77d0862d..1916d196c9 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger/Evaluation/ViewModel/Impl/ValueNodesVM.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger/Evaluation/ViewModel/Impl/ValueNodesVM.cs @@ -77,6 +77,16 @@ sealed class GuidObjectsProvider : IGuidObjectsProvider { public IEnumerable GetGuidObjects(GuidObjectsProviderArgs args) { yield return new GuidObject(ValueNodesVMConstants.GUIDOBJ_VALUENODESVM_GUID, vm); + + IList? nodes = null; + for (int i = 0; i < vm.TreeView.SelectedItems.Length; i++) { + if (vm.TreeView.SelectedItems[i] is ValueNodeImpl { RawNode: DebuggerValueRawNode debuggerValueRawNode }) { + nodes ??= new List(); + nodes.Add(debuggerValueRawNode.DebuggerValueNode); + } + } + if (nodes is not null) + yield return new GuidObject(new Guid(MenuConstants.GUIDOBJ_DBGVALUENODES_ARRAY_GUID), nodes.ToArray()); } } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Menus/MenuConstants.cs b/dnSpy/dnSpy.Contracts.DnSpy/Menus/MenuConstants.cs index 52b4c250ae..6a46805e97 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Menus/MenuConstants.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Menus/MenuConstants.cs @@ -190,6 +190,9 @@ public static class MenuConstants { /// Variables window treeview (autos, locals, watch) public static readonly string GUIDOBJ_VARIABLES_WINDOW_TREEVIEW_GUID = "6415325D-11CC-48C7-9E7B-15D363B7D18E"; + /// Variable value node array + public static readonly string GUIDOBJ_DBGVALUENODES_ARRAY_GUID = "8A90931A-6C56-4C6D-BAEF-F2A3338EED36"; + /// Group: App Menu: File, Group: Save public const string GROUP_APP_MENU_FILE_SAVE = "0,557C4B2D-5966-41AF-BFCA-D0A36DB5D6D8"; From 21df6a90651f327eea6b2777c50c133eae4061dc Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 23 Jun 2023 19:41:59 +0200 Subject: [PATCH 009/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 3130225ef8..003b2effe4 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 3130225ef8ff6a73890120efb56c58efeaf4f98d +Subproject commit 003b2effe4067eed5c8c58f0fcedd09a1995f15d From 0f9d37b0f90270296df108aae69eaea3daaced90 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 24 Jun 2023 13:54:35 +0200 Subject: [PATCH 010/122] Provide EE with CustomTypeInfo for return value aliases --- .../DbgCorDebugInternalRuntimeImpl.cs | 15 +++++- .../Engine/DbgEngineLanguageImpl.cs | 2 +- .../LanguageExpressionCompiler.cs | 53 +++++++++++++++++++ .../DbgDotNetExpressionCompiler.cs | 6 +++ 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/Evaluation/DbgCorDebugInternalRuntimeImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/Evaluation/DbgCorDebugInternalRuntimeImpl.cs index c59aab5c4c..09bfaa570b 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/Evaluation/DbgCorDebugInternalRuntimeImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/Evaluation/DbgCorDebugInternalRuntimeImpl.cs @@ -31,6 +31,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.CallStack; using dnSpy.Contracts.Debugger.DotNet.CorDebug; using dnSpy.Contracts.Debugger.DotNet.Evaluation; +using dnSpy.Contracts.Debugger.DotNet.Evaluation.ExpressionCompiler; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Metadata; @@ -821,6 +822,7 @@ DbgDotNetAliasInfo[] GetAliasesCore(DbgEvaluationInfo evalInfo) { exception = GetExceptionCore(evalInfo, DbgDotNetRuntimeConstants.ExceptionId); stowedException = GetStowedExceptionCore(evalInfo, DbgDotNetRuntimeConstants.StowedExceptionId); returnValues = GetReturnValuesCore(evalInfo); + evalInfo.Context.TryGetData(out DbgDotNetExpressionCompiler? expressionCompiler); int count = (exception is not null ? 1 : 0) + (stowedException is not null ? 1 : 0) + returnValues.Length + (returnValues.Length != 0 ? 1 : 0); if (count == 0) @@ -833,10 +835,19 @@ DbgDotNetAliasInfo[] GetAliasesCore(DbgEvaluationInfo evalInfo) { if (stowedException is not null) res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.StowedException, stowedException.Type, DbgDotNetRuntimeConstants.StowedExceptionId, null); if (returnValues.Length != 0) { - res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, returnValues[returnValues.Length - 1].Value.Type, DbgDotNetRuntimeConstants.LastReturnValueId, null); + var lastReturnVal = returnValues[returnValues.Length - 1]; + res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, lastReturnVal.Value.Type, DbgDotNetRuntimeConstants.LastReturnValueId, CreateCustomTypeInfo(lastReturnVal)); + foreach (var returnValue in returnValues) { Debug.Assert(returnValue.Id != DbgDotNetRuntimeConstants.LastReturnValueId); - res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, returnValue.Value.Type, returnValue.Id, null); + res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, returnValue.Value.Type, returnValue.Id, CreateCustomTypeInfo(returnValue)); + } + + DbgDotNetCustomTypeInfo? CreateCustomTypeInfo(DbgDotNetReturnValueInfo returnVal) { + var method = returnVal.Method as DmdMethodInfo; + if (method?.ReturnType.Equals(returnVal.Value.Type) == true) + return expressionCompiler?.CreateCustomTypeInfo(method.ReturnParameter); + return null; } } if (w != res.Length) 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 e47cb58cde..32d46c7e93 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()) + // Needed by DebuggerRuntimeImpl (calls expressionCompiler.TryGetAliasInfo()) and DbgCorDebugInternalRuntimeImpl (calls expressionCompiler.CreateCustomTypeInfo()) context.GetOrCreateData(() => expressionCompiler); if ((context.Options & DbgEvaluationContextOptions.NoMethodBody) == 0 && location is IDbgDotNetCodeLocation loc) { diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ExpressionCompiler/LanguageExpressionCompiler.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ExpressionCompiler/LanguageExpressionCompiler.cs index eca138f5db..cb8794cf4d 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ExpressionCompiler/LanguageExpressionCompiler.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ExpressionCompiler/LanguageExpressionCompiler.cs @@ -20,7 +20,9 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.Diagnostics; +using System.Linq; using System.Threading; using dnlib.DotNet; using dnSpy.Contracts.Debugger.CallStack; @@ -33,6 +35,7 @@ You should have received a copy of the GNU General Public License using dnSpy.Contracts.Debugger.Text; using dnSpy.Contracts.Debugger.Text.DnSpy; using dnSpy.Contracts.Decompiler; +using dnSpy.Debugger.DotNet.Metadata; using dnSpy.Roslyn.Text; using dnSpy.Roslyn.Text.Classification; using Microsoft.CodeAnalysis; @@ -564,6 +567,56 @@ protected DbgDotNetCompilationResult CompileGetLocals(EvalContextState state, Me public override bool TryGetAliasInfo(string aliasName, out DbgDotNetParsedAlias aliasInfo) => AliasConstants.TryGetAliasInfo(aliasName, IsCaseSensitive, out aliasInfo); + public override DbgDotNetCustomTypeInfo? CreateCustomTypeInfo(IDmdCustomAttributeProvider customAttributeProvider) { + var tupleAttr = customAttributeProvider.FindCustomAttribute("System.Runtime.CompilerServices.TupleElementNamesAttribute", false); + var dynamicAttr = customAttributeProvider.FindCustomAttribute("System.Runtime.CompilerServices.DynamicAttribute", false); + + ReadOnlyCollection? tupleNames = null; + if (tupleAttr is not null && tupleAttr.ConstructorArguments.Count == 1 && tupleAttr.ConstructorArguments[0].Value is IList names) { + string?[]? array = new string?[names.Count]; + for (var i = 0; i < names.Count; i++) { + var argValue = names[i].Value; + if (argValue is string str) + array[i] = str; + else if (argValue is null) + array[i] = null; + else { + array = null; + break; + } + } + + if (array is not null) + tupleNames = new ReadOnlyCollection(array); + } + + ReadOnlyCollection? dynamicFlags = null; + if (dynamicAttr is not null) { + 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; + if (argValue is bool b) + array[i] = b; + else { + array = null; + break; + } + } + + if (array is not null) + dynamicFlags = DynamicFlagsCustomTypeInfo.ToBytes(array); + } + } + + var encoded = CustomTypeInfo.Encode(dynamicFlags, tupleNames); + if (encoded is null) + return null; + return new DbgDotNetCustomTypeInfo(CustomTypeInfo.PayloadTypeId, encoded); + } + protected DbgDotNetText CreateText(DbgDotNetAliasKind kind, string expression) { DbgTextColor color; switch (kind) { diff --git a/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ExpressionCompiler/DbgDotNetExpressionCompiler.cs b/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ExpressionCompiler/DbgDotNetExpressionCompiler.cs index 0d916d54fe..fe6e6d8761 100644 --- a/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ExpressionCompiler/DbgDotNetExpressionCompiler.cs +++ b/dnSpy/dnSpy.Contracts.Debugger.DotNet/Evaluation/ExpressionCompiler/DbgDotNetExpressionCompiler.cs @@ -81,6 +81,12 @@ public abstract class DbgDotNetExpressionCompiler { /// Updated with alias info /// public abstract bool TryGetAliasInfo(string aliasName, out DbgDotNetParsedAlias aliasInfo); + + /// + /// Creates encoded custom type information for use by the EE. + /// + /// Custom attributes used to construct the custom type info + public abstract DbgDotNetCustomTypeInfo? CreateCustomTypeInfo(IDmdCustomAttributeProvider customAttributeProvider); } /// Metadata From 55e9aa2cb9184ee01153d2b12b345f7222de9292 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 24 Jun 2023 13:55:28 +0200 Subject: [PATCH 011/122] Improved formatting of type names shown in Locals window --- .../Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs | 4 ++-- .../Debugger/ValueNodes/LanguageValueNodeFactory.cs | 3 ++- .../Debugger/ValueNodes/ReturnValueColumnFormatter.cs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs index 66d3edd760..5d33dc7ea4 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeImpl.cs @@ -101,11 +101,11 @@ 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) ?? + columnFormatter?.FormatActualType(evalInfo, output, formatter, options, valueOptions, cultureInfo) == true || FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, 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) ?? + columnFormatter?.FormatExpectedType(evalInfo, output, formatter, options, valueOptions, cultureInfo) == true || FormatDebuggerDisplayAttributeType(evalInfo, output, formatter, valueOptions, cultureInfo); bool FormatDebuggerDisplayAttributeType(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterOptions options, CultureInfo? cultureInfo) { diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs index 5c2f6c09e7..1260497776 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/LanguageValueNodeFactory.cs @@ -135,7 +135,8 @@ 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); - return CreateValue(evalInfo, default, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, value.Type, false, columnFormatter); + var expectedType = method is DmdMethodInfo mi ? mi.ReturnType : value.Type; + return CreateValue(evalInfo, default, value, formatSpecifiers, options, expression, imageName, isReadOnly, causesSideEffects, expectedType, false, columnFormatter); } internal void FormatReturnValueMethodName(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgValueFormatterOptions options, CultureInfo? cultureInfo, DmdMethodBase method) => diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ReturnValueColumnFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ReturnValueColumnFormatter.cs index dac9231d8a..df62873fc6 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ReturnValueColumnFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ReturnValueColumnFormatter.cs @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ +using System; using System.Diagnostics; using System.Globalization; using dnSpy.Contracts.Debugger.DotNet.Evaluation.Formatters; @@ -38,11 +39,11 @@ public ReturnValueColumnFormatter(LanguageValueNodeFactory owner, DmdMethodBase public override bool FormatName(DbgEvaluationInfo evalInfo, IDbgTextWriter output, DbgDotNetFormatter formatter, DbgValueFormatterOptions options, CultureInfo? cultureInfo) { var formatString = dnSpy_Roslyn_Resources.LocalsWindow_MethodOrProperty_Returned; const string pattern = "{0}"; - int index = formatString.IndexOf(pattern); + int index = formatString.IndexOf(pattern, StringComparison.Ordinal); Debug.Assert(index >= 0); if (index < 0) { formatString = "{0} returned"; - index = formatString.IndexOf(pattern); + index = formatString.IndexOf(pattern, StringComparison.Ordinal); } if (index != 0) From 673588b00930f56223b6e89db8b117dcbd3c72f8 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 25 Jun 2023 11:11:36 +0200 Subject: [PATCH 012/122] Fixed missing parenthesis in debugger evaluation expressions --- .../DbgDotNetValueNodeProviderFactory.cs | 16 ++++++++-------- .../DynamicViewMembersValueNodeProvider.cs | 6 ++++-- .../ResultsViewMembersValueNodeProvider.cs | 6 ++++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs index 2a428fbbb5..84153a4a63 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DbgDotNetValueNodeProviderFactory.cs @@ -208,7 +208,7 @@ public DbgDotNetValueNodeProviderResult Create(DbgEvaluationInfo evalInfo, bool public DbgDotNetValueNodeProviderResult CreateDynamicView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var state = GetTypeState(nodeInfo); - var provider = TryCreateDynamicView(state, nodeInfo.Expression, nodeInfo.Value, slotType, options); + var provider = TryCreateDynamicView(state, nodeInfo.Expression, addParens, nodeInfo.Value, slotType, options); if (provider is not null) return new DbgDotNetValueNodeProviderResult(provider); return new DbgDotNetValueNodeProviderResult(dnSpy_Roslyn_Resources.DynamicView_MustBeDynamicOrComType); @@ -216,7 +216,7 @@ public DbgDotNetValueNodeProviderResult CreateDynamicView(DbgEvaluationInfo eval public DbgDotNetValueNodeProviderResult CreateResultsView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var state = GetTypeState(nodeInfo); - var provider = TryCreateResultsView(state, nodeInfo.Expression, nodeInfo.Value, slotType, options); + 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); @@ -551,15 +551,15 @@ void CreateCore(DbgEvaluationInfo evalInfo, List pro AddProviders(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, forceRawView); } - DbgDotNetValueNodeProvider? TryCreateDynamicView(TypeState state, string expression, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { + DbgDotNetValueNodeProvider? TryCreateDynamicView(TypeState state, string expression, bool addParens, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { if (state.IsDynamicViewType && !value.IsNull) - return new DynamicViewMembersValueNodeProvider(this, valueNodeFactory, value, expectedType, expression, state.Type.AppDomain, evalOptions); + return new DynamicViewMembersValueNodeProvider(this, valueNodeFactory, value, expectedType, expression, addParens, state.Type.AppDomain, evalOptions); return null; } - DbgDotNetValueNodeProvider? TryCreateResultsView(TypeState state, string expression, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { + DbgDotNetValueNodeProvider? TryCreateResultsView(TypeState state, string expression, bool addParens, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { if (state.EnumerableType is not null && !value.IsNull) - return new ResultsViewMembersValueNodeProvider(this, valueNodeFactory, state.EnumerableType, value, expectedType, expression, evalOptions); + return new ResultsViewMembersValueNodeProvider(this, valueNodeFactory, state.EnumerableType, value, expectedType, expression, addParens, evalOptions); return null; } @@ -617,10 +617,10 @@ void AddProviders(List providers, TypeState state, s if (staticMembersInfos.Members.Length != 0) providers.Add(new StaticMembersValueNodeProvider(this, valueNodeFactory, StaticMembersName, state.TypeExpression, staticMembersInfos, membersEvalOptions)); - var provider = TryCreateResultsView(state, expression, value, slotType, evalOptions); + var provider = TryCreateResultsView(state, expression, addParens, value, slotType, evalOptions); if (provider is not null) providers.Add(provider); - provider = TryCreateDynamicView(state, expression, value, slotType, evalOptions); + provider = TryCreateDynamicView(state, expression, addParens, value, slotType, evalOptions); if (provider is not null) providers.Add(provider); } diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DynamicViewMembersValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DynamicViewMembersValueNodeProvider.cs index 8091dd1e35..dad3e91e24 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DynamicViewMembersValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/DynamicViewMembersValueNodeProvider.cs @@ -42,16 +42,18 @@ sealed class DynamicViewMembersValueNodeProvider : MembersValueNodeProvider { readonly DbgDotNetValue instanceValue; readonly DmdType expectedType; readonly string valueExpression; + readonly bool addParens; readonly DmdAppDomain appDomain; string dynamicViewProxyExpression; DbgDotNetValue? getDynamicViewValue; - public DynamicViewMembersValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, LanguageValueNodeFactory valueNodeFactory, DbgDotNetValue instanceValue, DmdType expectedType, string valueExpression, DmdAppDomain appDomain, DbgValueNodeEvaluationOptions evalOptions) + public DynamicViewMembersValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, LanguageValueNodeFactory valueNodeFactory, DbgDotNetValue instanceValue, DmdType expectedType, string valueExpression, bool addParens, DmdAppDomain appDomain, DbgValueNodeEvaluationOptions evalOptions) : base(valueNodeFactory, dynamicViewName, valueExpression + ", " + PredefinedFormatSpecifiers.DynamicView, default, evalOptions) { this.valueNodeProviderFactory = valueNodeProviderFactory; this.instanceValue = instanceValue; this.expectedType = expectedType; this.valueExpression = valueExpression; + this.addParens = addParens; this.appDomain = appDomain; dynamicViewProxyExpression = string.Empty; } @@ -101,7 +103,7 @@ string GetRequiredAssemblyFilename(DbgRuntime runtime) => "Microsoft.CSharp.dll"; protected override (DbgDotNetValueNode node, bool canHide) CreateValueNode(DbgEvaluationInfo evalInfo, int index, DbgValueNodeEvaluationOptions options, ReadOnlyCollection? formatSpecifiers) => - CreateValueNode(evalInfo, false, getDynamicViewValue!.Type, getDynamicViewValue, index, options, dynamicViewProxyExpression, formatSpecifiers); + CreateValueNode(evalInfo, addParens, getDynamicViewValue!.Type, getDynamicViewValue, index, options, dynamicViewProxyExpression, formatSpecifiers); protected override (DbgDotNetValueNode? node, bool canHide) TryCreateInstanceValueNode(DbgEvaluationInfo evalInfo, DbgDotNetValueResult valueResult) { var noResultsNode = DebugViewNoResultsValueNode.TryCreate(evalInfo, Expression, valueResult); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ResultsViewMembersValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ResultsViewMembersValueNodeProvider.cs index 604990a7cc..d870d15bc9 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ResultsViewMembersValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/ResultsViewMembersValueNodeProvider.cs @@ -44,16 +44,18 @@ sealed class ResultsViewMembersValueNodeProvider : MembersValueNodeProvider { readonly DbgDotNetValue instanceValue; readonly DmdType expectedType; readonly string valueExpression; + readonly bool addParens; string resultsViewProxyExpression; DbgDotNetValue? getResultsViewValue; - public ResultsViewMembersValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, LanguageValueNodeFactory valueNodeFactory, DmdType enumerableType, DbgDotNetValue instanceValue, DmdType expectedType, string valueExpression, DbgValueNodeEvaluationOptions evalOptions) + public ResultsViewMembersValueNodeProvider(DbgDotNetValueNodeProviderFactory valueNodeProviderFactory, LanguageValueNodeFactory valueNodeFactory, DmdType enumerableType, DbgDotNetValue instanceValue, DmdType expectedType, string valueExpression, bool addParens, DbgValueNodeEvaluationOptions evalOptions) : base(valueNodeFactory, resultsViewName, valueExpression + ", " + PredefinedFormatSpecifiers.ResultsView, default, evalOptions) { this.valueNodeProviderFactory = valueNodeProviderFactory; this.enumerableType = enumerableType; this.instanceValue = instanceValue; this.expectedType = expectedType; this.valueExpression = valueExpression; + this.addParens = addParens; resultsViewProxyExpression = string.Empty; } @@ -171,7 +173,7 @@ string GetRequiredAssemblyFilename(DbgRuntime runtime) { } protected override (DbgDotNetValueNode node, bool canHide) CreateValueNode(DbgEvaluationInfo evalInfo, int index, DbgValueNodeEvaluationOptions options, ReadOnlyCollection? formatSpecifiers) => - CreateValueNode(evalInfo, false, getResultsViewValue!.Type, getResultsViewValue, index, options, resultsViewProxyExpression, formatSpecifiers); + CreateValueNode(evalInfo, addParens, getResultsViewValue!.Type, getResultsViewValue, index, options, resultsViewProxyExpression, formatSpecifiers); protected override (DbgDotNetValueNode? node, bool canHide) TryCreateInstanceValueNode(DbgEvaluationInfo evalInfo, DbgDotNetValueResult valueResult) { var noResultsNode = DebugViewNoResultsValueNode.TryCreate(evalInfo, Expression, valueResult); From fde84b38d78376d2f7751916e31d8900ae0ba1dd Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 25 Jun 2023 18:50:13 +0200 Subject: [PATCH 013/122] Add support for `ref` types in the C# decompiler --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- .../CSharp/CSharpDecompiler.cs | 12 ++++++------ .../VisualBasic/VBDecompiler.cs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 003b2effe4..ee81b7a22d 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 003b2effe4067eed5c8c58f0fcedd09a1995f15d +Subproject commit ee81b7a22d580c664ff61c0fbd2e6c33bf5719c6 diff --git a/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/CSharp/CSharpDecompiler.cs b/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/CSharp/CSharpDecompiler.cs index 55b949f43e..92a2472011 100644 --- a/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/CSharp/CSharpDecompiler.cs +++ b/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/CSharp/CSharpDecompiler.cs @@ -1,14 +1,14 @@ // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE @@ -250,7 +250,7 @@ void RunTransformsAndGenerateCode(ref BuilderState state, IDecompilerOutput outp astBuilder.GenerateCode(output); } - internal static void AddXmlDocumentation(ref BuilderState state, DecompilerSettings settings, AstBuilder astBuilder) { + internal static void AddXmlDocumentation(ref BuilderState state, DecompilerSettings settings, AstBuilder astBuilder) { if (settings.ShowXmlDocumentation) { var module = state.AstBuilder.Context.CurrentModule; var hasXmlDocFileTmp = state.State.HasXmlDocFile(module); @@ -358,8 +358,8 @@ void TypeToString(IDecompilerOutput output, ConvertTypeOptions options, ITypeDef AstType astType = AstBuilder.ConvertType(type, new StringBuilder(), typeAttributes, options); if (WriteRefIfByRef(output, type.TryGetByRefSig(), typeAttributes as ParamDef)) { - if (astType is ComposedType && ((ComposedType)astType).PointerRank > 0) - ((ComposedType)astType).PointerRank--; + if (astType is ComposedType composedType && composedType.HasRefSpecifier) + composedType.HasRefSpecifier = false; } var ctx = new DecompilerContext(langSettings.Settings.SettingsVersion, type.Module, MetadataTextColorProvider); diff --git a/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/VisualBasic/VBDecompiler.cs b/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/VisualBasic/VBDecompiler.cs index 0bc50c71ce..0bb5c2c45a 100644 --- a/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/VisualBasic/VBDecompiler.cs +++ b/Extensions/ILSpy.Decompiler/dnSpy.Decompiler.ILSpy.Core/VisualBasic/VBDecompiler.cs @@ -254,8 +254,8 @@ void TypeToString(IDecompilerOutput output, ConvertTypeOptions options, ITypeDef if (type.TryGetByRefSig() is not null) { output.Write("ByRef", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); - if (astType is ICSharpCode.NRefactory.CSharp.ComposedType && ((ICSharpCode.NRefactory.CSharp.ComposedType)astType).PointerRank > 0) - ((ICSharpCode.NRefactory.CSharp.ComposedType)astType).PointerRank--; + if (astType is ComposedType composedType && composedType.HasRefSpecifier) + composedType.HasRefSpecifier = false; } var vbAstType = astType.AcceptVisitor(converter, null); From fcede3e83a479ab5f67d77bbdea7b8ba78b24e55 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 5 Jul 2023 12:17:34 +0200 Subject: [PATCH 014/122] Update `CustomAttributeType` coded token table --- dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/CodedToken.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/CodedToken.cs b/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/CodedToken.cs index 93f929ead3..7d3a1339ac 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/CodedToken.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/CodedToken.cs @@ -84,8 +84,8 @@ public sealed class CodedToken { }); /// CustomAttributeType coded token - public static readonly CodedToken CustomAttributeType = new CodedToken(3, new Table[4] { - 0, 0, Table.Method, Table.MemberRef, + public static readonly CodedToken CustomAttributeType = new CodedToken(3, new Table[5] { + 0, 0, Table.Method, Table.MemberRef, 0, }); /// ResolutionScope coded token From 19a3d8427b65cce3aeaed22df4f59c1f9248d425 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 5 Jul 2023 13:02:09 +0200 Subject: [PATCH 015/122] Assembly editor support for `null` ResolutionScope --- Extensions/dnSpy.AsmEditor/Compiler/MDEditorPatcher.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Extensions/dnSpy.AsmEditor/Compiler/MDEditorPatcher.cs b/Extensions/dnSpy.AsmEditor/Compiler/MDEditorPatcher.cs index d826eeea6f..accd93594a 100644 --- a/Extensions/dnSpy.AsmEditor/Compiler/MDEditorPatcher.cs +++ b/Extensions/dnSpy.AsmEditor/Compiler/MDEditorPatcher.cs @@ -162,6 +162,11 @@ void PatchTypeRefsToEditedType(TypeDef nonNestedEditedType) { bool CheckResolutionScopeIsSameModule(MDToken resolutionScope, ModuleDef module) { switch (resolutionScope.Table) { case Table.Module: + // ECMA II.22.38 states that in this case we should check ExportedTypes of the current module. + // The CLR however checks both TypeDefs and ExportedTypes. + if (resolutionScope.IsNull) + return true; + return resolutionScope.Rid == 1; case Table.ModuleRef: From dd6544334f5134434df12000491aeeefbdfccb4a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 5 Jul 2023 14:06:13 +0200 Subject: [PATCH 016/122] Add support for `#JTD` stream for EnC metadata in Hex editor --- .../Hex/Files/DotNet/DotNetHeap.cs | 7 ++++++- .../Hex/Files/DotNet/DotNetHeapsReader.cs | 9 +++++++- .../Hex/Files/DotNet/DotNetTableSizes.cs | 21 +++++++++++-------- .../dnSpy/Hex/Files/DotNet/TablesHeapImpl.cs | 7 ++++++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/DotNetHeap.cs b/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/DotNetHeap.cs index cd5b3c4341..918798774f 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/DotNetHeap.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Hex/Files/DotNet/DotNetHeap.cs @@ -54,7 +54,7 @@ protected DotNetHeap(HexBufferSpan span, DotNetHeapKind heapKind) { public abstract DotNetMetadataHeaders Metadata { get; } /// - /// Gets a structure or null + /// Gets a structure or null /// /// Position /// @@ -137,6 +137,11 @@ public abstract class TablesHeap : DotNetHeap { /// public abstract ulong SortedMask { get; } + /// + /// All columns that can be 2 or 4 bytes are forced to be 4 bytes. + /// + public abstract bool ForceAllBigColumns { get; } + /// /// Constructor /// diff --git a/dnSpy/dnSpy/Hex/Files/DotNet/DotNetHeapsReader.cs b/dnSpy/dnSpy/Hex/Files/DotNet/DotNetHeapsReader.cs index 5b61dad576..8fbed8034b 100644 --- a/dnSpy/dnSpy/Hex/Files/DotNet/DotNetHeapsReader.cs +++ b/dnSpy/dnSpy/Hex/Files/DotNet/DotNetHeapsReader.cs @@ -129,7 +129,8 @@ DotNetHeap[] CreateENCHeaps() { USHeap? usHeap = null; BlobHeap? blobHeap = null; GUIDHeap? guidHeap = null; - TablesHeap? tablesHeap = null; + TablesHeapImpl? tablesHeap = null; + bool forceAllBig = false; foreach (var ssh in storageStreamHeaders) { var span = new HexBufferSpan(file.Buffer, ssh.DataSpan); @@ -166,6 +167,11 @@ DotNetHeap[] CreateENCHeaps() { } break; + case "#JTD": + forceAllBig = true; + list.Add(new UnknownHeapImpl(span)); + continue; + case "#~": // Only if #Schema is used case "#-": if (tablesHeap is null && span.Length >= TablesHeapImpl.MinimumSize) { @@ -177,6 +183,7 @@ DotNetHeap[] CreateENCHeaps() { } list.Add(new UnknownHeapImpl(span)); } + tablesHeap?.SetForceAllBigColumns(forceAllBig); return list.ToArray(); } diff --git a/dnSpy/dnSpy/Hex/Files/DotNet/DotNetTableSizes.cs b/dnSpy/dnSpy/Hex/Files/DotNet/DotNetTableSizes.cs index aaaa562423..4aabc63fbb 100644 --- a/dnSpy/dnSpy/Hex/Files/DotNet/DotNetTableSizes.cs +++ b/dnSpy/dnSpy/Hex/Files/DotNet/DotNetTableSizes.cs @@ -31,6 +31,7 @@ sealed class DotNetTableSizes { bool bigStrings; bool bigGuid; bool bigBlob; + bool forceAllBig; uint[]? rowCounts; TableInfo[]? tableInfos; @@ -41,11 +42,13 @@ sealed class DotNetTableSizes { /// true if #GUID size >= 0x10000 /// true if #Blob size >= 0x10000 /// Count of rows in each table - public void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, uint[] rowCounts) { + /// Force all columns to 4 bytes instead of 2 or 4 bytes + public void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, uint[] rowCounts, bool forceAllBig) { Debug2.Assert(tableInfos is not null); - this.bigStrings = bigStrings; - this.bigGuid = bigGuid; - this.bigBlob = bigBlob; + this.bigStrings = bigStrings || forceAllBig; + this.bigGuid = bigGuid || forceAllBig; + this.bigBlob = bigBlob || forceAllBig; + this.forceAllBig = forceAllBig; this.rowCounts = rowCounts; for (int i = 0; i < tableInfos.Length; i++) { var tableInfo = tableInfos[i]; @@ -67,7 +70,7 @@ int GetSize(ColumnSize columnSize) { if (ColumnSize.Module <= columnSize && columnSize <= ColumnSize.CustomDebugInformation) { int table = (int)(columnSize - ColumnSize.Module); uint count = table >= rowCounts.Length ? 0 : rowCounts[table]; - return count > 0xFFFF ? 4 : 2; + return forceAllBig || count > 0xFFFF ? 4 : 2; } else if (ColumnSize.TypeDefOrRef <= columnSize && columnSize <= ColumnSize.HasCustomDebugInformation) { CodedToken info; @@ -97,7 +100,7 @@ int GetSize(ColumnSize columnSize) { } // Can't overflow since maxRows <= 0x00FFFFFF and info.Bits < 8 uint finalRows = maxRows << info.Bits; - return finalRows > 0xFFFF ? 4 : 2; + return forceAllBig || finalRows > 0xFFFF ? 4 : 2; } else { switch (columnSize) { @@ -106,9 +109,9 @@ int GetSize(ColumnSize columnSize) { case ColumnSize.UInt16: return 2; case ColumnSize.Int32: return 4; case ColumnSize.UInt32: return 4; - case ColumnSize.Strings:return bigStrings ? 4 : 2; - case ColumnSize.GUID: return bigGuid ? 4 : 2; - case ColumnSize.Blob: return bigBlob ? 4 : 2; + case ColumnSize.Strings:return forceAllBig || bigStrings ? 4 : 2; + case ColumnSize.GUID: return forceAllBig || bigGuid ? 4 : 2; + case ColumnSize.Blob: return forceAllBig || bigBlob ? 4 : 2; } } throw new InvalidOperationException($"Invalid ColumnSize: {columnSize}"); diff --git a/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeapImpl.cs b/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeapImpl.cs index ef0f6fba07..fe395acfc7 100644 --- a/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeapImpl.cs +++ b/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeapImpl.cs @@ -102,6 +102,10 @@ public override ulong SortedMask { } } + public override bool ForceAllBigColumns => forceAllBigColumns; + + internal void SetForceAllBigColumns(bool value) => forceAllBigColumns = value; + bool initialized; HexSpan headerSpan; HexSpan tablesSpan; @@ -114,6 +118,7 @@ public override ulong SortedMask { byte log2Rid; ulong validMask; ulong sortedMask; + bool forceAllBigColumns; uint extraData; MDTable[]? mdTables; ReadOnlyCollection? mdTablesReadOnly; @@ -177,7 +182,7 @@ void Initialize() { headerSpan = HexSpan.FromBounds(Span.Span.Start, pos); - dnTableSizes.InitializeSizes((flags & MDStreamFlags.BigStrings) != 0, (flags & MDStreamFlags.BigGUID) != 0, (flags & MDStreamFlags.BigBlob) != 0, sizes); + dnTableSizes.InitializeSizes((flags & MDStreamFlags.BigStrings) != 0, (flags & MDStreamFlags.BigGUID) != 0, (flags & MDStreamFlags.BigBlob) != 0, sizes, forceAllBigColumns); mdTables = new MDTable[tableInfos.Length]; mdTablesReadOnly = new ReadOnlyCollection(mdTables); tableRecordDataFactories = new TableRecordDataFactory[tableInfos.Length]; From 3a6188b3f63edd8e80b7c5bdee83ee31129fe735 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 6 Jul 2023 14:14:24 +0200 Subject: [PATCH 017/122] Use `MDToken` type when working with metadata tokens --- .../Hex/Nodes/MetadataTableNode.cs | 2 +- .../dnSpy.AsmEditor/Hex/Nodes/PENode.cs | 2 +- .../Hex/Nodes/TablesStreamNode.cs | 2 +- .../Impl/CorDebugTypeCreator.cs | 4 +- .../Impl/DbgEngineImpl.cs | 3 +- .../Impl/DmdDynamicModuleHelperImpl.cs | 8 +- .../Metadata/DbgDynamicModuleProviderImpl.cs | 8 +- .../dndbg/DotNet/CorMethodDef.cs | 4 +- .../dndbg/DotNet/CorModuleDef.cs | 8 +- .../dndbg/DotNet/CorTypeDef.cs | 12 +- .../dndbg/Engine/BreakProcessHelper.cs | 22 ++-- .../dndbg/Engine/CorModuleDefHelper.cs | 9 +- .../Impl/COMD/DmdComMetadataReader.cs | 119 +++++++++--------- .../Impl/COMD/DmdConstructorDefCOMD.cs | 7 +- .../Impl/COMD/DmdEventDefCOMD.cs | 9 +- .../Impl/COMD/DmdExportedTypeCOMD.cs | 21 ++-- .../Impl/COMD/DmdFieldDefCOMD.cs | 7 +- .../Impl/COMD/DmdGenericParameterTypeCOMD.cs | 2 +- .../Impl/COMD/DmdMethodDefCOMD.cs | 13 +- .../Impl/COMD/DmdPropertyDefCOMD.cs | 12 +- .../Impl/COMD/DmdTypeDefCOMD.cs | 24 ++-- .../Impl/COMD/DmdTypeRefCOMD.cs | 22 ++-- .../Impl/COMD/MDAPI.cs | 4 +- .../Impl/DmdConstructorDef.cs | 4 +- .../Impl/DmdEventDef.cs | 4 +- .../Impl/DmdFieldDef.cs | 4 +- .../Impl/DmdGenericParameterType.cs | 4 +- .../Impl/DmdMetadataReaderBase.cs | 106 ++++++++-------- .../Impl/DmdMethodBodyReader.cs | 4 +- .../Impl/DmdMethodDef.cs | 4 +- .../Impl/DmdParameterDef.cs | 4 +- .../Impl/DmdPropertyDef.cs | 4 +- .../Impl/DmdSignatureReader.cs | 9 +- .../Impl/DmdTypeDef.cs | 4 +- .../Impl/DmdTypeRef.cs | 3 +- .../Impl/MD/DmdEcma335MetadataReader.cs | 28 ++--- .../Impl/MD/DmdEventDefMD.cs | 2 +- .../Impl/MD/DmdExportedTypeMD.cs | 14 ++- .../Impl/MD/DmdPropertyDefMD.cs | 2 +- .../Impl/MD/DmdTypeDefMD.cs | 5 +- .../Impl/MD/DmdTypeRefMD.cs | 16 +-- .../Impl/ReflectionTests.cs | 12 +- .../Impl/DbgEngineImpl.Breakpoints.cs | 5 +- .../Impl/Evaluation/MonoDebugTypeCreator.cs | 2 +- dnSpy/dnSpy/Hex/Commands/GoToMetadataVM.cs | 4 +- .../Hex/Commands/HexCommandOperations.cs | 4 +- dnSpy/dnSpy/MainApp/CachedMefInfo.cs | 4 +- 47 files changed, 316 insertions(+), 260 deletions(-) diff --git a/Extensions/dnSpy.AsmEditor/Hex/Nodes/MetadataTableNode.cs b/Extensions/dnSpy.AsmEditor/Hex/Nodes/MetadataTableNode.cs index bf838de6ee..5aa349bf68 100644 --- a/Extensions/dnSpy.AsmEditor/Hex/Nodes/MetadataTableNode.cs +++ b/Extensions/dnSpy.AsmEditor/Hex/Nodes/MetadataTableNode.cs @@ -155,7 +155,7 @@ public override void OnBufferChanged(NormalizedHexChangeCollection changes) { } public MetadataTableRecordNode? FindTokenNode(uint token) { - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); if (rid - 1 >= MetadataTableVM.Rows) return null; TreeNode.EnsureChildrenLoaded(); diff --git a/Extensions/dnSpy.AsmEditor/Hex/Nodes/PENode.cs b/Extensions/dnSpy.AsmEditor/Hex/Nodes/PENode.cs index f19b805a9c..e8ab345890 100644 --- a/Extensions/dnSpy.AsmEditor/Hex/Nodes/PENode.cs +++ b/Extensions/dnSpy.AsmEditor/Hex/Nodes/PENode.cs @@ -128,7 +128,7 @@ public bool Decompile(IDecompileNodeContext context) { } public MetadataTableRecordNode? FindTokenNode(uint token) { - if ((token & 0x00FFFFFF) == 0) + if (MDToken.ToRID(token) == 0) return null; TreeNode.EnsureChildrenLoaded(); var stgStreamNode = (StorageStreamNode?)TreeNode.DataChildren.FirstOrDefault(a => a is StorageStreamNode && ((StorageStreamNode)a).HeapKind == DotNetHeapKind.Tables); diff --git a/Extensions/dnSpy.AsmEditor/Hex/Nodes/TablesStreamNode.cs b/Extensions/dnSpy.AsmEditor/Hex/Nodes/TablesStreamNode.cs index e28a12025c..021429d638 100644 --- a/Extensions/dnSpy.AsmEditor/Hex/Nodes/TablesStreamNode.cs +++ b/Extensions/dnSpy.AsmEditor/Hex/Nodes/TablesStreamNode.cs @@ -72,7 +72,7 @@ protected override void WriteCore(ITextColorWriter output, DocumentNodeWriteOpti output.Write(BoxedTextColor.HexTablesStream, dnSpy_AsmEditor_Resources.HexNode_TablesStream); public MetadataTableRecordNode? FindTokenNode(uint token) { - var mdTblNode = (MetadataTableNode?)TreeNode.DataChildren.FirstOrDefault(a => ((MetadataTableNode)a).TableInfo.Table == (Table)(token >> 24)); + var mdTblNode = (MetadataTableNode?)TreeNode.DataChildren.FirstOrDefault(a => ((MetadataTableNode)a).TableInfo.Table == MDToken.ToTable(token)); return mdTblNode?.FindTokenNode(token); } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/CorDebugTypeCreator.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/CorDebugTypeCreator.cs index 25a8c0e476..1af3da1704 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/CorDebugTypeCreator.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/CorDebugTypeCreator.cs @@ -22,6 +22,8 @@ You should have received a copy of the GNU General Public License using System.Diagnostics; using dndbg.COM.CorDebug; using dndbg.Engine; +using dnlib.DotNet; +using dnlib.DotNet.MD; using dnSpy.Debugger.DotNet.Metadata; namespace dnSpy.Debugger.DotNet.CorDebug.Impl { @@ -59,7 +61,7 @@ public CorType Create(DmdType type) { case DmdTypeSignatureKind.Type: if (!engine.TryGetDnModule(type.Module.GetDebuggerModule() ?? throw new InvalidOperationException(), out dnModule)) throw new InvalidOperationException(); - Debug.Assert((type.MetadataToken >> 24) == 0x02); + Debug.Assert(MDToken.ToTable(type.MetadataToken) == Table.TypeDef); result = dnModule.CorModule.GetClassFromToken((uint)type.MetadataToken)?.GetParameterizedType(type.IsValueType ? CorElementType.ValueType : CorElementType.Class); break; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DbgEngineImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DbgEngineImpl.cs index 8616c5262b..cbef84a1ef 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DbgEngineImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DbgEngineImpl.cs @@ -29,6 +29,7 @@ You should have received a copy of the GNU General Public License using dndbg.COM.MetaData; using dndbg.DotNet; using dndbg.Engine; +using dnlib.DotNet; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.DotNet.Code; using dnSpy.Contracts.Debugger.DotNet.CorDebug; @@ -263,7 +264,7 @@ internal DmdDynamicModuleHelperImpl GetDynamicModuleHelper(DnModule dnModule) { var mdi = exactType.GetMetaDataImport(out uint token); var list = new List(4); - while ((token & 0x00FFFFFF) != 0) { + while (MDToken.ToRID(token) != 0) { var name = MDAPI.GetTypeDefName(mdi, token); if (name is null) break; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DmdDynamicModuleHelperImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DmdDynamicModuleHelperImpl.cs index 45cd4b7a8b..dca4948c35 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DmdDynamicModuleHelperImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Impl/DmdDynamicModuleHelperImpl.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU General Public License using System.Diagnostics; using dndbg.DotNet; using dndbg.Engine; +using dnlib.DotNet; using dnSpy.Debugger.DotNet.Metadata; namespace dnSpy.Debugger.DotNet.CorDebug.Impl { @@ -94,7 +95,8 @@ public override void Dispose() { } // it's the 1-byte or fat header. reader = new ProcessBinaryReader(new CorProcessReader(dnModule.Process), 0); uint locVarSigTok = func.LocalVarSigToken; - bool isBig = codeSize >= 0x40 || (locVarSigTok & 0x00FFFFFF) != 0; + uint locVarSigRid = MDToken.ToRID(locVarSigTok); + bool isBig = codeSize >= 0x40 || locVarSigRid != 0; if (!isBig) { reader.Position = (long)addr - 1; byte b = reader.ReadByte(); @@ -108,8 +110,8 @@ public override void Dispose() { } uint headerCodeSize = reader.ReadUInt32(); uint headerLocVarSigTok = reader.ReadUInt32(); bool valid = headerCodeSize == codeSize && - (locVarSigTok & 0x00FFFFFF) == (headerLocVarSigTok & 0x00FFFFFF) && - ((locVarSigTok & 0x00FFFFFF) == 0 || locVarSigTok == headerLocVarSigTok); + locVarSigRid == MDToken.ToRID(headerLocVarSigTok) && + (locVarSigRid == 0 || locVarSigTok == headerLocVarSigTok); Debug.Assert(valid); if (!valid) return null; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Metadata/DbgDynamicModuleProviderImpl.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Metadata/DbgDynamicModuleProviderImpl.cs index 0dcbed1ce6..4b5a60550b 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Metadata/DbgDynamicModuleProviderImpl.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/Metadata/DbgDynamicModuleProviderImpl.cs @@ -110,19 +110,17 @@ public override IEnumerable GetModifiedTypes(DbgModule module) { if (oldLastValid.Equals(lastValid)) return hash; - const uint TYPEDEF_TOKEN = 0x02000000; - // Optimization if we loaded a big file if (oldLastValid.TypeDefRid == 0) { for (uint rid = 1; rid <= lastValid.TypeDefRid; rid++) - hash.Add(TYPEDEF_TOKEN + rid); + hash.Add(new MDToken(Table.TypeDef, rid).Raw); return hash; } var methodRids = new HashSet(); var gpRids = new HashSet(); for (uint rid = oldLastValid.TypeDefRid + 1; rid <= lastValid.TypeDefRid; rid++) - hash.Add(TYPEDEF_TOKEN + rid); + hash.Add(new MDToken(Table.TypeDef, rid).Raw); for (uint rid = oldLastValid.FieldRid + 1; rid <= lastValid.FieldRid; rid++) { var typeOwner = cmod.GetFieldOwnerToken(rid); if (typeOwner.Rid != 0) @@ -197,7 +195,7 @@ public override void InitializeNonLoadedClasses(DbgModule module, uint[] nonLoad if (cmod is null) return; foreach (uint token in nonLoadedTokens) - cmod.ForceInitializeTypeDef(token & 0x00FFFFFF); + cmod.ForceInitializeTypeDef(MDToken.ToRID(token)); } LastValidRids UpdateLastValidRids(DynamicModuleData data, CorModuleDef module) { diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorMethodDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorMethodDef.cs index 990f76bb18..5e218555a3 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorMethodDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorMethodDef.cs @@ -149,7 +149,7 @@ void InitParamDefs_NoLock() { var itemTokens = MDAPI.GetParamTokens(mdi, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorParamDef(readerModule, itemRid, this), cmd => cmd.Initialize()); } @@ -172,7 +172,7 @@ void InitGenericParams_NoLock() { var itemTokens = MDAPI.GetGenericParamTokens(mdi2, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorGenericParam(readerModule, itemRid, this), cmd => cmd.Initialize()); } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorModuleDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorModuleDef.cs index 46e768cbaa..96d1bcadd0 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorModuleDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorModuleDef.cs @@ -234,7 +234,7 @@ public void Initialize() { } public bool IsValidToken(uint token) { - if ((token & 0x00FFFFFF) == 0) + if (MDToken.ToRID(token) == 0) return false; return MDAPI.IsValidToken(mdi, token); } @@ -979,7 +979,7 @@ void InitializeTypeTables() { void UpdateTypeTables(uint[] tokens) { Array.Sort(tokens); foreach (uint token in tokens) { - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); Debug.Assert(rid != 0); Debug.Assert(!ridToNested.ContainsKey(rid)); @@ -1016,7 +1016,7 @@ void UpdateTypeTables(CorTypeDef type) { foreach (var token in tokens) { bool b; CorTypeDef td; - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); if (token == type.OriginalToken.Raw) td = type; else { @@ -1056,7 +1056,7 @@ uint[] GetNewTokens(uint rid) { break; if (rid == 0 || !hash.Add(rid)) break; - rid = MDAPI.GetTypeDefEnclosingType(mdi, new MDToken(Table.TypeDef, rid).Raw) & 0x00FFFFFF; + rid = MDToken.ToRID(MDAPI.GetTypeDefEnclosingType(mdi, new MDToken(Table.TypeDef, rid).Raw)); } var tokens = new uint[hash.Count]; int i = 0; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorTypeDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorTypeDef.cs index 888fcb29d2..cca73d7885 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorTypeDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/DotNet/CorTypeDef.cs @@ -106,7 +106,7 @@ unsafe Dictionary CalculateFieldOffsets() { if (fieldOffsets is not null) { foreach (var fo in fieldOffsets) { if (fo.Offset != uint.MaxValue) - fieldRidToFieldOffset[fo.FieldToken & 0x00FFFFFF] = fo.Offset; + fieldRidToFieldOffset[MDToken.ToRID(fo.FieldToken)] = fo.Offset; } } return fieldRidToFieldOffset; @@ -140,7 +140,7 @@ void InitFields_NoLock() { var itemTokens = MDAPI.GetFieldTokens(mdi, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorFieldDef(readerModule, itemRid, this), cmd => cmd.Initialize()); } @@ -163,7 +163,7 @@ void InitMethods_NoLock() { var itemTokens = MDAPI.GetMethodTokens(mdi, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorMethodDef(readerModule, itemRid, this), cmd => cmd.Initialize()); } @@ -186,7 +186,7 @@ void InitGenericParams_NoLock() { var itemTokens = MDAPI.GetGenericParamTokens(mdi2, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorGenericParam(readerModule, itemRid, this), cmd => cmd.Initialize()); } @@ -209,7 +209,7 @@ void InitProperties_NoLock() { var itemTokens = MDAPI.GetPropertyTokens(mdi, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorPropertyDef(readerModule, itemRid, this), cmd => cmd.Initialize()); } @@ -232,7 +232,7 @@ void InitEvents_NoLock() { var itemTokens = MDAPI.GetEventTokens(mdi, token); var newItems = new MemberInfo[itemTokens.Length]; for (int i = 0; i < itemTokens.Length; i++) { - uint itemRid = itemTokens[i] & 0x00FFFFFF; + uint itemRid = MDToken.ToRID(itemTokens[i]); newItems[i] = readerModule.Register(new CorEventDef(readerModule, itemRid, this), cmd => cmd.Initialize()); } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/BreakProcessHelper.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/BreakProcessHelper.cs index ad62c626e1..c9bb352867 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/BreakProcessHelper.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/BreakProcessHelper.cs @@ -68,24 +68,22 @@ bool OnLoadModule(DebugEventBreakpointConditionContext ctx) { if (mod is null || mod.IsDynamic || mod.IsInMemory) return false; - uint memberToken = 0; + uint methodToken = 0; if (type == BreakProcessKind.ModuleCctorOrEntryPoint) - memberToken = GetGlobalStaticConstructor(mod.GetMetaDataInterface()); - - if (memberToken == 0) { - var filename = mod.Name; - uint epToken = GetEntryPointToken(filename); - if ((Table)(epToken >> 24) != Table.Method || (epToken & 0x00FFFFFF) == 0) - return false; - memberToken = epToken; - } + methodToken = GetGlobalStaticConstructor(mod.GetMetaDataInterface()); + + if (methodToken == 0) + methodToken = GetEntryPointToken(mod.Name); + + if (MDToken.ToTable(methodToken) != Table.Method || MDToken.ToRID(methodToken) == 0) + return false; debugger.RemoveBreakpoint(breakpoint!); breakpoint = null; Debug.Assert(!mod.IsDynamic && !mod.IsInMemory); // It's not a dyn/in-mem module so id isn't used var moduleId = mod.GetModuleId(uint.MaxValue); - SetILBreakpoint(moduleId, memberToken); + SetILBreakpoint(moduleId, methodToken); return false; } @@ -102,7 +100,7 @@ static uint GetEntryPointToken(string? filename) { if ((cor20Header.Flags & ComImageFlags.NativeEntryPoint) != 0) return 0; uint token = cor20Header.EntryPointToken_or_RVA; - if ((Table)(token >> 24) == Table.Method && (token & 0x00FFFFFF) != 0) + if (MDToken.ToTable(token) == Table.Method && MDToken.ToRID(token) != 0) return token; } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/CorModuleDefHelper.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/CorModuleDefHelper.cs index 7bcb1df0e5..ed47f60be9 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/CorModuleDefHelper.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.CorDebug/dndbg/Engine/CorModuleDefHelper.cs @@ -103,7 +103,8 @@ public bool TryCreateBodyReader(uint bodyRva, uint mdToken, out DataReader reade // it's the 1-byte or fat header. var procReader = new ProcessBinaryReader(new CorProcessReader(module.Process), 0); uint locVarSigTok = func.LocalVarSigToken; - bool isBig = codeSize >= 0x40 || (locVarSigTok & 0x00FFFFFF) != 0; + uint locVarSigRid = MDToken.ToRID(locVarSigTok); + bool isBig = codeSize >= 0x40 || locVarSigRid != 0; if (!isBig) { procReader.Position = (long)addr - 1; byte b = procReader.ReadByte(); @@ -117,8 +118,8 @@ public bool TryCreateBodyReader(uint bodyRva, uint mdToken, out DataReader reade uint headerCodeSize = procReader.ReadUInt32(); uint headerLocVarSigTok = procReader.ReadUInt32(); bool valid = headerCodeSize == codeSize && - (locVarSigTok & 0x00FFFFFF) == (headerLocVarSigTok & 0x00FFFFFF) && - ((locVarSigTok & 0x00FFFFFF) == 0 || locVarSigTok == headerLocVarSigTok); + locVarSigRid == MDToken.ToRID(headerLocVarSigTok) && + (locVarSigRid == 0 || locVarSigTok == headerLocVarSigTok); Debug.Assert(valid); if (!valid) return false; @@ -197,7 +198,7 @@ ImageSectionHeader[] GetOrCreateSectionHeaders() { public bool TryCreateResourceStream(uint offset, [NotNullWhen(true)] out DataReaderFactory? dataReaderFactory, out uint resourceOffset, out uint resourceLength) { if (module.IsDynamic) { - //TODO: + //TODO: dataReaderFactory = null; resourceOffset = 0; resourceLength = 0; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdComMetadataReader.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdComMetadataReader.cs index c1e8d70f22..338e4d6da2 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdComMetadataReader.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdComMetadataReader.cs @@ -174,23 +174,25 @@ T COMThread(Func action) { return dispatcher.Invoke(action); } + bool IsValidToken(Table table, uint rid) => MDAPI.IsValidToken(MetaDataImport, new MDToken(table, rid).Raw); + DmdTypeRefCOMD? TryCreateTypeRefCOMD_COMThread(uint rid) { dispatcher.VerifyAccess(); - if (!MDAPI.IsValidToken(MetaDataImport, 0x01000000 + rid)) + if (!IsValidToken(Table.TypeRef, rid)) return null; return new DmdTypeRefCOMD(this, rid, null); } DmdTypeDefCOMD? TryCreateTypeDefCOMD_COMThread(uint rid) { dispatcher.VerifyAccess(); - if (!MDAPI.IsValidToken(MetaDataImport, 0x02000000 + rid)) + if (!IsValidToken(Table.TypeDef, rid)) return null; return new DmdTypeDefCOMD(this, rid, null); } DmdExportedTypeCOMD? TryCreateExportedTypeCOMD_COMThread(uint rid) { dispatcher.VerifyAccess(); - if (!MDAPI.IsValidToken(MetaDataImport, 0x27000000 + rid)) + if (!IsValidToken(Table.ExportedType, rid)) return null; return new DmdExportedTypeCOMD(this, rid, null); } @@ -231,7 +233,7 @@ void UpdateTypeTables_COMThread(uint[] tokens) { Debug2.Assert(ridToEnclosing is not null); Array.Sort(tokens); foreach (uint token in tokens) { - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); Debug.Assert(rid != 0); Debug.Assert(!ridToNested.ContainsKey(rid)); @@ -260,20 +262,20 @@ void UpdateTypeTables_COMThread(uint[] tokens) { void DmdDynamicModuleHelper_TypeLoaded_COMThread(object? sender, DmdTypeLoadedEventArgs e) { dispatcher.VerifyAccess(); - bool b = (e.MetadataToken >> 24) == 0x02 && (e.MetadataToken & 0x00FFFFFF) != 0 && MDAPI.IsValidToken(MetaDataImport, (uint)e.MetadataToken); + var mdToken = new MDToken(e.MetadataToken); + bool b = mdToken.Table == Table.TypeDef && mdToken.Rid != 0 && MDAPI.IsValidToken(MetaDataImport, mdToken.Raw); Debug.Assert(b); if (!b) return; if (!module.IsDynamic) return; - uint typeToken = (uint)e.MetadataToken; uint[] newTokens; if (ridToNested is not null) - newTokens = UpdateTypeTables_COMThread(typeToken); + newTokens = UpdateTypeTables_COMThread(mdToken.Raw); else - newTokens = GetNewTokens_COMThread(typeToken); - typeDefList.TryGet(typeToken)?.DynamicType_InvalidateCachedMembers(); + newTokens = GetNewTokens_COMThread(mdToken.Raw); + typeDefList.TryGet(mdToken.Raw)?.DynamicType_InvalidateCachedMembers(); TypesUpdated?.Invoke(this, new DmdTypesUpdatedEventArgs(newTokens)); } @@ -282,18 +284,18 @@ void DmdDynamicModuleHelper_TypeLoaded_COMThread(object? sender, DmdTypeLoadedEv uint[] UpdateTypeTables_COMThread(uint typeToken) { dispatcher.VerifyAccess(); - uint typeRid = typeToken & 0x00FFFFFF; + uint typeRid = MDToken.ToRID(typeToken); bool b = ridToEnclosing is not null && !ridToEnclosing.ContainsKey(typeRid); Debug.Assert(b); if (!b) return new[] { typeToken }; Debug2.Assert(ridToEnclosing is not null); - var tokens = GetNewTokens_COMThread(typeRid); + var tokens = GetNewTokens_COMThread(typeToken); UpdateTypeTables_COMThread(tokens); foreach (var token in tokens) { - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); if (token != typeToken) { b = typeDefList.TryGet(rid) is not null; Debug.Assert(!b); @@ -313,30 +315,31 @@ uint[] UpdateTypeTables_COMThread(uint typeToken) { return tokens; } - uint[] GetNewTokens_COMThread(uint rid) { + uint[] GetNewTokens_COMThread(uint token) { dispatcher.VerifyAccess(); if (ridToEnclosing is null) - return new[] { rid }; + return new[] { token }; var hash = tmpHash; hash.Clear(); for (;;) { + uint rid = MDToken.ToRID(token); if (ridToEnclosing.ContainsKey(rid)) break; if (rid == 0 || !hash.Add(rid)) break; - rid = MDAPI.GetTypeDefEnclosingType(MetaDataImport, 0x02000000 + rid) & 0x00FFFFFF; + token = MDAPI.GetTypeDefEnclosingType(MetaDataImport, token); } var tokens = new uint[hash.Count]; int i = 0; foreach (uint rid2 in hash) - tokens[i++] = 0x02000000 + rid2; + tokens[i++] = new MDToken(Table.TypeDef, rid2).Raw; return tokens; } readonly HashSet tmpHash = new HashSet(); (DmdType? type, bool containedGenericParams) TryCreateTypeSpecCOMD_COMThread(uint rid, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - uint token = 0x1B000000 + rid; + uint token = new MDToken(Table.TypeSpec, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return (null, containedGenericParams: true); var blob = MDAPI.GetTypeSpecSignatureBlob(MetaDataImport, token); @@ -345,7 +348,7 @@ uint[] GetNewTokens_COMThread(uint rid) { DmdFieldDefCOMD? CreateResolvedField_COMThread(uint rid, DmdTypeDef? declaringType) { dispatcher.VerifyAccess(); - uint token = 0x04000000 + rid; + uint token = new MDToken(Table.Field, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return null; if (declaringType is null) @@ -395,7 +398,7 @@ internal DmdType ReadFieldType_COMThread((IntPtr addr, uint size) signature, ILi DmdMethodBase? CreateResolvedMethod_COMThread(uint rid, DmdTypeDef? declaringType) { dispatcher.VerifyAccess(); - uint token = 0x06000000 + rid; + uint token = new MDToken(Table.Method, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return null; if (declaringType is null) @@ -414,7 +417,7 @@ internal DmdMethodBase CreateMethodDef_COMThread(uint rid, DmdType declaringType DmdMethodBase CreateMethodDefCore_COMThread(uint rid, DmdType declaringType, DmdType reflectedType) { dispatcher.VerifyAccess(); - uint token = 0x06000000 + rid; + uint token = new MDToken(Table.Method, rid).Raw; var name = MDAPI.GetMethodName(MetaDataImport, token) ?? string.Empty; MDAPI.GetMethodAttributes(MetaDataImport, token, out var attrs, out var implAttrs); if ((attrs & DmdMethodAttributes.RTSpecialName) != 0 && name.Length > 0 && name[0] == '.') { @@ -457,7 +460,7 @@ internal DmdMethodSignature ReadMethodSignature_COMThread((IntPtr addr, uint siz var parameters = sigParamTypes.Count == 0 ? Array.Empty() : new DmdParameterInfo[sigParamTypes.Count]; for (int i = 0; i < tokens.Length; i++) { uint token = tokens[i]; - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); var name = MDAPI.GetParamName(MetaDataImport, token); if (!MDAPI.GetParamSeqAndAttrs(MetaDataImport, token, out uint seq, out var attrs)) continue; @@ -485,7 +488,7 @@ internal DmdMethodSignature ReadMethodSignature_COMThread((IntPtr addr, uint siz DmdEventDef? CreateResolvedEvent_COMThread(uint rid, DmdTypeDef? declaringType) { dispatcher.VerifyAccess(); - uint token = 0x14000000 + rid; + uint token = new MDToken(Table.Event, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return null; if (declaringType is null) @@ -509,7 +512,7 @@ DmdEventDef CreateEventDefCore_COMThread(uint rid, DmdType declaringType, DmdTyp DmdPropertyDef? CreateResolvedProperty_COMThread(uint rid, DmdTypeDef? declaringType) { dispatcher.VerifyAccess(); - uint token = 0x17000000 + rid; + uint token = new MDToken(Table.Property, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return null; if (declaringType is null) @@ -539,7 +542,7 @@ DmdPropertyDef CreatePropertyDefCore_COMThread(uint rid, DmdType declaringType, var genericParams = new DmdType[tokens.Length]; for (int i = 0; i < genericParams.Length; i++) { uint token = tokens[i]; - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); var gpName = MDAPI.GetGenericParamName(MetaDataImport, token) ?? string.Empty; if (!MDAPI.GetGenericParamNumAndAttrs(MetaDataImport, token, out var gpNumber, out var gpAttrs)) return null; @@ -551,7 +554,7 @@ DmdPropertyDef CreatePropertyDefCore_COMThread(uint rid, DmdType declaringType, (DmdMemberInfo member, bool containedGenericParams) CreateResolvedMemberRef_COMThread(uint rid, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - uint token = 0x0A000000 + rid; + uint token = new MDToken(Table.MemberRef, rid).Raw; var signature = MDAPI.GetMemberRefSignatureBlob(MetaDataImport, token); var name = MDAPI.GetMemberRefName(MetaDataImport, token) ?? string.Empty; @@ -564,7 +567,7 @@ DmdPropertyDef CreatePropertyDefCore_COMThread(uint rid, DmdType declaringType, var rawInfo = info.containedGenericParams ? ReadMethodSignatureOrFieldType_COMThread(signature, null, null) : info; bool containedGenericParams = info.containedGenericParams; - if ((classToken >> 24) == 0x1B) + if (MDToken.ToTable(classToken) == Table.TypeSpec) containedGenericParams = true; if (info.fieldType is not null) { @@ -586,8 +589,7 @@ DmdPropertyDef CreatePropertyDefCore_COMThread(uint rid, DmdType declaringType, DmdType GetMemberRefParent_COMThread(uint classToken, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - uint rid = classToken & 0x00FFFFFF; - switch ((Table)(classToken >> 24)) { + switch (MDToken.ToTable(classToken)) { case Table.TypeRef: case Table.TypeDef: case Table.TypeSpec: @@ -601,7 +603,7 @@ DmdType GetMemberRefParent_COMThread(uint classToken, IList? genericTyp return referencedModule?.GlobalType ?? Module.AppDomain.System_Void; case Table.Method: - return ResolveMethodDef_COMThread(rid)?.DeclaringType ?? Module.AppDomain.System_Void; + return ResolveMethodDef_COMThread(MDToken.ToRID(classToken))?.DeclaringType ?? Module.AppDomain.System_Void; default: return Module.AppDomain.System_Void; @@ -681,9 +683,10 @@ DmdType GetMemberRefParent_COMThread(uint classToken, IList? genericTyp (DmdType type, bool isPinned)[] IMethodBodyResolver.ReadLocals(int localSignatureMetadataToken, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - if ((localSignatureMetadataToken & 0x00FFFFFF) == 0 || (localSignatureMetadataToken >> 24) != 0x11) + var localSigToken = new MDToken(localSignatureMetadataToken); + if (localSigToken.IsNull || localSigToken.Table != Table.StandAloneSig) return Array.Empty<(DmdType, bool)>(); - var signature = MDAPI.GetStandAloneSigBlob(MetaDataImport, (uint)localSignatureMetadataToken); + var signature = MDAPI.GetStandAloneSigBlob(MetaDataImport, localSigToken.Raw); if (signature.addr == IntPtr.Zero) return Array.Empty<(DmdType, bool)>(); return DmdSignatureReader.ReadLocalsSignature(module, new DmdPointerDataStream(signature), genericTypeArguments, genericMethodArguments, resolveTypes); @@ -699,7 +702,7 @@ public override DmdTypeDef[] GetTypes() { DmdTypeDef[] GetTypes_COMThread() { dispatcher.VerifyAccess(); var result = new List(); - for (uint rid = 1; rid <= 0x00FFFFFF; rid++) { + for (uint rid = 1; rid <= MDToken.RID_MAX; rid++) { var type = ResolveTypeDef_COMThread(rid); if (type is null) break; @@ -721,7 +724,7 @@ public override DmdTypeRef[] GetExportedTypes() { DmdTypeRef[] GetExportedTypes_COMThread() { dispatcher.VerifyAccess(); List? result = null; - for (uint rid = 1; rid <= 0x00FFFFFF; rid++) { + for (uint rid = 1; rid <= MDToken.RID_MAX; rid++) { var type = ResolveExportedType_COMThread(rid); if (type is null) break; @@ -872,7 +875,7 @@ DmdTypeRef[] GetExportedTypes_COMThread() { DmdMethodBase? ResolveMethodSpec_COMThread(uint rid, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - uint token = 0x2B000000 + rid; + uint token = new MDToken(Table.MethodSpec, rid).Raw; if (!MDAPI.IsValidToken(MetaDataImport, token)) return null; var signature = MDAPI.GetMethodSpecProps(MetaDataImport, token, out var methodToken); @@ -892,7 +895,7 @@ DmdTypeRef[] GetExportedTypes_COMThread() { DmdMethodSignature? ResolveMethodSignature_COMThread(uint rid, IList? genericTypeArguments, IList? genericMethodArguments) { dispatcher.VerifyAccess(); - var signature = MDAPI.GetStandAloneSigBlob(MetaDataImport, 0x11000000 + rid); + var signature = MDAPI.GetStandAloneSigBlob(MetaDataImport, new MDToken(Table.StandAloneSig, rid).Raw); if (signature.addr == IntPtr.Zero) return null; return ReadMethodSignature_COMThread(signature, genericTypeArguments, genericMethodArguments, isProperty: false); @@ -950,32 +953,32 @@ static byte[] GetData((IntPtr addr, uint size) info) { byte[] ResolveFieldSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetFieldSignatureBlob(MetaDataImport, 0x04000000 + rid)); + return GetData(MDAPI.GetFieldSignatureBlob(MetaDataImport, new MDToken(Table.Field, rid).Raw)); } byte[] ResolveMethodSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetMethodSignatureBlob(MetaDataImport, 0x06000000 + rid)); + return GetData(MDAPI.GetMethodSignatureBlob(MetaDataImport, new MDToken(Table.Method, rid).Raw)); } byte[] ResolveMemberRefSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetMemberRefSignatureBlob(MetaDataImport, 0x0A000000 + rid)); + return GetData(MDAPI.GetMemberRefSignatureBlob(MetaDataImport, new MDToken(Table.MemberRef, rid).Raw)); } byte[] ResolveStandAloneSigSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetStandAloneSigBlob(MetaDataImport, 0x11000000 + rid)); + return GetData(MDAPI.GetStandAloneSigBlob(MetaDataImport, new MDToken(Table.StandAloneSig, rid).Raw)); } byte[] ResolveTypeSpecSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetTypeSpecSignatureBlob(MetaDataImport, 0x1B000000 + rid)); + return GetData(MDAPI.GetTypeSpecSignatureBlob(MetaDataImport, new MDToken(Table.TypeSpec, rid).Raw)); } byte[] ResolveMethodSpecSignature_COMThread(uint rid) { dispatcher.VerifyAccess(); - return GetData(MDAPI.GetMethodSpecProps(MetaDataImport, 0x2B000000 + rid, out _)); + return GetData(MDAPI.GetMethodSpecProps(MetaDataImport, new MDToken(Table.MethodSpec, rid).Raw, out _)); } protected override string ResolveStringCore(uint offset) { @@ -1043,14 +1046,14 @@ DmdReadOnlyAssemblyName[] GetReferencedAssemblies_COMThread() { for (uint token = 0x23000001; ; token++) { if (!MDAPI.IsValidToken(MetaDataImport, token)) break; - list.Add(ReadAssemblyName_COMThread(token & 0x00FFFFFF)); + list.Add(ReadAssemblyName_COMThread(MDToken.ToRID(token))); } return list.ToArray(); } internal DmdReadOnlyAssemblyName ReadAssemblyName_COMThread(uint rid) { dispatcher.VerifyAccess(); - uint token = 0x23000000 + rid; + uint token = new MDToken(Table.AssemblyRef, rid).Raw; var name = MDAPI.GetAssemblyRefSimpleName(MetaDataAssemblyImport, token) ?? string.Empty; var version = MDAPI.GetAssemblyRefVersionAndLocale(MetaDataAssemblyImport, token, out var locale) ?? new Version(0, 0, 0, 0); var cultureName = locale ?? string.Empty; @@ -1060,16 +1063,17 @@ internal DmdReadOnlyAssemblyName ReadAssemblyName_COMThread(uint rid) { public override unsafe bool ReadMemory(uint rva, void* destination, int size) => false; - protected override DmdCustomAttributeData[] ReadAssemblyCustomAttributes(uint rid) => ReadCustomAttributesCore(0x20000000 + rid); - protected override DmdCustomAttributeData[] ReadModuleCustomAttributes(uint rid) => ReadCustomAttributesCore(0x00000000 + rid); - protected override DmdCustomAttributeData[] ReadTypeDefCustomAttributes(uint rid) => ReadCustomAttributesCore(0x02000000 + rid); - protected override DmdCustomAttributeData[] ReadFieldCustomAttributes(uint rid) => ReadCustomAttributesCore(0x04000000 + rid); - protected override DmdCustomAttributeData[] ReadMethodCustomAttributes(uint rid) => ReadCustomAttributesCore(0x06000000 + rid); - protected override DmdCustomAttributeData[] ReadParamCustomAttributes(uint rid) => ReadCustomAttributesCore(0x08000000 + rid); - protected override DmdCustomAttributeData[] ReadEventCustomAttributes(uint rid) => ReadCustomAttributesCore(0x14000000 + rid); - protected override DmdCustomAttributeData[] ReadPropertyCustomAttributes(uint rid) => ReadCustomAttributesCore(0x17000000 + rid); - - DmdCustomAttributeData[] ReadCustomAttributesCore(uint token) { + protected override DmdCustomAttributeData[] ReadAssemblyCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Assembly, rid); + protected override DmdCustomAttributeData[] ReadModuleCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Module, rid); + protected override DmdCustomAttributeData[] ReadTypeDefCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.TypeDef, rid); + protected override DmdCustomAttributeData[] ReadFieldCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Field, rid); + protected override DmdCustomAttributeData[] ReadMethodCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Method, rid); + protected override DmdCustomAttributeData[] ReadParamCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Param, rid); + protected override DmdCustomAttributeData[] ReadEventCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Event, rid); + protected override DmdCustomAttributeData[] ReadPropertyCustomAttributes(uint rid) => ReadCustomAttributesCore(Table.Property, rid); + + DmdCustomAttributeData[] ReadCustomAttributesCore(Table table, uint rid) { + uint token = new MDToken(table, rid).Raw; if (IsCOMThread) return ReadCustomAttributesCore_COMThread(token); else @@ -1104,11 +1108,12 @@ internal DmdCustomAttributeData[] ReadCustomAttributesCore_COMThread(uint token) return res; } - protected override DmdCustomAttributeData[] ReadAssemblySecurityAttributes(uint rid) => ReadSecurityAttributesCore(0x20000000 + rid); - protected override DmdCustomAttributeData[] ReadTypeDefSecurityAttributes(uint rid) => ReadSecurityAttributesCore(0x02000000 + rid); - protected override DmdCustomAttributeData[] ReadMethodSecurityAttributes(uint rid) => ReadSecurityAttributesCore(0x06000000 + rid); + protected override DmdCustomAttributeData[] ReadAssemblySecurityAttributes(uint rid) => ReadSecurityAttributesCore(Table.Assembly, rid); + protected override DmdCustomAttributeData[] ReadTypeDefSecurityAttributes(uint rid) => ReadSecurityAttributesCore(Table.TypeDef, rid); + protected override DmdCustomAttributeData[] ReadMethodSecurityAttributes(uint rid) => ReadSecurityAttributesCore(Table.Method, rid); - DmdCustomAttributeData[] ReadSecurityAttributesCore(uint token) { + DmdCustomAttributeData[] ReadSecurityAttributesCore(Table table, uint rid) { + uint token = new MDToken(table, rid).Raw; if (IsCOMThread) return ReadSecurityAttributesCore_COMThread(token); else diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdConstructorDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdConstructorDefCOMD.cs index 89a612bde8..9af0f45826 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdConstructorDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdConstructorDefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdConstructorDefCOMD : DmdConstructorDef { @@ -35,7 +37,8 @@ public DmdConstructorDefCOMD(DmdComMetadataReader reader, DmdMethodAttributes at MethodImplementationFlags = implementationFlags; Attributes = attributes; Name = name ?? throw new ArgumentNullException(nameof(name)); - methodSignature = reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport, 0x06000000 + rid), DeclaringType!.GetGenericArguments(), GetGenericArguments(), isProperty: false); + uint token = new MDToken(Table.Method, rid).Raw; + methodSignature = reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport, token), DeclaringType!.GetGenericArguments(), GetGenericArguments(), isProperty: false); } T COMThread(Func action) => reader.Dispatcher.Invoke(action); @@ -56,6 +59,6 @@ public DmdConstructorDefCOMD(DmdComMetadataReader reader, DmdMethodAttributes at } protected override uint GetRVA() => COMThread(GetRVA_COMThread); - uint GetRVA_COMThread() => MDAPI.GetRVA(reader.MetaDataImport, 0x06000000 + Rid) ?? 0; + uint GetRVA_COMThread() => MDAPI.GetRVA(reader.MetaDataImport, (uint)MetadataToken) ?? 0; } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdEventDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdEventDefCOMD.cs index 1324f84326..f6f4e3c87b 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdEventDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdEventDefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Diagnostics; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdEventDefCOMD : DmdEventDef { @@ -31,7 +33,7 @@ sealed class DmdEventDefCOMD : DmdEventDef { public DmdEventDefCOMD(DmdComMetadataReader reader, uint rid, DmdType declaringType, DmdType reflectedType) : base(rid, declaringType, reflectedType) { this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); reader.Dispatcher.VerifyAccess(); - uint token = 0x14000000 + rid; + uint token = new MDToken(Table.Event, rid).Raw; Name = MDAPI.GetEventName(reader.MetaDataImport, token) ?? string.Empty; Attributes = MDAPI.GetEventAttributes(reader.MetaDataImport, token); var eventTypeToken = MDAPI.GetEventTypeToken(reader.MetaDataImport, token); @@ -68,9 +70,10 @@ protected override void GetMethods(out DmdMethodInfo? addMethod, out DmdMethodIn } DmdMethodInfo? Lookup_COMThread(uint token) { - if ((token >> 24) != 0x06 || (token & 0x00FFFFFF) == 0) + var methodToken = new MDToken(token); + if (methodToken.Table != Table.Method || methodToken.IsNull) return null; - var method = ReflectedType!.GetMethod(Module, (int)token) as DmdMethodInfo; + var method = ReflectedType!.GetMethod(Module, methodToken.ToInt32()) as DmdMethodInfo; Debug2.Assert(method is not null); return method; } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdExportedTypeCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdExportedTypeCOMD.cs index 57454c50fc..b68484dfa8 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdExportedTypeCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdExportedTypeCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdExportedTypeCOMD : DmdTypeRef { @@ -33,25 +35,26 @@ public DmdExportedTypeCOMD(DmdComMetadataReader reader, uint rid, IList> 24) { - case 0x23: - TypeScope = new DmdTypeScope(reader.ReadAssemblyName_COMThread(implToken & 0x00FFFFFF)); + MDAPI.GetExportedTypeProps(reader.MetaDataAssemblyImport, token, out var implTokenRaw, out _, out _); + var implToken = new MDToken(implTokenRaw); + switch (implToken.Table) { + case Table.AssemblyRef: + TypeScope = new DmdTypeScope(reader.ReadAssemblyName_COMThread(implToken.Rid)); break; - case 0x26: - var moduleName = MDAPI.GetFileName(reader.MetaDataAssemblyImport, implToken) ?? string.Empty; + case Table.File: + var moduleName = MDAPI.GetFileName(reader.MetaDataAssemblyImport, implToken.Raw) ?? string.Empty; TypeScope = new DmdTypeScope(reader.GetName(), moduleName); break; - case 0x27: + case Table.ExportedType: TypeScope = DmdTypeScope.Invalid; - baseTypeToken = (int)implToken; + baseTypeToken = implToken.ToInt32(); break; default: diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdFieldDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdFieldDefCOMD.cs index 9083680046..3b30d2ff37 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdFieldDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdFieldDefCOMD.cs @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License */ using System; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdFieldDefCOMD : DmdFieldDef { @@ -31,8 +33,7 @@ sealed class DmdFieldDefCOMD : DmdFieldDef { public DmdFieldDefCOMD(DmdComMetadataReader reader, uint rid, DmdType declaringType, DmdType reflectedType) : base(rid, declaringType, reflectedType) { this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); reader.Dispatcher.VerifyAccess(); - - uint token = 0x04000000 + rid; + uint token = new MDToken(Table.Field, rid).Raw; Attributes = MDAPI.GetFieldAttributes(reader.MetaDataImport, token); Name = MDAPI.GetFieldName(reader.MetaDataImport, token) ?? string.Empty; FieldType = reader.ReadFieldType_COMThread(MDAPI.GetFieldSignatureBlob(reader.MetaDataImport, token), DeclaringType!.GetGenericArguments()); @@ -50,7 +51,7 @@ public DmdFieldDefCOMD(DmdComMetadataReader reader, uint rid, DmdType declaringT reader.Dispatcher.VerifyAccess(); var marshalType = reader.ReadFieldMarshalType_COMThread(MetadataToken, ReflectedType!.Module, null); var cas = reader.ReadCustomAttributesCore_COMThread((uint)MetadataToken); - var fieldOffset = MDAPI.GetFieldOffset(reader.MetaDataImport, (uint)ReflectedType.MetadataToken, 0x04000000 + Rid); + var fieldOffset = MDAPI.GetFieldOffset(reader.MetaDataImport, (uint)ReflectedType.MetadataToken, (uint)MetadataToken); return (cas, fieldOffset, marshalType); } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdGenericParameterTypeCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdGenericParameterTypeCOMD.cs index 0a915eef41..b4e3078853 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdGenericParameterTypeCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdGenericParameterTypeCOMD.cs @@ -39,7 +39,7 @@ public DmdGenericParameterTypeCOMD(DmdComMetadataReader reader, uint rid, DmdMet protected override DmdType[]? CreateGenericParameterConstraints() => COMThread(CreateGenericParameterConstraints_COMThread); DmdType[]? CreateGenericParameterConstraints_COMThread() { reader.Dispatcher.VerifyAccess(); - var tokens = MDAPI.GetGenericParamConstraintTokens(reader.MetaDataImport, 0x2A000000 + Rid); + var tokens = MDAPI.GetGenericParamConstraintTokens(reader.MetaDataImport, (uint)MetadataToken); if (tokens.Length == 0) return null; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdMethodDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdMethodDefCOMD.cs index b0ab44817e..60e9d4f883 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdMethodDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdMethodDefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdMethodDefCOMD : DmdMethodDef { @@ -35,7 +37,8 @@ public DmdMethodDefCOMD(DmdComMetadataReader reader, DmdMethodAttributes attribu MethodImplementationFlags = implementationFlags; Attributes = attributes; Name = name ?? throw new ArgumentNullException(nameof(name)); - methodSignature = reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport, 0x06000000 + rid), DeclaringType!.GetGenericArguments(), GetGenericArguments(), isProperty: false); + uint token = new MDToken(Table.Method, rid).Raw; + methodSignature = reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport, token), DeclaringType!.GetGenericArguments(), GetGenericArguments(), isProperty: false); } T COMThread(Func action) => reader.Dispatcher.Invoke(action); @@ -50,8 +53,8 @@ public DmdMethodDefCOMD(DmdComMetadataReader reader, DmdMethodAttributes attribu protected override (DmdCustomAttributeData[]? cas, DmdCustomAttributeData[]? sas, DmdImplMap? implMap) CreateCustomAttributes() => COMThread(CreateCustomAttributes_COMThread); (DmdCustomAttributeData[]? cas, DmdCustomAttributeData[]? sas, DmdImplMap? implMap) CreateCustomAttributes_COMThread() { reader.Dispatcher.VerifyAccess(); - var cas = reader.ReadCustomAttributes(MetadataToken); - var sas = reader.ReadSecurityAttributes(MetadataToken); + var cas = reader.ReadCustomAttributesCore_COMThread((uint)MetadataToken); + var sas = reader.ReadSecurityAttributesCore_COMThread((uint)MetadataToken); DmdImplMap? implMap; if (IsPinvokeImpl) { var name = MDAPI.GetPinvokeMapName(reader.MetaDataImport, (uint)MetadataToken); @@ -67,9 +70,9 @@ public DmdMethodDefCOMD(DmdComMetadataReader reader, DmdMethodAttributes attribu return (cas, sas, implMap); } - private protected override DmdMethodSignature GetMethodSignatureCore(IList genericMethodArguments) => COMThread(() => reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport, 0x06000000 + Rid), DeclaringType!.GetGenericArguments(), genericMethodArguments, isProperty: false)); + private protected override DmdMethodSignature GetMethodSignatureCore(IList genericMethodArguments) => COMThread(() => reader.ReadMethodSignature_COMThread(MDAPI.GetMethodSignatureBlob(reader.MetaDataImport,(uint)MetadataToken), DeclaringType!.GetGenericArguments(), genericMethodArguments, isProperty: false)); protected override uint GetRVA() => COMThread(GetRVA_COMThread); - uint GetRVA_COMThread() => MDAPI.GetRVA(reader.MetaDataImport, 0x06000000 + Rid) ?? 0; + uint GetRVA_COMThread() => MDAPI.GetRVA(reader.MetaDataImport, (uint)MetadataToken) ?? 0; } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdPropertyDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdPropertyDefCOMD.cs index 5bf6ce7b84..f14a005dc3 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdPropertyDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdPropertyDefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Diagnostics; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdPropertyDefCOMD : DmdPropertyDef { @@ -31,7 +33,7 @@ sealed class DmdPropertyDefCOMD : DmdPropertyDef { public DmdPropertyDefCOMD(DmdComMetadataReader reader, uint rid, DmdType declaringType, DmdType reflectedType) : base(rid, declaringType, reflectedType) { this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); reader.Dispatcher.VerifyAccess(); - uint token = 0x17000000 + rid; + uint token = new MDToken(Table.Property, rid).Raw; Name = MDAPI.GetPropertyName(reader.MetaDataImport, token) ?? string.Empty; Attributes = MDAPI.GetPropertyAttributes(reader.MetaDataImport, token); methodSignature = reader.ReadMethodSignature_COMThread(MDAPI.GetPropertySignatureBlob(reader.MetaDataImport, token), DeclaringType!.GetGenericArguments(), null, isProperty: true); @@ -52,9 +54,8 @@ protected override void GetMethods(out DmdMethodInfo? getMethod, out DmdMethodIn (DmdMethodInfo? getMethod, DmdMethodInfo? setMethod, DmdMethodInfo[] otherMethods) GetMethods_COMThread() { reader.Dispatcher.VerifyAccess(); - uint token = 0x17000000 + Rid; - MDAPI.GetPropertyGetterSetter(reader.MetaDataImport, token, out uint getToken, out uint setToken); - var otherMethodTokens = MDAPI.GetPropertyOtherMethodTokens(reader.MetaDataImport, token); + MDAPI.GetPropertyGetterSetter(reader.MetaDataImport, (uint)MetadataToken, out uint getToken, out uint setToken); + var otherMethodTokens = MDAPI.GetPropertyOtherMethodTokens(reader.MetaDataImport, (uint)MetadataToken); var getMethod = Lookup_COMThread(getToken); var setMethod = Lookup_COMThread(setToken); var otherMethods = otherMethodTokens.Length == 0 ? Array.Empty() : new DmdMethodInfo[otherMethodTokens.Length]; @@ -70,7 +71,8 @@ protected override void GetMethods(out DmdMethodInfo? getMethod, out DmdMethodIn } DmdMethodInfo? Lookup_COMThread(uint token) { - if ((token >> 24) != 0x06 || (token & 0x00FFFFFF) == 0) + var methodToken = new MDToken(token); + if (methodToken.Table != Table.Method || methodToken.IsNull) return null; var method = ReflectedType!.GetMethod(Module, (int)token) as DmdMethodInfo; Debug2.Assert(method is not null); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeDefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeDefCOMD.cs index 0fdc6a0b62..bba34cbc3d 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeDefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeDefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdTypeDefCOMD : DmdTypeDef { @@ -33,7 +35,7 @@ sealed class DmdTypeDefCOMD : DmdTypeDef { public DmdTypeDefCOMD(DmdComMetadataReader reader, uint rid, IList? customModifiers) : base(rid, customModifiers) { this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); reader.Dispatcher.VerifyAccess(); - uint token = 0x02000000 + rid; + uint token = new MDToken(Table.TypeDef, rid).Raw; DmdTypeUtilities.SplitFullName(MDAPI.GetTypeDefName(reader.MetaDataImport, token) ?? string.Empty, out var @namespace, out var name); MetadataNamespace = @namespace; MetadataName = name; @@ -57,13 +59,13 @@ public override DmdType WithCustomModifiers(IList? customModi DmdType? GetDeclaringType_COMThread() { reader.Dispatcher.VerifyAccess(); - return reader.Module.ResolveType((int)(0x02000000 + reader.GetEnclosingTypeDefRid_COMThread(Rid)), DmdResolveOptions.None); + return reader.Module.ResolveType(new MDToken(Table.TypeDef, reader.GetEnclosingTypeDefRid_COMThread(Rid)).ToInt32(), DmdResolveOptions.None); } protected override DmdType? GetBaseTypeCore(IList genericTypeArguments) => COMThread(() => GetBaseTypeCore_COMThread(genericTypeArguments)); DmdType? GetBaseTypeCore_COMThread(IList genericTypeArguments) { reader.Dispatcher.VerifyAccess(); - uint extends = MDAPI.GetTypeDefExtends(reader.MetaDataImport, 0x02000000 + Rid); + uint extends = MDAPI.GetTypeDefExtends(reader.MetaDataImport, (uint)MetadataToken); return reader.Module.ResolveType((int)extends, genericTypeArguments, null, DmdResolveOptions.None); } @@ -76,7 +78,7 @@ public override DmdType WithCustomModifiers(IList? customModi var genericParams = new DmdType[tokens.Length]; for (int i = 0; i < genericParams.Length; i++) { uint token = tokens[i]; - uint rid = token & 0x00FFFFFF; + uint rid = MDToken.ToRID(token); var gpName = MDAPI.GetGenericParamName(reader.MetaDataImport, token) ?? string.Empty; if (!MDAPI.GetGenericParamNumAndAttrs(reader.MetaDataImport, token, out var gpNumber, out var gpAttrs)) return null; @@ -95,7 +97,7 @@ DmdFieldInfo[] ReadDeclaredFields_COMThread(DmdType declaringType, DmdType refle return Array.Empty(); var fields = new DmdFieldInfo[tokens.Length]; for (int i = 0; i < fields.Length; i++) { - uint rid = tokens[i] & 0x00FFFFFF; + uint rid = MDToken.ToRID(tokens[i]); fields[i] = reader.CreateFieldDef_COMThread(rid, declaringType, reflectedType); } return fields; @@ -110,7 +112,7 @@ DmdMethodBase[] ReadDeclaredMethods_COMThread(DmdType declaringType, DmdType ref return Array.Empty(); var methods = new DmdMethodBase[tokens.Length]; for (int i = 0; i < methods.Length; i++) { - uint rid = tokens[i] & 0x00FFFFFF; + uint rid = MDToken.ToRID(tokens[i]); methods[i] = reader.CreateMethodDef_COMThread(rid, declaringType, reflectedType); } return methods; @@ -125,7 +127,7 @@ DmdPropertyInfo[] ReadDeclaredProperties_COMThread(DmdType declaringType, DmdTyp return Array.Empty(); var properties = new DmdPropertyInfo[tokens.Length]; for (int i = 0; i < properties.Length; i++) { - uint rid = tokens[i] & 0x00FFFFFF; + uint rid = MDToken.ToRID(tokens[i]); properties[i] = reader.CreatePropertyDef_COMThread(rid, declaringType, reflectedType); } return properties; @@ -140,7 +142,7 @@ DmdEventInfo[] ReadDeclaredEvents_COMThread(DmdType declaringType, DmdType refle return Array.Empty(); var events = new DmdEventInfo[tokens.Length]; for (int i = 0; i < events.Length; i++) { - uint rid = tokens[i] & 0x00FFFFFF; + uint rid = MDToken.ToRID(tokens[i]); events[i] = reader.CreateEventDef_COMThread(rid, declaringType, reflectedType); } return events; @@ -170,8 +172,8 @@ DmdEventInfo[] ReadDeclaredEvents_COMThread(DmdType declaringType, DmdType refle return null; var res = new DmdType[nestedRids.Length]; for (int i = 0; i < res.Length; i++) { - uint rid = nestedRids[i]; - var nestedType = Module.ResolveType(0x02000000 + (int)rid, DmdResolveOptions.None); + var token = new MDToken(Table.TypeDef, nestedRids[i]); + var nestedType = Module.ResolveType(token.ToInt32(), DmdResolveOptions.None); if (nestedType is null) return null; res[i] = nestedType; @@ -190,7 +192,7 @@ DmdEventInfo[] ReadDeclaredEvents_COMThread(DmdType declaringType, DmdType refle protected override (int packingSize, int classSize) GetClassLayout() => COMThread(GetClassLayout_COMThread); (int packingSize, int classSize) GetClassLayout_COMThread() { reader.Dispatcher.VerifyAccess(); - MDAPI.GetClassLayout(reader.MetaDataImport, 0x02000000 + Rid, out ushort packingSize, out uint classSize); + MDAPI.GetClassLayout(reader.MetaDataImport, (uint)MetadataToken, out ushort packingSize, out uint classSize); return (packingSize, (int)classSize); } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeRefCOMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeRefCOMD.cs index 39ae57be25..57b6807992 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeRefCOMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/DmdTypeRefCOMD.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.COMD { sealed class DmdTypeRefCOMD : DmdTypeRef { @@ -32,29 +34,29 @@ sealed class DmdTypeRefCOMD : DmdTypeRef { public DmdTypeRefCOMD(DmdComMetadataReader reader, uint rid, IList? customModifiers) : base(reader.Module, rid, customModifiers) { this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); reader.Dispatcher.VerifyAccess(); - uint token = 0x01000000 + rid; + uint token = new MDToken(Table.TypeRef, rid).Raw; DmdTypeUtilities.SplitFullName(MDAPI.GetTypeRefName(reader.MetaDataImport, token) ?? string.Empty, out var @namespace, out var name); MetadataNamespace = @namespace; MetadataName = name; - var resScopeToken = MDAPI.GetTypeRefResolutionScope(reader.MetaDataImport, token); - switch (resScopeToken >> 24) { - case 0x00: + var resScopeToken = new MDToken(MDAPI.GetTypeRefResolutionScope(reader.MetaDataImport, token)); + switch (resScopeToken.Table) { + case Table.Module: TypeScope = new DmdTypeScope(reader.Module); break; - case 0x01: + case Table.TypeRef: TypeScope = DmdTypeScope.Invalid; - declTypeToken = (int)resScopeToken; + declTypeToken = resScopeToken.ToInt32(); break; - case 0x1A: - var moduleName = MDAPI.GetModuleRefName(reader.MetaDataImport, resScopeToken) ?? string.Empty; + case Table.ModuleRef: + var moduleName = MDAPI.GetModuleRefName(reader.MetaDataImport, resScopeToken.Raw) ?? string.Empty; TypeScope = new DmdTypeScope(reader.GetName(), moduleName); break; - case 0x23: - TypeScope = new DmdTypeScope(reader.ReadAssemblyName_COMThread(resScopeToken & 0x00FFFFFF)); + case Table.AssemblyRef: + TypeScope = new DmdTypeScope(reader.ReadAssemblyName_COMThread(resScopeToken.Rid)); break; default: diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/MDAPI.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/MDAPI.cs index 70c626a552..770bd29968 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/MDAPI.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/COMD/MDAPI.cs @@ -625,7 +625,7 @@ public static unsafe (IntPtr addr, uint size) GetStandAloneSigBlob(IMetaDataImpo public unsafe static COR_FIELD_OFFSET[]? GetFieldOffsets(IMetaDataImport2 mdi, uint token) { if (mdi is null) return null; - if ((token & 0x00FFFFFF) == 0) + if (MDToken.ToRID(token) == 0) return null; int cFieldOffset = 0; @@ -654,7 +654,7 @@ public static unsafe (IntPtr addr, uint size) GetStandAloneSigBlob(IMetaDataImpo public unsafe static (IntPtr addr, uint size) GetFieldMarshalBlob(IMetaDataImport2 mdi, uint token) { if (mdi is null) return (IntPtr.Zero, 0); - if ((token & 0x00FFFFFF) == 0) + if (MDToken.ToRID(token) == 0) return (IntPtr.Zero, 0); int hr = mdi.GetFieldMarshal(token, out var pvNativeType, out uint cbNativeType); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdConstructorDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdConstructorDef.cs index 8a37f93a6e..bf4b493764 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdConstructorDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdConstructorDef.cs @@ -21,12 +21,14 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Diagnostics; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdConstructorDef : DmdConstructorInfoBase { public sealed override DmdType? DeclaringType { get; } public sealed override DmdType? ReflectedType { get; } - public sealed override int MetadataToken => (int)(0x06000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Method, rid).ToInt32(); public sealed override bool IsMetadataReference => false; public sealed override bool IsGenericMethodDefinition => GetMethodSignature().GenericParameterCount != 0; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdEventDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdEventDef.cs index 1d7a9ee366..59376aca07 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdEventDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdEventDef.cs @@ -22,6 +22,8 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Linq; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdEventDef : DmdEventInfo { @@ -30,7 +32,7 @@ abstract class DmdEventDef : DmdEventInfo { public sealed override DmdModule Module => DeclaringType!.Module; public sealed override DmdType? DeclaringType { get; } public sealed override DmdType? ReflectedType { get; } - public sealed override int MetadataToken => (int)(0x14000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Event, rid).ToInt32(); protected uint Rid => rid; readonly uint rid; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdFieldDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdFieldDef.cs index f4fff51a76..3942e94ea7 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdFieldDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdFieldDef.cs @@ -20,12 +20,14 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.ObjectModel; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdFieldDef : DmdFieldInfoBase { public sealed override DmdType? DeclaringType { get; } public sealed override DmdType? ReflectedType { get; } - public sealed override int MetadataToken => (int)(0x04000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Field, rid).ToInt32(); public sealed override bool IsMetadataReference => false; protected uint Rid => rid; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdGenericParameterType.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdGenericParameterType.cs index a929b4f830..709fb1c35b 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdGenericParameterType.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdGenericParameterType.cs @@ -22,6 +22,8 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdGenericParameterType : DmdTypeBase { @@ -36,7 +38,7 @@ abstract class DmdGenericParameterType : DmdTypeBase { public sealed override DmdTypeAttributes Attributes => DmdTypeAttributes.Public; public sealed override int GenericParameterPosition { get; } public sealed override string? MetadataName { get; } - public sealed override int MetadataToken => (int)(0x2A000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.GenericParam, rid).ToInt32(); public sealed override bool IsMetadataReference => false; internal override bool HasTypeEquivalence => false; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMetadataReaderBase.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMetadataReaderBase.cs index 3013ce4a5e..a91f86856a 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMetadataReaderBase.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMetadataReaderBase.cs @@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdMetadataReaderBase : DmdMetadataReader { @@ -30,15 +32,15 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { static DmdMethodBase TryResolve(DmdMethodBase member, DmdResolveOptions options) => (options & DmdResolveOptions.NoTryResolveRefs) != 0 ? member : member.ResolveMethodBaseNoThrow() ?? member; public sealed override DmdMethodBase? ResolveMethod(int metadataToken, IList? genericTypeArguments, IList? genericMethodArguments, DmdResolveOptions options) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x06: + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.Method: var method = ResolveMethodDef(rid); if (method is not null) return method; break; - case 0x0A: + case Table.MemberRef: var mr = ResolveMemberRef(rid, genericTypeArguments, genericMethodArguments); if (mr is not null) { if (mr is DmdMethodBase methodRef) @@ -48,7 +50,7 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { } break; - case 0x2B: + case Table.MethodSpec: var methodSpec = ResolveMethodSpec(rid, genericTypeArguments, genericMethodArguments); if (methodSpec is not null) return TryResolve(methodSpec, options); @@ -61,15 +63,15 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { } public sealed override DmdFieldInfo? ResolveField(int metadataToken, IList? genericTypeArguments, IList? genericMethodArguments, DmdResolveOptions options) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x04: + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.Field: var field = ResolveFieldDef(rid); if (field is not null) return field; break; - case 0x0A: + case Table.MemberRef: var memberRef = ResolveMemberRef(rid, genericTypeArguments, genericMethodArguments); if (memberRef is not null) { if (memberRef is DmdFieldInfo fieldRef) @@ -86,27 +88,27 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { } public sealed override DmdType? ResolveType(int metadataToken, IList? genericTypeArguments, IList? genericMethodArguments, DmdResolveOptions options) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x01: + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.TypeRef: var typeRef = ResolveTypeRef(rid); if (typeRef is not null) return TryResolve(typeRef, options); break; - case 0x02: + case Table.TypeDef: var typeDef = ResolveTypeDef(rid); if (typeDef is not null) return typeDef; break; - case 0x1B: + case Table.TypeSpec: var typeSpec = ResolveTypeSpec(rid, genericTypeArguments, genericMethodArguments); if (typeSpec is not null) return TryResolve(typeSpec, options); break; - case 0x27: + case Table.ExportedType: var exportedType = ResolveExportedType(rid); if (exportedType is not null) return exportedType;// Don't try to resolve it, callers want the actual reference @@ -119,51 +121,51 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { } public sealed override DmdMemberInfo? ResolveMember(int metadataToken, IList? genericTypeArguments, IList? genericMethodArguments, DmdResolveOptions options) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x01: + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.TypeRef: var typeRef = ResolveTypeRef(rid); if (typeRef is not null) return TryResolve(typeRef, options); break; - case 0x02: + case Table.TypeDef: var typeDef = ResolveTypeDef(rid); if (typeDef is not null) return typeDef; break; - case 0x04: + case Table.Field: var field = ResolveFieldDef(rid); if (field is not null) return field; break; - case 0x06: + case Table.Method: var method = ResolveMethodDef(rid); if (method is not null) return method; break; - case 0x0A: + case Table.MemberRef: var memberRef = ResolveMemberRef(rid, genericTypeArguments, genericMethodArguments); if (memberRef is not null) return TryResolve(memberRef, options); break; - case 0x1B: + case Table.TypeSpec: var typeSpec = ResolveTypeSpec(rid, genericTypeArguments, genericMethodArguments); if (typeSpec is not null) return TryResolve(typeSpec, options); break; - case 0x27: + case Table.ExportedType: var exportedType = ResolveExportedType(rid); if (exportedType is not null) return exportedType;// Don't try to resolve it, callers want the actual reference break; - case 0x2B: + case Table.MethodSpec: var methodSpec = ResolveMethodSpec(rid, genericTypeArguments, genericMethodArguments); if (methodSpec is not null) return TryResolve(methodSpec, options); @@ -176,9 +178,9 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { } public sealed override DmdMethodSignature? ResolveMethodSignature(int metadataToken, IList? genericTypeArguments, IList? genericMethodArguments, DmdResolveOptions options) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x11: + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.StandAloneSig: var methodSig = ResolveMethodSignature(rid, genericTypeArguments, genericMethodArguments); if (methodSig is not null) return methodSig; @@ -204,15 +206,15 @@ abstract class DmdMetadataReaderBase : DmdMetadataReader { public sealed override byte[]? ResolveSignature(int metadataToken) { byte[]? res; - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x04: res = ResolveFieldSignature(rid); break; - case 0x06: res = ResolveMethodSignature(rid); break; - case 0x0A: res = ResolveMemberRefSignature(rid); break; - case 0x11: res = ResolveStandAloneSigSignature(rid); break; - case 0x1B: res = ResolveTypeSpecSignature(rid); break; - case 0x2B: res = ResolveMethodSpecSignature(rid); break; - default: res = null; break; + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.Field: res = ResolveFieldSignature(rid); break; + case Table.Method: res = ResolveMethodSignature(rid); break; + case Table.MemberRef: res = ResolveMemberRefSignature(rid); break; + case Table.StandAloneSig: res = ResolveStandAloneSigSignature(rid); break; + case Table.TypeSpec: res = ResolveTypeSpecSignature(rid); break; + case Table.MethodSpec: res = ResolveMethodSpecSignature(rid); break; + default: res = null; break; } return res ?? throw new ArgumentOutOfRangeException(nameof(metadataToken)); } @@ -236,16 +238,16 @@ public sealed override string ResolveString(int metadataToken) { protected abstract string ResolveStringCore(uint offset); public sealed override DmdCustomAttributeData[] ReadCustomAttributes(int metadataToken) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x00: return ReadModuleCustomAttributes(rid); - case 0x02: return ReadTypeDefCustomAttributes(rid); - case 0x04: return ReadFieldCustomAttributes(rid); - case 0x06: return ReadMethodCustomAttributes(rid); - case 0x08: return ReadParamCustomAttributes(rid); - case 0x14: return ReadEventCustomAttributes(rid); - case 0x17: return ReadPropertyCustomAttributes(rid); - case 0x20: return ReadAssemblyCustomAttributes(rid); + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.Module: return ReadModuleCustomAttributes(rid); + case Table.TypeDef: return ReadTypeDefCustomAttributes(rid); + case Table.Field: return ReadFieldCustomAttributes(rid); + case Table.Method: return ReadMethodCustomAttributes(rid); + case Table.Param: return ReadParamCustomAttributes(rid); + case Table.Event: return ReadEventCustomAttributes(rid); + case Table.Property: return ReadPropertyCustomAttributes(rid); + case Table.Assembly: return ReadAssemblyCustomAttributes(rid); default: throw new ArgumentOutOfRangeException(nameof(metadataToken)); } } @@ -260,11 +262,11 @@ public sealed override DmdCustomAttributeData[] ReadCustomAttributes(int metadat protected abstract DmdCustomAttributeData[] ReadPropertyCustomAttributes(uint rid); public sealed override DmdCustomAttributeData[] ReadSecurityAttributes(int metadataToken) { - uint rid = (uint)(metadataToken & 0x00FFFFFF); - switch ((uint)metadataToken >> 24) { - case 0x02: return ReadTypeDefSecurityAttributes(rid); - case 0x06: return ReadMethodSecurityAttributes(rid); - case 0x20: return ReadAssemblySecurityAttributes(rid); + uint rid = MDToken.ToRID(metadataToken); + switch (MDToken.ToTable(metadataToken)) { + case Table.TypeDef: return ReadTypeDefSecurityAttributes(rid); + case Table.Method: return ReadMethodSecurityAttributes(rid); + case Table.Assembly: return ReadAssemblySecurityAttributes(rid); default: throw new ArgumentOutOfRangeException(nameof(metadataToken)); } } diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodBodyReader.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodBodyReader.cs index 4345226bbb..34c9ce1636 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodBodyReader.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodBodyReader.cs @@ -20,6 +20,8 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.IO; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { interface IMethodBodyResolver { @@ -56,7 +58,7 @@ readonly struct DmdMethodBodyReader { return null; DmdLocalVariableInfo[] localVariables; - if ((localSignatureMetadataToken & 0x00FFFFFF) != 0 && (byte)(localSignatureMetadataToken >> 24) == 0x11) { + if (MDToken.ToRID(localSignatureMetadataToken) != 0 && MDToken.ToTable(localSignatureMetadataToken) == Table.StandAloneSig) { var localTypes = methodBodyResolver.ReadLocals(localSignatureMetadataToken, genericTypeArguments, genericMethodArguments); localVariables = new DmdLocalVariableInfo[localTypes.Length]; for (int i = 0; i < localVariables.Length; i++) { diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodDef.cs index b1886909e2..8012600d24 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdMethodDef.cs @@ -22,12 +22,14 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Diagnostics; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdMethodDef : DmdMethodInfoBase { public sealed override DmdType? DeclaringType { get; } public sealed override DmdType? ReflectedType { get; } - public sealed override int MetadataToken => (int)(0x06000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Method, rid).ToInt32(); public sealed override bool IsMetadataReference => false; public sealed override bool IsGenericMethodDefinition => GetMethodSignature().GenericParameterCount != 0; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdParameterDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdParameterDef.cs index 929645f22c..be92b4337b 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdParameterDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdParameterDef.cs @@ -20,13 +20,15 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.ObjectModel; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdParameterDef : DmdParameterInfoBase { public sealed override DmdMemberInfo Member { get; } public sealed override int Position { get; } public sealed override DmdType ParameterType { get; } - public sealed override int MetadataToken => (int)(0x08000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Param, rid).ToInt32(); protected uint Rid => rid; readonly uint rid; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdPropertyDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdPropertyDef.cs index f901510de0..ac68041f21 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdPropertyDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdPropertyDef.cs @@ -21,6 +21,8 @@ You should have received a copy of the GNU General Public License using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdPropertyDef : DmdPropertyInfo { @@ -29,7 +31,7 @@ abstract class DmdPropertyDef : DmdPropertyInfo { public sealed override DmdModule Module => DeclaringType!.Module; public sealed override DmdType? DeclaringType { get; } public sealed override DmdType? ReflectedType { get; } - public sealed override int MetadataToken => (int)(0x17000000 + rid); + public sealed override int MetadataToken => new MDToken(Table.Property, rid).ToInt32(); public sealed override DmdType PropertyType => GetMethodSignature().ReturnType; protected uint Rid => rid; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdSignatureReader.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdSignatureReader.cs index a01571ea37..b9053bafd6 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdSignatureReader.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdSignatureReader.cs @@ -286,13 +286,12 @@ enum TypeFlags { } DmdType ReadTypeDefOrRef() { - uint codedToken; - codedToken = reader.ReadCompressedUInt32(); - if (!DMD.MD.CodedToken.TypeDefOrRef.Decode(codedToken, out uint token)) + uint codedToken = reader.ReadCompressedUInt32(); + if (!DMD.MD.CodedToken.TypeDefOrRef.Decode(codedToken, out DMD.MDToken token)) return module.AppDomain.System_Void; - if ((token >> 24) == 0x1B) + if (token.Table == DMD.MD.Table.TypeSpec) containedGenericParams = true; - var type = module.ResolveType((int)token, genericTypeArguments, null, DmdResolveOptions.None) ?? module.AppDomain.System_Void; + var type = module.ResolveType(token.ToInt32(), genericTypeArguments, null, DmdResolveOptions.None) ?? module.AppDomain.System_Void; if (resolve) return type.ResolveNoThrow() ?? type; return type; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeDef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeDef.cs index cc3bc22084..d115757e94 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeDef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeDef.cs @@ -22,6 +22,8 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Threading; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdTypeDef : DmdTypeBase { @@ -30,7 +32,7 @@ abstract class DmdTypeDef : DmdTypeBase { public override bool IsMetadataReference => false; public override bool IsGenericType => GetGenericArguments().Count != 0; public override bool IsGenericTypeDefinition => GetGenericArguments().Count != 0; - public override int MetadataToken => (int)(0x02000000 + rid); + public override int MetadataToken => new MDToken(Table.TypeDef, rid).ToInt32(); internal override bool HasTypeEquivalence { get { const byte BoolBit = 1; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeRef.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeRef.cs index 4f070ffce5..50a4ef73d5 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeRef.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/DmdTypeRef.cs @@ -22,6 +22,7 @@ You should have received a copy of the GNU General Public License using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Threading; +using dnlib.DotNet; namespace dnSpy.Debugger.DotNet.Metadata.Impl { abstract class DmdTypeRef : DmdTypeBase { @@ -46,7 +47,7 @@ public virtual DmdTypeRef? DeclaringTypeRef { if (!declaringTypeRefInitd) { int declTypeToken = GetDeclaringTypeRefToken(); DmdTypeRef? newDT; - if ((declTypeToken & 0x00FFFFFF) == 0) + if (MDToken.ToRID(declTypeToken) == 0) newDT = null; else newDT = (DmdTypeRef?)ownerModule.ResolveType(declTypeToken, DmdResolveOptions.NoTryResolveRefs); diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEcma335MetadataReader.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEcma335MetadataReader.cs index ac0c243ddd..9d68469f09 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEcma335MetadataReader.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEcma335MetadataReader.cs @@ -37,7 +37,7 @@ public override DmdMethodInfo? EntryPoint { if ((Metadata.ImageCor20Header.Flags & ComImageFlags.NativeEntryPoint) != 0) return null; uint token = Metadata.ImageCor20Header.EntryPointToken_or_RVA; - if ((token >> 24) != (uint)Table.File) + if (MDToken.ToTable(token) != Table.File) return ResolveMethod((int)token, null, null, DmdResolveOptions.None) as DmdMethodInfo; return null; } @@ -206,7 +206,7 @@ internal DmdMethodSignature ReadMethodSignature(uint signature, IList? } internal (DmdParameterInfo? returnParameter, DmdParameterInfo[] parameters) CreateParameters(DmdMethodBase method, bool createReturnParameter) { - var ridList = Metadata.GetParamRidList((uint)method.MetadataToken & 0x00FFFFFF); + var ridList = Metadata.GetParamRidList(MDToken.ToRID(method.MetadataToken)); var methodSignature = method.GetMethodSignature(); var sigParamTypes = methodSignature.GetParameterTypes(); DmdParameterInfo? returnParameter = null; @@ -272,7 +272,7 @@ DmdPropertyDef CreatePropertyDefCore(uint rid, DmdType declaringType, DmdType re new DmdPropertyDefMD(this, rid, declaringType, reflectedType); internal DmdType[]? CreateGenericParameters(DmdMethodBase method) { - var ridList = Metadata.GetGenericParamRidList(Table.Method, (uint)method.MetadataToken & 0x00FFFFFF); + var ridList = Metadata.GetGenericParamRidList(Table.Method, MDToken.ToRID(method.MetadataToken)); if (ridList.Count == 0) return null; var genericParams = new DmdType[ridList.Count]; @@ -300,7 +300,7 @@ DmdPropertyDef CreatePropertyDefCore(uint rid, DmdType declaringType, DmdType re var rawInfo = info.containedGenericParams ? ReadMethodSignatureOrFieldType(row.Signature, null, null) : info; bool containedGenericParams = info.containedGenericParams; - if ((classToken >> 24) == 0x1B) + if (MDToken.ToTable(classToken) == Table.TypeSpec) containedGenericParams = true; if (info.fieldType is not null) { @@ -321,15 +321,15 @@ DmdPropertyDef CreatePropertyDefCore(uint rid, DmdType declaringType, DmdType re } DmdType GetMemberRefParent(uint classToken, IList? genericTypeArguments, IList? genericMethodArguments) { - uint rid = classToken & 0x00FFFFFF; - switch ((Table)(classToken >> 24)) { + uint rid = MDToken.ToRID(classToken); + switch (MDToken.ToTable(classToken)) { case Table.TypeRef: case Table.TypeDef: case Table.TypeSpec: return ResolveType((int)classToken, genericTypeArguments, genericMethodArguments, DmdResolveOptions.None) ?? Module.AppDomain.System_Void; case Table.ModuleRef: - TablesStream.TryReadModuleRefRow(classToken & 0x00FFFFFF, out var moduleRefRow); + TablesStream.TryReadModuleRefRow(rid, out var moduleRefRow); var moduleName = StringsStream.ReadNoNull(moduleRefRow.Name); if (StringComparer.OrdinalIgnoreCase.Equals(moduleName, Module.ScopeName)) return Module.GlobalType; @@ -389,7 +389,7 @@ DmdType GetMemberRefParent(uint classToken, IList? genericTypeArguments internal DmdMethodBody? GetMethodBody(DmdMethodBase method, IList? genericTypeArguments, IList? genericMethodArguments) { if ((method.MethodImplementationFlags & DmdMethodImplAttributes.CodeTypeMask) != DmdMethodImplAttributes.IL) return null; - if (!TablesStream.TryReadMethodRow((uint)method.MetadataToken & 0x00FFFFFF, out var row)) + if (!TablesStream.TryReadMethodRow(MDToken.ToRID(method.MetadataToken), out var row)) return null; if (row.RVA == 0) return null; @@ -402,15 +402,15 @@ DmdType GetMemberRefParent(uint classToken, IList? genericTypeArguments } internal uint GetRVA(DmdMethodBase method) { - TablesStream.TryReadMethodRow((uint)method.MetadataToken & 0x00FFFFFF, out var row); + TablesStream.TryReadMethodRow(MDToken.ToRID(method.MetadataToken), out var row); return row.RVA; } (DmdType type, bool isPinned)[] IMethodBodyResolver.ReadLocals(int localSignatureMetadataToken, IList? genericTypeArguments, IList? genericMethodArguments) { - if ((localSignatureMetadataToken & 0x00FFFFFF) == 0 || (localSignatureMetadataToken >> 24) != 0x11) + var localSigToken = new MDToken(localSignatureMetadataToken); + if (localSigToken.IsNull || localSigToken.Table != Table.StandAloneSig) return Array.Empty<(DmdType, bool)>(); - uint rid = (uint)localSignatureMetadataToken & 0x00FFFFFF; - if (!TablesStream.TryReadStandAloneSigRow(rid, out var row)) + if (!TablesStream.TryReadStandAloneSigRow(localSigToken.Rid, out var row)) return Array.Empty<(DmdType, bool)>(); var reader = BlobStream.CreateReader(row.Signature); return DmdSignatureReader.ReadLocalsSignature(module, new DmdDataStreamImpl(ref reader), genericTypeArguments, genericMethodArguments, resolveTypes); @@ -674,7 +674,7 @@ DmdCustomAttributeData[] ReadSecurityAttributesCore(Table table, uint rid) { #pragma warning restore SYSLIB0003 // SecurityAction internal DmdMarshalType? ReadMarshalType(int metadataToken, DmdModule module, IList? genericTypeArguments) { - if (!TablesStream.TryReadFieldMarshalRow(Metadata.GetFieldMarshalRid((Table)((uint)metadataToken >> 24), (uint)metadataToken & 0x00FFFFFF), out var row)) + if (!TablesStream.TryReadFieldMarshalRow(Metadata.GetFieldMarshalRid(MDToken.ToTable(metadataToken), MDToken.ToRID(metadataToken)), out var row)) return null; var reader = BlobStream.CreateReader(row.NativeType); return DmdMarshalBlobReader.Read(module, new DmdDataStreamImpl(ref reader), genericTypeArguments); @@ -688,7 +688,7 @@ DmdCustomAttributeData[] ReadSecurityAttributesCore(Table table, uint rid) { } internal (object? value, bool hasValue) ReadConstant(int metadataToken) { - var constantRid = Metadata.GetConstantRid((Table)((uint)metadataToken >> 24), (uint)(metadataToken & 0x00FFFFFF)); + var constantRid = Metadata.GetConstantRid(MDToken.ToTable(metadataToken), MDToken.ToRID(metadataToken)); if (constantRid == 0) return (null, false); if (!TablesStream.TryReadConstantRow(constantRid, out var row)) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEventDefMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEventDefMD.cs index c1b0f4b1cc..905ea5890c 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEventDefMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdEventDefMD.cs @@ -52,7 +52,7 @@ protected override void GetMethods(out DmdMethodInfo? addMethod, out DmdMethodIn for (int i = 0; i < ridList.Count; i++) { if (!reader.TablesStream.TryReadMethodSemanticsRow(ridList[i], out var row)) continue; - var method = ReflectedType!.GetMethod(Module, 0x06000000 + (int)row.Method) as DmdMethodInfo; + var method = ReflectedType!.GetMethod(Module, new MDToken(Table.Method, row.Method).ToInt32()) as DmdMethodInfo; if (method is null) continue; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdExportedTypeMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdExportedTypeMD.cs index f793188485..f6f1377393 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdExportedTypeMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdExportedTypeMD.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.Diagnostics; +using dnlib.DotNet; using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.MD { @@ -42,18 +43,19 @@ public DmdExportedTypeMD(DmdEcma335MetadataReader reader, uint rid, IList> 24) { - case 0x23: - TypeScope = new DmdTypeScope(reader.ReadAssemblyName(implToken & 0x00FFFFFF)); + var implRid = MDToken.ToRID(implToken); + switch (MDToken.ToTable(implToken)) { + case Table.AssemblyRef: + TypeScope = new DmdTypeScope(reader.ReadAssemblyName(implRid)); break; - case 0x26: - reader.TablesStream.TryReadFileRow(implToken & 0x00FFFFFF, out var fileRow); + case Table.File: + reader.TablesStream.TryReadFileRow(implRid, out var fileRow); var moduleName = reader.StringsStream.ReadNoNull(fileRow.Name); TypeScope = new DmdTypeScope(reader.GetName(), moduleName); break; - case 0x27: + case Table.ExportedType: TypeScope = DmdTypeScope.Invalid; baseTypeToken = (int)implToken; break; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdPropertyDefMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdPropertyDefMD.cs index 799ab5ac67..ca90ff3f65 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdPropertyDefMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdPropertyDefMD.cs @@ -53,7 +53,7 @@ protected override void GetMethods(out DmdMethodInfo? getMethod, out DmdMethodIn for (int i = 0; i < ridList.Count; i++) { if (!reader.TablesStream.TryReadMethodSemanticsRow(ridList[i], out var row)) continue; - var method = ReflectedType!.GetMethod(Module, 0x06000000 + (int)row.Method) as DmdMethodInfo; + var method = ReflectedType!.GetMethod(Module, new MDToken(Table.Method, row.Method).ToInt32()) as DmdMethodInfo; if (method is null) continue; diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdTypeDefMD.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdTypeDefMD.cs index 08864cda71..277d979e6e 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdTypeDefMD.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/MD/DmdTypeDefMD.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.Diagnostics; +using dnlib.DotNet; using dnlib.DotNet.MD; namespace dnSpy.Debugger.DotNet.Metadata.Impl.MD { @@ -48,7 +49,7 @@ public DmdTypeDefMD(DmdEcma335MetadataReader reader, uint rid, IList genericTypeArguments) { @@ -147,7 +148,7 @@ public DmdTypeDefMD(DmdEcma335MetadataReader reader, uint rid, IList> 24) { - case 0x00: + var resScopeRid = MDToken.ToRID(resScopeToken); + switch (MDToken.ToTable(resScopeToken)) { + case Table.Module: TypeScope = new DmdTypeScope(reader.Module); break; - case 0x01: + case Table.TypeRef: TypeScope = DmdTypeScope.Invalid; declTypeToken = (int)resScopeToken; break; - case 0x1A: - reader.TablesStream.TryReadModuleRefRow(resScopeToken & 0x00FFFFFF, out var moduleRefRow); + case Table.ModuleRef: + reader.TablesStream.TryReadModuleRefRow(resScopeRid, out var moduleRefRow); var moduleName = reader.StringsStream.ReadNoNull(moduleRefRow.Name); TypeScope = new DmdTypeScope(reader.GetName(), moduleName); break; - case 0x23: - TypeScope = new DmdTypeScope(reader.ReadAssemblyName(resScopeToken & 0x00FFFFFF)); + case Table.AssemblyRef: + TypeScope = new DmdTypeScope(reader.ReadAssemblyName(resScopeRid)); break; default: diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/ReflectionTests.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/ReflectionTests.cs index 382e699755..90d5b32a56 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/ReflectionTests.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Metadata/Impl/ReflectionTests.cs @@ -25,12 +25,13 @@ You should have received a copy of the GNU General Public License using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using dnlib.DotNet; using dnlib.DotNet.MD; using DNE = dnlib.DotNet.Emit; namespace dnSpy.Debugger.DotNet.Metadata.Impl { /// - /// + /// /// public static class ReflectionTests { const bool TESTEXCEPTIONS = false; @@ -124,7 +125,7 @@ void Test() { */ /// - /// + /// /// /// public static void Test(DmdAppDomain ad) { @@ -359,10 +360,11 @@ static void Test(DmdAssembly asm1, Assembly asm2) { Test(mod1, mod2); for (int rid = 2; ; rid++) { - var t1 = mod1.ResolveType(0x02000000 + rid, DmdResolveOptions.None); + var token = new MDToken(Table.TypeDef, rid).ToInt32(); + var t1 = mod1.ResolveType(token, DmdResolveOptions.None); Type t2; try { - t2 = mod2.ResolveType(0x02000000 + rid); + t2 = mod2.ResolveType(token); } catch (ArgumentOutOfRangeException) { t2 = null; @@ -943,7 +945,7 @@ static void TestInstructions(DmdMethodBase m1, MethodBase m2, byte[] a1, byte[] case DNE.OperandType.InlineTok: token = BitConverter.ToInt32(a1, pos); pos += 4; - switch ((Table)(token >> 24)) { + switch (MDToken.ToTable(token)) { case Table.TypeRef: case Table.TypeDef: case Table.TypeSpec: diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/DbgEngineImpl.Breakpoints.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/DbgEngineImpl.Breakpoints.cs index a0780e8708..bb546728f5 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/DbgEngineImpl.Breakpoints.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/DbgEngineImpl.Breakpoints.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU General Public License using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using dnlib.DotNet; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.Breakpoints.Code; using dnSpy.Contracts.Debugger.Code; @@ -215,11 +216,11 @@ public PendingBreakpoint(DbgEngineBoundCodeBreakpoint boundBreakpoint, Action on } } - public bool IsTypeLoaded(int metadataToken) => (metadataToken & 0x00FFFFFF) != 0 && loadedTypes.Contains(metadataToken); + public bool IsTypeLoaded(int metadataToken) => MDToken.ToRID(metadataToken) != 0 && loadedTypes.Contains(metadataToken); public void OnTypeLoaded(TypeMirror monoType) { int typeToken = monoType.MetadataToken; - if ((typeToken & 0x00FFFFFF) == 0) + if (MDToken.ToRID(typeToken) == 0) return; // This can fail if it's a generic instantiated type, eg. List if List has already been loaded diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/Evaluation/MonoDebugTypeCreator.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/Evaluation/MonoDebugTypeCreator.cs index 6a7674f92a..795a86158d 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/Evaluation/MonoDebugTypeCreator.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Impl/Evaluation/MonoDebugTypeCreator.cs @@ -72,7 +72,7 @@ public static TypeMirror GetType(DbgEngineImpl engine, DmdType type, MonoTypeLoa case DmdTypeSignatureKind.Type: if (!engine.TryGetMonoModule(type.Module.GetDebuggerModule() ?? throw new InvalidOperationException(), out var monoModule)) throw new InvalidOperationException(); - Debug.Assert((type.MetadataToken >> 24) == 0x02); + Debug.Assert(MDToken.ToTable(type.MetadataToken) == Table.TypeDef); //TODO: This can sometimes crash Unity's old mono fork //TODO: It's possible to resolve types, but it's an internal method and it requires a method in the module TypeMirror? typeMirror = null; diff --git a/dnSpy/dnSpy/Hex/Commands/GoToMetadataVM.cs b/dnSpy/dnSpy/Hex/Commands/GoToMetadataVM.cs index 9110571b2a..61e389b137 100644 --- a/dnSpy/dnSpy/Hex/Commands/GoToMetadataVM.cs +++ b/dnSpy/dnSpy/Hex/Commands/GoToMetadataVM.cs @@ -216,11 +216,11 @@ bool CheckOffsetToken(uint value) { var tablesStream = mdHeaders.TablesStream; if (tablesStream is null) return null; - var table = token >> 24; + var table = (uint)MDToken.ToTable(token); if (table >= (uint)tablesStream.MDTables.Count) return null; var mdTable = tablesStream.MDTables[(int)table]; - return mdTable?.IsValidRID(token & 0x00FFFFFF) == true ? mdTable : null; + return mdTable?.IsValidRID(MDToken.ToRID(token)) == true ? mdTable : null; } } diff --git a/dnSpy/dnSpy/Hex/Commands/HexCommandOperations.cs b/dnSpy/dnSpy/Hex/Commands/HexCommandOperations.cs index cbf5e70ab8..443f8f20b0 100644 --- a/dnSpy/dnSpy/Hex/Commands/HexCommandOperations.cs +++ b/dnSpy/dnSpy/Hex/Commands/HexCommandOperations.cs @@ -239,11 +239,11 @@ public override void GoToMetadata(GoToMetadataKind mdKind) { var tablesStream = mdHeaders.TablesStream; if (tablesStream is null) return null; - var table = token >> 24; + var table = (uint)MDToken.ToTable(token); if (table >= (uint)tablesStream.MDTables.Count) return null; var mdTable = tablesStream.MDTables[(int)table]; - return mdTable?.IsValidRID(token & 0x00FFFFFF) == true ? mdTable : null; + return mdTable?.IsValidRID(MDToken.ToRID(token)) == true ? mdTable : null; } bool MoveTo(HexBufferPoint start, HexBufferPoint end, HexBufferPoint caret, bool select) { diff --git a/dnSpy/dnSpy/MainApp/CachedMefInfo.cs b/dnSpy/dnSpy/MainApp/CachedMefInfo.cs index ce7aa9d694..b0a8e889a1 100644 --- a/dnSpy/dnSpy/MainApp/CachedMefInfo.cs +++ b/dnSpy/dnSpy/MainApp/CachedMefInfo.cs @@ -21,6 +21,8 @@ You should have received a copy of the GNU General Public License using System.Diagnostics; using System.IO; using System.Reflection; +using dnlib.DotNet; +using dnlib.DotNet.MD; namespace dnSpy.MainApp { sealed class CachedMefInfo { @@ -128,7 +130,7 @@ public bool CheckFile(ResourceManagerTokenCacheImpl resourceManagerTokenCacheImp resourceManagerTokensOffset = reader.BaseStream.Position; for (int i = 0; i < tokens.Length; i++) { var token = reader.ReadInt32(); - if (!(token == 0 || ((token >> 24) == 0x06 && (token & 0x00FFFFFF) != 0))) + if (!(token == 0 || (MDToken.ToTable(token) == Table.Method && MDToken.ToRID(token) != 0))) return false; tokens[i] = token; } From 3d17404583339de5c3a04028d10f8eccadf19b72 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 6 Jul 2023 22:18:29 +0200 Subject: [PATCH 018/122] Slightly optimized XML documentation lookup for invalid keys --- .../XmlDoc/XmlDocumentationProvider.cs | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocumentationProvider.cs b/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocumentationProvider.cs index 47ccd96632..23966e61c1 100644 --- a/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocumentationProvider.cs +++ b/dnSpy/dnSpy.Contracts.Logic/Decompiler/XmlDoc/XmlDocumentationProvider.cs @@ -328,6 +328,17 @@ static int GetHashCode(string key) return h; } } + + static int GetHashCode(StringBuilder key) + { + unchecked { + int h = 0; + for (int i = 0; i < key.Length; i++) { + h = (h << 5) - h + key[i]; + } + return h; + } + } #endregion #region GetDocumentation @@ -348,8 +359,7 @@ static int GetHashCode(string key) { if (key is null) return null; - //TODO: Try to prevent ToString() - return GetDocumentation(key.ToString(), true); + return GetDocumentation(key, true); } string? GetDocumentation(string key, bool allowReload) @@ -389,6 +399,44 @@ static int GetHashCode(string key) } } + string? GetDocumentation(StringBuilder key, bool allowReload) + { + int hashcode = GetHashCode(key); + var index = this.index; // read volatile field + // index is sorted, so we can use binary search + int m = Array.BinarySearch(index, new IndexEntry(hashcode, 0)); + if (m < 0) + return null; + // correct hash code found. + // possibly there are multiple items with the same hash, so go to the first. + while (--m >= 0 && index[m].HashCode == hashcode); + // m is now 1 before the first item with the correct hash + + var keyStr = key.ToString(); + XmlDocumentationCache cache = this.cache; + lock (cache) { + if (!cache.TryGet(keyStr, out string? val)) { + try { + // go through all items that have the correct hash + while (++m < index.Length && index[m].HashCode == hashcode) { + val = LoadDocumentation(keyStr, index[m].PositionInFile); + if (val is not null) + break; + } + // cache the result (even if it is null) + cache.Add(keyStr, val); + } catch (IOException) { + // may happen if the documentation file was deleted/is inaccessible/changed (EndOfStreamException) + return allowReload ? ReloadAndGetDocumentation(keyStr) : null; + } catch (XmlException) { + // may happen if the documentation file was changed so that the file position no longer starts on a valid XML element + return allowReload ? ReloadAndGetDocumentation(keyStr) : null; + } + } + return val; + } + } + string? ReloadAndGetDocumentation(string key) { try { From ac858413e9d9c283c7d906538089faa3e17bd954 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Jul 2023 11:17:54 +0200 Subject: [PATCH 019/122] 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 020/122] 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 021/122] 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 2a43cda36aa94bec29e02496dafe35322141efd5 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 9 Jul 2023 20:05:24 +0200 Subject: [PATCH 022/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- Extensions/ILSpy.Decompiler/NRefactory | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index ee81b7a22d..b46c6dbdb3 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit ee81b7a22d580c664ff61c0fbd2e6c33bf5719c6 +Subproject commit b46c6dbdb3ceeb630ebc0ae3999cd5ab36419a5a diff --git a/Extensions/ILSpy.Decompiler/NRefactory b/Extensions/ILSpy.Decompiler/NRefactory index 7a10f702cb..32e5593384 160000 --- a/Extensions/ILSpy.Decompiler/NRefactory +++ b/Extensions/ILSpy.Decompiler/NRefactory @@ -1 +1 @@ -Subproject commit 7a10f702cb0a793745b61014e2a4193098d074a5 +Subproject commit 32e5593384d3db9b14fc21637dfdd5ed7d2be0a7 From e40a2fa7979a16346a1d59e15ee8c2c91dfb5e85 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 10 Jul 2023 20:50:21 +0200 Subject: [PATCH 023/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index b46c6dbdb3..f295be0742 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit b46c6dbdb3ceeb630ebc0ae3999cd5ab36419a5a +Subproject commit f295be07423658e970e768d15b97820a40ebbf01 From b9652f9e0f2881765190abf3b36bb4b2c8f65d2b Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 11 Jul 2023 21:45:43 +0200 Subject: [PATCH 024/122] Improve formatting of multidimensional arrays --- .../Formatters/CSharp/CSharpTypeFormatter.cs | 49 +++++++++++++------ .../VisualBasic/VisualBasicTypeFormatter.cs | 49 +++++++++++++------ .../CSharp/CSharpFormatter.cs | 8 +-- .../VisualBasic/VisualBasicFormatter.cs | 8 +-- 4 files changed, 74 insertions(+), 40 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs index 67847d5fb3..9522209f5e 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs @@ -158,31 +158,48 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider foreach (var tuple in arrayTypesList) { var aryType = tuple.type; var aryValue = tuple.value; - uint elementCount; if (aryType.IsVariableBoundArray) { OutputWrite(ARRAY_OPEN_PAREN, DbgTextColor.Punctuation); int rank = Math.Min(aryType.GetArrayRank(), MAX_ARRAY_RANK); if (rank <= 0) OutputWrite("???", DbgTextColor.Error); else { - if (aryValue is null || aryValue.IsNull || !aryValue.GetArrayInfo(out elementCount, out var dimensionInfos)) - dimensionInfos = null; - if (ShowArrayValueSizes && dimensionInfos is not null && dimensionInfos.Length == rank) { - for (int i = 0; i < rank; i++) { - if (i > 0) { - OutputWrite(",", DbgTextColor.Punctuation); - WriteSpace(); + bool sizesShown = false; + if (ShowArrayValueSizes) { + if (aryValue is not null && !aryValue.IsNull && aryValue.GetArrayInfo(out _, out var dimensionInfos) && dimensionInfos.Length == rank) { + for (int i = 0; i < rank; i++) { + if (i > 0) + WriteCommaSpace(); + if (dimensionInfos[i].BaseIndex == 0) + WriteInt32((int)dimensionInfos[i].Length); + else { + WriteInt32(dimensionInfos[i].BaseIndex); + OutputWrite("..", DbgTextColor.Operator); + WriteInt32(dimensionInfos[i].BaseIndex + (int)dimensionInfos[i].Length - 1); + } } - if (dimensionInfos[i].BaseIndex == 0) - WriteUInt32(dimensionInfos[i].Length); - else { - WriteInt32(dimensionInfos[i].BaseIndex); - OutputWrite("..", DbgTextColor.Operator); - WriteInt32(dimensionInfos[i].BaseIndex + (int)dimensionInfos[i].Length - 1); + sizesShown = true; + } + else { + var indexes = aryType.GetArrayLowerBounds(); + var sizes = aryType.GetArraySizes(); + if (sizes.Count == rank) { + for (int i = 0; i < rank; i++) { + if (i > 0) + WriteCommaSpace(); + if (i >= indexes.Count || indexes[i] == 0) + WriteInt32(sizes[i]); + else { + WriteInt32(indexes[i]); + OutputWrite("..", DbgTextColor.Operator); + WriteInt32(indexes[i] + sizes[i] - 1); + } + } + sizesShown = true; } } } - else { + if (!sizesShown) { if (rank == 1) OutputWrite("*", DbgTextColor.Operator); OutputWrite(TypeFormatterUtils.GetArrayCommas(rank), DbgTextColor.Punctuation); @@ -194,7 +211,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider Debug.Assert(aryType.IsSZArray); OutputWrite(ARRAY_OPEN_PAREN, DbgTextColor.Punctuation); if (ShowArrayValueSizes && aryValue is not null && !aryValue.IsNull) { - if (aryValue.GetArrayCount(out elementCount)) + if (aryValue.GetArrayCount(out uint elementCount)) WriteUInt32(elementCount); } OutputWrite(ARRAY_CLOSE_PAREN, DbgTextColor.Punctuation); diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs index f6351c6480..29d3a0205a 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/VisualBasic/VisualBasicTypeFormatter.cs @@ -157,31 +157,48 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider foreach (var tuple in arrayTypesList) { var aryType = tuple.type; var aryValue = tuple.value; - uint elementCount; if (aryType.IsVariableBoundArray) { OutputWrite(ARRAY_OPEN_PAREN, DbgTextColor.Punctuation); int rank = Math.Min(aryType.GetArrayRank(), MAX_ARRAY_RANK); if (rank <= 0) OutputWrite("???", DbgTextColor.Error); else { - if (aryValue is null || aryValue.IsNull || !aryValue.GetArrayInfo(out elementCount, out var dimensionInfos)) - dimensionInfos = null; - if (ShowArrayValueSizes && dimensionInfos is not null && dimensionInfos.Length == rank) { - for (int i = 0; i < rank; i++) { - if (i > 0) { - OutputWrite(",", DbgTextColor.Punctuation); - WriteSpace(); + bool sizesShown = false; + if (ShowArrayValueSizes) { + if (aryValue is not null && !aryValue.IsNull && aryValue.GetArrayInfo(out _, out var dimensionInfos) && dimensionInfos.Length == rank) { + for (int i = 0; i < rank; i++) { + if (i > 0) + WriteCommaSpace(); + if (dimensionInfos[i].BaseIndex == 0) + WriteUInt32(dimensionInfos[i].Length); + else { + WriteInt32(dimensionInfos[i].BaseIndex); + OutputWrite("..", DbgTextColor.Operator); + WriteInt32(dimensionInfos[i].BaseIndex + (int)dimensionInfos[i].Length - 1); + } } - if (dimensionInfos[i].BaseIndex == 0) - WriteUInt32(dimensionInfos[i].Length); - else { - WriteInt32(dimensionInfos[i].BaseIndex); - OutputWrite("..", DbgTextColor.Operator); - WriteInt32(dimensionInfos[i].BaseIndex + (int)dimensionInfos[i].Length - 1); + sizesShown = true; + } + else { + var indexes = aryType.GetArrayLowerBounds(); + var sizes = aryType.GetArraySizes(); + if (sizes.Count == rank) { + for (int i = 0; i < rank; i++) { + if (i > 0) + WriteCommaSpace(); + if (i >= indexes.Count || indexes[i] == 0) + WriteInt32(sizes[i]); + else { + WriteInt32(indexes[i]); + OutputWrite("..", DbgTextColor.Operator); + WriteInt32(indexes[i] + sizes[i] - 1); + } + } + sizesShown = true; } } } - else { + if (!sizesShown) { if (rank == 1) OutputWrite("*", DbgTextColor.Operator); OutputWrite(TypeFormatterUtils.GetArrayCommas(rank), DbgTextColor.Punctuation); @@ -193,7 +210,7 @@ void FormatCore(DmdType type, DbgDotNetValue? value, IAdditionalTypeInfoProvider Debug.Assert(aryType.IsSZArray); OutputWrite(ARRAY_OPEN_PAREN, DbgTextColor.Punctuation); if (ShowArrayValueSizes && aryValue is not null && !aryValue.IsNull) { - if (aryValue.GetArrayCount(out elementCount)) + if (aryValue.GetArrayCount(out uint elementCount)) WriteUInt32(elementCount); } OutputWrite(ARRAY_CLOSE_PAREN, DbgTextColor.Punctuation); diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index e6c1585400..33ab87fa2c 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -928,13 +928,13 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr else { var indexes = aryType.GetLowerBounds(); var dims = aryType.GetSizes(); - if (ShowArrayValueSizes && (uint)indexes.Count == rank && (uint)dims.Count == rank) { - for (int i = 0; (uint)i < rank; i++) { + if (ShowArrayValueSizes && dims.Count == rank) { + for (int i = 0; i < rank; i++) { if (i > 0) WriteCommaSpace(); - if (i < indexes.Count && indexes[i] == 0) + if (i >= indexes.Count || indexes[i] == 0) FormatInt32((int)dims[i]); - else if (i < indexes.Count && i < dims.Count) { + else { FormatInt32(indexes[i]); OutputWrite("..", BoxedTextColor.Operator); FormatInt32((int)(indexes[i] + dims[i] - 1)); diff --git a/dnSpy/dnSpy.Decompiler/VisualBasic/VisualBasicFormatter.cs b/dnSpy/dnSpy.Decompiler/VisualBasic/VisualBasicFormatter.cs index f463eb7dd8..2e1d9c1a5a 100644 --- a/dnSpy/dnSpy.Decompiler/VisualBasic/VisualBasicFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/VisualBasic/VisualBasicFormatter.cs @@ -891,13 +891,13 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr else { var indexes = aryType.GetLowerBounds(); var dims = aryType.GetSizes(); - if (ShowArrayValueSizes && (uint)indexes.Count == rank && (uint)dims.Count == rank) { - for (int i = 0; (uint)i < rank; i++) { + if (ShowArrayValueSizes && dims.Count == rank) { + for (int i = 0; i < rank; i++) { if (i > 0) WriteCommaSpace(); - if (i < indexes.Count && indexes[i] == 0) + if (i >= indexes.Count || indexes[i] == 0) FormatInt32((int)dims[i]); - else if (i < indexes.Count && i < dims.Count) { + else { FormatInt32(indexes[i]); OutputWrite("..", BoxedTextColor.Operator); FormatInt32((int)(indexes[i] + dims[i] - 1)); From 11fd0ee2778f89edd56334c2f11063b0a8e72b8e Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 13 Jul 2023 21:30:39 +0200 Subject: [PATCH 025/122] Improve assembly resolution when certain runtimes are unavailable --- dnSpy/dnSpy/Documents/AssemblyResolver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnSpy/dnSpy/Documents/AssemblyResolver.cs b/dnSpy/dnSpy/Documents/AssemblyResolver.cs index c2ec31a94d..1ac2d99f29 100644 --- a/dnSpy/dnSpy/Documents/AssemblyResolver.cs +++ b/dnSpy/dnSpy/Documents/AssemblyResolver.cs @@ -524,6 +524,8 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { IDsDocument? ResolveNormal(IAssembly assembly, ModuleDef? sourceModule) { var fwkKind = GetFrameworkKind(sourceModule, out var netVersion, out var sourceModuleDirectoryHint); + if ((fwkKind == FrameworkKind.DotNet || fwkKind == FrameworkKind.DotNetStandard) && !dotNetPathProvider.HasDotNet) + fwkKind = FrameworkKind.DotNetFramework4; if (fwkKind == FrameworkKind.DotNetStandard) { if (netVersion is not null && dotNetPathProvider.TryGetClosestNetStandardCompatibleVersion(netVersion, out var coreVersion)) @@ -533,8 +535,6 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { netVersion = null; } } - if (fwkKind == FrameworkKind.DotNet && !dotNetPathProvider.HasDotNet) - fwkKind = FrameworkKind.DotNetFramework4; bool loaded; IDsDocument? document; IDsDocument? existingDocument; @@ -545,7 +545,7 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { case FrameworkKind.DotNetFramework4: case FrameworkKind.DotNetStandard: int gacVersion; - if (!GacInfo.HasGAC2) + if (fwkKind == FrameworkKind.DotNetFramework2 && !GacInfo.HasGAC2) fwkKind = FrameworkKind.DotNetFramework4; bool redirected; IAssembly tempAsm; From 928b57b322870f678bb8b4db00d362710859c396 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 13 Jul 2023 21:48:46 +0200 Subject: [PATCH 026/122] Don't try to locate assembly files with invalid filename characters --- dnSpy/dnSpy/Documents/AssemblyResolver.cs | 61 +++++++++++++---------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/dnSpy/dnSpy/Documents/AssemblyResolver.cs b/dnSpy/dnSpy/Documents/AssemblyResolver.cs index 1ac2d99f29..c6ca7a7e73 100644 --- a/dnSpy/dnSpy/Documents/AssemblyResolver.cs +++ b/dnSpy/dnSpy/Documents/AssemblyResolver.cs @@ -522,6 +522,8 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { return true; } + static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); + IDsDocument? ResolveNormal(IAssembly assembly, ModuleDef? sourceModule) { var fwkKind = GetFrameworkKind(sourceModule, out var netVersion, out var sourceModuleDirectoryHint); if ((fwkKind == FrameworkKind.DotNet || fwkKind == FrameworkKind.DotNetStandard) && !dotNetPathProvider.HasDotNet) @@ -539,6 +541,7 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { IDsDocument? document; IDsDocument? existingDocument; FindAssemblyOptions options; + bool isValidFilename; switch (fwkKind) { case FrameworkKind.Unknown: case FrameworkKind.DotNetFramework2: @@ -586,27 +589,30 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { if (existingDocument is not null) return existingDocument; - (document, loaded) = LookupFromSearchPaths(assembly, sourceModule, sourceModuleDirectoryHint, netVersion); - if (document is not null) - return documentService.GetOrAddCanDispose(document, assembly, loaded); - - var gacFile = GacInfo.FindInGac(assembly, gacVersion); - if (gacFile is not null) - return documentService.TryGetOrCreateInternal(DsDocumentInfo.CreateDocument(gacFile), true, true); - foreach (var gacPath in GacInfo.OtherGacPaths) { - if (gacVersion == 4) { - if (gacPath.Version != GacVersion.V4) - continue; - } - else if (gacVersion == 2) { - if (gacPath.Version != GacVersion.V2) - continue; - } - else - Debug.Assert(gacVersion == -1); - document = TryLoadFromDir(assembly, checkVersion: true, checkPublicKeyToken: true, gacPath.Path); + isValidFilename = assembly.Name.String?.IndexOfAny(invalidFileNameChars) < 0; + if (isValidFilename) { + (document, loaded) = LookupFromSearchPaths(assembly, sourceModule, sourceModuleDirectoryHint, netVersion); if (document is not null) - return documentService.GetOrAddCanDispose(document, assembly, isAutoLoaded: true); + return documentService.GetOrAddCanDispose(document, assembly, loaded); + + var gacFile = GacInfo.FindInGac(assembly, gacVersion); + if (gacFile is not null) + return documentService.TryGetOrCreateInternal(DsDocumentInfo.CreateDocument(gacFile), true, true); + foreach (var gacPath in GacInfo.OtherGacPaths) { + if (gacVersion == 4) { + if (gacPath.Version != GacVersion.V4) + continue; + } + else if (gacVersion == 2) { + if (gacPath.Version != GacVersion.V2) + continue; + } + else + Debug.Assert(gacVersion == -1); + document = TryLoadFromDir(assembly, checkVersion: true, checkPublicKeyToken: true, gacPath.Path); + if (document is not null) + return documentService.GetOrAddCanDispose(document, assembly, isAutoLoaded: true); + } } break; @@ -618,12 +624,15 @@ static bool StartsWith(UTF8String? s, UTF8String? value) { if (document is not null) return document; - // If it's a self-contained .NET app, we don't need the version since we must only search - // the current directory. - Debug2.Assert(fwkKind == FrameworkKind.DotNet || netVersion is null); - (document, loaded) = LookupFromSearchPaths(assembly, sourceModule, sourceModuleDirectoryHint, netVersion); - if (document is not null) - return documentService.GetOrAddCanDispose(document, assembly, loaded); + isValidFilename = assembly.Name.String?.IndexOfAny(invalidFileNameChars) < 0; + if (isValidFilename) { + // If it's a self-contained .NET app, we don't need the version since we must only search + // the current directory. + Debug2.Assert(fwkKind == FrameworkKind.DotNet || netVersion is null); + (document, loaded) = LookupFromSearchPaths(assembly, sourceModule, sourceModuleDirectoryHint, netVersion); + if (document is not null) + return documentService.GetOrAddCanDispose(document, assembly, loaded); + } // If it already exists in assembly explorer, use it options = DsDocumentService.DefaultOptions; From 83239cda5d15589387b9db7788356ea3e8117e4d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 14 Jul 2023 19:03:41 +0200 Subject: [PATCH 027/122] 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 395322fe57e2288a2b7d7440fc88f02853a50c8a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 14 Jul 2023 21:51:27 +0200 Subject: [PATCH 028/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- dnSpy/dnSpy.Contracts.Logic/Decompiler/Extensions.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index f295be0742..d7b8fbf741 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit f295be07423658e970e768d15b97820a40ebbf01 +Subproject commit d7b8fbf741720c2a1f343ff8aebf8efe4b513053 diff --git a/dnSpy/dnSpy.Contracts.Logic/Decompiler/Extensions.cs b/dnSpy/dnSpy.Contracts.Logic/Decompiler/Extensions.cs index f3a68b1569..3a8584edd0 100644 --- a/dnSpy/dnSpy.Contracts.Logic/Decompiler/Extensions.cs +++ b/dnSpy/dnSpy.Contracts.Logic/Decompiler/Extensions.cs @@ -254,14 +254,16 @@ public static bool IsIndexer(this PropertyDef? property) { return false; } - static string? GetDefaultMemberName(TypeDef type) { + static string? GetDefaultMemberName(TypeDef? type) { if (type is null) return null; foreach (var ca in type.CustomAttributes.FindAll("System.Reflection.DefaultMemberAttribute")) { if (ca.Constructor is not null && ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)" && - ca.ConstructorArguments.Count == 1 && - ca.ConstructorArguments[0].Value is UTF8String) { - return (UTF8String)ca.ConstructorArguments[0].Value; + ca.ConstructorArguments.Count == 1) { + var value = ca.ConstructorArguments[0].Value; + var memberName = (value as UTF8String)?.String ?? value as string; + if (memberName is not null) + return memberName; } } return null; From b815728b57462f0b749eff674813fb3c0641994c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 15 Jul 2023 12:37:25 +0200 Subject: [PATCH 029/122] Improve handling of `TypeRef.Scope` being `null` --- .../ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- .../dnSpy.Analyzer/TreeNodes/Helpers.cs | 23 +++++++++++-------- .../TreeView/MemberReferenceFinder.cs | 7 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index d7b8fbf741..82cf72901c 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit d7b8fbf741720c2a1f343ff8aebf8efe4b513053 +Subproject commit 82cf72901c71ea74e42d7229636cf3a72f6dcb4a diff --git a/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs b/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs index 2a00b5f14b..eede7181f6 100644 --- a/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs +++ b/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs @@ -1,14 +1,14 @@ // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE @@ -26,8 +26,8 @@ static class Helpers { public static bool IsReferencedBy(TypeDef? type, ITypeDefOrRef? typeRef) => new SigComparer().Equals(type, typeRef.GetScopeType()); public static IMemberRef GetOriginalCodeLocation(IMemberRef member) { - if (member is MethodDef) - return GetOriginalCodeLocation((MethodDef)member); + if (member is MethodDef methodDef) + return GetOriginalCodeLocation(methodDef); return member; } @@ -45,7 +45,7 @@ static MethodDef GetOriginalCodeLocation(MethodDef method) { /// Given a compiler-generated type, returns the method where that type is used. /// Used to detect the 'parent method' for a lambda/iterator/async state machine. /// - static MethodDef? GetOriginalCodeLocation(TypeDef type) { + static MethodDef? GetOriginalCodeLocation(TypeDef? type) { if (type is not null && type.DeclaringType is not null && IsCompilerGenerated(type)) { if (type.IsValueType) { // Value types might not have any constructor; but they must be stored in a local var @@ -106,13 +106,18 @@ internal static bool CheckEquals(IMemberRef? mr1, IMemberRef? mr2) => return null; } - static TypeDef? ResolveWithinSameModule(ITypeDefOrRef type) { - if (type is not null && type.Scope == type.Module) + static TypeDef? ResolveWithinSameModule(ITypeDefOrRef? type) { + if (type is null) + return null; + if (type is TypeDef typeDef) + return typeDef; + var scope = type.Scope; + if (scope is null || scope == type.Module) return type.ResolveTypeDef(); return null; } - static bool IsCompilerGenerated(this IHasCustomAttribute hca) => + static bool IsCompilerGenerated(this IHasCustomAttribute? hca) => hca is not null && hca.CustomAttributes.IsDefined("System.Runtime.CompilerServices.CompilerGeneratedAttribute"); } } diff --git a/dnSpy/dnSpy/Documents/TreeView/MemberReferenceFinder.cs b/dnSpy/dnSpy/Documents/TreeView/MemberReferenceFinder.cs index c9661442bc..94d26bc4ef 100644 --- a/dnSpy/dnSpy/Documents/TreeView/MemberReferenceFinder.cs +++ b/dnSpy/dnSpy/Documents/TreeView/MemberReferenceFinder.cs @@ -94,11 +94,12 @@ bool IsOurType(ITypeDefOrRef tdr) { if (tdr is TypeDef) return true; if (tdr is TypeRef tr) { - if (tr.Scope == module) + var scope = tr.Scope; + if (scope is null || scope == module) return true; - if (tr.Scope is AssemblyRef asmRef) + if (scope is AssemblyRef asmRef) return AssemblyNameComparer.CompareAll.Equals(asmRef, module.Assembly); - if (tr.Scope is ModuleRef modRef) + if (scope is ModuleRef modRef) return StringComparer.OrdinalIgnoreCase.Equals(modRef.Name, module.Name); } return false; From 0e63f3ecdf46d891d40318b20ee49e64ead30a15 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 17 Jul 2023 14:06:54 +0200 Subject: [PATCH 030/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 82cf72901c..1115e23a39 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 82cf72901c71ea74e42d7229636cf3a72f6dcb4a +Subproject commit 1115e23a39b33864ab3a2c5a716ab9a20ce40dcb From 560875f864af2a6ea94142561b5ce7b156018e52 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 18 Jul 2023 15:09:51 +0200 Subject: [PATCH 031/122] Improve decompilation of XAML styles --- .../Handlers/Records/PropertyTypeReferenceHandler.cs | 3 +-- Extensions/dnSpy.BamlDecompiler/Xaml/XamlProperty.cs | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Extensions/dnSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs b/Extensions/dnSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs index 91130a8cae..e65a5ee873 100644 --- a/Extensions/dnSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs +++ b/Extensions/dnSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs @@ -30,7 +30,6 @@ sealed class PropertyTypeReferenceHandler : IHandler { public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyTypeReferenceRecord)((BamlRecordNode)node).Record; - var attr = ctx.ResolveProperty(record.AttributeId); var type = ctx.ResolveType(record.TypeId); var typeName = type.ToMarkupExtensionName(ctx, parent.Xaml); @@ -39,7 +38,7 @@ public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) var elemAttr = ctx.ResolveProperty(record.AttributeId); elem.Xaml = new XElement(elemAttr.ToXName(ctx, null)); - if (attr.ResolvedMember?.FullName == "System.Windows.Style.TargetType") + if (elemAttr.DeclaringType.TypeNamespace == "System.Windows" && elemAttr.DeclaringType.TypeName == "Style" && elemAttr.PropertyName == "TargetType") parent.Xaml.Element.AddAnnotation(new TargetTypeAnnotation(type)); elem.Xaml.Element.AddAnnotation(elemAttr); diff --git a/Extensions/dnSpy.BamlDecompiler/Xaml/XamlProperty.cs b/Extensions/dnSpy.BamlDecompiler/Xaml/XamlProperty.cs index 30432c7810..9b01623be8 100644 --- a/Extensions/dnSpy.BamlDecompiler/Xaml/XamlProperty.cs +++ b/Extensions/dnSpy.BamlDecompiler/Xaml/XamlProperty.cs @@ -41,8 +41,10 @@ public XamlProperty(XamlType type, string name) { } public void TryResolve() { - if (ResolvedMember is not null) + if (ResolvedMember is not null) { + ResolvedMemberDeclaringType ??= ResolvedMember.DeclaringType; return; + } (ResolvedMember, ResolvedMemberDeclaringType) = FindProperty(DeclaringType.ResolvedType, PropertyName); if (ResolvedMember is not null) From 1521cc620da098dc9e0dc1e5ce5d665729eecdf0 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 19 Jul 2023 15:13:36 +0200 Subject: [PATCH 032/122] 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 9ded9086436cd5f2b1149fd8ebeae66461fef4f4 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 19 Jul 2023 18:49:53 +0200 Subject: [PATCH 033/122] Update Iced to 1.20.0 --- DnSpyCommon.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 4ae3002753..13212d59ec 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -37,7 +37,7 @@ 1.7.0 3.6.0 - 1.19.0 + 1.20.0 17.1.0 1.1.142101 17.6.17 From 9bc1b501cbc91af5f6bd6bc0e3cefd463a3f7db3 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 19 Jul 2023 20:02:15 +0200 Subject: [PATCH 034/122] Fix incorrect `dynamic` type display in tooltips --- dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index 33ab87fa2c..685b02c789 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -886,11 +886,15 @@ void WriteNamespace(string ns) { } void Write(TypeSig? type, ParamDef? ownerParam, IList? typeGenArgs, IList? methGenArgs, bool forceReadOnly = false, IHasCustomAttribute? attributeProvider = null) { - WriteRefIfByRef(type, ownerParam, forceReadOnly); int dynamicTypeIndex = 0; - if (type.RemovePinnedAndModifiers() is ByRefSig byRef) { - type = byRef.Next; + while (type.RemovePinned() is ModifierSig mod) { + dynamicTypeIndex++; + type = mod.Next; + } + WriteRefIfByRef(type, ownerParam, forceReadOnly); + if (type is ByRefSig byRef) { dynamicTypeIndex++; + type = byRef.Next; } int tupleNameIndex = 0; int nativeIntIndex = 0; From 260f8d99c9c11abbefd6e3f685f10c53e46b345f Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 27 Jul 2023 22:20:10 +0200 Subject: [PATCH 035/122] Enable Visual Studio build acceleration --- DnSpyCommon.props | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 13212d59ec..588f5cb27c 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -23,6 +23,10 @@ win-x86;win-x64 cs;de;es;es-ES;fa;fr;hu;it;pt-BR;pt-PT;ru;tr;uk;zh-CN;vi + + true + true + 6.4.0.0 From 6514274202b2f75a5ec26ecf99e22f55df2c10ff Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 30 Jul 2023 12:21:45 +0200 Subject: [PATCH 036/122] Set `EnableWindowsTargeting` to `true` (#223) --- DnSpyCommon.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 588f5cb27c..15cc59d107 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -23,6 +23,9 @@ win-x86;win-x64 cs;de;es;es-ES;fa;fr;hu;it;pt-BR;pt-PT;ru;tr;uk;zh-CN;vi + + true + true true From 29a65a04fc564f15f4c2baadf8104af10d0eeb2d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 1 Aug 2023 11:01:46 +0200 Subject: [PATCH 037/122] 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 038/122] 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 d309109c2ab40de03c10bf12fe84142bcecbe03b Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 1 Aug 2023 14:24:23 +0200 Subject: [PATCH 039/122] Extend minimal corelib for filter expressions with additional primitives --- .../DbgFilterExpressionEvaluatorImpl.cs | 8 ++++++++ .../FilterExpressionEvaluator/EvalDelegateCreator.cs | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/DbgFilterExpressionEvaluatorImpl.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/DbgFilterExpressionEvaluatorImpl.cs index 9f914a48bb..00eefa5244 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/DbgFilterExpressionEvaluatorImpl.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/DbgFilterExpressionEvaluatorImpl.cs @@ -188,9 +188,17 @@ public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } + public struct Char { } + public struct Byte { } + public struct SByte { } + public struct Int16 { } + public struct UInt16 { } public struct Int32 { } public struct UInt32 { } + public struct Int64 { } public struct UInt64 { } + public struct Single { } + public struct Double { } public sealed class String { public static bool operator ==(String left, String right) => false; public static bool operator !=(String left, String right) => false; diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/EvalDelegateCreator.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/EvalDelegateCreator.cs index 2e83a51909..9dd0f587b2 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/EvalDelegateCreator.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/FilterExpressionEvaluator/EvalDelegateCreator.cs @@ -235,10 +235,19 @@ Type Import(TypeSig typeSig) { typeSig = typeSig.Next; switch (typeSig.ElementType) { case ElementType.Boolean: return typeof(bool); + case ElementType.Char: return typeof(char); + case ElementType.I1: return typeof(sbyte); + case ElementType.U1: return typeof(byte); + case ElementType.I2: return typeof(short); + case ElementType.U2: return typeof(ushort); case ElementType.I4: return typeof(int); case ElementType.U4: return typeof(uint); + case ElementType.I8: return typeof(long); case ElementType.U8: return typeof(ulong); + case ElementType.R4: return typeof(float); + case ElementType.R8: return typeof(double); case ElementType.String: return typeof(string); + case ElementType.Object: return typeof(object); default: throw new EvalDelegateCreatorException(); } } From f724f0d7dee27ee917ee837312b03dd44a3c777c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 1 Aug 2023 14:51:41 +0200 Subject: [PATCH 040/122] Use C# `ref` type syntax instead of IL `&` modifier in C# tooltips --- dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index 685b02c789..4db0ff005d 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -1013,8 +1013,9 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.ByRef: dynamicTypeIndex++; + OutputWrite(Keyword_ref, BoxedTextColor.Keyword); + WriteSpace(); Write(type.Next, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); - OutputWrite("&", BoxedTextColor.Operator); break; case ElementType.ValueType: From 5eaddbce4788257de7422113975f253178155647 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 2 Aug 2023 12:37:53 +0200 Subject: [PATCH 041/122] Fix decoding of `DynamicAttribute` and `NativeIntegerAttribute` --- ...ustomAttributeAdditionalTypeInfoProvider.cs | 4 ++-- dnSpy/dnSpy.Decompiler/TypeFormatterUtils.cs | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs index aa99170146..30b513c4bf 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CustomAttributeAdditionalTypeInfoProvider.cs @@ -41,7 +41,7 @@ public bool IsDynamicType(int typeIndex) { dynamicValuesInitialized = true; } - return dynamicMatchAll || dynamicValues is not null && typeIndex < dynamicValues.Count && dynamicValues[typeIndex].Value is bool b && b; + return dynamicMatchAll || dynamicValues is not null && (typeIndex >= dynamicValues.Count || dynamicValues[typeIndex].Value is bool b && b); } bool tupleElementNamesInitialized; @@ -87,7 +87,7 @@ public bool IsNativeIntegerType(int typeIndex) { nativeIntegerValuesInitialized = true; } - return nativeIntegerMatchAll || nativeIntegerValues is not null && typeIndex < nativeIntegerValues.Count && nativeIntegerValues[typeIndex].Value is bool b && b; + return nativeIntegerMatchAll || nativeIntegerValues is not null && (typeIndex >= nativeIntegerValues.Count || nativeIntegerValues[typeIndex].Value is bool b && b); } } } diff --git a/dnSpy/dnSpy.Decompiler/TypeFormatterUtils.cs b/dnSpy/dnSpy.Decompiler/TypeFormatterUtils.cs index 4a50cbb782..ffa62e9053 100644 --- a/dnSpy/dnSpy.Decompiler/TypeFormatterUtils.cs +++ b/dnSpy/dnSpy.Decompiler/TypeFormatterUtils.cs @@ -512,9 +512,12 @@ public static bool HasDynamicAttribute(IHasCustomAttribute? attributeProvider, i continue; if (a.ConstructorArguments.Count == 0) return true; - if (a.ConstructorArguments.Count == 1 && a.ConstructorArguments[0].Value is IList values && typeIndex < values.Count && - values[typeIndex].Value is bool b) - return b; + if (a.ConstructorArguments.Count == 1 && a.ConstructorArguments[0].Value is IList values) { + if (typeIndex >= values.Count) + return true; + if (values[typeIndex].Value is bool b) + return b; + } } return false; } @@ -551,9 +554,12 @@ public static bool HasNativeIntegerAttribute(IHasCustomAttribute? attributeProvi continue; if (a.ConstructorArguments.Count == 0) return true; - if (a.ConstructorArguments.Count == 1 && a.ConstructorArguments[0].Value is IList values && typeIndex < values.Count && - values[typeIndex].Value is bool b) - return b; + if (a.ConstructorArguments.Count == 1 && a.ConstructorArguments[0].Value is IList values) { + if (typeIndex >= values.Count) + return true; + if (values[typeIndex].Value is bool b) + return b; + } } return false; } From 05bf0b29edc50f3f253588392a615394c0073f58 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 2 Aug 2023 12:38:34 +0200 Subject: [PATCH 042/122] Refactor C# type formatting code --- .../CSharp/CSharpFormatter.cs | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index 4db0ff005d..431bed611a 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -885,23 +885,27 @@ void WriteNamespace(string ns) { } } + struct TypeState { + internal int DynamicTypeIndex; + internal int TupleNameIndex; + internal int NativeIntIndex; + } + void Write(TypeSig? type, ParamDef? ownerParam, IList? typeGenArgs, IList? methGenArgs, bool forceReadOnly = false, IHasCustomAttribute? attributeProvider = null) { - int dynamicTypeIndex = 0; + var state = new TypeState(); while (type.RemovePinned() is ModifierSig mod) { - dynamicTypeIndex++; + state.DynamicTypeIndex++; type = mod.Next; } WriteRefIfByRef(type, ownerParam, forceReadOnly); if (type is ByRefSig byRef) { - dynamicTypeIndex++; + state.DynamicTypeIndex++; type = byRef.Next; } - int tupleNameIndex = 0; - int nativeIntIndex = 0; - Write(type, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(type, typeGenArgs, methGenArgs, ref state, attributeProvider); } - void Write(TypeSig? type, IList? typeGenArgs, IList? methGenArgs, ref int dynamicTypeIndex, ref int tupleNameIndex, ref int nativeIntIndex, IHasCustomAttribute? attributeProvider) { + void Write(TypeSig? type, IList? typeGenArgs, IList? methGenArgs, ref TypeState state, IHasCustomAttribute? attributeProvider) { if (type is null) { WriteError(); return; @@ -921,8 +925,8 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr type = type.Next; } if (list is not null) { - dynamicTypeIndex += list.Count; - Write(list[list.Count - 1].Next, typeGenArgs, Array.Empty(), ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex += list.Count; + Write(list[list.Count - 1].Next, typeGenArgs, Array.Empty(), ref state, attributeProvider); foreach (var aryType in list) { if (aryType.ElementType == ElementType.Array) { OutputWrite(ArrayParenOpen, BoxedTextColor.Punctuation); @@ -981,7 +985,7 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.R8: WriteSystemTypeKeyword("Double", "double", true); break; case ElementType.String: WriteSystemTypeKeyword("String", "string", false); break; case ElementType.Object: - if (TypeFormatterUtils.HasDynamicAttribute(attributeProvider, dynamicTypeIndex)) + if (TypeFormatterUtils.HasDynamicAttribute(attributeProvider, state.DynamicTypeIndex)) OutputWrite("dynamic", BoxedTextColor.Keyword); else WriteSystemTypeKeyword("Object", "object", false); @@ -992,30 +996,30 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr break; case ElementType.I: - if (TypeFormatterUtils.HasNativeIntegerAttribute(attributeProvider, nativeIntIndex++)) + if (TypeFormatterUtils.HasNativeIntegerAttribute(attributeProvider, state.NativeIntIndex++)) OutputWrite("nint", BoxedTextColor.Keyword); else WriteSystemType("IntPtr", true); break; case ElementType.U: - if (TypeFormatterUtils.HasNativeIntegerAttribute(attributeProvider, nativeIntIndex++)) + if (TypeFormatterUtils.HasNativeIntegerAttribute(attributeProvider, state.NativeIntIndex++)) OutputWrite("nuint", BoxedTextColor.Keyword); else WriteSystemType("UIntPtr", true); break; case ElementType.Ptr: - dynamicTypeIndex++; - Write(type.Next, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(type.Next, typeGenArgs, methGenArgs, ref state, attributeProvider); OutputWrite("*", BoxedTextColor.Operator); break; case ElementType.ByRef: - dynamicTypeIndex++; + state.DynamicTypeIndex++; OutputWrite(Keyword_ref, BoxedTextColor.Keyword); WriteSpace(); - Write(type.Next, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(type.Next, typeGenArgs, methGenArgs, ref state, attributeProvider); break; case ElementType.ValueType: @@ -1028,7 +1032,7 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.MVar: var gsType = Read(type.ElementType == ElementType.Var ? typeGenArgs : methGenArgs, (int)((GenericSig)type).Number); if (gsType is not null && gsType != type) - Write(gsType, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(gsType, typeGenArgs, methGenArgs, ref state, attributeProvider); else { var gp = ((GenericSig)type).GenericParam; if (gp is not null) @@ -1050,14 +1054,14 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr var gis = (GenericInstSig?)type; Debug2.Assert(gis is not null); if (TypeFormatterUtils.IsSystemNullable(gis)) { - dynamicTypeIndex++; - Write(GenericArgumentResolver.Resolve(gis.GenericArguments[0], typeGenArgs, methGenArgs), null, null, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(GenericArgumentResolver.Resolve(gis.GenericArguments[0], typeGenArgs, methGenArgs), null, null, ref state, attributeProvider); OutputWrite("?", BoxedTextColor.Operator); break; } if (TypeFormatterUtils.IsSystemValueTuple(gis, out int tupleCardinality)) { - int localtupleNameIndex = tupleNameIndex; - tupleNameIndex += tupleCardinality; + int localTupleNameIndex = state.TupleNameIndex; + state.TupleNameIndex += tupleCardinality; if (tupleCardinality > 1) { OutputWrite(TupleParenOpen, BoxedTextColor.Punctuation); bool needComma = false; @@ -1066,9 +1070,9 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr if (needComma) WriteCommaSpace(); needComma = true; - dynamicTypeIndex++; - var elementName = TypeFormatterUtils.GetTupleElementNameAtIndex(attributeProvider, localtupleNameIndex++); - Write(GenericArgumentResolver.Resolve(gis.GenericArguments[j], typeGenArgs, methGenArgs), null, null, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + var elementName = TypeFormatterUtils.GetTupleElementNameAtIndex(attributeProvider, localTupleNameIndex++); + Write(GenericArgumentResolver.Resolve(gis.GenericArguments[j], typeGenArgs, methGenArgs), null, null, ref state, attributeProvider); if (elementName is not null) { WriteSpace(); OutputWrite(elementName, BoxedTextColor.InstanceField); @@ -1077,24 +1081,24 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr if (gis.GenericArguments.Count != 8) break; gis = gis.GenericArguments[gis.GenericArguments.Count - 1] as GenericInstSig; - dynamicTypeIndex++; + state.DynamicTypeIndex++; if (gis is null) { WriteError(); break; } - tupleNameIndex += TypeFormatterUtils.GetSystemValueTupleRank(gis); + state.TupleNameIndex += TypeFormatterUtils.GetSystemValueTupleRank(gis); } OutputWrite(TupleParenClose, BoxedTextColor.Punctuation); break; } } - Write(gis.GenericType, null, null, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(gis.GenericType, null, null, ref state, attributeProvider); OutputWrite(GenericParenOpen, BoxedTextColor.Punctuation); for (int i = 0; i < gis.GenericArguments.Count; i++) { if (i > 0) WriteCommaSpace(); - dynamicTypeIndex++; - Write(GenericArgumentResolver.Resolve(gis.GenericArguments[i], typeGenArgs, methGenArgs), null, null, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(GenericArgumentResolver.Resolve(gis.GenericArguments[i], typeGenArgs, methGenArgs), null, null, ref state, attributeProvider); } OutputWrite(GenericParenClose, BoxedTextColor.Punctuation); break; @@ -1102,16 +1106,16 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.FnPtr: var sig = ((FnPtrSig)type).MethodSig; - dynamicTypeIndex++; - Write(sig.RetType, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(sig.RetType, typeGenArgs, methGenArgs, ref state, attributeProvider); WriteSpace(); OutputWrite(MethodParenOpen, BoxedTextColor.Punctuation); for (int i = 0; i < sig.Params.Count; i++) { if (i > 0) WriteCommaSpace(); - dynamicTypeIndex++; - Write(sig.Params[i], typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(sig.Params[i], typeGenArgs, methGenArgs, ref state, attributeProvider); } if (sig.ParamsAfterSentinel is not null) { if (sig.Params.Count > 0) @@ -1119,7 +1123,7 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr OutputWrite("...", BoxedTextColor.Punctuation); for (int i = 0; i < sig.ParamsAfterSentinel.Count; i++) { WriteCommaSpace(); - Write(sig.ParamsAfterSentinel[i], typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(sig.ParamsAfterSentinel[i], typeGenArgs, methGenArgs, ref state, attributeProvider); } } OutputWrite(MethodParenClose, BoxedTextColor.Punctuation); @@ -1127,12 +1131,12 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.CModReqd: case ElementType.CModOpt: - dynamicTypeIndex++; - Write(type.Next, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + state.DynamicTypeIndex++; + Write(type.Next, typeGenArgs, methGenArgs, ref state, attributeProvider); break; case ElementType.Pinned: - Write(type.Next, typeGenArgs, methGenArgs, ref dynamicTypeIndex, ref tupleNameIndex, ref nativeIntIndex, attributeProvider); + Write(type.Next, typeGenArgs, methGenArgs, ref state, attributeProvider); break; case ElementType.End: From 27ee5bc2c9f27c601478fb48e7b98180cd92e26a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 2 Aug 2023 13:56:48 +0200 Subject: [PATCH 043/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 1115e23a39..9692259796 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 1115e23a39b33864ab3a2c5a716ab9a20ce40dcb +Subproject commit 96922597965d866204c6646e636cc61ae571abca From 8f808c5a58531e3ddfa842f0ef8a528b4b34aa38 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 2 Aug 2023 15:02:02 +0200 Subject: [PATCH 044/122] Use C# 9 function pointer syntax in tooltips --- .../CSharp/CSharpFormatter.cs | 185 ++++++++++++++++-- 1 file changed, 169 insertions(+), 16 deletions(-) diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index 431bed611a..529f5173f4 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -78,6 +78,10 @@ public struct CSharpFormatter { const string GenericParenClose = ">"; const string DefaultParamValueParenOpen = "["; const string DefaultParamValueParenClose = "]"; + const string FunctionPointerParenOpen = "<"; + const string FunctionPointerParenClose = ">"; + const string FunctionPointerCallConvParenOpen = "["; + const string FunctionPointerCallConvParenClose = "]"; int recursionCounter; int lineLength; @@ -675,7 +679,7 @@ void Write(PropertyDef? prop, bool writeAccessors) { } if (prop.SetMethods.Count > 0) { if (prop.SetMethods.Count == 1 && prop.SetMethod.ReturnType is CModReqdSig modReq && - modReq.Modifier.FullName == "System.Runtime.CompilerServices.IsExternalInit") { + FullNameFactory.FullName(modReq.Modifier, false, null, sb.Clear()) == "System.Runtime.CompilerServices.IsExternalInit") { WriteSpace(); OutputWrite(Keyword_init, BoxedTextColor.Keyword); OutputWrite(";", BoxedTextColor.Punctuation); @@ -1106,27 +1110,107 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr case ElementType.FnPtr: var sig = ((FnPtrSig)type).MethodSig; + OutputWrite(Keyword_delegate, BoxedTextColor.Keyword); + OutputWrite("*", BoxedTextColor.Operator); + + if (sig.IsUnmanaged || !sig.IsDefault) { + WriteSpace(); + OutputWrite("unmanaged", BoxedTextColor.Keyword); + } + + var returnType = sig.GetRetType().RemovePinned(); + var customCallConvs = new List(); + while (returnType is ModifierSig modReturn) { + if (modReturn.Modifier.Name.StartsWith("CallConv", StringComparison.Ordinal) && modReturn.Modifier.Namespace == "System.Runtime.CompilerServices") { + returnType = modReturn.Next.RemovePinned(); + state.DynamicTypeIndex++; + customCallConvs.Add(modReturn.Modifier); + } + else + break; + } + + bool needsComma = false; + if (!sig.IsDefault || customCallConvs.Count > 0) { + OutputWrite(FunctionPointerCallConvParenOpen, BoxedTextColor.Punctuation); + if (!sig.IsDefault) { + OutputWrite((sig.CallingConvention & CallingConvention.Mask) switch { + CallingConvention.C => "Cdecl", + CallingConvention.StdCall => "Stdcall", + CallingConvention.ThisCall => "Thiscall", + CallingConvention.FastCall => "Fastcall", + CallingConvention.VarArg => "Varargs", + _ => sig.CallingConvention.ToString() + }, BoxedTextColor.Keyword); + needsComma = true; + } + + for (var i = 0; i < customCallConvs.Count; i++) { + if (needsComma) + WriteCommaSpace(); + needsComma = true; + var customCallConv = customCallConvs[i]; + if (customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && + customCallConv.Name.Length > 8) + OutputWrite(customCallConv.Name.Substring(8), BoxedTextColor.Keyword); + else + Write(customCallConv); + } + + OutputWrite(FunctionPointerCallConvParenClose, BoxedTextColor.Punctuation); + } + + OutputWrite(FunctionPointerParenOpen, BoxedTextColor.Punctuation); + state.DynamicTypeIndex++; - Write(sig.RetType, typeGenArgs, methGenArgs, ref state, attributeProvider); + var parametersState = state; + UpdateTypeState(returnType, ref parametersState); - WriteSpace(); - OutputWrite(MethodParenOpen, BoxedTextColor.Punctuation); + needsComma = false; for (int i = 0; i < sig.Params.Count; i++) { - if (i > 0) - WriteCommaSpace(); - state.DynamicTypeIndex++; - Write(sig.Params[i], typeGenArgs, methGenArgs, ref state, attributeProvider); - } - if (sig.ParamsAfterSentinel is not null) { - if (sig.Params.Count > 0) - WriteCommaSpace(); - OutputWrite("...", BoxedTextColor.Punctuation); - for (int i = 0; i < sig.ParamsAfterSentinel.Count; i++) { + if (needsComma) WriteCommaSpace(); - Write(sig.ParamsAfterSentinel[i], typeGenArgs, methGenArgs, ref state, attributeProvider); + needsComma = true; + + parametersState.DynamicTypeIndex++; + var paramType = sig.Params[i].RemovePinned(); + bool modifierWritten = false; + if (paramType is CModReqdSig modreq) { + string modifier = FullNameFactory.FullName(modreq.Modifier, false, null, sb.Clear()); + if (modifier == "System.Runtime.InteropServices.InAttribute") { + modifierWritten = true; + OutputWrite(Keyword_in, BoxedTextColor.Keyword); + WriteSpace(); + parametersState.DynamicTypeIndex++; + paramType = modreq.Next.RemovePinned(); + } + else if (modifier == "System.Runtime.InteropServices.OutAttribute") { + modifierWritten = true; + OutputWrite(Keyword_out, BoxedTextColor.Keyword); + WriteSpace(); + parametersState.DynamicTypeIndex++; + paramType = modreq.Next.RemovePinned(); + } } + if (paramType is ByRefSig byRef) { + if (!modifierWritten) { + OutputWrite(Keyword_ref, BoxedTextColor.Keyword); + WriteSpace(); + } + parametersState.DynamicTypeIndex++; + paramType = byRef.Next; + } + + Write(paramType, typeGenArgs, methGenArgs, ref parametersState, attributeProvider); } - OutputWrite(MethodParenClose, BoxedTextColor.Punctuation); + + if (needsComma) + WriteCommaSpace(); + Write(returnType, typeGenArgs, methGenArgs, ref state, attributeProvider); + + OutputWrite(FunctionPointerParenClose, BoxedTextColor.Punctuation); + + state = parametersState; break; case ElementType.CModReqd: @@ -1156,6 +1240,75 @@ void Write(TypeSig? type, IList? typeGenArgs, IList? methGenAr } } + static void UpdateTypeState(TypeSig type, ref TypeState state) { + type = type.RemovePinned(); + while (type is NonLeafSig) { + switch (type.ElementType) { + case ElementType.Array: + case ElementType.SZArray: + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.CModOpt: + case ElementType.CModReqd: + state.DynamicTypeIndex++; + break; + } + type = type.Next.RemovePinned(); + } + + switch (type.ElementType) { + case ElementType.I: + case ElementType.U: + state.NativeIntIndex++; + return; + case ElementType.GenericInst: + var gis = (GenericInstSig?)type; + Debug2.Assert(gis is not null); + if (TypeFormatterUtils.IsSystemNullable(gis)) { + state.DynamicTypeIndex++; + UpdateTypeState(gis.GenericArguments[0], ref state); + break; + } + if (TypeFormatterUtils.IsSystemValueTuple(gis, out int tupleCardinality)) { + state.TupleNameIndex += tupleCardinality; + if (tupleCardinality > 1) { + for (int i = 0; i < 1000; i++) { + for (int j = 0; j < gis.GenericArguments.Count && j < 7; j++) { + state.DynamicTypeIndex++; + UpdateTypeState(gis.GenericArguments[j], ref state); + } + if (gis.GenericArguments.Count != 8) + break; + gis = gis.GenericArguments[gis.GenericArguments.Count - 1] as GenericInstSig; + state.DynamicTypeIndex++; + if (gis is null) + break; + state.TupleNameIndex += TypeFormatterUtils.GetSystemValueTupleRank(gis); + } + break; + } + } + + UpdateTypeState(gis.GenericType, ref state); + for (int i = 0; i < gis.GenericArguments.Count; i++) { + state.DynamicTypeIndex++; + UpdateTypeState(gis.GenericArguments[i], ref state); + } + break; + case ElementType.FnPtr: + var sig = ((FnPtrSig)type).MethodSig; + + state.DynamicTypeIndex++; + UpdateTypeState(sig.RetType, ref state); + + for (int i = 0; i < sig.Params.Count; i++) { + state.DynamicTypeIndex++; + UpdateTypeState(sig.Params[i], ref state); + } + break; + } + } + static TypeSig? Read(IList list, int index) { if ((uint)index < (uint)list.Count) return list[index]; From 2fd18f158c2eb9028245536e2084ca8c04de5b7c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 3 Aug 2023 21:03:06 +0200 Subject: [PATCH 045/122] 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 046/122] 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 047/122] 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 048/122] 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 049/122] 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 050/122] 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; From d09883118624cd2e66f5b4572abf830afefeda3d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 12 Aug 2023 21:41:27 +0200 Subject: [PATCH 051/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 9692259796..cb5b7d7d7f 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 96922597965d866204c6646e636cc61ae571abca +Subproject commit cb5b7d7d7f5969d0b55383fe3ed9c4ae9744ba50 From 039c51add05ca8ad9a54b10edcc3521ebc7b8062 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 13 Aug 2023 14:38:47 +0200 Subject: [PATCH 052/122] Format function pointers using C# 9 syntax in debugger tool windows --- .../Formatters/CSharp/CSharpTypeFormatter.cs | 124 +++++++++++++++--- .../Debugger/Formatters/TypeFormatterUtils.cs | 45 ++++++- .../ValueNodes/TupleValueNodeProvider.cs | 39 +----- .../CSharp/CSharpFormatter.cs | 25 +--- 4 files changed, 150 insertions(+), 83 deletions(-) diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs index 2499f11955..7775d2f994 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/CSharp/CSharpTypeFormatter.cs @@ -40,14 +40,17 @@ struct CSharpTypeFormatter { const string GENERICS_CLOSE_PAREN = ">"; const string TUPLE_OPEN_PAREN = "("; const string TUPLE_CLOSE_PAREN = ")"; - const string METHOD_OPEN_PAREN = "("; - const string METHOD_CLOSE_PAREN = ")"; + const string FNPTR_CALLCONV_OPEN_PAREN = "["; + const string FNPTR_CALLCONV_CLOSE_PAREN = "]"; + const string FNPTR_OPEN_PAREN = "<"; + const string FNPTR_CLOSE_PAREN = ">"; const string HEX_PREFIX = "0x"; const string IDENTIFIER_ESCAPE = "@"; const string BYREF_KEYWORD = "ref"; const string IN_KEYWORD = "in"; const string OUT_KEYWORD = "out"; const string READONLY_KEYWORD = "readonly"; + const string DELEGATE_KEYWORD = "delegate"; const int MAX_ARRAY_RANK = 100; bool ShowArrayValueSizes => (options & TypeFormatterOptions.ShowArrayValueSizes) != 0; @@ -309,28 +312,109 @@ void FormatCore(DmdType type, DbgDotNetValue? value, ref AdditionalTypeInfoState case DmdTypeSignatureKind.FunctionPointer: var sig = type.GetFunctionPointerMethodSignature(); - state.DynamicTypeIndex++; - 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(); - state.DynamicTypeIndex++; - FormatCore(types[i], null, ref state); + + OutputWrite(DELEGATE_KEYWORD, DbgTextColor.Keyword); + OutputWrite("*", DbgTextColor.Operator); + + bool isUnmanaged = (sig.Flags & DmdSignatureCallingConvention.Mask) == DmdSignatureCallingConvention.Unmanaged; + bool isDefault = (sig.Flags & DmdSignatureCallingConvention.Mask) == DmdSignatureCallingConvention.Default; + if (isUnmanaged || !isDefault) { + WriteSpace(); + OutputWrite("unmanaged", DbgTextColor.Keyword); } - types = sig.GetVarArgsParameterTypes(); - if (types.Count > 0) { - if (sig.GetParameterTypes().Count > 0) - WriteCommaSpace(); - OutputWrite("...", DbgTextColor.Punctuation); - for (int i = 0; i < types.Count; i++) { + + var returnType = sig.ReturnType; + var customCallConvs = new List(); + foreach (var modifier in returnType.GetCustomModifiers()) { + if (modifier.Type.Name.StartsWith("CallConv", StringComparison.Ordinal) && modifier.Type.Namespace == "System.Runtime.CompilerServices") { + state.DynamicTypeIndex++; + customCallConvs.Add(modifier.Type); + } + else + break; + } + + bool needsComma = false; + if (!isDefault || customCallConvs.Count > 0) { + OutputWrite(FNPTR_CALLCONV_OPEN_PAREN, DbgTextColor.Punctuation); + if (!isDefault) { + OutputWrite((sig.Flags & DmdSignatureCallingConvention.Mask) switch { + DmdSignatureCallingConvention.C => "Cdecl", + DmdSignatureCallingConvention.StdCall => "Stdcall", + DmdSignatureCallingConvention.ThisCall => "Thiscall", + DmdSignatureCallingConvention.FastCall => "Fastcall", + DmdSignatureCallingConvention.VarArg => "Varargs", + _ => sig.Flags.ToString() + }, DbgTextColor.Keyword); + needsComma = true; + } + + for (var i = 0; i < customCallConvs.Count; i++) { + if (needsComma) + WriteCommaSpace(); + needsComma = true; + var customCallConv = customCallConvs[i]; + if (customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && + customCallConv.Name.Length > 8) + OutputWrite(customCallConv.Name.Substring(8), DbgTextColor.Keyword); + else + Format(customCallConv); + } + + OutputWrite(FNPTR_CALLCONV_CLOSE_PAREN, DbgTextColor.Punctuation); + } + + OutputWrite(FNPTR_OPEN_PAREN, DbgTextColor.Punctuation); + + state.DynamicTypeIndex++; + var parametersState = state; + TypeFormatterUtils.UpdateTypeState(returnType, ref parametersState); + + needsComma = false; + var parameters = sig.GetParameterTypes(); + for (int i = 0; i < parameters.Count; i++) { + if (needsComma) WriteCommaSpace(); - FormatCore(types[i], null, ref state); + needsComma = true; + + parametersState.DynamicTypeIndex++; + var paramType = parameters[i]; + bool modifierWritten = false; + + var modifiers = paramType.GetRequiredCustomModifiers(); + + if (modifiers.Length > 0) { + string? modifier = modifiers[0].FullName; + if (modifier == "System.Runtime.InteropServices.InAttribute") { + modifierWritten = true; + OutputWrite(IN_KEYWORD, DbgTextColor.Keyword); + WriteSpace(); + parametersState.DynamicTypeIndex++; + } + else if (modifier == "System.Runtime.InteropServices.OutAttribute") { + modifierWritten = true; + OutputWrite(OUT_KEYWORD, DbgTextColor.Keyword); + WriteSpace(); + parametersState.DynamicTypeIndex++; + } + } + if (paramType.IsByRef) { + if (!modifierWritten) { + OutputWrite(BYREF_KEYWORD, DbgTextColor.Keyword); + WriteSpace(); + } + parametersState.DynamicTypeIndex++; + paramType = paramType.GetElementType()!; } + + FormatCore(paramType, null, ref parametersState); } - OutputWrite(METHOD_CLOSE_PAREN, DbgTextColor.Punctuation); + + if (needsComma) + WriteCommaSpace(); + FormatCore(returnType, null, ref state); + + OutputWrite(FNPTR_CLOSE_PAREN, DbgTextColor.Punctuation); break; default: diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/TypeFormatterUtils.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/TypeFormatterUtils.cs index 980155538b..c6ae7ff6d9 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/TypeFormatterUtils.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/Formatters/TypeFormatterUtils.cs @@ -116,7 +116,7 @@ public static string RemoveGenericTick(string s) { return s.Substring(0, index); } - public static (DmdPropertyInfo? property, AccessorKind kind) TryGetProperty(DmdMethodBase method) { + public static (DmdPropertyInfo? property, AccessorKind kind) TryGetProperty(DmdMethodBase? method) { if (method is null) return (null, AccessorKind.None); foreach (var p in method.DeclaringType!.Properties) { @@ -128,7 +128,7 @@ public static (DmdPropertyInfo? property, AccessorKind kind) TryGetProperty(DmdM return (null, AccessorKind.None); } - public static (DmdEventInfo? @event, AccessorKind kind) TryGetEvent(DmdMethodBase method) { + public static (DmdEventInfo? @event, AccessorKind kind) TryGetEvent(DmdMethodBase? method) { if (method is null) return (null, AccessorKind.None); foreach (var e in method.DeclaringType!.Events) { @@ -252,6 +252,47 @@ static bool HasIsReadOnlyAttribute(ReadOnlyCollection cu } return false; } + + public 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: + if (type == type.AppDomain.System_IntPtr || type == type.AppDomain.System_UIntPtr) + state.NativeIntTypeIndex++; + return; + case DmdTypeSignatureKind.TypeGenericParameter: + case DmdTypeSignatureKind.MethodGenericParameter: + return; + case DmdTypeSignatureKind.GenericInstance: + if (IsSystemValueTuple(type, out int cardinality)) + state.TupleNameIndex += cardinality; + + UpdateTypeState(type.GetGenericTypeDefinition(), ref state); + 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; + } + } } enum AccessorKind { diff --git a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs index 4ff8d34987..dfafc8f7e4 100644 --- a/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs +++ b/dnSpy/Roslyn/dnSpy.Roslyn/Debugger/ValueNodes/TupleValueNodeProvider.cs @@ -66,7 +66,7 @@ AdditionalTypeInfoState GetCachedTypeInfoState(int tupleFieldIndex) { var typeInfo = cachedTypeInfoStates[cachedIndex]; while (cachedIndex < tupleFieldIndex) { ref readonly var previousField = ref tupleFields[cachedIndex]; - UpdateTypeState(previousField.Fields[previousField.Fields.Length - 1].FieldType, ref typeInfo); + TypeFormatterUtils.UpdateTypeState(previousField.Fields[previousField.Fields.Length - 1].FieldType, ref typeInfo); typeInfo.DynamicTypeIndex++; @@ -86,43 +86,6 @@ AdditionalTypeInfoState GetCachedTypeInfoState(int tupleFieldIndex) { 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]; diff --git a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs index 529f5173f4..7bd1e1f093 100644 --- a/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs +++ b/dnSpy/dnSpy.Decompiler/CSharp/CSharpFormatter.cs @@ -1264,30 +1264,9 @@ static void UpdateTypeState(TypeSig type, ref TypeState state) { case ElementType.GenericInst: var gis = (GenericInstSig?)type; Debug2.Assert(gis is not null); - if (TypeFormatterUtils.IsSystemNullable(gis)) { - state.DynamicTypeIndex++; - UpdateTypeState(gis.GenericArguments[0], ref state); - break; - } - if (TypeFormatterUtils.IsSystemValueTuple(gis, out int tupleCardinality)) { + + if (TypeFormatterUtils.IsSystemValueTuple(gis, out int tupleCardinality)) state.TupleNameIndex += tupleCardinality; - if (tupleCardinality > 1) { - for (int i = 0; i < 1000; i++) { - for (int j = 0; j < gis.GenericArguments.Count && j < 7; j++) { - state.DynamicTypeIndex++; - UpdateTypeState(gis.GenericArguments[j], ref state); - } - if (gis.GenericArguments.Count != 8) - break; - gis = gis.GenericArguments[gis.GenericArguments.Count - 1] as GenericInstSig; - state.DynamicTypeIndex++; - if (gis is null) - break; - state.TupleNameIndex += TypeFormatterUtils.GetSystemValueTupleRank(gis); - } - break; - } - } UpdateTypeState(gis.GenericType, ref state); for (int i = 0; i < gis.GenericArguments.Count; i++) { From e9a57f05245614381a5b7fd30d55173e09ff18ed Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 20 Aug 2023 12:12:21 +0200 Subject: [PATCH 053/122] Remove link to nonexistent unity mono binaries --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 923993e4a0..92076fb479 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ cd dnSpy ./build.ps1 -NoMsbuild ``` -To debug Unity games, you need this repo too: https://github.com/dnSpyEx/dnSpy-Unity-mono (or get the binaries from https://github.com/dnSpyEx/dnSpy/releases/unity) +To debug Unity games, you need this repo too: https://github.com/dnSpyEx/dnSpy-Unity-mono # Debugger From bbfac1e2ea615b4d4d11211466997ef51f91a59b Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 21 Aug 2023 21:23:19 +0200 Subject: [PATCH 054/122] Update dependencies Microsoft.VisualStudio.Composition to 17.7.18 NuGet.Configuration to 6.7.0 --- DnSpyCommon.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 15cc59d107..4424e6d44d 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -47,14 +47,14 @@ 1.20.0 17.1.0 1.1.142101 - 17.6.17 + 17.7.18 15.5.27130 15.5.27130 13.0.3 5.0.1 4.6.0 7.0.0 - 6.6.1 + 6.7.0 From 3de5a553ef76bf41a4cbfbcfc1d2b04e2788734f Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 21 Aug 2023 21:46:11 +0200 Subject: [PATCH 055/122] Update Roslyn compiler to 4.7.0 --- DnSpyCommon.props | 2 +- dnSpy/Roslyn/Roslyn.ExpressionCompiler | 2 +- dnSpy/dnSpy/app.config | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 4424e6d44d..6b7a8c6dba 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -52,7 +52,7 @@ 15.5.27130 13.0.3 5.0.1 - 4.6.0 + 4.7.0 7.0.0 6.7.0 diff --git a/dnSpy/Roslyn/Roslyn.ExpressionCompiler b/dnSpy/Roslyn/Roslyn.ExpressionCompiler index 3f5e4ca312..5886889453 160000 --- a/dnSpy/Roslyn/Roslyn.ExpressionCompiler +++ b/dnSpy/Roslyn/Roslyn.ExpressionCompiler @@ -1 +1 @@ -Subproject commit 3f5e4ca312c698eee81e8558805734e3920bdec1 +Subproject commit 58868894530261dc66312af780ac805c91967be6 diff --git a/dnSpy/dnSpy/app.config b/dnSpy/dnSpy/app.config index eb10372c77..8efdeab870 100644 --- a/dnSpy/dnSpy/app.config +++ b/dnSpy/dnSpy/app.config @@ -50,55 +50,55 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + From 98764167422e3dc3358946c45ae42e7d68488efd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 23:01:31 +0300 Subject: [PATCH 056/122] Bump actions/checkout from 3.5.3 to 3.6.0 (#236) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.5.3...v3.6.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d67541cd2..63b999bb8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: build-dir: net6.0-windows\win-x64\publish steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v3.6.0 with: submodules: true From e08de1db25d57766aa81ee452811ebf9d99c7d6a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 31 Aug 2023 15:52:58 +0200 Subject: [PATCH 057/122] Add binding redirects to make older extensions load effortlessly --- dnSpy/dnSpy/app.config | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dnSpy/dnSpy/app.config b/dnSpy/dnSpy/app.config index 8efdeab870..cdb732c52e 100644 --- a/dnSpy/dnSpy/app.config +++ b/dnSpy/dnSpy/app.config @@ -47,6 +47,14 @@ + + + + + + + + @@ -109,6 +117,14 @@ + + + + + + + + From f9ab0e16c54b1d86ce2c2d06738115174707666e Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 2 Sep 2023 14:21:42 +0200 Subject: [PATCH 058/122] Update to dnlib 4.1.0 --- DnSpyCommon.props | 2 +- Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs | 2 +- Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs | 2 +- .../Documents/TreeView/Resources/SerializationUtilities.cs | 2 +- .../TreeView/Resources/SerializedImageListStreamerUtilities.cs | 2 +- .../Documents/TreeView/Resources/SerializedImageUtilities.cs | 2 +- .../Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs | 2 +- dnSpy/dnSpy/app.config | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index 6b7a8c6dba..ddc935493e 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -43,7 +43,7 @@ 1.7.0 - 3.6.0 + 4.1.0 1.20.0 17.1.0 1.1.142101 diff --git a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs index 8dffff5754..8bc9bf5ee4 100644 --- a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs +++ b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs @@ -766,7 +766,7 @@ static void Execute(Lazy undoCommandService, IAppService ap return; var outStream = new MemoryStream(); - ResourceWriter.Write(module, outStream, new ResourceElementSet()); + ResourceWriter.Write(module, outStream, ResourceElementSet.CreateForResourceReader(module)); var er = new EmbeddedResource(data.Name, outStream.ToArray(), data.Attributes); var treeView = appService.DocumentTreeView.TreeView; var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup); diff --git a/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs b/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs index 1c916de69e..8f9fd38118 100644 --- a/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs +++ b/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs @@ -292,7 +292,7 @@ IResourceData CreateResourceData() { case ResourceElementType.TimeSpan: return new BuiltInResourceData((ResourceTypeCode)code, TimeSpanVM.Value); case ResourceElementType.ByteArray: return new BuiltInResourceData((ResourceTypeCode)code, Data ?? Array.Empty()); case ResourceElementType.Stream: return new BuiltInResourceData((ResourceTypeCode)code, Data ?? Array.Empty()); - case ResourceElementType.SerializedType: return new BinaryResourceData(new UserResourceType(UserTypeVM.TypeFullName, ResourceTypeCode.UserTypes), UserTypeVM.GetSerializedData()); + case ResourceElementType.SerializedType: return new BinaryResourceData(new UserResourceType(UserTypeVM.TypeFullName, ResourceTypeCode.UserTypes), UserTypeVM.GetSerializedData(), SerializationFormat.BinaryFormatter); default: throw new InvalidOperationException(); } } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs index a7e7269c51..e9dfbae570 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs @@ -56,7 +56,7 @@ static ResourceElement CreateSerializedImage(Stream stream, string filename) { var userType = new UserResourceType(typeName, ResourceTypeCode.UserTypes); var rsrcElem = new ResourceElement { Name = Path.GetFileName(filename), - ResourceData = new BinaryResourceData(userType, serializedData), + ResourceData = new BinaryResourceData(userType, serializedData, SerializationFormat.BinaryFormatter), }; return rsrcElem; diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs index 020d38944e..d7641656d6 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs @@ -93,7 +93,7 @@ public static ResourceElement Serialize(ImageListOptions opts) { var typeName = SystemWindowsFormsImageListStreamer.AssemblyQualifiedName; return new ResourceElement { Name = opts.Name, - ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj)), + ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj), SerializationFormat.BinaryFormatter), }; } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs index f9bcf38ee9..de4d0add50 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs @@ -116,7 +116,7 @@ public static ResourceElement Serialize(ResourceElement resElem) { return new ResourceElement { Name = resElem.Name, - ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj)), + ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj), SerializationFormat.BinaryFormatter), }; } } diff --git a/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs b/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs index 0db9a99c2c..0fe368892a 100644 --- a/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs +++ b/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs @@ -99,7 +99,7 @@ public override void RegenerateEmbeddedResource() { void RegenerateEmbeddedResource(ModuleDef module) { TreeNode.EnsureChildrenLoaded(); var outStream = new MemoryStream(); - var resources = new ResourceElementSet(); + var resources = resourceElementSet.Clone(); foreach (DocumentTreeNodeData child in TreeNode.DataChildren) { var resourceElement = ResourceElementNode.GetResourceElement(child); if (resourceElement is null) diff --git a/dnSpy/dnSpy/app.config b/dnSpy/dnSpy/app.config index cdb732c52e..16f54a9c01 100644 --- a/dnSpy/dnSpy/app.config +++ b/dnSpy/dnSpy/app.config @@ -45,7 +45,7 @@ - + From ebd6935bbed29d34b6793694f991bb245440c4c3 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 3 Sep 2023 21:39:15 +0200 Subject: [PATCH 059/122] Add RESX support for additional resource element serialization formats --- .../MSBuild/ResXResourceFileWriter.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs b/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs index 717cc87765..94fd9187e3 100644 --- a/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs +++ b/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs @@ -1,4 +1,4 @@ -/* +/* Copyright (C) 2023 ElektroKill This file is part of dnSpy @@ -182,8 +182,19 @@ ResXResourceInfo GetNodeInfo(IResourceData resourceData) { throw new ArgumentOutOfRangeException(); } } - if (resourceData is BinaryResourceData binaryResourceData) - return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.BinSerializedObjectMimeType); + if (resourceData is BinaryResourceData binaryResourceData) { + switch (binaryResourceData.Format) { + case SerializationFormat.BinaryFormatter: + return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.BinSerializedObjectMimeType); + case SerializationFormat.TypeConverterByteArray: + case SerializationFormat.ActivatorStream: + // RESX does not have a way to represent creation of an object using Activator.CreateInstance, + // so we fallback to the same representation as data passed into TypeConverter. + return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.ByteArraySerializedObjectMimeType); + case SerializationFormat.TypeConverterString: + return new ResXResourceInfo(Encoding.UTF8.GetString(binaryResourceData.Data), binaryResourceData.TypeName); + } + } throw new ArgumentOutOfRangeException(); } From 4be7b3e1c541ebafb1b7822b40dbb81526a38d4d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 3 Sep 2023 21:55:15 +0200 Subject: [PATCH 060/122] Add support for deserializing images stored in newer formats --- .../Resources/SerializedImageUtilities.cs | 83 +++++++++++++++---- .../SerializedImageResourceElementNode.cs | 6 +- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs index de4d0add50..a1675056b2 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Text; using dnlib.DotNet; using dnlib.DotNet.Resources; @@ -36,34 +37,84 @@ public static class SerializedImageUtilities { /// Serialized data /// Updated with the image data /// - public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) { + public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) => + GetImageData(module, typeName, serializedData, SerializationFormat.BinaryFormatter, out imageData); + + /// + /// Gets the image data + /// + /// Module + /// Name of type + /// Serialized data + /// Format of serialized data + /// Updated with the image data + /// + public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, SerializationFormat format, [NotNullWhen(true)] out byte[]? imageData) { imageData = null; + if (CouldBeBitmap(module, typeName)) { - var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData); - // Bitmap loops over every item looking for "Data" (case insensitive) - foreach (var v in dict.Values) { - var d = v.Value as byte[]; - if (d is null) - continue; - if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) { - imageData = d; - return true; + if (format == SerializationFormat.BinaryFormatter) { + var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData); + // Bitmap loops over every item looking for "Data" (case insensitive) + foreach (var v in dict.Values) { + var d = v.Value as byte[]; + if (d is null) + continue; + if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) { + imageData = d; + return true; + } } + return false; + } + if (format == SerializationFormat.ActivatorStream) { + imageData = serializedData; + return true; + } + if (format == SerializationFormat.TypeConverterByteArray) { + imageData = GetBitmapData(serializedData) ?? serializedData; + return true; } - return false; } if (CouldBeIcon(module, typeName)) { - var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData); - if (!dict.TryGetValue("IconData", out var info)) - return false; - imageData = info.Value as byte[]; - return imageData is not null; + if (format == SerializationFormat.BinaryFormatter) { + var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData); + if (!dict.TryGetValue("IconData", out var info)) + return false; + imageData = info.Value as byte[]; + return imageData is not null; + } + if (format == SerializationFormat.ActivatorStream || format == SerializationFormat.TypeConverterByteArray) { + imageData = serializedData; + return true; + } } return false; } + static byte[]? GetBitmapData(byte[] rawData) { + // Based on ImageConverter.GetBitmapStream + // See https://github.com/dotnet/winforms/blob/main/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs + if (rawData.Length <= 18) + return null; + + short sig = (short)(rawData[0] | rawData[1] << 8); + if (sig != 0x1C15) + return null; + + short headerSize = (short)(rawData[2] | rawData[3] << 8); + if (rawData.Length <= headerSize + 18) + return null; + if (Encoding.ASCII.GetString(rawData, headerSize + 12, 6) != "PBrush") + return null; + + var newData = new byte[rawData.Length - 78]; + Buffer.BlockCopy(rawData, 78, newData, 0, newData.Length); + return newData; + } + static bool CouldBeBitmap(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingBitmap); static bool CouldBeIcon(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingIcon); diff --git a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs index d17a7ef783..045ad294e8 100644 --- a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs +++ b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs @@ -42,7 +42,7 @@ sealed class SerializedImageResourceElementNodeProvider : IResourceNodeProvider if (serializedData is null) return null; - if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, out var imageData)) + if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, serializedData.Format, out var imageData)) return new SerializedImageResourceElementNodeImpl(treeNodeGroup, resourceElement, imageData); return null; @@ -94,7 +94,7 @@ protected override IEnumerable GetDeserializedData() { return res; var binData = (BinaryResourceData)newResElem.ResourceData; - if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData)) + if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData)) return dnSpy_Resources.NewDataIsNotAnImage; try { @@ -111,7 +111,7 @@ public override void UpdateData(ResourceElement newResElem) { base.UpdateData(newResElem); var binData = (BinaryResourceData)newResElem.ResourceData; - SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData); + SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData); Debug2.Assert(imageData is not null); InitializeImageData(imageData); } From efe9817fc156ca7aac540f176fc54f5858dbb781 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Tue, 5 Sep 2023 20:31:17 +0200 Subject: [PATCH 061/122] Extend support for new serialization formats --- .../Resources/ResourceCommands.cs | 3 +- .../Resources/SerializedImageUtilities.cs | 33 ++++++++++- .../SerializedResourceElementNode.cs | 58 +++++++++++++++++-- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs index 8bc9bf5ee4..430cd24bb6 100644 --- a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs +++ b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs @@ -2038,7 +2038,8 @@ static void Execute(Lazy undoCommandService, IAppService ap var opts = data.CreateResourceElementOptions(); string? error; try { - opts = new ResourceElementOptions(SerializedImageUtilities.Serialize(opts.Create())); + var format = ((BinaryResourceData)imgRsrcElNode.ResourceElement.ResourceData).Format; + opts = new ResourceElementOptions(SerializedImageUtilities.Serialize(opts.Create(), format)); error = imgRsrcElNode.CheckCanUpdateData(opts.Create()); } catch (Exception ex) { diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs index a1675056b2..ef778c4e7f 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License */ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; @@ -150,7 +151,15 @@ public static bool CheckType(ModuleDef? module, string name, TypeRef expectedTyp /// /// Resource element /// - public static ResourceElement Serialize(ResourceElement resElem) { + public static ResourceElement Serialize(ResourceElement resElem) => Serialize(resElem, SerializationFormat.BinaryFormatter); + + /// + /// Serializes the image + /// + /// Resource element + /// Serialization format to use + /// + public static ResourceElement Serialize(ResourceElement resElem, SerializationFormat format) { var data = (byte[])((BuiltInResourceData)resElem.ResourceData).Data; bool isIcon = BitConverter.ToUInt32(data, 0) == 0x00010000; @@ -165,9 +174,29 @@ public static ResourceElement Serialize(ResourceElement resElem) { typeName = SystemDrawingBitmap.AssemblyQualifiedName; } + byte[] serializedData; + if (format == SerializationFormat.BinaryFormatter) { + serializedData = SerializationUtilities.Serialize(obj); + } + else if (format == SerializationFormat.TypeConverterByteArray) { + var converter = TypeDescriptor.GetConverter(obj.GetType()); + serializedData = (byte[])converter.ConvertTo(obj, typeof(byte[])); + } + else if (format == SerializationFormat.ActivatorStream) { + using (var stream = new MemoryStream()) { + if (obj is System.Drawing.Bitmap bitmap) + bitmap.Save(stream, bitmap.RawFormat); + else + ((System.Drawing.Icon)obj).Save(stream); + serializedData = stream.ToArray(); + } + } + else + throw new ArgumentOutOfRangeException(); + return new ResourceElement { Name = resElem.Name, - ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj), SerializationFormat.BinaryFormatter), + ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), serializedData, format), }; } } diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs index 8560f692fb..50acb6a645 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs @@ -17,10 +17,13 @@ You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.Serialization.Formatters.Binary; +using System.Text; using System.Threading; using dnlib.DotNet.Resources; using dnSpy.Contracts.Images; @@ -91,16 +94,59 @@ public void Deserialize() { if (!CanDeserialize) return; - var serializedData = ((BinaryResourceData)ResourceElement.ResourceData).Data; - var formatter = new BinaryFormatter(); - try { + var binaryResourceData = ((BinaryResourceData)ResourceElement.ResourceData); + var serializedData = binaryResourceData.Data; + if (binaryResourceData.Format == SerializationFormat.BinaryFormatter) { + var formatter = new BinaryFormatter(); + try { #pragma warning disable SYSLIB0011 - deserializedData = formatter.Deserialize(new MemoryStream(serializedData)); + deserializedData = formatter.Deserialize(new MemoryStream(serializedData)); #pragma warning restore SYSLIB0011 + } + catch { + return; + } } - catch { - return; + else if (binaryResourceData.Format == SerializationFormat.TypeConverterByteArray) { + try { + var type = Type.GetType(binaryResourceData.TypeName); + if (type is null) + return; + var converter = TypeDescriptor.GetConverter(type); + if (converter is null) + return; + deserializedData = converter.ConvertFrom(serializedData); + } + catch { + return; + } } + else if (binaryResourceData.Format == SerializationFormat.TypeConverterString) { + try { + var type = Type.GetType(binaryResourceData.TypeName); + if (type is null) + return; + var converter = TypeDescriptor.GetConverter(type); + if (converter is null) + return; + deserializedData = converter.ConvertFromInvariantString(Encoding.UTF8.GetString(serializedData)); + } + catch { + return; + } + } + else if (binaryResourceData.Format == SerializationFormat.ActivatorStream) { + try { + var type = Type.GetType(binaryResourceData.TypeName); + if (type is null) + return; + deserializedData = Activator.CreateInstance(type, new MemoryStream(serializedData)); + } + catch { + return; + } + } + if (deserializedData is null) return; From fdd8899b6aa6eb170d4d8d89c1ac262968212e61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:03:47 +0000 Subject: [PATCH 062/122] Bump actions/checkout from 3.6.0 to 4.0.0 (#237) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63b999bb8c..5750e53dab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: build-dir: net6.0-windows\win-x64\publish steps: - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.0.0 with: submodules: true From 03b586d8ab008807417565a2468b728406a37f58 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Wed, 6 Sep 2023 21:14:17 +0200 Subject: [PATCH 063/122] Fixed nullability warnings --- .../TreeView/Resources/SerializedImageUtilities.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs index ef778c4e7f..a7ef3939c8 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs @@ -180,7 +180,10 @@ public static ResourceElement Serialize(ResourceElement resElem, SerializationFo } else if (format == SerializationFormat.TypeConverterByteArray) { var converter = TypeDescriptor.GetConverter(obj.GetType()); - serializedData = (byte[])converter.ConvertTo(obj, typeof(byte[])); + var byteArr = converter.ConvertTo(obj, typeof(byte[])); + if (byteArr is not byte[] d) + throw new InvalidOperationException("Failed to serialize image"); + serializedData = d; } else if (format == SerializationFormat.ActivatorStream) { using (var stream = new MemoryStream()) { @@ -192,7 +195,7 @@ public static ResourceElement Serialize(ResourceElement resElem, SerializationFo } } else - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(format)); return new ResourceElement { Name = resElem.Name, From 66868661a8c7f8861f47da7b96172f5cb26bb58d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 8 Sep 2023 19:29:59 +0200 Subject: [PATCH 064/122] Bump version to v6.4.1 --- DnSpyCommon.props | 4 ++-- dnSpy/dnSpy/app.config | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/DnSpyCommon.props b/DnSpyCommon.props index d31cf60f50..45a52f9685 100644 --- a/DnSpyCommon.props +++ b/DnSpyCommon.props @@ -24,9 +24,9 @@ cs;de;es;es-ES;fa;fr;hu;it;pt-BR;pt-PT;ru;tr;uk;zh-CN;vi - 6.4.0.0 + 6.4.1.0 - v6.4.0 + v6.4.1 Copyright (C) 2014-2020 de4dot@gmail.com diff --git a/dnSpy/dnSpy/app.config b/dnSpy/dnSpy/app.config index eb10372c77..def221d0c1 100644 --- a/dnSpy/dnSpy/app.config +++ b/dnSpy/dnSpy/app.config @@ -4,43 +4,43 @@ - + - + - + - + - + - + - + - + - + - + From 84c120f1d55cd14f12814ac98a749faae3f3e7de Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 8 Sep 2023 22:28:38 +0200 Subject: [PATCH 065/122] Update decompiler submodule (refs #241) --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index cb5b7d7d7f..20b7cf1679 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit cb5b7d7d7f5969d0b55383fe3ed9c4ae9744ba50 +Subproject commit 20b7cf1679c8c22b3929f0e0e1a9f96d07d6c73d From 605cb6a57c148ed27d5a835ae4776b048833aa56 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 9 Sep 2023 13:42:25 +0200 Subject: [PATCH 066/122] Fix a comparison bug causing incorrect analyzer results --- Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs b/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs index eede7181f6..871f97ebab 100644 --- a/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs +++ b/Extensions/dnSpy.Analyzer/TreeNodes/Helpers.cs @@ -86,7 +86,8 @@ static MethodDef GetOriginalCodeLocation(MethodDef method) { } internal static bool CheckEquals(IMemberRef? mr1, IMemberRef? mr2) => - new SigComparer(SigComparerOptions.CompareDeclaringTypes | SigComparerOptions.PrivateScopeIsComparable).Equals(mr1, mr2); + new SigComparer(SigComparerOptions.CompareDeclaringTypes | SigComparerOptions.PrivateScopeIsComparable | + SigComparerOptions.ReferenceCompareForMemberDefsInSameModule).Equals(mr1, mr2); static MethodDef? FindVariableOfTypeUsageInType(TypeDef type, TypeDef variableType) { foreach (MethodDef method in type.Methods) { From 7c2a7862d5d95129eb7714e7560e33298404540c Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 10 Sep 2023 15:31:07 +0200 Subject: [PATCH 067/122] Update decompiler submodule --- Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler index 20b7cf1679..07104605cd 160000 --- a/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler +++ b/Extensions/ILSpy.Decompiler/ICSharpCode.Decompiler @@ -1 +1 @@ -Subproject commit 20b7cf1679c8c22b3929f0e0e1a9f96d07d6c73d +Subproject commit 07104605cd90a0b7b17493216df278e67429daed From 653fca2751311fcf7eea5d02359edb2feba55416 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:21:16 +0000 Subject: [PATCH 068/122] Bump actions/upload-artifact from 3.1.2 to 3.1.3 (#243) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5750e53dab..2dc0554db5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,7 @@ jobs: - name: Copy release files for upload run: Copy-Item -Path dnSpy\dnSpy\bin\Release\${{matrix.build-dir}}\* -Destination C:\builtfiles\dnSpy-${{matrix.platform}} -Recurse - - uses: actions/upload-artifact@v3.1.2 + - uses: actions/upload-artifact@v3.1.3 if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') with: name: dnSpy-${{matrix.package-name}} From 2cbd254a310eaec5de5b5819076ba6222fb9e2a9 Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Wed, 13 Sep 2023 13:00:57 -0700 Subject: [PATCH 069/122] Fix some processes missing from "Attach to Process (Unity)" (#242) --- .../AttachToProcess/UnityPlayerAttachProgramOptionsProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Dialogs/AttachToProcess/UnityPlayerAttachProgramOptionsProvider.cs b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Dialogs/AttachToProcess/UnityPlayerAttachProgramOptionsProvider.cs index 55399067d4..8eb8ed8cce 100644 --- a/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Dialogs/AttachToProcess/UnityPlayerAttachProgramOptionsProvider.cs +++ b/Extensions/dnSpy.Debugger/dnSpy.Debugger.DotNet.Mono/Dialogs/AttachToProcess/UnityPlayerAttachProgramOptionsProvider.cs @@ -92,7 +92,7 @@ public override IEnumerable Create(AttachProgramOptionsPro } } - static readonly Regex playerAnnounceStringRegex = new Regex(@"^\[IP\] (\S+) \[Port\] (\d+) \[Flags\] (-?\d+) \[Guid\] (\d+) \[EditorId\] (\d+) \[Version\] (\d+) \[Id\] ([^\(]+)\(([^\)]+)\)(:(\d+))? \[Debug\] (\d+)"); + static readonly Regex playerAnnounceStringRegex = new Regex(@"^\[IP\] (\S+) \[Port\] (\d+) \[Flags\] (-?\d+) \[Guid\] (\d+) \[EditorId\] (\d+) \[Version\] (\d+) \[Id\] ([^\(]+)\((?:\d+,)?([^\)]+)\)(:(\d+))? \[Debug\] (\d+)"); bool TryParseUnityPlayerData(string s, [NotNullWhen(true)] out string? ipAddress, out ushort port, [NotNullWhen(true)] out string? playerId) { ipAddress = null; port = 0; From 2defb01c253037c50ea69b2ca7608ed974edc3a0 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 17 Sep 2023 21:32:32 +0200 Subject: [PATCH 070/122] Add new InfoBar control for displaying notifications to the user --- .../dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs | 33 +++++++ dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs | 5 + .../App/IInfoBarElement.cs | 40 ++++++++ .../App/IInfoBarInteractionContext.cs | 40 ++++++++ .../dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs | 40 ++++++++ .../App/InfoBarInteraction.cs | 45 +++++++++ .../dnSpy.Contracts.DnSpy/Themes/ColorType.cs | 5 + dnSpy/dnSpy/MainApp/AppInfoBar.cs | 42 ++++++++ dnSpy/dnSpy/MainApp/AppWindow.cs | 7 +- dnSpy/dnSpy/MainApp/InfoBar.xaml | 83 ++++++++++++++++ dnSpy/dnSpy/MainApp/InfoBar.xaml.cs | 27 +++++ dnSpy/dnSpy/MainApp/InfoBarVM.cs | 99 +++++++++++++++++++ dnSpy/dnSpy/Themes/ColorInfos.cs | 20 ++++ dnSpy/dnSpy/Themes/blue.dntheme | 5 + dnSpy/dnSpy/Themes/dark.dntheme | 5 + dnSpy/dnSpy/Themes/light.dntheme | 5 + 16 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 dnSpy/dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs create mode 100644 dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarElement.cs create mode 100644 dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarInteractionContext.cs create mode 100644 dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs create mode 100644 dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarInteraction.cs create mode 100644 dnSpy/dnSpy/MainApp/AppInfoBar.cs create mode 100644 dnSpy/dnSpy/MainApp/InfoBar.xaml create mode 100644 dnSpy/dnSpy/MainApp/InfoBar.xaml.cs create mode 100644 dnSpy/dnSpy/MainApp/InfoBarVM.cs diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs new file mode 100644 index 0000000000..c43537874b --- /dev/null +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs @@ -0,0 +1,33 @@ +/* + 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.Contracts.App { + /// + /// App info bar + /// + public interface IAppInfoBar { + /// + /// Shows a new info bar element of the specific icon with the given message and interactions. + /// + /// The message to display + /// The icon of message + /// Possible interactions on the element + public IInfoBarElement Show(string message, InfoBarIcon icon = InfoBarIcon.Information, params InfoBarInteraction[] interactions); + } +} diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs index c0dc4caead..eee77a4197 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs @@ -53,6 +53,11 @@ public interface IAppWindow { /// IAppStatusBar StatusBar { get; } + /// + /// Gets the instance + /// + IAppInfoBar InfoBar { get; } + /// /// true if the app has been loaded /// diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarElement.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarElement.cs new file mode 100644 index 0000000000..9344ffba53 --- /dev/null +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarElement.cs @@ -0,0 +1,40 @@ +/* + 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.Contracts.App { + /// + /// A single element in the info bar + /// + public interface IInfoBarElement { + /// + /// Message being displayed as part of this info bar element + /// + string Message { get; } + + /// + /// Icon being displayed as part of this info bar element + /// + InfoBarIcon Icon { get; } + + /// + /// Closes the info bar element + /// + void Close(); + } +} diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarInteractionContext.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarInteractionContext.cs new file mode 100644 index 0000000000..fc75af2eac --- /dev/null +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarInteractionContext.cs @@ -0,0 +1,40 @@ +/* + 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.Contracts.App { + /// + /// Context passed to an interaction + /// + public interface IInfoBarInteractionContext { + /// + /// Interaction text + /// + string InteractionText { get; } + + /// + /// Closes the owning this interaction + /// + void CloseElement(); + + /// + /// Removes the interaction from the + /// + void RemoveInteraction(); + } +} diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs new file mode 100644 index 0000000000..25c8429f56 --- /dev/null +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs @@ -0,0 +1,40 @@ +/* + 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.Contracts.App { + /// + /// Icon displayed in the info bar + /// + public enum InfoBarIcon { + /// + /// Information icon + /// + Information, + + /// + /// Warning icon + /// + Warning, + + /// + /// Error icon + /// + Error + } +} diff --git a/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarInteraction.cs b/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarInteraction.cs new file mode 100644 index 0000000000..340539a887 --- /dev/null +++ b/dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarInteraction.cs @@ -0,0 +1,45 @@ +/* + 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; + +namespace dnSpy.Contracts.App { + /// + /// Interaction on an + /// + public readonly struct InfoBarInteraction { + /// + /// Interaction text + /// + public string Text { get; } + + /// + /// Action to perform when the interaction is clicked + /// + public Action Action { get; } + + /// + /// Creates a new + /// + public InfoBarInteraction(string text, Action action) { + Text = text; + Action = action; + } + } +} diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Themes/ColorType.cs b/dnSpy/dnSpy.Contracts.DnSpy/Themes/ColorType.cs index ab5685e65e..83d291d3c5 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Themes/ColorType.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Themes/ColorType.cs @@ -789,6 +789,11 @@ public enum ColorType : uint { HyperlinkNormal, HyperlinkMouseOver, HyperlinkDisabled, + InfoBarBackground, + InfoBarText, + InfoBarInteractionText, + InfoBarCloseButton, + InfoBarCloseButtonHover, // Add new color types before this comment diff --git a/dnSpy/dnSpy/MainApp/AppInfoBar.cs b/dnSpy/dnSpy/MainApp/AppInfoBar.cs new file mode 100644 index 0000000000..c7caa918e1 --- /dev/null +++ b/dnSpy/dnSpy/MainApp/AppInfoBar.cs @@ -0,0 +1,42 @@ +/* + 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.ComponentModel.Composition; +using dnSpy.Contracts.App; +using dnSpy.Controls; + +namespace dnSpy.MainApp { + [Export] + sealed class AppInfoBar : IAppInfoBar, IStackedContentChild { + readonly InfoBar infoBar; + readonly InfoBarVM infoBarVM; + + public object UIObject => infoBar; + + public AppInfoBar() => infoBar = new InfoBar { DataContext = infoBarVM = new InfoBarVM() }; + + public IInfoBarElement Show(string message, InfoBarIcon icon = InfoBarIcon.Information, params InfoBarInteraction[] interactions) { + var notification = new InfoBarElementVM(infoBarVM, message, icon); + foreach (var interaction in interactions) + notification.AddInteraction(interaction.Text, interaction.Action); + infoBarVM.Elements.Insert(0, notification); + return notification; + } + } +} diff --git a/dnSpy/dnSpy/MainApp/AppWindow.cs b/dnSpy/dnSpy/MainApp/AppWindow.cs index b7135002e9..50883b5ef0 100644 --- a/dnSpy/dnSpy/MainApp/AppWindow.cs +++ b/dnSpy/dnSpy/MainApp/AppWindow.cs @@ -42,6 +42,9 @@ sealed class AppWindow : IAppWindow, IDsLoaderContentProvider { public IAppStatusBar StatusBar => statusBar; readonly AppStatusBar statusBar; + public IAppInfoBar InfoBar => infoBar; + readonly AppInfoBar infoBar; + Window IAppWindow.MainWindow => mainWindow!; internal MainWindow MainWindow => mainWindow!; MainWindow? mainWindow; @@ -96,7 +99,7 @@ public void Write() { readonly MainWindowControl mainWindowControl; [ImportingConstructor] - AppWindow(ISettingsService settingsService, IDocumentTabService documentTabService, AppToolBar appToolBar, MainWindowControl mainWindowControl, IWpfCommandService wpfCommandService) { + AppWindow(ISettingsService settingsService, IDocumentTabService documentTabService, AppToolBar appToolBar, AppInfoBar infoBar, MainWindowControl mainWindowControl, IWpfCommandService wpfCommandService) { assemblyInformationalVersion = CalculateAssemblyInformationalVersion(GetType().Assembly); uiSettings = new UISettings(settingsService); uiSettings.Read(); @@ -104,6 +107,7 @@ public void Write() { this.documentTabService = documentTabService; statusBar = new AppStatusBar(); this.appToolBar = appToolBar; + this.infoBar = infoBar; this.mainWindowControl = mainWindowControl; this.wpfCommandService = wpfCommandService; mainWindowCommands = wpfCommandService.GetCommands(ControlConstants.GUID_MAINWINDOW); @@ -124,6 +128,7 @@ static string CalculateAssemblyInformationalVersion(Assembly asm) { public Window InitializeMainWindow() { var sc = new StackedContent(false); sc.AddChild(appToolBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto))); + sc.AddChild(infoBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto))); sc.AddChild(stackedContent, StackedContentChildInfo.CreateVertical(new GridLength(1, GridUnitType.Star))); sc.AddChild(statusBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto))); mainWindow = new MainWindow(sc.UIObject); diff --git a/dnSpy/dnSpy/MainApp/InfoBar.xaml b/dnSpy/dnSpy/MainApp/InfoBar.xaml new file mode 100644 index 0000000000..40d5f653a3 --- /dev/null +++ b/dnSpy/dnSpy/MainApp/InfoBar.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnSpy/dnSpy/MainApp/InfoBar.xaml.cs b/dnSpy/dnSpy/MainApp/InfoBar.xaml.cs new file mode 100644 index 0000000000..702e51e07f --- /dev/null +++ b/dnSpy/dnSpy/MainApp/InfoBar.xaml.cs @@ -0,0 +1,27 @@ +/* + 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.Windows.Controls; + +namespace dnSpy.MainApp { + public partial class InfoBar : UserControl { + public InfoBar() => InitializeComponent(); + } +} diff --git a/dnSpy/dnSpy/MainApp/InfoBarVM.cs b/dnSpy/dnSpy/MainApp/InfoBarVM.cs new file mode 100644 index 0000000000..efcafcfcec --- /dev/null +++ b/dnSpy/dnSpy/MainApp/InfoBarVM.cs @@ -0,0 +1,99 @@ +/* + 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; +using System.Collections.ObjectModel; +using System.Windows.Input; +using dnSpy.Contracts.App; +using dnSpy.Contracts.Images; +using dnSpy.Contracts.MVVM; + +namespace dnSpy.MainApp { + sealed class InfoBarVM : ViewModelBase { + public ObservableCollection Elements { get; } = new ObservableCollection(); + + public ICommand RemoveElementCommand { get; } + + public InfoBarVM() => RemoveElementCommand = new RelayCommand(RemoveElement); + + internal void RemoveElement(object? obj) { + if (obj is not InfoBarElementVM notification) + return; + Elements.Remove(notification); + } + } + + sealed class InfoBarElementVM : ViewModelBase, IInfoBarElement { + readonly InfoBarVM parent; + + public string Message { get; } + + public InfoBarIcon Icon { get; } + + public ImageReference Image => Icon switch { + InfoBarIcon.Information => DsImages.StatusInformation, + InfoBarIcon.Warning => DsImages.StatusWarning, + InfoBarIcon.Error => DsImages.StatusError, + _ => DsImages.QuestionMark + }; + + public ObservableCollection Interactions { get; } = new ObservableCollection(); + + public InfoBarElementVM(InfoBarVM parent, string message, InfoBarIcon icon) { + this.parent = parent; + Message = message; + Icon = icon; + } + + public void Close() => parent.RemoveElement(this); + + public void AddInteraction(string text, Action action) => Interactions.Add(new InfoBarInteractionVM(this, text, action)); + + public void RemoveInteraction(InfoBarInteractionVM interaction) => Interactions.Remove(interaction); + } + + sealed class InfoBarInteractionVM : ViewModelBase { + internal readonly InfoBarElementVM parent; + + public string Text { get; } + public ICommand ActionCommand { get; } + + public InfoBarInteractionVM(InfoBarElementVM parent, string text, Action action) { + this.parent = parent; + Text = text; + ActionCommand = new RelayCommand(vm => { + if (vm is not InfoBarInteractionVM interactionVM) + return; + action(new NotificationInteractionContext(interactionVM)); + }); + } + } + + sealed class NotificationInteractionContext : IInfoBarInteractionContext { + readonly InfoBarInteractionVM _interactionVm; + + public string InteractionText => _interactionVm.Text; + + public NotificationInteractionContext(InfoBarInteractionVM interactionVm) => _interactionVm = interactionVm; + + public void CloseElement() => _interactionVm.parent.Close(); + + public void RemoveInteraction() => _interactionVm.parent.RemoveInteraction(_interactionVm); + } +} diff --git a/dnSpy/dnSpy/Themes/ColorInfos.cs b/dnSpy/dnSpy/Themes/ColorInfos.cs index 5182b678d8..e6fce28ad3 100644 --- a/dnSpy/dnSpy/Themes/ColorInfos.cs +++ b/dnSpy/dnSpy/Themes/ColorInfos.cs @@ -1877,6 +1877,26 @@ static class ColorInfos { DefaultBackground = "#FF6D6D6D", BackgroundResourceKey = "HyperlinkDisabled", }, + new BrushColorInfo(ColorType.InfoBarBackground, "") { + DefaultBackground = "#FFFDFBAC", + BackgroundResourceKey = "InfoBarBackground", + }, + new BrushColorInfo(ColorType.InfoBarText, "") { + DefaultBackground = "#FF000000", + BackgroundResourceKey = "InfoBarText", + }, + new BrushColorInfo(ColorType.InfoBarInteractionText, "") { + DefaultBackground = "#FF3399FF", + BackgroundResourceKey = "InfoBarInteractionText", + }, + new BrushColorInfo(ColorType.InfoBarCloseButton, "") { + DefaultBackground = "#FF696969", + BackgroundResourceKey = "InfoBarCloseButton", + }, + new BrushColorInfo(ColorType.InfoBarCloseButtonHover, "") { + DefaultBackground = "#FF000000", + BackgroundResourceKey = "InfoBarCloseButtonHover", + }, new BrushColorInfo(ColorType.LineNumber, "Line number"), new BrushColorInfo(ColorType.ReplLineNumberInput1, "REPL line number #1 (input)"), new BrushColorInfo(ColorType.ReplLineNumberInput2, "REPL line number #2 (input)"), diff --git a/dnSpy/dnSpy/Themes/blue.dntheme b/dnSpy/dnSpy/Themes/blue.dntheme index f3c9f507e4..cfbc2aa701 100644 --- a/dnSpy/dnSpy/Themes/blue.dntheme +++ b/dnSpy/dnSpy/Themes/blue.dntheme @@ -749,5 +749,10 @@ + + + + + diff --git a/dnSpy/dnSpy/Themes/dark.dntheme b/dnSpy/dnSpy/Themes/dark.dntheme index 8a6f724791..8e956e70af 100644 --- a/dnSpy/dnSpy/Themes/dark.dntheme +++ b/dnSpy/dnSpy/Themes/dark.dntheme @@ -749,5 +749,10 @@ + + + + + diff --git a/dnSpy/dnSpy/Themes/light.dntheme b/dnSpy/dnSpy/Themes/light.dntheme index 815bb335d3..929b1dc6ca 100644 --- a/dnSpy/dnSpy/Themes/light.dntheme +++ b/dnSpy/dnSpy/Themes/light.dntheme @@ -317,5 +317,10 @@ + + + + + From 4eeea762a2d745bae16520d6c76ca56a7b783395 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 17 Sep 2023 21:53:43 +0200 Subject: [PATCH 071/122] Add update checker which runs on startup --- dnSpy/dnSpy/MainApp/AboutScreen.cs | 22 +- .../Settings/GeneralAppSettingsPage.cs | 24 ++- dnSpy/dnSpy/MainApp/StartupUpdateCheck.cs | 75 +++++++ dnSpy/dnSpy/MainApp/UpdateChecker.cs | 100 --------- dnSpy/dnSpy/MainApp/UpdateService.cs | 172 ++++++++++++++++ .../Properties/dnSpy.Resources.Designer.cs | 45 ++++ dnSpy/dnSpy/Properties/dnSpy.Resources.resx | 15 ++ dnSpy/dnSpy/Themes/wpf.styles.templates.xaml | 194 +++++++++--------- 8 files changed, 441 insertions(+), 206 deletions(-) create mode 100644 dnSpy/dnSpy/MainApp/StartupUpdateCheck.cs delete mode 100644 dnSpy/dnSpy/MainApp/UpdateChecker.cs create mode 100644 dnSpy/dnSpy/MainApp/UpdateService.cs diff --git a/dnSpy/dnSpy/MainApp/AboutScreen.cs b/dnSpy/dnSpy/MainApp/AboutScreen.cs index 6bf224427e..0333078e2f 100644 --- a/dnSpy/dnSpy/MainApp/AboutScreen.cs +++ b/dnSpy/dnSpy/MainApp/AboutScreen.cs @@ -46,13 +46,15 @@ sealed class AboutScreenDocumentTabContentFactory : IDocumentTabContentFactory { readonly IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider; readonly IAppWindow appWindow; readonly IExtensionService extensionService; + readonly IUpdateService updateService; readonly IContentType aboutContentType; [ImportingConstructor] - AboutScreenDocumentTabContentFactory(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IAppWindow appWindow, IExtensionService extensionService, IContentTypeRegistryService contentTypeRegistryService) { + AboutScreenDocumentTabContentFactory(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IAppWindow appWindow, IExtensionService extensionService, IUpdateService updateService, IContentTypeRegistryService contentTypeRegistryService) { this.documentViewerContentFactoryProvider = documentViewerContentFactoryProvider; this.appWindow = appWindow; this.extensionService = extensionService; + this.updateService = updateService; aboutContentType = contentTypeRegistryService.GetContentType(ContentTypes.AboutDnSpy); } @@ -62,7 +64,7 @@ sealed class AboutScreenDocumentTabContentFactory : IDocumentTabContentFactory { public DocumentTabContent? Deserialize(Guid guid, ISettingsSection section, IDocumentTabContentFactoryContext context) { if (guid == GUID_SerializedContent) - return new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, aboutContentType); + return new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, updateService, aboutContentType); return null; } @@ -79,20 +81,22 @@ sealed class AboutScreenMenuItem : MenuItemBase { readonly IDocumentTabService documentTabService; readonly IAppWindow appWindow; readonly IExtensionService extensionService; + readonly IUpdateService updateService; readonly IContentType aboutContentType; [ImportingConstructor] - AboutScreenMenuItem(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IDocumentTabService documentTabService, IAppWindow appWindow, IExtensionService extensionService, IContentTypeRegistryService contentTypeRegistryService) { + AboutScreenMenuItem(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IDocumentTabService documentTabService, IAppWindow appWindow, IExtensionService extensionService, IUpdateService updateService, IContentTypeRegistryService contentTypeRegistryService) { this.documentViewerContentFactoryProvider = documentViewerContentFactoryProvider; this.documentTabService = documentTabService; this.appWindow = appWindow; this.extensionService = extensionService; + this.updateService = updateService; aboutContentType = contentTypeRegistryService.GetContentType(ContentTypes.AboutDnSpy); } public override void Execute(IMenuItemContext context) { var tab = documentTabService.GetOrCreateActiveTab(); - tab.Show(new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, aboutContentType), null, null); + tab.Show(new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, updateService, aboutContentType), null, null); documentTabService.SetFocus(tab); } } @@ -102,17 +106,19 @@ sealed class AboutScreenDocumentTabContent : DocumentTabContent { readonly IAppWindow appWindow; readonly IExtensionService extensionService; + readonly IUpdateService updateService; readonly IContentType aboutContentType; readonly IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider; - public AboutScreenDocumentTabContent(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IAppWindow appWindow, IExtensionService extensionService, IContentType aboutContentType) { + public AboutScreenDocumentTabContent(IDocumentViewerContentFactoryProvider documentViewerContentFactoryProvider, IAppWindow appWindow, IExtensionService extensionService, IUpdateService updateService, IContentType aboutContentType) { this.documentViewerContentFactoryProvider = documentViewerContentFactoryProvider; this.appWindow = appWindow; this.extensionService = extensionService; + this.updateService = updateService; this.aboutContentType = aboutContentType; } - public override DocumentTabContent Clone() => new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, aboutContentType); + public override DocumentTabContent Clone() => new AboutScreenDocumentTabContent(documentViewerContentFactoryProvider, appWindow, extensionService, updateService, aboutContentType); public override DocumentTabUIContext CreateUIContext(IDocumentTabUIContextLocator locator) => (DocumentTabUIContext)locator.Get(); public override void OnShow(IShowContext ctx) { @@ -224,14 +230,14 @@ void Write(IDocumentViewerOutput output) { WriteResourceFile(output, "dnSpy.LicenseInfo.CREDITS.txt"); } - static async void OnUpdateButtonClick(object sender, RoutedEventArgs e) { + async void OnUpdateButtonClick(object sender, RoutedEventArgs e) { e.Handled = true; var button = (Button)sender; button.IsEnabled = false; button.Content = dnSpy_Resources.AboutScreen_CheckingForUpdates; - var updateResult = await UpdateChecker.CheckForUpdatesAsync(); + var updateResult = await updateService.CheckForUpdatesAsync(); if (!updateResult.Success) MsgBox.Instance.Show(dnSpy_Resources.AboutScreen_FailedToRetrieveUpdateInfo); else if (!updateResult.UpdateAvailable) diff --git a/dnSpy/dnSpy/MainApp/Settings/GeneralAppSettingsPage.cs b/dnSpy/dnSpy/MainApp/Settings/GeneralAppSettingsPage.cs index 2f898b3ee6..77d3d80faa 100644 --- a/dnSpy/dnSpy/MainApp/Settings/GeneralAppSettingsPage.cs +++ b/dnSpy/dnSpy/MainApp/Settings/GeneralAppSettingsPage.cs @@ -43,9 +43,10 @@ sealed class GeneralAppSettingsPageProvider : IAppSettingsPageProvider { readonly IDsDocumentServiceSettings documentServiceSettings; readonly AppSettingsImpl appSettings; readonly MessageBoxService messageBoxService; + readonly IUpdateService updateService; [ImportingConstructor] - GeneralAppSettingsPageProvider(IThemeServiceImpl themeService, IWindowsExplorerIntegrationService windowsExplorerIntegrationService, IDocumentTabServiceSettings documentTabServiceSettings, DocumentTreeViewSettingsImpl documentTreeViewSettings, IDsDocumentServiceSettings documentServiceSettings, AppSettingsImpl appSettings, MessageBoxService messageBoxService) { + GeneralAppSettingsPageProvider(IThemeServiceImpl themeService, IWindowsExplorerIntegrationService windowsExplorerIntegrationService, IDocumentTabServiceSettings documentTabServiceSettings, DocumentTreeViewSettingsImpl documentTreeViewSettings, IDsDocumentServiceSettings documentServiceSettings, AppSettingsImpl appSettings, MessageBoxService messageBoxService, IUpdateService updateService) { this.themeService = themeService; this.windowsExplorerIntegrationService = windowsExplorerIntegrationService; this.documentTabServiceSettings = documentTabServiceSettings; @@ -53,10 +54,11 @@ sealed class GeneralAppSettingsPageProvider : IAppSettingsPageProvider { this.documentServiceSettings = documentServiceSettings; this.appSettings = appSettings; this.messageBoxService = messageBoxService; + this.updateService = updateService; } public IEnumerable Create() { - yield return new GeneralAppSettingsPage(themeService, windowsExplorerIntegrationService, documentTabServiceSettings, documentTreeViewSettings, documentServiceSettings, appSettings, messageBoxService); + yield return new GeneralAppSettingsPage(themeService, windowsExplorerIntegrationService, documentTabServiceSettings, documentTreeViewSettings, documentServiceSettings, appSettings, messageBoxService, updateService); } } @@ -74,8 +76,10 @@ sealed class GeneralAppSettingsPage : AppSettingsPage, IAppSettingsPage2 { readonly IDsDocumentServiceSettings documentServiceSettings; readonly AppSettingsImpl appSettings; readonly MessageBoxService messageBoxService; + readonly IUpdateService updateService; public ICommand ClearAllWarningsCommand => new RelayCommand(a => messageBoxService.EnableAllWarnings(), a => messageBoxService.CanEnableAllWarnings); + public ICommand ResetIgnoredUpdatesCommand => new RelayCommand(a => updateService.ResetIgnoredUpdates(), a => updateService.CanResetIgnoredUpdates); public ObservableCollection ThemesVM { get; } @@ -156,7 +160,18 @@ public bool UseMemoryMappedIO { } bool useMemoryMappedIO; - public GeneralAppSettingsPage(IThemeServiceImpl themeService, IWindowsExplorerIntegrationService windowsExplorerIntegrationService, IDocumentTabServiceSettings documentTabServiceSettings, DocumentTreeViewSettingsImpl documentTreeViewSettings, IDsDocumentServiceSettings documentServiceSettings, AppSettingsImpl appSettings, MessageBoxService messageBoxService) { + public bool CheckForUpdateOnStartup { + get => checkForUpdateOnStartup; + set { + if (checkForUpdateOnStartup != value) { + checkForUpdateOnStartup = value; + OnPropertyChanged(nameof(CheckForUpdateOnStartup)); + } + } + } + bool checkForUpdateOnStartup; + + public GeneralAppSettingsPage(IThemeServiceImpl themeService, IWindowsExplorerIntegrationService windowsExplorerIntegrationService, IDocumentTabServiceSettings documentTabServiceSettings, DocumentTreeViewSettingsImpl documentTreeViewSettings, IDsDocumentServiceSettings documentServiceSettings, AppSettingsImpl appSettings, MessageBoxService messageBoxService, IUpdateService updateService) { this.themeService = themeService ?? throw new ArgumentNullException(nameof(themeService)); this.windowsExplorerIntegrationService = windowsExplorerIntegrationService ?? throw new ArgumentNullException(nameof(windowsExplorerIntegrationService)); this.documentTabServiceSettings = documentTabServiceSettings ?? throw new ArgumentNullException(nameof(documentTabServiceSettings)); @@ -164,6 +179,7 @@ public GeneralAppSettingsPage(IThemeServiceImpl themeService, IWindowsExplorerIn this.documentServiceSettings = documentServiceSettings ?? throw new ArgumentNullException(nameof(documentServiceSettings)); this.appSettings = appSettings ?? throw new ArgumentNullException(nameof(appSettings)); this.messageBoxService = messageBoxService ?? throw new ArgumentNullException(nameof(messageBoxService)); + this.updateService = updateService ?? throw new ArgumentNullException(nameof(updateService)); ThemesVM = new ObservableCollection(themeService.VisibleThemes.Select(a => new ThemeVM(a))); if (!ThemesVM.Any(a => a.Theme == themeService.Theme)) @@ -177,6 +193,7 @@ public GeneralAppSettingsPage(IThemeServiceImpl themeService, IWindowsExplorerIn RestoreTabs = documentTabServiceSettings.RestoreTabs; DeserializeResources = documentTreeViewSettings.DeserializeResources; UseMemoryMappedIO = documentServiceSettings.UseMemoryMappedIO; + CheckForUpdateOnStartup = updateService.CheckForUpdatesOnStartup; } public override string[]? GetSearchStrings() => ThemesVM.Select(a => a.Name).ToArray(); @@ -197,6 +214,7 @@ public void OnApply(IAppRefreshSettings appRefreshSettings) { appRefreshSettings.Add(AppSettingsConstants.DISABLE_MEMORY_MAPPED_IO); } + updateService.CheckForUpdatesOnStartup = CheckForUpdateOnStartup; } } diff --git a/dnSpy/dnSpy/MainApp/StartupUpdateCheck.cs b/dnSpy/dnSpy/MainApp/StartupUpdateCheck.cs new file mode 100644 index 0000000000..8163c3e37f --- /dev/null +++ b/dnSpy/dnSpy/MainApp/StartupUpdateCheck.cs @@ -0,0 +1,75 @@ +/* + 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.ComponentModel.Composition; +using System.Threading.Tasks; +using dnSpy.Contracts.App; +using dnSpy.Contracts.Extension; +using dnSpy.Properties; +using dnSpy.UI; + +namespace dnSpy.MainApp { + [ExportAutoLoaded] + sealed class StartupUpdateCheck : IAutoLoaded { + readonly IUpdateService updateService; + readonly IMessageBoxService messageBoxService; + readonly UIDispatcher uiDispatcher; + readonly IAppWindow appWindow; + + [ImportingConstructor] + public StartupUpdateCheck(IUpdateService updateService, IMessageBoxService messageBoxService, IAppWindow appWindow, UIDispatcher uiDispatcher) { + this.updateService = updateService; + this.messageBoxService = messageBoxService; + this.uiDispatcher = uiDispatcher; + this.appWindow = appWindow; + + if (updateService.CheckForUpdatesOnStartup) + Task.Run(CheckForUpdate); + } + + async void CheckForUpdate() { + var updateInfo = await updateService.CheckForUpdatesAsync(); + + if (!updateInfo.Success) + return; + if (!updateInfo.UpdateAvailable || updateService.IsUpdateIgnored(updateInfo.VersionInfo)) + return; + + string message = string.Format(dnSpy_Resources.InfoBar_NewUpdateAvailable, updateInfo.VersionInfo.Version); + DisplayNotification(message, InfoBarIcon.Information, new[] { + new InfoBarInteraction(dnSpy_Resources.InfoBar_OpenDownloadPage, ctx => { + AboutHelpers.OpenWebPage(updateInfo.VersionInfo.DownloadUrl, messageBoxService); + ctx.CloseElement(); + }), + new InfoBarInteraction(dnSpy_Resources.InfoBar_IgnoreThisUpdate, ctx => { + updateService.MarkUpdateAsIgnored(updateInfo.VersionInfo); + ctx.CloseElement(); + }) + }); + } + + void DisplayNotification(string message, InfoBarIcon icon, InfoBarInteraction[] interactions) { + if (uiDispatcher.CheckAccess()) { + appWindow.InfoBar.Show(message, icon, interactions); + return; + } + uiDispatcher.UIBackground(() => appWindow.InfoBar.Show(message, icon, interactions)); + } + } +} diff --git a/dnSpy/dnSpy/MainApp/UpdateChecker.cs b/dnSpy/dnSpy/MainApp/UpdateChecker.cs deleted file mode 100644 index 816da559bb..0000000000 --- a/dnSpy/dnSpy/MainApp/UpdateChecker.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright (C) 2022 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; -using System.Diagnostics; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; - -namespace dnSpy.MainApp { - static class UpdateChecker { - internal readonly struct UpdateCheckInfo { - public readonly bool Success; - public readonly bool UpdateAvailable; - public readonly VersionInfo VersionInfo; - - public UpdateCheckInfo(bool updateAvailable, VersionInfo versionInfo) { - Success = true; - UpdateAvailable = updateAvailable; - VersionInfo = versionInfo; - } - } - - internal readonly struct VersionInfo { - public readonly Version Version; - public readonly string DownloadUrl; - - public VersionInfo(Version version, string url) { - Version = version; - DownloadUrl = url; - } - } - - static readonly Version currentVersion = GetCurrentVersion(); - - static Version GetCurrentVersion() { - var currentAsm = typeof(StartUpClass).Assembly; - try { - var fileVer = FileVersionInfo.GetVersionInfo(currentAsm.Location).FileVersion; - if (!string2.IsNullOrEmpty(fileVer)) - return new Version(fileVer); - } - catch { - } - return currentAsm.GetName().Version!; - } - - public static async Task CheckForUpdatesAsync() { - var updateInfo = await TryGetLatestVersionAsync(); - if (updateInfo is not null) { - if (updateInfo.Value.Version > currentVersion) - return new UpdateCheckInfo(true, updateInfo.Value); - return new UpdateCheckInfo(false, default); - } - - return default; - } - - static async Task TryGetLatestVersionAsync() { - try { - string result; - using (var client = new HttpClient(new HttpClientHandler { UseProxy = true, UseDefaultCredentials = true })) { - client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("dnSpyEx", currentVersion.ToString())); - - result = await client.GetStringAsync("https://api.github.com/repos/dnSpyEx/dnSpy/releases/latest"); - } - - var json = JObject.Parse(result); - if (!json.TryGetValue("tag_name", out var tagToken) || tagToken is not JValue tagValue || tagValue.Value is not string tagName) - return null; - if (!json.TryGetValue("html_url", out var urlToken) || urlToken is not JValue urlValue || urlValue.Value is not string htmlUrl) - return null; - if (tagName[0] == 'v') - tagName = tagName.Remove(0, 1); - if (Version.TryParse(tagName, out var version)) - return new VersionInfo(version, htmlUrl); - } - catch { - } - return null; - } - } -} diff --git a/dnSpy/dnSpy/MainApp/UpdateService.cs b/dnSpy/dnSpy/MainApp/UpdateService.cs new file mode 100644 index 0000000000..6215293e27 --- /dev/null +++ b/dnSpy/dnSpy/MainApp/UpdateService.cs @@ -0,0 +1,172 @@ +/* + 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; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using dnSpy.Contracts.Settings; +using Newtonsoft.Json.Linq; + +namespace dnSpy.MainApp { + public readonly struct UpdateCheckInfo { + public readonly bool Success; + public readonly bool UpdateAvailable; + public readonly VersionInfo VersionInfo; + + public UpdateCheckInfo(bool updateAvailable, VersionInfo versionInfo) { + Success = true; + UpdateAvailable = updateAvailable; + VersionInfo = versionInfo; + } + } + + public readonly struct VersionInfo { + public readonly Version Version; + public readonly string DownloadUrl; + + public VersionInfo(Version version, string url) { + Version = version; + DownloadUrl = url; + } + + public static implicit operator Version(VersionInfo versionInfo) => versionInfo.Version; + } + + public interface IUpdateService { + bool CheckForUpdatesOnStartup { get; set; } + Task CheckForUpdatesAsync(); + void MarkUpdateAsIgnored(Version version); + bool IsUpdateIgnored(Version version); + bool CanResetIgnoredUpdates { get; } + void ResetIgnoredUpdates(); + } + + [Export(typeof(IUpdateService))] + sealed class UpdateService : IUpdateService { + static readonly Guid SETTINGS_GUID = new Guid("611A864B-FD4E-4505-8C5F-0165CD09B77A"); + const string IGNORED_SECTION = "Ignored"; + const string IGNORED_ATTR = "id"; + + public bool CheckForUpdatesOnStartup { + get => checkForUpdatesOnStartup; + set { + if (checkForUpdatesOnStartup != value) { + checkForUpdatesOnStartup = value; + SaveSettings(); + } + } + } + bool checkForUpdatesOnStartup; + + public bool CanResetIgnoredUpdates => ignoredVersions.Count > 0; + + readonly ISettingsService settingsService; + readonly HashSet ignoredVersions; + + [ImportingConstructor] + public UpdateService(ISettingsService settingsService) { + this.settingsService = settingsService; + ignoredVersions = new HashSet(); + + var sect = settingsService.GetOrCreateSection(SETTINGS_GUID); + checkForUpdatesOnStartup = sect.Attribute(nameof(CheckForUpdatesOnStartup)) ?? true; + + foreach (var ignoredSect in sect.SectionsWithName(IGNORED_SECTION)) { + var versionString = ignoredSect.Attribute(IGNORED_ATTR); + if (!Version.TryParse(versionString, out var version)) + continue; + ignoredVersions.Add(version); + } + } + + static readonly Version currentVersion = GetCurrentVersion(); + + static Version GetCurrentVersion() { + var currentAsm = typeof(StartUpClass).Assembly; + try { + var fileVer = FileVersionInfo.GetVersionInfo(currentAsm.Location).FileVersion; + if (!string2.IsNullOrEmpty(fileVer)) + return new Version(fileVer); + } + catch { + } + return currentAsm.GetName().Version!; + } + + public async Task CheckForUpdatesAsync() { + var updateInfo = await TryGetLatestVersionAsync(); + if (updateInfo is not null) { + if (updateInfo.Value.Version > currentVersion) + return new UpdateCheckInfo(true, updateInfo.Value); + return new UpdateCheckInfo(false, default); + } + + return default; + } + + static async Task TryGetLatestVersionAsync() { + try { + string result; + using (var client = new HttpClient(new HttpClientHandler { UseProxy = true, UseDefaultCredentials = true })) { + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("dnSpyEx", currentVersion.ToString())); + + result = await client.GetStringAsync("https://api.github.com/repos/dnSpyEx/dnSpy/releases/latest"); + } + + var json = JObject.Parse(result); + if (!json.TryGetValue("tag_name", out var tagToken) || tagToken is not JValue tagValue || tagValue.Value is not string tagName) + return null; + if (!json.TryGetValue("html_url", out var urlToken) || urlToken is not JValue urlValue || urlValue.Value is not string htmlUrl) + return null; + if (tagName[0] == 'v') + tagName = tagName.Remove(0, 1); + if (Version.TryParse(tagName, out var version)) + return new VersionInfo(version, htmlUrl); + } + catch { + } + return null; + } + + public void MarkUpdateAsIgnored(Version version) { + ignoredVersions.Add(version); + SaveSettings(); + } + + void SaveSettings() { + var sect = settingsService.RecreateSection(SETTINGS_GUID); + sect.Attribute(nameof(CheckForUpdatesOnStartup), CheckForUpdatesOnStartup); + foreach (var version in ignoredVersions) { + var ignoredSect = sect.CreateSection(IGNORED_SECTION); + ignoredSect.Attribute(IGNORED_ATTR, version); + } + } + + public bool IsUpdateIgnored(Version version) => ignoredVersions.Contains(version); + + public void ResetIgnoredUpdates() { + ignoredVersions.Clear(); + SaveSettings(); + } + } +} diff --git a/dnSpy/dnSpy/Properties/dnSpy.Resources.Designer.cs b/dnSpy/dnSpy/Properties/dnSpy.Resources.Designer.cs index 23b89f76b7..1a53d78bb7 100644 --- a/dnSpy/dnSpy/Properties/dnSpy.Resources.Designer.cs +++ b/dnSpy/dnSpy/Properties/dnSpy.Resources.Designer.cs @@ -2908,6 +2908,33 @@ public static string IncrementalSearchCommand { } } + /// + /// Looks up a localized string similar to Ignore this update. + /// + public static string InfoBar_IgnoreThisUpdate { + get { + return ResourceManager.GetString("InfoBar_IgnoreThisUpdate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A new version of dnSpy is available: {0}. + /// + public static string InfoBar_NewUpdateAvailable { + get { + return ResourceManager.GetString("InfoBar_NewUpdateAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open download page. + /// + public static string InfoBar_OpenDownloadPage { + get { + return ResourceManager.GetString("InfoBar_OpenDownloadPage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Integrate with Windows Explorer. /// @@ -3691,6 +3718,24 @@ public static string Options_Misc_Button_EnableAllWarnings { } } + /// + /// Looks up a localized string similar to Reset ignored updates. + /// + public static string Options_Misc_Button_ResetIgnoredUpdates { + get { + return ResourceManager.GetString("Options_Misc_Button_ResetIgnoredUpdates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Check for updates on startup. + /// + public static string Options_Misc_CheckForUpdatesOnStartup { + get { + return ResourceManager.GetString("Options_Misc_CheckForUpdatesOnStartup", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use memory mapped I/O. /// diff --git a/dnSpy/dnSpy/Properties/dnSpy.Resources.resx b/dnSpy/dnSpy/Properties/dnSpy.Resources.resx index 0e198a1212..472c982637 100644 --- a/dnSpy/dnSpy/Properties/dnSpy.Resources.resx +++ b/dnSpy/dnSpy/Properties/dnSpy.Resources.resx @@ -483,9 +483,15 @@ Options + + Check for updates on startup + Enable all warning messages + + Reset ignored updates + Use memory mapped I/O @@ -1100,6 +1106,15 @@ Incremental Search + + A new version of dnSpy is available: {0} + + + Open download page + + + Ignore this update + Ctrl+I diff --git a/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml b/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml index 44ab2472fa..51fd23d3ee 100644 --- a/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml +++ b/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml @@ -262,9 +262,9 @@ - @@ -274,22 +274,22 @@ - - - - - @@ -864,7 +864,7 @@ - + - - + + - +