From fc1e9eb5bcfd9e36e2a3d3270883a00480c36eb2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 1 Oct 2022 22:26:03 +0200 Subject: [PATCH 001/230] Display linker timestamp in UTC and local time for convenience. --- ILSpy/Metadata/CoffHeaderTreeNode.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ILSpy/Metadata/CoffHeaderTreeNode.cs b/ILSpy/Metadata/CoffHeaderTreeNode.cs index 8e7c3fe57..d7c25fa85 100644 --- a/ILSpy/Metadata/CoffHeaderTreeNode.cs +++ b/ILSpy/Metadata/CoffHeaderTreeNode.cs @@ -67,10 +67,12 @@ namespace ICSharpCode.ILSpy.Metadata var headers = module.Reader.PEHeaders; var header = headers.CoffHeader; + var linkerDateTime = DateTimeOffset.FromUnixTimeSeconds(unchecked((uint)header.TimeDateStamp)).DateTime; + var entries = new List(); entries.Add(new Entry(headers.CoffHeaderStartOffset, (int)header.Machine, 2, "Machine", header.Machine.ToString())); entries.Add(new Entry(headers.CoffHeaderStartOffset + 2, (int)header.NumberOfSections, 2, "Number of Sections", "Number of sections; indicates size of the Section Table, which immediately follows the headers.")); - entries.Add(new Entry(headers.CoffHeaderStartOffset + 4, header.TimeDateStamp, 4, "Time/Date Stamp", DateTimeOffset.FromUnixTimeSeconds(unchecked((uint)header.TimeDateStamp)).DateTime + " - Time and date the file was created in seconds since January 1st 1970 00:00:00 or 0.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 4, header.TimeDateStamp, 4, "Time/Date Stamp", $"{linkerDateTime} (UTC) / {linkerDateTime.ToLocalTime()} - Time and date the file was created in seconds since January 1st 1970 00:00:00 or 0. Note that for deterministic builds this value is meaningless.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 8, header.PointerToSymbolTable, 4, "Pointer to Symbol Table", "Always 0 in .NET executables.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 12, header.NumberOfSymbols, 4, "Number of Symbols", "Always 0 in .NET executables.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 16, (int)header.SizeOfOptionalHeader, 2, "Optional Header Size", "Size of the optional header.")); From ed02b2eb9e7a63454784dffd87b2481dc8f6f7d8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 2 Oct 2022 22:46:37 +0200 Subject: [PATCH 002/230] Add MemberNotNullWhenAttribute to IMethod, IProperty and IEvent. Make sure that it's guaranteed that AccessorOwner is non-null, when IsAccessor is true. --- .../ICSharpCode.Decompiler.csproj | 1 + ICSharpCode.Decompiler/NRTAttributes.cs | 25 +++++++++++++++++++ ICSharpCode.Decompiler/TypeSystem/IEvent.cs | 5 ++++ ICSharpCode.Decompiler/TypeSystem/IMethod.cs | 2 ++ .../TypeSystem/IProperty.cs | 4 +++ .../Implementation/MetadataMethod.cs | 4 +-- .../TypeSystem/NormalizeTypeVisitor.cs | 22 +++++++++++++--- .../TypeSystem/Nullability.cs | 20 ++++++++++++--- .../TypeSystem/TypeVisitor.cs | 2 ++ 9 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 ICSharpCode.Decompiler/NRTAttributes.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 0efd42831..b3b5cc7a6 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -90,6 +90,7 @@ + diff --git a/ICSharpCode.Decompiler/NRTAttributes.cs b/ICSharpCode.Decompiler/NRTAttributes.cs new file mode 100644 index 000000000..bdcaab026 --- /dev/null +++ b/ICSharpCode.Decompiler/NRTAttributes.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + public bool ReturnValue { get; } + + public string[] Members { get; } + + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new string[1] { member }; + } + + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/IEvent.cs b/ICSharpCode.Decompiler/TypeSystem/IEvent.cs index 732840c62..635b031da 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IEvent.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IEvent.cs @@ -18,12 +18,17 @@ #nullable enable +using System.Diagnostics.CodeAnalysis; + namespace ICSharpCode.Decompiler.TypeSystem { public interface IEvent : IMember { + [MemberNotNullWhen(true, nameof(AddAccessor))] bool CanAdd { get; } + [MemberNotNullWhen(true, nameof(RemoveAccessor))] bool CanRemove { get; } + [MemberNotNullWhen(true, nameof(InvokeAccessor))] bool CanInvoke { get; } IMethod? AddAccessor { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs index 26e4626c8..6438a1315 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs @@ -19,6 +19,7 @@ #nullable enable using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace ICSharpCode.Decompiler.TypeSystem @@ -81,6 +82,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets whether the method is a property/event accessor. /// + [MemberNotNullWhen(true, nameof(AccessorOwner))] bool IsAccessor { get; } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs index f8b6203d1..cad688dbe 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs @@ -18,6 +18,8 @@ #nullable enable +using System.Diagnostics.CodeAnalysis; + namespace ICSharpCode.Decompiler.TypeSystem { /// @@ -25,7 +27,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public interface IProperty : IParameterizedMember { + [MemberNotNullWhen(true, nameof(Getter))] bool CanGet { get; } + [MemberNotNullWhen(true, nameof(Setter))] bool CanSet { get; } IMethod? Getter { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index abc4252ea..c3760e42e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -67,7 +67,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var (accessorOwner, semanticsAttribute) = module.PEFile.MethodSemanticsLookup.GetSemantics(handle); const MethodAttributes finalizerAttributes = (MethodAttributes.Virtual | MethodAttributes.Family | MethodAttributes.HideBySig); this.typeParameters = MetadataTypeParameter.Create(module, this, def.GetGenericParameters()); - if (semanticsAttribute != 0) + if (semanticsAttribute != 0 && !accessorOwner.IsNil + && accessorOwner.Kind is HandleKind.PropertyDefinition or HandleKind.EventDefinition) { this.symbolKind = SymbolKind.Accessor; this.accessorOwner = accessorOwner; @@ -129,7 +130,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public bool HasBody => module.metadata.GetMethodDefinition(handle).HasBody(); - public IMember AccessorOwner { get { if (accessorOwner.IsNil) diff --git a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs index 505a06359..8f097f683 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs @@ -1,6 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) 2019 Daniel Grunwald +// +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable using ICSharpCode.Decompiler.TypeSystem.Implementation; diff --git a/ICSharpCode.Decompiler/TypeSystem/Nullability.cs b/ICSharpCode.Decompiler/TypeSystem/Nullability.cs index 17ce52520..fc934ebea 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Nullability.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Nullability.cs @@ -1,6 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) 2019 Daniel Grunwald +// +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. namespace ICSharpCode.Decompiler.TypeSystem { diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs index da76087c7..d5636fe24 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem From 927b46b17d863cae03515a3cadcdce8d6953b15b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 2 Oct 2022 22:53:36 +0200 Subject: [PATCH 003/230] Fix #2787: Enable NRT in TransformCollectionAndObjectInitializers and fix problems. --- ...ransformCollectionAndObjectInitializers.cs | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 75d3006da..635c962f9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -44,13 +46,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms IType instType; var blockKind = BlockKind.CollectionInitializer; var insertionPos = initInst.ChildIndex; - var siblings = initInst.Parent.Children; + var siblings = initInst.Parent!.Children; + IMethod currentMethod = context.Function.Method!; switch (initInst) { case NewObj newObjInst: if (newObjInst.ILStackWasEmpty && v.Kind == VariableKind.Local - && !context.Function.Method.IsConstructor - && !context.Function.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + && !currentMethod.IsConstructor + && !currentMethod.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), @@ -70,7 +73,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms instType = newObjInst.Method.DeclaringType; break; case DefaultValue defaultVal: - if (defaultVal.ILStackWasEmpty && v.Kind == VariableKind.Local && !context.Function.Method.IsConstructor) + if (defaultVal.ILStackWasEmpty && v.Kind == VariableKind.Local && !currentMethod.IsConstructor) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), @@ -100,10 +103,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; } int initializerItemsCount = 0; - possibleIndexVariables = new Dictionary(); - currentPath = new List(); + possibleIndexVariables.Clear(); + currentPath.Clear(); isCollection = false; - pathStack = new Stack>(); + pathStack.Clear(); pathStack.Push(new HashSet()); // Detect initializer type by scanning the following statements // each must be a callvirt with ldloc v as first argument @@ -192,10 +195,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - Dictionary possibleIndexVariables; - List currentPath; + readonly Dictionary possibleIndexVariables = new Dictionary(); + readonly List currentPath = new List(); bool isCollection; - Stack> pathStack; + readonly Stack> pathStack = new Stack>(); bool IsPartOfInitializer(InstructionCollection instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind, StatementTransformContext context) { @@ -248,7 +251,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms case AccessPathKind.Setter: if (isCollection || !pathStack.Peek().Add(lastElement)) return false; - if (values.Count != 1 || !IsValidObjectInitializerTarget(currentPath)) + if (values?.Count != 1 || !IsValidObjectInitializerTarget(currentPath)) return false; if (blockKind != BlockKind.ObjectInitializer && blockKind != BlockKind.WithInitializer) blockKind = BlockKind.ObjectInitializer; @@ -264,9 +267,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; var element = path.Last(); var previous = path.SkipLast(1).LastOrDefault(); - if (!(element.Member is IProperty p)) + if (element.Member is not IProperty p) + return true; + if (!p.IsIndexer) return true; - return !p.IsIndexer || NormalizeTypeVisitor.IgnoreNullabilityAndTuples.EquivalentTypes(previous.Member?.ReturnType, element.Member.DeclaringType); + if (previous != default) + { + return NormalizeTypeVisitor.IgnoreNullabilityAndTuples + .EquivalentTypes(previous.Member.ReturnType, element.Member.DeclaringType); + } + return false; } } @@ -279,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public struct AccessPathElement : IEquatable { - public AccessPathElement(OpCode opCode, IMember member, ILInstruction[] indices = null) + public AccessPathElement(OpCode opCode, IMember member, ILInstruction[]? indices = null) { this.OpCode = opCode; this.Member = member; @@ -288,24 +298,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms public readonly OpCode OpCode; public readonly IMember Member; - public readonly ILInstruction[] Indices; + public readonly ILInstruction[]? Indices; public override string ToString() => $"[{Member}, {Indices}]"; - public static (AccessPathKind Kind, List Path, List Values, ILVariable Target) GetAccessPath( - ILInstruction instruction, IType rootType, DecompilerSettings settings = null, - CSharpTypeResolveContext resolveContext = null, - Dictionary possibleIndexVariables = null) + public static (AccessPathKind Kind, List Path, List? Values, ILVariable? Target) GetAccessPath( + ILInstruction instruction, IType rootType, DecompilerSettings? settings = null, + CSharpTypeResolveContext? resolveContext = null, + Dictionary? possibleIndexVariables = null) { List path = new List(); - ILVariable target = null; + ILVariable? target = null; AccessPathKind kind = AccessPathKind.Invalid; - List values = null; + List? values = null; IMethod method; - var inst = instruction; - while (instruction != null) + ILInstruction? inst = instruction; + while (inst != null) { - switch (instruction) + switch (inst) { case CallInstruction call: if (!(call is CallVirt || call is Call)) @@ -313,12 +323,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms method = call.Method; if (resolveContext != null && !IsMethodApplicable(method, call.Arguments, rootType, resolveContext, settings)) goto default; - instruction = call.Arguments[0]; - if (method.IsAccessor) + inst = call.Arguments[0]; + if (method.AccessorOwner is not null) { - var property = method.AccessorOwner as IProperty; - if (!CanBeUsedInInitializer(property, resolveContext, kind, path)) + if (method.AccessorOwner is IProperty property && + !CanBeUsedInInitializer(property, resolveContext, kind)) + { goto default; + } + var isGetter = method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter; var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray(); if (indices.Length > 0 && settings?.DictionaryInitializers == false) @@ -359,7 +372,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (ldobj.Target is LdFlda ldflda && (kind != AccessPathKind.Setter || !ldflda.Field.IsReadOnly)) { path.Insert(0, new AccessPathElement(ldobj.OpCode, ldflda.Field)); - instruction = ldflda.Target; + inst = ldflda.Target; break; } goto default; @@ -369,7 +382,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (stobj.Target is LdFlda ldflda) { path.Insert(0, new AccessPathElement(stobj.OpCode, ldflda.Field)); - instruction = ldflda.Target; + inst = ldflda.Target; if (values == null) { values = new List(new[] { stobj.Value }); @@ -381,35 +394,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms } case LdLoc ldloc: target = ldloc.Variable; - instruction = null; + inst = null; break; case LdLoca ldloca: target = ldloca.Variable; - instruction = null; + inst = null; break; case LdFlda ldflda: path.Insert(0, new AccessPathElement(ldflda.OpCode, ldflda.Field)); - instruction = ldflda.Target; + inst = ldflda.Target; break; default: kind = AccessPathKind.Invalid; - instruction = null; + inst = null; break; } } - if (kind != AccessPathKind.Invalid && values.SelectMany(v => v.Descendants).OfType().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca))) + if (kind != AccessPathKind.Invalid && values != null && values.SelectMany(v => v.Descendants).OfType().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca))) kind = AccessPathKind.Invalid; return (kind, path, values, target); } - private static bool CanBeUsedInInitializer(IProperty property, CSharpTypeResolveContext resolveContext, AccessPathKind kind, List path) + private static bool CanBeUsedInInitializer(IProperty property, CSharpTypeResolveContext? resolveContext, AccessPathKind kind) { if (property.CanSet && (property.Accessibility == property.Setter.Accessibility || IsAccessorAccessible(property.Setter, resolveContext))) return true; return kind != AccessPathKind.Setter; } - private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContext resolveContext) + private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContext? resolveContext) { if (resolveContext == null) return true; @@ -417,7 +430,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition); } - static bool IsMethodApplicable(IMethod method, IReadOnlyList arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings settings) + static bool IsMethodApplicable(IMethod method, IReadOnlyList arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings? settings) { if (method.IsStatic && !method.IsExtensionMethod) return false; @@ -453,7 +466,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - static IType GetReturnTypeFromInstruction(ILInstruction instruction) + static IType? GetReturnTypeFromInstruction(ILInstruction instruction) { switch (instruction) { @@ -474,7 +487,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is AccessPathElement) return Equals((AccessPathElement)obj); @@ -494,8 +507,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms public bool Equals(AccessPathElement other) { - return other.Member.Equals(this.Member) - && (other.Indices == this.Indices || other.Indices.SequenceEqual(this.Indices, ILInstructionMatchComparer.Instance)); + return (other.Member == this.Member + || this.Member.Equals(other.Member)) + && (other.Indices == this.Indices + || (other.Indices != null && this.Indices != null && this.Indices.SequenceEqual(other.Indices, ILInstructionMatchComparer.Instance))); } public static bool operator ==(AccessPathElement lhs, AccessPathElement rhs) @@ -513,7 +528,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public static readonly ILInstructionMatchComparer Instance = new ILInstructionMatchComparer(); - public bool Equals(ILInstruction x, ILInstruction y) + public bool Equals(ILInstruction? x, ILInstruction? y) { if (x == y) return true; From 6c3e5421ffa0873fbc22bb3602a43985b0ea1042 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 01:44:28 +0000 Subject: [PATCH 004/230] Bump actions/setup-dotnet from 2 to 3 Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-frontends.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-frontends.yml b/.github/workflows/build-frontends.yml index c35da7dab..c216fdaae 100644 --- a/.github/workflows/build-frontends.yml +++ b/.github/workflows/build-frontends.yml @@ -15,7 +15,7 @@ jobs: with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4cc0d34d7..9d33c4b96 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: languages: ${{ matrix.language }} - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x From ac6a2e54caa8c4661385b1c51caff6dd3cf06ffc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 3 Oct 2022 14:56:51 +0200 Subject: [PATCH 005/230] Use IsAccessor again. --- .../IL/Transforms/TransformCollectionAndObjectInitializers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 635c962f9..1f8845d93 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (resolveContext != null && !IsMethodApplicable(method, call.Arguments, rootType, resolveContext, settings)) goto default; inst = call.Arguments[0]; - if (method.AccessorOwner is not null) + if (method.IsAccessor) { if (method.AccessorOwner is IProperty property && !CanBeUsedInInitializer(property, resolveContext, kind)) From 47250d670b773dd911da09358eb1995934e5a628 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 3 Oct 2022 16:09:18 +0200 Subject: [PATCH 006/230] Fix #2791: Ensure that the capture scope used is either a loop or the ILFunction root container. --- .../TestCases/Pretty/DelegateConstruction.cs | 44 +++++++++++++++++++ .../CSharp/Transforms/DeclareVariables.cs | 16 +++++-- ICSharpCode.Decompiler/IL/ILVariable.cs | 2 +- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 4046bebd1..6fe579180 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -601,6 +601,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction } } + internal class Issue2791 + { + public void M() + { + Run(delegate (object o) { + try + { + List list = o as List; + Action action = delegate { + list.Select((int x) => x * 2); + }; +#if OPT && ROSLYN + Action obj = delegate { +#else + Action action2 = delegate { +#endif + list.Select((int x) => x * 2); + }; + Console.WriteLine(); + action(); + Console.WriteLine(); +#if OPT && ROSLYN + obj(); +#else + action2(); +#endif + } + catch (Exception) + { + Console.WriteLine("catch"); + } + finally + { + Console.WriteLine("finally"); + } + }, null); + } + + private void Run(ParameterizedThreadStart del, object x) + { + del(x); + } + } + [AttributeUsage(AttributeTargets.All)] internal class MyAttribute : Attribute { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 4eda0ba10..b146e85a4 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -281,7 +281,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms void FindInsertionPoints(AstNode node, int nodeLevel) { BlockContainer scope = node.Annotation(); - if (scope != null && (scope.EntryPoint.IncomingEdgeCount > 1 || scope.Parent is ILFunction)) + if (scope != null && IsRelevantScope(scope)) { // track loops and function bodies as scopes, for comparison with CaptureScope. scopeTracking.Add((new InsertionPoint { level = nodeLevel, nextNode = node }, scope)); @@ -316,9 +316,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { InsertionPoint newPoint; int startIndex = scopeTracking.Count - 1; - if (variable.CaptureScope != null && startIndex > 0 && variable.CaptureScope != scopeTracking[startIndex].Scope) + BlockContainer captureScope = variable.CaptureScope; + while (captureScope != null && !IsRelevantScope(captureScope)) { - while (startIndex > 0 && scopeTracking[startIndex].Scope != variable.CaptureScope) + captureScope = BlockContainer.FindClosestContainer(captureScope.Parent); + } + if (captureScope != null && startIndex > 0 && captureScope != scopeTracking[startIndex].Scope) + { + while (startIndex > 0 && scopeTracking[startIndex].Scope != captureScope) startIndex--; newPoint = scopeTracking[startIndex + 1].InsertionPoint; } @@ -366,6 +371,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } + private static bool IsRelevantScope(BlockContainer scope) + { + return scope.EntryPoint.IncomingEdgeCount > 1 || scope.Parent is ILFunction; + } + internal static bool VariableNeedsDeclaration(VariableKind kind) { switch (kind) diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index bbe6dfaa6..5f2610449 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Gets the block container in which this variable is captured. /// For captured variables declared inside the loop, the capture scope is the BlockContainer of the loop. - /// For captured variables declared outside of the loop, the capture scope is the BlockContainer of the parent. + /// For captured variables declared outside of the loop, the capture scope is the BlockContainer of the parent function. /// /// /// This property returns null for variables that are not captured. From b9f2fc4f963b710e64b20d513e6ca2e28b714075 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 3 Oct 2022 22:43:33 +0200 Subject: [PATCH 007/230] Fix #2781: Fix missing nullability annotation on base type --- .../TypeSystem/Implementation/MetadataTypeDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 1f58efc4c..560683e55 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation EntityHandle baseTypeHandle = td.BaseType; if (!baseTypeHandle.IsNil) { - baseType = module.ResolveType(baseTypeHandle, context); + baseType = module.ResolveType(baseTypeHandle, context, metadata.GetCustomAttributes(this.handle), Nullability.Oblivious); } } catch (BadImageFormatException) From ed46e9b4abe5d0ed975edc04509156e84fe1ca22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:23:13 +0200 Subject: [PATCH 008/230] Bump actions/setup-dotnet from 2 to 3 (#2795) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-frontends.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-frontends.yml b/.github/workflows/build-frontends.yml index c35da7dab..c216fdaae 100644 --- a/.github/workflows/build-frontends.yml +++ b/.github/workflows/build-frontends.yml @@ -15,7 +15,7 @@ jobs: with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4cc0d34d7..9d33c4b96 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: languages: ${{ matrix.language }} - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x From b87f3753e220727a2c5a43c2b28208d990bd2d00 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 4 Oct 2022 22:15:21 +0200 Subject: [PATCH 009/230] Fix #2786: Structs with static reference type fields not recognized as unmanaged --- .../Helpers/Tester.cs | 5 +++ .../PrettyTestRunner.cs | 2 +- .../TestCases/Pretty/UnsafeCode.cs | 44 +++++++++++++++++++ .../TypeSystem/TypeSystemExtensions.cs | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 5a55e10c2..3b880ca57 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -69,6 +69,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers UseMcs5_23 = 0x2000, UseTestRunner = 0x4000, NullableEnable = 0x8000, + ReferenceUnsafe = 0x10000, UseMcsMask = UseMcs2_6_4 | UseMcs5_23, UseRoslynMask = UseRoslyn1_3_2 | UseRoslyn2_10_0 | UseRoslyn3_11_0 | UseRoslynLatest } @@ -425,6 +426,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers { references = references.Concat(new[] { "-r:\"Microsoft.VisualBasic.dll\"" }); } + if (useRoslyn && !targetNet40 && flags.HasFlag(CompilerOptions.ReferenceUnsafe)) + { + references = references.Concat(new[] { "-r:\"System.Runtime.CompilerServices.Unsafe.dll\"" }); + } string otherOptions = $"-noconfig " + $"-langversion:{languageVersion} " + $"-unsafe -o{(flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- ")}"; diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index b2542be8d..549273f56 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -376,7 +376,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { - await RunForLibrary(cscOptions: cscOptions); + await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ReferenceUnsafe); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index ce890fcd0..2bb122eb3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -17,10 +17,54 @@ // DEALINGS IN THE SOFTWARE. using System; +#if !NET40 && ROSLYN +using System.Runtime.CompilerServices; +#endif using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { + internal class SizeofTest + { + private struct StructWithStaticField + { + public static object StaticObj; + + public IntPtr A; + } + + private struct UnmanagedStruct + { + public StructWithStaticField Value; + } + + private struct ManagedStruct + { + public object Obj; + } +#if CS73 + private unsafe int GenericMethod() where T : unmanaged + { + return sizeof(T); + } +#endif + + private unsafe void Test(out int s1, out int s2, out int s3) + { + s1 = 0; + s2 = 0; + s3 = 0; +#if CS73 + GenericMethod(); +#endif + s1 = sizeof(UnmanagedStruct); +#if !NET40 && ROSLYN + s2 = Unsafe.SizeOf(); + s3 = Unsafe.SizeOf(); +#endif + } + } + public class UnsafeCode { public struct SimpleStruct diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 7115696d1..c0e109de8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.TypeSystem types = new HashSet(); } types.Add(type); - foreach (var f in type.GetFields()) + foreach (var f in type.GetFields(f => !f.IsStatic)) { if (types.Contains(f.Type)) { From 18481efc44ce2a019e451834a1510eb8c0163e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Thu, 29 Sep 2022 21:32:19 +0200 Subject: [PATCH 010/230] Fix initialization of FakeProperty with setter Seems like a typo - the setter method was assigned to the Getter property The test GuessAccessors needed adjustments in the generated code. ILSpy is more eager to merge property assignments --- .../TestCases/ILPretty/GuessAccessors.cs | 30 ++++++++----------- .../TypeSystem/MetadataModule.cs | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs index 37d64c6e5..f6ddbbd3f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs @@ -15,9 +15,8 @@ namespace ClassLibrary1 //IL_0007: Expected O, but got Unknown UnknownClass val = new UnknownClass(); int? unknownProperty = val.UnknownProperty; - int? num = unknownProperty.GetValueOrDefault(); - val.UnknownProperty = num; - int? num2 = num; + int? num2 = (val.UnknownProperty = unknownProperty.GetValueOrDefault()); + int? num3 = num2; List list = new List { val[unknownProperty.Value] ?? "", val.NotProperty, @@ -51,10 +50,9 @@ namespace ClassLibrary1 //IL_00e1: Expected O, but got Unknown //IL_00e1: Expected O, but got Unknown UnknownGenericClass val = new UnknownGenericClass(); - UnknownEventArgs unknownProperty = val.UnknownProperty; - val.UnknownProperty = unknownProperty; + UnknownEventArgs val2 = (val.UnknownProperty = val.UnknownProperty); List list = new List { - val[((object)unknownProperty).GetHashCode()] ?? "", + val[((object)val2).GetHashCode()] ?? "", val.NotProperty, val.get_NotPropertyWithGeneric(42), val[42], @@ -63,18 +61,17 @@ namespace ClassLibrary1 }; val.OnEvent += Instance_OnEvent; val.OnEvent -= Instance_OnEvent; - UnknownEventArgs val2 = val[(UnknownEventArgs)null]; - val[new UnknownEventArgs()] = val2; - UnknownEventArgs val3 = val[new UnknownEventArgs(), new UnknownEventArgs()]; - val[new UnknownEventArgs(), new UnknownEventArgs()] = val3; + UnknownEventArgs val3 = val[(UnknownEventArgs)null]; + val[new UnknownEventArgs()] = val3; + UnknownEventArgs val4 = val[new UnknownEventArgs(), new UnknownEventArgs()]; + val[new UnknownEventArgs(), new UnknownEventArgs()] = val4; } public void MethodUnknownStatic() { - int? unknownProperty = UnknownStaticClass.UnknownProperty; - UnknownStaticClass.UnknownProperty = unknownProperty; + int? num = (UnknownStaticClass.UnknownProperty = UnknownStaticClass.UnknownProperty); List list = new List { - UnknownStaticClass[unknownProperty.Value] ?? "", + UnknownStaticClass[num.Value] ?? "", UnknownStaticClass.NotProperty, UnknownStaticClass.get_NotPropertyWithGeneric(42), UnknownStaticClass[42], @@ -87,10 +84,9 @@ namespace ClassLibrary1 public void MethodUnknownStaticGeneric() { - string unknownProperty = UnknownStaticGenericClass.UnknownProperty; - UnknownStaticGenericClass.UnknownProperty = unknownProperty; + string text = (UnknownStaticGenericClass.UnknownProperty = UnknownStaticGenericClass.UnknownProperty); List list = new List { - UnknownStaticGenericClass[unknownProperty.Length] ?? "", + UnknownStaticGenericClass[text.Length] ?? "", UnknownStaticGenericClass.NotProperty, UnknownStaticGenericClass.get_NotPropertyWithGeneric(42), UnknownStaticGenericClass[42], @@ -121,4 +117,4 @@ namespace ClassLibrary1 throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index 818e1ecd9..cf6ae1be5 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -670,7 +670,7 @@ namespace ICSharpCode.Decompiler.TypeSystem m.AccessorKind = MethodSemanticsAttributes.Setter; m.AccessorOwner = fakeProperty; - fakeProperty.Getter = m; + fakeProperty.Setter = m; fakeProperty.ReturnType = parameters.Last().Type; fakeProperty.IsIndexer = parameters.Count > 1; fakeProperty.Parameters = parameters.SkipLast(1).ToArray(); From e8c4f29738d371c4b3b05e5b1cbed17229d96ef1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 5 Oct 2022 21:10:34 +0200 Subject: [PATCH 011/230] #2797: Clarify exception message, if type is not found in the module being decompiled. --- ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index c14b83010..3371ce180 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -953,7 +953,7 @@ namespace ICSharpCode.Decompiler.CSharp if (type == null) throw new InvalidOperationException($"Could not find type definition {fullTypeName} in type system."); if (type.ParentModule != typeSystem.MainModule) - throw new NotSupportedException("Decompiling types that are not part of the main module is not supported."); + throw new NotSupportedException($"Type {fullTypeName} was not found in the module being decompiled, but only in {type.ParentModule.Name}"); var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); var decompileRun = CreateDecompileRun(); syntaxTree = new SyntaxTree(); From b85d1ba65af5d929eccd5f6695a9a9e23ce9f6ab Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 5 Oct 2022 21:13:43 +0200 Subject: [PATCH 012/230] Fix #2771: Allow MetadataTypeDefinition.DefaultMemberName to return null if DefaultMemberAttribute is not present. --- .../TypeSystem/Implementation/MetadataTypeDefinition.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 560683e55..8417e33b0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IEvent[] events; IMethod[] methods; List directBaseTypes; + bool defaultMemberNameInitialized; string defaultMemberName; internal MetadataTypeDefinition(MetadataModule module, TypeDefinitionHandle handle) @@ -459,7 +460,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public string DefaultMemberName { get { string defaultMemberName = LazyInit.VolatileRead(ref this.defaultMemberName); - if (defaultMemberName != null) + if (defaultMemberName != null || defaultMemberNameInitialized) return defaultMemberName; var metadata = module.metadata; var typeDefinition = metadata.GetTypeDefinition(handle); @@ -475,7 +476,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation break; } } - return LazyInit.GetOrSet(ref this.defaultMemberName, defaultMemberName ?? "Item"); + defaultMemberName = LazyInit.GetOrSet(ref this.defaultMemberName, defaultMemberName); + defaultMemberNameInitialized = true; + return defaultMemberName; } } #endregion From 9017592d1db514692d241bf1f3fcb2fd01797d2e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 6 Oct 2022 12:34:45 +0200 Subject: [PATCH 013/230] Fix #2764: Move constant field initializers regardless of whether the other field initializers can be moved. --- .../TransformFieldAndConstructorInitializers.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index 82b4666c2..bb32e2ac6 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -315,9 +315,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms SRM.MethodDefinition ctorMethodDef = metadata.GetMethodDefinition((SRM.MethodDefinitionHandle)ctorMethod.MetadataToken); SRM.TypeDefinition declaringType = metadata.GetTypeDefinition(ctorMethodDef.GetDeclaringType()); bool declaringTypeIsBeforeFieldInit = declaringType.HasFlag(TypeAttributes.BeforeFieldInit); - while (true) + int pos = 0; + while (pos < staticCtor.Body.Statements.Count) { - ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; + ExpressionStatement es = staticCtor.Body.Statements.ElementAtOrDefault(pos) as ExpressionStatement; if (es == null) break; AssignmentExpression assignment = es.Expression as AssignmentExpression; @@ -329,7 +330,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms // Only move fields that are constants, if the declaring type is not marked beforefieldinit. if (!declaringTypeIsBeforeFieldInit && fieldOrProperty is not IField { IsConst: true }) { - break; + pos++; + continue; } var fieldOrPropertyDecl = members.FirstOrDefault(f => f.GetSymbol() == fieldOrProperty) as EntityDeclaration; if (fieldOrPropertyDecl == null) From 711db00ac41a7e5b835a865161ad1251da4737ee Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 6 Oct 2022 14:11:04 +0200 Subject: [PATCH 014/230] #2798: Add missing token column in debug info tables. --- .../DebugTables/CustomDebugInformationTableTreeNode.cs | 4 +++- ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs | 2 ++ ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs | 2 ++ ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs | 2 ++ ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs | 2 ++ ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs | 2 ++ .../DebugTables/MethodDebugInformationTableTreeNode.cs | 2 ++ ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs | 4 +++- 8 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index 47fb5e36e..f3e191de6 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -188,7 +188,9 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); - public object Offset => offset == null ? null : offset; + public int Token => MetadataTokens.GetToken(handle); + + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] [LinkToTable] diff --git a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs index ab1660d3e..c3cef36da 100644 --- a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs @@ -82,6 +82,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; public string Name => metadata.GetString(document.Name); diff --git a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs index a73e65bdc..e7841965d 100644 --- a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs @@ -86,6 +86,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] diff --git a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs index 91ca9c0b4..2a095fee4 100644 --- a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs @@ -84,6 +84,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; public string Name => metadata.GetString(localConst.Name); diff --git a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs index e934deadf..546e5c395 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -86,6 +86,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] diff --git a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs index 458bd5abe..ed8744e2e 100644 --- a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs @@ -80,6 +80,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] diff --git a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs index 8ed8b05fb..46dc93885 100644 --- a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs @@ -84,6 +84,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID => MetadataTokens.GetRowNumber(handle); + public int Token => MetadataTokens.GetToken(handle); + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] diff --git a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs index 8407f7328..5e30b145f 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.ILSpy.Metadata StateMachineMethodEntry scrollTargetEntry = default; var length = metadata.GetTableRowCount(TableIndex.StateMachineMethod); var reader = new BlobReader(metadata.MetadataPointer, metadata.MetadataLength); - reader.Offset = +metadata.GetTableMetadataOffset(TableIndex.StateMachineMethod); + reader.Offset = metadata.GetTableMetadataOffset(TableIndex.StateMachineMethod); for (int rid = 1; rid <= length; rid++) { @@ -88,6 +88,8 @@ namespace ICSharpCode.ILSpy.Metadata public int RID { get; } + public int Token => 0x36000000 + RID; + public object Offset => offset == null ? "n/a" : (object)offset; [StringFormat("X8")] From 6153f9cf3518f90e0bac79487d49200a034a762d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Wed, 5 Oct 2022 16:27:53 +0200 Subject: [PATCH 015/230] Add test for indexer initializers --- .../TestCases/ILPretty/GuessAccessors.cs | 9 ++++++++ .../TestCases/ILPretty/GuessAccessors.il | 22 +++++++++++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 ++ 3 files changed, 33 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs index f6ddbbd3f..c9e0f56e0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs @@ -97,6 +97,15 @@ namespace ClassLibrary1 UnknownStaticGenericClass.OnEvent -= Instance_OnEvent; } + public void MethodUnknownIndexerInitializer() + { + //IL_0006: Unknown result type (might be due to invalid IL or missing references) + new UnknownClass { + ["a"] = 1, + ["b"] = 2 + }; + } + private void Instance_OnEvent(object sender, EventArgs e) { throw new NotImplementedException(); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.il index 7fd6756ec..35fe005d1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.il @@ -378,6 +378,28 @@ IL_0098: ret } // end of method UnknownClassTest::MethodUnknownStaticGeneric + .method public hidebysig + instance void MethodUnknownIndexerInitializer () cil managed + { + // Method begins at RVA 0x2050 + // Code size 32 (0x20) + .maxstack 8 + + IL_0000: nop + IL_0001: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownClass::.ctor() + IL_0006: dup + IL_0007: ldstr "a" + IL_000c: ldc.i4.1 + IL_000d: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_Item(string, int32) + IL_0012: nop + IL_0013: ldstr "b" + IL_0018: ldc.i4.2 + IL_0019: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_Item(string, int32) + IL_001e: nop + IL_001f: ret + } // end of method C::MethodUnknownIndexerInitializer + + .method /* 100663301 */ private hidebysig instance void Instance_OnEvent ( object sender, diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 5213febe3..ddbc92759 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -539,6 +539,8 @@ namespace ICSharpCode.Decompiler.CSharp public ExpressionWithResolveResult BuildDictionaryInitializerExpression(OpCode callOpCode, IMethod method, InitializedObjectResolveResult target, IReadOnlyList indices, ILInstruction value = null) { + if (method is null) + throw new ArgumentNullException(nameof(method)); ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; var callArguments = new List(); From 93eecf93a54522d097d27d41536cba2c3e96f282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Thu, 29 Sep 2022 21:33:36 +0200 Subject: [PATCH 016/230] Add more asserts and ToStrings around the FakeProperty.Setter problem --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 1 + .../TypeSystem/Implementation/FakeMember.cs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index e96407395..e11a9e10d 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3340,6 +3340,7 @@ namespace ICSharpCode.Decompiler.CSharp { var property = (IProperty)lastElement.Member; Debug.Assert(property.IsIndexer); + Debug.Assert(property.Setter != null, $"Indexer property {property} has no setter"); elementsStack.Peek().Add( new CallBuilder(this, typeSystem, settings) .BuildDictionaryInitializerExpression(lastElement.OpCode, property.Setter, initObjRR, GetIndices(lastElement.Indices, indexVariables).ToList(), info.Values.Single()) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs index 43549b6c6..2c4a2f67e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -199,6 +199,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public bool IsIndexer { get; set; } public bool ReturnTypeIsRefReadOnly => false; public IReadOnlyList Parameters { get; set; } + + public override string ToString() => + "FakeProperty " + ReturnType + " " + DeclaringType.Name + "." + Name + + (Parameters.Count == 0 + ? "" + : "[" + string.Join(", ", Parameters) + "]") + + " { " + + (CanGet ? "get; " : "") + + (CanSet ? "set; " : "") + + "}"; } sealed class FakeEvent : FakeMember, IEvent From b66684b7ae8b715569cede68f890cc318c5c7a15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 16 Oct 2022 07:44:16 +0200 Subject: [PATCH 017/230] Bump NuGet.Protocol from 6.2.1 to 6.2.2 in /ICSharpCode.Decompiler.Tests (#2804) Bumps [NuGet.Protocol](https://github.com/NuGet/NuGet.Client) from 6.2.1 to 6.2.2. - [Release notes](https://github.com/NuGet/NuGet.Client/releases) - [Commits](https://github.com/NuGet/NuGet.Client/commits) --- updated-dependencies: - dependency-name: NuGet.Protocol dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../ICSharpCode.Decompiler.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index d342695c0..90c855872 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -45,7 +45,7 @@ - + From 17d7e5e0881d4b52f21de7e80dd1e6eb4e18d8bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 01:44:54 +0000 Subject: [PATCH 018/230] Bump test-summary/action from 1 to 2 Bumps [test-summary/action](https://github.com/test-summary/action) from 1 to 2. - [Release notes](https://github.com/test-summary/action/releases) - [Commits](https://github.com/test-summary/action/compare/v1...v2) --- updated-dependencies: - dependency-name: test-summary/action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-ilspy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index e6a91feee..bf10a7d94 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -60,7 +60,7 @@ jobs: path: 'test-results/${{ matrix.configuration }}.xml' - name: Create Test Report - uses: test-summary/action@v1 + uses: test-summary/action@v2 if: always() with: paths: "test-results/${{ matrix.configuration }}.xml" From 6e70db7dd3f02aa230be8d6c938b2f76005a14eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:00:57 +0200 Subject: [PATCH 019/230] Bump test-summary/action from 1 to 2 (#2805) Bumps [test-summary/action](https://github.com/test-summary/action) from 1 to 2. - [Release notes](https://github.com/test-summary/action/releases) - [Commits](https://github.com/test-summary/action/compare/v1...v2) --- updated-dependencies: - dependency-name: test-summary/action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-ilspy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index e6a91feee..bf10a7d94 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -60,7 +60,7 @@ jobs: path: 'test-results/${{ matrix.configuration }}.xml' - name: Create Test Report - uses: test-summary/action@v1 + uses: test-summary/action@v2 if: always() with: paths: "test-results/${{ matrix.configuration }}.xml" From a494bfadbe5327cd8577b53c40f744d0f39a7072 Mon Sep 17 00:00:00 2001 From: "Andrew Crawley (US - DIAGNOSTICS)" Date: Thu, 13 Oct 2022 17:22:27 -0700 Subject: [PATCH 020/230] Add progress reporting to PortablePdbWriter This commit adds a new parameter to PortablePdbWriter.WritePdb that allows the caller to provide an implementation of IProgress to receive status updates on the pdb generation process. Currently, the progress reports include the number of files generated so far and the total number of files. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PdbGenerationTestRunner.cs | 58 +++++++++++++++++-- .../TestCases/PdbGen/CustomPdbId.xml | 4 +- .../TestCases/PdbGen/ProgressReporting.xml | 44 ++++++++++++++ .../DebugInfo/PortablePdbWriter.cs | 31 +++++++++- 5 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ProgressReporting.xml diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index d342695c0..2e50ffcf1 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -312,6 +312,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index 174711c8b..c2a58c3d8 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; @@ -9,13 +8,10 @@ using System.Text; using System.Xml.Linq; using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Tests.Helpers; -using ICSharpCode.Decompiler.TypeSystem; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.DiaSymReader.Tools; using NUnit.Framework; @@ -72,6 +68,60 @@ namespace ICSharpCode.Decompiler.Tests } } + [Test] + public void ProgressReporting() + { + // Generate a PDB for an assembly and validate that the progress reporter is called with reasonable values + (string peFileName, string pdbFileName) = CompileTestCase(nameof(ProgressReporting)); + + var moduleDefinition = new PEFile(peFileName); + var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); + var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); + + var lastFilesWritten = 0; + var totalFiles = -1; + + Action reportFunc = progress => { + if (totalFiles == -1) + { + // Initialize value on first call + totalFiles = progress.TotalFiles; + } + + Assert.AreEqual(progress.TotalFiles, totalFiles); + Assert.AreEqual(progress.FilesWritten, lastFilesWritten + 1); + + lastFilesWritten = progress.FilesWritten; + }; + + using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + { + pdbStream.SetLength(0); + PortablePdbWriter.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream, noLogo: true, progress: new TestProgressReporter(reportFunc)); + + pdbStream.Position = 0; + var metadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + var generatedPdbId = new BlobContentId(metadataReader.DebugMetadataHeader.Id); + } + + Assert.AreEqual(totalFiles, lastFilesWritten); + } + + private class TestProgressReporter : IProgress + { + private Action reportFunc; + + public TestProgressReporter(Action reportFunc) + { + this.reportFunc = reportFunc; + } + + public void Report(WritePortablePdbProgress value) + { + reportFunc(value); + } + } + private void TestGeneratePdb([CallerMemberName] string testName = null) { const PdbToXmlOptions options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.ThrowOnError | PdbToXmlOptions.IncludeTokens | PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeMethodSpans; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml index e0708da65..e5d691c3d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml @@ -1,11 +1,11 @@ - + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index cc8e342bd..3be497b77 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -28,6 +28,7 @@ using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Security.Cryptography; using System.Text; +using System.Threading; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; @@ -40,6 +41,12 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.DebugInfo { + public struct WritePortablePdbProgress + { + public int TotalFiles { get; internal set; } + public int FilesWritten { get; internal set; } + } + public class PortablePdbWriter { static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); @@ -49,7 +56,14 @@ namespace ICSharpCode.Decompiler.DebugInfo return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); } - public static void WritePdb(PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream, bool noLogo = false, BlobContentId? pdbId = null) + public static void WritePdb( + PEFile file, + CSharpDecompiler decompiler, + DecompilerSettings settings, + Stream targetStream, + bool noLogo = false, + BlobContentId? pdbId = null, + IProgress progress = null) { MetadataBuilder metadata = new MetadataBuilder(); MetadataReader reader = file.Metadata; @@ -72,10 +86,23 @@ namespace ICSharpCode.Decompiler.DebugInfo return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); } - foreach (var sourceFile in reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName)) + var sourceFiles = reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName).ToList(); + WritePortablePdbProgress currentProgress = new WritePortablePdbProgress() { + TotalFiles = sourceFiles.Count, + FilesWritten = 0 + }; + + foreach (var sourceFile in sourceFiles) { // Generate syntax tree var syntaxTree = decompiler.DecompileTypes(sourceFile); + + if (progress != null) + { + currentProgress.FilesWritten++; + progress.Report(currentProgress); + } + if (!syntaxTree.HasChildren) continue; From 343694c5499d2dd68896563bbbfc72604231deef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 21 Oct 2022 09:16:44 +0200 Subject: [PATCH 021/230] Generalize progress reporting for decompilation and PDB generation. --- .../GetDecompiledProjectCmdlet.cs | 4 +- .../PdbGenerationTestRunner.cs | 18 +++---- .../WholeProjectDecompiler.cs | 20 ++------ .../DebugInfo/PortablePdbWriter.cs | 17 +++---- .../DecompilationProgress.cs | 48 +++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 1 + ILSpy/Commands/GeneratePdbContextMenuEntry.cs | 4 +- ILSpy/DecompilationOptions.cs | 9 ++++ ILSpy/Languages/CSharpLanguage.cs | 1 + ILSpy/MainWindow.xaml.cs | 6 ++- 10 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 ICSharpCode.Decompiler/DecompilationProgress.cs diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs index 4db4ad455..9b87d99be 100644 --- a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs +++ b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs @@ -33,8 +33,8 @@ namespace ICSharpCode.Decompiler.PowerShell lock (syncObject) { completed++; - progress = new ProgressRecord(1, "Decompiling " + fileName, $"Completed {completed} of {value.TotalNumberOfFiles}: {value.Status}") { - PercentComplete = (int)(completed * 100.0 / value.TotalNumberOfFiles) + progress = new ProgressRecord(1, "Decompiling " + fileName, $"Completed {completed} of {value.TotalUnits}: {value.Status}") { + PercentComplete = (int)(completed * 100.0 / value.TotalUnits) }; } } diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index c2a58c3d8..991c59cae 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -81,17 +81,17 @@ namespace ICSharpCode.Decompiler.Tests var lastFilesWritten = 0; var totalFiles = -1; - Action reportFunc = progress => { + Action reportFunc = progress => { if (totalFiles == -1) { // Initialize value on first call - totalFiles = progress.TotalFiles; + totalFiles = progress.TotalUnits; } - Assert.AreEqual(progress.TotalFiles, totalFiles); - Assert.AreEqual(progress.FilesWritten, lastFilesWritten + 1); + Assert.AreEqual(progress.TotalUnits, totalFiles); + Assert.AreEqual(progress.UnitsCompleted, lastFilesWritten + 1); - lastFilesWritten = progress.FilesWritten; + lastFilesWritten = progress.UnitsCompleted; }; using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) @@ -107,16 +107,16 @@ namespace ICSharpCode.Decompiler.Tests Assert.AreEqual(totalFiles, lastFilesWritten); } - private class TestProgressReporter : IProgress + private class TestProgressReporter : IProgress { - private Action reportFunc; + private Action reportFunc; - public TestProgressReporter(Action reportFunc) + public TestProgressReporter(Action reportFunc) { this.reportFunc = reportFunc; } - public void Report(WritePortablePdbProgress value) + public void Report(DecompilationProgress value) { reportFunc(value); } diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index 3057cbb6f..c49fde67c 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -223,8 +223,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler return Path.Combine(dir, file); } }, StringComparer.OrdinalIgnoreCase).ToList(); - int total = files.Count; - var progress = ProgressIndicator; + var progressReporter = ProgressIndicator; + var progress = new DecompilationProgress { TotalUnits = files.Count, Title = "Exporting project..." }; DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings); Parallel.ForEach( Partitioner.Create(files, loadBalance: true), @@ -253,7 +253,9 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException); } } - progress?.Report(new DecompilationProgress(total, file.Key)); + progress.Status = file.Key; + Interlocked.Increment(ref progress.UnitsCompleted); + progressReporter?.Report(progress); }); return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); } @@ -705,16 +707,4 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler return TargetServices.DetectTargetFramework(module).Moniker != null; } } - - public readonly struct DecompilationProgress - { - public readonly int TotalNumberOfFiles; - public readonly string Status; - - public DecompilationProgress(int total, string status = null) - { - this.TotalNumberOfFiles = total; - this.Status = status ?? ""; - } - } } diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index 3be497b77..b0b419aab 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -41,12 +41,6 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.DebugInfo { - public struct WritePortablePdbProgress - { - public int TotalFiles { get; internal set; } - public int FilesWritten { get; internal set; } - } - public class PortablePdbWriter { static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); @@ -63,7 +57,7 @@ namespace ICSharpCode.Decompiler.DebugInfo Stream targetStream, bool noLogo = false, BlobContentId? pdbId = null, - IProgress progress = null) + IProgress progress = null) { MetadataBuilder metadata = new MetadataBuilder(); MetadataReader reader = file.Metadata; @@ -87,9 +81,10 @@ namespace ICSharpCode.Decompiler.DebugInfo } var sourceFiles = reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName).ToList(); - WritePortablePdbProgress currentProgress = new WritePortablePdbProgress() { - TotalFiles = sourceFiles.Count, - FilesWritten = 0 + DecompilationProgress currentProgress = new() { + TotalUnits = sourceFiles.Count, + UnitsCompleted = 0, + Title = "Generating portable PDB..." }; foreach (var sourceFile in sourceFiles) @@ -99,7 +94,7 @@ namespace ICSharpCode.Decompiler.DebugInfo if (progress != null) { - currentProgress.FilesWritten++; + currentProgress.UnitsCompleted++; progress.Report(currentProgress); } diff --git a/ICSharpCode.Decompiler/DecompilationProgress.cs b/ICSharpCode.Decompiler/DecompilationProgress.cs new file mode 100644 index 000000000..3d9634998 --- /dev/null +++ b/ICSharpCode.Decompiler/DecompilationProgress.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +namespace ICSharpCode.Decompiler +{ + /// + /// Information used for (optional) progress reporting by the decompiler. + /// + public struct DecompilationProgress + { + /// + /// The total number of units to process. If set to a value <= 0, an indeterminate progress bar is displayed. + /// + public int TotalUnits; + + /// + /// The number of units currently completed. Should be a positive number. + /// + public int UnitsCompleted; + + /// + /// Optional information displayed alongside the progress bar. + /// + public string? Status; + + /// + /// Optional custom title for the operation. + /// + public string? Title; + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index b3b5cc7a6..25d9b0bf9 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -90,6 +90,7 @@ + diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index 3af1c3b64..84bafc423 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -75,12 +75,14 @@ namespace ICSharpCode.ILSpy Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Stopwatch stopwatch = Stopwatch.StartNew(); + options.CancellationToken = ct; using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { try { var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(), options.DecompilerSettings); - PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream); + decompiler.CancellationToken = ct; + PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress); } catch (OperationCanceledException) { diff --git a/ILSpy/DecompilationOptions.cs b/ILSpy/DecompilationOptions.cs index d241da8f1..7d6feb5c0 100644 --- a/ILSpy/DecompilationOptions.cs +++ b/ILSpy/DecompilationOptions.cs @@ -19,6 +19,7 @@ using System; using System.Threading; +using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpyX; @@ -55,6 +56,14 @@ namespace ICSharpCode.ILSpy /// public CancellationToken CancellationToken { get; set; } + /// + /// Gets the progress reporter. + /// + /// + /// If decompilers do not implement progress reporting, an indeterminate wait bar is displayed. + /// + public IProgress Progress { get; set; } + /// /// Gets the settings for the decompiler. /// diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index a1d942882..492ecf220 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -404,6 +404,7 @@ namespace ICSharpCode.ILSpy options.DecompilerSettings.UseSdkStyleProjectFormat = false; } var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); + decompiler.ProgressIndicator = options.Progress; return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index cf64691d7..3ffe81d2d 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -105,7 +105,11 @@ namespace ICSharpCode.ILSpy public DisplaySettings CurrentDisplaySettings { get; internal set; } - public DecompilationOptions CreateDecompilationOptions() => new DecompilationOptions(CurrentLanguageVersion, CurrentDecompilerSettings, CurrentDisplaySettings); + public DecompilationOptions CreateDecompilationOptions() + { + var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress; + return new DecompilationOptions(CurrentLanguageVersion, CurrentDecompilerSettings, CurrentDisplaySettings) { Progress = decompilerView }; + } public MainWindow() { From 4619b997e530eff34aadef5d034d2925a1d409c2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 21 Oct 2022 09:19:15 +0200 Subject: [PATCH 022/230] Implement better progress reporting in ILSpy. --- ILSpy/TextView/DecompilerTextView.cs | 39 ++++++++++++++++++-------- ILSpy/TextView/DecompilerTextView.xaml | 21 +++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 02c00bfda..539c0b4c4 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.ILSpy.TextView /// Manages the TextEditor showing the decompiled code. /// Contains all the threading logic that makes the decompiler work in the background. /// - public sealed partial class DecompilerTextView : UserControl, IDisposable, IHaveState + public sealed partial class DecompilerTextView : UserControl, IDisposable, IHaveState, IProgress { readonly ReferenceElementGenerator referenceElementGenerator; readonly UIElementGenerator uiElementGenerator; @@ -540,18 +540,26 @@ namespace ICSharpCode.ILSpy.TextView #endregion #region RunWithCancellation - /// - /// Switches the GUI into "waiting" mode, then calls to create - /// the task. - /// When the task completes without being cancelled, the - /// callback is called on the GUI thread. - /// When the task is cancelled before completing, the callback is not called; and any result - /// of the task (including exceptions) are ignored. - /// - [Obsolete("RunWithCancellation(taskCreation).ContinueWith(taskCompleted) instead")] - public void RunWithCancellation(Func> taskCreation, Action> taskCompleted) + public void Report(DecompilationProgress value) { - RunWithCancellation(taskCreation).ContinueWith(taskCompleted, CancellationToken.None, TaskContinuationOptions.NotOnCanceled, TaskScheduler.FromCurrentSynchronizationContext()); + double v = (double)value.UnitsCompleted / value.TotalUnits; + Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate { + progressBar.IsIndeterminate = !double.IsFinite(v); + progressBar.Value = v * 100.0; + progressTitle.Text = !string.IsNullOrWhiteSpace(value.Title) ? value.Title : Properties.Resources.Decompiling; + progressText.Text = value.Status; + progressText.Visibility = !string.IsNullOrWhiteSpace(progressText.Text) ? Visibility.Visible : Visibility.Collapsed; + var taskBar = MainWindow.Instance.TaskbarItemInfo; + if (taskBar != null) + { + taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal; + taskBar.ProgressValue = v; + } + if (this.DataContext is TabPageModel model) + { + model.Title = progressTitle.Text; + } + }); } /// @@ -566,7 +574,10 @@ namespace ICSharpCode.ILSpy.TextView waitAdorner.Visibility = Visibility.Visible; // Work around a WPF bug by setting IsIndeterminate only while the progress bar is visible. // https://github.com/icsharpcode/ILSpy/issues/593 + progressTitle.Text = Properties.Resources.Decompiling; progressBar.IsIndeterminate = true; + progressText.Text = null; + progressText.Visibility = Visibility.Collapsed; waitAdorner.BeginAnimation(OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.5)), FillBehavior.Stop)); var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) @@ -605,6 +616,8 @@ namespace ICSharpCode.ILSpy.TextView currentCancellationTokenSource = null; waitAdorner.Visibility = Visibility.Collapsed; progressBar.IsIndeterminate = false; + progressText.Text = null; + progressText.Visibility = Visibility.Collapsed; var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { @@ -828,6 +841,7 @@ namespace ICSharpCode.ILSpy.TextView return RunWithCancellation( delegate (CancellationToken ct) { // creation of the background task context.Options.CancellationToken = ct; + context.Options.Progress = this; decompiledNodes = context.TreeNodes; return DecompileAsync(context, outputLengthLimit); }) @@ -1091,6 +1105,7 @@ namespace ICSharpCode.ILSpy.TextView { bool originalProjectFormatSetting = context.Options.DecompilerSettings.UseSdkStyleProjectFormat; context.Options.EscapeInvalidIdentifiers = true; + context.Options.Progress = this; AvalonEditTextOutput output = new AvalonEditTextOutput { EnableHyperlinks = true, Title = string.Join(", ", context.TreeNodes.Select(n => n.Text)) diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index 66960f102..6ff6d2130 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -20,8 +20,7 @@ folding:FoldingMargin.FoldingMarkerBackgroundBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" folding:FoldingMargin.SelectedFoldingMarkerBackgroundBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" folding:FoldingMargin.FoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" - folding:FoldingMargin.SelectedFoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" - > + folding:FoldingMargin.SelectedFoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"> @@ -209,9 +203,9 @@ ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Workspace.ActiveTabPage.FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> - - - + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index aa8eb92b2..eaa65475b 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -184,7 +184,7 @@ namespace ICSharpCode.ILSpy case nameof(SessionSettings.ActiveAssemblyList): ShowAssemblyList(sessionSettings.ActiveAssemblyList); break; - case nameof(SessionSettings.IsDarkMode): + case nameof(SessionSettings.Theme): // update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change) DecompilerTextView.RegisterHighlighting(); DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState); diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 60114829a..f9002b35d 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -567,15 +566,6 @@ namespace ICSharpCode.ILSpy.Properties { } } - /// - /// Looks up a localized string similar to Dark Mode. - /// - public static string DarkMode { - get { - return ResourceManager.GetString("DarkMode", resourceCulture); - } - } - /// /// Looks up a localized string similar to DEBUG -- Decompile All. /// @@ -2646,6 +2636,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Theme. + /// + public static string Theme { + get { + return ResourceManager.GetString("Theme", resourceCulture); + } + } + /// /// Looks up a localized string similar to Toggle All Folding. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 9f873ec56..f9e949fa8 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -222,9 +222,6 @@ Are you sure you want to continue? DEBUG -- Dump PDB as XML - - Dark Mode - Debug Steps @@ -907,6 +904,9 @@ Do you want to continue? Tab size: + + Theme + Toggle All Folding diff --git a/ILSpy/Properties/Resources.zh-Hans.resx b/ILSpy/Properties/Resources.zh-Hans.resx index bf8f4d400..3715d1004 100644 --- a/ILSpy/Properties/Resources.zh-Hans.resx +++ b/ILSpy/Properties/Resources.zh-Hans.resx @@ -222,9 +222,6 @@ 调试 -- PDB 转储为 XML - - 深色 - 调试步骤 diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs index a39262ee2..245dd99d1 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -64,7 +64,7 @@ namespace ICSharpCode.ILSpy this.TopPaneSplitterPosition = FromString((string)doc.Element("TopPaneSplitterPosition"), 0.3); this.BottomPaneSplitterPosition = FromString((string)doc.Element("BottomPaneSplitterPosition"), 0.3); this.SelectedSearchMode = FromString((string)doc.Element("SelectedSearchMode"), SearchMode.TypeAndMember); - this.IsDarkMode = FromString((string)doc.Element(nameof(IsDarkMode)), false); + this.Theme = FromString((string)doc.Element(nameof(Theme)), ThemeManager.Current.DefaultTheme); string currentCulture = (string)doc.Element(nameof(CurrentCulture)); this.CurrentCulture = string.IsNullOrEmpty(currentCulture) ? null : currentCulture; @@ -81,10 +81,10 @@ namespace ICSharpCode.ILSpy public FilterSettings FilterSettings { get; internal set; } public SearchMode SelectedSearchMode { get; set; } - public bool IsDarkMode { - get => ThemeManager.Current.IsDarkMode; + public string Theme { + get => ThemeManager.Current.Theme; set { - ThemeManager.Current.IsDarkMode = value; + ThemeManager.Current.Theme = value; OnPropertyChanged(); } } @@ -149,7 +149,7 @@ namespace ICSharpCode.ILSpy doc.Add(new XElement("TopPaneSplitterPosition", ToString(this.TopPaneSplitterPosition))); doc.Add(new XElement("BottomPaneSplitterPosition", ToString(this.BottomPaneSplitterPosition))); doc.Add(new XElement("SelectedSearchMode", ToString(this.SelectedSearchMode))); - doc.Add(new XElement(nameof(IsDarkMode), ToString(this.IsDarkMode))); + doc.Add(new XElement(nameof(Theme), ToString(this.Theme))); if (this.CurrentCulture != null) { doc.Add(new XElement(nameof(CurrentCulture), this.CurrentCulture)); diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 21991df25..0697e92a8 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -1364,7 +1364,7 @@ namespace ICSharpCode.ILSpy.TextView string[] extensions, string resourceName) { - if (ThemeManager.Current.IsDarkMode) + if (ThemeManager.Current.Theme == "Dark") { resourceName += "-Dark"; } diff --git a/ILSpy/Themes/DarkTheme.xaml b/ILSpy/Themes/DarkTheme.xaml index 044f7f1b5..21308eb83 100644 --- a/ILSpy/Themes/DarkTheme.xaml +++ b/ILSpy/Themes/DarkTheme.xaml @@ -63,4 +63,6 @@ MediumVioletRed CornflowerBlue + + \ No newline at end of file diff --git a/ILSpy/Themes/ResourceKeys.cs b/ILSpy/Themes/ResourceKeys.cs index 6150f7d6d..15347570e 100644 --- a/ILSpy/Themes/ResourceKeys.cs +++ b/ILSpy/Themes/ResourceKeys.cs @@ -29,5 +29,7 @@ namespace ICSharpCode.ILSpy.Themes public static ResourceKey LinkTextForegroundBrush = new ComponentResourceKey(typeof(ResourceKeys), "LinkTextForegroundBrush"); public static ResourceKey BracketHighlightBackgroundBrush = new ComponentResourceKey(typeof(ResourceKeys), "BracketHighlightBackgroundBrush"); public static ResourceKey BracketHighlightBorderPen = new ComponentResourceKey(typeof(ResourceKeys), "BracketHighlightBorderPen"); + + public static ResourceKey ThemeAwareButtonEffect = new ComponentResourceKey(typeof(ResourceKeys), nameof(ThemeAwareButtonEffect)); } } diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index 08e117815..cc7368754 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -17,17 +17,18 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using System.Windows; using System.Windows.Controls; namespace ICSharpCode.ILSpy.Themes { - internal class ThemeManager + public class ThemeManager { - private bool _isDarkMode; + private static List _allThemes; + private string _theme; private readonly ResourceDictionary _themeDictionaryContainer = new ResourceDictionary(); - public static readonly ThemeManager Current = new ThemeManager(); private ThemeManager() @@ -35,16 +36,16 @@ namespace ICSharpCode.ILSpy.Themes Application.Current.Resources.MergedDictionaries.Add(_themeDictionaryContainer); } - public bool IsDarkMode { - get => _isDarkMode; + public string DefaultTheme => "Light"; + public static IReadOnlyCollection AllThemes => new[] { "Light", "Dark" }; + + public string Theme { + get => _theme; set { - _isDarkMode = value; + _theme = value; _themeDictionaryContainer.MergedDictionaries.Clear(); - - string theme = value ? "Dark" : "Light"; - - _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{theme}Theme.xaml", UriKind.Relative) }); + _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{value}Theme.xaml", UriKind.Relative) }); } } From aa0abcdd51714683150bd5940b4ac04f3cba4e5a Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Mon, 20 Feb 2023 22:46:12 +0100 Subject: [PATCH 108/230] Override syntax colors from theme dictionary --- ILSpy/ILSpy.csproj | 4 - ILSpy/TextView/Asm-Mode-Dark.xshd | 1211 -------------------------- ILSpy/TextView/CSharp-Mode-Dark.xshd | 149 ---- ILSpy/TextView/DecompilerTextView.cs | 13 +- ILSpy/TextView/ILAsm-Mode-Dark.xshd | 530 ----------- ILSpy/TextView/XML-Mode-Dark.xshd | 63 -- ILSpy/Themes/DarkTheme.xaml | 69 ++ ILSpy/Themes/SyntaxColor.cs | 31 + ILSpy/Themes/ThemeManager.cs | 52 +- 9 files changed, 148 insertions(+), 1974 deletions(-) delete mode 100644 ILSpy/TextView/Asm-Mode-Dark.xshd delete mode 100644 ILSpy/TextView/CSharp-Mode-Dark.xshd delete mode 100644 ILSpy/TextView/ILAsm-Mode-Dark.xshd delete mode 100644 ILSpy/TextView/XML-Mode-Dark.xshd create mode 100644 ILSpy/Themes/SyntaxColor.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index eb2cf3f62..74f845893 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -69,10 +69,6 @@ - - - - diff --git a/ILSpy/TextView/Asm-Mode-Dark.xshd b/ILSpy/TextView/Asm-Mode-Dark.xshd deleted file mode 100644 index 9953acbf0..000000000 --- a/ILSpy/TextView/Asm-Mode-Dark.xshd +++ /dev/null @@ -1,1211 +0,0 @@ - - - - - - - - - - - - - - aaa - aad - aam - aas - adc - add - and - call - cbw - cdqe - clc - cld - cli - cmc - cmp - cmps - cmpsb - cmpsw - cwd - daa - das - dec - div - esc - hlt - idiv - imul - in - inc - int - into - iret - ja - jae - jb - jbe - jc - jcxz - je - jg - jge - jl - jle - jmp - jna - jnae - jnb - jnbe - jnc - jne - jng - jnge - jnl - jnle - jno - jnp - jns - jnz - jo - jp - jpe - jpo - js - jz - lahf - lds - lea - les - lods - lodsb - lodsw - loop - loope - loopew - loopne - loopnew - loopnz - loopnzw - loopw - loopz - loopzw - mov - movabs - movs - movsb - movsw - mul - neg - nop - not - or - out - pop - popf - push - pushf - rcl - rcr - ret - retf - retn - rol - ror - sahf - sal - sar - sbb - scas - scasb - scasw - shl - shr - stc - std - sti - stos - stosb - stosw - sub - test - wait - xchg - xlat - xlatb - xor - bound - enter - ins - insb - insw - leave - outs - outsb - outsw - popa - pusha - pushw - arpl - lar - lsl - sgdt - sidt - sldt - smsw - str - verr - verw - clts - lgdt - lidt - lldt - lmsw - ltr - bsf - bsr - bt - btc - btr - bts - cdq - cmpsd - cwde - insd - iretd - iretdf - iretf - jecxz - lfs - lgs - lodsd - loopd - looped - loopned - loopnzd - loopzd - lss - movsd - movsx - movsxd - movzx - outsd - popad - popfd - pushad - pushd - pushfd - scasd - seta - setae - setb - setbe - setc - sete - setg - setge - setl - setle - setna - setnae - setnb - setnbe - setnc - setne - setng - setnge - setnl - setnle - setno - setnp - setns - setnz - seto - setp - setpe - setpo - sets - setz - shld - shrd - stosd - bswap - cmpxchg - invd - invlpg - wbinvd - xadd - lock - rep - repe - repne - repnz - repz - cflush - cpuid - emms - femms - cmovo - cmovno - cmovb - cmovc - cmovnae - cmovae - cmovnb - cmovnc - cmove - cmovz - cmovne - cmovnz - cmovbe - cmovna - cmova - cmovnbe - cmovs - cmovns - cmovp - cmovpe - cmovnp - cmovpo - cmovl - cmovnge - cmovge - cmovnl - cmovle - cmovng - cmovg - cmovnle - cmpxchg486 - cmpxchg8b - loadall - loadall286 - ibts - icebp - int1 - int3 - int01 - int03 - iretw - popaw - popfw - pushaw - pushfw - rdmsr - rdpmc - rdshr - rdtsc - rsdc - rsldt - rsm - rsts - salc - smi - smint - smintold - svdc - svldt - svts - syscall - sysenter - sysexit - sysret - ud0 - ud1 - ud2 - umov - xbts - wrmsr - wrshr - - - f2xm1 - fabs - fadd - faddp - fbld - fbstp - fchs - fclex - fcom - fcomp - fcompp - fdecstp - fdisi - fdiv - fdivp - fdivr - fdivrp - feni - ffree - fiadd - ficom - ficomp - fidiv - fidivr - fild - fimul - fincstp - finit - fist - fistp - fisub - fisubr - fld - fld1 - fldcw - fldenv - fldenvw - fldl2e - fldl2t - fldlg2 - fldln2 - fldpi - fldz - fmul - fmulp - fnclex - fndisi - fneni - fninit - fnop - fnsave - fnsavew - fnstcw - fnstenv - fnstenvw - fnstsw - fpatan - fprem - fptan - frndint - frstor - frstorw - fsave - fsavew - fscale - fsqrt - fst - fstcw - fstenv - fstenvw - fstp - fstsw - fsub - fsubp - fsubr - fsubrp - ftst - fwait - fxam - fxch - fxtract - fyl2x - fyl2xp1 - fsetpm - fcos - fldenvd - fnsaved - fnstenvd - fprem1 - frstord - fsaved - fsin - fsincos - fstenvd - fucom - fucomp - fucompp - fcomi - fcomip - ffreep - fcmovb - fcmove - fcmovbe - fcmovu - fcmovnb - fcmovne - fcmovnbe - fcmovnu - - - ah - al - ax - bh - bl - bp - bx - ch - cl - cr0 - cr2 - cr3 - cr4 - cs - cx - dh - di - dl - dr0 - dr1 - dr2 - dr3 - dr6 - dr7 - ds - dx - eax - ebp - ebx - ecx - edi - edx - es - esi - esp - fs - gs - rax - rbx - rcx - rdx - rdi - rsi - rbp - rsp - r8 - r9 - r10 - r11 - r12 - r13 - r14 - r15 - r8d - r9d - r10d - r11d - r12d - r13d - r14d - r15d - r8w - r9w - r10w - r11w - r12w - r13w - r14w - r15w - r8b - r9b - r10b - r11b - r12b - r13b - r14b - r15b - si - sp - ss - st - tr3 - tr4 - tr5 - tr6 - tr7 - st0 - st1 - st2 - st3 - st4 - st5 - st6 - st7 - mm0 - mm1 - mm2 - mm3 - mm4 - mm5 - mm6 - mm7 - xmm0 - xmm1 - xmm2 - xmm3 - xmm4 - xmm5 - xmm6 - xmm7 - xmm8 - xmm9 - xmm10 - xmm11 - xmm12 - xmm13 - xmm14 - xmm15 - - - .186 - .286 - .286c - .286p - .287 - .386 - .386c - .386p - .387 - .486 - .486p - .8086 - .8087 - .alpha - .break - .code - .const - .continue - .cref - .data - .data? - .dosseg - .else - .elseif - .endif - .endw - .err - .err1 - .err2 - .errb - .errdef - .errdif - .errdifi - .erre - .erridn - .erridni - .errnb - .errndef - .errnz - .exit - .fardata - .fardata? - .if - .lall - .lfcond - .list - .listall - .listif - .listmacro - .listmacroall - .model - .no87 - .nocref - .nolist - .nolistif - .nolistmacro - .radix - .repeat - .sall - .seq - .sfcond - .stack - .startup - .tfcond - .type - .until - .untilcxz - .while - .xall - .xcref - .xlist - alias - align - assume - catstr - comm - comment - db - dd - df - dosseg - dq - dt - dup - dw - echo - else - elseif - elseif1 - elseif2 - elseifb - elseifdef - elseifdif - elseifdifi - elseife - elseifidn - elseifidni - elseifnb - elseifndef - end - endif - endm - endp - ends - eq - equ - even - exitm - extern - externdef - extrn - for - forc - ge - goto - group - gt - high - highword - if - if1 - if2 - ifb - ifdef - ifdif - ifdifi - ife - ifidn - ifidni - ifnb - ifndef - include - includelib - instr - invoke - irp - irpc - label - le - length - lengthof - local - low - lowword - lroffset - lt - macro - mask - mod - .msfloat - name - ne - offset - opattr - option - org - %out - page - popcontext - proc - proto - ptr - public - purge - pushcontext - record - repeat - rept - seg - segment - short - size - sizeof - sizestr - struc - struct - substr - subtitle - subttl - textequ - this - title - type - typedef - union - while - width - resb - resw - resd - resq - rest - incbin - times - %define - %idefine - %xdefine - %xidefine - %undef - %assign - %iassign - %strlen - %substr - %macro - %imacro - %endmacro - %rotate - %if - %elif - %else - %endif - %ifdef - %ifndef - %elifdef - %elifndef - %ifmacro - %ifnmacro - %elifmacro - %elifnmacro - %ifctk - %ifnctk - %elifctk - %elifnctk - %ifidn - %ifnidn - %elifidn - %elifnidn - %ifidni - %ifnidni - %elifidni - %elifnidni - %ifid - %ifnid - %elifid - %elifnid - %ifstr - %ifnstr - %elifstr - %elifnstr - %ifnum - %ifnnum - %elifnum - %elifnnum - %error - %rep - %endrep - %exitrep - %include - %push - %pop - %repl - endstruc - istruc - at - iend - alignb - %arg - %stacksize - %local - %line - bits - use16 - use32 - section - absolute - global - common - cpu - import - export - - - $ - ? - @b - @f - addr - basic - byte - c - carry? - dword - far - far16 - fortran - fword - near - near16 - overflow? - parity? - pascal - qword - real4 - real8 - real10 - sbyte - sdword - sign? - stdcall - sword - syscall - tbyte - vararg - word - zero? - flat - near32 - far32 - abs - all - assumes - at - casemap - common - compact - cpu - dotname - emulator - epilogue - error - export - expr16 - expr32 - farstack - forceframe - huge - language - large - listing - ljmp - loadds - m510 - medium - memory - nearstack - nodotname - noemulator - nokeyword - noljmp - nom510 - none - nonunique - nooldmacros - nooldstructs - noreadonly - noscoped - nosignextend - nothing - notpublic - oldmacros - oldstructs - os_dos - para - private - prologue - radix - readonly - req - scoped - setif2 - smallstack - tiny - use16 - use32 - uses - a16 - a32 - o16 - o32 - nosplit - $$ - seq - wrt - small - .text - .data - .bss - %0 - %1 - %2 - %3 - %4 - %5 - %6 - %7 - %8 - %9 - - - addpd - addps - addsd - addss - andpd - andps - andnpd - andnps - cmpeqpd - cmpltpd - cmplepd - cmpunordpd - cmpnepd - cmpnltpd - cmpnlepd - cmpordpd - cmpeqps - cmpltps - cmpleps - cmpunordps - cmpneps - cmpnltps - cmpnleps - cmpordps - cmpeqsd - cmpltsd - cmplesd - cmpunordsd - cmpnesd - cmpnltsd - cmpnlesd - cmpordsd - cmpeqss - cmpltss - cmpless - cmpunordss - cmpness - cmpnltss - cmpnless - cmpordss - comisd - comiss - cvtdq2pd - cvtdq2ps - cvtpd2dq - cvtpd2pi - cvtpd2ps - cvtpi2pd - cvtpi2ps - cvtps2dq - cvtps2pd - cvtps2pi - cvtss2sd - cvtss2si - cvtsd2si - cvtsd2ss - cvtsi2sd - cvtsi2ss - cvttpd2dq - cvttpd2pi - cvttps2dq - cvttps2pi - cvttsd2si - cvttss2si - divpd - divps - divsd - divss - fxrstor - fxsave - ldmxscr - lfence - mfence - maskmovdqu - maskmovdq - maxpd - maxps - paxsd - maxss - minpd - minps - minsd - minss - movapd - movaps - movdq2q - movdqa - movdqu - movhlps - movhpd - movhps - movd - movq - movlhps - movlpd - movlps - movmskpd - movmskps - movntdq - movnti - movntpd - movntps - movntq - movq2dq - movsd - movss - movupd - movups - mulpd - mulps - mulsd - mulss - orpd - orps - packssdw - packsswb - packuswb - paddb - paddsb - paddw - paddsw - paddd - paddsiw - paddq - paddusb - paddusw - pand - pandn - pause - paveb - pavgb - pavgw - pavgusb - pdistib - pextrw - pcmpeqb - pcmpeqw - pcmpeqd - pcmpgtb - pcmpgtw - pcmpgtd - pf2id - pf2iw - pfacc - pfadd - pfcmpeq - pfcmpge - pfcmpgt - pfmax - pfmin - pfmul - pmachriw - pmaddwd - pmagw - pmaxsw - pmaxub - pminsw - pminub - pmovmskb - pmulhrwc - pmulhriw - pmulhrwa - pmulhuw - pmulhw - pmullw - pmuludq - pmvzb - pmvnzb - pmvlzb - pmvgezb - pfnacc - pfpnacc - por - prefetch - prefetchw - prefetchnta - prefetcht0 - prefetcht1 - prefetcht2 - pfrcp - pfrcpit1 - pfrcpit2 - pfrsqit1 - pfrsqrt - pfsub - pfsubr - pi2fd - pinsrw - psadbw - pshufd - pshufhw - pshuflw - pshufw - psllw - pslld - psllq - pslldq - psraw - psrad - psrlw - psrld - psrlq - psrldq - psubb - psubw - psubd - psubq - psubsb - psubsw - psubusb - psubusw - psubsiw - pswapd - punpckhbw - punpckhwd - punpckhdq - punpckhqdq - punpcklbw - punpcklwd - punpckldq - punpcklqdq - pxor - rcpps - rcpss - rsqrtps - rsqrtss - sfence - shufpd - shufps - sqrtpd - sqrtps - sqrtsd - sqrtss - stmxcsr - subpd - subps - subsd - subss - ucomisd - ucomiss - unpckhpd - unpckhps - unpcklpd - unpcklps - xorpd - xorps - - - ; - - - \b(0[xXhH])?[0-9a-fA-F_`]+[h]? # hex number - | - ( \b\d+(\.[0-9]+)? #number with optional floating point - | \.[0-9]+ #or just starting with floating point - ) - ([eE][+-]?[0-9]+)? # optional exponent - - - - - TODO - FIXME - - - HACK - UNDONE - - - \ No newline at end of file diff --git a/ILSpy/TextView/CSharp-Mode-Dark.xshd b/ILSpy/TextView/CSharp-Mode-Dark.xshd deleted file mode 100644 index 7d7c7aa73..000000000 --- a/ILSpy/TextView/CSharp-Mode-Dark.xshd +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TODO - FIXME - - - HACK - UNDONE - - - - - - - \# - - - - (define|undef|if|elif|else|endif|line)\b - - - - // - - - - - - (region|endregion|error|warning|pragma)\b - - - - - - - ///(?!/) - - - - - - - - // - - - - /\* - \*/ - - - - " - " - - - - - - - - ' - ' - - - - - - - - @" - " - - - - - - - - \$" - " - - - - - - - - - - - - \b0[xX][0-9a-fA-F]+ # hex number - | - ( \b\d+(\.[0-9]+)? #number with optional floating point - | \.[0-9]+ #or just starting with floating point - ) - ([eE][+-]?[0-9]+)? # optional exponent - - - diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 0697e92a8..6bdee75ba 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -1364,15 +1364,8 @@ namespace ICSharpCode.ILSpy.TextView string[] extensions, string resourceName) { - if (ThemeManager.Current.Theme == "Dark") - { - resourceName += "-Dark"; - } - - resourceName += ".xshd"; - Stream? resourceStream = typeof(DecompilerTextView).Assembly - .GetManifestResourceStream(typeof(DecompilerTextView), resourceName); + .GetManifestResourceStream(typeof(DecompilerTextView), resourceName + ".xshd"); if (resourceStream != null) { @@ -1382,7 +1375,9 @@ namespace ICSharpCode.ILSpy.TextView using (resourceStream) using (XmlTextReader reader = new XmlTextReader(resourceStream)) { - return HighlightingLoader.Load(reader, manager); + var highlightingDefinition = HighlightingLoader.Load(reader, manager); + ThemeManager.Current.UpdateColors(highlightingDefinition); + return highlightingDefinition; } }); } diff --git a/ILSpy/TextView/ILAsm-Mode-Dark.xshd b/ILSpy/TextView/ILAsm-Mode-Dark.xshd deleted file mode 100644 index 563d1bc55..000000000 --- a/ILSpy/TextView/ILAsm-Mode-Dark.xshd +++ /dev/null @@ -1,530 +0,0 @@ - - - - - - - - - - - nop - break - ldarg.0 - ldarg.1 - ldarg.2 - ldarg.3 - ldloc.0 - ldloc.1 - ldloc.2 - ldloc.3 - stloc.0 - stloc.1 - stloc.2 - stloc.3 - ldarg.s - ldarga.s - starg.s - ldloc.s - ldloca.s - stloc.s - ldnull - ldc.i4.m1 - ldc.i4.0 - ldc.i4.1 - ldc.i4.2 - ldc.i4.3 - ldc.i4.4 - ldc.i4.5 - ldc.i4.6 - ldc.i4.7 - ldc.i4.8 - ldc.i4.s - ldc.i4 - ldc.i8 - ldc.r4 - ldc.r8 - dup - pop - jmp - call - calli - ret - br.s - brfalse.s - brtrue.s - beq.s - bge.s - bgt.s - ble.s - blt.s - bne.un.s - bge.un.s - bgt.un.s - ble.un.s - blt.un.s - br - brfalse - brtrue - beq - bge - bgt - ble - blt - bne.un - bge.un - bgt.un - ble.un - blt.un - switch - ldind.i1 - ldind.u1 - ldind.i2 - ldind.u2 - ldind.i4 - ldind.u4 - ldind.i8 - ldind.i - ldind.r4 - ldind.r8 - ldind.ref - stind.ref - stind.i1 - stind.i2 - stind.i4 - stind.i8 - stind.r4 - stind.r8 - add - sub - mul - div - div.un - rem - rem.un - and - or - xor - shl - shr - shr.un - neg - not - conv.i1 - conv.i2 - conv.i4 - conv.i8 - conv.r4 - conv.r8 - conv.u4 - conv.u8 - callvirt - cpobj - ldobj - ldstr - newobj - castclass - isinst - conv.r.un - unbox - throw - ldfld - ldflda - stfld - ldsfld - ldsflda - stsfld - stobj - conv.ovf.i1.un - conv.ovf.i2.un - conv.ovf.i4.un - conv.ovf.i8.un - conv.ovf.u1.un - conv.ovf.u2.un - conv.ovf.u4.un - conv.ovf.u8.un - conv.ovf.i.un - conv.ovf.u.un - box - newarr - ldlen - ldelema - ldelem - ldelem.i1 - ldelem.u1 - ldelem.i2 - ldelem.u2 - ldelem.i4 - ldelem.u4 - ldelem.i8 - ldelem.i - ldelem.r4 - ldelem.r8 - ldelem.ref - stelem - stelem.i - stelem.i1 - stelem.i2 - stelem.i4 - stelem.i8 - stelem.r4 - stelem.r8 - stelem.ref - conv.ovf.i1 - conv.ovf.u1 - conv.ovf.i2 - conv.ovf.u2 - conv.ovf.i4 - conv.ovf.u4 - conv.ovf.i8 - conv.ovf.u8 - refanyval - ckfinite - mkrefany - ldtoken - conv.u2 - conv.u1 - conv.i - conv.ovf.i - conv.ovf.u - add.ovf - add.ovf.un - mul.ovf - mul.ovf.un - sub.ovf - sub.ovf.un - endfinally - leave - leave.s - stind.i - conv.u - prefix7 - prefix6 - prefix5 - prefix4 - prefix3 - prefix2 - prefix1 - prefixref - arglist - ceq - cgt - cgt.un - clt - clt.un - ldftn - ldvirtftn - ldarg - ldarga - starg - ldloc - ldloca - stloc - localloc - endfilter - unaligned. - volatile. - tail. - initobj - cpblk - initblk - rethrow - sizeof - refanytype - illegal - endmac - brnull - brnull.s - brzero - brzero.s - brinst - brinst.s - ldind.u8 - ldelem.u8 - ldc.i4.M1 - endfault - - - void - bool - char - wchar - int - int8 - int16 - int32 - int64 - uint8 - uint16 - uint32 - uint64 - float - float32 - float64 - refany - typedref - object - string - native - unsigned - value - valuetype - class - const - vararg - default - stdcall - thiscall - fastcall - unmanaged - not_in_gc_heap - beforefieldinit - instance - filter - catch - static - public - private - synchronized - interface - extends - implements - handler - finally - fault - to - abstract - auto - sequential - explicit - wrapper - ansi - unicode - autochar - import - enum - virtual - notremotable - special - il - cil - optil - managed - preservesig - runtime - method - field - bytearray - final - sealed - specialname - family - assembly - famandassem - famorassem - privatescope - nested - hidebysig - newslot - rtspecialname - pinvokeimpl - unmanagedexp - reqsecobj - .ctor - .cctor - initonly - literal - notserialized - forwardref - internalcall - noinlining - aggressiveinlining - nomangle - lasterr - winapi - cdecl - stdcall - thiscall - fastcall - as - pinned - modreq - modopt - serializable - at - tls - true - false - strict - - - .class - .namespace - .method - .field - .emitbyte - .try - .maxstack - .locals - .entrypoint - .zeroinit - .pdirect - .data - .event - .addon - .removeon - .fire - .other - protected - .property - .set - .get - default - .import - .permission - .permissionset - .line - .language - #line - - - request - demand - assert - deny - permitonly - linkcheck - inheritcheck - reqmin - reqopt - reqrefuse - prejitgrant - prejitdeny - noncasdemand - noncaslinkdemand - noncasinheritance - - - - .custom - - init - - .size - .pack - - .file - nometadata - .hash - .assembly - implicitcom - noappdomain - noprocess - nomachine - .publickey - .publickeytoken - algorithm - .ver - .locale - extern - .export - .manifestres - .mresource - .localized - - - .module - marshal - custom - sysstring - fixed - variant - currency - syschar - decimal - date - bstr - tbstr - lpstr - lpwstr - lptstr - objectref - iunknown - idispatch - struct - safearray - byvalstr - lpvoid - any - array - lpstruct - - - .vtfixup - fromunmanaged - callmostderived - .vtentry - - - in - out - opt - lcid - retval - .param - - - .override - with - - - null - error - hresult - carray - userdefined - record - filetime - blob - stream - storage - streamed_object - stored_object - blob_object - cf - clsid - vector - - - nullref - - - .subsystem - .corflags - .stackreserve - alignment - .imagebase - - - // - - - /\* - \*/ - - - " - " - - - - - TODO - FIXME - - - HACK - UNDONE - - - \ No newline at end of file diff --git a/ILSpy/TextView/XML-Mode-Dark.xshd b/ILSpy/TextView/XML-Mode-Dark.xshd deleted file mode 100644 index cea0680a7..000000000 --- a/ILSpy/TextView/XML-Mode-Dark.xshd +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - <!-- - --> - - - <!\[CDATA\[ - ]]> - - - <!DOCTYPE - > - - - <\? - \?> - - - < - > - - - - " - "|(?=<) - - - ' - '|(?=<) - - [\d\w_\-\.]+(?=(\s*=)) - = - - - - - - - - & - [\w\d\#]+ - ; - - - - & - [\w\d\#]* - #missing ; - - - \ No newline at end of file diff --git a/ILSpy/Themes/DarkTheme.xaml b/ILSpy/Themes/DarkTheme.xaml index 21308eb83..ee4af9991 100644 --- a/ILSpy/Themes/DarkTheme.xaml +++ b/ILSpy/Themes/DarkTheme.xaml @@ -65,4 +65,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Themes/SyntaxColor.cs b/ILSpy/Themes/SyntaxColor.cs new file mode 100644 index 000000000..2a7ddd3d0 --- /dev/null +++ b/ILSpy/Themes/SyntaxColor.cs @@ -0,0 +1,31 @@ +#nullable enable + +using System.Windows; +using System.Windows.Media; + +using ICSharpCode.AvalonEdit.Highlighting; + +namespace ICSharpCode.ILSpy.Themes; + +public class SyntaxColor +{ + public Color? Foreground { get; set; } + public Color? Background { get; set; } + public FontWeight? FontWeight { get; set; } + public FontStyle? FontStyle { get; set; } + + public void ApplyTo(HighlightingColor color) + { + if (Foreground is { } foreground) + color.Foreground = new SimpleHighlightingBrush(foreground); + + if (Background is { } background) + color.Background = new SimpleHighlightingBrush(background); + + if (FontWeight is { } fontWeight) + color.FontWeight = fontWeight; + + if (FontStyle is { } fontStyle) + color.FontStyle = fontStyle; + } +} diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index cc7368754..52d9673bf 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -16,20 +16,25 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; +using System.Collections; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; +using ICSharpCode.AvalonEdit.Highlighting; + namespace ICSharpCode.ILSpy.Themes { public class ThemeManager { - private static List _allThemes; - private string _theme; - private readonly ResourceDictionary _themeDictionaryContainer = new ResourceDictionary(); + private string? _theme; + private readonly ResourceDictionary _themeDictionaryContainer = new(); + private readonly Dictionary _syntaxColors = new(); - public static readonly ThemeManager Current = new ThemeManager(); + public static readonly ThemeManager Current = new(); private ThemeManager() { @@ -40,12 +45,10 @@ namespace ICSharpCode.ILSpy.Themes public static IReadOnlyCollection AllThemes => new[] { "Light", "Dark" }; public string Theme { - get => _theme; + get => _theme ?? DefaultTheme; set { _theme = value; - - _themeDictionaryContainer.MergedDictionaries.Clear(); - _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{value}Theme.xaml", UriKind.Relative) }); + UpdateTheme(); } } @@ -65,5 +68,38 @@ namespace ICSharpCode.ILSpy.Themes { return new Style(typeof(Button), (Style)Application.Current.FindResource(ToolBar.ButtonStyleKey)); } + + public void UpdateColors(IHighlightingDefinition highlightingDefinition) + { + var prefix = $"SyntaxColor.{highlightingDefinition.Name}."; + + foreach (var (key, syntaxColor) in _syntaxColors) + { + var color = highlightingDefinition.GetNamedColor(key.Substring(prefix.Length)); + if (color is not null) + syntaxColor.ApplyTo(color); + } + } + + private void UpdateTheme() + { + _themeDictionaryContainer.MergedDictionaries.Clear(); + _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{Theme}Theme.xaml", UriKind.Relative) }); + + _syntaxColors.Clear(); + ProcessDictionary(_themeDictionaryContainer); + + void ProcessDictionary(ResourceDictionary resourceDictionary) + { + foreach (DictionaryEntry entry in resourceDictionary) + { + if (entry is { Key: string key, Value: SyntaxColor syntaxColor }) + _syntaxColors.TryAdd(key, syntaxColor); + } + + foreach (ResourceDictionary mergedDictionary in resourceDictionary.MergedDictionaries) + ProcessDictionary(mergedDictionary); + } + } } } From f5e2caa20f59b16bfda9aacc8c9c53e59548d600 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Tue, 21 Feb 2023 23:21:57 +0100 Subject: [PATCH 109/230] Add the VS Light+ theme from VS Code --- ILSpy/Themes/SyntaxColor.cs | 15 ++---- ILSpy/Themes/ThemeManager.cs | 13 +++-- ILSpy/Themes/VSLightTheme.xaml | 87 ++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 ILSpy/Themes/VSLightTheme.xaml diff --git a/ILSpy/Themes/SyntaxColor.cs b/ILSpy/Themes/SyntaxColor.cs index 2a7ddd3d0..1f44eb56c 100644 --- a/ILSpy/Themes/SyntaxColor.cs +++ b/ILSpy/Themes/SyntaxColor.cs @@ -16,16 +16,9 @@ public class SyntaxColor public void ApplyTo(HighlightingColor color) { - if (Foreground is { } foreground) - color.Foreground = new SimpleHighlightingBrush(foreground); - - if (Background is { } background) - color.Background = new SimpleHighlightingBrush(background); - - if (FontWeight is { } fontWeight) - color.FontWeight = fontWeight; - - if (FontStyle is { } fontStyle) - color.FontStyle = fontStyle; + color.Foreground = Foreground is { } foreground ? new SimpleHighlightingBrush(foreground) : null; + color.Background = Background is { } background ? new SimpleHighlightingBrush(background) : null; + color.FontWeight = FontWeight ?? FontWeights.Normal; + color.FontStyle = FontStyle ?? FontStyles.Normal; } } diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index 52d9673bf..a90a143dc 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -42,10 +42,15 @@ namespace ICSharpCode.ILSpy.Themes } public string DefaultTheme => "Light"; - public static IReadOnlyCollection AllThemes => new[] { "Light", "Dark" }; - public string Theme { - get => _theme ?? DefaultTheme; + public static IReadOnlyCollection AllThemes => new[] { + "Dark", + "Light", + "VS Light" + }; + + public string? Theme { + get => _theme; set { _theme = value; UpdateTheme(); @@ -84,7 +89,7 @@ namespace ICSharpCode.ILSpy.Themes private void UpdateTheme() { _themeDictionaryContainer.MergedDictionaries.Clear(); - _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{Theme}Theme.xaml", UriKind.Relative) }); + _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/{(_theme ?? DefaultTheme).Replace(" ", "")}Theme.xaml", UriKind.Relative) }); _syntaxColors.Clear(); ProcessDictionary(_themeDictionaryContainer); diff --git a/ILSpy/Themes/VSLightTheme.xaml b/ILSpy/Themes/VSLightTheme.xaml new file mode 100644 index 000000000..7ee71f111 --- /dev/null +++ b/ILSpy/Themes/VSLightTheme.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2599c6b00e7ec457fbbd65e74ac67d9b4a2c249b Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Wed, 22 Feb 2023 19:46:01 +0100 Subject: [PATCH 110/230] Rename theme files --- .../{DarkTheme.xaml => Theme.Dark.xaml} | 0 .../{LightTheme.xaml => Theme.Light.xaml} | 0 ...tTheme.xaml => Theme.VSCodeLightPlus.xaml} | 2 +- ILSpy/Themes/ThemeManager.cs | 21 ++++++++++++------- 4 files changed, 15 insertions(+), 8 deletions(-) rename ILSpy/Themes/{DarkTheme.xaml => Theme.Dark.xaml} (100%) rename ILSpy/Themes/{LightTheme.xaml => Theme.Light.xaml} (100%) rename ILSpy/Themes/{VSLightTheme.xaml => Theme.VSCodeLightPlus.xaml} (98%) diff --git a/ILSpy/Themes/DarkTheme.xaml b/ILSpy/Themes/Theme.Dark.xaml similarity index 100% rename from ILSpy/Themes/DarkTheme.xaml rename to ILSpy/Themes/Theme.Dark.xaml diff --git a/ILSpy/Themes/LightTheme.xaml b/ILSpy/Themes/Theme.Light.xaml similarity index 100% rename from ILSpy/Themes/LightTheme.xaml rename to ILSpy/Themes/Theme.Light.xaml diff --git a/ILSpy/Themes/VSLightTheme.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml similarity index 98% rename from ILSpy/Themes/VSLightTheme.xaml rename to ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 7ee71f111..1e4050862 100644 --- a/ILSpy/Themes/VSLightTheme.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index 1055b6c40..8cfbaa5ce 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -45,9 +45,10 @@ namespace ICSharpCode.ILSpy.Themes public string DefaultTheme => "Light"; public static IReadOnlyCollection AllThemes => new[] { - "Dark", "Light", - "VS Code Light+" + "Dark", + "VS Code Light+", + "VS Code Dark+" }; public string? Theme { @@ -92,7 +93,6 @@ namespace ICSharpCode.ILSpy.Themes var themeFileName = _theme .Replace("+", "Plus") - .Replace("#", "Sharp") .Replace(" ", ""); _themeDictionaryContainer.MergedDictionaries.Clear(); From eaf8e7bc49536678b11dea97b0d7338e02886b75 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Wed, 22 Feb 2023 20:39:14 +0100 Subject: [PATCH 112/230] Add R# Light theme --- ILSpy/Themes/Theme.RSharpLight.xaml | 82 +++++++++++++++++++++++++++++ ILSpy/Themes/ThemeManager.cs | 4 +- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 ILSpy/Themes/Theme.RSharpLight.xaml diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml new file mode 100644 index 000000000..ab69cef7b --- /dev/null +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index 8cfbaa5ce..a115fae9f 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -48,7 +48,8 @@ namespace ICSharpCode.ILSpy.Themes "Light", "Dark", "VS Code Light+", - "VS Code Dark+" + "VS Code Dark+", + "R# Light" }; public string? Theme { @@ -93,6 +94,7 @@ namespace ICSharpCode.ILSpy.Themes var themeFileName = _theme .Replace("+", "Plus") + .Replace("#", "Sharp") .Replace(" ", ""); _themeDictionaryContainer.MergedDictionaries.Clear(); From 1f0dc2ee4f22e9e095c7393c2743681452449d4a Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Wed, 22 Feb 2023 20:55:30 +0100 Subject: [PATCH 113/230] Add R# Dark theme --- ILSpy/Themes/Theme.RSharpDark.xaml | 82 ++++++++++++++++++++++++++++++ ILSpy/Themes/ThemeManager.cs | 3 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 ILSpy/Themes/Theme.RSharpDark.xaml diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml new file mode 100644 index 000000000..5699e0f52 --- /dev/null +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index a115fae9f..d5f9daef5 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -49,7 +49,8 @@ namespace ICSharpCode.ILSpy.Themes "Dark", "VS Code Light+", "VS Code Dark+", - "R# Light" + "R# Light", + "R# Dark" }; public string? Theme { From 0253b1ab68b5b67e7f6c40119d5386eaa57053f7 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Wed, 22 Feb 2023 21:48:41 +0100 Subject: [PATCH 114/230] Add colors for line prefixes in IL/Asm and text markers --- ILSpy/TextView/Asm-Mode.xshd | 6 +++++- ILSpy/TextView/ILAsm-Mode.xshd | 6 +++++- ILSpy/Themes/Theme.Dark.xaml | 2 +- ILSpy/Themes/Theme.RSharpDark.xaml | 5 +++++ ILSpy/Themes/Theme.RSharpLight.xaml | 5 +++++ ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 5 +++++ ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 5 +++++ 7 files changed, 31 insertions(+), 3 deletions(-) diff --git a/ILSpy/TextView/Asm-Mode.xshd b/ILSpy/TextView/Asm-Mode.xshd index 9bf688b83..8d4fb8c2f 100644 --- a/ILSpy/TextView/Asm-Mode.xshd +++ b/ILSpy/TextView/Asm-Mode.xshd @@ -8,7 +8,8 @@ - + + aaa @@ -1189,6 +1190,9 @@ ; + + ^ \s* [0-9A-F]+ \s+ [0-9A-F]+ + \b(0[xXhH])?[0-9a-fA-F_`]+[h]? # hex number | diff --git a/ILSpy/TextView/ILAsm-Mode.xshd b/ILSpy/TextView/ILAsm-Mode.xshd index f24c9f6c8..86b4c3f37 100644 --- a/ILSpy/TextView/ILAsm-Mode.xshd +++ b/ILSpy/TextView/ILAsm-Mode.xshd @@ -5,7 +5,8 @@ - + + nop @@ -516,6 +517,9 @@ " " + + ^ \s* \w+ : + diff --git a/ILSpy/Themes/Theme.Dark.xaml b/ILSpy/Themes/Theme.Dark.xaml index ee4af9991..0abb57e31 100644 --- a/ILSpy/Themes/Theme.Dark.xaml +++ b/ILSpy/Themes/Theme.Dark.xaml @@ -80,7 +80,7 @@ - + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index 5699e0f52..a420235c7 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -10,6 +10,9 @@ + #623916 + #483D8B + @@ -17,6 +20,7 @@ + @@ -28,6 +32,7 @@ + diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index ab69cef7b..f749e6f9a 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -10,6 +10,9 @@ + #F6B94D + #87CEFA + @@ -17,6 +20,7 @@ + @@ -28,6 +32,7 @@ + diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index 825626aff..e4cd880ac 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -15,6 +15,9 @@ + #264F78 + #343A40 + @@ -22,6 +25,7 @@ + @@ -33,6 +37,7 @@ + diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 1e4050862..e527cde58 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -15,6 +15,9 @@ + #ADD6FF + #D6EAFF + @@ -22,6 +25,7 @@ + @@ -33,6 +37,7 @@ + From 39cb275456695f8d7b62f84a8f64085e0fb89d34 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Thu, 23 Feb 2023 22:03:28 +0100 Subject: [PATCH 115/230] Add semantic highlighting for properties and events Also add a fallback mechanism for colors: if a color definition is empty, another one can be used instead. --- .../Output/TextTokenWriter.cs | 2 +- .../CSharpHighlightingTokenWriter.cs | 93 ++++++++++++------- ILSpy/TextView/CSharp-Mode.xshd | 9 ++ ILSpy/Themes/Theme.RSharpDark.xaml | 2 + ILSpy/Themes/Theme.RSharpLight.xaml | 2 + ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 2 + ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 2 + 7 files changed, 77 insertions(+), 35 deletions(-) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 0562bcedd..d225feeaa 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -457,7 +457,7 @@ namespace ICSharpCode.Decompiler { if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement)) return true; - if (node is VariableInitializer && node.Parent is FieldDeclaration) + if (node is VariableInitializer && node.Parent is FieldDeclaration or EventDeclaration) { node = node.Parent; return true; diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index e4ce9eba6..e2e66a090 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -55,9 +55,12 @@ namespace ICSharpCode.ILSpy HighlightingColor methodCallColor; HighlightingColor methodDeclarationColor; - HighlightingColor fieldDeclarationColor; HighlightingColor fieldAccessColor; + HighlightingColor propertyDeclarationColor; + HighlightingColor propertyAccessColor; + HighlightingColor eventDeclarationColor; + HighlightingColor eventAccessColor; HighlightingColor valueKeywordColor; HighlightingColor thisKeywordColor; @@ -74,39 +77,49 @@ namespace ICSharpCode.ILSpy this.locatable = locatable; this.textOutput = textOutput; - this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility"); - this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords"); - this.structureKeywordsColor = highlighting.GetNamedColor("Keywords"); - this.gotoKeywordsColor = highlighting.GetNamedColor("GotoKeywords"); - this.queryKeywordsColor = highlighting.GetNamedColor("QueryKeywords"); - this.exceptionKeywordsColor = highlighting.GetNamedColor("ExceptionKeywords"); - this.checkedKeywordColor = highlighting.GetNamedColor("CheckedKeyword"); - this.unsafeKeywordsColor = highlighting.GetNamedColor("UnsafeKeywords"); - this.valueTypeKeywordsColor = highlighting.GetNamedColor("ValueTypeKeywords"); - this.referenceTypeKeywordsColor = highlighting.GetNamedColor("ReferenceTypeKeywords"); - this.operatorKeywordsColor = highlighting.GetNamedColor("OperatorKeywords"); - this.parameterModifierColor = highlighting.GetNamedColor("ParameterModifiers"); - this.modifiersColor = highlighting.GetNamedColor("Modifiers"); - this.accessorKeywordsColor = highlighting.GetNamedColor("GetSetAddRemove"); + this.visibilityKeywordsColor = GetColor("Visibility") ?? GetColor("Keywords"); + this.namespaceKeywordsColor = GetColor("NamespaceKeywords") ?? GetColor("Keywords"); + this.structureKeywordsColor = GetColor("Keywords"); + this.gotoKeywordsColor = GetColor("GotoKeywords") ?? GetColor("Keywords"); + this.queryKeywordsColor = GetColor("QueryKeywords") ?? GetColor("Keywords"); + this.exceptionKeywordsColor = GetColor("ExceptionKeywords") ?? GetColor("Keywords"); + this.checkedKeywordColor = GetColor("CheckedKeyword") ?? GetColor("Keywords"); + this.unsafeKeywordsColor = GetColor("UnsafeKeywords") ?? GetColor("Keywords"); + this.valueTypeKeywordsColor = GetColor("ValueTypeKeywords") ?? GetColor("Keywords"); + this.referenceTypeKeywordsColor = GetColor("ReferenceTypeKeywords") ?? GetColor("Keywords"); + this.operatorKeywordsColor = GetColor("OperatorKeywords") ?? GetColor("Keywords"); + this.parameterModifierColor = GetColor("ParameterModifiers") ?? GetColor("Keywords"); + this.modifiersColor = GetColor("Modifiers") ?? GetColor("Keywords"); + this.accessorKeywordsColor = GetColor("GetSetAddRemove") ?? GetColor("Keywords"); - this.referenceTypeColor = highlighting.GetNamedColor("ReferenceTypes"); - this.valueTypeColor = highlighting.GetNamedColor("ValueTypes"); - this.interfaceTypeColor = highlighting.GetNamedColor("InterfaceTypes"); - this.enumerationTypeColor = highlighting.GetNamedColor("EnumTypes"); - this.typeParameterTypeColor = highlighting.GetNamedColor("TypeParameters"); - this.delegateTypeColor = highlighting.GetNamedColor("DelegateTypes"); - this.methodDeclarationColor = this.methodCallColor = highlighting.GetNamedColor("MethodCall"); - //this.eventDeclarationColor = this.eventAccessColor = defaultTextColor; - //this.propertyDeclarationColor = this.propertyAccessColor = defaultTextColor; - this.fieldDeclarationColor = this.fieldAccessColor = highlighting.GetNamedColor("FieldAccess"); + this.referenceTypeColor = GetColor("ReferenceTypes") ?? GetColor("Types"); + this.valueTypeColor = GetColor("ValueTypes") ?? GetColor("Types"); + this.interfaceTypeColor = GetColor("InterfaceTypes") ?? GetColor("Types"); + this.enumerationTypeColor = GetColor("EnumTypes") ?? GetColor("Types"); + this.typeParameterTypeColor = GetColor("TypeParameters") ?? GetColor("Types"); + this.delegateTypeColor = GetColor("DelegateTypes") ?? GetColor("Types"); + this.methodDeclarationColor = GetColor("MethodDeclaration") ?? GetColor("MethodCall"); + this.methodCallColor = GetColor("MethodCall") ?? GetColor("MethodDeclaration"); + this.fieldDeclarationColor = GetColor("FieldDeclaration") ?? GetColor("FieldAccess"); + this.fieldAccessColor = GetColor("FieldAccess") ?? GetColor("FieldDeclaration"); + this.propertyDeclarationColor = GetColor("PropertyDeclaration") ?? GetColor("PropertyAccess"); + this.propertyAccessColor = GetColor("PropertyAccess") ?? GetColor("PropertyDeclaration"); + this.eventDeclarationColor = GetColor("EventDeclaration") ?? GetColor("EventAccess"); + this.eventAccessColor = GetColor("EventAccess") ?? GetColor("EventDeclaration"); //this.variableDeclarationColor = this.variableAccessColor = defaultTextColor; //this.parameterDeclarationColor = this.parameterAccessColor = defaultTextColor; - this.valueKeywordColor = highlighting.GetNamedColor("NullOrValueKeywords"); - this.thisKeywordColor = highlighting.GetNamedColor("ThisOrBaseReference"); - this.trueKeywordColor = highlighting.GetNamedColor("TrueFalse"); - this.typeKeywordsColor = highlighting.GetNamedColor("TypeKeywords"); - this.attributeKeywordsColor = highlighting.GetNamedColor("AttributeKeywords"); + this.valueKeywordColor = GetColor("NullOrValueKeywords") ?? GetColor("Keywords"); + this.thisKeywordColor = GetColor("ThisOrBaseReference") ?? GetColor("Keywords"); + this.trueKeywordColor = GetColor("TrueFalse") ?? GetColor("Keywords"); + this.typeKeywordsColor = GetColor("TypeKeywords") ?? GetColor("Keywords"); + this.attributeKeywordsColor = GetColor("AttributeKeywords") ?? GetColor("Keywords"); //this.externAliasKeywordColor = ...; + + HighlightingColor GetColor(string colorName) + { + var color = highlighting.GetNamedColor(colorName); + return color is not { Foreground: null, Background: null, FontFamily: null, FontWeight: null, FontSize: null, FontStyle: null, Strikethrough: null, Underline: null } ? color : null; + } } public override void WriteKeyword(Role role, string keyword) @@ -380,12 +393,18 @@ namespace ICSharpCode.ILSpy break; } break; - case IMethod m: + case IMethod: color = methodDeclarationColor; break; - case IField f: + case IField: color = fieldDeclarationColor; break; + case IProperty: + color = propertyDeclarationColor; + break; + case IEvent: + color = eventDeclarationColor; + break; } switch (GetCurrentMemberReference()) { @@ -409,12 +428,18 @@ namespace ICSharpCode.ILSpy break; } break; - case IMethod m: + case IMethod: color = methodCallColor; break; - case IField f: + case IField: color = fieldAccessColor; break; + case IProperty: + color = propertyAccessColor; + break; + case IEvent: + color = eventAccessColor; + break; } if (color != null) { diff --git a/ILSpy/TextView/CSharp-Mode.xshd b/ILSpy/TextView/CSharp-Mode.xshd index 5f5e0a2fd..8c9041d47 100644 --- a/ILSpy/TextView/CSharp-Mode.xshd +++ b/ILSpy/TextView/CSharp-Mode.xshd @@ -33,14 +33,23 @@ + + + + + + + + + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index a420235c7..492fabc7a 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -70,6 +70,8 @@ + + diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index f749e6f9a..6b42b133d 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -70,6 +70,8 @@ + + diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index e4cd880ac..cf7f12f33 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -75,6 +75,8 @@ + + diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index e527cde58..b615d4332 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -75,6 +75,8 @@ + + From 7055182205978bfcfa3c8b2b1745b0960867b552 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Fri, 24 Feb 2023 00:25:53 +0100 Subject: [PATCH 116/230] Add theme color for text search results --- ILSpy/TextView/DecompilerTextView.cs | 1 + ILSpy/TextView/DecompilerTextView.xaml | 4 ++-- ILSpy/Themes/ResourceKeys.cs | 16 ++++++++-------- ILSpy/Themes/Theme.Dark.xaml | 5 +++-- ILSpy/Themes/Theme.Light.xaml | 5 +++-- ILSpy/Themes/Theme.RSharpDark.xaml | 9 +++++---- ILSpy/Themes/Theme.RSharpLight.xaml | 9 +++++---- ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 5 +++-- ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 5 +++-- 9 files changed, 33 insertions(+), 26 deletions(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 6bdee75ba..c97798477 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -127,6 +127,7 @@ namespace ICSharpCode.ILSpy.TextView // SearchPanel SearchPanel searchPanel = SearchPanel.Install(textEditor.TextArea); searchPanel.RegisterCommands(Application.Current.MainWindow.CommandBindings); + searchPanel.SetResourceReference(SearchPanel.MarkerBrushProperty, ResourceKeys.SearchResultBackgroundBrush); searchPanel.Loaded += (_, _) => { // HACK: fix the hardcoded but misaligned margin of the search text box. var textBox = searchPanel.VisualDescendants().OfType().FirstOrDefault(); diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index 36f87da8c..4776d1397 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -16,8 +16,8 @@ - - + + + #333337 #464646 diff --git a/ILSpy/Themes/Theme.Light.xaml b/ILSpy/Themes/Theme.Light.xaml index 1478cde4b..d5e54485a 100644 --- a/ILSpy/Themes/Theme.Light.xaml +++ b/ILSpy/Themes/Theme.Light.xaml @@ -6,8 +6,9 @@ - - + + + #FCFCFC #D8D8E0 diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index 492fabc7a..987844d01 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -7,11 +7,12 @@ - - + + + #995A23 - #623916 - #483D8B + #483D8B + #800000 diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index 6b42b133d..eb6d10991 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -7,11 +7,12 @@ - - + + + #F6B94D - #F6B94D - #87CEFA + #87CEFA + #FFB6C1 diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index cf7f12f33..7be7eb902 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -12,8 +12,9 @@ https://github.com/microsoft/vscode/blob/main/extensions/theme-defaults/themes/dark_plus.json --> - - + + + #264F78 #343A40 diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index b615d4332..08d5e4802 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -12,8 +12,9 @@ https://github.com/microsoft/vscode/blob/main/extensions/theme-defaults/themes/light_plus.json --> - - + + + #ADD6FF #D6EAFF From 628a804864aa8528aa3cfc856fc1b40f7913ac48 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 24 Feb 2023 22:12:49 +0100 Subject: [PATCH 117/230] Add support for record structs in `CSharpAmbience` (fixes #2910) --- ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 839fdbb33..2e04d0575 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -83,6 +83,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor case ClassType.RecordClass: writer.WriteKeyword(Roles.RecordKeyword, "record"); break; + case ClassType.RecordStruct: + writer.WriteKeyword(Roles.RecordKeyword, "record"); + writer.WriteKeyword(Roles.StructKeyword, "struct"); + break; default: throw new Exception("Invalid value for ClassType"); } From feb736a0d54d254e9d2aad3606c5bac7fcdf7a80 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 25 Feb 2023 11:56:07 +0100 Subject: [PATCH 118/230] Fix empty parameter names in delegate declarations (fixes #2908) --- ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 13d9c067b..bdf2e36ca 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1288,6 +1288,11 @@ namespace ICSharpCode.Decompiler.CSharp { typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var entityDecl = typeSystemAstBuilder.ConvertEntity(typeDef); + if (entityDecl is DelegateDeclaration delegateDeclaration) + { + // Fix empty parameter names in delegate declarations + FixParameterNames(delegateDeclaration); + } var typeDecl = entityDecl as TypeDeclaration; if (typeDecl == null) { From caeb880a35de0abbe7a85f4d9dcd462a2e0e64e1 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Sat, 25 Feb 2023 15:22:58 +0100 Subject: [PATCH 119/230] Cleanup, make everything explicit Colors won't be inherited from the xshd files in order to avoid mistakes. --- .../CSharpHighlightingTokenWriter.cs | 72 +++++----- ILSpy/TextView/CSharp-Mode.xshd | 1 - ILSpy/TextView/DecompilerTextView.cs | 6 +- ILSpy/Themes/Base.Dark.xaml | 69 ++++++++++ ILSpy/Themes/Base.Light.xaml | 61 +++++++++ ILSpy/Themes/SyntaxColor.cs | 8 ++ ILSpy/Themes/Theme.Dark.xaml | 69 ++-------- ILSpy/Themes/Theme.Light.xaml | 123 +++++++++++------- ILSpy/Themes/Theme.RSharpDark.xaml | 12 +- ILSpy/Themes/Theme.RSharpLight.xaml | 12 +- ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 10 +- ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 10 +- ILSpy/Themes/ThemeManager.cs | 6 +- 13 files changed, 293 insertions(+), 166 deletions(-) create mode 100644 ILSpy/Themes/Base.Dark.xaml create mode 100644 ILSpy/Themes/Base.Light.xaml diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index e2e66a090..2e698460c 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -77,49 +77,43 @@ namespace ICSharpCode.ILSpy this.locatable = locatable; this.textOutput = textOutput; - this.visibilityKeywordsColor = GetColor("Visibility") ?? GetColor("Keywords"); - this.namespaceKeywordsColor = GetColor("NamespaceKeywords") ?? GetColor("Keywords"); - this.structureKeywordsColor = GetColor("Keywords"); - this.gotoKeywordsColor = GetColor("GotoKeywords") ?? GetColor("Keywords"); - this.queryKeywordsColor = GetColor("QueryKeywords") ?? GetColor("Keywords"); - this.exceptionKeywordsColor = GetColor("ExceptionKeywords") ?? GetColor("Keywords"); - this.checkedKeywordColor = GetColor("CheckedKeyword") ?? GetColor("Keywords"); - this.unsafeKeywordsColor = GetColor("UnsafeKeywords") ?? GetColor("Keywords"); - this.valueTypeKeywordsColor = GetColor("ValueTypeKeywords") ?? GetColor("Keywords"); - this.referenceTypeKeywordsColor = GetColor("ReferenceTypeKeywords") ?? GetColor("Keywords"); - this.operatorKeywordsColor = GetColor("OperatorKeywords") ?? GetColor("Keywords"); - this.parameterModifierColor = GetColor("ParameterModifiers") ?? GetColor("Keywords"); - this.modifiersColor = GetColor("Modifiers") ?? GetColor("Keywords"); - this.accessorKeywordsColor = GetColor("GetSetAddRemove") ?? GetColor("Keywords"); + this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility"); + this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords"); + this.structureKeywordsColor = highlighting.GetNamedColor("Keywords"); + this.gotoKeywordsColor = highlighting.GetNamedColor("GotoKeywords"); + this.queryKeywordsColor = highlighting.GetNamedColor("QueryKeywords"); + this.exceptionKeywordsColor = highlighting.GetNamedColor("ExceptionKeywords"); + this.checkedKeywordColor = highlighting.GetNamedColor("CheckedKeyword"); + this.unsafeKeywordsColor = highlighting.GetNamedColor("UnsafeKeywords"); + this.valueTypeKeywordsColor = highlighting.GetNamedColor("ValueTypeKeywords"); + this.referenceTypeKeywordsColor = highlighting.GetNamedColor("ReferenceTypeKeywords"); + this.operatorKeywordsColor = highlighting.GetNamedColor("OperatorKeywords"); + this.parameterModifierColor = highlighting.GetNamedColor("ParameterModifiers"); + this.modifiersColor = highlighting.GetNamedColor("Modifiers"); + this.accessorKeywordsColor = highlighting.GetNamedColor("GetSetAddRemove"); - this.referenceTypeColor = GetColor("ReferenceTypes") ?? GetColor("Types"); - this.valueTypeColor = GetColor("ValueTypes") ?? GetColor("Types"); - this.interfaceTypeColor = GetColor("InterfaceTypes") ?? GetColor("Types"); - this.enumerationTypeColor = GetColor("EnumTypes") ?? GetColor("Types"); - this.typeParameterTypeColor = GetColor("TypeParameters") ?? GetColor("Types"); - this.delegateTypeColor = GetColor("DelegateTypes") ?? GetColor("Types"); - this.methodDeclarationColor = GetColor("MethodDeclaration") ?? GetColor("MethodCall"); - this.methodCallColor = GetColor("MethodCall") ?? GetColor("MethodDeclaration"); - this.fieldDeclarationColor = GetColor("FieldDeclaration") ?? GetColor("FieldAccess"); - this.fieldAccessColor = GetColor("FieldAccess") ?? GetColor("FieldDeclaration"); - this.propertyDeclarationColor = GetColor("PropertyDeclaration") ?? GetColor("PropertyAccess"); - this.propertyAccessColor = GetColor("PropertyAccess") ?? GetColor("PropertyDeclaration"); - this.eventDeclarationColor = GetColor("EventDeclaration") ?? GetColor("EventAccess"); - this.eventAccessColor = GetColor("EventAccess") ?? GetColor("EventDeclaration"); + this.referenceTypeColor = highlighting.GetNamedColor("ReferenceTypes"); + this.valueTypeColor = highlighting.GetNamedColor("ValueTypes"); + this.interfaceTypeColor = highlighting.GetNamedColor("InterfaceTypes"); + this.enumerationTypeColor = highlighting.GetNamedColor("EnumTypes"); + this.typeParameterTypeColor = highlighting.GetNamedColor("TypeParameters"); + this.delegateTypeColor = highlighting.GetNamedColor("DelegateTypes"); + this.methodDeclarationColor = highlighting.GetNamedColor("MethodDeclaration"); + this.methodCallColor = highlighting.GetNamedColor("MethodCall"); + this.fieldDeclarationColor = highlighting.GetNamedColor("FieldDeclaration"); + this.fieldAccessColor = highlighting.GetNamedColor("FieldAccess"); + this.propertyDeclarationColor = highlighting.GetNamedColor("PropertyDeclaration"); + this.propertyAccessColor = highlighting.GetNamedColor("PropertyAccess"); + this.eventDeclarationColor = highlighting.GetNamedColor("EventDeclaration"); + this.eventAccessColor = highlighting.GetNamedColor("EventAccess"); //this.variableDeclarationColor = this.variableAccessColor = defaultTextColor; //this.parameterDeclarationColor = this.parameterAccessColor = defaultTextColor; - this.valueKeywordColor = GetColor("NullOrValueKeywords") ?? GetColor("Keywords"); - this.thisKeywordColor = GetColor("ThisOrBaseReference") ?? GetColor("Keywords"); - this.trueKeywordColor = GetColor("TrueFalse") ?? GetColor("Keywords"); - this.typeKeywordsColor = GetColor("TypeKeywords") ?? GetColor("Keywords"); - this.attributeKeywordsColor = GetColor("AttributeKeywords") ?? GetColor("Keywords"); + this.valueKeywordColor = highlighting.GetNamedColor("NullOrValueKeywords"); + this.thisKeywordColor = highlighting.GetNamedColor("ThisOrBaseReference"); + this.trueKeywordColor = highlighting.GetNamedColor("TrueFalse"); + this.typeKeywordsColor = highlighting.GetNamedColor("TypeKeywords"); + this.attributeKeywordsColor = highlighting.GetNamedColor("AttributeKeywords"); //this.externAliasKeywordColor = ...; - - HighlightingColor GetColor(string colorName) - { - var color = highlighting.GetNamedColor(colorName); - return color is not { Foreground: null, Background: null, FontFamily: null, FontWeight: null, FontSize: null, FontStyle: null, Strikethrough: null, Underline: null } ? color : null; - } } public override void WriteKeyword(Role role, string keyword) diff --git a/ILSpy/TextView/CSharp-Mode.xshd b/ILSpy/TextView/CSharp-Mode.xshd index 8c9041d47..8bed02109 100644 --- a/ILSpy/TextView/CSharp-Mode.xshd +++ b/ILSpy/TextView/CSharp-Mode.xshd @@ -33,7 +33,6 @@ - diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index c97798477..b7e5f3211 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -140,10 +140,6 @@ namespace ICSharpCode.ILSpy.TextView ShowLineMargin(); SetHighlightCurrentLine(); - // add marker service & margin - textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); - textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); - ContextMenuProvider.Add(this); textEditor.TextArea.TextView.SetResourceReference(ICSharpCode.AvalonEdit.Rendering.TextView.LinkTextForegroundBrushProperty, ResourceKeys.LinkTextForegroundBrush); @@ -1377,7 +1373,7 @@ namespace ICSharpCode.ILSpy.TextView using (XmlTextReader reader = new XmlTextReader(resourceStream)) { var highlightingDefinition = HighlightingLoader.Load(reader, manager); - ThemeManager.Current.UpdateColors(highlightingDefinition); + ThemeManager.Current.ApplyHighlightingColors(highlightingDefinition); return highlightingDefinition; } }); diff --git a/ILSpy/Themes/Base.Dark.xaml b/ILSpy/Themes/Base.Dark.xaml new file mode 100644 index 000000000..4005dec0a --- /dev/null +++ b/ILSpy/Themes/Base.Dark.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + #333337 + #464646 + #252526 + #686868 + #9E9E9E + #F1F1F1 + #999999 + #3399FF + #FFFFFF + #F1F1F1 + #333337 + #1B1B1C + #1B1B1C + #F1F1F1 + #333337 + #F1F1F1 + #2D2D30 + #007ACC + #F1F1F1 + #2D2D30 + #434346 + #808080 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MediumVioletRed + CornflowerBlue + + + + diff --git a/ILSpy/Themes/Base.Light.xaml b/ILSpy/Themes/Base.Light.xaml new file mode 100644 index 000000000..8e7e5076b --- /dev/null +++ b/ILSpy/Themes/Base.Light.xaml @@ -0,0 +1,61 @@ + + + + + + + + LightGreen + + #FCFCFC + #D8D8E0 + #F5F5F5 + #C2C3C9 + #686868 + #1E1E1E + #717171 + #3399FF + #FFFFFF + #F6F6F6 + #F6F6F6 + #1E1E1E + #FFFFFF + #1E1E1E + #EEEEF2 + #71BDE2 + #1E1E1E + #EEEEF2 + #CCCEDB + #808080 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/Themes/SyntaxColor.cs b/ILSpy/Themes/SyntaxColor.cs index 1f44eb56c..cf9a29b00 100644 --- a/ILSpy/Themes/SyntaxColor.cs +++ b/ILSpy/Themes/SyntaxColor.cs @@ -21,4 +21,12 @@ public class SyntaxColor color.FontWeight = FontWeight ?? FontWeights.Normal; color.FontStyle = FontStyle ?? FontStyles.Normal; } + + public static void ResetColor(HighlightingColor color) + { + color.Foreground = null; + color.Background = null; + color.FontWeight = null; + color.FontStyle = null; + } } diff --git a/ILSpy/Themes/Theme.Dark.xaml b/ILSpy/Themes/Theme.Dark.xaml index f5c96f753..ede1f10f8 100644 --- a/ILSpy/Themes/Theme.Dark.xaml +++ b/ILSpy/Themes/Theme.Dark.xaml @@ -1,71 +1,14 @@  - + - #333337 - #464646 - #252526 - #686868 - #9E9E9E - #F1F1F1 - #999999 - #3399FF - #FFFFFF - #F1F1F1 - #333337 - #1B1B1C - #1B1B1C - #F1F1F1 - #333337 - #F1F1F1 - #2D2D30 - #007ACC - #F1F1F1 - #2D2D30 - #434346 - #808080 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MediumVioletRed - CornflowerBlue - - - @@ -73,6 +16,7 @@ + @@ -84,6 +28,7 @@ + @@ -119,8 +64,14 @@ + + + + + + @@ -135,4 +86,4 @@ - \ No newline at end of file + diff --git a/ILSpy/Themes/Theme.Light.xaml b/ILSpy/Themes/Theme.Light.xaml index d5e54485a..c55417bb4 100644 --- a/ILSpy/Themes/Theme.Light.xaml +++ b/ILSpy/Themes/Theme.Light.xaml @@ -1,60 +1,89 @@  - + - #FCFCFC - #D8D8E0 - #F5F5F5 - #C2C3C9 - #686868 - #1E1E1E - #717171 - #3399FF - #FFFFFF - #F6F6F6 - #F6F6F6 - #1E1E1E - #FFFFFF - #1E1E1E - #EEEEF2 - #71BDE2 - #1E1E1E - #EEEEF2 - #CCCEDB - #808080 + + + + + + + + - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index 987844d01..f783da16d 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -2,10 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"> - + - + @@ -41,7 +41,7 @@ - + @@ -69,9 +69,13 @@ + + + + @@ -87,4 +91,4 @@ - \ No newline at end of file + diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index eb6d10991..02021fb60 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -2,10 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"> - + - + @@ -41,7 +41,7 @@ - + @@ -69,9 +69,13 @@ + + + + @@ -87,4 +91,4 @@ - \ No newline at end of file + diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index 7be7eb902..e7ad85099 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"> - + 4.4.0 0.11.4 - 6.2.0.78 + 6.3.0.90 2.8.5 3.13.3 4.3.1 From ef9aa46cecc0549fa5dbfc460b617372b8d5340f Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Mon, 20 Mar 2023 22:27:37 +0100 Subject: [PATCH 124/230] Add theme menu item --- ILSpy/Commands/ILSpyCommands.cs | 2 ++ ILSpy/Commands/SetThemeCommand.cs | 11 +++++++++++ ILSpy/MainWindow.xaml | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 ILSpy/Commands/SetThemeCommand.cs diff --git a/ILSpy/Commands/ILSpyCommands.cs b/ILSpy/Commands/ILSpyCommands.cs index aab6d6f0b..50ace9379 100644 --- a/ILSpy/Commands/ILSpyCommands.cs +++ b/ILSpy/Commands/ILSpyCommands.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using System.Windows.Input; using ICSharpCode.ILSpy.Analyzers; +using ICSharpCode.ILSpy.Commands; namespace ICSharpCode.ILSpy { @@ -31,5 +32,6 @@ namespace ICSharpCode.ILSpy { public static readonly AnalyzeCommand Analyze = new AnalyzeCommand(); public static readonly ManageAssemblyListsCommand ManageAssemblyListsCommand = new ManageAssemblyListsCommand(); + public static readonly SetThemeCommand SetTheme = new SetThemeCommand(); } } diff --git a/ILSpy/Commands/SetThemeCommand.cs b/ILSpy/Commands/SetThemeCommand.cs new file mode 100644 index 000000000..9083300ab --- /dev/null +++ b/ILSpy/Commands/SetThemeCommand.cs @@ -0,0 +1,11 @@ +namespace ICSharpCode.ILSpy.Commands +{ + public class SetThemeCommand : SimpleCommand + { + public override void Execute(object parameter) + { + if (parameter is string theme) + MainWindow.Instance.SessionSettings.Theme = theme; + } + } +} diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 9335d38a1..585720cfc 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -20,6 +20,7 @@ xmlns:styles="urn:TomsToolbox.Wpf.Styles" xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" + xmlns:toms="urn:TomsToolbox" d:DataContext="{d:DesignInstance local:MainWindowViewModel}" > @@ -138,6 +139,23 @@ + + + + + From df4f18b32cf454d00e511e52fb348395274aea37 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Mon, 20 Mar 2023 23:29:38 +0100 Subject: [PATCH 125/230] Add theme option in settings --- ILSpy/Options/DisplaySettingsPanel.xaml | 5 +++++ ILSpy/Options/DisplaySettingsPanel.xaml.cs | 21 ++++++++++++++++----- ILSpy/Options/DisplaySettingsViewModel.cs | 17 +++++++++++++++++ ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ ILSpy/SessionSettings.cs | 7 ++++++- 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index f9206b829..52f5c3ab1 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -6,12 +6,17 @@ xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" d:DataContext="{d:DesignInstance local:DisplaySettingsViewModel}"> + + diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 5e6201ff1..908ed3501 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -121,6 +121,8 @@ namespace ICSharpCode.ILSpy.Options s.ShowRawOffsetsAndBytesBeforeInstruction = (bool?)e.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false; s.StyleWindowTitleBar = (bool?)e.Attribute("StyleWindowTitleBar") ?? false; + s.Theme = MainWindow.Instance.SessionSettings.Theme; + return s; } @@ -150,13 +152,22 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", s.ShowRawOffsetsAndBytesBeforeInstruction); section.SetAttributeValue("StyleWindowTitleBar", s.StyleWindowTitleBar); - XElement existingElement = root.Element("DisplaySettings"); - if (existingElement != null) - existingElement.ReplaceWith(section); - else - root.Add(section); + MainWindow.Instance.SessionSettings.Theme = s.Theme; + var sessionSettings = MainWindow.Instance.SessionSettings.ToXml(); MainWindow.Instance.CurrentDisplaySettings.CopyValues(s); + + Update(section); + Update(sessionSettings); + + void Update(XElement element) + { + var existingElement = root.Element(element.Name); + if (existingElement != null) + existingElement.ReplaceWith(element); + else + root.Add(element); + } } private void TextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) diff --git a/ILSpy/Options/DisplaySettingsViewModel.cs b/ILSpy/Options/DisplaySettingsViewModel.cs index 084d295b1..15b54ebe5 100644 --- a/ILSpy/Options/DisplaySettingsViewModel.cs +++ b/ILSpy/Options/DisplaySettingsViewModel.cs @@ -20,6 +20,8 @@ using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Media; +using ICSharpCode.ILSpy.Themes; + namespace ICSharpCode.ILSpy.Options { /// @@ -29,6 +31,7 @@ namespace ICSharpCode.ILSpy.Options { public DisplaySettingsViewModel() { + this.theme = ThemeManager.Current.DefaultTheme; this.selectedFont = new FontFamily("Consolas"); this.selectedFontSize = 10.0 * 4 / 3; this.sortResults = true; @@ -52,6 +55,19 @@ namespace ICSharpCode.ILSpy.Options } #endregion + string theme; + + public string Theme { + get { return theme; } + set { + if (theme != value) + { + theme = value; + OnPropertyChanged(); + } + } + } + FontFamily selectedFont; public FontFamily SelectedFont { @@ -314,6 +330,7 @@ namespace ICSharpCode.ILSpy.Options public void CopyValues(DisplaySettingsViewModel s) { + this.Theme = s.Theme; this.SelectedFont = s.selectedFont; this.SelectedFontSize = s.selectedFontSize; this.ShowLineNumbers = s.showLineNumbers; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index f9002b35d..94c50345b 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1558,6 +1558,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Theme:. + /// + public static string DisplaySettingsPanel_Theme { + get { + return ResourceManager.GetString("DisplaySettingsPanel_Theme", resourceCulture); + } + } + /// /// Looks up a localized string similar to Download. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index f9e949fa8..82d434c12 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -540,6 +540,9 @@ Are you sure you want to continue? Font: + + Theme: + Download diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs index 245dd99d1..ad53d408c 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -127,7 +127,7 @@ namespace ICSharpCode.ILSpy public DockLayoutSettings DockLayout { get; private set; } - public void Save() + public XElement ToXml() { XElement doc = new XElement("SessionSettings"); doc.Add(this.FilterSettings.SaveAsXml()); @@ -161,7 +161,12 @@ namespace ICSharpCode.ILSpy dockLayoutElement.Add(DockLayout.SaveAsXml()); } doc.Add(dockLayoutElement); + return doc; + } + public void Save() + { + var doc = ToXml(); ILSpySettings.SaveSettings(doc); } From 03b3fc464200c79d031acdb38ccf1ce2b4ce3319 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Mon, 20 Mar 2023 23:30:11 +0100 Subject: [PATCH 126/230] Remove theme combo box --- ILSpy/MainWindow.xaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 585720cfc..0d236371f 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -220,10 +220,6 @@ IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}" ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Workspace.ActiveTabPage.FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> - - From 1be0ccb3eb261e65f24bee74b2923382e04c8cda Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 25 Mar 2023 09:05:44 +0100 Subject: [PATCH 127/230] Update NuGet packages & clean up dependency references a bit --- ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj | 1 - ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj | 2 +- ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj | 5 ----- ILSpy/ILSpy.csproj | 10 +--------- packages.props | 8 ++++---- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj index 23c06798e..27a0ad480 100644 --- a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj +++ b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj @@ -54,7 +54,6 @@ - diff --git a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj index f5f01af1f..384cebd83 100644 --- a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj +++ b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj @@ -70,7 +70,7 @@ - + all diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 55a813f4f..e222ba25a 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -32,11 +32,6 @@ - - - - - diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 74f845893..d33163bad 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -41,19 +41,11 @@ - - - - + - - - - - diff --git a/packages.props b/packages.props index f5cbe1b80..13a55bbc6 100644 --- a/packages.props +++ b/packages.props @@ -19,11 +19,11 @@ 6.3.0.90 2.8.5 3.13.3 - 4.3.1 - 3.0.114 + 4.4.2 + 3.0.124 3.2.0 - 17.4.1 - 4.18.3 + 17.5.0 + 4.18.4 2017.7.26.1241 1.1.0-beta2-22171-02 From c62072c987973dea13c3b9c58006311e8cc0ff1d Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 25 Mar 2023 12:13:39 +0100 Subject: [PATCH 128/230] Update NuGets for R2R plugin --- ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj index 865fc3609..eccbd00e5 100644 --- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/ILSpy/Themes/Theme.Light.xaml b/ILSpy/Themes/Theme.Light.xaml index ca08971e2..8fd711382 100644 --- a/ILSpy/Themes/Theme.Light.xaml +++ b/ILSpy/Themes/Theme.Light.xaml @@ -8,6 +8,8 @@ + + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index 62bfddf03..778a5e385 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -9,7 +9,9 @@ - #995A23 + + + #483D8B #800000 diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index 8eb28d77c..7a0adab3e 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -9,7 +9,9 @@ - #F6B94D + + + #87CEFA #FFB6C1 diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index 7a94d4fab..124dd78e3 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -15,6 +15,8 @@ + + #264F78 #343A40 diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 2ab0d1000..5d3a4ccc0 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -15,6 +15,8 @@ + + #ADD6FF #D6EAFF From 353d63a022f2ddb25739212e0486f9a59f8e7d27 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Thu, 6 Apr 2023 20:41:04 +0200 Subject: [PATCH 144/230] Add theme colors for line numbers --- ILSpy/TextView/DecompilerTextView.xaml | 1 + ILSpy/Themes/Base.Dark.xaml | 1 + ILSpy/Themes/Base.Light.xaml | 1 + ILSpy/Themes/ResourceKeys.cs | 1 + ILSpy/Themes/Theme.Dark.xaml | 1 + ILSpy/Themes/Theme.Light.xaml | 1 + ILSpy/Themes/Theme.RSharpDark.xaml | 1 + ILSpy/Themes/Theme.RSharpLight.xaml | 1 + ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 1 + ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 1 + 10 files changed, 10 insertions(+) diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index ef4da7afe..42786e396 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -34,6 +34,7 @@ + diff --git a/ILSpy/Themes/Base.Light.xaml b/ILSpy/Themes/Base.Light.xaml index 243fb4585..b576c4220 100644 --- a/ILSpy/Themes/Base.Light.xaml +++ b/ILSpy/Themes/Base.Light.xaml @@ -9,6 +9,7 @@ + diff --git a/ILSpy/Themes/ResourceKeys.cs b/ILSpy/Themes/ResourceKeys.cs index 7eae534dd..8dd5d9abe 100644 --- a/ILSpy/Themes/ResourceKeys.cs +++ b/ILSpy/Themes/ResourceKeys.cs @@ -30,6 +30,7 @@ namespace ICSharpCode.ILSpy.Themes public static ResourceKey LinkTextForegroundBrush = new ComponentResourceKey(typeof(ResourceKeys), nameof(LinkTextForegroundBrush)); public static ResourceKey BracketHighlightBackgroundBrush = new ComponentResourceKey(typeof(ResourceKeys), nameof(BracketHighlightBackgroundBrush)); public static ResourceKey BracketHighlightBorderPen = new ComponentResourceKey(typeof(ResourceKeys), nameof(BracketHighlightBorderPen)); + public static ResourceKey LineNumbersForegroundBrush = new ComponentResourceKey(typeof(ResourceKeys), nameof(LineNumbersForegroundBrush)); public static ResourceKey CurrentLineBackgroundBrush = new ComponentResourceKey(typeof(ResourceKeys), nameof(CurrentLineBackgroundBrush)); public static ResourceKey CurrentLineBorderPen = new ComponentResourceKey(typeof(ResourceKeys), nameof(CurrentLineBorderPen)); public static ResourceKey ThemeAwareButtonEffect = new ComponentResourceKey(typeof(ResourceKeys), nameof(ThemeAwareButtonEffect)); diff --git a/ILSpy/Themes/Theme.Dark.xaml b/ILSpy/Themes/Theme.Dark.xaml index f2f0ee2f4..41441900e 100644 --- a/ILSpy/Themes/Theme.Dark.xaml +++ b/ILSpy/Themes/Theme.Dark.xaml @@ -8,6 +8,7 @@ + diff --git a/ILSpy/Themes/Theme.Light.xaml b/ILSpy/Themes/Theme.Light.xaml index 8fd711382..b1818ea23 100644 --- a/ILSpy/Themes/Theme.Light.xaml +++ b/ILSpy/Themes/Theme.Light.xaml @@ -8,6 +8,7 @@ + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index 778a5e385..bcd9672ad 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -10,6 +10,7 @@ + diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index 7a0adab3e..312919f2a 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -10,6 +10,7 @@ + diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index 124dd78e3..ef7aa20b1 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -15,6 +15,7 @@ + diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 5d3a4ccc0..4026ac36c 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -15,6 +15,7 @@ + From f5c49bf42edba14d19a59efaa655e1978a60211b Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Thu, 6 Apr 2023 20:52:58 +0200 Subject: [PATCH 145/230] Add theme colors for matching brackets --- ILSpy/Themes/Base.Dark.xaml | 5 ++--- ILSpy/Themes/Base.Light.xaml | 5 ++--- ILSpy/Themes/Theme.Dark.xaml | 2 ++ ILSpy/Themes/Theme.Light.xaml | 2 ++ ILSpy/Themes/Theme.RSharpDark.xaml | 2 ++ ILSpy/Themes/Theme.RSharpLight.xaml | 2 ++ ILSpy/Themes/Theme.VSCodeDarkPlus.xaml | 2 ++ ILSpy/Themes/Theme.VSCodeLightPlus.xaml | 2 ++ 8 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ILSpy/Themes/Base.Dark.xaml b/ILSpy/Themes/Base.Dark.xaml index 024715220..aa388d990 100644 --- a/ILSpy/Themes/Base.Dark.xaml +++ b/ILSpy/Themes/Base.Dark.xaml @@ -12,6 +12,8 @@ + + #333337 #464646 @@ -36,9 +38,6 @@ #434346 #808080 - - - diff --git a/ILSpy/Themes/Base.Light.xaml b/ILSpy/Themes/Base.Light.xaml index b576c4220..c2138b5a4 100644 --- a/ILSpy/Themes/Base.Light.xaml +++ b/ILSpy/Themes/Base.Light.xaml @@ -12,6 +12,8 @@ + + #FCFCFC #D8D8E0 @@ -34,9 +36,6 @@ #CCCEDB #808080 - - - diff --git a/ILSpy/Themes/Theme.Dark.xaml b/ILSpy/Themes/Theme.Dark.xaml index 41441900e..e087d39f8 100644 --- a/ILSpy/Themes/Theme.Dark.xaml +++ b/ILSpy/Themes/Theme.Dark.xaml @@ -11,6 +11,8 @@ + + diff --git a/ILSpy/Themes/Theme.Light.xaml b/ILSpy/Themes/Theme.Light.xaml index b1818ea23..f085856d8 100644 --- a/ILSpy/Themes/Theme.Light.xaml +++ b/ILSpy/Themes/Theme.Light.xaml @@ -11,6 +11,8 @@ + + diff --git a/ILSpy/Themes/Theme.RSharpDark.xaml b/ILSpy/Themes/Theme.RSharpDark.xaml index bcd9672ad..ca47f4087 100644 --- a/ILSpy/Themes/Theme.RSharpDark.xaml +++ b/ILSpy/Themes/Theme.RSharpDark.xaml @@ -13,6 +13,8 @@ + + #483D8B #800000 diff --git a/ILSpy/Themes/Theme.RSharpLight.xaml b/ILSpy/Themes/Theme.RSharpLight.xaml index 312919f2a..a41da7d32 100644 --- a/ILSpy/Themes/Theme.RSharpLight.xaml +++ b/ILSpy/Themes/Theme.RSharpLight.xaml @@ -13,6 +13,8 @@ + + #87CEFA #FFB6C1 diff --git a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml index ef7aa20b1..402b4327a 100644 --- a/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeDarkPlus.xaml @@ -18,6 +18,8 @@ + + #264F78 #343A40 diff --git a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 4026ac36c..480892348 100644 --- a/ILSpy/Themes/Theme.VSCodeLightPlus.xaml +++ b/ILSpy/Themes/Theme.VSCodeLightPlus.xaml @@ -18,6 +18,8 @@ + + #ADD6FF #D6EAFF From 1106a6251a38cd7efc035a362caf794d2a6eaa04 Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Thu, 6 Apr 2023 21:27:33 +0200 Subject: [PATCH 146/230] Align highlighted brackets border to whole pixels --- ILSpy/TextView/BracketHighlightRenderer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ILSpy/TextView/BracketHighlightRenderer.cs b/ILSpy/TextView/BracketHighlightRenderer.cs index 9a61bce31..4b958d407 100644 --- a/ILSpy/TextView/BracketHighlightRenderer.cs +++ b/ILSpy/TextView/BracketHighlightRenderer.cs @@ -114,6 +114,8 @@ namespace ICSharpCode.ILSpy.TextView BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder(); builder.CornerRadius = 1; + builder.AlignToWholePixels = true; + builder.BorderThickness = borderPen?.Thickness ?? 0; builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength }); builder.CloseFigure(); // prevent connecting the two segments From bf0fe3aa15eb20ec721d48f1f4c0f9fd9c487cc3 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 6 Apr 2023 22:00:10 +0200 Subject: [PATCH 147/230] Consider constructor type when lifting decimal constants --- .../IL/Transforms/ExpressionTransforms.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 59d0b5706..d6cb0af14 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Daniel Grunwald +// Copyright (c) 2014-2017 Daniel Grunwald // // 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 @@ -428,11 +428,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms var args = inst.Arguments; if (args.Count == 1) { - int val; - if (args[0].MatchLdcI4(out val)) + long val; + if (args[0].MatchLdcI(out val)) { - result = new LdcDecimal(val); - return true; + var paramType = inst.Method.Parameters[0].Type.GetDefinition()?.KnownTypeCode; + result = paramType switch { + KnownTypeCode.Int32 => new LdcDecimal(new decimal((int)val)), + KnownTypeCode.UInt32 => new LdcDecimal(new decimal((uint)val)), + KnownTypeCode.Int64 => new LdcDecimal(new decimal(val)), + KnownTypeCode.UInt64 => new LdcDecimal(new decimal((ulong)val)), + _ => null + }; + return result is not null; } } else if (args.Count == 5) From 01fd6e97f34bcc30ef045858ef201c0d0bbf86f1 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Apr 2023 12:20:07 +0200 Subject: [PATCH 148/230] Fixed overflow exception and added tests --- .../TestCases/Correctness/DecimalFields.cs | 22 ++++++++++++++++++- .../IL/Transforms/ExpressionTransforms.cs | 6 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs index f3873bc38..62679de69 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// Copyright (c) 2014 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 @@ -36,7 +36,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Console.WriteLine(field2); Console.WriteLine(field3); Console.WriteLine(field4); + Console.WriteLine(IntToDecimal()); + Console.WriteLine(UIntToDecimal()); + Console.WriteLine(LongToDecimal()); + Console.WriteLine(ULongToDecimal()); return 0; } + + public static decimal IntToDecimal() { + return (decimal)int.MaxValue; + } + + public static decimal UIntToDecimal() { + return (decimal)uint.MaxValue; + } + + public static decimal LongToDecimal() { + return (decimal)long.MaxValue; + } + + public static decimal ULongToDecimal() { + return (decimal)ulong.MaxValue; + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index d6cb0af14..296145397 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -433,10 +433,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms { var paramType = inst.Method.Parameters[0].Type.GetDefinition()?.KnownTypeCode; result = paramType switch { - KnownTypeCode.Int32 => new LdcDecimal(new decimal((int)val)), - KnownTypeCode.UInt32 => new LdcDecimal(new decimal((uint)val)), + KnownTypeCode.Int32 => new LdcDecimal(new decimal(unchecked((int)val))), + KnownTypeCode.UInt32 => new LdcDecimal(new decimal(unchecked((uint)val))), KnownTypeCode.Int64 => new LdcDecimal(new decimal(val)), - KnownTypeCode.UInt64 => new LdcDecimal(new decimal((ulong)val)), + KnownTypeCode.UInt64 => new LdcDecimal(new decimal(unchecked((ulong)val))), _ => null }; return result is not null; From c76f755ec6f34aa46244eed1af4fd7f332d105af Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Fri, 7 Apr 2023 13:09:27 +0200 Subject: [PATCH 149/230] Fix code style --- .../TestCases/Correctness/DecimalFields.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs index 62679de69..c843b422a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs @@ -43,19 +43,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness return 0; } - public static decimal IntToDecimal() { + public static decimal IntToDecimal() + { return (decimal)int.MaxValue; } - public static decimal UIntToDecimal() { + public static decimal UIntToDecimal() + { return (decimal)uint.MaxValue; } - public static decimal LongToDecimal() { + public static decimal LongToDecimal() + { return (decimal)long.MaxValue; } - public static decimal ULongToDecimal() { + public static decimal ULongToDecimal() + { return (decimal)ulong.MaxValue; } } From 4d6c5322e94b45e7ff4d2a393f21b0f1867b0e1f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 7 Apr 2023 13:17:45 +0200 Subject: [PATCH 150/230] Fix #2888: Tuple syntax cannot be used in is-expressions, use underlying type instead. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index fcdbdfa0e..fb571fce2 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -375,7 +375,7 @@ namespace ICSharpCode.Decompiler.CSharp { var arg = Translate(inst.Argument); arg = UnwrapBoxingConversion(arg); - return new IsExpression(arg.Expression, ConvertType(inst.Type)) + return new IsExpression(arg.Expression, ConvertType(inst.Type.TupleUnderlyingTypeOrSelf())) .WithILInstruction(inst) .WithRR(new TypeIsResolveResult(arg.ResolveResult, inst.Type, compilation.FindType(TypeCode.Boolean))); } From 12d3b93aa9055a09a946b0c02cd9111cdf3f8e7b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 9 Apr 2023 00:52:12 +0200 Subject: [PATCH 151/230] #2956: Prevent loading all ResourceDictionaries eagerly --- ILSpy/Themes/ThemeManager.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index fe03b32c2..97c3a57f5 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -103,21 +103,20 @@ namespace ICSharpCode.ILSpy.Themes .Replace(" ", ""); _themeDictionaryContainer.MergedDictionaries.Clear(); - _themeDictionaryContainer.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"themes/Theme.{themeFileName}.xaml", UriKind.Relative) }); - _syntaxColors.Clear(); - ProcessDictionary(_themeDictionaryContainer); - void ProcessDictionary(ResourceDictionary resourceDictionary) + // Load SyntaxColor info from theme XAML + var resourceDictionary = new ResourceDictionary { Source = new Uri($"/themes/Theme.{themeFileName}.xaml", UriKind.Relative) }; + _themeDictionaryContainer.MergedDictionaries.Add(resourceDictionary); + + // Iterate over keys first, because we don't want to instantiate all values eagerly, if we don't need them. + foreach (var item in resourceDictionary.Keys) { - foreach (DictionaryEntry entry in resourceDictionary) + if (item is string key && key.StartsWith("SyntaxColor.", StringComparison.Ordinal)) { - if (entry is { Key: string key, Value: SyntaxColor syntaxColor }) + if (resourceDictionary[key] is SyntaxColor syntaxColor) _syntaxColors.TryAdd(key, syntaxColor); } - - foreach (ResourceDictionary mergedDictionary in resourceDictionary.MergedDictionaries) - ProcessDictionary(mergedDictionary); } } } From 2f1a1b9dc9049b70c4ac5885aea222b385ae19b5 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Mon, 10 Apr 2023 10:17:22 +0200 Subject: [PATCH 152/230] Fix #2954 (#2955) * Fix #2954: Layout Options context menu over maximize button is not visible on Win11 via https://github.com/tom-englert/TomsToolbox/issues/16 --- packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.props b/packages.props index 13a55bbc6..bc4a05f6f 100644 --- a/packages.props +++ b/packages.props @@ -17,7 +17,7 @@ 4.4.0 0.11.4 6.3.0.90 - 2.8.5 + 2.8.7 3.13.3 4.4.2 3.0.124 From caec6a6a830ca07de141d128effe54b283c9006b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 10 Apr 2023 12:29:21 +0200 Subject: [PATCH 153/230] Fix #2945: Do not treat arbitrary method references pointing to members of the current type definition as part of the method. Only do so for compiler-generated methods. --- ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index ea43eabd4..d89d77648 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -849,7 +849,7 @@ namespace ICSharpCode.Decompiler.CSharp foreach (var m in closureType.GetMethods()) { var methodDef = module.Metadata.GetMethodDefinition(m); - if (methodDef.Name == memberRef.Name) + if (methodDef.Name == memberRef.Name && m.IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) connectedMethods.Enqueue(m); } } From b3f85abcc4fc8dd58a5d3907b6e79235da100b39 Mon Sep 17 00:00:00 2001 From: Brad Cleaver Date: Mon, 10 Apr 2023 14:14:34 -0700 Subject: [PATCH 154/230] Fix ArgumentOutOfRangeException on unexpected file in GAC --- .../Metadata/UniversalAssemblyResolver.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 9de8f2998..8b8575619 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -723,16 +723,22 @@ namespace ICSharpCode.Decompiler.Metadata continue; foreach (var item in new DirectoryInfo(rootPath).EnumerateFiles("*.dll", SearchOption.AllDirectories)) { - string[]? name = Path.GetDirectoryName(item.FullName)?.Substring(rootPath.Length + 1).Split(new[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); - if (name?.Length != 2) - continue; - var match = Regex.Match(name[1], $"(v4.0_)?(?[^_]+)_(?[^_]+)?_(?[^_]+)"); - if (!match.Success) - continue; - string culture = match.Groups["culture"].Value; - if (string.IsNullOrEmpty(culture)) - culture = "neutral"; - yield return AssemblyNameReference.Parse(name[0] + ", Version=" + match.Groups["version"].Value + ", Culture=" + culture + ", PublicKeyToken=" + match.Groups["publicKey"].Value); + // The root of the GAC should only contain folders, but make sure we handle the case where it does NOT in case + // someone has a non-standard layout (e.g. due to a broken installer). + string? assemblyParentPath = Path.GetDirectoryName(item.FullName); + if (assemblyParentPath?.Length > rootPath.Length) + { + string[]? name = assemblyParentPath.Substring(rootPath.Length + 1).Split(new[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); + if (name?.Length != 2) + continue; + var match = Regex.Match(name[1], $"(v4.0_)?(?[^_]+)_(?[^_]+)?_(?[^_]+)"); + if (!match.Success) + continue; + string culture = match.Groups["culture"].Value; + if (string.IsNullOrEmpty(culture)) + culture = "neutral"; + yield return AssemblyNameReference.Parse(name[0] + ", Version=" + match.Groups["version"].Value + ", Culture=" + culture + ", PublicKeyToken=" + match.Groups["publicKey"].Value); + } } } } From ba58ecf0171ae9ab2142dff6f19464d45c42cd85 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Wed, 5 Apr 2023 16:54:48 +0200 Subject: [PATCH 155/230] Alternate approach to #2947 --- ILSpy/ExtensionMethods.cs | 5 ++ ILSpy/NativeMethods.cs | 39 ++++++++++++++ ILSpy/Themes/WindowStyleManagerBehavior.cs | 60 +++++++++++++++++++--- 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs index 55339b316..f08fc8e11 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -161,5 +161,10 @@ namespace ICSharpCode.ILSpy container.IsSelected = true; view.Focus(); } + + public static double ToGray(this Color? color) + { + return color?.R * 0.3 + color?.G * 0.6 + color?.B * 0.1 ?? 0.0; + } } } diff --git a/ILSpy/NativeMethods.cs b/ILSpy/NativeMethods.cs index 9f9fdb0f0..be6ec0c51 100644 --- a/ILSpy/NativeMethods.cs +++ b/ILSpy/NativeMethods.cs @@ -193,6 +193,16 @@ namespace ICSharpCode.ILSpy return null; } } + + [DllImport("dwmapi.dll", PreserveSig = true)] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttribute attr, ref int attrValue, int attrSize); + + public static bool UseImmersiveDarkMode(IntPtr hWnd, bool enable) + { + int darkMode = enable ? 1 : 0; + int hr = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, ref darkMode, sizeof(int)); + return hr >= 0; + } } [return: MarshalAs(UnmanagedType.Bool)] @@ -212,4 +222,33 @@ namespace ICSharpCode.ILSpy this.Buffer = buffer; } } + + public enum DwmWindowAttribute : uint + { + NCRenderingEnabled = 1, + NCRenderingPolicy, + TransitionsForceDisabled, + AllowNCPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExcludedFromPeek, + Cloak, + Cloaked, + FreezeRepresentation, + PassiveUpdateMode, + UseHostBackdropBrush, + UseImmersiveDarkMode = 20, + WindowCornerPreference = 33, + BorderColor, + CaptionColor, + TextColor, + VisibleFrameBorderThickness, + SystemBackdropType, + Last + } } diff --git a/ILSpy/Themes/WindowStyleManagerBehavior.cs b/ILSpy/Themes/WindowStyleManagerBehavior.cs index e9f20ebd2..55d0c2c9b 100644 --- a/ILSpy/Themes/WindowStyleManagerBehavior.cs +++ b/ILSpy/Themes/WindowStyleManagerBehavior.cs @@ -16,11 +16,16 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.ComponentModel; using System.Windows; +using System.Windows.Controls; +using System.Windows.Interop; +using System.Windows.Media; using ICSharpCode.ILSpy.Options; +using TomsToolbox.Essentials; using TomsToolbox.Wpf; using TomsToolbox.Wpf.Interactivity; @@ -30,32 +35,46 @@ namespace ICSharpCode.ILSpy.Themes { private static readonly DispatcherThrottle restartNotificationThrottle = new DispatcherThrottle(ShowRestartNotification); + private INotifyChanged _foreground; + private INotifyChanged _background; + protected override void OnAttached() { base.OnAttached(); MainWindow.Instance.CurrentDisplaySettings.PropertyChanged += DisplaySettings_PropertyChanged; - UpdateWindowStyle(); + _foreground = AssociatedObject.Track(Control.ForegroundProperty); + _background = AssociatedObject.Track(Control.BackgroundProperty); + _foreground.Changed += Color_Changed; + _background.Changed += Color_Changed; + + UpdateWindowStyle(); + ApplyThemeToWindowCaption(); } protected override void OnDetaching() { base.OnDetaching(); + _foreground.Changed -= Color_Changed; + _background.Changed -= Color_Changed; + MainWindow.Instance.CurrentDisplaySettings.PropertyChanged -= DisplaySettings_PropertyChanged; } - private void UpdateWindowStyle() + private void Color_Changed(object sender, EventArgs e) { - if (!MainWindow.Instance.CurrentDisplaySettings.StyleWindowTitleBar) - { - return; - } + ApplyThemeToWindowCaption(); + } + private void UpdateWindowStyle() + { var window = AssociatedObject; - window.Style = (Style)window.FindResource(TomsToolbox.Wpf.Styles.ResourceKeys.WindowStyle); + + if (MainWindow.Instance.CurrentDisplaySettings.StyleWindowTitleBar) + window.Style = (Style)window.FindResource(TomsToolbox.Wpf.Styles.ResourceKeys.WindowStyle); } private static void ShowRestartNotification() @@ -76,5 +95,32 @@ namespace ICSharpCode.ILSpy.Themes UpdateWindowStyle(); } } + + private void ApplyThemeToWindowCaption() + { + var window = AssociatedObject; + + IntPtr hwnd = new WindowInteropHelper(window).Handle; + + if (hwnd != IntPtr.Zero) + { + var foreground = ((window.Foreground as SolidColorBrush)?.Color).ToGray(); + var background = ((window.Background as SolidColorBrush)?.Color).ToGray(); + + var isDarkTheme = background < foreground; + + NativeMethods.UseImmersiveDarkMode(hwnd, isDarkTheme); + } + else + { + void Initialized(object o, EventArgs eventArgs) + { + ApplyThemeToWindowCaption(); + window.SourceInitialized -= Initialized; + } + + window.SourceInitialized += Initialized; + } + } } } From 515c9626bc8ea01d324a607789d4f172fb502763 Mon Sep 17 00:00:00 2001 From: miloush Date: Thu, 20 Apr 2023 11:04:17 +0100 Subject: [PATCH 156/230] Search box for resource tables --- ILSpy/Controls/ResourceObjectTable.xaml | 8 +++++- ILSpy/Controls/ResourceObjectTable.xaml.cs | 29 +++++++++++++++++++++- ILSpy/Controls/ResourceStringTable.xaml | 8 +++++- ILSpy/Controls/ResourceStringTable.xaml.cs | 29 +++++++++++++++++++++- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/ILSpy/Controls/ResourceObjectTable.xaml b/ILSpy/Controls/ResourceObjectTable.xaml index c98364814..e719f15b1 100644 --- a/ILSpy/Controls/ResourceObjectTable.xaml +++ b/ILSpy/Controls/ResourceObjectTable.xaml @@ -27,16 +27,22 @@ + public partial class ResourceObjectTable : UserControl { + ICollectionView filteredView; + string filter; + public ResourceObjectTable(IEnumerable resources, FrameworkElement container) { InitializeComponent(); @@ -38,7 +44,22 @@ namespace ICSharpCode.ILSpy.Controls if (!double.IsNaN(container.ActualWidth)) Width = Math.Max(container.ActualWidth - 45, 0); MaxHeight = container.ActualHeight; - resourceListView.ItemsSource = resources; + + filteredView = CollectionViewSource.GetDefaultView(resources); + filteredView.Filter = OnResourceFilter; + resourceListView.ItemsSource = filteredView; + } + + private bool OnResourceFilter(object obj) + { + if (string.IsNullOrEmpty(filter)) + return true; + + if (obj is TreeNodes.ResourcesFileTreeNode.SerializedObjectRepresentation item) + return item.Key?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true || + item.Value?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true; + + return false; // make it obvious search is not working } private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) @@ -49,6 +70,12 @@ namespace ICSharpCode.ILSpy.Controls MaxHeight = e.NewSize.Height; } + private void OnFilterTextChanged(object sender, TextChangedEventArgs e) + { + filter = resourceFilterBox.Text; + filteredView?.Refresh(); + } + void ExecuteCopy(object sender, ExecutedRoutedEventArgs args) { StringBuilder sb = new StringBuilder(); diff --git a/ILSpy/Controls/ResourceStringTable.xaml b/ILSpy/Controls/ResourceStringTable.xaml index 3262de0b6..b2ce2127c 100644 --- a/ILSpy/Controls/ResourceStringTable.xaml +++ b/ILSpy/Controls/ResourceStringTable.xaml @@ -27,15 +27,21 @@ + public partial class ResourceStringTable : UserControl { + ICollectionView filteredView; + string filter; + public ResourceStringTable(IEnumerable strings, FrameworkElement container) { InitializeComponent(); @@ -38,7 +44,22 @@ namespace ICSharpCode.ILSpy.Controls if (!double.IsNaN(container.ActualWidth)) Width = Math.Max(container.ActualWidth - 45, 0); MaxHeight = container.ActualHeight; - resourceListView.ItemsSource = strings; + + filteredView = CollectionViewSource.GetDefaultView(strings); + filteredView.Filter = OnResourceFilter; + resourceListView.ItemsSource = filteredView; + } + + private bool OnResourceFilter(object obj) + { + if (string.IsNullOrEmpty(filter)) + return true; + + if (obj is KeyValuePair item) + return item.Key?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true || + item.Value?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true; + + return false; // make it obvious search is not working } private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) @@ -49,6 +70,12 @@ namespace ICSharpCode.ILSpy.Controls MaxHeight = e.NewSize.Height; } + private void OnFilterTextChanged(object sender, TextChangedEventArgs e) + { + filter = resourceFilterBox.Text; + filteredView?.Refresh(); + } + void ExecuteCopy(object sender, ExecutedRoutedEventArgs args) { StringBuilder sb = new StringBuilder(); From 8a1e8e3c6b63e579eeda65a62543ffd887d6eb51 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 22 Apr 2023 14:51:38 +0200 Subject: [PATCH 157/230] Make return duplication in `ControlFlowSimplification` less aggressive --- .../IL/ControlFlow/ControlFlowSimplification.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index c813d3465..c0b9de985 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Daniel Grunwald +// Copyright (c) 2014 Daniel Grunwald // // 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 @@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void SimplifyBranchChains(ILFunction function, ILTransformContext context) { - List<(BlockContainer, Block)> blocksToAdd = new List<(BlockContainer, Block)>(); + List<(Block Block, BlockContainer TargetContainer)> blocksToMove = new List<(Block, BlockContainer)>(); HashSet visitedBlocks = new HashSet(); foreach (var branch in function.Descendants.OfType()) { @@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.Step("Replace branch to return with return", branch); branch.ReplaceWith(targetBlock.Instructions[0].Clone()); } - else if (branch.TargetContainer != branch.Ancestors.OfType().First()) + else if (branch.TargetContainer != branch.Ancestors.OfType().First() && targetBlock.IncomingEdgeCount == 1) { // We don't want to always inline the return directly, because this // might force us to place the return within a loop, when it's better @@ -175,11 +175,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // But we do want to move the return block into the correct try-finally scope, // so that loop detection at least has the option to put it inside // the loop body. - context.Step("Copy return block into try block", branch); - Block blockCopy = (Block)branch.TargetBlock.Clone(); + context.Step("Move return block into try block", branch); BlockContainer localContainer = branch.Ancestors.OfType().First(); - blocksToAdd.Add((localContainer, blockCopy)); - branch.TargetBlock = blockCopy; + blocksToMove.Add((targetBlock, localContainer)); } } else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0] is Leave leave && leave.Value.MatchNop()) @@ -194,9 +192,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (targetBlock.IncomingEdgeCount == 0) targetBlock.Instructions.Clear(); // mark the block for deletion } - foreach (var (container, block) in blocksToAdd) + foreach ((Block block, BlockContainer targetContainer) in blocksToMove) { - container.Blocks.Add(block); + block.Remove(); + targetContainer.Blocks.Add(block); } } From 64e6c6318f6d024f9111f751036b19de7ef6fafa Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 24 Apr 2023 19:43:50 +0200 Subject: [PATCH 158/230] Add unit test --- .../TestCases/Pretty/ReduceNesting.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs index ece018663..296914030 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { @@ -566,5 +567,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + private static string DuplicateReturnTest(IDictionary dict) + { + string value; + lock (dict) + { + if (!dict.TryGetValue(1, out value)) + { + value = "test"; + dict.Add(1, value); + } + } + return value; + } } } From 8d7f8cb76bfc2965140ea98deaee6e92380f4959 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 24 Apr 2023 19:49:19 +0200 Subject: [PATCH 159/230] Adjust test name --- ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs index 296914030..006cac861 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs @@ -568,7 +568,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - private static string DuplicateReturnTest(IDictionary dict) + private static string ShouldNotDuplicateReturnStatementIntoTry(IDictionary dict) { string value; lock (dict) From 0167489f443a18b05cff823d14ac4652769271d7 Mon Sep 17 00:00:00 2001 From: Boring3 <16686147+Nyrest@users.noreply.github.com> Date: Wed, 26 Apr 2023 12:55:08 +0800 Subject: [PATCH 160/230] Update Chinese translation (#2970) * Update Chinese translation * Sort Resources.zh-Hans.resx * Fix trailing space and punctuation to match the original format. * Update Resources.zh-Hans.resx --- ILSpy/Properties/Resources.zh-Hans.resx | 40 +++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/ILSpy/Properties/Resources.zh-Hans.resx b/ILSpy/Properties/Resources.zh-Hans.resx index 3715d1004..041005759 100644 --- a/ILSpy/Properties/Resources.zh-Hans.resx +++ b/ILSpy/Properties/Resources.zh-Hans.resx @@ -202,7 +202,7 @@ 复制完全限定名称 - 无法使用SDK-style项目格式, 因为发现有不兼容目标框架别名 + 无法使用SDK-style项目格式,因为发现有不兼容目标框架别名。 创建 @@ -235,7 +235,7 @@ 反编译视图选项 - 反编译已取消 + 反编译已取消。 反编译 @@ -267,6 +267,9 @@ 始终使用大括号 + + 总是使用 "global::" 完全限定命名空间 + 在已加载的程序集上应用 Windows 运行时投影 @@ -378,6 +381,9 @@ 类型参数上的 IsUnmanagedAttribute 应替换为 unmanaged 约束 + + 'scoped' 生命周期注解关键字 + 使用 nint/nuint 类型 @@ -393,6 +399,9 @@ 其他 + + 使用方法参数非空校验 + 使用模式匹配表达式 @@ -408,6 +417,9 @@ 记录 + + 记录结构 + 删除死代码和无副作用的代码(请谨慎使用) @@ -417,6 +429,9 @@ 如果可能,删除可选参数 + + Required 必要成员 + 如果可能,分离局部变量的声明与初始化(int x = 5; -> int x; x = 5;) @@ -525,6 +540,9 @@ 字体: + + 主题: + 下载 @@ -631,7 +649,7 @@ 杂项 - .Net 版本 + .NET 版本 名称 @@ -674,7 +692,7 @@ 打开(_O) - 操作已取消 + 操作已取消。 选项 @@ -887,6 +905,9 @@ Tab 长度: + + 主题 + 切换所有折叠 @@ -911,6 +932,9 @@ 使用 logic 语法糖 + + 使用嵌套的命名空间结构 + 使用 Tab 替代空格 @@ -962,6 +986,12 @@ 添加到主列表(_A) + + 分析器(_A) + + + 程序集(_A) + 检查更新(_C) @@ -1028,4 +1058,4 @@ 窗口(_W) - \ No newline at end of file + From 29db7fac3043fb0ee984ced9f22c6b5912ddb71d Mon Sep 17 00:00:00 2001 From: Jan Hoek Date: Thu, 27 Apr 2023 17:41:04 +0200 Subject: [PATCH 161/230] PowerShell module manifest (#2976) * Ignore .vscode folders * Added PowerShell manifest * After building, copy the manifest to the target folder --------- Co-authored-by: Jan Hoek --- .gitignore | 1 + .../ICSharpCode.Decompiler.PowerShell.csproj | 3 + .../manifest.psd1 | 129 ++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 ICSharpCode.Decompiler.PowerShell/manifest.psd1 diff --git a/.gitignore b/.gitignore index 923f020d0..be16796fd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ multitargeting.props ILSpy.Installer/wix/ /VERSION /ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.cs +*/.vscode/ diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index 8e73fc14a..8c469191f 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -25,4 +25,7 @@ + + + diff --git a/ICSharpCode.Decompiler.PowerShell/manifest.psd1 b/ICSharpCode.Decompiler.PowerShell/manifest.psd1 new file mode 100644 index 000000000..575226e49 --- /dev/null +++ b/ICSharpCode.Decompiler.PowerShell/manifest.psd1 @@ -0,0 +1,129 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'ICSharpCode.Decompiler.PowerShell.dll' + + # Version number of this module. + ModuleVersion = '8.0.0.0' + + # Supported PSEditions + # CompatiblePSEditions = @() + + # ID used to uniquely identify this module + GUID = '198b4312-cbe7-417e-81a7-1aaff467ef06' + + # Author of this module + Author = 'ILSpy Contributors' + + # Company or vendor of this module + CompanyName = 'ic#code' + + # Copyright statement for this module + Copyright = 'Copyright 2011-2023 AlphaSierraPapa' + + # Description of the functionality provided by this module + Description = 'PowerShell front-end for ILSpy' + + # Minimum version of the PowerShell engine required by this module + # PowerShellVersion = '' + + # Name of the PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # ClrVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = @() + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @( + 'Get-DecompiledProject', + 'Get-DecompiledSource', + 'Get-DecompiledTypes', + 'Get-Decompiler', + 'Get-DecompilerVersion' + ) + + # Variables to export from this module + VariablesToExport = '*' + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/icsharpcode/ILSpy' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} + From e8d9da10be31fcc4da1f1c600110ccbe28cfedbb Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 27 Apr 2023 17:46:44 +0200 Subject: [PATCH 162/230] Update README for PS cmdlets module --- ICSharpCode.Decompiler.PowerShell/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.PowerShell/README.md b/ICSharpCode.Decompiler.PowerShell/README.md index 2bac472f4..acb6e671d 100644 --- a/ICSharpCode.Decompiler.PowerShell/README.md +++ b/ICSharpCode.Decompiler.PowerShell/README.md @@ -4,20 +4,21 @@ Built using https://github.com/PowerShell/PowerShell/blob/master/docs/cmdlet-exa Sample usage: Demo.ps1 -Tested with: PowerShell 5.1 on Windows, PowerShell Core on Windows and Mac (Beta9) +Tested with: PowerShell 5.1 on Windows, PowerShell 7+ on Windows and Mac ## Missing -.psd1 for deploying to https://www.powershellgallery.com/ +Publishing to https://www.powershellgallery.com/ +* https://learn.microsoft.com/en-us/powershell/gallery/how-to/publishing-packages/publishing-a-package +* https://learn.microsoft.com/en-us/powershell/gallery/concepts/publishing-guidelines ## Links for developing PS cmdlets -* https://docs.microsoft.com/en-us/powershell/gallery/psgallery/creating-and-publishing-an-item +* https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/how-to-write-a-simple-cmdlet +* https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands * https://github.com/mmaitre314/PowerShellGet-Test-Binary-Module * https://www.red-gate.com/simple-talk/dotnet/net-development/using-c-to-create-powershell-cmdlets-beyond-the-basics/ -* https://msdn.microsoft.com/en-us/library/dd878294(v=VS.85).aspx Writing a Windows PowerShell Cmdlet -* https://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx Approved verbs * https://www.google.com/search?q=write+a+module+for+powershell+core \ No newline at end of file From 7af3988ed7d363d740f4c105b3cd180442602b89 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Wed, 10 May 2023 10:01:40 -0700 Subject: [PATCH 163/230] Refresh ready to run package --- ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj | 2 +- packages.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj index 804c7c8e6..853436cc7 100644 --- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -38,7 +38,7 @@ - + - 6.0.0 - 6.0.1 + 7.0.0 + 7.0.0 6.0.0 6.0.0 From 61f79a01cc2bee0324e7d8f37731d39701eff7c3 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Fri, 12 May 2023 09:05:59 +0200 Subject: [PATCH 164/230] RC1 will be the next release --- .../Properties/DecompilerVersionInfo.template.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs index 37c2bc42d..659c5a7ee 100644 --- a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs +++ b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs @@ -4,7 +4,7 @@ public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "preview4"; + public const string VersionName = "rc1"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; public const string FullVersionWithShortCommitHash = FullVersion + "-$INSERTSHORTCOMMITHASH$"; From f9c7b6b66e8134a5fdf4a824a4543aec743474c2 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Mon, 10 Apr 2023 20:24:34 +0200 Subject: [PATCH 165/230] Extend `EliminateRedundantTryFinally` in `ReduceNestingTransform` --- .../IL/Transforms/ReduceNestingTransform.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index ec4c1f7dd..2876f141e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Siegfried Pammer +// Copyright (c) 2018 Siegfried Pammer // // 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 @@ -637,10 +637,25 @@ namespace ICSharpCode.Decompiler.IL // Finally is empty and redundant. But we'll delete the block only if there's a PinnedRegion. if (!(tryFinally.TryBlock is BlockContainer tryContainer)) return; - if (tryContainer.SingleInstruction() is PinnedRegion pinnedRegion) + if (tryContainer.Blocks.Count != 1) + return; + var tryBlock = tryContainer.Blocks[0]; + if (tryBlock.Instructions.Count == 1) { - context.Step("Removing try-finally around PinnedRegion", pinnedRegion); - tryFinally.ReplaceWith(pinnedRegion); + if (tryBlock.Instructions[0] is PinnedRegion pinnedRegion) + { + context.Step("Removing try-finally around PinnedRegion", pinnedRegion); + tryFinally.ReplaceWith(pinnedRegion); + } + } + else if (tryBlock.Instructions.Count == 2) + { + if (tryBlock.Instructions[0] is PinnedRegion pinnedRegion && + tryBlock.Instructions[1].MatchLeave(tryContainer)) + { + context.Step("Removing try-finally around PinnedRegion", pinnedRegion); + tryFinally.ReplaceWith(pinnedRegion); + } } } } From 5c678445004c406150ec6c9e84f175ee1f542ef0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 May 2023 13:58:52 +0200 Subject: [PATCH 166/230] Fix #2920: Implement support for DefaultParameterValueAttribute. --- .../TestCases/Pretty/OptionalArguments.cs | 58 +++++++++++++++++++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 2 +- .../Implementation/KnownAttributes.cs | 3 + .../Implementation/MetadataParameter.cs | 11 +++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs index d770c1d1b..f68b896d0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { @@ -225,5 +226,62 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } #endif + + public static void Issue2920a(int x) + { + } + public static void Issue2920b([DefaultParameterValue(3)] int x) + { + } + public static void Issue2920c(ref int x) + { + } + public static void Issue2920d([DefaultParameterValue(3)] ref int x) + { + } + public static void Issue2920e(out int x) + { + x = 0; + } + public static void Issue2920f([DefaultParameterValue(3)] out int x) + { + x = 0; + } +#if CS70 + public static void Issue2920g(in int x) + { + } + public static void Issue2920h([DefaultParameterValue(3)] in int x) + { + } +#endif + public static void Issue2920i([Optional] int x) + { + } + public static void Issue2920j(int x = 3) + { + } + public static void Issue2920k([Optional] ref int x) + { + } + public static void Issue2920l([Optional][DefaultParameterValue(3)] ref int x) + { + } + public static void Issue2920m([Optional] out int x) + { + x = 0; + } + public static void Issue2920n([Optional][DefaultParameterValue(3)] out int x) + { + x = 0; + } +#if CS70 + public static void Issue2920o([Optional] in int x) + { + } + public static void Issue2920p(in int x = 3) + { + } +#endif } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3b57da652..fb8f21a6f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1675,7 +1675,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { decl.Name = parameter.Name; } - if (parameter.IsOptional && parameter.HasConstantValueInSignature && this.ShowConstantValues) + if (parameter.IsOptional && decl.ParameterModifier is ParameterModifier.None or ParameterModifier.In && parameter.HasConstantValueInSignature && this.ShowConstantValues) { try { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index aa289b498..d6d99977a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -89,6 +89,7 @@ namespace ICSharpCode.Decompiler.TypeSystem In, Out, Optional, + DefaultParameterValue, CallerMemberName, CallerFilePath, CallerLineNumber, @@ -165,6 +166,7 @@ namespace ICSharpCode.Decompiler.TypeSystem new TopLevelTypeName("System.Runtime.InteropServices", nameof(InAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OutAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OptionalAttribute)), + new TopLevelTypeName("System.Runtime.InteropServices", nameof(DefaultParameterValueAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)), @@ -220,6 +222,7 @@ namespace ICSharpCode.Decompiler.TypeSystem case KnownAttribute.MarshalAs: case KnownAttribute.PermissionSet: case KnownAttribute.Optional: + case KnownAttribute.DefaultParameterValue: case KnownAttribute.In: case KnownAttribute.Out: case KnownAttribute.IndexerName: diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index adcf82540..506aab564 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -64,8 +64,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var metadata = module.metadata; var parameter = metadata.GetParameter(handle); - if (IsOptional && !HasConstantValueInSignature) + bool defaultValueAssignmentAllowed = ReferenceKind is ReferenceKind.None or ReferenceKind.In; + + if (IsOptional && (!defaultValueAssignmentAllowed || !HasConstantValueInSignature)) + { b.Add(KnownAttribute.Optional); + } + + if (!(IsDecimalConstant || !HasConstantValueInSignature) && (!defaultValueAssignmentAllowed || !IsOptional)) + { + b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false)); + } if (!IsOut && !IsIn) { From 539925f25915a3ffb7e468e6d367e93542cfe087 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 May 2023 13:39:47 +0200 Subject: [PATCH 167/230] Fix #2983: display-class locals should be named uniquely per top-level ILFunction. Add assertion to ResolveCollisions: ensure that colliding/merged variables have the same type. --- ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs | 1 + ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index b146e85a4..b17960e36 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -476,6 +476,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Debug.Assert(point1.level == point2.level); if (point1.nextNode.Parent == point2.nextNode.Parent) { + Debug.Assert(prev.Type.Equals(v.Type)); // We found a collision! v.InvolvedInCollision = true; prev.ReplacementDueToCollision = v; diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 4a8342cd7..e4331c74b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms Dictionary localFunctionMapping; HashSet loopCounters; const char maxLoopVariableName = 'n'; + int numDisplayClassLocals; public void Run(ILFunction function, ILTransformContext context) { @@ -151,6 +152,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms AddExistingName(reservedVariableNames, p.Name); } } + numDisplayClassLocals = 0; foreach (ILFunction f in function.Descendants.OfType().Reverse()) { PerformAssignment(f); @@ -195,7 +197,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms { // remove unused variables before assigning names function.Variables.RemoveDead(); - int numDisplayClassLocals = 0; Dictionary assignedLocalSignatureIndices = new Dictionary(); foreach (var v in function.Variables.OrderBy(v => v.Name)) { From 29ca38d338853742c80ba96dbaff28fdf0979b7b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 May 2023 18:32:32 +0200 Subject: [PATCH 168/230] Fix handling of recombined variables and nested functions in IntroduceNativeIntTypeOnLocals and IntroduceDynamicTypeOnLocals. --- .../IntroduceDynamicTypeOnLocals.cs | 49 +++++++++++++------ .../IntroduceNativeIntTypeOnLocals.cs | 40 ++++++++++----- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs b/ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs index 072113440..c1d73a19b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using ICSharpCode.Decompiler.IL.Transforms; @@ -29,28 +30,46 @@ namespace ICSharpCode.Decompiler.IL { public void Run(ILFunction function, ILTransformContext context) { - foreach (var variable in function.Variables) + foreach (var nestedFunction in function.Descendants.OfType()) { - if (variable.Kind != VariableKind.Local && - variable.Kind != VariableKind.StackSlot && - variable.Kind != VariableKind.ForeachLocal && - variable.Kind != VariableKind.UsingLocal) + HashSet dynamicVariables = new(); + foreach (var variable in nestedFunction.Variables) { - continue; - } - if (!variable.Type.IsKnownType(KnownTypeCode.Object) || variable.LoadCount == 0) - continue; - foreach (var load in variable.LoadInstructions) - { - if (load.Parent is DynamicInstruction dynamicInstruction) + if (variable.Kind != VariableKind.Local && + variable.Kind != VariableKind.StackSlot && + variable.Kind != VariableKind.ForeachLocal && + variable.Kind != VariableKind.UsingLocal) + { + continue; + } + if (!variable.Type.IsKnownType(KnownTypeCode.Object) || variable.LoadCount == 0) + continue; + foreach (var load in variable.LoadInstructions) { - var argumentInfo = dynamicInstruction.GetArgumentInfoOfChild(load.ChildIndex); - if (!argumentInfo.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) + if (load.Parent is DynamicInstruction dynamicInstruction) { - variable.Type = SpecialType.Dynamic; + var argumentInfo = dynamicInstruction.GetArgumentInfoOfChild(load.ChildIndex); + if (!argumentInfo.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) + { + variable.Type = SpecialType.Dynamic; + if (variable.Index.HasValue && variable.Kind == VariableKind.Local) + { + dynamicVariables.Add(variable.Index.Value); + } + break; + } } } } + foreach (var variable in nestedFunction.Variables) + { + if (variable.Index.HasValue && variable.Kind == VariableKind.Local + && dynamicVariables.Contains(variable.Index.Value)) + { + variable.Type = SpecialType.Dynamic; + continue; + } + } } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs b/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs index 910a4726a..a6fee58a7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs @@ -32,23 +32,37 @@ namespace ICSharpCode.Decompiler.IL { if (!context.Settings.NativeIntegers) return; - foreach (var variable in function.Variables) + foreach (var nestedFunction in function.Descendants.OfType()) { - if (variable.Kind != VariableKind.Local && - variable.Kind != VariableKind.StackSlot && - variable.Kind != VariableKind.PatternLocal && - variable.Kind != VariableKind.ForeachLocal && - variable.Kind != VariableKind.UsingLocal) + Dictionary variableTypeMapping = new(); + foreach (var variable in nestedFunction.Variables) { - continue; + if (variable.Kind != VariableKind.Local && + variable.Kind != VariableKind.StackSlot && + variable.Kind != VariableKind.PatternLocal && + variable.Kind != VariableKind.ForeachLocal && + variable.Kind != VariableKind.UsingLocal) + { + continue; + } + if (!(variable.Type.IsKnownType(KnownTypeCode.IntPtr) || variable.Type.IsKnownType(KnownTypeCode.UIntPtr))) + continue; + bool isUsedAsNativeInt = variable.LoadInstructions.Any(IsUsedAsNativeInt); + bool isAssignedNativeInt = variable.StoreInstructions.Any(store => IsNativeIntStore(store, context.TypeSystem)); + if (isUsedAsNativeInt || isAssignedNativeInt) + { + variable.Type = variable.Type.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; + if (variable.Kind == VariableKind.Local && variable.Index.HasValue) + variableTypeMapping[variable.Index.Value] = variable.Type; + } } - if (!(variable.Type.IsKnownType(KnownTypeCode.IntPtr) || variable.Type.IsKnownType(KnownTypeCode.UIntPtr))) - continue; - bool isUsedAsNativeInt = variable.LoadInstructions.Any(IsUsedAsNativeInt); - bool isAssignedNativeInt = variable.StoreInstructions.Any(store => IsNativeIntStore(store, context.TypeSystem)); - if (isUsedAsNativeInt || isAssignedNativeInt) + foreach (var variable in nestedFunction.Variables) { - variable.Type = variable.Type.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; + if (variable.Kind == VariableKind.Local && variable.Index.HasValue + && variableTypeMapping.TryGetValue(variable.Index.Value, out var type)) + { + variable.Type = type; + } } } } From ea1f6e7c254829df019eac88f4d4b5ebe9f705a8 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 22 Apr 2023 13:26:38 +0200 Subject: [PATCH 169/230] Fixed compound assignment and post/pre increment for pointer dereference --- ICSharpCode.Decompiler/IL/ILTypeExtensions.cs | 22 +++- .../IL/Transforms/TransformAssignment.cs | 108 +++++++++++++++--- 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 5b5767f3b..e67075aa3 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable // Copyright (c) 2014 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this @@ -78,6 +78,26 @@ namespace ICSharpCode.Decompiler.IL } } + public static bool HasOppositeSign(this PrimitiveType primitiveType) + { + switch (primitiveType) + { + case PrimitiveType.I1: + case PrimitiveType.I2: + case PrimitiveType.I4: + case PrimitiveType.I8: + case PrimitiveType.U1: + case PrimitiveType.U2: + case PrimitiveType.U4: + case PrimitiveType.U8: + case PrimitiveType.I: + case PrimitiveType.U: + return true; + default: + return false; + } + } + /// /// Gets the size in bytes of the primitive type. /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index d1f32a558..7935e7ec0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -145,10 +145,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms else if (pointerType is PointerType pointer) newType = pointer.ElementType; } - if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem)) + if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem, out bool canChangeSign)) { - // 'stobj' is implicitly truncating the value - return false; + if (canChangeSign) + { + // Change the sign of the type to skip implicit truncation + newType = SwapSign(newType, context.TypeSystem); + } + else + { + // 'stobj' is implicitly truncating the value + return false; + } } context.Step("Inline assignment stobj", stobj); stobj.Type = newType; @@ -214,6 +222,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + private static IType SwapSign(IType type, ICompilation compilation) + { + return type.ToPrimitiveType() switch { + PrimitiveType.I1 => compilation.FindType(KnownTypeCode.Byte), + PrimitiveType.I2 => compilation.FindType(KnownTypeCode.UInt16), + PrimitiveType.I4 => compilation.FindType(KnownTypeCode.UInt32), + PrimitiveType.I8 => compilation.FindType(KnownTypeCode.UInt64), + PrimitiveType.U1 => compilation.FindType(KnownTypeCode.SByte), + PrimitiveType.U2 => compilation.FindType(KnownTypeCode.Int16), + PrimitiveType.U4 => compilation.FindType(KnownTypeCode.Int32), + PrimitiveType.U8 => compilation.FindType(KnownTypeCode.Int64), + PrimitiveType.I => compilation.FindType(KnownTypeCode.UIntPtr), + PrimitiveType.U => compilation.FindType(KnownTypeCode.IntPtr), + _ => type + }; + } + static ILInstruction UnwrapSmallIntegerConv(ILInstruction inst, out Conv conv) { conv = inst as Conv; @@ -507,12 +532,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) + { + return IsImplicitTruncation(value, type, compilation, out _, allowNullableValue); + } + /// /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' /// due to implicit truncation. /// - static internal bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) + internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, out bool canChangeSign, bool allowNullableValue = false) { + canChangeSign = false; if (!type.IsSmallIntegerType()) { // Implicit truncation in ILAst only happens for small integer types; @@ -542,7 +573,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (value is Conv conv) { - return conv.TargetType != type.ToPrimitiveType(); + PrimitiveType primitiveType = type.ToPrimitiveType(); + PrimitiveType convTargetType = conv.TargetType; + if (convTargetType == primitiveType) + return false; + if (primitiveType.GetSize() == convTargetType.GetSize() && primitiveType.GetSign() != convTargetType.GetSign()) + canChangeSign = primitiveType.HasOppositeSign(); + return true; } else if (value is Comp) { @@ -557,14 +594,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms case BinaryNumericOperator.BitXor: // If both input values fit into the type without truncation, // the result of a binary operator will also fit. - return IsImplicitTruncation(bni.Left, type, compilation, allowNullableValue) - || IsImplicitTruncation(bni.Right, type, compilation, allowNullableValue); + bool leftIsTruncation = IsImplicitTruncation(bni.Left, type, compilation, out bool leftChangeSign, allowNullableValue); + // If the left side is truncating and a sign change is not possible we do not need to evaluate the right side + if (leftIsTruncation && !leftChangeSign) + return true; + bool rightIsTruncation = IsImplicitTruncation(bni.Right, type, compilation, out bool rightChangeSign, allowNullableValue); + if (!rightIsTruncation) + return false; + canChangeSign = rightChangeSign; + return true; } } else if (value is IfInstruction ifInst) { - return IsImplicitTruncation(ifInst.TrueInst, type, compilation, allowNullableValue) - || IsImplicitTruncation(ifInst.FalseInst, type, compilation, allowNullableValue); + bool trueIsTruncation = IsImplicitTruncation(ifInst.TrueInst, type, compilation, out bool trueChangeSign, allowNullableValue); + // If the true branch is truncating and a sign change is not possible we do not need to evaluate the false branch + if (trueIsTruncation && !trueChangeSign) + return true; + bool falseIsTruncation = IsImplicitTruncation(ifInst.FalseInst, type, compilation, out bool falseChangeSign, allowNullableValue); + if (!falseIsTruncation) + return false; + canChangeSign = falseChangeSign; + return true; } else { @@ -575,7 +626,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (inferredType.Kind != TypeKind.Unknown) { - return !(inferredType.GetSize() <= type.GetSize() && inferredType.GetSign() == type.GetSign()); + var inferredPrimitive = inferredType.ToPrimitiveType(); + var primitiveType = type.ToPrimitiveType(); + + bool sameSign = inferredPrimitive.GetSign() == primitiveType.GetSign(); + if (inferredPrimitive.GetSize() <= primitiveType.GetSize() && sameSign) + return false; + if (inferredPrimitive.GetSize() == primitiveType.GetSize() && !sameSign) + canChangeSign = primitiveType.HasOppositeSign(); + return true; } } return true; @@ -790,6 +849,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; + + if (conv is not null) + { + var primitiveType = targetType.ToPrimitiveType(); + if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign()) + targetType = SwapSign(targetType, context.TypeSystem); + } + if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) return false; stloc = binary.Left as StLoc; @@ -858,10 +925,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms var tmpVar = inst.Variable; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; - if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) + if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem, out bool canChangeSign)) { - // 'stloc tmp' is implicitly truncating the value - return false; + // If 'store' is a stobj and 'canChangeSign' is true, then the + // implicit truncation can be skipped by flipping the sign of the `stobj` type. + if (!canChangeSign || store is not StObj stObj || !stObj.Type.Equals(targetType)) + { + // 'stloc tmp' is implicitly truncating the value + return false; + } } if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: inst.Variable, @@ -883,6 +955,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (!(binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) return false; + if (canChangeSign && store is StObj stObj) + { + // Change the sign of the type to skip implicit truncation + stObj.Type = targetType = SwapSign(targetType, context.TypeSystem); + } if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); @@ -899,6 +976,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); + if (canChangeSign && store is StObj stObj) + { + // Change the sign of the type to skip implicit truncation + stObj.Type = targetType = SwapSign(targetType, context.TypeSystem); + } finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); From 21ddd402c2431d3d240a8293ead304c16f912b9a Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sat, 22 Apr 2023 14:12:59 +0200 Subject: [PATCH 170/230] Extend unit test for pointer compound assign --- .../TestCases/Pretty/CompoundAssignmentTest.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index b85802438..e9c391ae3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4586,6 +4586,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return *(ptr++); } + public unsafe int PostIncrementOfSmallIntegerPointerDereference(byte* ptr) + { + return (*ptr)++ * (*ptr)++; + } + + public unsafe int PreIncrementOfSmallIntegerPointerDereference(byte* ptr) + { + return ++(*ptr) * ++(*ptr); + } + + public unsafe int CompoundAssignSmallIntegerPointerDereference(byte* ptr) + { + return (*ptr += 5) * (*ptr += 5); + } + public int PostDecrementInstanceField() { return M().Field--; From 220b4cbd0675c3d1c5a7b26e9fa3ba4efb728ccd Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 20 May 2023 17:30:11 +0200 Subject: [PATCH 171/230] Set version for release --- .../Properties/DecompilerVersionInfo.template.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs index 659c5a7ee..a119f82d5 100644 --- a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs +++ b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs @@ -4,7 +4,7 @@ public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "rc1"; + public const string VersionName = null; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; public const string FullVersionWithShortCommitHash = FullVersion + "-$INSERTSHORTCOMMITHASH$"; From f04acbdd74f57bd3cf6ae89a79c6fdde2f2015a0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 28 May 2023 13:52:07 +0200 Subject: [PATCH 172/230] Simplify `IsImplicitTruncation` by using an enum with 3 options instead of a pair of bools. This also fixes the logic for combining the results for BinaryNumericInstruction/IfInstruction. --- .../IL/Transforms/TransformAssignment.cs | 147 ++++++++++-------- 1 file changed, 80 insertions(+), 67 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 7935e7ec0..7407179b4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -145,18 +145,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms else if (pointerType is PointerType pointer) newType = pointer.ElementType; } - if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem, out bool canChangeSign)) + var truncation = CheckImplicitTruncation(inst.Value, newType, context.TypeSystem); + if (truncation == ImplicitTruncationResult.ValueChanged) { - if (canChangeSign) - { - // Change the sign of the type to skip implicit truncation - newType = SwapSign(newType, context.TypeSystem); - } - else - { - // 'stobj' is implicitly truncating the value - return false; - } + // 'stobj' is implicitly truncating the value + return false; + } + if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch) + { + // Change the sign of the type to skip implicit truncation + newType = SwapSign(newType, context.TypeSystem); } context.Step("Inline assignment stobj", stobj); stobj.Type = newType; @@ -235,7 +233,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms PrimitiveType.U8 => compilation.FindType(KnownTypeCode.Int64), PrimitiveType.I => compilation.FindType(KnownTypeCode.UIntPtr), PrimitiveType.U => compilation.FindType(KnownTypeCode.IntPtr), - _ => type + _ => throw new ArgumentException("Type must have an opposing sign: " + type, nameof(type)) }; } @@ -534,56 +532,67 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) { - return IsImplicitTruncation(value, type, compilation, out _, allowNullableValue); + return CheckImplicitTruncation(value, type, compilation, allowNullableValue) != ImplicitTruncationResult.ValuePreserved; + } + + + internal enum ImplicitTruncationResult : byte + { + /// + /// The value is not implicitly truncated. + /// + ValuePreserved, + /// + /// The value is implicitly truncated. + /// + ValueChanged, + /// + /// The value is implicitly truncated, but the sign of the target type can be changed to remove the truncation. + /// + ValueChangedDueToSignMismatch } /// /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' /// due to implicit truncation. /// - internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, out bool canChangeSign, bool allowNullableValue = false) + internal static ImplicitTruncationResult CheckImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) { - canChangeSign = false; if (!type.IsSmallIntegerType()) { // Implicit truncation in ILAst only happens for small integer types; // other types of implicit truncation in IL cause the ILReader to insert // conv instructions. - return false; + return ImplicitTruncationResult.ValuePreserved; } // With small integer types, test whether the value might be changed by // truncation (based on type.GetSize()) followed by sign/zero extension (based on type.GetSign()). // (it's OK to have false-positives here if we're unsure) if (value.MatchLdcI4(out int val)) { - switch (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) - { - case KnownTypeCode.Boolean: - return !(val == 0 || val == 1); - case KnownTypeCode.Byte: - return !(val >= byte.MinValue && val <= byte.MaxValue); - case KnownTypeCode.SByte: - return !(val >= sbyte.MinValue && val <= sbyte.MaxValue); - case KnownTypeCode.Int16: - return !(val >= short.MinValue && val <= short.MaxValue); - case KnownTypeCode.UInt16: - case KnownTypeCode.Char: - return !(val >= ushort.MinValue && val <= ushort.MaxValue); - } + bool valueFits = (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) switch { + KnownTypeCode.Boolean => val == 0 || val == 1, + KnownTypeCode.Byte => val >= byte.MinValue && val <= byte.MaxValue, + KnownTypeCode.SByte => val >= sbyte.MinValue && val <= sbyte.MaxValue, + KnownTypeCode.Int16 => val >= short.MinValue && val <= short.MaxValue, + KnownTypeCode.UInt16 or KnownTypeCode.Char => val >= ushort.MinValue && val <= ushort.MaxValue, + _ => false + }; + return valueFits ? ImplicitTruncationResult.ValuePreserved : ImplicitTruncationResult.ValueChanged; } else if (value is Conv conv) { PrimitiveType primitiveType = type.ToPrimitiveType(); PrimitiveType convTargetType = conv.TargetType; if (convTargetType == primitiveType) - return false; - if (primitiveType.GetSize() == convTargetType.GetSize() && primitiveType.GetSign() != convTargetType.GetSign()) - canChangeSign = primitiveType.HasOppositeSign(); - return true; + return ImplicitTruncationResult.ValuePreserved; + if (primitiveType.GetSize() == convTargetType.GetSize() && primitiveType.GetSign() != convTargetType.GetSign() && primitiveType.HasOppositeSign()) + return ImplicitTruncationResult.ValueChangedDueToSignMismatch; + return ImplicitTruncationResult.ValueChanged; } else if (value is Comp) { - return false; // comp returns 0 or 1, which always fits + return ImplicitTruncationResult.ValuePreserved; // comp returns 0 or 1, which always fits } else if (value is BinaryNumericInstruction bni) { @@ -594,28 +603,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms case BinaryNumericOperator.BitXor: // If both input values fit into the type without truncation, // the result of a binary operator will also fit. - bool leftIsTruncation = IsImplicitTruncation(bni.Left, type, compilation, out bool leftChangeSign, allowNullableValue); + var leftTruncation = CheckImplicitTruncation(bni.Left, type, compilation, allowNullableValue); // If the left side is truncating and a sign change is not possible we do not need to evaluate the right side - if (leftIsTruncation && !leftChangeSign) - return true; - bool rightIsTruncation = IsImplicitTruncation(bni.Right, type, compilation, out bool rightChangeSign, allowNullableValue); - if (!rightIsTruncation) - return false; - canChangeSign = rightChangeSign; - return true; + if (leftTruncation == ImplicitTruncationResult.ValueChanged) + return ImplicitTruncationResult.ValueChanged; + var rightTruncation = CheckImplicitTruncation(bni.Right, type, compilation, allowNullableValue); + return CommonImplicitTruncation(leftTruncation, rightTruncation); } } else if (value is IfInstruction ifInst) { - bool trueIsTruncation = IsImplicitTruncation(ifInst.TrueInst, type, compilation, out bool trueChangeSign, allowNullableValue); + var trueTruncation = CheckImplicitTruncation(ifInst.TrueInst, type, compilation, allowNullableValue); // If the true branch is truncating and a sign change is not possible we do not need to evaluate the false branch - if (trueIsTruncation && !trueChangeSign) - return true; - bool falseIsTruncation = IsImplicitTruncation(ifInst.FalseInst, type, compilation, out bool falseChangeSign, allowNullableValue); - if (!falseIsTruncation) - return false; - canChangeSign = falseChangeSign; - return true; + if (trueTruncation == ImplicitTruncationResult.ValueChanged) + return ImplicitTruncationResult.ValueChanged; + var falseTruncation = CheckImplicitTruncation(ifInst.FalseInst, type, compilation, allowNullableValue); + return CommonImplicitTruncation(trueTruncation, falseTruncation); } else { @@ -631,13 +634,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool sameSign = inferredPrimitive.GetSign() == primitiveType.GetSign(); if (inferredPrimitive.GetSize() <= primitiveType.GetSize() && sameSign) - return false; - if (inferredPrimitive.GetSize() == primitiveType.GetSize() && !sameSign) - canChangeSign = primitiveType.HasOppositeSign(); - return true; + return ImplicitTruncationResult.ValuePreserved; + if (inferredPrimitive.GetSize() == primitiveType.GetSize() && !sameSign && primitiveType.HasOppositeSign()) + return ImplicitTruncationResult.ValueChangedDueToSignMismatch; + return ImplicitTruncationResult.ValueChanged; } } - return true; + // In unknown cases, assume that the value might be changed by truncation. + return ImplicitTruncationResult.ValueChanged; + } + + private static ImplicitTruncationResult CommonImplicitTruncation(ImplicitTruncationResult left, ImplicitTruncationResult right) + { + if (left == right) + return left; + // Note: in all cases where left!=right, we return ValueChanged: + // if only one side can be fixed by changing the sign, we don't want to change the sign of the other side. + return ImplicitTruncationResult.ValueChanged; } /// @@ -925,13 +938,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms var tmpVar = inst.Variable; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; - if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem, out bool canChangeSign)) + var truncation = CheckImplicitTruncation(inst.Value, targetType, context.TypeSystem); + if (truncation == ImplicitTruncationResult.ValueChanged) + { + // 'stloc tmp' is implicitly truncating the value + return false; + } + if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch) { - // If 'store' is a stobj and 'canChangeSign' is true, then the - // implicit truncation can be skipped by flipping the sign of the `stobj` type. - if (!canChangeSign || store is not StObj stObj || !stObj.Type.Equals(targetType)) + if (!(store is StObj stObj && stObj.Type.Equals(targetType))) { - // 'stloc tmp' is implicitly truncating the value + // We cannot apply the sign change, so we can't fix the truncation return false; } } @@ -955,7 +972,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (!(binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) return false; - if (canChangeSign && store is StObj stObj) + if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch && store is StObj stObj) { // Change the sign of the type to skip implicit truncation stObj.Type = targetType = SwapSign(targetType, context.TypeSystem); @@ -976,11 +993,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); - if (canChangeSign && store is StObj stObj) - { - // Change the sign of the type to skip implicit truncation - stObj.Type = targetType = SwapSign(targetType, context.TypeSystem); - } + Debug.Assert(truncation == ImplicitTruncationResult.ValuePreserved); finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); From c2490d79ec747fee72dff35492d79a6a8d95521f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 28 May 2023 13:59:43 +0200 Subject: [PATCH 173/230] Enable auto-formatting in commit-hook. --- BuildTools/pre-commit | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BuildTools/pre-commit b/BuildTools/pre-commit index 9a77bea3c..a55e3bdc4 100644 --- a/BuildTools/pre-commit +++ b/BuildTools/pre-commit @@ -12,9 +12,9 @@ if [ ! -d "$DOTNET_PATH" ]; then fi "$DOTNET_PATH/dotnet-format.exe" --version -#if git diff --quiet --ignore-submodules; then -# "$DOTNET_PATH/dotnet-format.exe" whitespace --no-restore --verbosity detailed ILSpy.sln -# git add -u -- \*\*.cs -#else +if git diff --quiet --ignore-submodules; then + "$DOTNET_PATH/dotnet-format.exe" whitespace --no-restore --verbosity detailed ILSpy.sln + git add -u -- \*\*.cs +else exec "$DOTNET_PATH/dotnet-format.exe" whitespace --verify-no-changes --no-restore --verbosity detailed ILSpy.sln -#fi +fi From 10129eaf0793c01074e3100f6de383c71494c63e Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sun, 28 May 2023 14:15:40 +0200 Subject: [PATCH 174/230] Create zip for ARM64 published folder --- .github/workflows/build-ilspy.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index 5367782b3..738d76c52 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -78,7 +78,16 @@ jobs: - name: Zip ILSpy (framework-dependent) run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries.zip .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.dll .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.exe .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.config .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.json .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*\ILSpy.resources.dll .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*\ILSpy.ReadyToRun.Plugin.resources.dll - - name: Zip ILSpy Release (self-contained win-x64) + - name: Zip ILSpy Release (arm64 framework-dependent) + if: matrix.configuration == 'release' + shell: pwsh + run: | + dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc + dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc + dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc + 7z a -tzip $env:StagingDirectory\ILSpy_binaries_arm64.zip .\ILSpy\bin\Release\net6.0-windows\win-arm64\publish\nsc\* + + - name: Zip ILSpy Release (win-x64 self-contained) if: matrix.configuration == 'release' shell: pwsh run: | @@ -137,7 +146,7 @@ jobs: path: ${{ env.StagingDirectory }}\ILSpy_binaries.zip if-no-files-found: error - - name: Upload self-contained zip build artifacts (Release-only) + - name: Upload x64 self-contained zip (Release-only) if: matrix.configuration == 'release' uses: actions/upload-artifact@v3 with: @@ -145,6 +154,14 @@ jobs: path: ${{ env.StagingDirectory }}\ILSpy_selfcontained_x64.zip if-no-files-found: error + - name: Upload arm64 framework-dependent zip (Release-only) + if: matrix.configuration == 'release' + uses: actions/upload-artifact@v3 + with: + name: ILSpy arm64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) + path: ${{ env.StagingDirectory }}\ILSpy_binaries_arm64.zip + if-no-files-found: error + - name: Upload installer artifact if: matrix.configuration == 'release' uses: actions/upload-artifact@v3 From 8cc186d59958496a5a714ed5c8a2fc6dc3a9978c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 21 Dec 2022 13:47:44 +0100 Subject: [PATCH 175/230] Use .NET 7.0 as target framework for the tests. This will be necessary for testing newer language features such as `ref` fields. --- .../ICSharpCode.Decompiler.TestRunner.csproj | 2 +- ICSharpCode.Decompiler.Tests/Helpers/Tester.cs | 10 +++++----- README.md | 2 +- global.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj b/ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj index c60a25481..8c8d40ddc 100644 --- a/ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj +++ b/ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj @@ -2,7 +2,7 @@ Exe - net6.0-windows + net7.0 enable diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 3d35779e1..d1a19a620 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -105,9 +105,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers TesterPath = Path.GetDirectoryName(typeof(Tester).Assembly.Location); TestCasePath = Path.Combine(TesterPath, "../../../../TestCases"); #if DEBUG - testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Debug/net6.0-windows"); + testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Debug/net7.0"); #else - testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Release/net6.0-windows"); + testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Release/net7.0"); #endif packagesPropsFile = Path.Combine(TesterPath, "../../../../../packages.props"); roslynLatestVersion = XDocument.Load(packagesPropsFile).XPathSelectElement("//RoslynVersion").Value; @@ -270,8 +270,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NET, - new Version(6, 0), "Microsoft.NETCore.App") - .GetReferenceAssemblyPath(".NETCoreApp,Version=v6.0"); + new Version(7, 0), "Microsoft.NETCore.App") + .GetReferenceAssemblyPath(".NETCoreApp,Version=v7.0"); public static readonly string RefAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2"); @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers const string targetFrameworkAttributeSnippet = @" -[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp,Version=v6.0"", FrameworkDisplayName = """")] +[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp,Version=v7.0"", FrameworkDisplayName = """")] "; diff --git a/README.md b/README.md index 19d4a4dc8..d17665bb0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ How to build - Follow Microsoft's instructions for [importing a configuration](https://docs.microsoft.com/en-us/visualstudio/install/import-export-installation-configurations?view=vs-2022#import-a-configuration), and import the .vsconfig file located at the root of the solution. - Alternatively, you can open the ILSpy solution (ILSpy.sln) and Visual Studio will [prompt you to install the missing components](https://docs.microsoft.com/en-us/visualstudio/install/import-export-installation-configurations?view=vs-2022#automatically-install-missing-components). - Finally, you can manually install the necessary components via the Visual Studio Installer. The workloads/components are as follows: - - Workload ".NET Desktop Development". This workload includes the .NET Framework 4.8 SDK and the .NET Framework 4.7.2 targeting pack, as well as the [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) (ILSpy.csproj targets .NET 6.0, but we have net472 projects too). _Note: The optional components of this workload are not required for ILSpy_ + - Workload ".NET Desktop Development". This workload includes the .NET Framework 4.8 SDK and the .NET Framework 4.7.2 targeting pack, as well as the [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) and [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0) (ILSpy.csproj targets .NET 6.0, but we have net472+net70 projects too). _Note: The optional components of this workload are not required for ILSpy_ - Workload "Visual Studio extension development" (ILSpy.sln contains a VS extension project) _Note: The optional components of this workload are not required for ILSpy_ - Individual Component "MSVC v143 - VS 2022 C++ x64/x86 build tools" (or similar) - _The VC++ toolset is optional_; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. diff --git a/global.json b/global.json index 68d9ecef8..1fcfe4fee 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.200", + "version": "7.0.100", "rollForward": "major", "allowPrerelease": true } From b9f6c88ed066dc124a6ad5f90e79893f711a153c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 23 Dec 2022 08:39:23 +0100 Subject: [PATCH 176/230] Fix parameters for StackTests test case. --- ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index d06c6768e..ed4a3f2bb 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -317,7 +317,7 @@ namespace ICSharpCode.Decompiler.Tests public async Task StackTests() { // IL contains .corflags = 32BITREQUIRED - await RunIL("StackTests.il", asmOptions: AssemblerOptions.Force32Bit); + await RunIL("StackTests.il", CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit); } [Test] @@ -483,6 +483,11 @@ namespace ICSharpCode.Decompiler.Tests string outputFile = null; CompilerResults decompiledOutputFile = null; + bool optionsForce32Bit = options.HasFlag(CompilerOptions.Force32Bit); + bool asmOptionsForce32Bit = asmOptions.HasFlag(AssemblerOptions.Force32Bit); + + Assert.AreEqual(optionsForce32Bit, asmOptionsForce32Bit, "Inconsistent architecture."); + try { options |= CompilerOptions.UseTestRunner; From 263360f3f3c4437cb400e3a61f249ba5dcf92b5e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 23 Dec 2022 08:58:50 +0100 Subject: [PATCH 177/230] Add RemoveCompilerFeatureRequiredAttribute --- .../CSharp/CSharpDecompiler.cs | 25 +++++++++++++++++++ .../Implementation/KnownAttributes.cs | 2 ++ 2 files changed, 27 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index d89d77648..86ae6bf22 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1406,6 +1406,7 @@ namespace ICSharpCode.Decompiler.CSharp attr.Remove(); } } + RemoveCompilerFeatureRequiredAttribute(typeDecl, "RefStructs"); } if (settings.RequiredMembers) { @@ -1851,6 +1852,30 @@ namespace ICSharpCode.Decompiler.CSharp return found; } + internal static bool RemoveCompilerFeatureRequiredAttribute(EntityDeclaration entityDecl, string feature) + { + bool found = false; + foreach (var section in entityDecl.Attributes) + { + foreach (var attr in section.Attributes) + { + var symbol = attr.Type.GetSymbol(); + if (symbol is ITypeDefinition td && td.FullTypeName == KnownAttribute.CompilerFeatureRequired.GetTypeName() + && attr.Arguments.Count == 1 && attr.Arguments.SingleOrDefault() is PrimitiveExpression pe + && pe.Value is string s && s == feature) + { + attr.Remove(); + found = true; + } + } + if (section.Attributes.Count == 0) + { + section.Remove(); + } + } + return found; + } + bool FindAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType, out Syntax.Attribute attribute) { attribute = null; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index d6d99977a..31fba0891 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem None, CompilerGenerated, + CompilerFeatureRequired, /// /// Marks a method as extension method; or a class as containing extension methods. /// @@ -119,6 +120,7 @@ namespace ICSharpCode.Decompiler.TypeSystem static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{ default, new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CompilerGeneratedAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", "CompilerFeatureRequiredAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ExtensionAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), From efeaf1356f5afdda8506209510e31b97bbe97b5c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 14:07:06 +0200 Subject: [PATCH 178/230] Add feature: C#11 nint without NativeIntegerAttribute Because it is no longer possible to distinguish IntPtr from nint, this required a lot of testcase adjustment. --- .../Helpers/Tester.cs | 1 + .../PrettyTestRunner.cs | 3 +- .../TestCases/Pretty/ConstantsTests.cs | 18 +++++++++- .../TestCases/Pretty/DynamicTests.cs | 7 ++++ .../TestCases/Pretty/FunctionPointers.cs | 14 +++++++- .../TestCases/Pretty/LocalFunctions.cs | 4 +-- .../TestCases/Pretty/NativeInts.cs | 36 +++++++++++++++++-- .../TestCases/Pretty/PInvoke.cs | 11 ++++++ .../TestCases/Pretty/UnsafeCode.cs | 17 +++++++++ ICSharpCode.Decompiler/DecompilerSettings.cs | 21 ++++++++++- .../TypeSystem/ApplyAttributeTypeVisitor.cs | 2 +- .../TypeSystem/DecompilerTypeSystem.cs | 14 +++++++- ILSpy/Properties/Resources.Designer.cs | 9 +++++ ILSpy/Properties/Resources.resx | 3 ++ README.md | 4 +-- 15 files changed, 152 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index d1a19a620..6219c2a1a 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -343,6 +343,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers { preprocessorSymbols.Add("NETCORE"); preprocessorSymbols.Add("NET60"); + preprocessorSymbols.Add("NET70"); } preprocessorSymbols.Add("ROSLYN"); preprocessorSymbols.Add("CS60"); diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index d1dfd0774..1a9a911e2 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -326,6 +326,7 @@ namespace ICSharpCode.Decompiler.Tests RemoveDeadStores = (cscOptions == CompilerOptions.None), UseExpressionBodyForCalculatedGetterOnlyProperties = false, FileScopedNamespaces = false, + NumericIntPtr = false, }); } @@ -486,7 +487,7 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public async Task NativeInts([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) + public async Task NativeInts([ValueSource(nameof(roslyn3OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs index 8032484ce..5df746cc0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs @@ -1,10 +1,25 @@ -using System; +#if !(CS110 && NET70) +using System; +#endif using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class ConstantsTests { +#if CS90 + public nint? NullableNInt() + { + return null; + } + + public nuint? NullableNUInt() + { + return null; + } +#endif + +#if !(CS110 && NET70) public IntPtr? NullableIntPtr() { return null; @@ -14,6 +29,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return null; } +#endif public ulong Issue1308(ulong u = 8uL) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs index c56524281..01edd50d2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -436,10 +436,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return true.Equals(a); } +#if CS110 && NET70 + private static nint NewIntPtr(dynamic a) + { + return new nint(a); + } +#else private static IntPtr NewIntPtr(dynamic a) { return new IntPtr(a); } +#endif private static dynamic GetDynamic(int i) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs index faa752bcf..9423c4254 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs @@ -1,4 +1,6 @@ -using System; +#if !(CS110 && NET70) +using System; +#endif using System.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -17,10 +19,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return &Overloaded; } +#if !(CS110 && NET70) public unsafe IntPtr GetAddressAsIntPtr() { return (IntPtr)(delegate*)(&Overloaded); } +#endif + + public unsafe nint GetAddressAsNInt() + { + return (nint)(delegate*)(&Overloaded); + } public unsafe void* GetAddressAsVoidPtr() { @@ -93,6 +102,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty internal class FunctionPointersWithNativeIntegerTypes { public unsafe delegate* F1; + #if !(CS110 && NET70) public unsafe delegate* F2; public unsafe delegate* F3; public unsafe delegate* F4; @@ -100,6 +110,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public unsafe delegate*> F6; public unsafe delegate*, IntPtr> F7; public unsafe delegate*> F8; + public unsafe delegate*> F9; + #endif } internal class FunctionPointersWithRefParams diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index c4d784fd4..677f6ebaf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -844,10 +844,10 @@ namespace LocalFunctions #if CS90 public void Issue2196() { - EnumWindows(IntPtr.Zero, IntPtr.Zero); + EnumWindows(0L, 0L); [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "EnumWindows")] - static extern int EnumWindows(IntPtr hWnd, IntPtr lParam); + static extern int EnumWindows(long hWnd, long lParam); } #endif } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs index 69d3a3e0c..4267944bb 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs @@ -26,23 +26,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private const nint nint_const = 42; private const nuint nuint_const = 99u; +#if CS110 && NET70 + // C#11 on .NET7 no longer uses NativeIntegerAttribute, + // instead nint+IntPtr are considered to be the same type. + private nint intptr; + private nuint uintptr; +#else private IntPtr intptr; private UIntPtr uintptr; +#endif private nint i; private nuint u; private int i32; private uint u32; private long i64; private ulong u64; +#if !(CS110 && NET70) private (IntPtr, nint, UIntPtr, nuint) tuple_field; private (object, int, IntPtr, nint, UIntPtr, nuint) tuple_field2; private Dictionary dict1; private Dictionary dict2; private Dictionary dict3; private Dictionary dict4; +#endif + private Dictionary dict5; public void Convert() { + i = (nint)u; + u = (nuint)i; +#if !(CS110 && NET70) intptr = i; intptr = (nint)u; intptr = (nint)(nuint)uintptr; @@ -58,15 +71,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty u = (nuint)i; u = uintptr; u = (nuint)(nint)intptr; +#endif } public void Convert2() { i32 = (int)i; i = i32; +#if !(CS110 && NET70) intptr = (IntPtr)i32; i64 = (long)intptr; +#endif i64 = i; i = (nint)i64; @@ -79,7 +95,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public void Arithmetic() { +#if !(CS110 && NET70) Console.WriteLine((nint)intptr * 2); +#endif Console.WriteLine(i * 2); Console.WriteLine(i + (nint)u); @@ -155,6 +173,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { GetInstance(3).u *= 2u; } +#if !(CS110 && NET70) GetInstance(4).intptr += (nint)i32; checked { @@ -164,10 +183,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } // multiplication results in compiler-error without the cast GetInstance(6).intptr *= (nint)2; +#endif - GetInstance(7).i <<= i32; + GetInstance(7).i += i32; + GetInstance(8).i <<= i32; } +#if !(CS110 && NET70) public void LocalTypeFromStore() { nint num = 42; @@ -188,10 +210,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty intptr = num3; intptr = intPtr; } - +#endif public void LocalTypeFromUse() { +#if CS110 && NET70 + nint num = intptr; + nint num2 = intptr; + + Console.WriteLine(); + + intptr = num; + i = num2 + 1; +#else IntPtr intPtr = intptr; nint num = intptr; @@ -199,6 +230,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty intptr = intPtr; i = num + 1; +#endif } public nint NegateUnsigned(nuint x) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs index b8483ecc1..6e7ca436a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs @@ -89,6 +89,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } +#if CS110 && NET70 + [DllImport("ws2_32.dll", SetLastError = true)] + internal static extern nint ioctlsocket([In] nint socketHandle, [In] int cmd, [In][Out] ref int argp); + + public void CallMethodWithInOutParameter() + { + int argp = 0; + ioctlsocket(nint.Zero, 0, ref argp); + } +#else [DllImport("ws2_32.dll", SetLastError = true)] internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In][Out] ref int argp); @@ -97,5 +107,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty int argp = 0; ioctlsocket(IntPtr.Zero, 0, ref argp); } +#endif } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index 2bb122eb3..9901fd69e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -30,7 +30,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static object StaticObj; +#if CS110 && NET70 + public nint A; +#else public IntPtr A; +#endif } private struct UnmanagedStruct @@ -605,6 +609,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return value.Integers[(int)s]; } +#if CS90 + private unsafe static void* CastNIntToVoidPtr(nint intptr) + { + return (void*)intptr; + } + + private unsafe static void* CastNIntToVoidPtr(nuint intptr) + { + return (void*)intptr; + } +#endif +#if !(CS110 && NET70) private unsafe static void* CastToVoidPtr(IntPtr intptr) { return (void*)intptr; @@ -614,6 +630,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return (void*)intptr; } +#endif private unsafe static void* CastToVoidPtr(int* intptr) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 4ce0c44c0..fe4789422 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -152,12 +152,13 @@ namespace ICSharpCode.Decompiler parameterNullCheck = false; lifetimeAnnotations = false; requiredMembers = false; + numericIntPtr = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (parameterNullCheck || lifetimeAnnotations || requiredMembers) + if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs) return CSharp.LanguageVersion.CSharp10_0; @@ -211,6 +212,24 @@ namespace ICSharpCode.Decompiler } } + bool numericIntPtr = true; + + /// + /// Treat IntPtr/UIntPtr as nint/nuint. + /// + [Category("C# 11.0 / VS 2022.4")] + [Description("DecompilerSettings.NumericIntPtr")] + public bool NumericIntPtr { + get { return numericIntPtr; } + set { + if (numericIntPtr != value) + { + numericIntPtr = value; + OnPropertyChanged(); + } + } + } + bool covariantReturns = true; /// diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index 5235da470..a6ba7ef15 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { bool hasDynamicAttribute = false; bool[] dynamicAttributeData = null; - bool hasNativeIntegersAttribute = false; + bool hasNativeIntegersAttribute = (options & TypeSystemOptions.NativeIntegersWithoutAttribute) != 0; bool[] nativeIntegersAttributeData = null; string[] tupleElementNames = null; Nullability nullability; diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index b20d1bb0e..5898fa32e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -125,11 +125,17 @@ namespace ICSharpCode.Decompiler.TypeSystem /// LifetimeAnnotations = 0x4000, /// + /// Replace 'IntPtr' types with the 'nint' type even in absence of [NativeIntegerAttribute]. + /// Note: DecompilerTypeSystem constructor removes this setting from the options if + /// not targeting .NET 7 or later. + /// + NativeIntegersWithoutAttribute = 0x8000, + /// /// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods - | NativeIntegers | FunctionPointers | LifetimeAnnotations + | NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegersWithoutAttribute } /// @@ -167,6 +173,8 @@ namespace ICSharpCode.Decompiler.TypeSystem typeSystemOptions |= TypeSystemOptions.FunctionPointers; if (settings.LifetimeAnnotations) typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations; + if (settings.NumericIntPtr) + typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute; return typeSystemOptions; } @@ -304,6 +312,10 @@ namespace ICSharpCode.Decompiler.TypeSystem } } + if (!(identifier == TargetFrameworkIdentifier.NET && version >= new Version(7, 0))) + { + typeSystemOptions &= ~TypeSystemOptions.NativeIntegersWithoutAttribute; + } var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions); var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions)); // Primitive types are necessary to avoid assertions in ILReader. diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 94c50345b..60123a44f 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1108,6 +1108,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Treat (U)IntPtr as n(u)int. + /// + public static string DecompilerSettings_NumericIntPtr { + get { + return ResourceManager.GetString("DecompilerSettings.NumericIntPtr", resourceCulture); + } + } + /// /// Looks up a localized string similar to Object/collection initializer expressions. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 82d434c12..186665d73 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -393,6 +393,9 @@ Are you sure you want to continue? Nullable reference types + + Treat (U)IntPtr as n(u)int + Object/collection initializer expressions diff --git a/README.md b/README.md index d17665bb0..92b843aaf 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Features ------- * Decompilation to C# (check out the [language support status](https://github.com/icsharpcode/ILSpy/issues/829)) - * Whole-project decompilation (csproj, not sln!) + * Whole-project decompilation * Search for types/methods/properties (learn about the [options](https://github.com/icsharpcode/ILSpy/wiki/Search-Options)) * Hyperlink-based type/method/property navigation * Base/Derived types navigation, history @@ -65,7 +65,7 @@ How to build - ILSpy.XPlat.slnf: for the cross-platform CLI or PowerShell cmdlets - ILSpy.AddIn.slnf: for the Visual Studio plugin -**Note:** Visual Studio 16.3 and later include a version of the .NET (Core) SDK that is managed by the Visual Studio installer - once you update, it may get upgraded too. +**Note:** Visual Studio includes a version of the .NET SDK that is managed by the Visual Studio installer - once you update, it may get upgraded too. Please note that ILSpy is only compatible with the .NET 6.0 SDK and Visual Studio will refuse to load some projects in the solution (and unit tests will fail). If this problem occurs, please manually install the .NET 6.0 SDK from [here](https://dotnet.microsoft.com/download/dotnet/6.0). From 9abc2b90da2ce7e1980879eb776ea7e2f0034ebd Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 15:01:25 +0200 Subject: [PATCH 179/230] Fix interaction of C# 11 nint==IntPtr with overload resolution. In C# 11+.NET 7 mode, we now always use type nint, never IntPtr, so that overload resolution works as expected. --- .../PrettyTestRunner.cs | 12 +++---- .../Correctness/OverloadResolution.cs | 31 +++++++++++++++++++ .../TestCases/Pretty/PInvoke.cs | 8 +---- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 15 ++++++++- .../TypeSystem/MetadataModule.cs | 5 +-- .../TypeSystem/TypeSystemExtensions.cs | 2 +- 6 files changed, 55 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 1a9a911e2..c4027fc39 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -321,13 +321,11 @@ namespace ICSharpCode.Decompiler.Tests [Test] public async Task Loops([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { - await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { - // legacy csc generates a dead store in debug builds - RemoveDeadStores = (cscOptions == CompilerOptions.None), - UseExpressionBodyForCalculatedGetterOnlyProperties = false, - FileScopedNamespaces = false, - NumericIntPtr = false, - }); + DecompilerSettings settings = Tester.GetSettings(cscOptions); + // legacy csc generates a dead store in debug builds + settings.RemoveDeadStores = (cscOptions == CompilerOptions.None); + settings.UseExpressionBodyForCalculatedGetterOnlyProperties = false; + await RunForLibrary(cscOptions: cscOptions, decompilerSettings: settings); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs index 6d829ce95..e206872a5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs @@ -36,6 +36,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Issue1747(); CallAmbiguousOutParam(); CallWithInParam(); +#if CS90 + NativeIntTests(new IntPtr(1), 2); +#endif Issue2444.M2(); Issue2741.B.Test(new Issue2741.C()); } @@ -337,6 +340,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness #endif #endregion +#if CS90 + static void NativeIntTests(IntPtr i1, nint i2) + { + Console.WriteLine("NativeIntTests(i1):"); + ObjectOrLong((object)i1); + ObjectOrLong((long)i1); + Console.WriteLine("NativeIntTests(i2):"); + ObjectOrLong((object)i2); + ObjectOrLong((long)i2); + Console.WriteLine("NativeIntTests(new IntPtr):"); + ObjectOrLong((object)new IntPtr(3)); + ObjectOrLong((long)new IntPtr(3)); + Console.WriteLine("NativeIntTests(IntPtr.Zero):"); + ObjectOrLong((object)IntPtr.Zero); + ObjectOrLong((long)IntPtr.Zero); + } + + static void ObjectOrLong(object o) + { + Console.WriteLine("object " + o); + } + + static void ObjectOrLong(long l) + { + Console.WriteLine("long " + l); + } +#endif + #region #2444 public struct Issue2444 { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs index 6e7ca436a..877c8e393 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs @@ -93,20 +93,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty [DllImport("ws2_32.dll", SetLastError = true)] internal static extern nint ioctlsocket([In] nint socketHandle, [In] int cmd, [In][Out] ref int argp); - public void CallMethodWithInOutParameter() - { - int argp = 0; - ioctlsocket(nint.Zero, 0, ref argp); - } #else [DllImport("ws2_32.dll", SetLastError = true)] internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In][Out] ref int argp); - +#endif public void CallMethodWithInOutParameter() { int argp = 0; ioctlsocket(IntPtr.Zero, 0, ref argp); } -#endif } } diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3d53263cd..d256cf625 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1553,12 +1553,25 @@ namespace ICSharpCode.Decompiler.CSharp CastArguments(argumentList.Arguments, argumentList.ExpectedParameters); break; // make sure that we don't not end up in an infinite loop } + IType returnTypeOverride = null; + if (typeSystem.MainModule.TypeSystemOptions.HasFlag(TypeSystemOptions.NativeIntegersWithoutAttribute)) + { + // For DeclaringType, we don't use nint/nuint (so that DeclaringType.GetConstructors etc. works), + // but in NativeIntegersWithoutAttribute mode we must use nint/nuint for expression types, + // so that the appropriate set of conversions is used for further overload resolution. + if (method.DeclaringType.IsKnownType(KnownTypeCode.IntPtr)) + returnTypeOverride = SpecialType.NInt; + else if (method.DeclaringType.IsKnownType(KnownTypeCode.UIntPtr)) + returnTypeOverride = SpecialType.NUInt; + } return new ObjectCreateExpression( expressionBuilder.ConvertType(method.DeclaringType), argumentList.GetArgumentExpressions() ).WithRR(new CSharpInvocationResolveResult( target, method, argumentList.GetArgumentResolveResults().ToArray(), - isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap + isExpandedForm: argumentList.IsExpandedForm, + argumentToParameterMap: argumentList.ArgumentToParameterMap, + returnTypeOverride: returnTypeOverride )); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index cf6ae1be5..3481ac1c8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -402,8 +402,9 @@ namespace ICSharpCode.Decompiler.TypeSystem IType ResolveDeclaringType(EntityHandle declaringTypeReference, GenericContext context) { // resolve without substituting dynamic/tuple types - var ty = ResolveType(declaringTypeReference, context, - options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations)); + const TypeSystemOptions removedOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple + | TypeSystemOptions.NullabilityAnnotations | TypeSystemOptions.NativeIntegers | TypeSystemOptions.NativeIntegersWithoutAttribute; + var ty = ResolveType(declaringTypeReference, context, options & ~removedOptions); // but substitute tuple types in type arguments: ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true); return ty; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 9da26dbe7..3ce5d0180 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -239,7 +239,7 @@ namespace ICSharpCode.Decompiler.TypeSystem bool IsUnmanagedTypeInternal(IType type) { - if (type.Kind is TypeKind.Enum or TypeKind.Pointer or TypeKind.FunctionPointer) + if (type.Kind is TypeKind.Enum or TypeKind.Pointer or TypeKind.FunctionPointer or TypeKind.NInt or TypeKind.NUInt) { return true; } From b823955ad6ee97715a66d070fd8fe0720a7731fa Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 16:52:50 +0200 Subject: [PATCH 180/230] Fix mcs-5 foreach/using pattern when C# 7 (pattern matching) is not enabled. --- .../IL/Transforms/UsingTransform.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index c2a84f46c..ee3b705ea 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -388,7 +388,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms isInlinedIsInst = false; if (condition.MatchCompNotEquals(out var left, out var right)) { - if (left.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType)) + if (left.MatchStLoc(out var inlineAssignVar, out var inlineAssignVal)) + { + if (!inlineAssignVal.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType)) + return false; + if (!inlineAssignVar.IsSingleDefinition || inlineAssignVar.LoadCount != 1) + return false; + if (!inlineAssignVar.Type.IsKnownType(disposeType)) + return false; + isInlinedIsInst = true; + left = arg; + if (!left.MatchLdLoc(objVar) || !right.MatchLdNull()) + return false; + objVar = inlineAssignVar; + return true; + } + else if (left.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType)) { isInlinedIsInst = true; left = arg; From eb2f024b8b5662ff24a1eb0db38c410e284f63d8 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 29 May 2023 14:55:44 +0000 Subject: [PATCH 181/230] Make Installer top-level solution and build it post-publish of ILSpy.sln (#2991) * Remove Installer from main solution * Split installer into separate solution that has to be run after ILSpy.sln has been built and published * Modify build action to account for new sln and correct ordering * Single-line run and release-only installer build * All publishing in ps1, better naming for publish folders --- .github/workflows/build-ilspy.yml | 26 ++++++++++----------- ILSpy.Installer.sln | 31 ++++++++++++++++++++++++++ ILSpy.Installer.slnf | 20 ----------------- ILSpy.Installer/ILSpy.Installer.csproj | 12 ---------- ILSpy.Installer/README.md | 8 +++++++ ILSpy.Installer/setup.cs | 16 +++++++++++-- ILSpy.sln | 16 +++++-------- publish.ps1 | 15 +++++++++++++ 8 files changed, 86 insertions(+), 58 deletions(-) create mode 100644 ILSpy.Installer.sln delete mode 100644 ILSpy.Installer.slnf create mode 100644 ILSpy.Installer/README.md create mode 100644 publish.ps1 diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index 738d76c52..980ba00c7 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -78,23 +78,23 @@ jobs: - name: Zip ILSpy (framework-dependent) run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries.zip .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.dll .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.exe .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.config .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*.json .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*\ILSpy.resources.dll .\ILSpy\bin\${{ matrix.configuration }}\net6.0-windows\*\ILSpy.ReadyToRun.Plugin.resources.dll - - name: Zip ILSpy Release (arm64 framework-dependent) - if: matrix.configuration == 'release' + - name: Publish x64/arm64 framework-dependent/self-contained shell: pwsh - run: | - dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc - dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc - dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o ./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/nsc - 7z a -tzip $env:StagingDirectory\ILSpy_binaries_arm64.zip .\ILSpy\bin\Release\net6.0-windows\win-arm64\publish\nsc\* + run: .\publish.ps1 - - name: Zip ILSpy Release (win-x64 self-contained) + - name: Zip ILSpy Release (x64 self-contained) if: matrix.configuration == 'release' - shell: pwsh + run: 7z a -tzip $env:StagingDirectory\ILSpy_selfcontained_x64.zip .\ILSpy\bin\Release\net6.0-windows\win-x64\publish\selfcontained\* + + - name: Zip ILSpy Release (arm64 framework-dependent) + if: matrix.configuration == 'release' + run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries_arm64.zip .\ILSpy\bin\Release\net6.0-windows\win-arm64\publish\fwdependent\* + + - name: Build Installer (x64 framework-dependent) + if: matrix.configuration == 'release' run: | - dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --self-contained -r win-x64 - dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --self-contained -r win-x64 - dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --self-contained -r win-x64 - 7z a -tzip $env:StagingDirectory\ILSpy_selfcontained_x64.zip .\ILSpy\bin\Release\net6.0-windows\win-x64\publish\* + msbuild ILSpy.Installer.sln /t:Restore /p:Configuration="Release" /p:Platform="Any CPU" + msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" # https://github.com/actions/upload-artifact - name: Upload VSIX (VS 2019) release build artifacts diff --git a/ILSpy.Installer.sln b/ILSpy.Installer.sln new file mode 100644 index 000000000..64b205024 --- /dev/null +++ b/ILSpy.Installer.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33723.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.Installer", "ILSpy.Installer\ILSpy.Installer.csproj", "{D27793B2-C3F9-4410-AAD0-E117BEDCCEB0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{3FE7AE02-D69D-4C76-9BC0-CF700DFD09FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D27793B2-C3F9-4410-AAD0-E117BEDCCEB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D27793B2-C3F9-4410-AAD0-E117BEDCCEB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D27793B2-C3F9-4410-AAD0-E117BEDCCEB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D27793B2-C3F9-4410-AAD0-E117BEDCCEB0}.Release|Any CPU.Build.0 = Release|Any CPU + {3FE7AE02-D69D-4C76-9BC0-CF700DFD09FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FE7AE02-D69D-4C76-9BC0-CF700DFD09FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FE7AE02-D69D-4C76-9BC0-CF700DFD09FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FE7AE02-D69D-4C76-9BC0-CF700DFD09FE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A343F649-2CFB-4022-9133-1BA55FBCE3D1} + EndGlobalSection +EndGlobal diff --git a/ILSpy.Installer.slnf b/ILSpy.Installer.slnf deleted file mode 100644 index cec81502a..000000000 --- a/ILSpy.Installer.slnf +++ /dev/null @@ -1,20 +0,0 @@ -{ - "solution": { - "path": "ILSpy.sln", - "projects": [ - "ICSharpCode.Decompiler.PdbProvider.Cecil\\ICSharpCode.Decompiler.PdbProvider.Cecil.csproj", - "ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj", - "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", - "ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj", - "ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj", - "ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj", - "ILSpy.BamlDecompiler\\ILSpy.BamlDecompiler.csproj", - "ILSpy.ReadyToRun\\ILSpy.ReadyToRun.csproj", - "ILSpy.Tests\\ILSpy.Tests.csproj", - "ILSpy\\ILSpy.csproj", - "ILSpy.Installer\\ILSpy.Installer.csproj", - "SharpTreeView\\ICSharpCode.TreeView.csproj", - "TestPlugin\\TestPlugin.csproj" - ] - } -} \ No newline at end of file diff --git a/ILSpy.Installer/ILSpy.Installer.csproj b/ILSpy.Installer/ILSpy.Installer.csproj index 475d1daee..5e6a1c768 100644 --- a/ILSpy.Installer/ILSpy.Installer.csproj +++ b/ILSpy.Installer/ILSpy.Installer.csproj @@ -13,18 +13,6 @@ - - false - true - - - false - true - - - false - true - diff --git a/ILSpy.Installer/README.md b/ILSpy.Installer/README.md new file mode 100644 index 000000000..f83b37d2c --- /dev/null +++ b/ILSpy.Installer/README.md @@ -0,0 +1,8 @@ +# Building the Installer + +It is mandatory to first publish(.ps1) the respective target platforms, then setup can be built, eg + +``` +msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" +msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" /p:DefineConstants="ARM64" +``` \ No newline at end of file diff --git a/ILSpy.Installer/setup.cs b/ILSpy.Installer/setup.cs index 7376db07c..adc10d6d5 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -19,7 +19,13 @@ namespace ILSpy.Installer #else var buildConfiguration = "Release"; #endif - var buildOutputDir = $@"ILSpy\bin\{buildConfiguration}\net6.0-windows"; + +#if ARM64 + var buildPlatform = "arm64"; +#else + var buildPlatform = "x64"; +#endif + var buildOutputDir = $@"ILSpy\bin\{buildConfiguration}\net6.0-windows\win-{buildPlatform}\publish\fwdependent"; var project = new Project("ILSpy", new InstallDir(@"%LocalAppData%\Programs\ILSpy", @@ -30,6 +36,12 @@ namespace ILSpy.Installer new Files(Path.Combine(buildOutputDir, "ILSpy.resources.dll")), new Files(Path.Combine(buildOutputDir, "ILSpy.ReadyToRun.Plugin.resources.dll")))); +#if ARM64 + project.Platform = Platform.arm64; +#else + project.Platform = Platform.x64; +#endif + project.GUID = new Guid("a12fdab1-731b-4a98-9749-d481ce8692ab"); project.Version = AppPackage.Version; project.SourceBaseDir = Path.GetDirectoryName(Environment.CurrentDirectory); @@ -59,7 +71,7 @@ namespace ILSpy.Installer new FileShortcut("ILSpy", @"%ProgramMenu%") }; - Compiler.BuildMsi(project, Path.Combine(Environment.CurrentDirectory, "wix", $"ILSpy-{AppPackage.Version}.msi")); + Compiler.BuildMsi(project, Path.Combine(Environment.CurrentDirectory, "wix", $"ILSpy-{AppPackage.Version}-{buildPlatform}.msi")); } } } diff --git a/ILSpy.sln b/ILSpy.sln index 898ff6601..369386289 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -38,18 +38,11 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ILSpy.AddIn.Shared", "ILSpy EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.AddIn.VS2022", "ILSpy.AddIn.VS2022\ILSpy.AddIn.VS2022.csproj", "{09A03980-D14A-4705-A38C-741AD7166DEE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.Installer", "ILSpy.Installer\ILSpy.Installer.csproj", "{A4BA0771-DA4A-4A94-A5EC-5BA10B52816F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.TestRunner", "ICSharpCode.Decompiler.TestRunner\ICSharpCode.Decompiler.TestRunner.csproj", "{4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.ILSpyX", "ICSharpCode.ILSpyX\ICSharpCode.ILSpyX.csproj", "{F8EFCF9D-B9A3-4BA0-A1B2-B026A71DAC22}" EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{09a03980-d14a-4705-a38c-741ad7166dee}*SharedItemsImports = 5 - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{9d7be6c0-b7b3-4a50-a54e-18a2d84a3384}*SharedItemsImports = 5 - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{acab1e5d-b3df-4092-aa72-692f8341e520}*SharedItemsImports = 13 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -107,10 +100,6 @@ Global {09A03980-D14A-4705-A38C-741AD7166DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU {09A03980-D14A-4705-A38C-741AD7166DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {09A03980-D14A-4705-A38C-741AD7166DEE}.Release|Any CPU.Build.0 = Release|Any CPU - {A4BA0771-DA4A-4A94-A5EC-5BA10B52816F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4BA0771-DA4A-4A94-A5EC-5BA10B52816F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4BA0771-DA4A-4A94-A5EC-5BA10B52816F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4BA0771-DA4A-4A94-A5EC-5BA10B52816F}.Release|Any CPU.Build.0 = Release|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -126,4 +115,9 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C764218F-7633-4412-923D-558CE7EE0560} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{09a03980-d14a-4705-a38c-741ad7166dee}*SharedItemsImports = 5 + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{9d7be6c0-b7b3-4a50-a54e-18a2d84a3384}*SharedItemsImports = 5 + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{acab1e5d-b3df-4092-aa72-692f8341e520}*SharedItemsImports = 13 + EndGlobalSection EndGlobal diff --git a/publish.ps1 b/publish.ps1 new file mode 100644 index 000000000..c2be01b3d --- /dev/null +++ b/publish.ps1 @@ -0,0 +1,15 @@ +$output_arm64 = "./ILSpy/bin/Release/net6.0-windows/win-arm64/publish/fwdependent" +$output_x64 = "./ILSpy/bin/Release/net6.0-windows/win-x64/publish/fwdependent" +$output_x64_selfcontained = "./ILSpy/bin/Release/net6.0-windows/win-x64/publish/selfcontained" + +dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o $output_arm64 +dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o $output_arm64 +dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --no-self-contained -r win-arm64 -o $output_arm64 + +dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --no-self-contained -r win-x64 -o $output_x64 +dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --no-self-contained -r win-x64 -o $output_x64 +dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --no-self-contained -r win-x64 -o $output_x64 + +dotnet publish ./ILSpy/ILSpy.csproj -c Release --no-restore --self-contained -r win-x64 -o $output_x64_selfcontained +dotnet publish ./ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj -c Release --no-restore --self-contained -r win-x64 -o $output_x64_selfcontained +dotnet publish ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj -c Release --no-restore --self-contained -r win-x64 -o $output_x64_selfcontained \ No newline at end of file From 475f2b3c28f22175ab20e04be66884ae978fc1ab Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 17:53:24 +0200 Subject: [PATCH 182/230] Fix "ref readonly" fields. --- .../ICSharpCode.Decompiler.Tests.csproj | 7 ++-- .../PrettyTestRunner.cs | 6 ++++ .../TestCases/Pretty/Records.cs | 4 ++- .../TestCases/Pretty/RefFields.cs | 36 +++++++++++++++++++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 4 +++ .../IL/Transforms/ILInlining.cs | 2 ++ ICSharpCode.Decompiler/TypeSystem/IField.cs | 5 +++ .../Implementation/AttributeListBuilder.cs | 1 + .../TypeSystem/Implementation/FakeMember.cs | 1 + .../Implementation/MetadataField.cs | 7 ++++ .../Implementation/SpecializedField.cs | 10 ++---- 11 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 52ad5e804..fabc14eba 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -9,7 +9,7 @@ True - 1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632 + 1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632;626;8618;8714;8602 False False @@ -33,11 +33,11 @@ - TRACE;DEBUG;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100 + TRACE;DEBUG;ROSLYN;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100 - TRACE;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100 + TRACE;ROSLYN;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100 @@ -220,6 +220,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index c4027fc39..c0f5e3763 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -538,6 +538,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task RefFields([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task ThrowExpressions([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs index d27914c88..d4d87d49e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -1,5 +1,5 @@ using System; -#if ROSLYN4 +#if CS100 using System.Runtime.InteropServices; #endif @@ -242,6 +242,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } #endif } +#if !NET60 namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] @@ -261,3 +262,4 @@ namespace System.Runtime.CompilerServices { } } +#endif diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs new file mode 100644 index 000000000..55c837ef6 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs @@ -0,0 +1,36 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal ref struct RefFields + { + private ref int Field0; + private ref readonly int Field1; + private readonly ref int Field2; + private readonly ref readonly int Field3; + + public int PropertyAccessingRefFieldByValue { + get { + return Field0; + } + set { + Field0 = value; + } + } + + public ref int PropertyReturningRefFieldByReference => ref Field0; + + public void Uses(int[] array) + { + Field1 = ref array[0]; + Field2 = array[0]; + } + + public void ReadonlyLocal() + { + ref readonly int field = ref Field1; + Console.WriteLine("No inlining"); + field.ToString(); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index fb8f21a6f..b377aa0d4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1970,6 +1970,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.AddAnnotation(new MemberResolveResult(null, field)); } decl.ReturnType = ConvertType(field.ReturnType); + if (decl.ReturnType is ComposedType ct && ct.HasRefSpecifier && field.ReturnTypeIsRefReadOnly) + { + ct.HasReadOnlySpecifier = true; + } Expression initializer = null; if (field.IsConst && this.ShowConstantValues) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index b77494f46..e357dfa44 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -494,6 +494,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // C# doesn't allow mutation of value-type temporaries return true; default: + if (addr.MatchLdFld(out _, out var field)) + return field.ReturnTypeIsRefReadOnly; return false; } } diff --git a/ICSharpCode.Decompiler/TypeSystem/IField.cs b/ICSharpCode.Decompiler/TypeSystem/IField.cs index 49a92fd26..42e13c40e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IField.cs @@ -35,6 +35,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// bool IsReadOnly { get; } + /// + /// Gets whether the field type is 'ref readonly'. + /// + bool ReturnTypeIsRefReadOnly { get; } + /// /// Gets whether this field is volatile. /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index d6b786201..405fff29b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation case SymbolKind.ReturnType: case SymbolKind.Property: case SymbolKind.Indexer: + case SymbolKind.Field: return true; // "ref readonly" is currently always active default: return false; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs index 2c4a2f67e..ca9b556d7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -109,6 +109,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } bool IField.IsReadOnly => false; + bool IField.ReturnTypeIsRefReadOnly => false; bool IField.IsVolatile => false; bool IVariable.IsConst => false; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs index c8e045753..d561e3be2 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -189,6 +189,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Field); } + public bool ReturnTypeIsRefReadOnly { + get { + var def = module.metadata.GetFieldDefinition(handle); + return def.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly); + } + } + public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; public string Namespace => DeclaringType?.Namespace ?? string.Empty; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs index ac0dc9c40..fe4dc9ccc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs @@ -45,13 +45,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation AddSubstitution(substitution); } - public bool IsReadOnly { - get { return fieldDefinition.IsReadOnly; } - } - - public bool IsVolatile { - get { return fieldDefinition.IsVolatile; } - } + public bool IsReadOnly => fieldDefinition.IsReadOnly; + public bool ReturnTypeIsRefReadOnly => fieldDefinition.ReturnTypeIsRefReadOnly; + public bool IsVolatile => fieldDefinition.IsVolatile; IType IVariable.Type { get { return this.ReturnType; } From 9359d47c0b06097e7c928882a108872ca54f18cd Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 18:59:49 +0200 Subject: [PATCH 183/230] Add support for ScopedRefAttribute --- .../TestCases/Pretty/RefFields.cs | 65 +++++++++++++++++-- .../OutputVisitor/CSharpOutputVisitor.cs | 5 -- .../TypeMembers/ParameterDeclaration.cs | 13 ++-- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 1 - .../Transforms/EscapeInvalidIdentifiers.cs | 1 + ICSharpCode.Decompiler/DecompilerSettings.cs | 3 +- .../TypeSystem/DecompilerTypeSystem.cs | 2 +- .../TypeSystem/IParameter.cs | 6 ++ .../Implementation/AttributeListBuilder.cs | 2 +- .../Implementation/KnownAttributes.cs | 4 +- .../Implementation/MetadataParameter.cs | 19 +----- 11 files changed, 79 insertions(+), 42 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs index 55c837ef6..aff38438b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs @@ -2,18 +2,61 @@ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { + internal class LifetimeTests + { + private static int staticField; + + public Span CreateWithoutCapture(scoped ref int value) + { + // Okay: value is not captured + return new Span(ref staticField); + } + + public Span CreateAndCapture(ref int value) + { + // Okay: value Rule 3 specifies that the safe-to-escape be limited to the ref-safe-to-escape + // of the ref argument. That is the *calling method* for value hence this is not allowed. + return new Span(ref value); + } + + public Span ScopedRefSpan(scoped ref Span span) + { + return span; + } + + public Span ScopedSpan(scoped Span span) + { + return default(Span); + } + + public void OutSpan(out Span span) + { + span = default(Span); + } + + public void Calls() + { + int value = 0; + Span span = CreateWithoutCapture(ref value); + //span = CreateAndCapture(ref value); -- would need scoped local, not yet implemented + span = ScopedRefSpan(ref span); + span = ScopedSpan(span); + OutSpan(out span); + } + } + internal ref struct RefFields { - private ref int Field0; - private ref readonly int Field1; - private readonly ref int Field2; - private readonly ref readonly int Field3; + public ref int Field0; + public ref readonly int Field1; + public readonly ref int Field2; + public readonly ref readonly int Field3; public int PropertyAccessingRefFieldByValue { - get { + get { return Field0; } - set { + set { Field0 = value; } } @@ -32,5 +75,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("No inlining"); field.ToString(); } + + public RefFields(ref int v) + { + Field0 = ref v; + Field1 = ref v; + Field2 = ref v; + Field3 = ref v; + } } -} \ No newline at end of file +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 633578c0c..4ce9341d5 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2589,11 +2589,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(); break; } - if (parameterDeclaration.IsValueScoped) - { - WriteKeyword(ParameterDeclaration.ValueScopedRole); - Space(); - } parameterDeclaration.Type.AcceptVisitor(this); if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index 8e1dca003..eb9f29ab9 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -26,6 +26,8 @@ #nullable enable +using System; + namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum ParameterModifier @@ -46,6 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole RefModifierRole = new TokenRole("ref"); public static readonly TokenRole OutModifierRole = new TokenRole("out"); public static readonly TokenRole InModifierRole = new TokenRole("in"); + [Obsolete("C# 11 preview: \"ref scoped\" no longer supported")] public static readonly TokenRole ValueScopedRole = new TokenRole("scoped"); public static readonly TokenRole ParamsModifierRole = new TokenRole("params"); @@ -102,7 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } bool hasThisModifier; - bool isRefScoped, isValueScoped; + bool isRefScoped; public CSharpTokenNode ThisKeyword { get { @@ -130,12 +133,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } + [Obsolete("C# 11 preview: \"ref scoped\" no longer supported")] public bool IsValueScoped { - get { return isValueScoped; } - set { - ThrowIfFrozen(); - isValueScoped = value; - } + get { return false; } + set { } } ParameterModifier parameterModifier; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index b377aa0d4..21965ff6d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1655,7 +1655,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.ParameterModifier = ParameterModifier.Params; } decl.IsRefScoped = parameter.Lifetime.RefScoped; - decl.IsValueScoped = parameter.Lifetime.ValueScoped; if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes())); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs index 1b57569ef..2b1dc7f1d 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs @@ -167,6 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms "System.Runtime.CompilerServices.NullableContextAttribute", "System.Runtime.CompilerServices.NativeIntegerAttribute", "System.Runtime.CompilerServices.RefSafetyRulesAttribute", + "System.Runtime.CompilerServices.ScopedRefAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute", }; diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index fe4789422..a7a9918ce 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -360,8 +360,7 @@ namespace ICSharpCode.Decompiler bool lifetimeAnnotations = true; /// - /// Use C# 9 delegate* unmanaged types. - /// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`. + /// Use C# 11 scoped modifier. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.LifetimeAnnotations")] diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 5898fa32e..b24e20d68 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// FunctionPointers = 0x2000, /// - /// Allow C# 11 scoped annotation. If this option is not enabled, LifetimeAnnotationAttribute + /// Allow C# 11 scoped annotation. If this option is not enabled, ScopedRefAttribute /// will be reported as custom attribute. /// LifetimeAnnotations = 0x4000, diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 59025af0f..4348e0929 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -18,6 +18,7 @@ #nullable enable +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -36,7 +37,12 @@ namespace ICSharpCode.Decompiler.TypeSystem public struct LifetimeAnnotation { + /// + /// C# 11 scoped annotation: "scoped ref" (ScopedRefAttribute) + /// public bool RefScoped; + + [Obsolete("C# 11 preview: \"ref scoped\" no longer supported")] public bool ValueScoped; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 405fff29b..7becbdc5a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation case "NullableContextAttribute": return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || IsMethodLike(target)); - case "LifetimeAnnotationAttribute": + case "ScopedRefAttribute": return (options & TypeSystemOptions.LifetimeAnnotations) != 0 && (target == SymbolKind.Parameter); default: diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 31fba0891..59dfc5863 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.TypeSystem CallerMemberName, CallerFilePath, CallerLineNumber, - LifetimeAnnotation, + ScopedRef, // Type parameter attributes: IsUnmanaged, @@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.TypeSystem new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)), - new TopLevelTypeName("System.Runtime.CompilerServices", "LifetimeAnnotationAttribute"), + new TopLevelTypeName("System.Runtime.CompilerServices", "ScopedRefAttribute"), // Type parameter attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "IsUnmanagedAttribute"), // Marshalling attributes: diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 506aab564..343cc8cfe 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -124,25 +124,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); - foreach (var h in parameterDef.GetCustomAttributes()) + if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef)) { - var custom = metadata.GetCustomAttribute(h); - if (!custom.IsKnownAttribute(metadata, KnownAttribute.LifetimeAnnotation)) - continue; - - var value = custom.DecodeValue(module.TypeProvider); - if (value.FixedArguments.Length != 2) - continue; - if (value.FixedArguments[0].Value is bool refScoped - && value.FixedArguments[1].Value is bool valueScoped) - { - return new LifetimeAnnotation { - RefScoped = refScoped, - ValueScoped = valueScoped - }; - } + return new LifetimeAnnotation { RefScoped = true }; } - return default; } } From 3f0995892245bb598c1e5bdfc8a44b6b5dc89f51 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 21:50:26 +0200 Subject: [PATCH 184/230] Fix #2987: `(nuint)(-3)` is not a compile-time constant. --- .../TestCases/Pretty/NativeInts.cs | 5 +++++ .../CSharp/Resolver/CSharpResolver.cs | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs index 69d3a3e0c..549fd42ee 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs @@ -206,6 +206,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return (nint)(0 - x); } + public bool CompareToMinus3(nuint x) + { + return x == unchecked((nuint)(-3)); + } + public nint SignedNotFittingIn32Bits() { // Explicit `unchecked` is necessary when casting oversized constant to nint diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index 70b066dc8..1e6d5df8b 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -1223,14 +1223,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver ResolveResult rr = ResolveCast(targetType, expression); if (rr.IsError) return rr; - Debug.Assert(rr.IsCompileTimeConstant); - return new ConstantResolveResult(nullableType, rr.ConstantValue); - } - else - { - return Convert(expression, nullableType, - isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); + if (rr.IsCompileTimeConstant) + return new ConstantResolveResult(nullableType, rr.ConstantValue); } + return Convert(expression, nullableType, + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } #endregion From f568123704f1a458035496a8d5f68c8645e32151 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 May 2023 21:32:07 +0200 Subject: [PATCH 185/230] Add support for UTF8 string literals --- .../TestCases/Pretty/InitializerTests.cs | 3 + .../CSharp/ExpressionBuilder.cs | 8 +++ .../InsertRequiredSpacesDecorator.cs | 5 +- .../OutputVisitor/TextWriterTokenWriter.cs | 6 ++ .../Syntax/Expressions/PrimitiveExpression.cs | 1 + ICSharpCode.Decompiler/DecompilerSettings.cs | 24 ++++++- ICSharpCode.Decompiler/IL/Instructions.cs | 65 +++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 2 + .../IL/Transforms/ExpressionTransforms.cs | 4 +- .../Transforms/ParameterNullCheckTransform.cs | 2 + .../Transforms/TransformArrayInitializers.cs | 39 +++++++++-- .../IL/Transforms/UsingTransform.cs | 2 +- ILSpy/Properties/Resources.Designer.cs | 10 +++ ILSpy/Properties/Resources.resx | 7 +- 14 files changed, 166 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs index 8feb4fdf5..3844d22cb 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -382,6 +382,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests public static ReadOnlySpan StaticData3 => new byte[3] { 1, 2, 3 }; public static Span StaticData3Span => new byte[3] { 1, 2, 3 }; +#endif +#if CS110 && !NET40 + public static ReadOnlySpan UTF8Literal => "Hello, world!"u8; #endif #endregion diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index fb571fce2..0972105f1 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -617,6 +617,14 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value)); } + protected internal override TranslatedExpression VisitLdStrUtf8(LdStrUtf8 inst, TranslationContext context) + { + var type = new ParameterizedType(compilation.FindType(KnownTypeCode.ReadOnlySpanOfT), new[] { compilation.FindType(KnownTypeCode.Byte) }); + return new PrimitiveExpression(inst.Value, LiteralFormat.Utf8Literal) + .WithILInstruction(inst) + .WithRR(new ConstantResolveResult(type, inst.Value)); + } + protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context) { return GetDefaultValueExpression(SpecialType.NullType).WithILInstruction(inst); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs index ede737fd9..6da12dd60 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -159,7 +159,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return; if (value is string) { - lastWritten = LastWritten.Other; + if (format == LiteralFormat.VerbatimStringLiteral) + lastWritten = LastWritten.KeywordOrIdentifier; + else + lastWritten = LastWritten.Other; } else if (value is char) { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index fc837a163..e305cb98e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -269,6 +269,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor textWriter.Write('"'); textWriter.Write(tmp); textWriter.Write('"'); + if (format == LiteralFormat.Utf8Literal) + { + textWriter.Write("u8"); + column += 2; + Length += 2; + } } else if (value is char) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs index 1e2138338..a1e8fcc3b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs @@ -42,6 +42,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax StringLiteral, VerbatimStringLiteral, CharLiteral, + Utf8Literal, } /// diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index a7a9918ce..0014824ca 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -153,12 +153,13 @@ namespace ICSharpCode.Decompiler lifetimeAnnotations = false; requiredMembers = false; numericIntPtr = false; + utf8StringLiterals = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr) + if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr || utf8StringLiterals) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs) return CSharp.LanguageVersion.CSharp10_0; @@ -434,9 +435,10 @@ namespace ICSharpCode.Decompiler /// /// Use C# 11 preview parameter null-checking (string param!!). /// - [Category("C# 11.0 / VS 2022.1")] + [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.ParameterNullCheck")] [Browsable(false)] + [Obsolete("This feature did not make it into C# 11, and may be removed in a future version of the decompiler.")] public bool ParameterNullCheck { get { return parameterNullCheck; } set { @@ -1173,6 +1175,24 @@ namespace ICSharpCode.Decompiler } } + bool utf8StringLiterals = true; + + /// + /// Gets/Sets whether to use C# 11.0 UTF-8 string literals + /// + [Category("C# 11.0 / VS 2022.4")] + [Description("DecompilerSettings.Utf8StringLiterals")] + public bool Utf8StringLiterals { + get { return utf8StringLiterals; } + set { + if (utf8StringLiterals != value) + { + utf8StringLiterals = value; + OnPropertyChanged(); + } + } + } + bool showXmlDocumentation = true; /// diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index ab6766660..c4ce2d8af 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -19,6 +19,7 @@ #nullable enable using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -121,6 +122,8 @@ namespace ICSharpCode.Decompiler.IL NullableRewrap, /// Loads a constant string. LdStr, + /// Loads a constant byte string (as ReadOnlySpan<byte>). + LdStrUtf8, /// Loads a constant 32-bit integer. LdcI4, /// Loads a constant 64-bit integer. @@ -2838,6 +2841,43 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// Loads a constant byte string (as ReadOnlySpan<byte>). + public sealed partial class LdStrUtf8 : SimpleInstruction + { + public LdStrUtf8(string value) : base(OpCode.LdStrUtf8) + { + this.Value = value; + } + public readonly string Value; + public override StackType ResultType { get { return StackType.O; } } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + Disassembler.DisassemblerHelpers.WriteOperand(output, Value); + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitLdStrUtf8(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitLdStrUtf8(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitLdStrUtf8(this, context); + } + protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) + { + var o = other as LdStrUtf8; + return o != null && this.Value == o.Value; + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Loads a constant 32-bit integer. public sealed partial class LdcI4 : SimpleInstruction @@ -6947,6 +6987,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitLdStrUtf8(LdStrUtf8 inst) + { + Default(inst); + } protected internal virtual void VisitLdcI4(LdcI4 inst) { Default(inst); @@ -7345,6 +7389,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst) + { + return Default(inst); + } protected internal virtual T VisitLdcI4(LdcI4 inst) { return Default(inst); @@ -7743,6 +7791,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitLdcI4(LdcI4 inst, C context) { return Default(inst, context); @@ -8013,6 +8065,7 @@ namespace ICSharpCode.Decompiler.IL "nullable.unwrap", "nullable.rewrap", "ldstr", + "ldstr.utf8", "ldc.i4", "ldc.i8", "ldc.f4", @@ -8285,6 +8338,17 @@ namespace ICSharpCode.Decompiler.IL value = default(string); return false; } + public bool MatchLdStrUtf8([NotNullWhen(true)] out string? value) + { + var inst = this as LdStrUtf8; + if (inst != null) + { + value = inst.Value; + return true; + } + value = default(string); + return false; + } public bool MatchLdcI4(out int value) { var inst = this as LdcI4; @@ -8751,3 +8815,4 @@ namespace ICSharpCode.Decompiler.IL } } } + diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 18a4cf4c4..72da23a16 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -204,6 +204,8 @@ new OpCode("ldstr", "Loads a constant string.", CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")), + new OpCode("ldstr.utf8", "Loads a constant byte string (as ReadOnlySpan<byte>).", + CustomClassName("LdStrUtf8"), LoadConstant("string"), ResultType("O")), new OpCode("ldc.i4", "Loads a constant 32-bit integer.", LoadConstant("int"), ResultType("I4")), new OpCode("ldc.i8", "Loads a constant 64-bit integer.", diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 296145397..8a6684c4e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -313,10 +313,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInlining.InlineIfPossible(block, stmt.ChildIndex, context); return; } - if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out block)) + if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out var replacement)) { context.Step("TransformSpanTArrayInitialization: single-dim", inst); - inst.ReplaceWith(block); + inst.ReplaceWith(replacement); return; } if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs index 8e0b26fb9..7deb3d9ea 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs @@ -31,8 +31,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms { void IILTransform.Run(ILFunction function, ILTransformContext context) { +#pragma warning disable 618 // ParameterNullCheck is obsolete if (!context.Settings.ParameterNullCheck) return; +#pragma warning restore 618 // we only need to look at the entry-point as parameter null-checks // do not produce any IL control-flow instructions Block entryPoint = ((BlockContainer)function.Body).EntryPoint; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 3bb5cc797..1944872a8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; +using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -113,9 +114,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out Block block) + internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out ILInstruction replacement) { - block = null; + replacement = null; if (!context.Settings.ArrayInitializers) return false; if (MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size)) @@ -124,10 +125,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms { var valuesList = new List(); var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem); + if (context.Settings.Utf8StringLiterals && + elementType.IsKnownType(KnownTypeCode.Byte) && + DecodeUTF8String(initialValue, size, out string text)) + { + replacement = new LdStrUtf8(text); + return true; + } if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList)) { var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType)); - block = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray()); + replacement = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray()); return true; } } @@ -135,13 +143,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out string text) + { + if (size > blob.RemainingBytes) + { + text = null; + return false; + } + for (int i = 0; i < size; i++) + { + byte val = blob.CurrentPointer[i]; + // If the string has control characters, it's probably binary data and not a string. + if (val < 0x20 && val is not ((byte)'\r' or (byte)'\n' or (byte)'\t')) + { + text = null; + return false; + } + } + text = Encoding.UTF8.GetString(blob.CurrentPointer, size); + // Only use UTF8 string literal if we can perfectly roundtrip the data + byte[] bytes = Encoding.UTF8.GetBytes(text); + return MemoryExtensions.SequenceEqual(bytes, new ReadOnlySpan(blob.CurrentPointer, size)); + } + static bool MatchSpanTCtorWithPointerAndSize(NewObj newObj, StatementTransformContext context, out IType elementType, out FieldDefinition field, out int size) { field = default; size = default; elementType = null; IType type = newObj.Method.DeclaringType; - if (!type.IsKnownType(KnownTypeCode.SpanOfT) && !type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) + if (!type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) return false; if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1) return false; diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index ee3b705ea..2025d390b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (left.MatchStLoc(out var inlineAssignVar, out var inlineAssignVal)) { - if (!inlineAssignVal.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType)) + if (!inlineAssignVal.MatchIsInst(out var arg, out var type) || !type.IsKnownType(disposeType)) return false; if (!inlineAssignVar.IsSingleDefinition || inlineAssignVar.LoadCount != 1) return false; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 60123a44f..981654695 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1,6 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -1477,6 +1478,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to UTF-8 string literals. + /// + public static string DecompilerSettings_Utf8StringLiterals { + get { + return ResourceManager.GetString("DecompilerSettings.Utf8StringLiterals", resourceCulture); + } + } + /// /// Looks up a localized string similar to VB-specific options. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 186665d73..5a76bc5aa 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -516,6 +516,9 @@ Are you sure you want to continue? Use variable names from debug symbols, if available + + UTF-8 string literals + VB-specific options @@ -544,7 +547,7 @@ Are you sure you want to continue? Font: - Theme: + Theme: Download @@ -911,7 +914,7 @@ Do you want to continue? Tab size: - Theme + Theme Toggle All Folding From 3dc2f3d5b6ccc7d397ae9628439a27bce803bfba Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 30 May 2023 11:32:25 +0200 Subject: [PATCH 186/230] Rename lifetime annotation to `ScopedRef` --- .../OutputVisitor/CSharpOutputVisitor.cs | 4 ++-- .../TypeMembers/ParameterDeclaration.cs | 19 ++++++++++++---- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 2 +- ICSharpCode.Decompiler/DecompilerSettings.cs | 22 ++++++++++++------- .../TypeSystem/DecompilerTypeSystem.cs | 10 +++++---- .../TypeSystem/IParameter.cs | 8 +++++++ .../Implementation/AttributeListBuilder.cs | 2 +- .../Implementation/MetadataParameter.cs | 4 ++-- ILSpy/Properties/Resources.Designer.cs | 18 +++++++-------- ILSpy/Properties/Resources.resx | 6 ++--- 10 files changed, 61 insertions(+), 34 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 4ce9341d5..ce48d69ca 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2565,9 +2565,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteKeyword(ParameterDeclaration.ThisModifierRole); Space(); } - if (parameterDeclaration.IsRefScoped) + if (parameterDeclaration.IsScopedRef) { - WriteKeyword(ParameterDeclaration.RefScopedRole); + WriteKeyword(ParameterDeclaration.ScopedRefRole); Space(); } switch (parameterDeclaration.ParameterModifier) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index eb9f29ab9..a86945d66 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -44,7 +44,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole ThisModifierRole = new TokenRole("this"); - public static readonly TokenRole RefScopedRole = new TokenRole("scoped"); + public static readonly TokenRole ScopedRefRole = new TokenRole("scoped"); + [Obsolete("Renamed to ScopedRefRole")] + public static readonly TokenRole RefScopedRole = ScopedRefRole; public static readonly TokenRole RefModifierRole = new TokenRole("ref"); public static readonly TokenRole OutModifierRole = new TokenRole("out"); public static readonly TokenRole InModifierRole = new TokenRole("in"); @@ -105,7 +107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } bool hasThisModifier; - bool isRefScoped; + bool isScopedRef; public CSharpTokenNode ThisKeyword { get { @@ -125,11 +127,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } + public bool IsScopedRef { + get { return isScopedRef; } + set { + ThrowIfFrozen(); + isScopedRef = value; + } + } + + [Obsolete("Renamed to IsScopedRef")] public bool IsRefScoped { - get { return isRefScoped; } + get { return isScopedRef; } set { ThrowIfFrozen(); - isRefScoped = value; + isScopedRef = value; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 21965ff6d..3751c4454 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1654,7 +1654,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { decl.ParameterModifier = ParameterModifier.Params; } - decl.IsRefScoped = parameter.Lifetime.RefScoped; + decl.IsScopedRef = parameter.Lifetime.ScopedRef; if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes())); diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 0014824ca..2a8e2a4ae 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -150,7 +150,7 @@ namespace ICSharpCode.Decompiler if (languageVersion < CSharp.LanguageVersion.CSharp11_0) { parameterNullCheck = false; - lifetimeAnnotations = false; + scopedRef = false; requiredMembers = false; numericIntPtr = false; utf8StringLiterals = false; @@ -159,7 +159,7 @@ namespace ICSharpCode.Decompiler public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr || utf8StringLiterals) + if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs) return CSharp.LanguageVersion.CSharp10_0; @@ -358,24 +358,30 @@ namespace ICSharpCode.Decompiler } } - bool lifetimeAnnotations = true; + bool scopedRef = true; /// /// Use C# 11 scoped modifier. /// [Category("C# 11.0 / VS 2022.4")] - [Description("DecompilerSettings.LifetimeAnnotations")] - public bool LifetimeAnnotations { - get { return lifetimeAnnotations; } + [Description("DecompilerSettings.ScopedRef")] + public bool ScopedRef { + get { return scopedRef; } set { - if (lifetimeAnnotations != value) + if (scopedRef != value) { - lifetimeAnnotations = value; + scopedRef = value; OnPropertyChanged(); } } } + [Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")] + public bool LifetimeAnnotations { + get { return ScopedRef; } + set { ScopedRef = value; } + } + bool requiredMembers = true; /// diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index b24e20d68..5a46088f6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -123,7 +123,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Allow C# 11 scoped annotation. If this option is not enabled, ScopedRefAttribute /// will be reported as custom attribute. /// - LifetimeAnnotations = 0x4000, + ScopedRef = 0x4000, + [Obsolete("Use ScopedRef instead")] + LifetimeAnnotations = ScopedRef, /// /// Replace 'IntPtr' types with the 'nint' type even in absence of [NativeIntegerAttribute]. /// Note: DecompilerTypeSystem constructor removes this setting from the options if @@ -135,7 +137,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods - | NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegersWithoutAttribute + | NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute } /// @@ -171,8 +173,8 @@ namespace ICSharpCode.Decompiler.TypeSystem typeSystemOptions |= TypeSystemOptions.NativeIntegers; if (settings.FunctionPointers) typeSystemOptions |= TypeSystemOptions.FunctionPointers; - if (settings.LifetimeAnnotations) - typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations; + if (settings.ScopedRef) + typeSystemOptions |= TypeSystemOptions.ScopedRef; if (settings.NumericIntPtr) typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute; return typeSystemOptions; diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 4348e0929..aee0078a9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -40,6 +40,14 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// C# 11 scoped annotation: "scoped ref" (ScopedRefAttribute) /// + public bool ScopedRef { +#pragma warning disable 618 + get { return RefScoped; } + set { RefScoped = value; } +#pragma warning restore 618 + } + + [Obsolete("Use ScopedRef property instead of directly accessing this field")] public bool RefScoped; [Obsolete("C# 11 preview: \"ref scoped\" no longer supported")] diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 7becbdc5a..cea813250 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -253,7 +253,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || IsMethodLike(target)); case "ScopedRefAttribute": - return (options & TypeSystemOptions.LifetimeAnnotations) != 0 + return (options & TypeSystemOptions.ScopedRef) != 0 && (target == SymbolKind.Parameter); default: return false; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 343cc8cfe..c2cba9824 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public LifetimeAnnotation Lifetime { get { - if ((module.TypeSystemOptions & TypeSystemOptions.LifetimeAnnotations) == 0) + if ((module.TypeSystemOptions & TypeSystemOptions.ScopedRef) == 0) { return default; } @@ -126,7 +126,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var parameterDef = metadata.GetParameter(handle); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef)) { - return new LifetimeAnnotation { RefScoped = true }; + return new LifetimeAnnotation { ScopedRef = true }; } return default; } diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 981654695..0e93e78f5 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1073,15 +1073,6 @@ namespace ICSharpCode.ILSpy.Properties { } } - /// - /// Looks up a localized string similar to 'scoped' lifetime annotation. - /// - public static string DecompilerSettings_LifetimeAnnotations { - get { - return ResourceManager.GetString("DecompilerSettings.LifetimeAnnotations", resourceCulture); - } - } - /// /// Looks up a localized string similar to Use nint/nuint types. /// @@ -1235,6 +1226,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to 'scoped' lifetime annotation. + /// + public static string DecompilerSettings_ScopedRef { + get { + return ResourceManager.GetString("DecompilerSettings.ScopedRef", resourceCulture); + } + } + /// /// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -> int x; x = 5;), if possible. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 5a76bc5aa..ffa61461d 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -381,9 +381,6 @@ Are you sure you want to continue? IsUnmanagedAttribute on type parameters should be replaced with 'unmanaged' constraints - - 'scoped' lifetime annotation - Use nint/nuint types @@ -435,6 +432,9 @@ Are you sure you want to continue? Required members + + 'scoped' lifetime annotation + Separate local variable declarations and initializers (int x = 5; -> int x; x = 5;), if possible From 9bfec8cf984050bafd0e8ce06d365ee5415a8410 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 30 May 2023 13:26:28 +0200 Subject: [PATCH 187/230] Fix #2860 --- .../TestCases/Pretty/NullableRefTypes.cs | 28 +++++++++++++++++++ .../TypeSystem/Implementation/AbstractType.cs | 3 +- .../Implementation/MetadataTypeDefinition.cs | 2 +- .../NullabilityAnnotatedType.cs | 3 +- .../TypeSystem/SpecialType.cs | 2 +- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index 49d191b08..545017357 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -111,4 +111,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private Entry[]? _entries; private IEqualityComparer? _comparer; } + + public class T05_NullableUnconstrainedGeneric + { + public static TValue? Default() + { + return default(TValue); + } + + public static void CallDefault() + { +#if OPT + string? format = Default(); +#else + // With optimizations it's a stack slot, so ILSpy picks a nullable type. + // Without optimizations it's a local, so the nullability is missing. + string format = Default(); +#endif + int num = Default(); +#if CS110 && NET70 + nint num2 = Default(); +#else + int num2 = Default(); +#endif + (object, string) tuple = Default<(object, string)>(); + Console.WriteLine("No inlining"); + Console.WriteLine(format, num, num2, tuple); + } + } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs index 3d7d1e115..b6b3bef3a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public virtual IType ChangeNullability(Nullability nullability) { - Debug.Assert(nullability == Nullability.Oblivious); + // Only some types support nullability, in the default implementation + // we just ignore the nullability change. return this; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 1cb4c5954..3eff3c142 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType ChangeNullability(Nullability nullability) { - if (nullability == Nullability.Oblivious) + if (nullability == Nullability.Oblivious || IsReferenceType == false) return this; else return new NullabilityAnnotatedType(this, nullability); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index 7a41c50c0..41ae7ee70 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -18,9 +18,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation Debug.Assert(nullability != Nullability.Oblivious); // Due to IType -> concrete type casts all over the type system, we can insert // the NullabilityAnnotatedType wrapper only in some limited places. - Debug.Assert(type is ITypeDefinition + Debug.Assert(type is ITypeDefinition { IsReferenceType: not false } || type.Kind == TypeKind.Dynamic - || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } diff --git a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs index 12601f9a4..c7cf02428 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public override IType ChangeNullability(Nullability nullability) { - if (nullability == base.Nullability) + if (nullability == base.Nullability || Kind is not TypeKind.Dynamic) return this; else return new NullabilityAnnotatedType(this, nullability); From ca3b9165777e0cd4972f16804683bf26b31466dd Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis Date: Tue, 30 May 2023 16:53:20 +0300 Subject: [PATCH 188/230] Remove unnecessary package references. (#2990) * Remove references to .NET Standard 1.x packages. * Also remove `System.Bufffers`. --- ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj index 384cebd83..86b9a2503 100644 --- a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj +++ b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj @@ -64,14 +64,11 @@ - - - all runtime; build; native; contentfiles; analyzers; buildtransitive From 641c1788fe84596f6582e5b334b54f0f3fb7945b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 13:51:03 +0200 Subject: [PATCH 189/230] Re-order packages.props --- packages.props | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages.props b/packages.props index 9b0177e17..a347475be 100644 --- a/packages.props +++ b/packages.props @@ -5,19 +5,20 @@ Note: when updating these, ensure to also adjust the binding redirects in app.config.template appropriately. --> + + 7.0.0 7.0.0 6.0.0 6.0.0 - - 6.0.0 - - 6.0.0 - - 4.4.0 + + 1.1.0-beta2-22171-02 0.11.4 6.3.0.90 2.8.7 + + + 3.13.3 4.4.2 3.0.124 @@ -25,6 +26,11 @@ 17.5.0 4.18.4 2017.7.26.1241 - 1.1.0-beta2-22171-02 + + 4.4.0 + + 6.0.0 + + 6.0.0 From 4aa9280a8e6589f65ac539e62d521395cabce778 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 14:03:20 +0200 Subject: [PATCH 190/230] Add test case for generic attributes. --- .../TestCases/Pretty/CustomAttributes.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs index df63a3635..b99c9fb14 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs @@ -163,8 +163,13 @@ namespace CustomAttributes public static void UseGenericAttribute() { } - // TODO: add test for generic attributes with arguments of type T - // This is blocked by https://github.com/dotnet/runtime/issues/58073 + [Generic(42)] + [Generic("Hi")] + [Generic("Hi")] + [Generic((short)42)] + public static void UseGenericAttributeWithArg() + { + } #endif } } From 94ee5ed21641d65daf8c67d467a776728b2423f2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 14:16:57 +0200 Subject: [PATCH 191/230] Fix assertion: UnknownType for unresolved reference can still be nullable; only the SpecialType.Unknown cannot. --- .../TypeSystem/Implementation/NullabilityAnnotatedType.cs | 1 + ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index 41ae7ee70..abfd5b184 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -20,6 +20,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // the NullabilityAnnotatedType wrapper only in some limited places. Debug.Assert(type is ITypeDefinition { IsReferenceType: not false } || type.Kind == TypeKind.Dynamic + || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs index 5db919db7..28a0f33b7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -107,7 +107,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override IType ChangeNullability(Nullability nullability) { - if (nullability == Nullability.Oblivious) + if (nullability == Nullability.Oblivious || isReferenceType == false) return this; else return new NullabilityAnnotatedType(this, nullability); From 4fc8f4e66e506e798e6be8c83fd6f1bfbc5a7fdf Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 14:38:04 +0200 Subject: [PATCH 192/230] Fix #2913: ArgumentException when generic class is missing `1 suffix. --- ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index df2cb6402..39c2bcad3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -72,6 +72,13 @@ namespace ICSharpCode.Decompiler.TypeSystem public IType GetGenericInstantiation(IType genericType, ImmutableArray typeArguments) { + int tpc = genericType.TypeParameterCount; + if (tpc == 0 || tpc != typeArguments.Length) + { + // This can occur when the genericType is from another assembly, + // doesn't have the typical `1 suffix, and that other assembly is not loaded. + return genericType; + } return new ParameterizedType(genericType, typeArguments); } From 6d671071c11a2e5befd6ecbe7026c3eb13421955 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 16:40:55 +0200 Subject: [PATCH 193/230] Fix #2851: assertion after cloning a block with expected result type --- ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 0330f079f..79b79d70c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL public override ILInstruction Clone() { - BlockContainer clone = new BlockContainer(); + BlockContainer clone = new BlockContainer(this.Kind, this.ExpectedResultType); clone.AddILRange(this); clone.Blocks.AddRange(this.Blocks.Select(block => (Block)block.Clone())); // Adjust branch instructions to point to the new container From 72a895f64fc369c649d33dc8aa8d9c76b8672876 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 31 May 2023 18:32:30 +0200 Subject: [PATCH 194/230] #nullable enable for ILReader --- ICSharpCode.Decompiler/IL/ILReader.cs | 57 +++++++++++++++------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 49501f501..154e65c5c 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -49,12 +51,12 @@ namespace ICSharpCode.Decompiler.IL readonly MetadataReader metadata; public bool UseDebugSymbols { get; set; } - public DebugInfo.IDebugInfoProvider DebugInfo { get; set; } + public DebugInfo.IDebugInfoProvider? DebugInfo { get; set; } public List Warnings { get; } = new List(); // List of candidate locations for sequence points. Includes empty il stack locations, any nop instructions, and the instruction following // a call instruction. - public List SequencePointCandidates { get; private set; } + public List SequencePointCandidates { get; } = new List(); /// /// Creates a new ILReader instance. @@ -69,29 +71,29 @@ namespace ICSharpCode.Decompiler.IL this.module = module; this.compilation = module.Compilation; this.metadata = module.metadata; - this.SequencePointCandidates = new List(); } + // The members initialized with `null!` are initialized in the Init method. GenericContext genericContext; - IMethod method; - MethodBodyBlock body; + IMethod method = null!; + MethodBodyBlock body = null!; StackType methodReturnStackType; BlobReader reader; - ImmutableStack currentStack; - List expressionStack; - ILVariable[] parameterVariables; - ILVariable[] localVariables; - BitSet isBranchTarget; - BlockContainer mainContainer; - List instructionBuilder; + ImmutableStack currentStack = ImmutableStack.Empty; + List expressionStack = new List(); + ILVariable[] parameterVariables = null!; + ILVariable[] localVariables = null!; + BitSet isBranchTarget = null!; + BlockContainer mainContainer = null!; + List instructionBuilder = new List(); int currentInstructionStart; // Dictionary that stores stacks for each IL instruction - Dictionary> stackByOffset; - Dictionary variableByExceptionHandler; - UnionFind unionFind; - List<(ILVariable, ILVariable)> stackMismatchPairs; - IEnumerable stackVariables; + Dictionary> stackByOffset = new Dictionary>(); + Dictionary variableByExceptionHandler = new Dictionary(); + UnionFind unionFind = null!; + List<(ILVariable, ILVariable)> stackMismatchPairs = new List<(ILVariable, ILVariable)>(); + IEnumerable? stackVariables; void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext) { @@ -114,9 +116,9 @@ namespace ICSharpCode.Decompiler.IL this.body = body; this.reader = body.GetILReader(); this.currentStack = ImmutableStack.Empty; - this.expressionStack = new List(); + this.expressionStack.Clear(); this.unionFind = new UnionFind(); - this.stackMismatchPairs = new List<(ILVariable, ILVariable)>(); + this.stackMismatchPairs.Clear(); this.methodReturnStackType = method.ReturnType.GetStackType(); InitParameterVariables(); localVariables = InitLocalVariables(); @@ -126,10 +128,10 @@ namespace ICSharpCode.Decompiler.IL v.UsesInitialValue = true; } this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); - this.instructionBuilder = new List(); + this.instructionBuilder.Clear(); this.isBranchTarget = new BitSet(reader.Length); - this.stackByOffset = new Dictionary>(); - this.variableByExceptionHandler = new Dictionary(); + this.stackByOffset.Clear(); + this.variableByExceptionHandler.Clear(); } EntityHandle ReadAndDecodeMetadataToken() @@ -256,7 +258,7 @@ namespace ICSharpCode.Decompiler.IL ILVariable CreateILVariable(int index, IType parameterType, string name) { Debug.Assert(!parameterType.IsUnbound()); - ITypeDefinition def = parameterType.GetDefinition(); + ITypeDefinition? def = parameterType.GetDefinition(); if (def != null && index < 0 && def.IsReferenceType == false) { parameterType = new ByReferenceType(parameterType); @@ -443,7 +445,11 @@ namespace ICSharpCode.Decompiler.IL if (inst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) { FlushExpressionStack(); - if (!stackByOffset.TryGetValue(end, out currentStack)) + if (stackByOffset.TryGetValue(end, out var stackAfterEnd)) + { + currentStack = stackAfterEnd; + } + else { currentStack = ImmutableStack.Empty; } @@ -634,6 +640,7 @@ namespace ICSharpCode.Decompiler.IL var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); function.Variables.AddRange(parameterVariables); function.Variables.AddRange(localVariables); + Debug.Assert(stackVariables != null); function.Variables.AddRange(stackVariables); function.Variables.AddRange(variableByExceptionHandler.Values); function.Variables.AddRange(blockBuilder.OnErrorDispatcherVariables); @@ -1553,7 +1560,7 @@ namespace ICSharpCode.Decompiler.IL return new StObj(target, value, type); } - IType constrainedPrefix; + IType? constrainedPrefix; private DecodedInstruction DecodeConstrainedCall() { From b93e65cdadf467426a0a358920a0b13c097bcb6b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 1 Jun 2023 13:50:35 +0200 Subject: [PATCH 195/230] Fix #901: Refactor ILReader: only read reachable code + support reimports This makes our logic more similar to that used by the dotnet runtime. This lets us infer correct stack types in edge cases such as #2401. It also improves support for obfuscated control flow such as #2878. --- .../TestCases/Correctness/StackTypes.il | 32 +- ICSharpCode.Decompiler/IL/BlockBuilder.cs | 134 ++---- ICSharpCode.Decompiler/IL/ILReader.cs | 409 ++++++++++-------- 3 files changed, 289 insertions(+), 286 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il index 9c178a2ce..47ebea33a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il @@ -79,7 +79,34 @@ pointless: box native int call void [mscorlib]System.Console::WriteLine(string, object) - /* + ldstr "Int32OrNativeReordered(0x7fffffff, false) = {0}" + ldc.i4 0x7fffffff + ldc.i4 0 + call native int Program::Int32OrNativeReordered(int32, bool) + box native int + call void [mscorlib]System.Console::WriteLine(string, object) + + ldstr "Int32OrNativeReordered(0x7fffffff, true) = {0}" + ldc.i4 0x7fffffff + ldc.i4 1 + call native int Program::Int32OrNativeReordered(int32, bool) + box native int + call void [mscorlib]System.Console::WriteLine(string, object) + + ldstr "Int32OrNativeReordered(-1, false) = {0}" + ldc.i4.m1 + ldc.i4 0 + call native int Program::Int32OrNativeReordered(int32, bool) + box native int + call void [mscorlib]System.Console::WriteLine(string, object) + + ldstr "Int32OrNativeReordered(-1, true) = {0}" + ldc.i4.m1 + ldc.i4 1 + call native int Program::Int32OrNativeReordered(int32, bool) + box native int + call void [mscorlib]System.Console::WriteLine(string, object) + ldstr "Int32OrNativeLoopStyle(0x7fffffff):" call void [mscorlib]System.Console::WriteLine(string) ldc.i4 0x7fffffff @@ -101,7 +128,6 @@ pointless: call native int Program::Int32OrNativeDeadCode(int32) box native int call void [mscorlib]System.Console::WriteLine(string, object) - */ ldc.i4 0x7fffffff call void Program::RunInt32OrNativeMultiUse(int32) @@ -127,7 +153,6 @@ pointless: ret } - /* .method public static native int Int32OrNativeReordered(int32 val, bool use_native) { // The spec is ambiguous whether the addition will be in 32-bits or native size. @@ -187,7 +212,6 @@ pointless: conv.u br after_if } - */ .method public static void RunInt32OrNativeMultiUse(int32 val) { diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index 75a7613bf..be06a5034 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -16,8 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -29,6 +29,11 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { + /// + /// Converts the list of basic blocks from ILReader into a BlockContainer structure. + /// This involves creating nested block containers for exception handlers, and creating + /// branches between the blocks. + /// class BlockBuilder { readonly MethodBodyBlock body; @@ -64,7 +69,6 @@ namespace ICSharpCode.Decompiler.IL var tryRange = new Interval(eh.TryOffset, eh.TryOffset + eh.TryLength); var handlerBlock = new BlockContainer(); handlerBlock.AddILRange(new Interval(eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength)); - handlerBlock.Blocks.Add(new Block()); handlerContainers.Add(handlerBlock.StartILOffset, handlerBlock); if (eh.Kind == ExceptionRegionKind.Fault || eh.Kind == ExceptionRegionKind.Finally) @@ -94,7 +98,6 @@ namespace ICSharpCode.Decompiler.IL { var filterBlock = new BlockContainer(expectedResultType: StackType.I4); filterBlock.AddILRange(new Interval(eh.FilterOffset, eh.HandlerOffset)); - filterBlock.Blocks.Add(new Block()); handlerContainers.Add(filterBlock.StartILOffset, filterBlock); filter = filterBlock; } @@ -117,20 +120,18 @@ namespace ICSharpCode.Decompiler.IL } int currentTryIndex; - TryInstruction nextTry; + TryInstruction? nextTry; - BlockContainer currentContainer; - Block currentBlock; + BlockContainer? currentContainer; readonly Stack containerStack = new Stack(); - public void CreateBlocks(BlockContainer mainContainer, List instructions, BitSet incomingBranches, CancellationToken cancellationToken) + public void CreateBlocks(BlockContainer mainContainer, IEnumerable basicBlocks, CancellationToken cancellationToken) { CreateContainerStructure(); mainContainer.SetILRange(new Interval(0, body.GetCodeSize())); - currentContainer = mainContainer; - if (instructions.Count == 0) + if (!basicBlocks.Any()) { - currentContainer.Blocks.Add(new Block { + mainContainer.Blocks.Add(new Block { Instructions = { new InvalidBranch("Empty body found. Decompiled assembly might be a reference assembly.") } @@ -138,99 +139,42 @@ namespace ICSharpCode.Decompiler.IL return; } - foreach (var inst in instructions) + currentContainer = mainContainer; + foreach (var block in basicBlocks.OrderBy(b => b.StartILOffset)) { cancellationToken.ThrowIfCancellationRequested(); - int start = inst.StartILOffset; - if (currentBlock == null || (incomingBranches[start] && !IsStackAdjustment(inst))) + int start = block.StartILOffset; + // Leave nested containers if necessary + while (start >= currentContainer.EndILOffset) { - // Finish up the previous block - FinalizeCurrentBlock(start, fallthrough: true); - // Leave nested containers if necessary - while (start >= currentContainer.EndILOffset) - { - currentContainer = containerStack.Pop(); - currentBlock = currentContainer.Blocks.Last(); - // this container is skipped (i.e. the loop will execute again) - // set ILRange to the last instruction offset inside the block. - if (start >= currentContainer.EndILOffset) - { - Debug.Assert(currentBlock.ILRangeIsEmpty); - currentBlock.AddILRange(new Interval(currentBlock.StartILOffset, start)); - } - } - // Enter a handler if necessary - if (handlerContainers.TryGetValue(start, out BlockContainer handlerContainer)) - { - containerStack.Push(currentContainer); - currentContainer = handlerContainer; - currentBlock = handlerContainer.EntryPoint; - } - else - { - FinalizeCurrentBlock(start, fallthrough: false); - // Create the new block - currentBlock = new Block(); - currentContainer.Blocks.Add(currentBlock); - } - currentBlock.SetILRange(new Interval(start, start)); + currentContainer = containerStack.Pop(); + } + // Enter a handler if necessary + if (handlerContainers.TryGetValue(start, out BlockContainer? handlerContainer)) + { + containerStack.Push(currentContainer); + currentContainer = handlerContainer; } + // Enter a try block if necessary while (nextTry != null && start == nextTry.TryBlock.StartILOffset) { - currentBlock.Instructions.Add(nextTry); + var blockForTry = new Block(); + blockForTry.SetILRange(nextTry); + blockForTry.Instructions.Add(nextTry); + currentContainer.Blocks.Add(blockForTry); + containerStack.Push(currentContainer); currentContainer = (BlockContainer)nextTry.TryBlock; - currentBlock = new Block(); - currentContainer.Blocks.Add(currentBlock); - currentBlock.SetILRange(new Interval(start, start)); nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex); } - currentBlock.Instructions.Add(inst); - if (inst.HasFlag(InstructionFlags.EndPointUnreachable)) - FinalizeCurrentBlock(inst.EndILOffset, fallthrough: false); - else if (!CreateExtendedBlocks && inst.HasFlag(InstructionFlags.MayBranch)) - FinalizeCurrentBlock(inst.EndILOffset, fallthrough: true); - } - FinalizeCurrentBlock(mainContainer.EndILOffset, fallthrough: false); - // Finish up all containers - while (containerStack.Count > 0) - { - currentContainer = containerStack.Pop(); - currentBlock = currentContainer.Blocks.Last(); - FinalizeCurrentBlock(mainContainer.EndILOffset, fallthrough: false); + currentContainer.Blocks.Add(block); } + Debug.Assert(currentTryIndex == tryInstructionList.Count && nextTry == null); ConnectBranches(mainContainer, cancellationToken); CreateOnErrorDispatchers(); } - static bool IsStackAdjustment(ILInstruction inst) - { - return inst is StLoc stloc && stloc.IsStackAdjustment; - } - - private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough) - { - if (currentBlock == null) - return; - Debug.Assert(currentBlock.ILRangeIsEmpty); - currentBlock.SetILRange(new Interval(currentBlock.StartILOffset, currentILOffset)); - if (fallthrough) - { - if (currentBlock.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) - { - // Instead of putting the default branch after the switch instruction - switchInst.Sections.Last().Body = new Branch(currentILOffset); - Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable)); - } - else - { - currentBlock.Instructions.Add(new Branch(currentILOffset)); - } - } - currentBlock = null; - } - void ConnectBranches(ILInstruction inst, CancellationToken cancellationToken) { switch (inst) @@ -238,12 +182,16 @@ namespace ICSharpCode.Decompiler.IL case Branch branch: cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(branch.TargetBlock == null); - branch.TargetBlock = FindBranchTarget(branch.TargetILOffset); - if (branch.TargetBlock == null) + var targetBlock = FindBranchTarget(branch.TargetILOffset); + if (targetBlock == null) { branch.ReplaceWith(new InvalidBranch("Could not find block for branch target " + Disassembler.DisassemblerHelpers.OffsetToString(branch.TargetILOffset)).WithILRange(branch)); } + else + { + branch.TargetBlock = targetBlock; + } break; case Leave leave: // ret (in void method) = leave(mainContainer) @@ -279,7 +227,7 @@ namespace ICSharpCode.Decompiler.IL } } - Block FindBranchTarget(int targetILOffset) + Block? FindBranchTarget(int targetILOffset) { foreach (var container in containerStack) { @@ -291,7 +239,7 @@ namespace ICSharpCode.Decompiler.IL if (container.SlotInfo == TryCatchHandler.BodySlot) { // catch handler is allowed to branch back into try block (VB On Error) - TryCatch tryCatch = (TryCatch)container.Parent.Parent; + TryCatch tryCatch = (TryCatch)container.Parent!.Parent!; if (tryCatch.TryBlock.StartILOffset < targetILOffset && targetILOffset < tryCatch.TryBlock.EndILOffset) { return CreateBranchTargetForOnErrorJump(tryCatch, targetILOffset); @@ -345,7 +293,7 @@ namespace ICSharpCode.Decompiler.IL { foreach (var (tryCatch, dispatch) in onErrorDispatchers) { - Block block = (Block)tryCatch.Parent; + Block block = (Block)tryCatch.Parent!; // Before the regular entry point of the try-catch, insert an. instruction that resets the dispatcher variable block.Instructions.Insert(tryCatch.ChildIndex, new StLoc(dispatch.Variable, new LdcI4(-1))); // Split the block, so that we can introduce branches that jump directly into the try block @@ -355,7 +303,7 @@ namespace ICSharpCode.Decompiler.IL newBlock.Instructions.AddRange(block.Instructions.Skip(splitAt)); block.Instructions.RemoveRange(splitAt, block.Instructions.Count - splitAt); block.Instructions.Add(new Branch(newBlock)); - ((BlockContainer)block.Parent).Blocks.Add(newBlock); + ((BlockContainer)block.Parent!).Blocks.Add(newBlock); // Update the branches that jump directly into the try block foreach (var b in dispatch.Branches) { diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 154e65c5c..f53a56acc 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -46,6 +46,77 @@ namespace ICSharpCode.Decompiler.IL /// public class ILReader { + /// + /// Represents a block of IL instructions. + /// + private sealed class ImportedBlock + { + // These members are immediately available after construction: + public readonly Block Block = new Block(BlockKind.ControlFlow); + public ImmutableStack InputStack; + + public int StartILOffset => Block.StartILOffset; + /// True if the import is in progress or completed. + public bool ImportStarted = false; + + // When the block is imported, Block.Instructions is filled with the imported instructions + // and the following members are initialized: + public List<(ImportedBlock, ImmutableStack)> OutgoingEdges = new(); + + public ImportedBlock(int offset, ImmutableStack inputStack) + { + this.InputStack = inputStack; + this.Block.AddILRange(new Interval(offset, offset)); + } + + /// + /// Compares stack types and update InputStack if necessary. + /// Returns true if InputStack was updated, making a reimport necessary. + /// + public bool MergeStackTypes(ImmutableStack newEdge) + { + var a = this.InputStack; + var b = newEdge; + bool changed = false; + while (!a.IsEmpty && !b.IsEmpty) + { + if (a.Peek().StackType < b.Peek().StackType) + { + changed = true; + } + a = a.Pop(); + b = b.Pop(); + } + if (!changed || !(a.IsEmpty && b.IsEmpty)) + return false; + a = this.InputStack; + b = newEdge; + var output = new List(); + while (!a.IsEmpty && !b.IsEmpty) + { + if (a.Peek().StackType < b.Peek().StackType) + { + output.Add(b.Peek()); + } + else + { + output.Add(a.Peek()); + } + a = a.Pop(); + b = b.Pop(); + } + this.InputStack = ImmutableStack.CreateRange(output); + this.ImportStarted = false; + return true; + } + + internal void ResetForReimport() + { + this.Block.Instructions.Clear(); + this.OutgoingEdges.Clear(); + } + } + readonly ICompilation compilation; readonly MetadataModule module; readonly MetadataReader metadata; @@ -80,19 +151,17 @@ namespace ICSharpCode.Decompiler.IL StackType methodReturnStackType; BlobReader reader; ImmutableStack currentStack = ImmutableStack.Empty; + ImportedBlock? currentBlock; List expressionStack = new List(); ILVariable[] parameterVariables = null!; ILVariable[] localVariables = null!; BitSet isBranchTarget = null!; BlockContainer mainContainer = null!; - List instructionBuilder = new List(); int currentInstructionStart; - // Dictionary that stores stacks for each IL instruction - Dictionary> stackByOffset = new Dictionary>(); + Dictionary blocksByOffset = new Dictionary(); + Queue importQueue = new Queue(); Dictionary variableByExceptionHandler = new Dictionary(); - UnionFind unionFind = null!; - List<(ILVariable, ILVariable)> stackMismatchPairs = new List<(ILVariable, ILVariable)>(); IEnumerable? stackVariables; void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext) @@ -117,8 +186,6 @@ namespace ICSharpCode.Decompiler.IL this.reader = body.GetILReader(); this.currentStack = ImmutableStack.Empty; this.expressionStack.Clear(); - this.unionFind = new UnionFind(); - this.stackMismatchPairs.Clear(); this.methodReturnStackType = method.ReturnType.GetStackType(); InitParameterVariables(); localVariables = InitLocalVariables(); @@ -128,9 +195,9 @@ namespace ICSharpCode.Decompiler.IL v.UsesInitialValue = true; } this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); - this.instructionBuilder.Clear(); + this.blocksByOffset.Clear(); + this.importQueue.Clear(); this.isBranchTarget = new BitSet(reader.Length); - this.stackByOffset.Clear(); this.variableByExceptionHandler.Clear(); } @@ -284,82 +351,71 @@ namespace ICSharpCode.Decompiler.IL Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstructionStart, message)); } - ImmutableStack MergeStacks(ImmutableStack a, ImmutableStack b) + /// + /// Check control flow edges for compatible stacks. + /// Returns union find data structure for unifying the different variables for the same stack slot. + /// Also inserts stack adjustments where necessary. + /// + UnionFind CheckOutgoingEdges() { - if (CheckStackCompatibleWithoutAdjustments(a, b)) - { - // We only need to union the input variables, but can - // otherwise re-use the existing stack. - ImmutableStack output = a; - while (!a.IsEmpty && !b.IsEmpty) - { - Debug.Assert(a.Peek().StackType == b.Peek().StackType); - unionFind.Merge(a.Peek(), b.Peek()); - a = a.Pop(); - b = b.Pop(); - } - return output; - } - else if (a.Count() != b.Count()) - { - // Let's not try to merge mismatched stacks. - Warn("Incompatible stack heights: " + a.Count() + " vs " + b.Count()); - return a; - } - else + var unionFind = new UnionFind(); + foreach (var block in blocksByOffset.Values) { - // The more complex case where the stacks don't match exactly. - var output = new List(); - while (!a.IsEmpty && !b.IsEmpty) + foreach (var (outgoing, stack) in block.OutgoingEdges) { - var varA = a.Peek(); - var varB = b.Peek(); - if (varA.StackType == varB.StackType) + var a = stack; + var b = outgoing.InputStack; + if (a.Count() != b.Count()) { - unionFind.Merge(varA, varB); - output.Add(varA); + // Let's not try to merge mismatched stacks. + Warnings.Add($"IL_{block.Block.EndILOffset:x4}->IL{outgoing.StartILOffset:x4}: Incompatible stack heights: {a.Count()} vs {b.Count()}"); + continue; } - else + while (!a.IsEmpty && !b.IsEmpty) { - if (!IsValidTypeStackTypeMerge(varA.StackType, varB.StackType)) - { - Warn("Incompatible stack types: " + varA.StackType + " vs " + varB.StackType); - } - if (varA.StackType > varB.StackType) + var varA = a.Peek(); + var varB = b.Peek(); + if (varA.StackType == varB.StackType) { - output.Add(varA); - // every store to varB should also store to varA - stackMismatchPairs.Add((varB, varA)); + // The stack types match, so we can merge the variables. + unionFind.Merge(varA, varB); } else { - output.Add(varB); - // every store to varA should also store to varB - stackMismatchPairs.Add((varA, varB)); + Debug.Assert(varA.StackType < varB.StackType); + if (!IsValidTypeStackTypeMerge(varA.StackType, varB.StackType)) + { + Warnings.Add($"IL_{block.Block.EndILOffset:x4}->IL{outgoing.StartILOffset:x4}: Incompatible stack types: {varA.StackType} vs {varB.StackType}"); + } + InsertStackAdjustment(block.Block, varA, varB); } + a = a.Pop(); + b = b.Pop(); } - a = a.Pop(); - b = b.Pop(); } - // because we built up output by popping from the input stacks, we need to reverse it to get back the original order - output.Reverse(); - return ImmutableStack.CreateRange(output); } + return unionFind; } - static bool CheckStackCompatibleWithoutAdjustments(ImmutableStack a, ImmutableStack b) + /// + /// Inserts a copy from varA to varB (with conversion) at the end of . + /// If the block ends with a branching instruction, the copy is inserted before the branching instruction. + /// + private void InsertStackAdjustment(Block block, ILVariable varA, ILVariable varB) { - while (!a.IsEmpty && !b.IsEmpty) + int insertionPosition = block.Instructions.Count; + while (insertionPosition > 0 && block.Instructions[insertionPosition - 1].HasFlag(InstructionFlags.MayBranch)) { - if (a.Peek().StackType != b.Peek().StackType) - return false; - a = a.Pop(); - b = b.Pop(); + // Branch instruction mustn't be initializing varA. + Debug.Assert(!block.Instructions[insertionPosition - 1].HasFlag(InstructionFlags.MayWriteLocals)); + insertionPosition--; } - return a.IsEmpty && b.IsEmpty; + ILInstruction value = new LdLoc(varA); + value = new Conv(value, varB.StackType.ToPrimitiveType(), false, Sign.Signed); + block.Instructions.Insert(insertionPosition, new StLoc(varB, value) { IsStackAdjustment = true }); } - private bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType2) + private static bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType2) { if (stackType1 == StackType.I && stackType2 == StackType.I4) return true; @@ -375,42 +431,66 @@ namespace ICSharpCode.Decompiler.IL /// /// Stores the given stack for a branch to `offset`. - /// - /// The stack may be modified if stack adjustments are necessary. (e.g. implicit I4->I conversion) /// - void StoreStackForOffset(int offset, ref ImmutableStack stack) + ImportedBlock StoreStackForOffset(int offset, ImmutableStack stack) { - if (stackByOffset.TryGetValue(offset, out var existing)) + if (blocksByOffset.TryGetValue(offset, out var existing)) { - stack = MergeStacks(existing, stack); - if (stack != existing) - stackByOffset[offset] = stack; + bool wasImported = existing.ImportStarted; + if (existing.MergeStackTypes(stack) && wasImported) + { + // If the stack changed, we need to re-import the block. + importQueue.Enqueue(existing); + } + return existing; } else { - stackByOffset.Add(offset, stack); + ImportedBlock newBlock = new ImportedBlock(offset, stack); + blocksByOffset.Add(offset, newBlock); + importQueue.Enqueue(newBlock); + return newBlock; } } void ReadInstructions(CancellationToken cancellationToken) { reader.Reset(); + StoreStackForOffset(0, ImmutableStack.Empty); ILParser.SetBranchTargets(ref reader, isBranchTarget); - reader.Reset(); PrepareBranchTargetsAndStacksForExceptionHandlers(); - bool nextInstructionBeginsNewBlock = false; + // Import of IL byte codes: + while (importQueue.Count > 0) + { + cancellationToken.ThrowIfCancellationRequested(); + ImportedBlock block = importQueue.Dequeue(); + ReadBlock(block, cancellationToken); + } - reader.Reset(); + // Merge different variables for same stack slot: + var unionFind = CheckOutgoingEdges(); + var visitor = new CollectStackVariablesVisitor(unionFind); + foreach (var block in blocksByOffset.Values) + { + block.Block.AcceptVisitor(visitor); + } + stackVariables = visitor.variables; + } + + void ReadBlock(ImportedBlock block, CancellationToken cancellationToken) + { + Debug.Assert(!block.ImportStarted); + block.ResetForReimport(); + block.ImportStarted = true; + reader.Offset = block.StartILOffset; + currentBlock = block; + currentStack = block.InputStack; + // Read instructions until we reach the end of the block. while (reader.RemainingBytes > 0) { cancellationToken.ThrowIfCancellationRequested(); int start = reader.Offset; - if (isBranchTarget[start]) - { - FlushExpressionStack(); - StoreStackForOffset(start, ref currentStack); - } currentInstructionStart = start; bool startedWithEmptyStack = CurrentStackIsEmpty(); DecodedInstruction decodedInstruction; @@ -432,9 +512,9 @@ namespace ICSharpCode.Decompiler.IL { // Flush to avoid re-ordering of side-effects FlushExpressionStack(); - instructionBuilder.Add(inst); + block.Block.Instructions.Add(inst); } - else if (isBranchTarget[start] || nextInstructionBeginsNewBlock) + else if (start == block.StartILOffset) { // If this instruction is the first in a new block, avoid it being inlined // into the next instruction. @@ -442,39 +522,39 @@ namespace ICSharpCode.Decompiler.IL // detect block starts, and doesn't search nested instructions. FlushExpressionStack(); } - if (inst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) + + if ((!decodedInstruction.PushedOnExpressionStack && IsSequencePointInstruction(inst)) || startedWithEmptyStack) { - FlushExpressionStack(); - if (stackByOffset.TryGetValue(end, out var stackAfterEnd)) - { - currentStack = stackAfterEnd; - } - else - { - currentStack = ImmutableStack.Empty; - } - nextInstructionBeginsNewBlock = true; + this.SequencePointCandidates.Add(inst.StartILOffset); } - else + + if (inst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) { - nextInstructionBeginsNewBlock = inst.HasFlag(InstructionFlags.MayBranch); + break; // end of block, don't parse following instructions if they are unreachable } - - if ((!decodedInstruction.PushedOnExpressionStack && IsSequencePointInstruction(inst)) || startedWithEmptyStack) + else if (isBranchTarget[end] || inst.HasFlag(InstructionFlags.MayBranch)) { - this.SequencePointCandidates.Add(inst.StartILOffset); + break; // end of block (we'll create fall through) } } - + // Finalize block FlushExpressionStack(); - - var visitor = new CollectStackVariablesVisitor(unionFind); - for (int i = 0; i < instructionBuilder.Count; i++) + block.Block.AddILRange(new Interval(block.StartILOffset, reader.Offset)); + if (!block.Block.HasFlag(InstructionFlags.EndPointUnreachable)) { - instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor); + // create fall through branch + MarkBranchTarget(reader.Offset, isFallThrough: true); + if (block.Block.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) + { + // Instead of putting the default branch after the switch instruction + switchInst.Sections.Last().Body = new Branch(reader.Offset); + Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable)); + } + else + { + block.Block.Instructions.Add(new Branch(reader.Offset)); + } } - stackVariables = visitor.variables; - InsertStackAdjustments(); } private bool CurrentStackIsEmpty() @@ -488,9 +568,7 @@ namespace ICSharpCode.Decompiler.IL foreach (var eh in body.ExceptionRegions) { // Always mark the start of the try block as a "branch target". - // We don't actually need to store the stack state here, - // but we need to ensure that no ILInstructions are inlined - // into the try-block. + // We need to ensure that we put a block boundary there. isBranchTarget[eh.TryOffset] = true; ImmutableStack ehStack; @@ -520,25 +598,23 @@ namespace ICSharpCode.Decompiler.IL if (eh.FilterOffset != -1) { isBranchTarget[eh.FilterOffset] = true; - StoreStackForOffset(eh.FilterOffset, ref ehStack); + StoreStackForOffset(eh.FilterOffset, ehStack); } if (eh.HandlerOffset != -1) { isBranchTarget[eh.HandlerOffset] = true; - StoreStackForOffset(eh.HandlerOffset, ref ehStack); + StoreStackForOffset(eh.HandlerOffset, ehStack); } } } - private bool IsSequencePointInstruction(ILInstruction instruction) + private static bool IsSequencePointInstruction(ILInstruction instruction) { - if (instruction.OpCode == OpCode.Nop || - (instructionBuilder.Count > 0 - && instructionBuilder.Last().OpCode is OpCode.Call - or OpCode.CallIndirect - or OpCode.CallVirt)) + if (instruction.OpCode is OpCode.Nop + or OpCode.Call + or OpCode.CallIndirect + or OpCode.CallVirt) { - return true; } else @@ -547,39 +623,6 @@ namespace ICSharpCode.Decompiler.IL } } - void InsertStackAdjustments() - { - if (stackMismatchPairs.Count == 0) - return; - var dict = new MultiDictionary(); - foreach (var (origA, origB) in stackMismatchPairs) - { - var a = unionFind.Find(origA); - var b = unionFind.Find(origB); - Debug.Assert(a.StackType < b.StackType); - // For every store to a, insert a converting store to b. - if (!dict[a].Contains(b)) - dict.Add(a, b); - } - var newInstructions = new List(); - foreach (var inst in instructionBuilder) - { - newInstructions.Add(inst); - if (inst is StLoc store) - { - foreach (var additionalVar in dict[store.Variable]) - { - ILInstruction value = new LdLoc(store.Variable); - value = new Conv(value, additionalVar.StackType.ToPrimitiveType(), false, Sign.Signed); - newInstructions.Add(new StLoc(additionalVar, value) { - IsStackAdjustment = true, - }.WithILRange(inst)); - } - } - } - instructionBuilder = newInstructions; - } - /// /// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout. /// @@ -588,39 +631,24 @@ namespace ICSharpCode.Decompiler.IL { Init(method, body, genericContext); ReadInstructions(cancellationToken); - foreach (var inst in instructionBuilder) + foreach (var importBlock in blocksByOffset.Values.OrderBy(b => b.StartILOffset)) { - if (inst is StLoc stloc && stloc.IsStackAdjustment) - { - output.Write(" "); - inst.WriteTo(output, new ILAstWritingOptions()); - output.WriteLine(); - continue; - } - if (stackByOffset.TryGetValue(inst.StartILOffset, out var stack)) + output.Write(" ["); + bool isFirstElement = true; + foreach (var element in importBlock.InputStack) { - output.Write(" ["); - bool isFirstElement = true; - foreach (var element in stack) - { - if (isFirstElement) - isFirstElement = false; - else - output.Write(", "); - output.WriteLocalReference(element.Name, element); - output.Write(":"); - output.Write(element.StackType); - } - output.Write(']'); - output.WriteLine(); + if (isFirstElement) + isFirstElement = false; + else + output.Write(", "); + output.WriteLocalReference(element.Name, element); + output.Write(":"); + output.Write(element.StackType); } - if (isBranchTarget[inst.StartILOffset]) - output.Write('*'); - else - output.Write(' '); - output.WriteLocalReference("IL_" + inst.StartILOffset.ToString("x4"), inst.StartILOffset, isDefinition: true); - output.Write(": "); - inst.WriteTo(output, new ILAstWritingOptions()); + output.Write(']'); + output.WriteLine(); + + importBlock.Block.WriteTo(output, new ILAstWritingOptions()); output.WriteLine(); } new Disassembler.MethodBodyDisassembler(output, cancellationToken) { DetectControlStructure = false } @@ -636,7 +664,7 @@ namespace ICSharpCode.Decompiler.IL Init(method, body, genericContext); ReadInstructions(cancellationToken); var blockBuilder = new BlockBuilder(body, variableByExceptionHandler, compilation); - blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken); + blockBuilder.CreateBlocks(mainContainer, blocksByOffset.Values.Select(ib => ib.Block), cancellationToken); var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); function.Variables.AddRange(parameterVariables); function.Variables.AddRange(localVariables); @@ -1187,7 +1215,7 @@ namespace ICSharpCode.Decompiler.IL return currentStack.Peek().StackType; } - class CollectStackVariablesVisitor : ILVisitor + sealed class CollectStackVariablesVisitor : ILVisitor { readonly UnionFind unionFind; internal readonly HashSet variables = new HashSet(); @@ -1216,7 +1244,7 @@ namespace ICSharpCode.Decompiler.IL { var variable = unionFind.Find(inst.Variable); if (variables.Add(variable)) - variable.Name = "S_" + (variables.Count - 1); + variable.Name = $"S_{variables.Count - 1}"; return new LdLoc(variable).WithILRange(inst); } return inst; @@ -1229,7 +1257,7 @@ namespace ICSharpCode.Decompiler.IL { var variable = unionFind.Find(inst.Variable); if (variables.Add(variable)) - variable.Name = "S_" + (variables.Count - 1); + variable.Name = $"S_{variables.Count - 1}"; return new StLoc(variable, inst.Value).WithILRange(inst); } return inst; @@ -1296,7 +1324,7 @@ namespace ICSharpCode.Decompiler.IL return Cast(inst, expectedType, Warnings, reader.Offset); } - internal static ILInstruction Cast(ILInstruction inst, StackType expectedType, List warnings, int ilOffset) + internal static ILInstruction Cast(ILInstruction inst, StackType expectedType, List? warnings, int ilOffset) { if (expectedType != inst.ResultType) { @@ -1913,11 +1941,13 @@ namespace ICSharpCode.Decompiler.IL } } - void MarkBranchTarget(int targetILOffset) + void MarkBranchTarget(int targetILOffset, bool isFallThrough = false) { FlushExpressionStack(); - Debug.Assert(isBranchTarget[targetILOffset]); - StoreStackForOffset(targetILOffset, ref currentStack); + Debug.Assert(isFallThrough || isBranchTarget[targetILOffset]); + var targetBlock = StoreStackForOffset(targetILOffset, currentStack); + Debug.Assert(currentBlock != null); + currentBlock.OutgoingEdges.Add((targetBlock, currentStack)); } /// @@ -1928,6 +1958,7 @@ namespace ICSharpCode.Decompiler.IL /// private void FlushExpressionStack() { + Debug.Assert(currentBlock != null); foreach (var inst in expressionStack) { Debug.Assert(inst.ResultType != StackType.Void); @@ -1935,7 +1966,7 @@ namespace ICSharpCode.Decompiler.IL var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType); v.HasGeneratedName = true; currentStack = currentStack.Push(v); - instructionBuilder.Add(new StLoc(v, inst).WithILRange(inst)); + currentBlock.Block.Instructions.Add(new StLoc(v, inst).WithILRange(inst)); } expressionStack.Clear(); } From b9ce8490f5d83c6cf673219b2d3cf20cc7c829f2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 1 Jun 2023 14:13:01 +0200 Subject: [PATCH 196/230] Remove hack that prevented inlining of the first instruction in each block. Now that ILReader already creates the basic blocks and BlockBuilder only arranges them in containers, this code is no longer necessary. --- .../IL/ControlFlow/ControlFlowSimplification.cs | 10 ++++------ ICSharpCode.Decompiler/IL/ILReader.cs | 8 -------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index c0b9de985..af864827e 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -258,7 +258,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (targetBlock.StartILOffset < block.StartILOffset && IsDeadTrueStore(block)) { // The C# compiler generates a dead store for the condition of while (true) loops. - block.Instructions.RemoveRange(block.Instructions.Count - 3, 2); + block.Instructions.RemoveAt(block.Instructions.Count - 2); } if (block.ILRangeIsEmpty) @@ -275,15 +275,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// private static bool IsDeadTrueStore(Block block) { - if (block.Instructions.Count < 3) + if (block.Instructions.Count < 2) return false; - if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore && block.Instructions[block.Instructions.Count - 3] is StLoc tempStore)) + if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore)) return false; if (!(deadStore.Variable.LoadCount == 0 && deadStore.Variable.AddressCount == 0)) return false; - if (!(deadStore.Value.MatchLdLoc(tempStore.Variable) && tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1)) - return false; - return tempStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean); + return deadStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean); } } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index f53a56acc..89d0739ae 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -514,14 +514,6 @@ namespace ICSharpCode.Decompiler.IL FlushExpressionStack(); block.Block.Instructions.Add(inst); } - else if (start == block.StartILOffset) - { - // If this instruction is the first in a new block, avoid it being inlined - // into the next instruction. - // This is necessary because the BlockBuilder uses inst.StartILOffset to - // detect block starts, and doesn't search nested instructions. - FlushExpressionStack(); - } if ((!decodedInstruction.PushedOnExpressionStack && IsSequencePointInstruction(inst)) || startedWithEmptyStack) { From 94d2ce0fcfc853a80aa918685324344f7bead8e4 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 1 Jun 2023 14:34:05 +0200 Subject: [PATCH 197/230] Fix crash when control flow reaches end of method. --- .../ILPrettyTestRunner.cs | 6 +++ .../TestCases/ILPretty/EmptyBodies.cs | 14 ++++++ .../TestCases/ILPretty/EmptyBodies.il | 45 +++++++++++++++++++ ICSharpCode.Decompiler/IL/ILReader.cs | 15 +++++-- 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.il diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index 237bdd402..5338bb7c9 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -263,6 +263,12 @@ namespace ICSharpCode.Decompiler.Tests await Run(); } + [Test] + public async Task EmptyBodies() + { + await Run(); + } + async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null, AssemblerOptions assemblerOptions = AssemblerOptions.Library) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs new file mode 100644 index 000000000..6945a1db1 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs @@ -0,0 +1,14 @@ +internal class EmptyBodies +{ + public static void RetVoid() + { + } + public static int RetInt() + { + return (int)/*Error near IL_0001: Stack underflow*/; + } + public static void Nop() + { + /*Error: End of method reached without returning.*/; + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.il new file mode 100644 index 000000000..bd14a188c --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.il @@ -0,0 +1,45 @@ +#define CORE_ASSEMBLY "System.Runtime" + +.assembly extern CORE_ASSEMBLY +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 4:0:0:0 +} + +.class private auto ansi beforefieldinit EmptyBodies + extends [CORE_ASSEMBLY]System.Object +{ + // I cannot test a truly empty body because the assembler will automatically add a ret instruction. + + .method public hidebysig static void RetVoid () cil managed + { + .maxstack 8 + ret + } + + .method public hidebysig static int32 RetInt () cil managed + { + .maxstack 8 + ret + } + + .method public hidebysig static void Nop () cil managed + { + .maxstack 8 + nop + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x206e + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Example::.ctor + +} // end of class EmptyBodies diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 89d0739ae..8b1072967 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -535,16 +535,25 @@ namespace ICSharpCode.Decompiler.IL if (!block.Block.HasFlag(InstructionFlags.EndPointUnreachable)) { // create fall through branch - MarkBranchTarget(reader.Offset, isFallThrough: true); + ILInstruction branch; + if (reader.RemainingBytes > 0) + { + MarkBranchTarget(reader.Offset, isFallThrough: true); + branch = new Branch(reader.Offset); + } + else + { + branch = new InvalidBranch("End of method reached without returning."); + } if (block.Block.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) { // Instead of putting the default branch after the switch instruction - switchInst.Sections.Last().Body = new Branch(reader.Offset); + switchInst.Sections.Last().Body = branch; Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable)); } else { - block.Block.Instructions.Add(new Branch(reader.Offset)); + block.Block.Instructions.Add(branch); } } } From 768cb02f0bf54104d8f435f8cd6736557e346886 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 1 Jun 2023 15:02:52 +0200 Subject: [PATCH 198/230] Fix #2964: Better error message when a method only contains a single ret instruction. --- .../TestCases/ILPretty/EmptyBodies.cs | 2 +- ICSharpCode.Decompiler/IL/BlockBuilder.cs | 9 --------- ICSharpCode.Decompiler/IL/ILReader.cs | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs index 6945a1db1..73998e2bd 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs @@ -5,7 +5,7 @@ internal class EmptyBodies } public static int RetInt() { - return (int)/*Error near IL_0001: Stack underflow*/; + /*Error: Method body consists only of 'ret', but nothing is being returned. Decompiled assembly might be a reference assembly.*/; } public static void Nop() { diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index be06a5034..c21881673 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -129,15 +129,6 @@ namespace ICSharpCode.Decompiler.IL { CreateContainerStructure(); mainContainer.SetILRange(new Interval(0, body.GetCodeSize())); - if (!basicBlocks.Any()) - { - mainContainer.Blocks.Add(new Block { - Instructions = { - new InvalidBranch("Empty body found. Decompiled assembly might be a reference assembly.") - } - }); - return; - } currentContainer = mainContainer; foreach (var block in basicBlocks.OrderBy(b => b.StartILOffset)) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 8b1072967..901076a22 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -457,6 +457,13 @@ namespace ICSharpCode.Decompiler.IL { reader.Reset(); StoreStackForOffset(0, ImmutableStack.Empty); + if (reader.Length == 0) + { + blocksByOffset[0].Block.Instructions.Add( + new InvalidBranch("Empty body found. Decompiled assembly might be a reference assembly.") + ); + return; + } ILParser.SetBranchTargets(ref reader, isBranchTarget); PrepareBranchTargetsAndStacksForExceptionHandlers(); @@ -1480,9 +1487,18 @@ namespace ICSharpCode.Decompiler.IL private ILInstruction Return() { if (methodReturnStackType == StackType.Void) + { return new IL.Leave(mainContainer); + } + else if (currentInstructionStart == 0) + { + Debug.Assert(expressionStack.Count == 0 && currentStack.IsEmpty); + return new InvalidBranch("Method body consists only of 'ret', but nothing is being returned. Decompiled assembly might be a reference assembly."); + } else + { return new IL.Leave(mainContainer, Pop(methodReturnStackType)); + } } private ILInstruction DecodeLdstr() From eab25345630dc3a9e2731bcbc65c9ef3bc962822 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 2 Jun 2023 20:40:25 +0200 Subject: [PATCH 199/230] Add test case for user defined operators --- .../PrettyTestRunner.cs | 6 + .../TestCases/Pretty/Operators.cs | 215 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index c0f5e3763..c341835e0 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -312,6 +312,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task Operators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs new file mode 100644 index 000000000..35e4a4a18 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs @@ -0,0 +1,215 @@ +// Copyright (c) Daniel Grunwald +// +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class AllOperators + { + public static AllOperators operator +(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator -(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator *(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator /(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator %(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator &(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator |(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator ^(AllOperators a, AllOperators b) + { + return null; + } + + public static AllOperators operator <<(AllOperators a, int b) + { + return null; + } + + public static AllOperators operator >>(AllOperators a, int b) + { + return null; + } + + public static AllOperators operator ~(AllOperators a) + { + return null; + } + + public static AllOperators operator !(AllOperators a) + { + return null; + } + + public static AllOperators operator -(AllOperators a) + { + return null; + } + + public static AllOperators operator +(AllOperators a) + { + return null; + } + + public static AllOperators operator ++(AllOperators a) + { + return null; + } + + public static AllOperators operator --(AllOperators a) + { + return null; + } + + public static bool operator true(AllOperators a) + { + return false; + } + + public static bool operator false(AllOperators a) + { + return false; + } + + public static bool operator ==(AllOperators a, AllOperators b) + { + return false; + } + + public static bool operator !=(AllOperators a, AllOperators b) + { + return false; + } + + public static bool operator <(AllOperators a, AllOperators b) + { + return false; + } + + public static bool operator >(AllOperators a, AllOperators b) + { + return false; + } + + public static bool operator <=(AllOperators a, AllOperators b) + { + return false; + } + + public static bool operator >=(AllOperators a, AllOperators b) + { + return false; + } + + public static implicit operator AllOperators(int a) + { + return null; + } + + public static explicit operator int(AllOperators a) + { + return 0; + } + } + + public class UseAllOperators + { + private AllOperators a = new AllOperators(); + private AllOperators b = new AllOperators(); + private AllOperators c; + public void Test() + { + c = a + b; + c = a - b; + c = a * b; + c = a / b; + c = a % b; + c = a & b; + c = a | b; + c = a ^ b; + c = a << 5; + c = a >> 5; + c = ~a; + c = !a; + c = -a; + c = +a; + c = ++a; + c = --a; + if (a) + { + Console.WriteLine("a"); + } + if (!a) + { + Console.WriteLine("!a"); + } + if (a == b) + { + Console.WriteLine("a == b"); + } + if (a != b) + { + Console.WriteLine("a != b"); + } + if (a < b) + { + Console.WriteLine("a < b"); + } + if (a > b) + { + Console.WriteLine("a > b"); + } + if (a <= b) + { + Console.WriteLine("a <= b"); + } + if (a >= b) + { + Console.WriteLine("a >= b"); + } + int num = (int)a; + a = num; + } + } +} \ No newline at end of file From 3c46271a1123d3c8a02fcb5151bcd239754776f7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 2 Jun 2023 21:37:42 +0200 Subject: [PATCH 200/230] Add support for unsigned right shift. Only for user-defined operators so far; builtin right shifts still cast to the appropriate type. --- .../Pretty/CompoundAssignmentTest.cs | 32 +++++++++++++++++++ .../TestCases/Pretty/Operators.cs | 10 ++++++ .../CSharp/CSharpDecompiler.cs | 1 + .../CSharp/ExpressionBuilder.cs | 7 ++-- .../CSharp/OutputVisitor/CSharpAmbience.cs | 1 + .../OutputVisitor/CSharpOutputVisitor.cs | 1 + .../GenericGrammarAmbiguityVisitor.cs | 3 ++ .../OutputVisitor/InsertParenthesesVisitor.cs | 1 + .../CSharp/Resolver/CSharpOperators.cs | 21 ++++++++++++ .../CSharp/Resolver/CSharpResolver.cs | 7 +++- .../Expressions/AssignmentExpression.cs | 9 ++++++ .../Expressions/BinaryOperatorExpression.cs | 5 +++ .../Syntax/TypeMembers/OperatorDeclaration.cs | 5 +++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 7 ++++ .../ReplaceMethodCallsWithOperators.cs | 6 ++-- ICSharpCode.Decompiler/DecompilerSettings.cs | 21 +++++++++++- .../IL/Transforms/TransformAssignment.cs | 2 +- ICSharpCode.Decompiler/Output/IAmbience.cs | 4 +++ ILSpy/Languages/CSharpLanguage.cs | 4 +++ ILSpy/Properties/Resources.Designer.cs | 9 ++++++ ILSpy/Properties/Resources.resx | 3 ++ 21 files changed, 152 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index e9c391ae3..abc6bb7e5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -201,6 +201,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { throw new NotImplementedException(); } +#if CS110 + public static CustomStruct operator >>>(CustomStruct lhs, int rhs) + { + throw new NotImplementedException(); + } +#endif public static CustomStruct operator &(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); @@ -4219,6 +4225,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } +#if CS110 + public static void CustomStructUnsignedRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) + { + //CustomStruct l = default(CustomStruct); + //p >>>= 5; + //l >>>= 5; + customStructField >>>= 5; + CustomStructProp >>>= 5; + c.CustomStructField >>>= 5; + c.CustomStructProp >>>= 5; + s.CustomStructField >>>= 5; + s.CustomStructProp >>>= 5; + customClassField.CustomStructField >>>= 5; + customClassField.CustomStructProp >>>= 5; + otherCustomStructField.CustomStructField >>>= 5; + otherCustomStructField.CustomStructProp >>>= 5; + CustomClassProp.CustomStructField >>>= 5; + CustomClassProp.CustomStructProp >>>= 5; + GetClass().CustomStructField >>>= 5; + GetClass().CustomStructProp >>>= 5; + GetRefStruct().CustomStructField >>>= 5; + GetRefStruct().CustomStructProp >>>= 5; + GetRefCustomStruct() >>>= 5; + } +#endif + public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s) { //CustomStruct l = default(CustomStruct); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs index 35e4a4a18..0e73a7952 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs @@ -72,6 +72,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return null; } +#if CS110 + public static AllOperators operator >>>(AllOperators a, int b) + { + return null; + } +#endif + public static AllOperators operator ~(AllOperators a) { return null; @@ -170,6 +177,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty c = a ^ b; c = a << 5; c = a >> 5; +#if CS110 + c = a >>> 5; +#endif c = ~a; c = !a; c = -a; diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 86ae6bf22..08d815d91 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -530,6 +530,7 @@ namespace ICSharpCode.Decompiler.CSharp typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors; typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses; typeSystemAstBuilder.SupportRecordStructs = settings.RecordStructs; + typeSystemAstBuilder.SupportUnsignedRightShift = settings.UnsignedRightShift; typeSystemAstBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal; return typeSystemAstBuilder; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0972105f1..91a86192a 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1729,6 +1729,7 @@ namespace ICSharpCode.Decompiler.CSharp case BinaryOperatorType.ExclusiveOr: case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: return false; default: return true; @@ -1804,7 +1805,7 @@ namespace ICSharpCode.Decompiler.CSharp else if (inst.Method.Parameters.Count == 2) { var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this); - AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name); + AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name, settings); Debug.Assert(op != null); return new AssignmentExpression(target, op.Value, value) @@ -1822,7 +1823,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name) + internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -1846,6 +1847,8 @@ namespace ICSharpCode.Decompiler.CSharp return AssignmentOperatorType.ShiftLeft; case "op_RightShift": return AssignmentOperatorType.ShiftRight; + case "op_UnsignedRightShift" when settings.UnsignedRightShift: + return AssignmentOperatorType.UnsignedShiftRight; default: return null; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 2e04d0575..207613994 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -237,6 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astBuilder.SupportInitAccessors = (ConversionFlags & ConversionFlags.SupportInitAccessors) != 0; astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0; astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0; + astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0; return astBuilder; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index ce48d69ca..9a4788f70 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -826,6 +826,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor break; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: spacePolicy = policy.SpaceAroundShiftOperator; break; case BinaryOperatorType.NullCoalescing: diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs index 74e99d8c0..e7f372f96 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs @@ -92,6 +92,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2: genericNestingLevel -= 2; break; + case BinaryOperatorType.UnsignedShiftRight when genericNestingLevel >= 3: + genericNestingLevel -= 3; + break; default: return true; // stop visiting, no ambiguity found } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index e279cf021..2fb3a4546 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -122,6 +122,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return PrecedenceLevel.Additive; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: return PrecedenceLevel.Shift; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs index b3f453157..fff06f134 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs @@ -674,6 +674,27 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } } + + OperatorMethod[]? unsignedShiftRightOperators; + + public OperatorMethod[] UnsignedShiftRightOperators { + get { + OperatorMethod[]? ops = LazyInit.VolatileRead(ref unsignedShiftRightOperators); + if (ops != null) + { + return ops; + } + else + { + return LazyInit.GetOrSet(ref unsignedShiftRightOperators, Lift( + new LambdaBinaryOperatorMethod(this, (a, b) => (int)((uint)a >> b)), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), + new LambdaBinaryOperatorMethod(this, (a, b) => (long)((ulong)a >> b)), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b) + )); + } + } + } #endregion #region Equality operators diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index 1e6d5df8b..f235e0a98 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -685,7 +685,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { isNullable = true; } - if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) + if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight || op == BinaryOperatorType.UnsignedShiftRight) { // special case: the shift operators allow "var x = null << null", producing int?. if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) @@ -805,6 +805,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver case BinaryOperatorType.ShiftRight: methodGroup = operators.ShiftRightOperators; break; + case BinaryOperatorType.UnsignedShiftRight: + methodGroup = operators.UnsignedShiftRightOperators; + break; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: case BinaryOperatorType.LessThan: @@ -1256,6 +1259,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return "op_LeftShift"; case BinaryOperatorType.ShiftRight: return "op_RightShift"; + case BinaryOperatorType.UnsignedShiftRight: + return "op_UnsignedRightShift"; case BinaryOperatorType.Equality: return "op_Equality"; case BinaryOperatorType.InEquality: diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index acd1038a8..378b33bfa 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole ModulusRole = new TokenRole("%="); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<="); public readonly static TokenRole ShiftRightRole = new TokenRole(">>="); + public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>="); public readonly static TokenRole BitwiseAndRole = new TokenRole("&="); public readonly static TokenRole BitwiseOrRole = new TokenRole("|="); public readonly static TokenRole ExclusiveOrRole = new TokenRole("^="); @@ -129,6 +130,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ShiftLeftRole; case AssignmentOperatorType.ShiftRight: return ShiftRightRole; + case AssignmentOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case AssignmentOperatorType.BitwiseAnd: return BitwiseAndRole; case AssignmentOperatorType.BitwiseOr: @@ -164,6 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return BinaryOperatorType.ShiftLeft; case AssignmentOperatorType.ShiftRight: return BinaryOperatorType.ShiftRight; + case AssignmentOperatorType.UnsignedShiftRight: + return BinaryOperatorType.UnsignedShiftRight; case AssignmentOperatorType.BitwiseAnd: return BinaryOperatorType.BitwiseAnd; case AssignmentOperatorType.BitwiseOr: @@ -195,6 +200,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ExpressionType.LeftShiftAssign; case AssignmentOperatorType.ShiftRight: return ExpressionType.RightShiftAssign; + case AssignmentOperatorType.UnsignedShiftRight: + return ExpressionType.Extension; case AssignmentOperatorType.BitwiseAnd: return ExpressionType.AndAssign; case AssignmentOperatorType.BitwiseOr: @@ -259,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ShiftLeft, /// left >>= right ShiftRight, + /// left >>>= right + UnsignedShiftRight, /// left &= right BitwiseAnd, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs index f64752e06..294b9f584 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole ModulusRole = new TokenRole("%"); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<"); public readonly static TokenRole ShiftRightRole = new TokenRole(">>"); + public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>"); public readonly static TokenRole NullCoalescingRole = new TokenRole("??"); public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; @@ -151,6 +152,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ShiftLeftRole; case BinaryOperatorType.ShiftRight: return ShiftRightRole; + case BinaryOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case BinaryOperatorType.NullCoalescing: return NullCoalescingRole; case BinaryOperatorType.Range: @@ -262,6 +265,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ShiftLeft, /// left >> right ShiftRight, + /// left >>> right + UnsignedShiftRight, /// left ?? right NullCoalescing, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs index c0f8236b8..4d8677ca7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs @@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ExclusiveOr, LeftShift, RightShift, + UnsignedRightShift, Equality, Inequality, GreaterThan, @@ -94,6 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole ExclusiveOrRole = new TokenRole("^"); public static readonly TokenRole LeftShiftRole = new TokenRole("<<"); public static readonly TokenRole RightShiftRole = new TokenRole(">>"); + public static readonly TokenRole UnsignedRightShiftRole = new TokenRole(">>>"); public static readonly TokenRole EqualityRole = new TokenRole("=="); public static readonly TokenRole InequalityRole = new TokenRole("!="); public static readonly TokenRole GreaterThanRole = new TokenRole(">"); @@ -127,6 +129,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" }; names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" }; names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" }; + names[(int)OperatorType.UnsignedRightShift] = new string[] { ">>>", "op_UnsignedRightShift" }; names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" }; names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" }; names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" }; @@ -230,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return LeftShiftRole; case OperatorType.RightShift: return RightShiftRole; + case OperatorType.UnsignedRightShift: + return UnsignedRightShiftRole; case OperatorType.Equality: return EqualityRole; case OperatorType.Inequality: diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3751c4454..17b896710 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -225,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public bool SupportRecordStructs { get; set; } + /// + /// Controls whether C# 11 "operator >>>" is supported. + /// + public bool SupportUnsignedRightShift { get; set; } + /// /// Controls whether all fully qualified type names should be prefixed with "global::". /// @@ -2217,6 +2222,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax OperatorType? opType = OperatorDeclaration.GetOperatorType(op.Name); if (opType == null) return ConvertMethod(op); + if (opType == OperatorType.UnsignedRightShift && !SupportUnsignedRightShift) + return ConvertMethod(op); OperatorDeclaration decl = new OperatorDeclaration(); decl.Modifiers = GetMemberModifiers(op); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index f2f7a355f..738fcd46a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms break; } - BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name); + BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name, context.Settings); if (bop != null && arguments.Length == 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression @@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) + static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -374,6 +374,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return BinaryOperatorType.ShiftLeft; case "op_RightShift": return BinaryOperatorType.ShiftRight; + case "op_UnsignedRightShift" when settings.UnsignedRightShift: + return BinaryOperatorType.UnsignedShiftRight; case "op_Equality": return BinaryOperatorType.Equality; case "op_Inequality": diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 2a8e2a4ae..dac595efa 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -154,12 +154,13 @@ namespace ICSharpCode.Decompiler requiredMembers = false; numericIntPtr = false; utf8StringLiterals = false; + unsignedRightShift = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals) + if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs) return CSharp.LanguageVersion.CSharp10_0; @@ -1199,6 +1200,24 @@ namespace ICSharpCode.Decompiler } } + bool unsignedRightShift = true; + + /// + /// Gets/Sets whether to use C# 11.0 unsigned right shift operator. + /// + [Category("C# 11.0 / VS 2022.4")] + [Description("DecompilerSettings.UnsignedRightShift")] + public bool UnsignedRightShift { + get { return unsignedRightShift; } + set { + if (unsignedRightShift != value) + { + unsignedRightShift = value; + OnPropertyChanged(); + } + } + } + bool showXmlDocumentation = true; /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 7407179b4..849ac8840 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { - if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null) + if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name, context.Settings) == null) return false; rhs = operatorCall.Arguments[1]; } diff --git a/ICSharpCode.Decompiler/Output/IAmbience.cs b/ICSharpCode.Decompiler/Output/IAmbience.cs index d496204b7..74bbcf072 100644 --- a/ICSharpCode.Decompiler/Output/IAmbience.cs +++ b/ICSharpCode.Decompiler/Output/IAmbience.cs @@ -109,6 +109,10 @@ namespace ICSharpCode.Decompiler.Output /// Support record structs. /// SupportRecordStructs = 0x40000, + /// + /// Support >>> as unsigned right shift operator. + /// + SupportUnsignedRightShift = 0x80000, StandardConversionFlags = ShowParameterNames | ShowAccessibility | diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index a5cd1e1a0..4486366a9 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -749,6 +749,10 @@ namespace ICSharpCode.ILSpy { flags |= ConversionFlags.SupportRecordStructs; } + if (settings.UnsignedRightShift) + { + flags |= ConversionFlags.SupportUnsignedRightShift; + } if (settings.InitAccessors) { flags |= ConversionFlags.SupportInitAccessors; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 0e93e78f5..f5396a9e2 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1280,6 +1280,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Unsigned right shift (>>>). + /// + public static string DecompilerSettings_UnsignedRightShift { + get { + return ResourceManager.GetString("DecompilerSettings.UnsignedRightShift", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use discards. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index ffa61461d..1368de08d 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -450,6 +450,9 @@ Are you sure you want to continue? Switch expressions + + Unsigned right shift (>>>) + Use discards From 8b9ba208475db8cf119a5d559fc0b26aeac187ca Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 3 Jun 2023 13:26:09 +0200 Subject: [PATCH 201/230] Builtin unsigned right shift operator. --- .../Pretty/CompoundAssignmentTest.cs | 74 +++++++++++++++++++ .../CSharp/ExpressionBuilder.cs | 34 ++++++++- .../Expressions/BinaryOperatorExpression.cs | 1 + .../CSharp/Transforms/PrettifyAssignments.cs | 2 + .../CompoundAssignmentInstruction.cs | 10 ++- 5 files changed, 115 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index abc6bb7e5..f0e437f49 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -1691,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } +#if CS110 + public static void ShortUnsignedRightShiftTest(short p, CustomClass c, CustomStruct2 s) + { + //X(p >>>= 5); + shortField >>>= 5; + ShortProp >>>= 5; + c.ShortField >>>= 5; + c.ShortProp >>>= 5; + s.ShortField >>>= 5; + s.ShortProp >>>= 5; + customClassField.ShortField >>>= 5; + customClassField.ShortProp >>>= 5; + otherCustomStructField.ShortField >>>= 5; + otherCustomStructField.ShortProp >>>= 5; + CustomClassProp.ShortField >>>= 5; + CustomClassProp.ShortProp >>>= 5; + GetClass().ShortField >>>= 5; + GetClass().ShortProp >>>= 5; + GetRefStruct().ShortField >>>= 5; + GetRefStruct().ShortProp >>>= 5; + GetRefShort() >>>= 5; + } +#endif + public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s) { //short l = 0; @@ -2053,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } +#if CS110 + public static void UshortUnsignedRightShiftTest(ushort p, CustomClass c, CustomStruct2 s) + { + //ushort l = 0; + //p >>>= 5; + //l >>>= 5; + ushortField >>>= 5; + UshortProp >>>= 5; + c.UshortField >>>= 5; + c.UshortProp >>>= 5; + s.UshortField >>>= 5; + s.UshortProp >>>= 5; + customClassField.UshortField >>>= 5; + customClassField.UshortProp >>>= 5; + otherCustomStructField.UshortField >>>= 5; + otherCustomStructField.UshortProp >>>= 5; + CustomClassProp.UshortField >>>= 5; + CustomClassProp.UshortProp >>>= 5; + GetClass().UshortField >>>= 5; + GetClass().UshortProp >>>= 5; + GetRefStruct().UshortField >>>= 5; + GetRefStruct().UshortProp >>>= 5; + GetRefUshort() >>>= 5; + } +#endif + public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s) { //ushort l = 0; @@ -2415,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } +#if CS110 + public static void IntUnsignedRightShiftTest(int p, CustomClass c, CustomStruct2 s) + { + X(p >>>= 5); + intField >>>= 5; + IntProp >>>= 5; + c.IntField >>>= 5; + c.IntProp >>>= 5; + s.IntField >>>= 5; + s.IntProp >>>= 5; + customClassField.IntField >>>= 5; + customClassField.IntProp >>>= 5; + otherCustomStructField.IntField >>>= 5; + otherCustomStructField.IntProp >>>= 5; + CustomClassProp.IntField >>>= 5; + CustomClassProp.IntProp >>>= 5; + GetClass().IntField >>>= 5; + GetClass().IntProp >>>= 5; + GetRefStruct().IntField >>>= 5; + GetRefStruct().IntProp >>>= 5; + GetRefInt() >>>= 5; + } +#endif + public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s) { //int l = 0; diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 91a86192a..20a1c7d8e 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1745,10 +1745,25 @@ namespace ICSharpCode.Decompiler.CSharp Sign sign = inst.Sign; var leftUType = NullableType.GetUnderlyingType(left.Type); - if (leftUType.IsCSharpSmallIntegerType() && sign != Sign.Unsigned && inst.UnderlyingResultType == StackType.I4) + bool couldUseUnsignedRightShift = ( + sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift + && (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType()) + ); + if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 && + (sign != Sign.Unsigned || couldUseUnsignedRightShift)) { // With small integer types, C# will promote to int and perform signed shifts. // We thus don't need any casts in this case. + // The >>> operator also promotes to signed int, but then performs an unsigned shift. + if (sign == Sign.Unsigned) + { + op = BinaryOperatorType.UnsignedShiftRight; + } + } + else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize()) + { + // Use C# 11 unsigned right shift operator. We don't need any casts in this case. + op = BinaryOperatorType.UnsignedShiftRight; } else { @@ -1890,7 +1905,22 @@ namespace ICSharpCode.Decompiler.CSharp case BinaryNumericOperator.ShiftLeft: return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft); case BinaryNumericOperator.ShiftRight: - return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); + if (inst.Sign == Sign.Unsigned && inst.Type.GetSign() == Sign.Signed) + { + Debug.Assert(settings.UnsignedRightShift); + return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight); + } + else if (inst.Sign == Sign.Unsigned && inst.Type.IsCSharpSmallIntegerType() && settings.UnsignedRightShift) + { + // For small unsigned integer types promoted to signed int, the sign bit will be zero, + // so there is no difference between signed and unsigned shift. + // However the IL still indicates which C# operator was used, so preserve that if the setting allows us to. + return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight); + } + else + { + return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); + } default: throw new ArgumentOutOfRangeException(); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs index 294b9f584..1386e1029 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -208,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax case BinaryOperatorType.NullCoalescing: return ExpressionType.Coalesce; case BinaryOperatorType.Range: + case BinaryOperatorType.UnsignedShiftRight: return ExpressionType.Extension; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs index d2ae9b2d5..7fb22fadb 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs @@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return AssignmentOperatorType.ShiftLeft; case BinaryOperatorType.ShiftRight: return AssignmentOperatorType.ShiftRight; + case BinaryOperatorType.UnsignedShiftRight: + return AssignmentOperatorType.UnsignedShiftRight; case BinaryOperatorType.BitwiseAnd: return AssignmentOperatorType.BitwiseAnd; case BinaryOperatorType.BitwiseOr: diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index c35f986e0..a25e39a96 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL return false; // operator not supported on pointer types } } - else if (type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) + else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt) { + // If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr): // "target.intptr *= 2;" is compiler error, but // "target.intptr *= (nint)2;" works if (settings != null && !settings.NativeIntegers) @@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL } if (binary.Sign != Sign.None) { + bool signMismatchAllowed = (binary.Sign == Sign.Unsigned && binary.Operator == BinaryNumericOperator.ShiftRight && (settings == null || settings.UnsignedRightShift)); if (type.IsCSharpSmallIntegerType()) { // C# will use numeric promotion to int, binary op must be signed - if (binary.Sign != Sign.Signed) + if (binary.Sign != Sign.Signed && !signMismatchAllowed) return false; } else { - // C# will use sign from type - if (type.GetSign() != binary.Sign) + // C# will use sign from type; except for right shift with C# 11 >>> operator. + if (type.GetSign() != binary.Sign && !signMismatchAllowed) return false; } } From a2019c5d78e25097a2cf847212eaa495d10ca9a6 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 3 Jun 2023 14:44:59 +0200 Subject: [PATCH 202/230] Avoid using `>>>` operator when the context expects a cast to unsigned anyway. --- .../TestCases/Pretty/TypeAnalysisTests.cs | 30 ++++++++++++++++++- .../CSharp/ExpressionBuilder.cs | 10 ++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs index 95f8e6269..7cd3368e6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs @@ -106,9 +106,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public uint RShiftByteWithZeroExtension(byte num) { + // zero extend -> cast to unsigned -> unsigned shift return (uint)num >> 8; } + public int RShiftByteWithZeroExtensionReturnAsSigned(byte num) + { +#if CS110 + // zero extend -> unsigned shift + return num >>> 8; +#else + // zero extend -> cast to unsigned -> unsigned shift -> cast to signed + return (int)((uint)num >> 8); +#endif + } + public int RShiftByteAsSByte(byte num) { return (sbyte)num >> 8; @@ -121,9 +133,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public uint RShiftSByteWithZeroExtension(sbyte num) { - return (uint)num >> 8; + return (uint)((byte)num >> 4); } + public uint RShiftSByteWithSignExtension(sbyte num) + { + // sign extend -> cast to unsigned -> unsigned shift + return (uint)num >> 4; + } + + public int RShiftSByteWithSignExtensionReturnAsSigned(sbyte num) + { +#if CS110 + // sign extend -> unsigned shift + return num >>> 4; +#else + // sign extend -> cast to unsigned -> unsigned shift -> cast to signed + return (int)((uint)num >> 4); +#endif + } public int RShiftSByteAsByte(sbyte num) { return (byte)num >> 8; diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 20a1c7d8e..6c0e30200 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1208,9 +1208,9 @@ namespace ICSharpCode.Decompiler.CSharp case BinaryNumericOperator.BitXor: return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context); case BinaryNumericOperator.ShiftLeft: - return HandleShift(inst, BinaryOperatorType.ShiftLeft); + return HandleShift(inst, BinaryOperatorType.ShiftLeft, context); case BinaryNumericOperator.ShiftRight: - return HandleShift(inst, BinaryOperatorType.ShiftRight); + return HandleShift(inst, BinaryOperatorType.ShiftRight, context); default: throw new ArgumentOutOfRangeException(); } @@ -1736,7 +1736,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op) + TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context) { var left = Translate(inst.Left); var right = Translate(inst.Right); @@ -1748,6 +1748,8 @@ namespace ICSharpCode.Decompiler.CSharp bool couldUseUnsignedRightShift = ( sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift && (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType()) + // If we need to cast to unsigned anyway, don't use >>> operator. + && context.TypeHint.GetSign() != Sign.Unsigned ); if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 && (sign != Sign.Unsigned || couldUseUnsignedRightShift)) @@ -1760,7 +1762,7 @@ namespace ICSharpCode.Decompiler.CSharp op = BinaryOperatorType.UnsignedShiftRight; } } - else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize()) + else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize() && leftUType.GetSign() == Sign.Signed) { // Use C# 11 unsigned right shift operator. We don't need any casts in this case. op = BinaryOperatorType.UnsignedShiftRight; From b6535a4d710bfc94dcc14d49fe5abc07aa7835e9 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sun, 4 Jun 2023 14:00:15 +0200 Subject: [PATCH 203/230] Verified WiX 3.14 would build an arm64 installer for us --- ILSpy.Installer/setup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ILSpy.Installer/setup.cs b/ILSpy.Installer/setup.cs index adc10d6d5..6b40f1ac1 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -38,6 +38,8 @@ namespace ILSpy.Installer #if ARM64 project.Platform = Platform.arm64; + // https://github.com/oleg-shilo/wixsharp/issues/1296#issuecomment-1575523854 3.11.2 won't do, need 3.14 + // Compiler.WixLocation = @"D:\GitWorkspace\_wix314-binaries"; #else project.Platform = Platform.x64; #endif From f12a3d35727ac6ff0a38128d5543a8c9fe232bed Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 7 Jun 2023 14:48:17 +0200 Subject: [PATCH 204/230] Fix #2984: Store pointing to v8.0 submission, building of arm64 setup --- .github/workflows/build-ilspy.yml | 17 +++++++++++++---- ILSpy.Installer/ILSpy.Installer.csproj | 4 ++-- ILSpy.Installer/setup.cs | 2 -- README.md | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index 980ba00c7..11faca8a4 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -90,11 +90,12 @@ jobs: if: matrix.configuration == 'release' run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries_arm64.zip .\ILSpy\bin\Release\net6.0-windows\win-arm64\publish\fwdependent\* - - name: Build Installer (x64 framework-dependent) + - name: Build Installer (x64 and arm64, framework-dependent) if: matrix.configuration == 'release' run: | msbuild ILSpy.Installer.sln /t:Restore /p:Configuration="Release" /p:Platform="Any CPU" msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" + msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" /p:DefineConstants="ARM64" # https://github.com/actions/upload-artifact - name: Upload VSIX (VS 2019) release build artifacts @@ -162,12 +163,20 @@ jobs: path: ${{ env.StagingDirectory }}\ILSpy_binaries_arm64.zip if-no-files-found: error - - name: Upload installer artifact + - name: Upload x64 installer artifact if: matrix.configuration == 'release' uses: actions/upload-artifact@v3 with: - name: ILSpy Installer ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) - path: ILSpy.Installer\wix\*.msi + name: ILSpy Installer x64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) + path: ILSpy.Installer\wix\*-x64.msi + if-no-files-found: error + + - name: Upload arm64 installer artifact + if: matrix.configuration == 'release' + uses: actions/upload-artifact@v3 + with: + name: ILSpy Installer arm64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) + path: ILSpy.Installer\wix\*-arm64.msi if-no-files-found: error - name: Upload ilspycmd release build artifacts diff --git a/ILSpy.Installer/ILSpy.Installer.csproj b/ILSpy.Installer/ILSpy.Installer.csproj index 5e6a1c768..bf8fa6fe5 100644 --- a/ILSpy.Installer/ILSpy.Installer.csproj +++ b/ILSpy.Installer/ILSpy.Installer.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/ILSpy.Installer/setup.cs b/ILSpy.Installer/setup.cs index 6b40f1ac1..adc10d6d5 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -38,8 +38,6 @@ namespace ILSpy.Installer #if ARM64 project.Platform = Platform.arm64; - // https://github.com/oleg-shilo/wixsharp/issues/1296#issuecomment-1575523854 3.11.2 won't do, need 3.14 - // Compiler.WixLocation = @"D:\GitWorkspace\_wix314-binaries"; #else project.Platform = Platform.x64; #endif diff --git a/README.md b/README.md index 92b843aaf..c57753c14 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ILSpy is the open-source .NET assembly browser and decompiler. -Download: [latest release](https://github.com/icsharpcode/ILSpy/releases) | [latest CI build (master)](https://github.com/icsharpcode/ILSpy/actions?query=workflow%3A%22Build+ILSpy%22+branch%3Amaster+is%3Asuccess+event%3Apush) | [Microsoft Store (RC & RTM versions only)](https://www.microsoft.com/store/apps/9MXFBKFVSQ13) +Download: [latest release](https://github.com/icsharpcode/ILSpy/releases) | [latest CI build (master)](https://github.com/icsharpcode/ILSpy/actions?query=workflow%3A%22Build+ILSpy%22+branch%3Amaster+is%3Asuccess+event%3Apush) | [Microsoft Store (RTM versions only)](https://apps.microsoft.com/store/detail/ilspy-fresh/XP8C26VDWLP4T4) Decompiler Frontends ------- From bdb32356635f2fdc66b9dfb8cd028675cce47883 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 7 Jun 2023 15:59:03 +0200 Subject: [PATCH 205/230] Lock looking for WiX binaries exclusively to packages folder --- ILSpy.Installer/setup.cs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ILSpy.Installer/setup.cs b/ILSpy.Installer/setup.cs index adc10d6d5..ce15c3089 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -71,7 +71,41 @@ namespace ILSpy.Installer new FileShortcut("ILSpy", @"%ProgramMenu%") }; + Compiler.WixLocation = GetWixBinLocationForPackage(); Compiler.BuildMsi(project, Path.Combine(Environment.CurrentDirectory, "wix", $"ILSpy-{AppPackage.Version}-{buildPlatform}.msi")); } + + // Copied from https://github.com/oleg-shilo/wixsharp/blob/c4f8615ce8e47c7162edb30656669d0d326f79ff/Source/src/WixSharp/Utilities/WixBinLocator.cs#L117 + private static string GetWixBinLocationForPackage() + { + //The global packages may be redirected with environment variable + //https://docs.microsoft.com/en-us/nuget/consume-packages/managing-the-global-packages-and-cache-folders + + string wixBinPackageDir; + var nugetPackagesEnvironmentVariable = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); + if (nugetPackagesEnvironmentVariable.IsNotEmpty() && Directory.Exists(nugetPackagesEnvironmentVariable)) + { + wixBinPackageDir = Path.Combine(nugetPackagesEnvironmentVariable, "wixsharp.wix.bin"); + } + else + { + wixBinPackageDir = @"%userprofile%\.nuget\packages\wixsharp.wix.bin".ExpandEnvVars(); + } + + if (Directory.Exists(wixBinPackageDir)) + { + Version greatestWixBinVersion = System.IO.Directory.GetDirectories(wixBinPackageDir) + .Select(dirPath => new Version(dirPath.PathGetFileName())) + .OrderDescending() + .FirstOrDefault(); + + if (greatestWixBinVersion != null) + { + return wixBinPackageDir.PathJoin(greatestWixBinVersion.ToString(), @"tools\bin"); + } + } + + return ""; + } } } From a929fcb5202824e3c061f4824c7fc9ba867d55af Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 11 Jun 2023 21:58:48 +0200 Subject: [PATCH 206/230] Make "Metadata" tree node text translatable. --- ILSpy/Metadata/MetadataTreeNode.cs | 5 +++-- ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ILSpy/Metadata/MetadataTreeNode.cs b/ILSpy/Metadata/MetadataTreeNode.cs index f6b0ea37c..5511d5ad5 100644 --- a/ILSpy/Metadata/MetadataTreeNode.cs +++ b/ILSpy/Metadata/MetadataTreeNode.cs @@ -25,6 +25,7 @@ using System.Windows.Data; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; @@ -32,7 +33,7 @@ namespace ICSharpCode.ILSpy.Metadata { class MetadataTreeNode : ILSpyTreeNode { - private PEFile module; + private readonly PEFile module; private AssemblyTreeNode assemblyTreeNode; public MetadataTreeNode(PEFile module, AssemblyTreeNode assemblyTreeNode) @@ -42,7 +43,7 @@ namespace ICSharpCode.ILSpy.Metadata this.LazyLoading = true; } - public override object Text => "Metadata"; + public override object Text => Resources.Metadata; public override object Icon => Images.Library; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 0e93e78f5..6ff0a2fda 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1892,6 +1892,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Metadata. + /// + public static string Metadata { + get { + return ResourceManager.GetString("Metadata", resourceCulture); + } + } + /// /// Looks up a localized string similar to Misc. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index ffa61461d..04fc39c00 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -651,6 +651,9 @@ Are you sure you want to continue? Manage assembly _lists... + + Metadata + Misc From 13227e433ee136d6ac0d8b43fa0fe28a9a6458eb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 11 Jun 2023 22:02:54 +0200 Subject: [PATCH 207/230] Fix compound assignments with local variables. --- .../Pretty/CompoundAssignmentTest.cs | 992 ++++++++++-------- .../CSharp/ExpressionBuilder.cs | 2 +- .../CSharp/Transforms/PrettifyAssignments.cs | 25 +- 3 files changed, 595 insertions(+), 424 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index e9c391ae3..1a0f2b6b2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -287,6 +287,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static string StaticStringProperty { get; set; } + private static void Use(ref byte b) + { + } + + private static void Use(ref sbyte b) + { + } + + private static void Use(ref T num) + { + } + private static CustomStruct2 GetStruct() { throw new NotImplementedException(); @@ -781,9 +793,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteAddTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p += 5; - //l += 5; + byte b = 0; + p += 5; + b += 5; + Use(ref b); byteField += 5; ByteProp += 5; c.ByteField += 5; @@ -807,9 +820,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteSubtractTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p -= 5; - //l -= 5; + byte b = 0; + p -= 5; + b -= 5; + Use(ref b); byteField -= 5; ByteProp -= 5; c.ByteField -= 5; @@ -833,9 +847,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteMultiplyTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p *= 5; - //l *= 5; + byte b = 0; + p *= 5; + b *= 5; + Use(ref b); byteField *= 5; ByteProp *= 5; c.ByteField *= 5; @@ -859,9 +874,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteDivideTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p /= 5; - //l /= 5; + byte b = 0; + p /= 5; + b /= 5; + Use(ref b); byteField /= 5; ByteProp /= 5; c.ByteField /= 5; @@ -885,9 +901,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteModulusTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p %= 5; - //l %= 5; + byte b = 0; + p %= 5; + b %= 5; + Use(ref b); byteField %= 5; ByteProp %= 5; c.ByteField %= 5; @@ -911,9 +928,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteLeftShiftTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p <<= 5; - //l <<= 5; + byte b = 0; + p <<= 5; + b <<= 5; + Use(ref b); byteField <<= 5; ByteProp <<= 5; c.ByteField <<= 5; @@ -937,9 +955,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteRightShiftTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p >>= 5; - //l >>= 5; + byte b = 0; + p >>= 5; + b >>= 5; + Use(ref b); byteField >>= 5; ByteProp >>= 5; c.ByteField >>= 5; @@ -963,9 +982,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteBitAndTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p &= 5; - //l &= 5; + byte b = 0; + p &= c.ByteField; + b &= c.ByteField; + Use(ref b); byteField &= 5; ByteProp &= 5; c.ByteField &= 5; @@ -989,9 +1009,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteBitOrTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p |= 5; - //l |= 5; + byte b = 0; + p |= c.ByteField; + b |= c.ByteField; + Use(ref b); byteField |= 5; ByteProp |= 5; c.ByteField |= 5; @@ -1015,9 +1036,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ByteBitXorTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //p ^= 5; - //l ^= 5; + byte b = 0; + p ^= c.ByteField; + b ^= c.ByteField; + Use(ref b); byteField ^= 5; ByteProp ^= 5; c.ByteField ^= 5; @@ -1041,9 +1063,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void BytePostIncTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //X(p++); - //X(l++); + byte b = 0; + X(p++); + X(b++); + Use(ref b); X(byteField++); X(ByteProp++); X(c.ByteField++); @@ -1067,9 +1090,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void BytePreIncTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //X(++p); - //X(++l); + byte b = 0; + X(++p); + X(++b); + Use(ref b); X(++byteField); X(++ByteProp); X(++c.ByteField); @@ -1092,9 +1116,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void BytePostDecTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //X(p--); - //X(l--); + byte b = 0; + X(p--); + X(b--); + Use(ref b); X(byteField--); X(ByteProp--); X(c.ByteField--); @@ -1118,9 +1143,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void BytePreDecTest(byte p, CustomClass c, CustomStruct2 s) { - //byte l = 0; - //X(--p); - //X(--l); + byte b = 0; + X(--p); + X(--b); + Use(ref b); X(--byteField); X(--ByteProp); X(--c.ByteField); @@ -1143,9 +1169,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void SbyteAddTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p += 5; - //l += 5; + sbyte b = 0; + p += 5; + b += 5; + Use(ref b); sbyteField += 5; SbyteProp += 5; c.SbyteField += 5; @@ -1169,9 +1196,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteSubtractTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p -= 5; - //l -= 5; + sbyte b = 0; + p -= 5; + b -= 5; + Use(ref b); sbyteField -= 5; SbyteProp -= 5; c.SbyteField -= 5; @@ -1195,9 +1223,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteMultiplyTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p *= 5; - //l *= 5; + sbyte b = 0; + p *= 5; + b *= 5; + Use(ref b); sbyteField *= 5; SbyteProp *= 5; c.SbyteField *= 5; @@ -1221,9 +1250,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteDivideTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p /= 5; - //l /= 5; + sbyte b = 0; + p /= 5; + b /= 5; + Use(ref b); sbyteField /= 5; SbyteProp /= 5; c.SbyteField /= 5; @@ -1247,9 +1277,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteModulusTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p %= 5; - //l %= 5; + sbyte b = 0; + p %= 5; + b %= 5; + Use(ref b); sbyteField %= 5; SbyteProp %= 5; c.SbyteField %= 5; @@ -1273,9 +1304,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteLeftShiftTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p <<= 5; - //l <<= 5; + sbyte b = 0; + p <<= 5; + b <<= 5; + Use(ref b); sbyteField <<= 5; SbyteProp <<= 5; c.SbyteField <<= 5; @@ -1299,9 +1331,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteRightShiftTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p >>= 5; - //l >>= 5; + sbyte b = 0; + p >>= 5; + b >>= 5; + Use(ref b); sbyteField >>= 5; SbyteProp >>= 5; c.SbyteField >>= 5; @@ -1325,9 +1358,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteBitAndTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p &= 5; - //l &= 5; + sbyte b = 0; + p &= 5; + b &= 5; + Use(ref b); sbyteField &= 5; SbyteProp &= 5; c.SbyteField &= 5; @@ -1351,9 +1385,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteBitOrTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p |= 5; - //l |= 5; + sbyte b = 0; + p |= 5; + b |= 5; + Use(ref b); sbyteField |= 5; SbyteProp |= 5; c.SbyteField |= 5; @@ -1377,9 +1412,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbyteBitXorTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //p ^= 5; - //l ^= 5; + sbyte b = 0; + p ^= 5; + b ^= 5; + Use(ref b); sbyteField ^= 5; SbyteProp ^= 5; c.SbyteField ^= 5; @@ -1403,9 +1439,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbytePostIncTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //X(p++); - //X(l++); + sbyte b = 0; + X(p++); + X(b++); + Use(ref b); X(sbyteField++); X(SbyteProp++); X(c.SbyteField++); @@ -1429,9 +1466,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbytePreIncTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //X(++p); - //X(++l); + sbyte b = 0; + X(++p); + X(++b); + Use(ref b); X(++sbyteField); X(++SbyteProp); X(++c.SbyteField); @@ -1454,9 +1492,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void SbytePostDecTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //X(p--); - //X(l--); + sbyte b = 0; + X(p--); + X(b--); + Use(ref b); X(sbyteField--); X(SbyteProp--); X(c.SbyteField--); @@ -1480,9 +1519,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SbytePreDecTest(sbyte p, CustomClass c, CustomStruct2 s) { - //sbyte l = 0; - //X(--p); - //X(--l); + sbyte b = 0; + X(--p); + X(--b); + Use(ref b); X(--sbyteField); X(--SbyteProp); X(--c.SbyteField); @@ -1505,9 +1545,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void ShortAddTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p += 5; - //l += 5; + short num = 0; + p += 5; + num += 5; + Use(ref num); shortField += 5; ShortProp += 5; c.ShortField += 5; @@ -1531,9 +1572,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortSubtractTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p -= 5; - //l -= 5; + short num = 0; + p -= 5; + num -= 5; + Use(ref num); shortField -= 5; ShortProp -= 5; c.ShortField -= 5; @@ -1557,9 +1599,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortMultiplyTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p *= 5; - //l *= 5; + short num = 0; + p *= 5; + num *= 5; + Use(ref num); shortField *= 5; ShortProp *= 5; c.ShortField *= 5; @@ -1583,9 +1626,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortDivideTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p /= 5; - //l /= 5; + short num = 0; + p /= 5; + num /= 5; + Use(ref num); shortField /= 5; ShortProp /= 5; c.ShortField /= 5; @@ -1609,9 +1653,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortModulusTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p %= 5; - //l %= 5; + short num = 0; + p %= 5; + num %= 5; + Use(ref num); shortField %= 5; ShortProp %= 5; c.ShortField %= 5; @@ -1635,9 +1680,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortLeftShiftTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p <<= 5; - //l <<= 5; + short num = 0; + p <<= 5; + num <<= 5; + Use(ref num); shortField <<= 5; ShortProp <<= 5; c.ShortField <<= 5; @@ -1661,9 +1707,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortRightShiftTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p >>= 5; - //l >>= 5; + short num = 0; + p >>= 5; + num >>= 5; + Use(ref num); shortField >>= 5; ShortProp >>= 5; c.ShortField >>= 5; @@ -1687,9 +1734,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p &= 5; - //l &= 5; + short num = 0; + p &= 5; + num &= 5; + Use(ref num); shortField &= 5; ShortProp &= 5; c.ShortField &= 5; @@ -1713,9 +1761,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortBitOrTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p |= 5; - //l |= 5; + short num = 0; + p |= 5; + num |= 5; + Use(ref num); shortField |= 5; ShortProp |= 5; c.ShortField |= 5; @@ -1739,9 +1788,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortBitXorTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //p ^= 5; - //l ^= 5; + short num = 0; + p ^= 5; + num ^= 5; + Use(ref num); shortField ^= 5; ShortProp ^= 5; c.ShortField ^= 5; @@ -1765,9 +1815,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortPostIncTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //X(p++); - //X(l++); + short num = 0; + X(p++); + X(num++); + Use(ref num); X(shortField++); X(ShortProp++); X(c.ShortField++); @@ -1791,9 +1842,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortPreIncTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //X(++p); - //X(++l); + short num = 0; + X(++p); + X(++num); + Use(ref num); X(++shortField); X(++ShortProp); X(++c.ShortField); @@ -1816,9 +1868,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void ShortPostDecTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //X(p--); - //X(l--); + short num = 0; + X(p--); + X(num--); + Use(ref num); X(shortField--); X(ShortProp--); X(c.ShortField--); @@ -1842,9 +1895,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ShortPreDecTest(short p, CustomClass c, CustomStruct2 s) { - //short l = 0; - //X(--p); - //X(--l); + short num = 0; + X(--p); + X(--num); + Use(ref num); X(--shortField); X(--ShortProp); X(--c.ShortField); @@ -1867,9 +1921,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UshortAddTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p += 5; - //l += 5; + ushort num = 0; + p += 5; + num += 5; + Use(ref num); ushortField += 5; UshortProp += 5; c.UshortField += 5; @@ -1893,9 +1948,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortSubtractTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p -= 5; - //l -= 5; + ushort num = 0; + p -= 5; + num -= 5; + Use(ref num); ushortField -= 5; UshortProp -= 5; c.UshortField -= 5; @@ -1919,9 +1975,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortMultiplyTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p *= 5; - //l *= 5; + ushort num = 0; + p *= 5; + num *= 5; + Use(ref num); ushortField *= 5; UshortProp *= 5; c.UshortField *= 5; @@ -1945,9 +2002,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortDivideTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p /= 5; - //l /= 5; + ushort num = 0; + p /= 5; + num /= 5; + Use(ref num); ushortField /= 5; UshortProp /= 5; c.UshortField /= 5; @@ -1971,9 +2029,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortModulusTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p %= 5; - //l %= 5; + ushort num = 0; + p %= 5; + num %= 5; + Use(ref num); ushortField %= 5; UshortProp %= 5; c.UshortField %= 5; @@ -1997,9 +2056,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortLeftShiftTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p <<= 5; - //l <<= 5; + ushort num = 0; + p <<= 5; + num <<= 5; + Use(ref num); ushortField <<= 5; UshortProp <<= 5; c.UshortField <<= 5; @@ -2023,9 +2083,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortRightShiftTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p >>= 5; - //l >>= 5; + ushort num = 0; + p >>= 5; + num >>= 5; + Use(ref num); ushortField >>= 5; UshortProp >>= 5; c.UshortField >>= 5; @@ -2049,9 +2110,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p &= 5; - //l &= 5; + ushort num = 0; + p &= c.UshortField; + num &= c.UshortField; + Use(ref num); ushortField &= 5; UshortProp &= 5; c.UshortField &= 5; @@ -2075,9 +2137,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortBitOrTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p |= 5; - //l |= 5; + ushort num = 0; + p |= c.UshortField; + num |= c.UshortField; + Use(ref num); ushortField |= 5; UshortProp |= 5; c.UshortField |= 5; @@ -2101,9 +2164,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortBitXorTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //p ^= 5; - //l ^= 5; + ushort num = 0; + p ^= c.UshortField; + num ^= c.UshortField; + Use(ref num); ushortField ^= 5; UshortProp ^= 5; c.UshortField ^= 5; @@ -2127,9 +2191,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortPostIncTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //X(p++); - //X(l++); + ushort num = 0; + X(p++); + X(num++); + Use(ref num); X(ushortField++); X(UshortProp++); X(c.UshortField++); @@ -2153,9 +2218,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortPreIncTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //X(++p); - //X(++l); + ushort num = 0; + X(++p); + X(++num); + Use(ref num); X(++ushortField); X(++UshortProp); X(++c.UshortField); @@ -2178,9 +2244,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UshortPostDecTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //X(p--); - //X(l--); + ushort num = 0; + X(p--); + X(num--); + Use(ref num); X(ushortField--); X(UshortProp--); X(c.UshortField--); @@ -2204,9 +2271,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UshortPreDecTest(ushort p, CustomClass c, CustomStruct2 s) { - //ushort l = 0; - //X(--p); - //X(--l); + ushort num = 0; + X(--p); + X(--num); + Use(ref num); X(--ushortField); X(--UshortProp); X(--c.UshortField); @@ -2229,9 +2297,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void IntAddTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p += 5; - //l += 5; + int num = 0; + p += 5; + num += 5; + Use(ref num); intField += 5; IntProp += 5; c.IntField += 5; @@ -2255,9 +2324,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntSubtractTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p -= 5; - //l -= 5; + int num = 0; + p -= 5; + num -= 5; + Use(ref num); intField -= 5; IntProp -= 5; c.IntField -= 5; @@ -2281,9 +2351,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntMultiplyTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p *= 5; - //l *= 5; + int num = 0; + p *= 5; + num *= 5; + Use(ref num); intField *= 5; IntProp *= 5; c.IntField *= 5; @@ -2307,9 +2378,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntDivideTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p /= 5; - //l /= 5; + int num = 0; + p /= 5; + num /= 5; + Use(ref num); intField /= 5; IntProp /= 5; c.IntField /= 5; @@ -2333,9 +2405,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntModulusTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p %= 5; - //l %= 5; + int num = 0; + p %= 5; + num %= 5; + Use(ref num); intField %= 5; IntProp %= 5; c.IntField %= 5; @@ -2359,9 +2432,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntLeftShiftTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p <<= 5; - //l <<= 5; + int num = 0; + p <<= 5; + num <<= 5; + Use(ref num); intField <<= 5; IntProp <<= 5; c.IntField <<= 5; @@ -2385,9 +2459,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntRightShiftTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p >>= 5; - //l >>= 5; + int num = 0; + p >>= 5; + num >>= 5; + Use(ref num); intField >>= 5; IntProp >>= 5; c.IntField >>= 5; @@ -2411,9 +2486,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p &= 5; - //l &= 5; + int num = 0; + p &= 5; + num &= 5; + Use(ref num); intField &= 5; IntProp &= 5; c.IntField &= 5; @@ -2437,9 +2513,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntBitOrTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p |= 5; - //l |= 5; + int num = 0; + p |= 5; + num |= 5; + Use(ref num); intField |= 5; IntProp |= 5; c.IntField |= 5; @@ -2463,9 +2540,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntBitXorTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //p ^= 5; - //l ^= 5; + int num = 0; + p ^= 5; + num ^= 5; + Use(ref num); intField ^= 5; IntProp ^= 5; c.IntField ^= 5; @@ -2489,9 +2567,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntPostIncTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //X(p++); - //X(l++); + int num = 0; + X(p++); + X(num++); + Use(ref num); X(intField++); X(IntProp++); X(c.IntField++); @@ -2515,9 +2594,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntPreIncTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //X(++p); - //X(++l); + int num = 0; + X(++p); + X(++num); + Use(ref num); X(++intField); X(++IntProp); X(++c.IntField); @@ -2540,9 +2620,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void IntPostDecTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //X(p--); - //X(l--); + int num = 0; + X(p--); + X(num--); + Use(ref num); X(intField--); X(IntProp--); X(c.IntField--); @@ -2566,9 +2647,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void IntPreDecTest(int p, CustomClass c, CustomStruct2 s) { - //int l = 0; - //X(--p); - //X(--l); + int num = 0; + X(--p); + X(--num); + Use(ref num); X(--intField); X(--IntProp); X(--c.IntField); @@ -2591,9 +2673,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UintAddTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p += 5u; - //l += 5u; + uint num = 0u; + p += 5; + num += 5; + Use(ref num); uintField += 5u; UintProp += 5u; c.UintField += 5u; @@ -2617,9 +2700,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintSubtractTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p -= 5u; - //l -= 5u; + uint num = 0u; + p -= 5; + num -= 5; + Use(ref num); uintField -= 5u; UintProp -= 5u; c.UintField -= 5u; @@ -2643,9 +2727,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintMultiplyTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p *= 5u; - //l *= 5u; + uint num = 0u; + p *= 5; + num *= 5; + Use(ref num); uintField *= 5u; UintProp *= 5u; c.UintField *= 5u; @@ -2669,9 +2754,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintDivideTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p /= 5u; - //l /= 5u; + uint num = 0u; + p /= 5; + num /= 5; + Use(ref num); uintField /= 5u; UintProp /= 5u; c.UintField /= 5u; @@ -2695,9 +2781,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintModulusTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p %= 5u; - //l %= 5u; + uint num = 0u; + p %= 5; + num %= 5; + Use(ref num); uintField %= 5u; UintProp %= 5u; c.UintField %= 5u; @@ -2721,9 +2808,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintLeftShiftTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p <<= 5; - //l <<= 5; + uint num = 0u; + p <<= 5; + num <<= 5; + Use(ref num); uintField <<= 5; UintProp <<= 5; c.UintField <<= 5; @@ -2747,9 +2835,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintRightShiftTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p >>= 5; - //l >>= 5; + uint num = 0u; + p >>= 5; + num >>= 5; + Use(ref num); uintField >>= 5; UintProp >>= 5; c.UintField >>= 5; @@ -2773,9 +2862,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintBitAndTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p &= 5u; - //l &= 5u; + uint num = 0u; + p &= 5u; + num &= 5u; + Use(ref num); uintField &= 5u; UintProp &= 5u; c.UintField &= 5u; @@ -2799,9 +2889,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintBitOrTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p |= 5u; - //l |= 5u; + uint num = 0u; + p |= 5u; + num |= 5u; + Use(ref num); uintField |= 5u; UintProp |= 5u; c.UintField |= 5u; @@ -2825,9 +2916,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintBitXorTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //p ^= 5u; - //l ^= 5u; + uint num = 0u; + p ^= 5u; + num ^= 5u; + Use(ref num); uintField ^= 5u; UintProp ^= 5u; c.UintField ^= 5u; @@ -2851,9 +2943,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintPostIncTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //X(p++); - //X(l++); + uint num = 0u; + X(p++); + X(num++); + Use(ref num); X(uintField++); X(UintProp++); X(c.UintField++); @@ -2877,9 +2970,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintPreIncTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //X(++p); - //X(++l); + uint num = 0u; + X(++p); + X(++num); + Use(ref num); X(++uintField); X(++UintProp); X(++c.UintField); @@ -2902,9 +2996,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UintPostDecTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //X(p--); - //X(l--); + uint num = 0u; + X(p--); + X(num--); + Use(ref num); X(uintField--); X(UintProp--); X(c.UintField--); @@ -2928,9 +3023,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UintPreDecTest(uint p, CustomClass c, CustomStruct2 s) { - //uint l = 0; - //X(--p); - //X(--l); + uint num = 0u; + X(--p); + X(--num); + Use(ref num); X(--uintField); X(--UintProp); X(--c.UintField); @@ -2953,9 +3049,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void LongAddTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p += 5L; - //l += 5L; + long num = 0L; + p += 5; + num += 5; + Use(ref num); longField += 5L; LongProp += 5L; c.LongField += 5L; @@ -2979,9 +3076,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongSubtractTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p -= 5L; - //l -= 5L; + long num = 0L; + p -= 5; + num -= 5; + Use(ref num); longField -= 5L; LongProp -= 5L; c.LongField -= 5L; @@ -3005,9 +3103,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongMultiplyTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p *= 5L; - //l *= 5L; + long num = 0L; + p *= 5; + num *= 5; + Use(ref num); longField *= 5L; LongProp *= 5L; c.LongField *= 5L; @@ -3031,9 +3130,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongDivideTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p /= 5L; - //l /= 5L; + long num = 0L; + p /= 5; + num /= 5; + Use(ref num); longField /= 5L; LongProp /= 5L; c.LongField /= 5L; @@ -3057,9 +3157,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongModulusTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p %= 5L; - //l %= 5L; + long num = 0L; + p %= 5; + num %= 5; + Use(ref num); longField %= 5L; LongProp %= 5L; c.LongField %= 5L; @@ -3083,9 +3184,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongLeftShiftTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p <<= 5; - //l <<= 5; + long num = 0L; + p <<= 5; + num <<= 5; + Use(ref num); longField <<= 5; LongProp <<= 5; c.LongField <<= 5; @@ -3109,9 +3211,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongRightShiftTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p >>= 5; - //l >>= 5; + long num = 0L; + p >>= 5; + num >>= 5; + Use(ref num); longField >>= 5; LongProp >>= 5; c.LongField >>= 5; @@ -3135,9 +3238,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongBitAndTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p &= 5L; - //l &= 5L; + long num = 0L; + p &= 5; + num &= 5; + Use(ref num); longField &= 5L; LongProp &= 5L; c.LongField &= 5L; @@ -3161,9 +3265,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongBitOrTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p |= 5L; - //l |= 5L; + long num = 0L; + p |= 5; + num |= 5; + Use(ref num); longField |= 5L; LongProp |= 5L; c.LongField |= 5L; @@ -3187,9 +3292,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongBitXorTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //p ^= 5L; - //l ^= 5L; + long num = 0L; + p ^= 5; + num ^= 5; + Use(ref num); longField ^= 5L; LongProp ^= 5L; c.LongField ^= 5L; @@ -3213,9 +3319,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongPostIncTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //X(p++); - //X(l++); + long num = 0L; + X(p++); + X(num++); + Use(ref num); X(longField++); X(LongProp++); X(c.LongField++); @@ -3239,9 +3346,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongPreIncTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //X(++p); - //X(++l); + long num = 0L; + X(++p); + X(++num); + Use(ref num); X(++longField); X(++LongProp); X(++c.LongField); @@ -3264,9 +3372,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void LongPostDecTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //X(p--); - //X(l--); + long num = 0L; + X(p--); + X(num--); + Use(ref num); X(longField--); X(LongProp--); X(c.LongField--); @@ -3290,9 +3399,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void LongPreDecTest(long p, CustomClass c, CustomStruct2 s) { - //long l = 0; - //X(--p); - //X(--l); + long num = 0L; + X(--p); + X(--num); + Use(ref num); X(--longField); X(--LongProp); X(--c.LongField); @@ -3315,9 +3425,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UlongAddTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p += 5uL; - //l += 5uL; + ulong num = 0uL; + p += 5; + num += 5; + Use(ref num); ulongField += 5uL; UlongProp += 5uL; c.UlongField += 5uL; @@ -3341,9 +3452,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongSubtractTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p -= 5uL; - //l -= 5uL; + ulong num = 0uL; + p -= 5; + num -= 5; + Use(ref num); ulongField -= 5uL; UlongProp -= 5uL; c.UlongField -= 5uL; @@ -3367,9 +3479,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongMultiplyTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p *= 5uL; - //l *= 5uL; + ulong num = 0uL; + p *= 5; + num *= 5; + Use(ref num); ulongField *= 5uL; UlongProp *= 5uL; c.UlongField *= 5uL; @@ -3393,9 +3506,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongDivideTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p /= 5uL; - //l /= 5uL; + ulong num = 0uL; + p /= 5; + num /= 5; + Use(ref num); ulongField /= 5uL; UlongProp /= 5uL; c.UlongField /= 5uL; @@ -3419,9 +3533,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongModulusTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p %= 5uL; - //l %= 5uL; + ulong num = 0uL; + p %= 5; + num %= 5; + Use(ref num); ulongField %= 5uL; UlongProp %= 5uL; c.UlongField %= 5uL; @@ -3445,9 +3560,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongLeftShiftTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p <<= 5; - //l <<= 5; + ulong num = 0uL; + p <<= 5; + num <<= 5; + Use(ref num); ulongField <<= 5; UlongProp <<= 5; c.UlongField <<= 5; @@ -3471,9 +3587,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongRightShiftTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p >>= 5; - //l >>= 5; + ulong num = 0uL; + p >>= 5; + num >>= 5; + Use(ref num); ulongField >>= 5; UlongProp >>= 5; c.UlongField >>= 5; @@ -3497,9 +3614,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongBitAndTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p &= 5uL; - //l &= 5uL; + ulong num = 0uL; + p &= 5; + num &= 5; + Use(ref num); ulongField &= 5uL; UlongProp &= 5uL; c.UlongField &= 5uL; @@ -3523,9 +3641,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongBitOrTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p |= 5uL; - //l |= 5uL; + ulong num = 0uL; + p |= 5; + num |= 5; + Use(ref num); ulongField |= 5uL; UlongProp |= 5uL; c.UlongField |= 5uL; @@ -3549,9 +3668,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongBitXorTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //p ^= 5uL; - //l ^= 5uL; + ulong num = 0uL; + p ^= 5; + num ^= 5; + Use(ref num); ulongField ^= 5uL; UlongProp ^= 5uL; c.UlongField ^= 5uL; @@ -3575,9 +3695,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongPostIncTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //X(p++); - //X(l++); + ulong num = 0uL; + X(p++); + X(num++); + Use(ref num); X(ulongField++); X(UlongProp++); X(c.UlongField++); @@ -3601,9 +3722,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongPreIncTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //X(++p); - //X(++l); + ulong num = 0uL; + X(++p); + X(++num); + Use(ref num); X(++ulongField); X(++UlongProp); X(++c.UlongField); @@ -3626,9 +3748,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void UlongPostDecTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //X(p--); - //X(l--); + ulong num = 0uL; + X(p--); + X(num--); + Use(ref num); X(ulongField--); X(UlongProp--); X(c.UlongField--); @@ -3652,9 +3775,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UlongPreDecTest(ulong p, CustomClass c, CustomStruct2 s) { - //ulong l = 0; - //X(--p); - //X(--l); + ulong num = 0uL; + X(--p); + X(--num); + Use(ref num); X(--ulongField); X(--UlongProp); X(--c.UlongField); @@ -3677,9 +3801,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void CustomClassAddTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p += (CustomClass)null; - //l += (CustomClass)null; + CustomClass num = null; + p += (CustomClass)null; + num += (CustomClass)null; + Use(ref num); customClassField += (CustomClass)null; CustomClassProp += (CustomClass)null; c.CustomClassField += (CustomClass)null; @@ -3703,9 +3828,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassSubtractTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p -= (CustomClass)null; - //l -= (CustomClass)null; + CustomClass num = null; + p -= (CustomClass)null; + num -= (CustomClass)null; + Use(ref num); customClassField -= (CustomClass)null; CustomClassProp -= (CustomClass)null; c.CustomClassField -= (CustomClass)null; @@ -3729,9 +3855,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassMultiplyTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p *= (CustomClass)null; - //l *= (CustomClass)null; + CustomClass num = null; + p *= (CustomClass)null; + num *= (CustomClass)null; + Use(ref num); customClassField *= (CustomClass)null; CustomClassProp *= (CustomClass)null; c.CustomClassField *= (CustomClass)null; @@ -3755,9 +3882,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassDivideTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p /= (CustomClass)null; - //l /= (CustomClass)null; + CustomClass num = null; + p /= (CustomClass)null; + num /= (CustomClass)null; + Use(ref num); customClassField /= (CustomClass)null; CustomClassProp /= (CustomClass)null; c.CustomClassField /= (CustomClass)null; @@ -3781,9 +3909,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassModulusTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p %= (CustomClass)null; - //l %= (CustomClass)null; + CustomClass num = null; + p %= (CustomClass)null; + num %= (CustomClass)null; + Use(ref num); customClassField %= (CustomClass)null; CustomClassProp %= (CustomClass)null; c.CustomClassField %= (CustomClass)null; @@ -3807,9 +3936,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassLeftShiftTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p <<= 5; - //l <<= 5; + CustomClass num = null; + p <<= 5; + num <<= 5; + Use(ref num); customClassField <<= 5; CustomClassProp <<= 5; c.CustomClassField <<= 5; @@ -3833,9 +3963,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassRightShiftTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p >>= 5; - //l >>= 5; + CustomClass num = null; + p >>= 5; + num >>= 5; + Use(ref num); customClassField >>= 5; CustomClassProp >>= 5; c.CustomClassField >>= 5; @@ -3859,9 +3990,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassBitAndTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p &= (CustomClass)null; - //l &= (CustomClass)null; + CustomClass num = null; + p &= (CustomClass)null; + num &= (CustomClass)null; + Use(ref num); customClassField &= (CustomClass)null; CustomClassProp &= (CustomClass)null; c.CustomClassField &= (CustomClass)null; @@ -3885,9 +4017,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassBitOrTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p |= (CustomClass)null; - //l |= (CustomClass)null; + CustomClass num = null; + p |= (CustomClass)null; + num |= (CustomClass)null; + Use(ref num); customClassField |= (CustomClass)null; CustomClassProp |= (CustomClass)null; c.CustomClassField |= (CustomClass)null; @@ -3911,9 +4044,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassBitXorTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //p ^= (CustomClass)null; - //l ^= (CustomClass)null; + CustomClass num = null; + p ^= (CustomClass)null; + num ^= (CustomClass)null; + Use(ref num); customClassField ^= (CustomClass)null; CustomClassProp ^= (CustomClass)null; c.CustomClassField ^= (CustomClass)null; @@ -3937,9 +4071,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassPostIncTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //X(p++); - //X(l++); + CustomClass num = null; + X(p++); + X(num++); + Use(ref num); X(customClassField++); X(CustomClassProp++); X(c.CustomClassField++); @@ -3963,9 +4098,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassPreIncTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //X(++p); - //X(++l); + CustomClass num = null; + X(++p); + X(++num); + Use(ref num); X(++customClassField); X(++CustomClassProp); X(++c.CustomClassField); @@ -3988,9 +4124,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void CustomClassPostDecTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //X(p--); - //X(l--); + CustomClass num = null; + X(p--); + X(num--); + Use(ref num); X(customClassField--); X(CustomClassProp--); X(c.CustomClassField--); @@ -4014,9 +4151,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomClassPreDecTest(CustomClass p, CustomClass c, CustomStruct2 s) { - //CustomClass l = null; - //X(--p); - //X(--l); + CustomClass num = null; + X(--p); + X(--num); + Use(ref num); X(--customClassField); X(--CustomClassProp); X(--c.CustomClassField); @@ -4039,9 +4177,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void CustomStructAddTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p += default(CustomStruct); - //l += default(CustomStruct); + CustomStruct num = default(CustomStruct); + p += default(CustomStruct); + num += default(CustomStruct); + Use(ref num); customStructField += default(CustomStruct); CustomStructProp += default(CustomStruct); c.CustomStructField += default(CustomStruct); @@ -4065,9 +4204,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructSubtractTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p -= default(CustomStruct); - //l -= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p -= default(CustomStruct); + num -= default(CustomStruct); + Use(ref num); customStructField -= default(CustomStruct); CustomStructProp -= default(CustomStruct); c.CustomStructField -= default(CustomStruct); @@ -4091,9 +4231,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructMultiplyTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p *= default(CustomStruct); - //l *= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p *= default(CustomStruct); + num *= default(CustomStruct); + Use(ref num); customStructField *= default(CustomStruct); CustomStructProp *= default(CustomStruct); c.CustomStructField *= default(CustomStruct); @@ -4117,9 +4258,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructDivideTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p /= default(CustomStruct); - //l /= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p /= default(CustomStruct); + num /= default(CustomStruct); + Use(ref num); customStructField /= default(CustomStruct); CustomStructProp /= default(CustomStruct); c.CustomStructField /= default(CustomStruct); @@ -4143,9 +4285,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructModulusTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p %= default(CustomStruct); - //l %= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p %= default(CustomStruct); + num %= default(CustomStruct); + Use(ref num); customStructField %= default(CustomStruct); CustomStructProp %= default(CustomStruct); c.CustomStructField %= default(CustomStruct); @@ -4169,9 +4312,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructLeftShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p <<= 5; - //l <<= 5; + CustomStruct num = default(CustomStruct); + p <<= 5; + num <<= 5; + Use(ref num); customStructField <<= 5; CustomStructProp <<= 5; c.CustomStructField <<= 5; @@ -4195,9 +4339,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p >>= 5; - //l >>= 5; + CustomStruct num = default(CustomStruct); + p >>= 5; + num >>= 5; + Use(ref num); customStructField >>= 5; CustomStructProp >>= 5; c.CustomStructField >>= 5; @@ -4221,9 +4366,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p &= default(CustomStruct); - //l &= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p &= default(CustomStruct); + num &= default(CustomStruct); + Use(ref num); customStructField &= default(CustomStruct); CustomStructProp &= default(CustomStruct); c.CustomStructField &= default(CustomStruct); @@ -4247,9 +4393,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructBitOrTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p |= default(CustomStruct); - //l |= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p |= default(CustomStruct); + num |= default(CustomStruct); + Use(ref num); customStructField |= default(CustomStruct); CustomStructProp |= default(CustomStruct); c.CustomStructField |= default(CustomStruct); @@ -4273,9 +4420,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructBitXorTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //p ^= default(CustomStruct); - //l ^= default(CustomStruct); + CustomStruct num = default(CustomStruct); + p ^= default(CustomStruct); + num ^= default(CustomStruct); + Use(ref num); customStructField ^= default(CustomStruct); CustomStructProp ^= default(CustomStruct); c.CustomStructField ^= default(CustomStruct); @@ -4299,9 +4447,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructPostIncTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //X(p++); - //X(l++); + CustomStruct num = default(CustomStruct); + X(p++); + X(num++); + Use(ref num); X(customStructField++); X(CustomStructProp++); X(c.CustomStructField++); @@ -4325,9 +4474,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructPreIncTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //X(++p); - //X(++l); + CustomStruct num = default(CustomStruct); + X(++p); + X(++num); + Use(ref num); X(++customStructField); X(++CustomStructProp); X(++c.CustomStructField); @@ -4350,9 +4500,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public static void CustomStructPostDecTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //X(p--); - //X(l--); + CustomStruct num = default(CustomStruct); + X(p--); + X(num--); + Use(ref num); X(customStructField--); X(CustomStructProp--); X(c.CustomStructField--); @@ -4376,9 +4527,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void CustomStructPreDecTest(CustomStruct p, CustomClass c, CustomStruct2 s) { - //CustomStruct l = default(CustomStruct); - //X(--p); - //X(--l); + CustomStruct num = default(CustomStruct); + X(--p); + X(--num); + Use(ref num); X(--customStructField); X(--CustomStructProp); X(--c.CustomStructField); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0972105f1..761a4d6b9 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1587,7 +1587,7 @@ namespace ICSharpCode.Decompiler.CSharp var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); if (rr.IsError || NullableType.GetUnderlyingType(rr.Type).GetStackType() != inst.UnderlyingResultType - || !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign)) + || !IsCompatibleWithSign(rr.Type, inst.Sign)) { // Left and right operands are incompatible, so convert them to a common type Sign sign = inst.Sign; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs index d2ae9b2d5..d76604530 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs @@ -19,6 +19,7 @@ using System; using System.Linq; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; @@ -44,10 +45,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { base.VisitAssignmentExpression(assignment); // Combine "x = x op y" into "x op= y" - BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression; - if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) + // Also supports "x = (T)(x op y)" -> "x op= y", if x.GetType() == T + // and y is implicitly convertible to T. + Expression rhs = assignment.Right; + IType expectedType = null; + if (assignment.Right is CastExpression { Type: var astType } cast) { - if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)) + rhs = cast.Expression; + expectedType = astType.GetResolveResult().Type; + } + if (rhs is BinaryOperatorExpression binary && assignment.Operator == AssignmentOperatorType.Assign) + { + if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left) + && IsImplicitlyConvertible(binary.Right, expectedType)) { assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator); if (assignment.Operator != AssignmentOperatorType.Assign) @@ -78,6 +88,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } } + + bool IsImplicitlyConvertible(Expression rhs, IType expectedType) + { + if (expectedType == null) + return true; + + var conversions = CSharpConversions.Get(context.TypeSystem); + return conversions.ImplicitConversion(rhs.GetResolveResult(), expectedType).IsImplicit; + } } public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop) From 1548555665657c857bac622d5ef1d9aaea489b22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 10:43:58 +0200 Subject: [PATCH 208/230] Bump dessant/lock-threads from 4.0.0 to 4.0.1 (#3005) Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/dessant/lock-threads/releases) - [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md) - [Commits](https://github.com/dessant/lock-threads/compare/v4.0.0...v4.0.1) --- updated-dependencies: - dependency-name: dessant/lock-threads dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index d9e7b4c39..f3de4cb41 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4.0.0 + - uses: dessant/lock-threads@v4.0.1 with: github-token: ${{ github.token }} issue-inactive-days: '90' From 9cc35cdca2b726b939cca852875af58f2d38f82e Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Jun 2023 12:12:15 +0200 Subject: [PATCH 209/230] Move VS Extensions projects to a separate solution --- .github/workflows/build-ilspy.yml | 6 ++++ ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj | 15 -------- ILSpy.AddIn.slnf | 21 ----------- ILSpy.AddIn/ILSpy.AddIn.csproj | 15 -------- ILSpy.VSExtensions.sln | 38 ++++++++++++++++++++ ILSpy.sln | 19 ---------- 6 files changed, 44 insertions(+), 70 deletions(-) delete mode 100644 ILSpy.AddIn.slnf create mode 100644 ILSpy.VSExtensions.sln diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index 11faca8a4..2b6b3ed2c 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -97,6 +97,12 @@ jobs: msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" /p:DefineConstants="ARM64" + - name: Build VS Extensions (for 2017-2019 and 2022) + if: matrix.configuration == 'release' + run: | + msbuild ILSpy.VSExtensions.sln /t:Restore /p:Configuration="Release" /p:Platform="Any CPU" + msbuild ILSpy.VSExtensions.sln /p:Configuration="Release" /p:Platform="Any CPU" + # https://github.com/actions/upload-artifact - name: Upload VSIX (VS 2019) release build artifacts if: matrix.configuration == 'release' diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index 88f9c5294..4e959d0d5 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -55,21 +55,6 @@ - - - false - true - - - false - true - - - false - true - - - diff --git a/ILSpy.AddIn.slnf b/ILSpy.AddIn.slnf deleted file mode 100644 index 3dbf7547f..000000000 --- a/ILSpy.AddIn.slnf +++ /dev/null @@ -1,21 +0,0 @@ -{ - "solution": { - "path": "ILSpy.sln", - "projects": [ - "ICSharpCode.Decompiler.TestRunner\\ICSharpCode.Decompiler.TestRunner.csproj", - "ICSharpCode.Decompiler.Tests\\ICSharpCode.Decompiler.Tests.csproj", - "ICSharpCode.Decompiler\\ICSharpCode.Decompiler.csproj", - "ICSharpCode.ILSpyX\\ICSharpCode.ILSpyX.csproj", - "ILSpy.AddIn.Shared\\ILSpy.AddIn.Shared.shproj", - "ILSpy.AddIn.VS2022\\ILSpy.AddIn.VS2022.csproj", - "ILSpy.AddIn\\ILSpy.AddIn.csproj", - "ILSpy.BamlDecompiler.Tests\\ILSpy.BamlDecompiler.Tests.csproj", - "ILSpy.BamlDecompiler\\ILSpy.BamlDecompiler.csproj", - "ILSpy.ReadyToRun\\ILSpy.ReadyToRun.csproj", - "ILSpy.Tests\\ILSpy.Tests.csproj", - "ILSpy\\ILSpy.csproj", - "SharpTreeView\\ICSharpCode.TreeView.csproj", - "TestPlugin\\TestPlugin.csproj" - ] - } -} \ No newline at end of file diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 175426057..8f710fffe 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -61,21 +61,6 @@ - - - false - true - - - false - true - - - false - true - - - diff --git a/ILSpy.VSExtensions.sln b/ILSpy.VSExtensions.sln new file mode 100644 index 000000000..d086a027a --- /dev/null +++ b/ILSpy.VSExtensions.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.33808.371 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ILSpy.AddIn.Shared", "ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.shproj", "{ACAB1E5D-B3DF-4092-AA72-692F8341E520}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.AddIn", "ILSpy.AddIn\ILSpy.AddIn.csproj", "{A1B6B501-15D4-4237-A4C3-5EFCB71B0F74}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.AddIn.VS2022", "ILSpy.AddIn.VS2022\ILSpy.AddIn.VS2022.csproj", "{31E6E2B0-24B4-4D2C-AC17-3B44DAACC9D4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B6B501-15D4-4237-A4C3-5EFCB71B0F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B6B501-15D4-4237-A4C3-5EFCB71B0F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B6B501-15D4-4237-A4C3-5EFCB71B0F74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B6B501-15D4-4237-A4C3-5EFCB71B0F74}.Release|Any CPU.Build.0 = Release|Any CPU + {31E6E2B0-24B4-4D2C-AC17-3B44DAACC9D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31E6E2B0-24B4-4D2C-AC17-3B44DAACC9D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31E6E2B0-24B4-4D2C-AC17-3B44DAACC9D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31E6E2B0-24B4-4D2C-AC17-3B44DAACC9D4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {84D734FE-9E9C-4593-8927-6F45FE3E460A} + EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{31e6e2b0-24b4-4d2c-ac17-3b44daacc9d4}*SharedItemsImports = 5 + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{a1b6b501-15d4-4237-a4c3-5efcb71b0f74}*SharedItemsImports = 5 + ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{acab1e5d-b3df-4092-aa72-692f8341e520}*SharedItemsImports = 13 + EndGlobalSection +EndGlobal diff --git a/ILSpy.sln b/ILSpy.sln index 369386289..692222f9a 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -24,8 +24,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.BamlDecompiler", "ILS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler.Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.AddIn", "ILSpy.AddIn\ILSpy.AddIn.csproj", "{9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.Tests", "ILSpy.Tests\ILSpy.Tests.csproj", "{B51C6636-B8D1-4200-9869-08F2689DE6C2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.ReadyToRun", "ILSpy.ReadyToRun\ILSpy.ReadyToRun.csproj", "{0313F581-C63B-43BB-AA9B-07615DABD8A3}" @@ -34,10 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.ILSpyCmd", "ICS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.PowerShell", "ICSharpCode.Decompiler.PowerShell\ICSharpCode.Decompiler.PowerShell.csproj", "{50060E0C-FA25-41F4-B72F-8490324EC9F0}" EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ILSpy.AddIn.Shared", "ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.shproj", "{ACAB1E5D-B3DF-4092-AA72-692F8341E520}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.AddIn.VS2022", "ILSpy.AddIn.VS2022\ILSpy.AddIn.VS2022.csproj", "{09A03980-D14A-4705-A38C-741AD7166DEE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.TestRunner", "ICSharpCode.Decompiler.TestRunner\ICSharpCode.Decompiler.TestRunner.csproj", "{4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.ILSpyX", "ICSharpCode.ILSpyX\ICSharpCode.ILSpyX.csproj", "{F8EFCF9D-B9A3-4BA0-A1B2-B026A71DAC22}" @@ -76,10 +70,6 @@ Global {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.Build.0 = Debug|Any CPU {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.ActiveCfg = Release|Any CPU {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|Any CPU - {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Release|Any CPU.Build.0 = Release|Any CPU {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -96,10 +86,6 @@ Global {50060E0C-FA25-41F4-B72F-8490324EC9F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {50060E0C-FA25-41F4-B72F-8490324EC9F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {50060E0C-FA25-41F4-B72F-8490324EC9F0}.Release|Any CPU.Build.0 = Release|Any CPU - {09A03980-D14A-4705-A38C-741AD7166DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09A03980-D14A-4705-A38C-741AD7166DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09A03980-D14A-4705-A38C-741AD7166DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09A03980-D14A-4705-A38C-741AD7166DEE}.Release|Any CPU.Build.0 = Release|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FBB470F-69EB-4C8B-8961-8B4DF4EBB999}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -115,9 +101,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C764218F-7633-4412-923D-558CE7EE0560} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{09a03980-d14a-4705-a38c-741ad7166dee}*SharedItemsImports = 5 - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{9d7be6c0-b7b3-4a50-a54e-18a2d84a3384}*SharedItemsImports = 5 - ILSpy.AddIn.Shared\ILSpy.AddIn.Shared.projitems*{acab1e5d-b3df-4092-aa72-692f8341e520}*SharedItemsImports = 13 - EndGlobalSection EndGlobal From 040ac1ac81357819b0a151259285198d855e26bd Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Jun 2023 12:16:21 +0200 Subject: [PATCH 210/230] Switch from default output directory to publish folder --- ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj | 2 +- ILSpy.AddIn/ILSpy.AddIn.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index 4e959d0d5..e9bf4e153 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -74,7 +74,7 @@ - ..\ILSpy\bin\$(Configuration)\net6.0-windows\ + ..\ILSpy\bin\$(Configuration)\net6.0-windows\win-x64\publish\fwdependent\ diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 8f710fffe..d210a29e9 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -80,7 +80,7 @@ - ..\ILSpy\bin\$(Configuration)\net6.0-windows\ + ..\ILSpy\bin\$(Configuration)\net6.0-windows\win-x64\publish\fwdependent\ From d43a5c94e76619dce073c9aca238df804c6c506d Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Jun 2023 12:26:33 +0200 Subject: [PATCH 211/230] Adapt GetILSpyPath and copy both x64 and arm64 ILSpy into the VSIX for VS 2022 --- ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs | 6 ------ ILSpy.AddIn.Shared/ILSpyInstance.cs | 10 +++++++++- ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj | 17 ++++++++++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs index 6759a54c1..13caafd9c 100644 --- a/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs @@ -47,12 +47,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands protected abstract void OnExecute(object sender, EventArgs e); - protected string GetILSpyPath() - { - var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location); - return Path.Combine(basePath, "ILSpy", "ILSpy.exe"); - } - protected void OpenAssembliesInILSpy(ILSpyParameters parameters) { ThreadHelper.ThrowIfNotOnUIThread(); diff --git a/ILSpy.AddIn.Shared/ILSpyInstance.cs b/ILSpy.AddIn.Shared/ILSpyInstance.cs index 8144eb140..dc90e39a2 100644 --- a/ILSpy.AddIn.Shared/ILSpyInstance.cs +++ b/ILSpy.AddIn.Shared/ILSpyInstance.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -31,8 +32,15 @@ namespace ICSharpCode.ILSpy.AddIn static string GetILSpyPath() { + // Only VS2022 supports arm64, so we can gloss over 2017-2019 support + string archPathSegment = "x64"; + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + archPathSegment = "arm64"; + } + var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location); - return Path.Combine(basePath, "ILSpy", "ILSpy.exe"); + return Path.Combine(basePath, archPathSegment, "ILSpy", "ILSpy.exe"); } public void Start() diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index e9bf4e153..c992a0853 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -74,16 +74,23 @@ - ..\ILSpy\bin\$(Configuration)\net6.0-windows\win-x64\publish\fwdependent\ + ..\ILSpy\bin\$(Configuration)\net6.0-windows\win-x64\publish\fwdependent\ + ..\ILSpy\bin\$(Configuration)\net6.0-windows\win-arm64\publish\fwdependent\ - - \ILSpy\zh-Hans\ + + \x64\ILSpy\zh-Hans\ - - \ILSpy + + \x64\ILSpy + + + \arm64\ILSpy\zh-Hans\ + + + \arm64\ILSpy From 636219d539a0fe8bc61278b3492d0b2370e9288d Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Jun 2023 13:18:11 +0200 Subject: [PATCH 212/230] Enable arm64 installation --- ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template b/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template index db13ce77e..be1488335 100644 --- a/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template +++ b/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template @@ -13,6 +13,9 @@ amd64 + + arm64 + From fb2f98061c79de3231ba8f949d5f769976f93157 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Jun 2023 14:00:34 +0200 Subject: [PATCH 213/230] Update Microsoft.VSSDK.BuildTools --- ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index c992a0853..b65918c3d 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -41,7 +41,7 @@ - + all runtime; build; native; contentfiles; analyzers From a6a86a6165b631c53b8cbfbb60cb1367f2beaffe Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 15 Jun 2023 10:19:54 +0200 Subject: [PATCH 214/230] Microsoft.VSSDK.BuildTools 17.6.2164 for both projects --- ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj | 4 ++-- ILSpy.AddIn/ILSpy.AddIn.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index b65918c3d..5d09db657 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -41,9 +41,9 @@ - + all - runtime; build; native; contentfiles; analyzers + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index d210a29e9..021cfed03 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -47,7 +47,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 002c8c53f741512c7dd9e1e72a1df2e71cc25c8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 05:09:12 +0000 Subject: [PATCH 215/230] Bump NuGet.Protocol from 6.2.2 to 6.2.4 in /ICSharpCode.Decompiler.Tests Bumps [NuGet.Protocol](https://github.com/NuGet/NuGet.Client) from 6.2.2 to 6.2.4. - [Release notes](https://github.com/NuGet/NuGet.Client/releases) - [Commits](https://github.com/NuGet/NuGet.Client/commits) --- updated-dependencies: - dependency-name: NuGet.Protocol dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../ICSharpCode.Decompiler.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index fabc14eba..03432b95f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -45,7 +45,7 @@ - + From b9c9e05a94d4b434ad57bc446afeaa156c7d343e Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 17 Jun 2023 10:49:15 +0200 Subject: [PATCH 216/230] Disentangle AboutPage from checking for updates (prepare for the later possibility of introducing AutoUpdate for MSI installs) --- ILSpy/AboutPage.cs | 166 +---------------------- ILSpy/MainWindow.xaml.cs | 8 +- ILSpy/Updates/AppUpdateService.cs | 35 +++++ ILSpy/Updates/AvailableVersionInfo.cs | 28 ++++ ILSpy/Updates/NotifyOfUpdatesStrategy.cs | 114 ++++++++++++++++ ILSpy/Updates/UpdateSettings.cs | 89 ++++++++++++ 6 files changed, 277 insertions(+), 163 deletions(-) create mode 100644 ILSpy/Updates/AppUpdateService.cs create mode 100644 ILSpy/Updates/AvailableVersionInfo.cs create mode 100644 ILSpy/Updates/NotifyOfUpdatesStrategy.cs create mode 100644 ILSpy/Updates/UpdateSettings.cs diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 0ac9866bf..1dcef578a 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -17,24 +17,20 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.ComponentModel; using System.IO; -using System.Linq; -using System.Net.Http; using System.Text.RegularExpressions; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Navigation; -using System.Xml.Linq; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Themes; +using ICSharpCode.ILSpy.Updates; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy @@ -50,11 +46,6 @@ namespace ICSharpCode.ILSpy ); } - static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml"); - const string band = "stable"; - - static AvailableVersionInfo latestAvailableVersion; - public static void Display(DecompilerTextView textView) { AvalonEditTextOutput output = new AvalonEditTextOutput() { @@ -71,14 +62,14 @@ namespace ICSharpCode.ILSpy StackPanel stackPanel = new StackPanel(); stackPanel.HorizontalAlignment = HorizontalAlignment.Center; stackPanel.Orientation = Orientation.Horizontal; - if (latestAvailableVersion == null) + if (NotifyOfUpdatesStrategy.LatestAvailableVersion == null) { AddUpdateCheckButton(stackPanel, textView); } else { // we already retrieved the latest version sometime earlier - ShowAvailableVersion(latestAvailableVersion, stackPanel); + ShowAvailableVersion(NotifyOfUpdatesStrategy.LatestAvailableVersion, stackPanel); } CheckBox checkBox = new CheckBox(); checkBox.Margin = new Thickness(4); @@ -142,7 +133,7 @@ namespace ICSharpCode.ILSpy try { - AvailableVersionInfo vInfo = await GetLatestVersionAsync(); + AvailableVersionInfo vInfo = await NotifyOfUpdatesStrategy.GetLatestVersionAsync(); stackPanel.Children.Clear(); ShowAvailableVersion(vInfo, stackPanel); } @@ -155,11 +146,9 @@ namespace ICSharpCode.ILSpy }; } - static readonly Version currentVersion = new Version(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision); - static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPanel stackPanel) { - if (currentVersion == availableVersion.Version) + if (AppUpdateService.CurrentVersion == availableVersion.Version) { stackPanel.Children.Add( new Image { @@ -173,7 +162,7 @@ namespace ICSharpCode.ILSpy VerticalAlignment = VerticalAlignment.Bottom }); } - else if (currentVersion < availableVersion.Version) + else if (AppUpdateService.CurrentVersion < availableVersion.Version) { stackPanel.Children.Add( new TextBlock { @@ -197,149 +186,6 @@ namespace ICSharpCode.ILSpy stackPanel.Children.Add(new TextBlock { Text = Resources.UsingNightlyBuildNewerThanLatestRelease }); } } - - static async Task GetLatestVersionAsync() - { - var client = new HttpClient(new HttpClientHandler() { - UseProxy = true, - UseDefaultCredentials = true, - }); - string data = await client.GetStringAsync(UpdateUrl); - - XDocument doc = XDocument.Load(new StringReader(data)); - var bands = doc.Root.Elements("band"); - var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First(); - Version version = new Version((string)currentBand.Element("latestVersion")); - string url = (string)currentBand.Element("downloadUrl"); - if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal))) - url = null; // don't accept non-urls - - latestAvailableVersion = new AvailableVersionInfo { Version = version, DownloadUrl = url }; - return latestAvailableVersion; - } - - sealed class AvailableVersionInfo - { - public Version Version; - public string DownloadUrl; - } - - sealed class UpdateSettings : INotifyPropertyChanged - { - public UpdateSettings(ILSpySettings spySettings) - { - XElement s = spySettings["UpdateSettings"]; - this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true; - try - { - this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck"); - } - catch (FormatException) - { - // avoid crashing on settings files invalid due to - // https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2 - } - } - - bool automaticUpdateCheckEnabled; - - public bool AutomaticUpdateCheckEnabled { - get { return automaticUpdateCheckEnabled; } - set { - if (automaticUpdateCheckEnabled != value) - { - automaticUpdateCheckEnabled = value; - Save(); - OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled)); - } - } - } - - DateTime? lastSuccessfulUpdateCheck; - - public DateTime? LastSuccessfulUpdateCheck { - get { return lastSuccessfulUpdateCheck; } - set { - if (lastSuccessfulUpdateCheck != value) - { - lastSuccessfulUpdateCheck = value; - Save(); - OnPropertyChanged(nameof(LastSuccessfulUpdateCheck)); - } - } - } - - public void Save() - { - XElement updateSettings = new XElement("UpdateSettings"); - updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled)); - if (lastSuccessfulUpdateCheck != null) - updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck)); - ILSpySettings.SaveSettings(updateSettings); - } - - public event PropertyChangedEventHandler PropertyChanged; - - void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } - - /// - /// If automatic update checking is enabled, checks if there are any updates available. - /// Returns the download URL if an update is available. - /// Returns null if no update is available, or if no check was performed. - /// - public static async Task CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings) - { - UpdateSettings s = new UpdateSettings(spySettings); - - // If we're in an MSIX package, updates work differently - if (s.AutomaticUpdateCheckEnabled) - { - // perform update check if we never did one before; - // or if the last check wasn't in the past 7 days - if (s.LastSuccessfulUpdateCheck == null - || s.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7) - || s.LastSuccessfulUpdateCheck > DateTime.UtcNow) - { - return await CheckForUpdateInternal(s); - } - else - { - return null; - } - } - else - { - return null; - } - } - - public static Task CheckForUpdatesAsync(ILSpySettings spySettings) - { - UpdateSettings s = new UpdateSettings(spySettings); - return CheckForUpdateInternal(s); - } - - static async Task CheckForUpdateInternal(UpdateSettings s) - { - try - { - var v = await GetLatestVersionAsync(); - s.LastSuccessfulUpdateCheck = DateTime.UtcNow; - if (v.Version > currentVersion) - return v.DownloadUrl; - else - return null; - } - catch (Exception) - { - // ignore errors getting the version info - return null; - } - } } /// diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index eaa65475b..c31d35aff 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -50,6 +50,7 @@ using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.Updates; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Settings; @@ -954,13 +955,14 @@ namespace ICSharpCode.ILSpy string downloadUrl; if (forceCheck) { - downloadUrl = await AboutPage.CheckForUpdatesAsync(spySettings); + downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(spySettings); } else { - downloadUrl = await AboutPage.CheckForUpdatesIfEnabledAsync(spySettings); + downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesIfEnabledAsync(spySettings); } + // The Update Panel is only available for NotifyOfUpdatesStrategy, AutoUpdate will have differing UI requirements AdjustUpdateUIAfterCheck(downloadUrl, forceCheck); } @@ -978,7 +980,7 @@ namespace ICSharpCode.ILSpy else { updatePanel.Visibility = Visibility.Collapsed; - string downloadUrl = await AboutPage.CheckForUpdatesAsync(ILSpySettings.Load()); + string downloadUrl = await NotifyOfUpdatesStrategy.CheckForUpdatesAsync(ILSpySettings.Load()); AdjustUpdateUIAfterCheck(downloadUrl, true); } } diff --git a/ILSpy/Updates/AppUpdateService.cs b/ILSpy/Updates/AppUpdateService.cs new file mode 100644 index 000000000..04b3f1635 --- /dev/null +++ b/ILSpy/Updates/AppUpdateService.cs @@ -0,0 +1,35 @@ +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace ICSharpCode.ILSpy.Updates +{ + internal enum UpdateStrategy + { + NotifyOfUpdates, + // AutoUpdate + } + + internal static class AppUpdateService + { + public static readonly UpdateStrategy updateStrategy = UpdateStrategy.NotifyOfUpdates; + + public static readonly Version CurrentVersion = new Version(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision); + } +} diff --git a/ILSpy/Updates/AvailableVersionInfo.cs b/ILSpy/Updates/AvailableVersionInfo.cs new file mode 100644 index 000000000..995619b5d --- /dev/null +++ b/ILSpy/Updates/AvailableVersionInfo.cs @@ -0,0 +1,28 @@ +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace ICSharpCode.ILSpy.Updates +{ + sealed class AvailableVersionInfo + { + public Version Version; + public string DownloadUrl; + } +} diff --git a/ILSpy/Updates/NotifyOfUpdatesStrategy.cs b/ILSpy/Updates/NotifyOfUpdatesStrategy.cs new file mode 100644 index 000000000..1d5123c26 --- /dev/null +++ b/ILSpy/Updates/NotifyOfUpdatesStrategy.cs @@ -0,0 +1,114 @@ +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Xml.Linq; + +using ICSharpCode.ILSpyX.Settings; + +namespace ICSharpCode.ILSpy.Updates +{ + internal static class NotifyOfUpdatesStrategy + { + static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml"); + const string band = "stable"; + + public static AvailableVersionInfo LatestAvailableVersion { get; private set; } + + public static async Task GetLatestVersionAsync() + { + var client = new HttpClient(new HttpClientHandler() { + UseProxy = true, + UseDefaultCredentials = true, + }); + string data = await client.GetStringAsync(UpdateUrl).ConfigureAwait(false); + + XDocument doc = XDocument.Load(new StringReader(data)); + var bands = doc.Root.Elements("band"); + var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First(); + Version version = new Version((string)currentBand.Element("latestVersion")); + string url = (string)currentBand.Element("downloadUrl"); + if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal))) + url = null; // don't accept non-urls + + LatestAvailableVersion = new AvailableVersionInfo { Version = version, DownloadUrl = url }; + return LatestAvailableVersion; + } + + + + /// + /// If automatic update checking is enabled, checks if there are any updates available. + /// Returns the download URL if an update is available. + /// Returns null if no update is available, or if no check was performed. + /// + public static async Task CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings) + { + UpdateSettings s = new UpdateSettings(spySettings); + + // If we're in an MSIX package, updates work differently + if (s.AutomaticUpdateCheckEnabled) + { + // perform update check if we never did one before; + // or if the last check wasn't in the past 7 days + if (s.LastSuccessfulUpdateCheck == null + || s.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7) + || s.LastSuccessfulUpdateCheck > DateTime.UtcNow) + { + return await CheckForUpdateInternal(s).ConfigureAwait(false); + } + else + { + return null; + } + } + else + { + return null; + } + } + + public static Task CheckForUpdatesAsync(ILSpySettings spySettings) + { + UpdateSettings s = new UpdateSettings(spySettings); + return CheckForUpdateInternal(s); + } + + static async Task CheckForUpdateInternal(UpdateSettings s) + { + try + { + var v = await GetLatestVersionAsync().ConfigureAwait(false); + s.LastSuccessfulUpdateCheck = DateTime.UtcNow; + if (v.Version > AppUpdateService.CurrentVersion) + return v.DownloadUrl; + else + return null; + } + catch (Exception) + { + // ignore errors getting the version info + return null; + } + } + } +} diff --git a/ILSpy/Updates/UpdateSettings.cs b/ILSpy/Updates/UpdateSettings.cs new file mode 100644 index 000000000..777f23eee --- /dev/null +++ b/ILSpy/Updates/UpdateSettings.cs @@ -0,0 +1,89 @@ +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel; +using System.Xml.Linq; + +using ICSharpCode.ILSpyX.Settings; + +namespace ICSharpCode.ILSpy.Updates +{ + sealed class UpdateSettings : INotifyPropertyChanged + { + public UpdateSettings(ILSpySettings spySettings) + { + XElement s = spySettings["UpdateSettings"]; + this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true; + try + { + this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck"); + } + catch (FormatException) + { + // avoid crashing on settings files invalid due to + // https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2 + } + } + + bool automaticUpdateCheckEnabled; + + public bool AutomaticUpdateCheckEnabled { + get { return automaticUpdateCheckEnabled; } + set { + if (automaticUpdateCheckEnabled != value) + { + automaticUpdateCheckEnabled = value; + Save(); + OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled)); + } + } + } + + DateTime? lastSuccessfulUpdateCheck; + + public DateTime? LastSuccessfulUpdateCheck { + get { return lastSuccessfulUpdateCheck; } + set { + if (lastSuccessfulUpdateCheck != value) + { + lastSuccessfulUpdateCheck = value; + Save(); + OnPropertyChanged(nameof(LastSuccessfulUpdateCheck)); + } + } + } + + public void Save() + { + XElement updateSettings = new XElement("UpdateSettings"); + updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled)); + if (lastSuccessfulUpdateCheck != null) + updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck)); + ILSpySettings.SaveSettings(updateSettings); + } + + public event PropertyChangedEventHandler PropertyChanged; + + void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + +} From c6400ca0738222c50e9221d7184ba1d690c731fe Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:50:36 +0200 Subject: [PATCH 217/230] Move FileUtilityTests to correct namespace. --- ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs b/ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs index cef3d91f3..e188f9478 100644 --- a/ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs +++ b/ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs @@ -16,9 +16,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using ICSharpCode.Decompiler.Util; + using NUnit.Framework; -namespace ICSharpCode.Decompiler.Util +namespace ICSharpCode.Decompiler.Tests.Util { [TestFixture] public class FileUtilityTests From f0e3277d050313cb9b756127bd736c0e60606834 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:51:17 +0200 Subject: [PATCH 218/230] Fix performance of WriteNiceBase64. --- .../Util/ResXResourceWriter.cs | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs index b51c2e9ae..759dc2ee0 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -131,26 +131,10 @@ namespace ICSharpCode.Decompiler.Util void WriteNiceBase64(byte[] value, int offset, int length) { - string b64; - StringBuilder sb; - int pos; - int inc; - string ins; - - b64 = Convert.ToBase64String(value, offset, length); - - // Wild guess; two extra newlines, and one newline/tab pair for every 80 chars - sb = new StringBuilder(b64, b64.Length + ((b64.Length + 160) / 80) * 3); - pos = 0; - inc = 80 + Environment.NewLine.Length + 1; - ins = Environment.NewLine + "\t"; - while (pos < sb.Length) - { - sb.Insert(pos, ins); - pos += inc; - } - sb.Insert(sb.Length, Environment.NewLine); - writer.WriteString(sb.ToString()); + string base64 = Convert.ToBase64String( + value, offset, length, + Base64FormattingOptions.InsertLineBreaks); + writer.WriteString(base64); } void WriteBytes(string name, Type type, byte[] value, int offset, int length, string comment) From efce9193c6b22af66655fae26236fc42d6df27b5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:55:21 +0200 Subject: [PATCH 219/230] Remove binary formatter logic. --- .../Util/ResXResourceWriter.cs | 192 ++++++++---------- 1 file changed, 81 insertions(+), 111 deletions(-) diff --git a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs index 759dc2ee0..54e67fc98 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -32,6 +32,7 @@ using System; using System.ComponentModel; +using System.Globalization; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Text; @@ -99,6 +100,7 @@ namespace ICSharpCode.Decompiler.Util #endregion // Constructors & Destructor const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + const string MSCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string ResXNullRefTypeName = "System.Resources.ResXNullRef" + WinFormsAssemblyName; void InitWriter() @@ -137,17 +139,17 @@ namespace ICSharpCode.Decompiler.Util writer.WriteString(base64); } - void WriteBytes(string name, Type type, byte[] value, int offset, int length, string comment) + void WriteBytes(string name, string type, byte[] value, int offset, int length, string comment) { writer.WriteStartElement("data"); writer.WriteAttributeString("name", name); if (type != null) { - writer.WriteAttributeString("type", type.AssemblyQualifiedName); + writer.WriteAttributeString("type", type); // byte[] should never get a mimetype, otherwise MS.NET won't be able // to parse the data. - if (type != typeof(byte[])) + if (type != "System.Byte[]" + MSCorLibAssemblyName) writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType); writer.WriteStartElement("value"); WriteNiceBase64(value, offset, length); @@ -161,7 +163,7 @@ namespace ICSharpCode.Decompiler.Util writer.WriteEndElement(); - if (!(comment == null || comment.Equals(String.Empty))) + if (!string.IsNullOrEmpty(comment)) { writer.WriteStartElement("comment"); writer.WriteString(comment); @@ -171,29 +173,22 @@ namespace ICSharpCode.Decompiler.Util writer.WriteEndElement(); } - void WriteBytes(string name, Type type, byte[] value, string comment) - { - WriteBytes(name, type, value, 0, value.Length, comment); - } - - void WriteString(string name, string value) - { - WriteString(name, value, null); - } - void WriteString(string name, string value, string type) - { - WriteString(name, value, type, String.Empty); - } void WriteString(string name, string value, string type, string comment) { writer.WriteStartElement("data"); writer.WriteAttributeString("name", name); if (type != null) + { writer.WriteAttributeString("type", type); + } + else + { + writer.WriteAttributeString("xml:space", "preserve"); + } writer.WriteStartElement("value"); writer.WriteString(value); writer.WriteEndElement(); - if (!(comment == null || comment.Equals(String.Empty))) + if (!string.IsNullOrEmpty(comment)) { writer.WriteStartElement("comment"); writer.WriteString(comment); @@ -205,34 +200,16 @@ namespace ICSharpCode.Decompiler.Util public void AddResource(string name, byte[] value) { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (written) - throw new InvalidOperationException("The resource is already generated."); - - if (writer == null) - InitWriter(); - - WriteBytes(name, value.GetType(), value, null); + AddResource(name, value, string.Empty); } public void AddResource(string name, object value) { - AddResource(name, value, String.Empty); + AddResource(name, value, string.Empty); } private void AddResource(string name, object value, string comment) { - if (value is string) - { - AddResource(name, (string)value, comment); - return; - } - if (name == null) throw new ArgumentNullException(nameof(name)); @@ -242,64 +219,74 @@ namespace ICSharpCode.Decompiler.Util if (writer == null) InitWriter(); - if (value is byte[]) - { - WriteBytes(name, value.GetType(), (byte[])value, comment); - return; - } - if (value is MemoryStream memoryStream) - { - WriteBytes(name, null, memoryStream.ToArray(), comment); - return; - } - if (value is ResourceSerializedObject rso) - { - var bytes = rso.GetBytes(); - WriteBytes(name, null, bytes, 0, bytes.Length, comment); - return; - } - - if (value == null) - { - // nulls written as ResXNullRef - WriteString(name, "", ResXNullRefTypeName, comment); - return; - } - - if (value != null && !value.GetType().IsSerializable) - throw new InvalidOperationException(String.Format("The element '{0}' of type '{1}' is not serializable.", name, value.GetType().Name)); - - TypeConverter converter = TypeDescriptor.GetConverter(value); - - if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string))) - { - string str = (string)converter.ConvertToInvariantString(value); - WriteString(name, str, value.GetType().AssemblyQualifiedName, comment); - return; - } - - if (converter != null && converter.CanConvertTo(typeof(byte[])) && converter.CanConvertFrom(typeof(byte[]))) - { - byte[] b = (byte[])converter.ConvertTo(value, typeof(byte[])); - WriteBytes(name, value.GetType(), b, comment); - return; - } - - MemoryStream ms = new MemoryStream(); - BinaryFormatter fmt = new BinaryFormatter(); - try - { - fmt.Serialize(ms, value); - } - catch (Exception e) + switch (value) { - throw new InvalidOperationException("Cannot add a " + value.GetType() + - "because it cannot be serialized: " + - e.Message); + case null: + // nulls written as ResXNullRef + WriteString(name, "", ResXNullRefTypeName, comment); + break; + case string s: + WriteString(name, s, null, comment); + break; + case bool bo: + WriteString(name, bo.ToString(CultureInfo.InvariantCulture), "System.Boolean" + MSCorLibAssemblyName, comment); + break; + case char ch: + WriteString(name, ch.ToString(CultureInfo.InvariantCulture), "System.Char" + MSCorLibAssemblyName, comment); + break; + case sbyte sb: + WriteString(name, sb.ToString(CultureInfo.InvariantCulture), "System.SByte" + MSCorLibAssemblyName, comment); + break; + case byte b: + WriteString(name, b.ToString(CultureInfo.InvariantCulture), "System.Byte" + MSCorLibAssemblyName, comment); + break; + case short sh: + WriteString(name, sh.ToString(CultureInfo.InvariantCulture), "System.Int16" + MSCorLibAssemblyName, comment); + break; + case ushort ush: + WriteString(name, ush.ToString(CultureInfo.InvariantCulture), "System.UInt16" + MSCorLibAssemblyName, comment); + break; + case int i: + WriteString(name, i.ToString(CultureInfo.InvariantCulture), "System.Int32" + MSCorLibAssemblyName, comment); + break; + case uint u: + WriteString(name, u.ToString(CultureInfo.InvariantCulture), "System.UInt32" + MSCorLibAssemblyName, comment); + break; + case long l: + WriteString(name, l.ToString(CultureInfo.InvariantCulture), "System.Int64" + MSCorLibAssemblyName, comment); + break; + case ulong ul: + WriteString(name, ul.ToString(CultureInfo.InvariantCulture), "System.UInt64" + MSCorLibAssemblyName, comment); + break; + case float f: + WriteString(name, f.ToString(CultureInfo.InvariantCulture), "System.Single" + MSCorLibAssemblyName, comment); + break; + case double d: + WriteString(name, d.ToString(CultureInfo.InvariantCulture), "System.Double" + MSCorLibAssemblyName, comment); + break; + case decimal m: + WriteString(name, m.ToString(CultureInfo.InvariantCulture), "System.Decimal" + MSCorLibAssemblyName, comment); + break; + case DateTime dt: + WriteString(name, dt.ToString(CultureInfo.InvariantCulture), "System.DateTime" + MSCorLibAssemblyName, comment); + break; + case TimeSpan sp: + WriteString(name, sp.ToString(), "System.TimeSpan" + MSCorLibAssemblyName, comment); + break; + case byte[] array: + WriteBytes(name, "System.Byte[]" + MSCorLibAssemblyName, array, 0, array.Length, comment); + break; + case MemoryStream memoryStream: + var arr = memoryStream.ToArray(); + WriteBytes(name, null, arr, 0, arr.Length, comment); + break; + case ResourceSerializedObject rso: + var bytes = rso.GetBytes(); + WriteBytes(name, null, bytes, 0, bytes.Length, comment); + break; + default: + throw new NotSupportedException($"Value '{value}' of type {value.GetType().FullName} is not supported by this version of ResXResourceWriter. Use byte arrays or streams instead."); } - - WriteBytes(name, null, ms.GetBuffer(), 0, (int)ms.Length, comment); - ms.Close(); } public void AddResource(string name, string value) @@ -307,23 +294,6 @@ namespace ICSharpCode.Decompiler.Util AddResource(name, value, string.Empty); } - private void AddResource(string name, string value, string comment) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (written) - throw new InvalidOperationException("The resource is already generated."); - - if (writer == null) - InitWriter(); - - WriteString(name, value, null, comment); - } - public void AddMetadata(string name, string value) { if (name == null) From 5e55f5b701d5fe21970ba8c438fe364c97008c61 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:55:54 +0200 Subject: [PATCH 220/230] Strip AddMetadata API. --- .../Util/ResXResourceWriter.cs | 156 ------------------ 1 file changed, 156 deletions(-) diff --git a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs index 54e67fc98..1417718aa 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -294,162 +294,6 @@ namespace ICSharpCode.Decompiler.Util AddResource(name, value, string.Empty); } - public void AddMetadata(string name, string value) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (written) - throw new InvalidOperationException("The resource is already generated."); - - if (writer == null) - InitWriter(); - - writer.WriteStartElement("metadata"); - writer.WriteAttributeString("name", name); - writer.WriteAttributeString("xml:space", "preserve"); - - writer.WriteElementString("value", value); - - writer.WriteEndElement(); - } - - public void AddMetadata(string name, byte[] value) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (written) - throw new InvalidOperationException("The resource is already generated."); - - if (writer == null) - InitWriter(); - - writer.WriteStartElement("metadata"); - writer.WriteAttributeString("name", name); - - writer.WriteAttributeString("type", value.GetType().AssemblyQualifiedName); - - writer.WriteStartElement("value"); - WriteNiceBase64(value, 0, value.Length); - writer.WriteEndElement(); - - writer.WriteEndElement(); - } - - public void AddMetadata(string name, object value) - { - if (value is string) - { - AddMetadata(name, (string)value); - return; - } - - if (value is byte[]) - { - AddMetadata(name, (byte[])value); - return; - } - - if (name == null) - throw new ArgumentNullException(nameof(name)); - - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (!value.GetType().IsSerializable) - throw new InvalidOperationException(String.Format("The element '{0}' of type '{1}' is not serializable.", name, value.GetType().Name)); - - if (written) - throw new InvalidOperationException("The resource is already generated."); - - if (writer == null) - InitWriter(); - - Type type = value.GetType(); - - TypeConverter converter = TypeDescriptor.GetConverter(value); - if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string))) - { - string str = (string)converter.ConvertToInvariantString(value); - writer.WriteStartElement("metadata"); - writer.WriteAttributeString("name", name); - if (type != null) - writer.WriteAttributeString("type", type.AssemblyQualifiedName); - writer.WriteStartElement("value"); - writer.WriteString(str); - writer.WriteEndElement(); - writer.WriteEndElement(); - writer.WriteWhitespace("\n "); - return; - } - - if (converter != null && converter.CanConvertTo(typeof(byte[])) && converter.CanConvertFrom(typeof(byte[]))) - { - byte[] b = (byte[])converter.ConvertTo(value, typeof(byte[])); - writer.WriteStartElement("metadata"); - writer.WriteAttributeString("name", name); - - if (type != null) - { - writer.WriteAttributeString("type", type.AssemblyQualifiedName); - writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType); - writer.WriteStartElement("value"); - WriteNiceBase64(b, 0, b.Length); - } - else - { - writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType); - writer.WriteStartElement("value"); - writer.WriteBase64(b, 0, b.Length); - } - - writer.WriteEndElement(); - writer.WriteEndElement(); - return; - } - - MemoryStream ms = new MemoryStream(); - BinaryFormatter fmt = new BinaryFormatter(); - try - { - fmt.Serialize(ms, value); - } - catch (Exception e) - { - throw new InvalidOperationException("Cannot add a " + value.GetType() + - "because it cannot be serialized: " + - e.Message); - } - - writer.WriteStartElement("metadata"); - writer.WriteAttributeString("name", name); - - if (type != null) - { - writer.WriteAttributeString("type", type.AssemblyQualifiedName); - writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType); - writer.WriteStartElement("value"); - WriteNiceBase64(ms.GetBuffer(), 0, ms.GetBuffer().Length); - } - else - { - writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType); - writer.WriteStartElement("value"); - writer.WriteBase64(ms.GetBuffer(), 0, ms.GetBuffer().Length); - } - - writer.WriteEndElement(); - writer.WriteEndElement(); - ms.Close(); - } - public void Close() { if (writer != null) From 05281e7732b73ccd4fff51433483b11d6af9d91b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:56:31 +0200 Subject: [PATCH 221/230] Code cleanup. --- ICSharpCode.Decompiler/Util/ResXResourceWriter.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs index 1417718aa..7d18ef551 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -31,10 +31,8 @@ // includes code by Mike Krüger and Lluis Sanchez using System; -using System.ComponentModel; using System.Globalization; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Xml; @@ -47,25 +45,20 @@ namespace ICSharpCode.Decompiler.Util #endif class ResXResourceWriter : IDisposable { - #region Local Variables private string filename; private Stream stream; private TextWriter textwriter; private XmlTextWriter writer; private bool written; private string base_path; - #endregion // Local Variables - #region Static Fields public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64"; public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64"; public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType; public static readonly string ResMimeType = "text/microsoft-resx"; public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64"; public static readonly string Version = "2.0"; - #endregion // Static Fields - #region Constructors & Destructor public ResXResourceWriter(Stream stream) { if (stream == null) @@ -97,7 +90,6 @@ namespace ICSharpCode.Decompiler.Util { Dispose(false); } - #endregion // Constructors & Destructor const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string MSCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; @@ -332,7 +324,7 @@ namespace ICSharpCode.Decompiler.Util Close(); } - static string schema = @" + static readonly string schema = @" @@ -362,11 +354,9 @@ namespace ICSharpCode.Decompiler.Util ".Replace("'", "\"").Replace("\t", " "); - #region Public Properties public string BasePath { get { return base_path; } set { base_path = value; } } - #endregion } } From e83af524f7b21bf044ba49a1814cadbc08a414d0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Jun 2023 07:56:52 +0200 Subject: [PATCH 222/230] Add test cases. --- .../ICSharpCode.Decompiler.Tests.csproj | 4 + .../Util/ResourceReaderWriterTests.cs | 189 ++++++++++++++++++ .../Util/Test.resources | Bin 0 -> 684 bytes 3 files changed, 193 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/Util/ResourceReaderWriterTests.cs create mode 100644 ICSharpCode.Decompiler.Tests/Util/Test.resources diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index fabc14eba..040170ca1 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -17,6 +17,7 @@ false True + True True ..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk @@ -61,6 +62,7 @@ + @@ -113,6 +115,7 @@ + @@ -337,6 +340,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/Util/ResourceReaderWriterTests.cs b/ICSharpCode.Decompiler.Tests/Util/ResourceReaderWriterTests.cs new file mode 100644 index 000000000..cad0afa93 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/Util/ResourceReaderWriterTests.cs @@ -0,0 +1,189 @@ +// Copyright (c) 2023 Siegfried Pammer +// +// 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 +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using System.Xml.XPath; + +using ICSharpCode.Decompiler.Util; + +using NUnit.Framework; +using NUnit.Framework.Internal; + +namespace ICSharpCode.Decompiler.Tests.Util +{ + [TestFixture] + public class ResourceReaderWriterTests + { + const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + const string MSCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + [Serializable] + public class SerializableClass + { + public string Name { get; set; } + public int Age { get; set; } + } + + static readonly object[][] TestWriteCases = { + new object[] { "Decimal", 1.0m, "1.0", "System.Decimal" + MSCorLibAssemblyName }, + new object[] { "TimeSpan", TimeSpan.FromSeconds(42), "00:00:42", "System.TimeSpan" + MSCorLibAssemblyName }, + new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture), "06/18/2023 21:36:30", "System.DateTime" + MSCorLibAssemblyName }, + }; + + static readonly object[][] TestReadCases = { + new object[] { "Decimal", 1.0m }, + new object[] { "TimeSpan", TimeSpan.FromSeconds(42) }, + new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture) }, + }; + + static MemoryStream ProduceResourcesTestFile(string name, T value) + { + var ms = new MemoryStream(); + var writer = new ResourceWriter(ms); + writer.AddResource(name, value); + writer.Generate(); + ms.Position = 0; + return ms; + } + + static XElement ProduceResXTest(string name, T value) + { + using var ms = new MemoryStream(); + var writer = new ResXResourceWriter(ms); + writer.AddResource(name, value); + writer.Generate(); + ms.Position = 0; + var doc = XDocument.Load(ms); + return doc.XPathSelectElement(".//data"); + } + + [TestCase("Null", null)] + [TestCase("String", "Hello World!")] + [TestCase("Char", 'A')] + [TestCase("Bool", true)] + [TestCase("Bool", false)] + [TestCase("Byte", (byte)1)] + [TestCase("SByte", (sbyte)-1)] + [TestCase("Int16", (short)1)] + [TestCase("UInt16", (ushort)1)] + [TestCase("Int32", 1)] + [TestCase("UInt32", (uint)1)] + [TestCase("Int64", (long)1)] + [TestCase("UInt64", (ulong)1)] + [TestCase("Single", 1.0f)] + [TestCase("Double", 1.0d)] + [TestCase("Bytes", new byte[] { 42, 43, 44 })] + [TestCaseSource(nameof(TestReadCases))] + public void Read(string name, object value) + { + using var testFile = ProduceResourcesTestFile(name, value); + using var reader = new ResourcesFile(testFile); + var items = reader.ToArray(); + Assert.AreEqual(1, items.Length); + Assert.AreEqual(name, items[0].Key); + Assert.AreEqual(value, items[0].Value); + } + + [TestCase("Null", null, null, "System.Resources.ResXNullRef" + WinFormsAssemblyName)] + [TestCase("String", "Hello World!", "Hello World!", null)] + [TestCase("Bool", true, "True", "System.Boolean" + MSCorLibAssemblyName)] + [TestCase("Bool", false, "False", "System.Boolean" + MSCorLibAssemblyName)] + [TestCase("Char", 'A', "A", "System.Char" + MSCorLibAssemblyName)] + [TestCase("Byte", (byte)1, "1", "System.Byte" + MSCorLibAssemblyName)] + [TestCase("SByte", (sbyte)-1, "-1", "System.SByte" + MSCorLibAssemblyName)] + [TestCase("Int16", (short)1, "1", "System.Int16" + MSCorLibAssemblyName)] + [TestCase("UInt16", (ushort)1, "1", "System.UInt16" + MSCorLibAssemblyName)] + [TestCase("Int32", 1, "1", "System.Int32" + MSCorLibAssemblyName)] + [TestCase("UInt32", (uint)1, "1", "System.UInt32" + MSCorLibAssemblyName)] + [TestCase("Int64", (long)1, "1", "System.Int64" + MSCorLibAssemblyName)] + [TestCase("UInt64", (ulong)1, "1", "System.UInt64" + MSCorLibAssemblyName)] + [TestCase("Single", 1.0f, "1", "System.Single" + MSCorLibAssemblyName)] + [TestCase("Double", 1.0d, "1", "System.Double" + MSCorLibAssemblyName)] + [TestCaseSource(nameof(TestWriteCases))] + public void Write(string name, object value, string serializedValue, string typeName) + { + var element = ProduceResXTest(name, value); + Assert.AreEqual(name, element.Attribute("name")?.Value); + if (typeName != null) + { + Assert.AreEqual(typeName, element.Attribute("type")?.Value); + } + var v = element.Element("value"); + Assert.IsNotNull(v); + Assert.IsTrue(v.IsEmpty ? serializedValue == null : v.Value == serializedValue); + } + + [Test] + public void ResXSerializableClassIsRejected() + { + Assert.Throws( + () => ProduceResXTest("Serial", new SerializableClass { Name = "Hugo", Age = 42 }) + ); + } + + [Test] + public void BitmapIsResourceSerializedObject() + { + Stream stream = typeof(ResourceReaderWriterTests).Assembly + .GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources"); + using var reader = new ResourcesFile(stream); + var items = reader.ToArray(); + Assert.AreEqual(3, items.Length); + var item = items.FirstOrDefault(i => i.Key == "Bitmap"); + Assert.IsNotNull(item.Key); + Assert.IsInstanceOf(item.Value); + } + + [Test] + public void ByteArrayIsSupported() + { + Stream stream = typeof(ResourceReaderWriterTests).Assembly + .GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources"); + using var reader = new ResourcesFile(stream); + var items = reader.ToArray(); + Assert.AreEqual(3, items.Length); + var item = items.FirstOrDefault(i => i.Key == "Byte[]"); + Assert.IsNotNull(item.Key); + Assert.IsInstanceOf(item.Value); + byte[] array = (byte[])item.Value; + Assert.AreEqual(3, array.Length); + Assert.AreEqual(42, array[0]); + Assert.AreEqual(43, array[1]); + Assert.AreEqual(44, array[2]); + } + + [Test] + public void MemoryStreamIsSupported() + { + Stream stream = typeof(ResourceReaderWriterTests).Assembly + .GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources"); + using var reader = new ResourcesFile(stream); + var items = reader.ToArray(); + Assert.AreEqual(3, items.Length); + var item = items.FirstOrDefault(i => i.Key == "MemoryStream"); + Assert.IsNotNull(item.Key); + Assert.IsInstanceOf(item.Value); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/Util/Test.resources b/ICSharpCode.Decompiler.Tests/Util/Test.resources new file mode 100644 index 0000000000000000000000000000000000000000..375a8c249994f49322c7b0aa5fe8fc53c8beceb9 GIT binary patch literal 684 zcmX?i>is@O1_p+SK%5g?SzMBus~417oL^d$oLUTL1*ImYq!#HYR*8GxXUf^%t3Noi54ZC+|=Nl{{sjzU0bQch;FcWPxwes*e}ZIZcpqG__J znW3ezNveT`r81^vrFkWpxv4PQgHubGfR17YVvqwfpz2+U63a95()FA&OL7wnbQDm9 z$#Iy0ahhqGd77bNim5@Oabkd@3lO10|`_D1yUKJ8Dbf9fGQ+>ft*~1e1;;RL@-dQ2*^(aikJW;9l-wi4+J28 zfdCK4qk&ZS2`k8aQB-50;RcdnaY-ymWM=}|%?S!+eh|?K#7vz5e(t Date: Mon, 19 Jun 2023 12:18:24 +0200 Subject: [PATCH 223/230] Fix #3010: Remove compiler-generated attributes for required members with custom ctors. --- .../TestCases/Pretty/AutoProperties.cs | 17 +++++++ .../TestCases/Pretty/Records.cs | 5 +- .../CSharp/CSharpDecompiler.cs | 47 ++++++++++++------- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AutoProperties.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AutoProperties.cs index 2a9ae6440..8a540a601 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AutoProperties.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AutoProperties.cs @@ -4,6 +4,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class AutoProperties { +#if CS110 + public required int RequiredField; +#endif public int A { get; } = 1; public int B { get; set; } = 2; @@ -22,9 +25,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public int issue1319 { get; } +#if CS110 + public required int RequiredProperty { get; set; } +#endif + public AutoProperties(int issue1319) { this.issue1319 = issue1319; +#if CS110 + RequiredProperty = 42; + RequiredField = 42; +#endif } } +#if !NET70 + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + internal sealed class RequiredMemberAttribute : Attribute + { + } +#endif } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs index d4d87d49e..49a4fba3e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -256,10 +256,13 @@ namespace System.Runtime.CompilerServices internal class IsExternalInit { } - +#endif +#if !NET70 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute { } +#endif +#if !NET60 } #endif diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 08d815d91..9615359d3 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1397,16 +1397,7 @@ namespace ICSharpCode.Decompiler.CSharp } if (settings.IntroduceRefModifiersOnStructs) { - if (FindAttribute(typeDecl, KnownAttribute.Obsolete, out var attr)) - { - if (obsoleteAttributePattern.IsMatch(attr)) - { - if (attr.Parent is AttributeSection section && section.Attributes.Count == 1) - section.Remove(); - else - attr.Remove(); - } - } + RemoveObsoleteAttribute(typeDecl, "Types with embedded references are not supported in this version of your compiler."); RemoveCompilerFeatureRequiredAttribute(typeDecl, "RefStructs"); } if (settings.RequiredMembers) @@ -1584,14 +1575,6 @@ namespace ICSharpCode.Decompiler.CSharp return firstValue == 0 ? EnumValueDisplayMode.None : EnumValueDisplayMode.FirstOnly; } - static readonly Syntax.Attribute obsoleteAttributePattern = new Syntax.Attribute() { - Type = new TypePattern(typeof(ObsoleteAttribute)), - Arguments = { - new PrimitiveExpression("Types with embedded references are not supported in this version of your compiler."), - new Choice() { new PrimitiveExpression(true), new PrimitiveExpression(false) } - } - }; - EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == method); @@ -1645,6 +1628,10 @@ namespace ICSharpCode.Decompiler.CSharp methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); methodDecl.Modifiers |= Modifiers.Override; } + if (method.IsConstructor && settings.RequiredMembers && RemoveCompilerFeatureRequiredAttribute(methodDecl, "RequiredMembers")) + { + RemoveObsoleteAttribute(methodDecl, "Constructors of types with required members are not supported in this version of your compiler."); + } return methodDecl; bool IsTypeHierarchyKnown(IType type) @@ -1877,6 +1864,30 @@ namespace ICSharpCode.Decompiler.CSharp return found; } + internal static bool RemoveObsoleteAttribute(EntityDeclaration entityDecl, string message) + { + bool found = false; + foreach (var section in entityDecl.Attributes) + { + foreach (var attr in section.Attributes) + { + var symbol = attr.Type.GetSymbol(); + if (symbol is ITypeDefinition td && td.FullTypeName == KnownAttribute.Obsolete.GetTypeName() + && attr.Arguments.Count >= 1 && attr.Arguments.First() is PrimitiveExpression pe + && pe.Value is string s && s == message) + { + attr.Remove(); + found = true; + } + } + if (section.Attributes.Count == 0) + { + section.Remove(); + } + } + return found; + } + bool FindAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType, out Syntax.Attribute attribute) { attribute = null; From 7451b216503cecc4be3141acafea6ba5206dc81b Mon Sep 17 00:00:00 2001 From: "Andrew Crawley (US - DIAGNOSTICS)" Date: Wed, 21 Jun 2023 18:59:02 -0700 Subject: [PATCH 224/230] Fix decompilation of record with missing base type This commit updates `RecordDecompiler` to avoid a null ref when the decompiler is unable to determine the base type of a record (e.g. because the base type is defined in another assembly that is not loaded). --- ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 2d832dfd7..6ddc23b43 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -53,8 +53,8 @@ namespace ICSharpCode.Decompiler.CSharp this.settings = settings; this.cancellationToken = cancellationToken; this.baseClass = recordTypeDef.DirectBaseTypes.FirstOrDefault(b => b.Kind == TypeKind.Class); - this.isStruct = baseClass.IsKnownType(KnownTypeCode.ValueType); - this.isInheritedRecord = !isStruct && !baseClass.IsKnownType(KnownTypeCode.Object); + this.isStruct = baseClass?.IsKnownType(KnownTypeCode.ValueType) ?? false; + this.isInheritedRecord = !isStruct && !(baseClass?.IsKnownType(KnownTypeCode.Object) ?? false); this.isSealed = recordTypeDef.IsSealed; DetectAutomaticProperties(); this.orderedMembers = DetectMemberOrder(recordTypeDef, backingFieldToAutoProperty); @@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.CSharp // virtual bool Equals(R? other): generated unless user-declared return IsGeneratedEquals(method); } - else if (isInheritedRecord && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(paramType, baseClass) && method.IsOverride) + else if (isInheritedRecord && baseClass != null && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(paramType, baseClass) && method.IsOverride) { // override bool Equals(BaseClass? obj): always generated return true; @@ -772,7 +772,7 @@ namespace ICSharpCode.Decompiler.CSharp return false; if (!(conditions[pos] is Call { Method: { Name: "Equals" } } call)) return false; - if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType, baseClass)) + if (baseClass != null && !NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType, baseClass)) return false; if (call.Arguments.Count != 2) return false; From 2aad6817c4e89a4149043434aef8d1dfec4db00e Mon Sep 17 00:00:00 2001 From: Ilyas Timir-Bulatov Date: Thu, 22 Jun 2023 08:40:59 +0300 Subject: [PATCH 225/230] Update readme .NET SDK -> 7.0 Unix/Mac section (#3018) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c57753c14..9db6276d5 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ If this problem occurs, please manually install the .NET 6.0 SDK from [here](htt #### Unix / Mac: -- Make sure [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) is installed. +- Make sure [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0) is installed. - Make sure [PowerShell](https://github.com/PowerShell/PowerShell) is installed (formerly known as PowerShell Core) - Clone the repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). From ccab6f45173ee6ddaed08665ffd3a4e49001af89 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 22 Jun 2023 15:50:16 +0200 Subject: [PATCH 226/230] Conditional-ize pwsh/powershell in PS cmdlet project. See #3019. --- .../ICSharpCode.Decompiler.PowerShell.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index 8c469191f..c50801c16 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -26,6 +26,7 @@ - + + From bce0f7b23bced123ec066849fe6a889852f110c0 Mon Sep 17 00:00:00 2001 From: James May Date: Thu, 6 Oct 2022 17:18:56 +1100 Subject: [PATCH 227/230] Add CustomDebugInformation kind EncStateMachineStateMap --- .../DebugInfo/KnownGuids.cs | 2 + .../CustomDebugInformationTableTreeNode.cs | 69 ++++++------------- 2 files changed, 24 insertions(+), 47 deletions(-) diff --git a/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs b/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs index 353b49acf..283818df5 100644 --- a/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs +++ b/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs @@ -10,11 +10,13 @@ namespace ICSharpCode.Decompiler.DebugInfo public static readonly Guid VBLanguageGuid = new Guid("3a12d0b8-c26c-11d0-b442-00a0244a1dd2"); public static readonly Guid FSharpLanguageGuid = new Guid("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3"); + // https://github.com/dotnet/roslyn/blob/main/src/Dependencies/CodeAnalysis.Debugging/PortableCustomDebugInfoKinds.cs public static readonly Guid StateMachineHoistedLocalScopes = new Guid("6DA9A61E-F8C7-4874-BE62-68BC5630DF71"); public static readonly Guid DynamicLocalVariables = new Guid("83C563C4-B4F3-47D5-B824-BA5441477EA8"); public static readonly Guid DefaultNamespaces = new Guid("58b2eab6-209f-4e4e-a22c-b2d0f910c782"); public static readonly Guid EditAndContinueLocalSlotMap = new Guid("755F52A8-91C5-45BE-B4B8-209571E552BD"); public static readonly Guid EditAndContinueLambdaAndClosureMap = new Guid("A643004C-0240-496F-A783-30D64F4979DE"); + public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3"); public static readonly Guid EmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe"); public static readonly Guid SourceLink = new Guid("CC110556-A091-4D38-9FEC-25AB9A351A6A"); public static readonly Guid MethodSteppingInformation = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8"); diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index f3e191de6..1bb909b25 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -120,6 +120,7 @@ namespace ICSharpCode.ILSpy.Metadata DefaultNamespaces, EditAndContinueLocalSlotMap, EditAndContinueLambdaAndClosureMap, + EncStateMachineStateMap, EmbeddedSource, SourceLink, MethodSteppingInformation, @@ -154,6 +155,10 @@ namespace ICSharpCode.ILSpy.Metadata { return CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap; } + if (KnownGuids.EncStateMachineStateMap == guid) + { + return CustomDebugInformationKind.EncStateMachineStateMap; + } if (KnownGuids.EmbeddedSource == guid) { return CustomDebugInformationKind.EmbeddedSource; @@ -211,7 +216,6 @@ namespace ICSharpCode.ILSpy.Metadata } string kindString; - public string Kind { get { if (kindString != null) @@ -226,52 +230,23 @@ namespace ICSharpCode.ILSpy.Metadata { guid = Guid.Empty; } - switch (kind) - { - case CustomDebugInformationKind.None: - kindString = ""; - break; - case CustomDebugInformationKind.StateMachineHoistedLocalScopes: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - State Machine Hoisted Local Scopes (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.DynamicLocalVariables: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Dynamic Local Variables (C#) [{guid}]"; - break; - case CustomDebugInformationKind.DefaultNamespaces: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Default Namespaces (VB) [{guid}]"; - break; - case CustomDebugInformationKind.EditAndContinueLocalSlotMap: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Local Slot Map (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Lambda And Closure Map (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.EmbeddedSource: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Embedded Source (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.SourceLink: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Source Link (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.MethodSteppingInformation: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Method Stepping Information (C# / VB) [{guid}]"; - break; - case CustomDebugInformationKind.CompilationOptions: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Options (C# / VB) [{ guid}]"; - break; - case CustomDebugInformationKind.CompilationMetadataReferences: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Metadata References (C# / VB) [{ guid}]"; - break; - case CustomDebugInformationKind.TupleElementNames: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Tuple Element Names (C#) [{ guid}]"; - break; - case CustomDebugInformationKind.TypeDefinitionDocuments: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Type Definition Documents (C# / VB) [{ guid}]"; - break; - default: - kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Unknown [{guid}]"; - break; - } - + kindString = kind switch { + CustomDebugInformationKind.None => "", + CustomDebugInformationKind.StateMachineHoistedLocalScopes => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - State Machine Hoisted Local Scopes (C# / VB) [{guid}]", + CustomDebugInformationKind.DynamicLocalVariables => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Dynamic Local Variables (C#) [{guid}]", + CustomDebugInformationKind.DefaultNamespaces => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Default Namespaces (VB) [{guid}]", + CustomDebugInformationKind.EditAndContinueLocalSlotMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Local Slot Map (C# / VB) [{guid}]", + CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Lambda And Closure Map (C# / VB) [{guid}]", + CustomDebugInformationKind.EncStateMachineStateMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue State Machine State Map (C# / VB) [{guid}]", + CustomDebugInformationKind.EmbeddedSource => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Embedded Source (C# / VB) [{guid}]", + CustomDebugInformationKind.SourceLink => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Source Link (C# / VB) [{guid}]", + CustomDebugInformationKind.MethodSteppingInformation => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Method Stepping Information (C# / VB) [{guid}]", + CustomDebugInformationKind.CompilationOptions => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Options (C# / VB) [{guid}]", + CustomDebugInformationKind.CompilationMetadataReferences => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Metadata References (C# / VB) [{guid}]", + CustomDebugInformationKind.TupleElementNames => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Tuple Element Names (C#) [{guid}]", + CustomDebugInformationKind.TypeDefinitionDocuments => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Type Definition Documents (C# / VB) [{guid}]", + _ => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Unknown [{guid}]", + }; return kindString; } } From 8ebe6a36733f8b7014f1edccb1b43052233752ad Mon Sep 17 00:00:00 2001 From: James May Date: Fri, 7 Oct 2022 11:49:46 +1100 Subject: [PATCH 228/230] CustomDebugInformationTableTreeNode: improvements * add Parent kind to tooltip * Format value blob heap offset to 8 digits --- .../DebugTables/CustomDebugInformationTableTreeNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index 1bb909b25..981802be0 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -211,7 +211,7 @@ namespace ICSharpCode.ILSpy.Metadata ITextOutput output = new PlainTextOutput(); var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); debugInfo.Parent.WriteTo(module, output, context); - return output.ToString(); + return $"{debugInfo.Parent.Kind}:\n{output}"; } } @@ -251,7 +251,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [StringFormat("X8")] public int Value => MetadataTokens.GetHeapOffset(debugInfo.Value); public string ValueTooltip { From 92a69cc12cbc2b3a8a6e93aa98c8c7083cdf8a88 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 24 Jun 2023 15:56:48 +0200 Subject: [PATCH 229/230] Introduce ColumnKind enum. Rename StringFormatAttribute -> ColumnInfoAttribute. --- .../CorTables/AssemblyRefTableTreeNode.cs | 4 +-- .../CorTables/AssemblyTableTreeNode.cs | 4 +-- .../CorTables/ClassLayoutTableTreeNode.cs | 9 +++--- .../CorTables/ConstantTableTreeNode.cs | 7 ++--- .../CorTables/CustomAttributeTableTreeNode.cs | 8 ++--- .../CorTables/DeclSecurityTableTreeNode.cs | 7 ++--- .../CorTables/EventMapTableTreeNode.cs | 6 ++-- .../Metadata/CorTables/EventTableTreeNode.cs | 5 ++-- .../CorTables/ExportedTypeTableTreeNode.cs | 5 ++-- .../CorTables/FieldLayoutTableTreeNode.cs | 5 ++-- .../CorTables/FieldMarshalTableTreeNode.cs | 5 ++-- .../CorTables/FieldRVATableTreeNode.cs | 13 ++++---- .../Metadata/CorTables/FieldTableTreeNode.cs | 4 +-- ILSpy/Metadata/CorTables/FileTableTreeNode.cs | 4 +-- .../GenericParamConstraintTableTreeNode.cs | 6 ++-- .../CorTables/GenericParamTableTreeNode.cs | 5 ++-- .../CorTables/ImplMapTableTreeNode.cs | 8 ++--- .../CorTables/InterfaceImplTableTreeNode.cs | 6 ++-- .../ManifestResourceTableTreeNode.cs | 5 ++-- .../CorTables/MemberRefTableTreeNode.cs | 5 ++-- .../CorTables/MethodImplTableTreeNode.cs | 9 ++---- .../CorTables/MethodSemanticsTableTreeNode.cs | 8 ++--- .../CorTables/MethodSpecTableTreeNode.cs | 5 ++-- .../Metadata/CorTables/MethodTableTreeNode.cs | 6 ++-- .../Metadata/CorTables/ModuleTableTreeNode.cs | 6 ++-- .../CorTables/NestedClassTableTreeNode.cs | 6 ++-- .../Metadata/CorTables/ParamTableTreeNode.cs | 2 +- .../CorTables/PropertyMapTableTreeNode.cs | 6 ++-- .../CorTables/PropertyTableTreeNode.cs | 4 +-- .../CorTables/StandAloneSigTableTreeNode.cs | 2 +- .../CorTables/TypeDefTableTreeNode.cs | 11 +++---- .../CorTables/TypeRefTableTreeNode.cs | 3 +- .../CorTables/TypeSpecTableTreeNode.cs | 2 +- .../CustomDebugInformationTableTreeNode.cs | 5 ++-- .../DebugTables/DocumentTableTreeNode.cs | 6 ++-- .../DebugTables/ImportScopeTableTreeNode.cs | 5 ++-- .../DebugTables/LocalConstantTableTreeNode.cs | 2 +- .../DebugTables/LocalScopeTableTreeNode.cs | 12 +++----- .../DebugTables/LocalVariableTableTreeNode.cs | 2 +- .../MethodDebugInformationTableTreeNode.cs | 8 ++--- .../StateMachineMethodTableTreeNode.cs | 6 ++-- ILSpy/Metadata/GoToTokenCommand.cs | 2 +- ILSpy/Metadata/Helpers.cs | 30 ++++++++++++------- 43 files changed, 116 insertions(+), 153 deletions(-) diff --git a/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs index 4be8cd37a..4ad56815d 100644 --- a/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs @@ -87,14 +87,14 @@ namespace ICSharpCode.ILSpy.Metadata public Version Version => assemblyRef.Version; - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public AssemblyFlags Flags => assemblyRef.Flags; public object FlagsTooltip => new FlagsTooltip((int)assemblyRef.Flags, null) { FlagGroup.CreateMultipleChoiceGroup(typeof(AssemblyFlags), selectedValue: (int)assemblyRef.Flags, includeAll: false) }; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int PublicKeyOrToken => MetadataTokens.GetHeapOffset(assemblyRef.PublicKeyOrToken); public string PublicKeyOrTokenTooltip { diff --git a/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs b/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs index 4c16f6713..5d8409657 100644 --- a/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs @@ -72,14 +72,14 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Assembly) + metadata.GetTableRowSize(TableIndex.Assembly) * (RID - 1); - [StringFormat("X4")] + [ColumnInfo("X4", Kind = ColumnKind.Other)] public AssemblyHashAlgorithm HashAlgorithm => assembly.HashAlgorithm; public object HashAlgorithmTooltip => new FlagsTooltip() { FlagGroup.CreateSingleChoiceGroup(typeof(AssemblyHashAlgorithm), selectedValue: (int)assembly.HashAlgorithm, defaultFlag: new Flag("None (0000)", 0, false), includeAny: false) }; - [StringFormat("X4")] + [ColumnInfo("X4", Kind = ColumnKind.Other)] public AssemblyFlags Flags => assembly.Flags; public object FlagsTooltip => new FlagsTooltip() { diff --git a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs index 97be91596..9da461b58 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -100,8 +100,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(classLayout.Parent); public void OnParentClick() @@ -113,15 +112,15 @@ namespace ICSharpCode.ILSpy.Metadata get { ITextOutput output = new PlainTextOutput(); var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)classLayout.Parent).WriteTo(module, output, context); + classLayout.Parent.WriteTo(module, output, context); return output.ToString(); } } - [StringFormat("X4")] + [ColumnInfo("X4", Kind = ColumnKind.Other)] public ushort PackingSize => classLayout.PackingSize; - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public uint ClassSize => classLayout.ClassSize; public ClassLayoutEntry(PEFile module, byte* ptr, int metadataOffset, int row) diff --git a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs index 0493abd8f..d7b30b4b8 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -87,13 +87,12 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Constant) + metadata.GetTableRowSize(TableIndex.Constant) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public ConstantTypeCode Type => constant.TypeCode; public string TypeTooltip => constant.TypeCode.ToString(); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(constant.Parent); public void OnParentClick() @@ -110,7 +109,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(constant.Value); public string ValueTooltip { diff --git a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs index 4070ad9be..7691be4b8 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -87,8 +87,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.CustomAttribute) + metadata.GetTableRowSize(TableIndex.CustomAttribute) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(customAttr.Parent); public void OnParentClick() @@ -105,8 +104,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Constructor => MetadataTokens.GetToken(customAttr.Constructor); public void OnConstructorClick() @@ -123,7 +121,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(customAttr.Value); public string ValueTooltip { diff --git a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs index bcfb719ad..8e3b82330 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -88,8 +88,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.DeclSecurity) + metadata.GetTableRowSize(TableIndex.DeclSecurity) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(declSecAttr.Parent); public void OnParentClick() @@ -106,7 +105,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public DeclarativeSecurityAction Action => declSecAttr.Action; public string ActionTooltip { @@ -115,7 +114,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int PermissionSet => MetadataTokens.GetHeapOffset(declSecAttr.PermissionSet); public string PermissionSetTooltip { diff --git a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs index d25738f72..6cf0f9349 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(eventMap.Parent); public void OnParentClick() @@ -116,8 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EventList => MetadataTokens.GetToken(eventMap.EventList); public void OnEventListClick() diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs index 4ed3b9a84..dabcb7cc4 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Event) + metadata.GetTableRowSize(TableIndex.Event) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public EventAttributes Attributes => eventDef.Attributes; public object AttributesTooltip => new FlagsTooltip { @@ -103,8 +103,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(eventDef.Type); public void OnTypeClick() diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index fee2aff10..878024a00 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.ExportedType) + metadata.GetTableRowSize(TableIndex.ExportedType) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public TypeAttributes Attributes => type.Attributes; const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); @@ -110,8 +110,7 @@ namespace ICSharpCode.ILSpy.Metadata public string TypeNamespace => metadata.GetString(type.Namespace); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Implementation => MetadataTokens.GetToken(type.Implementation); public void OnImplementationClick() diff --git a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs index d959a49b6..c5aa00d71 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Field => MetadataTokens.GetToken(fieldLayout.Field); public void OnFieldClick() @@ -116,7 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public int FieldOffset => fieldLayout.Offset; public FieldLayoutEntry(PEFile module, byte* ptr, int metadataOffset, int row) diff --git a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs index a4d74b9bc..b85d3c5ec 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(fieldMarshal.Parent); public void OnParentClick() @@ -116,7 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int NativeType => MetadataTokens.GetHeapOffset(fieldMarshal.NativeType); public FieldMarshalEntry(PEFile module, byte* ptr, int metadataOffset, int row) diff --git a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs index c0d54a023..890b71cdf 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata { readonly PEFile module; readonly MetadataReader metadata; - readonly FieldRVA fieldLayout; + readonly FieldRVA fieldRVA; public int RID { get; } @@ -98,13 +98,12 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] - public int Field => MetadataTokens.GetToken(fieldLayout.Field); + [ColumnInfo("X8", Kind = ColumnKind.Token)] + public int Field => MetadataTokens.GetToken(fieldRVA.Field); public void OnFieldClick() { - MainWindow.Instance.JumpToReference(new EntityReference(module, fieldLayout.Field, protocol: "metadata")); + MainWindow.Instance.JumpToReference(new EntityReference(module, fieldRVA.Field, protocol: "metadata")); } public string FieldTooltip { @@ -116,8 +115,8 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] - public int FieldOffset => fieldLayout.Offset; + [ColumnInfo("X8", Kind = ColumnKind.Other)] + public int FieldOffset => fieldRVA.Offset; public FieldRVAEntry(PEFile module, byte* ptr, int metadataOffset, int row) { diff --git a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs index bdc927b28..73eceefa1 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Field) + metadata.GetTableRowSize(TableIndex.Field) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public FieldAttributes Attributes => fieldDef.Attributes; const FieldAttributes otherFlagsMask = ~(FieldAttributes.FieldAccessMask); @@ -110,7 +110,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/FileTableTreeNode.cs b/ILSpy/Metadata/CorTables/FileTableTreeNode.cs index c48b8890b..ddf0e5189 100644 --- a/ILSpy/Metadata/CorTables/FileTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FileTableTreeNode.cs @@ -85,7 +85,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.File) + metadata.GetTableRowSize(TableIndex.File) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public int Attributes => assemblyFile.ContainsMetadata ? 1 : 0; public string AttributesTooltip => assemblyFile.ContainsMetadata ? "ContainsMetaData" : "ContainsNoMetaData"; @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(assemblyFile.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int HashValue => MetadataTokens.GetHeapOffset(assemblyFile.HashValue); public string HashValueTooltip { diff --git a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs index 760cb2df1..4fcf1bc66 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -86,8 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.GenericParamConstraint) + metadata.GetTableRowSize(TableIndex.GenericParamConstraint) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Owner => MetadataTokens.GetToken(genericParamConstraint.Parameter); public void OnOwnerClick() @@ -111,8 +110,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(genericParamConstraint.Type); public void OnTypeClick() diff --git a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs index 5bb682d2b..fd1590817 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Number => genericParam.Index; - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public GenericParameterAttributes Attributes => genericParam.Attributes; public object AttributesTooltip => new FlagsTooltip { @@ -96,8 +96,7 @@ namespace ICSharpCode.ILSpy.Metadata FlagGroup.CreateSingleChoiceGroup(typeof(GenericParameterAttributes), "Managed type: ", (int)GenericParameterAttributes.SpecialConstraintMask, (int)(genericParam.Attributes & GenericParameterAttributes.SpecialConstraintMask), new Flag("None (0000)", 0, false), includeAny: false), }; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Owner => MetadataTokens.GetToken(genericParam.Parent); public void OnParentClick() diff --git a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs index ccfd7801b..8ad12af71 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public PInvokeAttributes MappingFlags => implMap.MappingFlags; const PInvokeAttributes otherFlagsMask = ~(PInvokeAttributes.CallConvMask | PInvokeAttributes.CharSetMask); @@ -115,8 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata FlagGroup.CreateMultipleChoiceGroup(typeof(PInvokeAttributes), "Flags:", (int)otherFlagsMask, (int)(implMap.MappingFlags & otherFlagsMask), includeAll: false), }; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MemberForwarded => MetadataTokens.GetToken(implMap.MemberForwarded); public void OnMemberForwardedClick() @@ -133,8 +132,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(implMap.ImportScope); public void OnImportScopeClick() diff --git a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs index 6f5090032..8764c1c5a 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Class => MetadataTokens.GetToken(interfaceImpl.Class); public void OnClassClick() @@ -116,8 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Interface => MetadataTokens.GetToken(interfaceImpl.Interface); public void OnInterfaceClick() diff --git a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs index e720ee1c1..0d9619cd9 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.ManifestResource) + metadata.GetTableRowSize(TableIndex.ManifestResource) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public ManifestResourceAttributes Attributes => manifestResource.Attributes; public object AttributesTooltip => manifestResource.Attributes.ToString(); @@ -97,8 +97,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(manifestResource.Name):X} \"{Name}\""; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Implementation => MetadataTokens.GetToken(manifestResource.Implementation); public void OnImplementationClick() diff --git a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs index 5c4987846..e4701270f 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -87,8 +87,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MemberRef) + metadata.GetTableRowSize(TableIndex.MemberRef) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(memberRef.Parent); public void OnParentClick() @@ -108,7 +107,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(memberRef.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(memberRef.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs index bd7292df8..8b761b27d 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -86,8 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodDeclaration => MetadataTokens.GetToken(methodImpl.MethodDeclaration); public void OnMethodDeclarationClick() @@ -103,8 +102,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody); public void OnMethodBodyClick() @@ -120,8 +118,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(methodImpl.Type); public void OnTypeClick() diff --git a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs index 73316effc..6956e0cc6 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -90,13 +90,12 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodSemanticsAttributes Semantics => semantics; public string SemanticsTooltip => semantics.ToString(); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(method); public void OnMethodClick() @@ -112,8 +111,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Association => MetadataTokens.GetToken(association); public void OnAssociationClick() diff --git a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs index 702f66594..780b502ef 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -87,8 +87,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodSpec) + metadata.GetTableRowSize(TableIndex.MethodSpec) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(methodSpec.Method); public void OnMethodClick() @@ -104,7 +103,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(methodSpec.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs index c1b1160f2..639e6f025 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodAttributes Attributes => methodDef.Attributes; const MethodAttributes otherFlagsMask = ~(MethodAttributes.MemberAccessMask | MethodAttributes.VtableLayoutMask); @@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.Metadata FlagGroup.CreateMultipleChoiceGroup(typeof(MethodAttributes), "Flags:", (int)otherFlagsMask, (int)(methodDef.Attributes & otherFlagsMask), includeAll: false), }; - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodImplAttributes ImplAttributes => methodDef.ImplAttributes; public object ImplAttributesTooltip => new FlagsTooltip { @@ -117,7 +117,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(methodDef.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(methodDef.Signature); string signatureTooltip; diff --git a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs index b86387ecd..ba6509426 100644 --- a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs @@ -82,17 +82,17 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(moduleDef.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Mvid => MetadataTokens.GetHeapOffset(moduleDef.Mvid); public string MvidTooltip => metadata.GetGuid(moduleDef.Mvid).ToString(); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int GenerationId => MetadataTokens.GetHeapOffset(moduleDef.GenerationId); public string GenerationIdTooltip => moduleDef.GenerationId.IsNil ? null : metadata.GetGuid(moduleDef.GenerationId).ToString(); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int BaseGenerationId => MetadataTokens.GetHeapOffset(moduleDef.BaseGenerationId); public string BaseGenerationIdTooltip => moduleDef.BaseGenerationId.IsNil ? null : metadata.GetGuid(moduleDef.BaseGenerationId).ToString(); diff --git a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs index d9560ab7e..dda3ad8e1 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int NestedClass => MetadataTokens.GetToken(nestedClass.Nested); public void OnNestedClassClick() @@ -116,8 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EnclosingClass => MetadataTokens.GetToken(nestedClass.Enclosing); public void OnEnclosingClassClick() diff --git a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs index 5aea6118d..5fee320ee 100644 --- a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs @@ -85,7 +85,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Param) + metadata.GetTableRowSize(TableIndex.Param) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public ParameterAttributes Attributes => param.Attributes; public object AttributesTooltip => new FlagsTooltip { diff --git a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs index c4291171f..b34023f2a 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata public int Offset { get; } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(propertyMap.Parent); public void OnParentClick() @@ -116,8 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int PropertyList => MetadataTokens.GetToken(propertyMap.PropertyList); public void OnPropertyListClick() diff --git a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs index 39c380806..0b428934e 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.Property) + metadata.GetTableRowSize(TableIndex.Property) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public PropertyAttributes Attributes => propertyDef.Attributes; public object AttributesTooltip => new FlagsTooltip { @@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.Metadata IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs index ff6fbfb76..89e79afbc 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.StandAloneSig) + metadata.GetTableRowSize(TableIndex.StandAloneSig) * (RID - 1); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index 078d8afb0..47b38af0a 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -91,7 +91,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.TypeDef) + metadata.GetTableRowSize(TableIndex.TypeDef) * (RID - 1); - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public TypeAttributes Attributes => typeDef.Attributes; const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); @@ -113,8 +113,7 @@ namespace ICSharpCode.ILSpy.Metadata public string Namespace => metadata.GetString(typeDef.Namespace); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int BaseType => MetadataTokens.GetToken(typeDef.BaseType); public void OnBaseTypeClick() @@ -145,8 +144,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int FieldList => MetadataTokens.GetToken(typeDef.GetFields().FirstOrDefault()); public void OnFieldListClick() @@ -166,8 +164,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodList => MetadataTokens.GetToken(typeDef.GetMethods().FirstOrDefault()); public void OnMethodListClick() diff --git a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs index 24db219f9..6e34da4da 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -86,8 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.TypeRef) + metadata.GetTableRowSize(TableIndex.TypeRef) * (RID - 1); - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ResolutionScope => MetadataTokens.GetToken(typeRef.ResolutionScope); public void OnResolutionScopeClick() diff --git a/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs index 1d19874e5..5b87adaa1 100644 --- a/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableMetadataOffset(TableIndex.TypeSpec) + metadata.GetTableRowSize(TableIndex.TypeSpec) * (RID - 1); - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(typeSpec.Signature); public string SignatureTooltip { diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index 981802be0..329cc86b6 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -197,8 +197,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(debugInfo.Parent); public void OnParentClick() @@ -251,7 +250,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(debugInfo.Value); public string ValueTooltip { diff --git a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs index c3cef36da..2a7fdb8e4 100644 --- a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(document.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int HashAlgorithm => MetadataTokens.GetHeapOffset(document.HashAlgorithm); public string HashAlgorithmTooltip { @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Hash => MetadataTokens.GetHeapOffset(document.Hash); public string HashTooltip { @@ -118,7 +118,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Language => MetadataTokens.GetHeapOffset(document.Language); public string LanguageTooltip { diff --git a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs index e7841965d..0e6ee99d0 100644 --- a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs @@ -90,8 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(localScope.Parent); public void OnParentClick() @@ -99,7 +98,7 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, localScope.Parent, protocol: "metadata")); } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Imports => MetadataTokens.GetHeapOffset(localScope.ImportsBlob); public ImportScopeEntry(PEFile module, MetadataReader metadata, bool isEmbedded, ImportScopeHandle handle) diff --git a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs index 2a095fee4..9eaf8be2a 100644 --- a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata public string NameTooltip => $"{MetadataTokens.GetHeapOffset(localConst.Name):X} \"{Name}\""; - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(localConst.Signature); public LocalConstantEntry(PEFile module, MetadataReader metadata, bool isEmbedded, LocalConstantHandle handle) diff --git a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs index 546e5c395..6854b64e0 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -90,8 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(localScope.Method); public void OnMethodClick() @@ -107,8 +106,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(localScope.ImportScope); public void OnImportScopeClick() @@ -116,8 +114,7 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, localScope.ImportScope, protocol: "metadata")); } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int VariableList => MetadataTokens.GetToken(localScope.GetLocalVariables().FirstOrDefault()); public void OnVariableListClick() @@ -125,8 +122,7 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, localScope.GetLocalVariables().FirstOrDefault(), protocol: "metadata")); } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ConstantList => MetadataTokens.GetToken(localScope.GetLocalConstants().FirstOrDefault()); public void OnConstantListClick() diff --git a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs index ed8744e2e..9913e1a6b 100644 --- a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public LocalVariableAttributes Attributes => localVar.Attributes; public object AttributesTooltip => new FlagsTooltip() { diff --git a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs index 46dc93885..d439308e4 100644 --- a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs @@ -88,8 +88,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Document => MetadataTokens.GetToken(debugInfo.Document); public void OnDocumentClick() @@ -106,7 +105,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int SequencePoints => MetadataTokens.GetHeapOffset(debugInfo.SequencePointsBlob); public string SequencePointsTooltip { @@ -122,8 +121,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int LocalSignature => MetadataTokens.GetToken(debugInfo.LocalSignature); public void OnLocalSignatureClick() diff --git a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs index 5e30b145f..4973f38d3 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -92,8 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata public object Offset => offset == null ? "n/a" : (object)offset; - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MoveNextMethod => MetadataTokens.GetToken(moveNextMethod); public void OnMoveNextMethodClick() @@ -110,8 +109,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int KickoffMethod => MetadataTokens.GetToken(kickoffMethod); public void OnKickofMethodClick() diff --git a/ILSpy/Metadata/GoToTokenCommand.cs b/ILSpy/Metadata/GoToTokenCommand.cs index b35db7d08..d8963ce60 100644 --- a/ILSpy/Metadata/GoToTokenCommand.cs +++ b/ILSpy/Metadata/GoToTokenCommand.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Commands Type type = cell.Item.GetType(); var property = type.GetProperty(cell.Column.Header.ToString()); var moduleField = type.GetField("module", BindingFlags.NonPublic | BindingFlags.Instance); - if (property == null || property.PropertyType != typeof(int) || !property.GetCustomAttributes(false).Any(a => a is StringFormatAttribute sf && sf.Format == "X8")) + if (property == null || property.PropertyType != typeof(int) || !property.GetCustomAttributes(false).Any(a => a is ColumnInfoAttribute { Kind: ColumnKind.Token } c)) return null; module = (PEFile)moduleField.GetValue(cell.Item); return (int)property.GetValue(cell.Item); diff --git a/ILSpy/Metadata/Helpers.cs b/ILSpy/Metadata/Helpers.cs index cd6558a17..da418b554 100644 --- a/ILSpy/Metadata/Helpers.cs +++ b/ILSpy/Metadata/Helpers.cs @@ -143,7 +143,7 @@ namespace ICSharpCode.ILSpy.Metadata var descriptor = (PropertyDescriptor)e.PropertyDescriptor; - if (descriptor.Attributes.OfType().Any()) + if (descriptor.Attributes.OfType().Any(c => c.Kind == ColumnKind.Token || c.LinkToTable)) { return new DataGridTemplateColumn() { Header = e.PropertyName, @@ -200,12 +200,12 @@ namespace ICSharpCode.ILSpy.Metadata string key = descriptor.PropertyType.Name + "Filter"; column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[key]); } - var stringFormat = descriptor.Attributes.OfType().FirstOrDefault(); - if (stringFormat != null) + var columnInfo = descriptor.Attributes.OfType().FirstOrDefault(); + if (columnInfo != null) { - binding.StringFormat = stringFormat.Format; + binding.StringFormat = columnInfo.Format; if (!descriptor.PropertyType.IsEnum - && stringFormat.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase)) + && columnInfo.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase)) { column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]); } @@ -297,20 +297,28 @@ namespace ICSharpCode.ILSpy.Metadata } } - class StringFormatAttribute : Attribute + enum ColumnKind + { + HeapOffset, + Token, + Other + } + + [AttributeUsage(AttributeTargets.Property)] + class ColumnInfoAttribute : Attribute { public string Format { get; } - public StringFormatAttribute(string format) + public ColumnKind Kind { get; set; } + + public bool LinkToTable { get; set; } + + public ColumnInfoAttribute(string format) { this.Format = format; } } - class LinkToTableAttribute : Attribute - { - } - [Flags] internal enum TableMask : ulong { From a0d3dc87c486ee0714bab3d776e27e451fc86bd0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 24 Jun 2023 15:59:45 +0200 Subject: [PATCH 230/230] Introduce GenerateTooltip: Add more information to tooltips; display token kind and "Name" if available --- .../CorTables/ClassLayoutTableTreeNode.cs | 11 +-- .../CorTables/ConstantTableTreeNode.cs | 11 +-- .../CorTables/CustomAttributeTableTreeNode.cs | 22 ++---- .../CorTables/DeclSecurityTableTreeNode.cs | 11 +-- .../CorTables/EventMapTableTreeNode.cs | 22 ++---- .../Metadata/CorTables/EventTableTreeNode.cs | 10 +-- .../CorTables/ExportedTypeTableTreeNode.cs | 13 +--- .../CorTables/FieldLayoutTableTreeNode.cs | 11 +-- .../CorTables/FieldMarshalTableTreeNode.cs | 11 +-- .../CorTables/FieldRVATableTreeNode.cs | 13 ++-- .../Metadata/CorTables/FieldTableTreeNode.cs | 11 +-- .../GenericParamConstraintTableTreeNode.cs | 10 +-- .../CorTables/GenericParamTableTreeNode.cs | 12 ++-- .../CorTables/ImplMapTableTreeNode.cs | 22 ++---- .../CorTables/InterfaceImplTableTreeNode.cs | 22 ++---- .../ManifestResourceTableTreeNode.cs | 13 +--- .../CorTables/MemberRefTableTreeNode.cs | 21 ++---- .../CorTables/MethodImplTableTreeNode.cs | 30 +++----- .../CorTables/MethodSemanticsTableTreeNode.cs | 20 ++---- .../CorTables/MethodSpecTableTreeNode.cs | 10 +-- .../Metadata/CorTables/MethodTableTreeNode.cs | 13 +--- .../CorTables/NestedClassTableTreeNode.cs | 22 ++---- .../CorTables/PropertyMapTableTreeNode.cs | 22 ++---- .../CorTables/PropertyTableTreeNode.cs | 10 +-- .../CorTables/StandAloneSigTableTreeNode.cs | 11 +-- .../CorTables/TypeDefTableTreeNode.cs | 14 ++-- .../CorTables/TypeRefTableTreeNode.cs | 28 +------- .../CustomDebugInformationTableTreeNode.cs | 10 +-- .../DebugTables/LocalScopeTableTreeNode.cs | 10 +-- .../StateMachineMethodTableTreeNode.cs | 22 ++---- ILSpy/Metadata/MetadataTableTreeNode.cs | 72 ++++++++++++++++++- 31 files changed, 194 insertions(+), 346 deletions(-) diff --git a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs index 9da461b58..af78f3044 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -108,14 +108,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, classLayout.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - classLayout.Parent.WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, classLayout.Parent); [ColumnInfo("X4", Kind = ColumnKind.Other)] public ushort PackingSize => classLayout.PackingSize; @@ -132,6 +126,7 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableRowSize(TableIndex.ClassLayout) * (row - 1); this.Offset = metadataOffset + rowOffset; this.classLayout = new ClassLayout(ptr + rowOffset, metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4); + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs index d7b30b4b8..3f9dc97c8 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -100,14 +100,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, constant.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - constant.Parent.WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, constant.Parent); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(constant.Value); @@ -125,6 +119,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.constant = metadata.GetConstant(handle); + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs index 7691be4b8..30a6cf47f 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -95,14 +95,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, customAttr.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - customAttr.Parent.WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, customAttr.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Constructor => MetadataTokens.GetToken(customAttr.Constructor); @@ -112,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, customAttr.Constructor, protocol: "metadata")); } - public string ConstructorTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - customAttr.Constructor.WriteTo(module, output, context); - return output.ToString(); - } - } + string constructorTooltip; + public string ConstructorTooltip => GenerateTooltip(ref constructorTooltip, module, customAttr.Constructor); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(customAttr.Value); @@ -137,6 +125,8 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.customAttr = metadata.GetCustomAttribute(handle); + this.parentTooltip = null; + this.constructorTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs index 8e3b82330..601b7a8b1 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -96,14 +96,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, declSecAttr.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - declSecAttr.Parent.WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, declSecAttr.Parent); [ColumnInfo("X8", Kind = ColumnKind.Other)] public DeclarativeSecurityAction Action => declSecAttr.Action; @@ -130,6 +124,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.declSecAttr = metadata.GetDeclarativeSecurityAttribute(handle); + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs index 6cf0f9349..ae0833708 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, eventMap.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)eventMap.Parent).WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, eventMap.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EventList => MetadataTokens.GetToken(eventMap.EventList); @@ -123,14 +117,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, eventMap.EventList, protocol: "metadata")); } - public string EventListTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)eventMap.EventList).WriteTo(module, output, context); - return output.ToString(); - } - } + string eventListTooltip; + public string EventListTooltip => GenerateTooltip(ref eventListTooltip, module, eventMap.EventList); public EventMapEntry(PEFile module, byte* ptr, int metadataOffset, int row) { @@ -143,6 +131,8 @@ namespace ICSharpCode.ILSpy.Metadata int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; int eventDefSize = metadata.GetTableRowCount(TableIndex.Event) < ushort.MaxValue ? 2 : 4; this.eventMap = new EventMap(ptr + rowOffset, typeDefSize, eventDefSize); + this.parentTooltip = null; + this.eventListTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs index dabcb7cc4..b24f093dc 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -111,13 +111,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, eventDef.Type, protocol: "metadata")); } - public string TypeTooltip { - get { - ITextOutput output = new PlainTextOutput(); - eventDef.Type.WriteTo(module, output, default); - return output.ToString(); - } - } + string typeTooltip; + public string TypeTooltip => GenerateTooltip(ref typeTooltip, module, eventDef.Type); public EventDefEntry(PEFile module, EventDefinitionHandle handle) { @@ -126,6 +121,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.eventDef = metadata.GetEventDefinition(handle); + this.typeTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index 878024a00..b95a5a9e8 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -118,16 +118,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, type.Implementation, protocol: "metadata")); } - public string ImplementationTooltip { - get { - if (type.Implementation.IsNil) - return null; - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - type.Implementation.WriteTo(module, output, context); - return output.ToString(); - } - } + string implementationTooltip; + public string ImplementationTooltip => GenerateTooltip(ref implementationTooltip, module, type.Implementation); public ExportedTypeEntry(int metadataOffset, PEFile module, ExportedTypeHandle handle, ExportedType type) { @@ -136,6 +128,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.type = type; + this.implementationTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs index c5aa00d71..8a80966b0 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, fieldLayout.Field, protocol: "metadata")); } - public string FieldTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)fieldLayout.Field).WriteTo(module, output, context); - return output.ToString(); - } - } + string fieldTooltip; + public string FieldTooltip => GenerateTooltip(ref fieldTooltip, module, fieldLayout.Field); [ColumnInfo("X8", Kind = ColumnKind.Other)] public int FieldOffset => fieldLayout.Offset; @@ -128,6 +122,7 @@ namespace ICSharpCode.ILSpy.Metadata this.Offset = metadataOffset + rowOffset; int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4; this.fieldLayout = new FieldLayout(ptr + rowOffset, fieldDefSize); + this.fieldTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs index b85d3c5ec..a9f26ff13 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, fieldMarshal.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)fieldMarshal.Parent).WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, fieldMarshal.Parent); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int NativeType => MetadataTokens.GetHeapOffset(fieldMarshal.NativeType); @@ -129,6 +123,7 @@ namespace ICSharpCode.ILSpy.Metadata int hasFieldMarshalRefSize = metadata.ComputeCodedTokenSize(32768, TableMask.Field | TableMask.Param); int blobHeapSize = metadata.GetHeapSize(HeapIndex.Blob) < ushort.MaxValue ? 2 : 4; this.fieldMarshal = new FieldMarshal(ptr + rowOffset, blobHeapSize, hasFieldMarshalRefSize); + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs index 890b71cdf..9317bb336 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, fieldRVA.Field, protocol: "metadata")); } - public string FieldTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)fieldLayout.Field).WriteTo(module, output, context); - return output.ToString(); - } - } + string fieldTooltip; + public string FieldTooltip => GenerateTooltip(ref fieldTooltip, module, fieldRVA.Field); [ColumnInfo("X8", Kind = ColumnKind.Other)] public int FieldOffset => fieldRVA.Offset; @@ -127,7 +121,8 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableRowSize(TableIndex.FieldRva) * (row - 1); this.Offset = metadataOffset + rowOffset; int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4; - this.fieldLayout = new FieldRVA(ptr + rowOffset, fieldDefSize); + this.fieldRVA = new FieldRVA(ptr + rowOffset, fieldDefSize); + this.fieldTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs index 73eceefa1..f03666168 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -113,14 +113,8 @@ namespace ICSharpCode.ILSpy.Metadata [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); - public string SignatureTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)handle).WriteTo(module, output, context); - return output.ToString(); - } - } + string signatureTooltip; + public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, module, handle); public FieldDefEntry(PEFile module, FieldDefinitionHandle handle) { @@ -129,6 +123,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.fieldDef = metadata.GetFieldDefinition(handle); + this.signatureTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs index 4fcf1bc66..1f97fdce6 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -118,13 +118,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, genericParamConstraint.Type, protocol: "metadata")); } - public string TypeTooltip { - get { - ITextOutput output = new PlainTextOutput(); - genericParamConstraint.Type.WriteTo(module, output, default); - return output.ToString(); - } - } + string typeTooltip; + public string TypeTooltip => GenerateTooltip(ref typeTooltip, module, genericParamConstraint.Type); public GenericParamConstraintEntry(PEFile module, GenericParameterConstraintHandle handle) { @@ -134,6 +129,7 @@ namespace ICSharpCode.ILSpy.Metadata this.handle = handle; this.genericParamConstraint = metadata.GetGenericParameterConstraint(handle); this.ownerTooltip = null; + this.typeTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs index fd1590817..789670a59 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -99,18 +99,13 @@ namespace ICSharpCode.ILSpy.Metadata [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Owner => MetadataTokens.GetToken(genericParam.Parent); - public void OnParentClick() + public void OnOwnerClick() { MainWindow.Instance.JumpToReference(new EntityReference(module, genericParam.Parent, protocol: "metadata")); } - public string OwnerTooltip { - get { - ITextOutput output = new PlainTextOutput(); - genericParam.Parent.WriteTo(module, output, default); - return output.ToString(); - } - } + string ownerTooltip; + public string OwnerTooltip => GenerateTooltip(ref ownerTooltip, module, genericParam.Parent); public string Name => metadata.GetString(genericParam.Name); @@ -123,6 +118,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.genericParam = metadata.GetGenericParameter(handle); + this.ownerTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs index 8ad12af71..c6c228ba6 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -123,14 +123,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, implMap.MemberForwarded, protocol: "metadata")); } - public string MemberForwardedTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)implMap.MemberForwarded).WriteTo(module, output, context); - return output.ToString(); - } - } + string memberForwardedTooltip; + public string MemberForwardedTooltip => GenerateTooltip(ref memberForwardedTooltip, module, implMap.MemberForwarded); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(implMap.ImportScope); @@ -140,14 +134,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, implMap.ImportScope, protocol: "metadata")); } - public string ImportScopeTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)implMap.ImportScope).WriteTo(module, output, context); - return output.ToString(); - } - } + string importScopeTooltip; + public string ImportScopeTooltip => GenerateTooltip(ref importScopeTooltip, module, implMap.ImportScope); public string ImportName => metadata.GetString(implMap.ImportName); @@ -165,6 +153,8 @@ namespace ICSharpCode.ILSpy.Metadata int memberForwardedTagRefSize = metadata.ComputeCodedTokenSize(32768, TableMask.MethodDef | TableMask.Field); int stringHandleSize = metadata.GetHeapSize(HeapIndex.String) < ushort.MaxValue ? 2 : 4; this.implMap = new ImplMap(ptr + rowOffset, moduleRefSize, memberForwardedTagRefSize, stringHandleSize); + this.importScopeTooltip = null; + this.memberForwardedTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs index 8764c1c5a..35c66ab85 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, interfaceImpl.Class, protocol: "metadata")); } - public string ClassTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)interfaceImpl.Class).WriteTo(module, output, context); - return output.ToString(); - } - } + string classTooltip; + public string ClassTooltip => GenerateTooltip(ref classTooltip, module, interfaceImpl.Class); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Interface => MetadataTokens.GetToken(interfaceImpl.Interface); @@ -123,14 +117,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, interfaceImpl.Interface, protocol: "metadata")); } - public string InterfaceTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)interfaceImpl.Interface).WriteTo(module, output, context); - return output.ToString(); - } - } + string interfaceTooltip; + public string InterfaceTooltip => GenerateTooltip(ref interfaceTooltip, module, interfaceImpl.Interface); public InterfaceImplEntry(PEFile module, byte* ptr, int metadataOffset, int row) { @@ -141,6 +129,8 @@ namespace ICSharpCode.ILSpy.Metadata + metadata.GetTableRowSize(TableIndex.InterfaceImpl) * (row - 1); this.Offset = metadataOffset + rowOffset; this.interfaceImpl = new InterfaceImpl(ptr + rowOffset, metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4, metadata.ComputeCodedTokenSize(16384, TableMask.TypeDef | TableMask.TypeRef | TableMask.TypeSpec)); + this.interfaceTooltip = null; + this.classTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs index 0d9619cd9..79fd5b76b 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -105,16 +105,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, manifestResource.Implementation, protocol: "metadata")); } - public string ImplementationTooltip { - get { - if (manifestResource.Implementation.IsNil) - return null; - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - manifestResource.Implementation.WriteTo(module, output, context); - return output.ToString(); - } - } + string implementationTooltip; + public string ImplementationTooltip => GenerateTooltip(ref implementationTooltip, module, manifestResource.Implementation); public ManifestResourceEntry(PEFile module, ManifestResourceHandle handle) { @@ -123,6 +115,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.manifestResource = metadata.GetManifestResource(handle); + this.implementationTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs index e4701270f..01e7806dc 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -95,13 +95,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, memberRef.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - memberRef.Parent.WriteTo(module, output, default); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, memberRef.Parent); public string Name => metadata.GetString(memberRef.Name); @@ -110,14 +105,8 @@ namespace ICSharpCode.ILSpy.Metadata [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(memberRef.Signature); - public string SignatureTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)handle).WriteTo(module, output, context); - return output.ToString(); - } - } + string signatureTooltip; + public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, module, handle); public MemberRefEntry(PEFile module, MemberReferenceHandle handle) { @@ -126,6 +115,8 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.memberRef = metadata.GetMemberReference(handle); + this.signatureTooltip = null; + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs index 8b761b27d..24b93afd0 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -94,13 +94,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, methodImpl.MethodDeclaration, protocol: "metadata")); } - public string MethodDeclarationTooltip { - get { - ITextOutput output = new PlainTextOutput(); - methodImpl.MethodDeclaration.WriteTo(module, output, default); - return output.ToString(); - } - } + string methodDeclarationTooltip; + public string MethodDeclarationTooltip => GenerateTooltip(ref methodDeclarationTooltip, module, methodImpl.MethodDeclaration); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody); @@ -110,13 +105,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, methodImpl.MethodBody, protocol: "metadata")); } - public string MethodBodyTooltip { - get { - ITextOutput output = new PlainTextOutput(); - methodImpl.MethodBody.WriteTo(module, output, default); - return output.ToString(); - } - } + string methodBodyTooltip; + public string MethodBodyTooltip => GenerateTooltip(ref methodBodyTooltip, module, methodImpl.MethodBody); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(methodImpl.Type); @@ -126,13 +116,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, methodImpl.Type, protocol: "metadata")); } - public string TypeTooltip { - get { - ITextOutput output = new PlainTextOutput(); - ((EntityHandle)methodImpl.Type).WriteTo(module, output, default); - return output.ToString(); - } - } + string typeTooltip; + public string TypeTooltip => GenerateTooltip(ref typeTooltip, module, methodImpl.Type); public MethodImplEntry(PEFile module, MethodImplementationHandle handle) { @@ -141,6 +126,9 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.methodImpl = metadata.GetMethodImplementation(handle); + this.typeTooltip = null; + this.methodBodyTooltip = null; + this.methodDeclarationTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs index 6956e0cc6..3f3ab38df 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -103,13 +103,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, method, protocol: "metadata")); } - public string MethodTooltip { - get { - ITextOutput output = new PlainTextOutput(); - ((EntityHandle)method).WriteTo(module, output, default); - return output.ToString(); - } - } + string methodTooltip; + public string MethodTooltip => GenerateTooltip(ref methodTooltip, module, method); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Association => MetadataTokens.GetToken(association); @@ -119,13 +114,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, association, protocol: "metadata")); } - public string AssociationTooltip { - get { - ITextOutput output = new PlainTextOutput(); - association.WriteTo(module, output, default); - return output.ToString(); - } - } + string associationTooltip; + public string AssociationTooltip => GenerateTooltip(ref associationTooltip, module, association); public MethodSemanticsEntry(PEFile module, Handle handle, MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) { @@ -136,6 +126,8 @@ namespace ICSharpCode.ILSpy.Metadata this.semantics = semantics; this.method = method; this.association = association; + this.methodTooltip = null; + this.associationTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs index 780b502ef..c050dbc58 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -95,13 +95,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, methodSpec.Method, protocol: "metadata")); } - public string MethodTooltip { - get { - ITextOutput output = new PlainTextOutput(); - methodSpec.Method.WriteTo(module, output, default); - return output.ToString(); - } - } + string methodTooltip; + public string MethodTooltip => GenerateTooltip(ref methodTooltip, module, methodSpec.Method); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(methodSpec.Signature); @@ -130,6 +125,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.methodSpec = metadata.GetMethodSpecification(handle); + this.methodTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs index 639e6f025..46f81cde4 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -122,18 +122,7 @@ namespace ICSharpCode.ILSpy.Metadata string signatureTooltip; - public string SignatureTooltip { - get { - if (signatureTooltip == null) - { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)handle).WriteTo(module, output, context); - signatureTooltip = output.ToString(); - } - return signatureTooltip; - } - } + public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, module, handle); IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemWithCurrentOptionsOrNull()?.MainModule).GetDefinition(handle); diff --git a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs index dda3ad8e1..de347fd33 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, nestedClass.Nested, protocol: "metadata")); } - public string NestedClassTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)nestedClass.Nested).WriteTo(module, output, context); - return output.ToString(); - } - } + string nestedClassTooltip; + public string NestedClassTooltip => GenerateTooltip(ref nestedClassTooltip, module, nestedClass.Nested); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EnclosingClass => MetadataTokens.GetToken(nestedClass.Enclosing); @@ -123,14 +117,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, nestedClass.Enclosing, protocol: "metadata")); } - public string EnclosingClassTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)nestedClass.Enclosing).WriteTo(module, output, context); - return output.ToString(); - } - } + string enclosingClassTooltip; + public string EnclosingClassTooltip => GenerateTooltip(ref enclosingClassTooltip, module, nestedClass.Enclosing); public unsafe NestedClassEntry(PEFile module, byte* ptr, int metadataOffset, int row) { @@ -142,6 +130,8 @@ namespace ICSharpCode.ILSpy.Metadata this.Offset = metadataOffset + rowOffset; int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; this.nestedClass = new NestedClass(ptr + rowOffset, typeDefSize); + this.nestedClassTooltip = null; + this.enclosingClassTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs index b34023f2a..64d9bb19a 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -106,14 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, propertyMap.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)propertyMap.Parent).WriteTo(module, output, context); - return output.ToString(); - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, propertyMap.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int PropertyList => MetadataTokens.GetToken(propertyMap.PropertyList); @@ -123,14 +117,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, propertyMap.PropertyList, protocol: "metadata")); } - public string PropertyListTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)propertyMap.PropertyList).WriteTo(module, output, context); - return output.ToString(); - } - } + string propertyListTooltip; + public string PropertyListTooltip => GenerateTooltip(ref propertyListTooltip, module, propertyMap.PropertyList); public PropertyMapEntry(PEFile module, byte* ptr, int metadataOffset, int row) { @@ -143,6 +131,8 @@ namespace ICSharpCode.ILSpy.Metadata int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; int propertyDefSize = metadata.GetTableRowCount(TableIndex.Property) < ushort.MaxValue ? 2 : 4; this.propertyMap = new PropertyMap(ptr + rowOffset, typeDefSize, propertyDefSize); + this.propertyListTooltip = null; + this.parentTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs index 0b428934e..8240c2ac5 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -106,13 +106,8 @@ namespace ICSharpCode.ILSpy.Metadata [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature); - public string SignatureTooltip { - get { - ITextOutput output = new PlainTextOutput(); - ((EntityHandle)handle).WriteTo(module, output, default); - return output.ToString(); - } - } + string signatureTooltip; + public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, module, handle); public PropertyDefEntry(PEFile module, PropertyDefinitionHandle handle) { @@ -121,6 +116,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.propertyDef = metadata.GetPropertyDefinition(handle); + this.signatureTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs index 89e79afbc..0e618aad2 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -89,14 +89,8 @@ namespace ICSharpCode.ILSpy.Metadata [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature); - public string SignatureTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)handle).WriteTo(module, output, context); - return output.ToString(); - } - } + string signatureTooltip; + public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, module, handle); public StandAloneSigEntry(PEFile module, StandaloneSignatureHandle handle) { @@ -105,6 +99,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.standaloneSig = metadata.GetStandaloneSignature(handle); + this.signatureTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index 47b38af0a..3388a244a 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -152,15 +152,13 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, typeDef.GetFields().FirstOrDefault(), protocol: "metadata")); } + string fieldListTooltip; public string FieldListTooltip { get { var field = typeDef.GetFields().FirstOrDefault(); if (field.IsNil) return null; - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)field).WriteTo(module, output, context); - return output.ToString(); + return GenerateTooltip(ref fieldListTooltip, module, field); } } @@ -172,15 +170,13 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, typeDef.GetMethods().FirstOrDefault(), protocol: "metadata")); } + string methodListTooltip; public string MethodListTooltip { get { var method = typeDef.GetMethods().FirstOrDefault(); if (method.IsNil) return null; - ITextOutput output = new PlainTextOutput(); - var context = new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)method).WriteTo(module, output, context); - return output.ToString(); + return GenerateTooltip(ref methodListTooltip, module, method); } } @@ -193,6 +189,8 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.typeDef = metadata.GetTypeDefinition(handle); + this.methodListTooltip = null; + this.fieldListTooltip = null; } } diff --git a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs index 6e34da4da..f0a773dc3 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -94,31 +94,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, typeRef.ResolutionScope, protocol: "metadata")); } - public string ResolutionScopeTooltip { - get { - if (typeRef.ResolutionScope.IsNil) - return null; - var output = new PlainTextOutput(); - switch (typeRef.ResolutionScope.Kind) - { - case HandleKind.ModuleDefinition: - output.Write(metadata.GetString(metadata.GetModuleDefinition().Name)); - break; - case HandleKind.ModuleReference: - ModuleReference moduleReference = metadata.GetModuleReference((ModuleReferenceHandle)typeRef.ResolutionScope); - output.Write(metadata.GetString(moduleReference.Name)); - break; - case HandleKind.AssemblyReference: - var asmRef = new Decompiler.Metadata.AssemblyReference(module, (AssemblyReferenceHandle)typeRef.ResolutionScope); - output.Write(asmRef.ToString()); - break; - default: - typeRef.ResolutionScope.WriteTo(module, output, default); - break; - } - return output.ToString(); - } - } + string resolutionScopeTooltip; + public string ResolutionScopeTooltip => GenerateTooltip(ref resolutionScopeTooltip, module, typeRef.ResolutionScope); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(typeRef.Name):X} \"{Name}\""; @@ -135,6 +112,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = module.Metadata; this.handle = handle; this.typeRef = metadata.GetTypeReference(handle); + this.resolutionScopeTooltip = null; } } diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index 329cc86b6..9e9d46c61 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -205,14 +205,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, debugInfo.Parent, protocol: "metadata")); } - public string ParentTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - debugInfo.Parent.WriteTo(module, output, context); - return $"{debugInfo.Parent.Kind}:\n{output}"; - } - } + string parentTooltip; + public string ParentTooltip => GenerateTooltip(ref parentTooltip, module, debugInfo.Parent); string kindString; public string Kind { diff --git a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs index 6854b64e0..8b8ac8b1a 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -98,13 +98,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, localScope.Method, protocol: "metadata")); } - public string MethodTooltip { - get { - ITextOutput output = new PlainTextOutput(); - ((EntityHandle)localScope.Method).WriteTo(module, output, default); - return output.ToString(); - } - } + string methodTooltip; + public string MethodTooltip => GenerateTooltip(ref methodTooltip, module, localScope.Method); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(localScope.ImportScope); @@ -142,6 +137,7 @@ namespace ICSharpCode.ILSpy.Metadata this.metadata = metadata; this.handle = handle; this.localScope = metadata.GetLocalScope(handle); + this.methodTooltip = null; } } diff --git a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs index 4973f38d3..e93c0c3d9 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -100,14 +100,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, moveNextMethod, protocol: "metadata")); } - public string MoveNextMethodTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)moveNextMethod).WriteTo(module, output, context); - return output.ToString(); - } - } + string moveNextMethodTooltip; + public string MoveNextMethodTooltip => GenerateTooltip(ref moveNextMethodTooltip, module, moveNextMethod); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int KickoffMethod => MetadataTokens.GetToken(kickoffMethod); @@ -117,14 +111,8 @@ namespace ICSharpCode.ILSpy.Metadata MainWindow.Instance.JumpToReference(new EntityReference(module, kickoffMethod, protocol: "metadata")); } - public string KickoffMethodTooltip { - get { - ITextOutput output = new PlainTextOutput(); - var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); - ((EntityHandle)kickoffMethod).WriteTo(module, output, context); - return output.ToString(); - } - } + string kickoffMethodTooltip; + public string KickoffMethodTooltip => GenerateTooltip(ref kickoffMethodTooltip, module, kickoffMethod); public StateMachineMethodEntry(PEFile module, ref BlobReader reader, bool isEmbedded, int row) { @@ -138,6 +126,8 @@ namespace ICSharpCode.ILSpy.Metadata int methodDefSize = metadata.GetTableRowCount(TableIndex.MethodDef) < ushort.MaxValue ? 2 : 4; this.moveNextMethod = MetadataTokens.MethodDefinitionHandle(methodDefSize == 2 ? reader.ReadInt16() : reader.ReadInt32()); this.kickoffMethod = MetadataTokens.MethodDefinitionHandle(methodDefSize == 2 ? reader.ReadInt16() : reader.ReadInt32()); + this.kickoffMethodTooltip = null; + this.moveNextMethodTooltip = null; } } diff --git a/ILSpy/Metadata/MetadataTableTreeNode.cs b/ILSpy/Metadata/MetadataTableTreeNode.cs index dbd8a424c..204270dc3 100644 --- a/ILSpy/Metadata/MetadataTableTreeNode.cs +++ b/ILSpy/Metadata/MetadataTableTreeNode.cs @@ -22,11 +22,10 @@ using System.Reflection.Metadata.Ecma335; using System.Windows.Controls; using System.Windows.Threading; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.ILSpy.TextView; +using ICSharpCode.Decompiler.IL; using ICSharpCode.ILSpy.TreeNodes; -using ICSharpCode.ILSpy.ViewModels; -using ICSharpCode.TreeView; namespace ICSharpCode.ILSpy.Metadata { @@ -62,6 +61,73 @@ namespace ICSharpCode.ILSpy.Metadata view.Loaded -= View_Loaded; this.scrollTarget = default; } + + protected static string GenerateTooltip(ref string tooltip, PEFile module, EntityHandle handle) + { + if (tooltip == null) + { + if (handle.IsNil) + { + return null; + } + ITextOutput output = new PlainTextOutput(); + var context = new MetadataGenericContext(default(TypeDefinitionHandle), module); + var metadata = module.Metadata; + switch (handle.Kind) + { + case HandleKind.ModuleDefinition: + output.Write(metadata.GetString(metadata.GetModuleDefinition().Name)); + output.Write(" (this module)"); + break; + case HandleKind.ModuleReference: + ModuleReference moduleReference = metadata.GetModuleReference((ModuleReferenceHandle)handle); + output.Write(metadata.GetString(moduleReference.Name)); + break; + case HandleKind.AssemblyReference: + var asmRef = new Decompiler.Metadata.AssemblyReference(module, (AssemblyReferenceHandle)handle); + output.Write(asmRef.ToString()); + break; + case HandleKind.Parameter: + var param = metadata.GetParameter((ParameterHandle)handle); + output.Write(param.SequenceNumber + " - " + metadata.GetString(param.Name)); + break; + case HandleKind.EventDefinition: + var @event = metadata.GetEventDefinition((EventDefinitionHandle)handle); + output.Write(metadata.GetString(@event.Name)); + break; + case HandleKind.PropertyDefinition: + var prop = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); + output.Write(metadata.GetString(prop.Name)); + break; + case HandleKind.AssemblyDefinition: + var ad = metadata.GetAssemblyDefinition(); + output.Write(metadata.GetString(ad.Name)); + output.Write(" (this assembly)"); + break; + case HandleKind.AssemblyFile: + var af = metadata.GetAssemblyFile((AssemblyFileHandle)handle); + output.Write(metadata.GetString(af.Name)); + break; + case HandleKind.GenericParameter: + var gp = metadata.GetGenericParameter((GenericParameterHandle)handle); + output.Write(metadata.GetString(gp.Name)); + break; + case HandleKind.ManifestResource: + var mfr = metadata.GetManifestResource((ManifestResourceHandle)handle); + output.Write(metadata.GetString(mfr.Name)); + break; + case HandleKind.Document: + var doc = metadata.GetDocument((DocumentHandle)handle); + output.Write(metadata.GetString(doc.Name)); + break; + default: + handle.WriteTo(module, output, context); + break; + } + tooltip = "(" + handle.Kind + ") " + output.ToString(); + } + return tooltip; + } } internal abstract class DebugMetadataTableTreeNode : MetadataTableTreeNode