From fc1e9eb5bcfd9e36e2a3d3270883a00480c36eb2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 1 Oct 2022 22:26:03 +0200 Subject: [PATCH 001/231] 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 8e7c3fe574..d7c25fa858 100644 --- a/ILSpy/Metadata/CoffHeaderTreeNode.cs +++ b/ILSpy/Metadata/CoffHeaderTreeNode.cs @@ -67,10 +67,12 @@ public override bool View(TabPageModel tabPage) 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/231] 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 0efd42831c..b3b5cc7a6f 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 0000000000..bdcaab0266 --- /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 732840c629..635b031dac 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 26e4626c88..6438a13153 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 @@ public interface IMethod : IParameterizedMember /// /// 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 f8b6203d12..cad688dbe3 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 abc4252ea6..c3760e42ec 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -67,7 +67,8 @@ internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle) 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 @@ public string Name { 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 505a063595..8f097f6832 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 17ce52520c..fc934ebeaa 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 da76087c72..d5636fe244 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/231] 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 75d3006da8..635c962f94 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 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con 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 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con 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 @@ void IStatementTransform.Run(Block block, int pos, StatementTransformContext con 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 @@ bool IsMethodCallOnVariable(ILInstruction inst, ILVariable variable) 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 @@ bool IsPartOfInitializer(InstructionCollection instructions, int 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 @@ bool IsValidObjectInitializerTarget(List path) 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 @@ public enum AccessPathKind 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 @@ public AccessPathElement(OpCode opCode, IMember member, ILInstruction[] indices 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 @@ public static (AccessPathKind Kind, List Path, List 0 && settings?.DictionaryInitializers == false) @@ -359,7 +372,7 @@ public static (AccessPathKind Kind, List Path, List Path, List(new[] { stobj.Value }); @@ -381,35 +394,35 @@ public static (AccessPathKind Kind, List Path, List 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 @@ private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContex 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 @@ out bool success } } - static IType GetReturnTypeFromInstruction(ILInstruction instruction) + static IType? GetReturnTypeFromInstruction(ILInstruction instruction) { switch (instruction) { @@ -474,7 +487,7 @@ static IType GetReturnTypeFromInstruction(ILInstruction instruction) } } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is AccessPathElement) return Equals((AccessPathElement)obj); @@ -494,8 +507,10 @@ public override int GetHashCode() 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 @@ class ILInstructionMatchComparer : IEqualityComparer { 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/231] 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 c35da7dab2..c216fdaae8 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 4cc0d34d7d..9d33c4b969 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/231] 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 635c962f94..1f8845d930 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -324,7 +324,7 @@ public static (AccessPathKind Kind, List Path, List Date: Mon, 3 Oct 2022 16:09:18 +0200 Subject: [PATCH 006/231] 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 4046bebd16..6fe5791806 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -601,6 +601,50 @@ public Func TestLambda(Issue1867 x) } } + 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 4eda0ba102..b146e85a45 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -281,7 +281,7 @@ private static bool IsValidInStatementExpression(Expression expr) 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 @@ void FindInsertionPointForVariable(ILVariable variable) { 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 @@ void FindInsertionPointForVariable(ILVariable variable) } } + 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 bbe6dfaa65..5f2610449c 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -209,7 +209,7 @@ internal void CheckInvariant() /// /// 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/231] 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 1f58efc4c3..560683e55c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -323,7 +323,7 @@ public IEnumerable DirectBaseTypes { 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/231] 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 c35da7dab2..c216fdaae8 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 4cc0d34d7d..9d33c4b969 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/231] 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 5a55e10c29..3b880ca572 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -69,6 +69,7 @@ public enum CompilerOptions 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 @@ public static async Task CompileCSharp(string sourceFileName, C { 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 b2542be8d1..549273f566 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -376,7 +376,7 @@ public async Task CheckedUnchecked([ValueSource(nameof(defaultOptions))] Compile [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 ce890fcd09..2bb122eb3e 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 7115696d1b..c0e109de82 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -286,7 +286,7 @@ bool IsUnmanagedTypeInternal(IType type) 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/231] 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 37d64c6e5b..f6ddbbd3f5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs @@ -15,9 +15,8 @@ public void MethodUnknownClass() //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 @@ public void MethodUnknownGenericClass() //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 @@ public void MethodUnknownGenericClass() }; 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 @@ public void MethodUnknownStatic() 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 @@ private static void Instance_OnEvent(object sender, object e) throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index 818e1ecd95..cf6ae1be57 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -670,7 +670,7 @@ private void GuessFakeMethodAccessor(IType declaringType, string name, MethodSig 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/231] #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 c14b830104..3371ce180d 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -953,7 +953,7 @@ public SyntaxTree DecompileType(FullTypeName fullTypeName) 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/231] 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 560683e55c..8417e33b0c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -58,6 +58,7 @@ sealed class MetadataTypeDefinition : ITypeDefinition IEvent[] events; IMethod[] methods; List directBaseTypes; + bool defaultMemberNameInitialized; string defaultMemberName; internal MetadataTypeDefinition(MetadataModule module, TypeDefinitionHandle handle) @@ -459,7 +460,7 @@ public IAttribute GetAttribute(KnownAttribute attribute) 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 @@ public string DefaultMemberName { 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/231] 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 82b4666c2f..bb32e2ac6f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -315,9 +315,10 @@ void HandleStaticFieldInitializers(IEnumerable members) 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 @@ void HandleStaticFieldInitializers(IEnumerable members) // 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/231] #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 47fb5e36e3..f3e191de6d 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -188,7 +188,9 @@ static CustomDebugInformationKind GetKind(MetadataReader metadata, GuidHandle h) 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 ab1660d3e0..c3cef36da6 100644 --- a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs @@ -82,6 +82,8 @@ struct DocumentEntry 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 a73e65bdc7..e7841965df 100644 --- a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs @@ -86,6 +86,8 @@ struct ImportScopeEntry 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 91ca9c0b44..2a095fee4c 100644 --- a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs @@ -84,6 +84,8 @@ struct LocalConstantEntry 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 e934deadfd..546e5c3955 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -86,6 +86,8 @@ struct LocalScopeEntry 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 458bd5abe0..ed8744e2ef 100644 --- a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs @@ -80,6 +80,8 @@ struct LocalVariableEntry 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 8ed8b05fb6..46dc938855 100644 --- a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs @@ -84,6 +84,8 @@ struct MethodDebugInformationEntry 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 8407f73284..5e30b145fc 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -54,7 +54,7 @@ public unsafe override bool View(ViewModels.TabPageModel tabPage) 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 @@ struct StateMachineMethodEntry 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/231] 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 f6ddbbd3f5..c9e0f56e07 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs @@ -97,6 +97,15 @@ public void MethodUnknownStaticGeneric() 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 7fd6756ecc..35fe005d14 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 5213febe30..ddbc92759c 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -539,6 +539,8 @@ public ExpressionWithResolveResult BuildCollectionInitializerExpression(OpCode c 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/231] 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 e964073951..e11a9e10d1 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3340,6 +3340,7 @@ private ArrayInitializerExpression BuildArrayInitializerExpression(Block block, { 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 43549b6c6d..2c4a2f67e4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -199,6 +199,16 @@ public override IMember Specialize(TypeParameterSubstitution substitution) 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/231] 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 d342695c05..90c8558727 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/231] 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 e6a91feee0..bf10a7d94c 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/231] 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 e6a91feee0..bf10a7d94c 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/231] 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 d342695c05..2e50ffcf1f 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 174711c8b5..c2a58c3d82 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.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 @@ public void CustomPdbId() } } + [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 e0708da65a..e5d691c3da 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 cc8e342bde..3be497b77b 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -28,6 +28,7 @@ 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 @@ 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 @@ public static bool HasCodeViewDebugDirectoryEntry(PEFile file) 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 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) 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/231] 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 4db4ad4554..9b87d99bee 100644 --- a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs +++ b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs @@ -33,8 +33,8 @@ public void Report(DecompilationProgress value) 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 c2a58c3d82..991c59caef 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -81,17 +81,17 @@ public void ProgressReporting() 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 @@ public void ProgressReporting() 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 3057cbb6f7..c49fde67cd 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -223,8 +223,8 @@ CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) 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 @@ CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) 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 @@ public static bool CanUseSdkStyleProjectFormat(PEFile module) 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 3be497b77b..b0b419aab1 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -41,12 +41,6 @@ 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 @@ public static void WritePdb( 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 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) } 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 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) 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 0000000000..3d96349989 --- /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 b3b5cc7a6f..25d9b0bf98 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 3af1c3b643..84bafc4239 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -75,12 +75,14 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly) 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 d241da8f14..7d6feb5c04 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 @@ public class DecompilationOptions /// 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 a1d9428829..492ecf2207 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -404,6 +404,7 @@ public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput 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 cf64691d78..3ffe81d2d3 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -105,7 +105,11 @@ public SearchPane SearchPane { 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/231] 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 02c00bfda2..539c0b4c4f 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 @@ void HighlightBrackets(object? sender, EventArgs e) #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 @@ public Task RunWithCancellation(Func> taskCreat 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 @@ public Task RunWithCancellation(Func> taskCreat 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 @@ Task DoDecompile(DecompilationContext context, int outputLengthLimit) 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 @@ Task SaveToDiskAsync(DecompilationContext context, string { 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 66960f102d..6ff6d2130d 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 aa8eb92b2b..eaa65475ba 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -184,7 +184,7 @@ private void SessionSettings_PropertyChanged(object sender, PropertyChangedEvent 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 60114829a8..f9002b35d3 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 @@ public static string CultureLabel { } } - /// - /// 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 @@ public static string TabSize { } } + /// + /// 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 9f873ec564..f9e949fa8d 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 bf8f4d400e..3715d10047 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 a39262ee24..245dd99d1c 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -64,7 +64,7 @@ public SessionSettings(ILSpySettings spySettings) 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 @@ void OnPropertyChanged([CallerMemberName] string propertyName = null) 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 @@ public void Save() 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 21991df252..0697e92a8a 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -1364,7 +1364,7 @@ public static void RegisterHighlighting( 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 044f7f1b5b..21308eb831 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 6150f7d6db..15347570e2 100644 --- a/ILSpy/Themes/ResourceKeys.cs +++ b/ILSpy/Themes/ResourceKeys.cs @@ -29,5 +29,7 @@ public static class ResourceKeys 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 08e1178151..cc7368754f 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 @@ private ThemeManager() 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 109/231] 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 eb2cf3f624..74f8458936 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 9953acbf07..0000000000 --- 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 7d7c7aa732..0000000000 --- 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 0697e92a8a..6bdee75ba4 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -1364,15 +1364,8 @@ public static void RegisterHighlighting( 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 @@ public static void RegisterHighlighting( 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 563d1bc55e..0000000000 --- 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 cea0680a7a..0000000000 --- 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 21308eb831..ee4af9991f 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 0000000000..2a7ddd3d07 --- /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 cc7368754f..52d9673bff 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 @@ private ThemeManager() 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 @@ public Style CreateToolBarButtonStyle() { 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 110/231] 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 2a7ddd3d07..1f44eb56c8 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 52d9673bff..a90a143dcf 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -42,10 +42,15 @@ private ThemeManager() } 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 @@ public void UpdateColors(IHighlightingDefinition highlightingDefinition) 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 0000000000..7ee71f111e --- /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 111/231] 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} (99%) 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 99% rename from ILSpy/Themes/VSLightTheme.xaml rename to ILSpy/Themes/Theme.VSCodeLightPlus.xaml index 7ee71f111e..1e40508624 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 1055b6c403..8cfbaa5ced 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -45,9 +45,10 @@ private ThemeManager() 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 @@ private void UpdateTheme(string? themeName) 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 113/231] 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 0000000000..ab69cef7b8 --- /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 8cfbaa5ced..a115fae9ff 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -48,7 +48,8 @@ private ThemeManager() "Light", "Dark", "VS Code Light+", - "VS Code Dark+" + "VS Code Dark+", + "R# Light" }; public string? Theme { @@ -93,6 +94,7 @@ private void UpdateTheme(string? themeName) 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 114/231] 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 0000000000..5699e0f527 --- /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 a115fae9ff..d5f9daef51 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -49,7 +49,8 @@ private ThemeManager() "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 115/231] 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 9bf688b83f..8d4fb8c2f8 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 f24c9f6c8e..86b4c3f37f 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 ee4af9991f..0abb57e31d 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 5699e0f527..a420235c78 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 ab69cef7b8..f749e6f9af 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 825626aff2..e4cd880ac2 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 1e40508624..e527cde583 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 116/231] 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 0562bcedd7..d225feeaa3 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -457,7 +457,7 @@ public static bool IsDefinition(ref AstNode node) { 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 e4ce9eba69..e2e66a0903 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -55,9 +55,12 @@ class CSharpHighlightingTokenWriter : DecoratingTokenWriter HighlightingColor methodCallColor; HighlightingColor methodDeclarationColor; - HighlightingColor fieldDeclarationColor; HighlightingColor fieldAccessColor; + HighlightingColor propertyDeclarationColor; + HighlightingColor propertyAccessColor; + HighlightingColor eventDeclarationColor; + HighlightingColor eventAccessColor; HighlightingColor valueKeywordColor; HighlightingColor thisKeywordColor; @@ -74,39 +77,49 @@ public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutp 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 @@ public override void WriteIdentifier(Identifier identifier) 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 @@ public override void WriteIdentifier(Identifier identifier) 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 5f5e0a2fde..8c9041d47f 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 a420235c78..492fabc7aa 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 f749e6f9af..6b42b133d9 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 e4cd880ac2..cf7f12f33e 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 e527cde583..b615d43326 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 117/231] 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 6bdee75ba4..c977984773 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -127,6 +127,7 @@ public DecompilerTextView() // 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 36f87da8ce..4776d1397c 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 1478cde4bd..d5e54485a0 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 492fabc7aa..987844d016 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 6b42b133d9..eb6d109914 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 cf7f12f33e..7be7eb9026 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 b615d43326..08d5e4802b 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 118/231] 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 839fdbb331..2e04d0575a 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -83,6 +83,10 @@ public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOp 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 119/231] 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 13d9c067ba..bdf2e36cae 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1288,6 +1288,11 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun { 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 120/231] 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 e2e66a0903..2e698460cf 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -77,49 +77,43 @@ public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutp 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 8c9041d47f..8bed021095 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 c977984773..b7e5f32115 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -140,10 +140,6 @@ public DecompilerTextView() 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 @@ public static void RegisterHighlighting( 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 0000000000..4005dec0a2 --- /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 0000000000..8e7e5076bd --- /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 1f44eb56c8..cf9a29b00b 100644 --- a/ILSpy/Themes/SyntaxColor.cs +++ b/ILSpy/Themes/SyntaxColor.cs @@ -21,4 +21,12 @@ public void ApplyTo(HighlightingColor color) 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 f5c96f753d..ede1f10f8b 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 d5e54485a0..c55417bb45 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 987844d016..f783da16d2 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 eb6d109914..02021fb600 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 7be7eb9026..e7ad850998 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 125/231] 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 aab6d6f0b3..50ace93798 100644 --- a/ILSpy/Commands/ILSpyCommands.cs +++ b/ILSpy/Commands/ILSpyCommands.cs @@ -24,6 +24,7 @@ using System.Windows.Input; using ICSharpCode.ILSpy.Analyzers; +using ICSharpCode.ILSpy.Commands; namespace ICSharpCode.ILSpy { @@ -31,5 +32,6 @@ static class ILSpyCommands { 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 0000000000..9083300ab4 --- /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 9335d38a1d..585720cfc7 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 126/231] 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 f9206b8292..52f5c3ab13 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 5e6201ff1b..908ed35017 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -121,6 +121,8 @@ public static DisplaySettingsViewModel LoadDisplaySettings(ILSpySettings setting 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 @@ public void Save(XElement root) 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 084d295b12..15b54ebe5b 100644 --- a/ILSpy/Options/DisplaySettingsViewModel.cs +++ b/ILSpy/Options/DisplaySettingsViewModel.cs @@ -20,6 +20,8 @@ using System.Runtime.CompilerServices; using System.Windows.Media; +using ICSharpCode.ILSpy.Themes; + namespace ICSharpCode.ILSpy.Options { /// @@ -29,6 +31,7 @@ public class DisplaySettingsViewModel : INotifyPropertyChanged { 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 @@ protected void OnPropertyChanged([CallerMemberName] string propertyName = null) } #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 @@ public bool ShowRawOffsetsAndBytesBeforeInstruction { 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 f9002b35d3..94c50345bc 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1558,6 +1558,15 @@ public static string DisplaySettingsPanel_Font { } } + /// + /// 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 f9e949fa8d..82d434c12e 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 245dd99d1c..ad53d408cd 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -127,7 +127,7 @@ public string ActiveAssemblyList { 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 @@ public void Save() 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 127/231] Remove theme combo box --- ILSpy/MainWindow.xaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 585720cfc7..0d236371fb 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 128/231] 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 23c06798ed..27a0ad480e 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 f5f01af1f7..384cebd831 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 55a813f4fe..e222ba25a0 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 74f8458936..d33163bad0 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -41,19 +41,11 @@ - - - - + - - - - - diff --git a/packages.props b/packages.props index f5cbe1b809..13a55bbc6c 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 129/231] 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 865fc3609a..eccbd00e53 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 ca08971e2c..8fd7113829 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 62bfddf038..778a5e3857 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 8eb28d77c9..7a0adab3e8 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 7a94d4fab9..124dd78e31 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 2ab0d1000f..5d3a4ccc02 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 145/231] 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 ef4da7afea..42786e3968 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 243fb45855..b576c42202 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 7eae534dd2..8dd5d9abe0 100644 --- a/ILSpy/Themes/ResourceKeys.cs +++ b/ILSpy/Themes/ResourceKeys.cs @@ -30,6 +30,7 @@ public static class ResourceKeys 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 f2f0ee2f46..41441900e0 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 8fd7113829..b1818ea23f 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 778a5e3857..bcd9672adc 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 7a0adab3e8..312919f2a1 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 124dd78e31..ef7aa20b1c 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 5d3a4ccc02..4026ac36c4 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 146/231] 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 0247152209..aa388d9908 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 b576c42202..c2138b5a45 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 41441900e0..e087d39f86 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 b1818ea23f..f085856d8d 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 bcd9672adc..ca47f40877 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 312919f2a1..a41da7d324 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 ef7aa20b1c..402b4327a6 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 4026ac36c4..480892348d 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 147/231] 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 9a61bce318..4b958d407a 100644 --- a/ILSpy/TextView/BracketHighlightRenderer.cs +++ b/ILSpy/TextView/BracketHighlightRenderer.cs @@ -114,6 +114,8 @@ public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingCont 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 148/231] 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 59d0b5706a..d6cb0af14d 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 @@ bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result) 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 149/231] 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 f3873bc387..62679de693 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 @@ public static int Main() 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 d6cb0af14d..296145397b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -433,10 +433,10 @@ bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result) { 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 150/231] 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 62679de693..c843b422a0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs @@ -43,19 +43,23 @@ public static int Main() 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 151/231] 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 fcdbdfa0ee..fb571fce27 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -375,7 +375,7 @@ TranslatedExpression IsType(IsInst inst) { 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 152/231] #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 fe03b32c2c..97c3a57f54 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -103,21 +103,20 @@ private void UpdateTheme(string? themeName) .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 153/231] 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 13a55bbc6c..bc4a05f6f8 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 154/231] 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 ea43eabd4b..d89d776485 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -849,7 +849,7 @@ private static void ReadCodeMappingInfo(PEFile module, CodeMappingInfo info, Met 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 155/231] 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 9de8f29980..8b8575619e 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -723,16 +723,22 @@ public static IEnumerable EnumerateGac() 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 156/231] 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 55339b316b..f08fc8e117 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -161,5 +161,10 @@ public static void SelectItem(this DataGrid view, object item) 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 9f9fdb0f00..be6ec0c51d 100644 --- a/ILSpy/NativeMethods.cs +++ b/ILSpy/NativeMethods.cs @@ -193,6 +193,16 @@ public unsafe static string GetProcessNameFromWindow(IntPtr hWnd) 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 @@ public CopyDataStruct(IntPtr padding, int size, IntPtr buffer) 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 e9f20ebd2f..55d0c2c9b4 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 @@ public class WindowStyleManagerBehavior : FrameworkElementBehavior { 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 @@ private void DisplaySettings_PropertyChanged(object sender, PropertyChangedEvent 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 157/231] 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 c983648148..e719f15b1d 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 @@ public ResourceObjectTable(IEnumerable resources, FrameworkElement container) 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 @@ private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) 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 3262de0b6a..b2ce2127cd 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 @@ public ResourceStringTable(IEnumerable strings, FrameworkElement container) 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 @@ private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) 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 158/231] 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 c813d34655..c0b9de9850 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 @@ void InlineVariableInReturnBlock(Block block, ILTransformContext context) 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 @@ void SimplifyBranchChains(ILFunction function, ILTransformContext context) 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 @@ void SimplifyBranchChains(ILFunction function, ILTransformContext context) // 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 @@ void SimplifyBranchChains(ILFunction function, ILTransformContext context) 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 159/231] 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 ece018663c..2969140304 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 @@ public void SwitchInTryInLoopContinue() } } } + + 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 160/231] 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 2969140304..006cac8614 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs @@ -568,7 +568,7 @@ public void SwitchInTryInLoopContinue() } } - 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 161/231] 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 3715d10047..041005759c 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 162/231] 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 923f020d01..be16796fde 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 8e73fc14ae..8c469191fd 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 0000000000..575226e49b --- /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 163/231] 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 2bac472f43..acb6e671d0 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 164/231] 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 804c7c8e60..853436cc7a 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 165/231] 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 37c2bc42d8..659c5a7ee9 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 166/231] 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 ec4c1f7dd9..2876f141e7 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 @@ leave IL_003e (nop) // 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 167/231] 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 d770c1d1b2..f68b896d09 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 @@ public static void Definition_NullableNInt(nint? p = 100) } #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 3b57da6523..fb8f21a6fe 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1675,7 +1675,7 @@ public ParameterDeclaration ConvertParameter(IParameter parameter) { 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 aa289b498d..d6d99977ab 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -89,6 +89,7 @@ public enum KnownAttribute In, Out, Optional, + DefaultParameterValue, CallerMemberName, CallerFilePath, CallerLineNumber, @@ -165,6 +166,7 @@ public static class KnownAttributes 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 @@ public static bool IsCustomAttribute(this KnownAttribute knownAttribute) 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 adcf825401..506aab564d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -64,8 +64,17 @@ public IEnumerable GetAttributes() 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 168/231] 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 b146e85a45..b17960e36f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -476,6 +476,7 @@ void ResolveCollisions() 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 4a8342cd7d..e4331c74be 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -58,6 +58,7 @@ public class AssignVariableNames : IILTransform Dictionary localFunctionMapping; HashSet loopCounters; const char maxLoopVariableName = 'n'; + int numDisplayClassLocals; public void Run(ILFunction function, ILTransformContext context) { @@ -151,6 +152,7 @@ public void Run(ILFunction function, ILTransformContext context) AddExistingName(reservedVariableNames, p.Name); } } + numDisplayClassLocals = 0; foreach (ILFunction f in function.Descendants.OfType().Reverse()) { PerformAssignment(f); @@ -195,7 +197,6 @@ void PerformAssignment(ILFunction function) { // 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 169/231] 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 0721134402..c1d73a19b8 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 @@ public class IntroduceDynamicTypeOnLocals : IILTransform { 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 910a4726af..a6fee58a7b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs @@ -32,23 +32,37 @@ public void Run(ILFunction function, ILTransformContext context) { 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 170/231] 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 5b5767f3b8..e67075aa3f 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 @@ public static Sign GetSign(this PrimitiveType primitiveType) } } + 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 d1f32a5589..7935e7ec06 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -145,10 +145,18 @@ bool TransformInlineAssignmentStObjOrCall(Block block, int pos) 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 @@ bool TransformInlineAssignmentStObjOrCall(Block block, int pos) } } + 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 @@ bool TransformInlineAssignmentLocal(Block block, int pos) 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 @@ static internal bool IsImplicitTruncation(ILInstruction value, IType type, IComp } 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 @@ static internal bool IsImplicitTruncation(ILInstruction value, IType type, IComp 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 @@ static internal bool IsImplicitTruncation(ILInstruction value, IType type, IComp } 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 @@ bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) { 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 @@ bool TransformPostIncDecOperator(Block block, int i) 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 @@ bool TransformPostIncDecOperator(Block block, int i) } 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 @@ bool TransformPostIncDecOperator(Block block, int i) 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 171/231] 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 b858024384..e9c391ae32 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4586,6 +4586,21 @@ public unsafe int PostIncrementOfPointer(int* ptr) 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 172/231] 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 659c5a7ee9..a119f82d5b 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 173/231] 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 7935e7ec06..7407179b4e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -145,18 +145,16 @@ bool TransformInlineAssignmentStObjOrCall(Block block, int pos) 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 @@ private static IType SwapSign(IType type, ICompilation compilation) 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 @@ bool TransformInlineAssignmentLocal(Block block, int pos) 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 @@ internal static bool IsImplicitTruncation(ILInstruction value, IType type, IComp 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 @@ internal static bool IsImplicitTruncation(ILInstruction value, IType type, IComp 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 @@ bool TransformPostIncDecOperator(Block block, int i) 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 @@ bool TransformPostIncDecOperator(Block block, int i) } 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 @@ bool TransformPostIncDecOperator(Block block, int i) 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 174/231] 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 9a77bea3c1..a55e3bdc49 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 175/231] 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 5367782b33..738d76c52b 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 176/231] 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 c60a25481a..8c8d40ddc3 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 3d35779e1b..d1a19a6207 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -105,9 +105,9 @@ static Tester() 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 @@ private static string ReplacePrivImplDetails(string il) } 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 @@ private static string ReplacePrivImplDetails(string il) 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 19d4a4dc81..d17665bb0d 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 68d9ecef8c..1fcfe4feea 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 177/231] 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 d06c6768e7..ed4a3f2bb2 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -317,7 +317,7 @@ public async Task Jmp() 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 @@ async Task RunIL(string testFileName, CompilerOptions options = CompilerOptions. 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 178/231] 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 d89d776485..86ae6bf22e 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1406,6 +1406,7 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun attr.Remove(); } } + RemoveCompilerFeatureRequiredAttribute(typeDecl, "RefStructs"); } if (settings.RequiredMembers) { @@ -1851,6 +1852,30 @@ internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribut 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 d6d99977ab..31fba0891b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -33,6 +33,7 @@ public enum KnownAttribute None, CompilerGenerated, + CompilerFeatureRequired, /// /// Marks a method as extension method; or a class as containing extension methods. /// @@ -119,6 +120,7 @@ public static class KnownAttributes 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 179/231] 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 d1a19a6207..6219c2a1af 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -343,6 +343,7 @@ public static List GetPreprocessorSymbols(CompilerOptions flags) { 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 d1dfd07747..1a9a911e22 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -326,6 +326,7 @@ public async Task Loops([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOpt RemoveDeadStores = (cscOptions == CompilerOptions.None), UseExpressionBodyForCalculatedGetterOnlyProperties = false, FileScopedNamespaces = false, + NumericIntPtr = false, }); } @@ -486,7 +487,7 @@ public async Task NullableRefTypes([ValueSource(nameof(roslyn3OrNewerOptions))] } [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 8032484ce7..5df746cc0c 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 @@ internal class ConstantsTests { 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 c56524281a..01edd50d21 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -436,10 +436,17 @@ private static bool ConstantTarget(dynamic a) 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 faa752bcfe..9423c42542 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 @@ public static void Overloaded(int a) 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 @@ public class B 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 @@ internal class FunctionPointersWithNativeIntegerTypes 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 c4d784fd46..677f6ebafe 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -844,10 +844,10 @@ int ZZZ_1() #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 69d3a3e0ce..4267944bbc 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs @@ -26,23 +26,36 @@ internal class NativeInts 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 @@ public void Convert() 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 @@ public void Convert2() 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 @@ public void CompoundAssign() { GetInstance(3).u *= 2u; } +#if !(CS110 && NET70) GetInstance(4).intptr += (nint)i32; checked { @@ -164,10 +183,13 @@ public void CompoundAssign() } // 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 @@ public void LocalTypeFromStore() 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 @@ public void LocalTypeFromUse() 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 b8483ecc10..6e7ca436a6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs @@ -89,6 +89,16 @@ public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType { } +#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 @@ public void CallMethodWithInOutParameter() 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 2bb122eb3e..9901fd69e1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -30,7 +30,11 @@ private struct StructWithStaticField { public static object StaticObj; +#if CS110 && NET70 + public nint A; +#else public IntPtr A; +#endif } private struct UnmanagedStruct @@ -605,6 +609,18 @@ private unsafe static int Issue2305(StructWithFixedSizeMembers value, StringComp 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 @@ private unsafe static int Issue2305(StructWithFixedSizeMembers value, StringComp { return (void*)intptr; } +#endif private unsafe static void* CastToVoidPtr(int* intptr) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 4ce0c44c04..fe47894221 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -152,12 +152,13 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) 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 @@ public bool NativeIntegers { } } + 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 5235da4701..a6ba7ef15d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -44,7 +44,7 @@ public static IType ApplyAttributesToType( { 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 b20d1bb0ee..5898fa32ec 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -125,11 +125,17 @@ public enum TypeSystemOptions /// 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 @@ public static TypeSystemOptions GetOptions(DecompilerSettings settings) typeSystemOptions |= TypeSystemOptions.FunctionPointers; if (settings.LifetimeAnnotations) typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations; + if (settings.NumericIntPtr) + typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute; return typeSystemOptions; } @@ -304,6 +312,10 @@ private async Task InitializeAsync(PEFile mainModule, IAssemblyResolver assembly } } + 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 94c50345bc..60123a44fb 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1108,6 +1108,15 @@ public static string DecompilerSettings_NullPropagation { } } + /// + /// 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 82d434c12e..186665d73b 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 d17665bb0d..92b843aaf3 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 180/231] 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 1a9a911e22..c4027fc39c 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -321,13 +321,11 @@ public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions [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 6d829ce95b..e206872a5f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs @@ -36,6 +36,9 @@ static void Main() Issue1747(); CallAmbiguousOutParam(); CallWithInParam(); +#if CS90 + NativeIntTests(new IntPtr(1), 2); +#endif Issue2444.M2(); Issue2741.B.Test(new Issue2741.C()); } @@ -337,6 +340,34 @@ static void InVsRegularParam(int i) #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 6e7ca436a6..877c8e3938 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs @@ -93,20 +93,14 @@ public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType [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 3d53263cdb..d256cf6259 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1553,12 +1553,25 @@ ExpressionWithResolveResult HandleConstructorCall(ExpectedTargetDetails expected 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 cf6ae1be57..3481ac1c82 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -402,8 +402,9 @@ public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, Ty 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 9da26dbe70..3ce5d01805 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -239,7 +239,7 @@ public static bool IsUnmanagedType(this IType type, bool allowGenerics) 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 181/231] 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 c2a84f46cd..ee3b705ea5 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -388,7 +388,22 @@ bool MatchNullCheckOrTypeCheck(ILInstruction condition, ref ILVariable objVar, K 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 182/231] 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 738d76c52b..980ba00c78 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 0000000000..64b205024c --- /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 cec81502ac..0000000000 --- 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 475d1daee1..5e6a1c7685 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 0000000000..f83b37d2cb --- /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 7376db07ca..adc10d6d5f 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -19,7 +19,13 @@ static public void Main() #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 @@ static public void Main() 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 @@ static public void Main() 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 898ff66018..369386289c 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 0000000000..c2be01b3dc --- /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 183/231] 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 52ad5e8043..fabc14ebac 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 c4027fc39c..c0f5e37632 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -538,6 +538,12 @@ public async Task RefLocalsAndReturns([ValueSource(nameof(roslyn2OrNewerOptions) 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 d27914c88c..d4d87d49ec 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 @@ public record struct WithRequiredMembers } #endif } +#if !NET60 namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] @@ -261,3 +262,4 @@ internal sealed class RequiredMemberAttribute : Attribute { } } +#endif diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs new file mode 100644 index 0000000000..55c837ef61 --- /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 fb8f21a6fe..b377aa0d48 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1970,6 +1970,10 @@ FieldDeclaration ConvertField(IField field) 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 b77494f465..e357dfa443 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -494,6 +494,8 @@ internal static bool IsReadonlyReference(ILInstruction addr) // 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 49a92fd264..42e13c40ed 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IField.cs @@ -35,6 +35,11 @@ public interface IField : IMember, IVariable /// 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 d6b7862012..405fff29bf 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -238,6 +238,7 @@ internal bool IgnoreAttribute(TopLevelTypeName attributeType, SymbolKind target) 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 2c4a2f67e4..ca9b556d7d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -109,6 +109,7 @@ public FakeField(ICompilation compilation) : base(compilation) } 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 c8e0457532..d561e3be21 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -189,6 +189,13 @@ public IAttribute GetAttribute(KnownAttribute attribute) 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 ac0dc9c402..fe4dc9ccc9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs @@ -45,13 +45,9 @@ public SpecializedField(IField fieldDefinition, TypeParameterSubstitution substi 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 184/231] 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 55c837ef61..aff38438b7 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs @@ -2,18 +2,61 @@ 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 @@ public void ReadonlyLocal() 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 633578c0c3..4ce9341d52 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2589,11 +2589,6 @@ public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDecl 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 8e1dca003e..eb9f29ab93 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 @@ public class ParameterDeclaration : AstNode 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 @@ public AstNodeCollection Attributes { } bool hasThisModifier; - bool isRefScoped, isValueScoped; + bool isRefScoped; public CSharpTokenNode ThisKeyword { get { @@ -130,12 +133,10 @@ public bool IsRefScoped { } } + [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 b377aa0d48..21965ff6d2 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1655,7 +1655,6 @@ public ParameterDeclaration ConvertParameter(IParameter parameter) 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 1b57569eff..2b1dc7f1d7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs @@ -167,6 +167,7 @@ public class RemoveEmbeddedAttributes : DepthFirstAstVisitor, IAstTransform "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 fe47894221..a7a9918ce2 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -360,8 +360,7 @@ public bool FunctionPointers { 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 5898fa32ec..b24e20d68c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -120,7 +120,7 @@ public enum TypeSystemOptions /// 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 59025af0fc..4348e09297 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 @@ public enum ReferenceKind : byte 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 405fff29bf..7becbdc5a9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -252,7 +252,7 @@ internal bool IgnoreAttribute(TopLevelTypeName attributeType, SymbolKind target) 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 31fba0891b..59dfc5863a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -94,7 +94,7 @@ public enum KnownAttribute CallerMemberName, CallerFilePath, CallerLineNumber, - LifetimeAnnotation, + ScopedRef, // Type parameter attributes: IsUnmanaged, @@ -172,7 +172,7 @@ public static class KnownAttributes 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 506aab564d..343cc8cfe4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -124,25 +124,10 @@ public LifetimeAnnotation Lifetime { 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 185/231] 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 69d3a3e0ce..549fd42eee 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs @@ -206,6 +206,11 @@ public nint NegateUnsigned(nuint x) 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 70b066dc82..1e6d5df8b2 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -1223,14 +1223,11 @@ ResolveResult CastTo(IType targetType, bool isNullable, ResolveResult expression 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 186/231] 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 8feb4fdf56..3844d22cbe 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -382,6 +382,9 @@ public interface IData 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 fb571fce27..0972105f12 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -617,6 +617,14 @@ protected internal override TranslatedExpression VisitLdStr(LdStr inst, Translat .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 ede737fd98..6da12dd60e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -159,7 +159,10 @@ public override void WritePrimitiveValue(object value, LiteralFormat format = Li 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 fc837a1637..e305cb98ed 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -269,6 +269,12 @@ public override void WritePrimitiveValue(object value, LiteralFormat format = Li 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 1e2138338a..a1e8fcc3b8 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs @@ -42,6 +42,7 @@ public enum LiteralFormat : byte StringLiteral, VerbatimStringLiteral, CharLiteral, + Utf8Literal, } /// diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index a7a9918ce2..0014824cac 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -153,12 +153,13 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) 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 @@ public bool FileScopedNamespaces { /// /// 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 @@ public bool StringInterpolation { } } + 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 ab6766660c..c4ce2d8af1 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 @@ public enum OpCode : byte 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 @@ protected internal override bool PerformMatch(ILInstruction? other, ref Patterns } } 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 @@ protected internal virtual void VisitLdStr(LdStr inst) { Default(inst); } + protected internal virtual void VisitLdStrUtf8(LdStrUtf8 inst) + { + Default(inst); + } protected internal virtual void VisitLdcI4(LdcI4 inst) { Default(inst); @@ -7345,6 +7389,10 @@ protected internal virtual T VisitLdStr(LdStr inst) { 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 @@ protected internal virtual T VisitLdStr(LdStr inst, C context) { 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 @@ partial class InstructionOutputExtensions "nullable.unwrap", "nullable.rewrap", "ldstr", + "ldstr.utf8", "ldc.i4", "ldc.i8", "ldc.f4", @@ -8285,6 +8338,17 @@ public bool MatchLdStr([NotNullWhen(true)] out string? value) 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 @@ public bool MatchAwait([NotNullWhen(true)] out ILInstruction? value) } } } + diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 18a4cf4c48..72da23a16e 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 296145397b..8a6684c4e0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -313,10 +313,10 @@ protected internal override void VisitNewObj(NewObj inst) 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 8e0b26fb97..7deb3d9eaf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs @@ -31,8 +31,10 @@ class ParameterNullCheckTransform : IILTransform { 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 3bb5cc7978..1944872a84 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -20,6 +20,7 @@ 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 @@ bool DoTransform(ILFunction function, Block body, int pos) 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 @@ internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTra { 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 @@ internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTra 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 ee3b705ea5..2025d390b1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -390,7 +390,7 @@ bool MatchNullCheckOrTypeCheck(ILInstruction condition, ref ILVariable objVar, K { 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 60123a44fb..9816546950 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 @@ public static string DecompilerSettings_UseVariableNamesFromDebugSymbolsIfAvaila } } + /// + /// 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 186665d73b..5a76bc5aa3 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 187/231] 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 4ce9341d52..ce48d69ca9 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2565,9 +2565,9 @@ public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDecl 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 eb9f29ab93..a86945d665 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -44,7 +44,9 @@ public class ParameterDeclaration : AstNode { 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 @@ public AstNodeCollection Attributes { } bool hasThisModifier; - bool isRefScoped; + bool isScopedRef; public CSharpTokenNode ThisKeyword { get { @@ -125,11 +127,20 @@ public bool HasThisModifier { } } + 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 21965ff6d2..3751c4454c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1654,7 +1654,7 @@ public ParameterDeclaration ConvertParameter(IParameter parameter) { 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 0014824cac..2a8e2a4aed 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -150,7 +150,7 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) if (languageVersion < CSharp.LanguageVersion.CSharp11_0) { parameterNullCheck = false; - lifetimeAnnotations = false; + scopedRef = false; requiredMembers = false; numericIntPtr = false; utf8StringLiterals = false; @@ -159,7 +159,7 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) 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 @@ public bool FunctionPointers { } } - 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 b24e20d68c..5a46088f68 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -123,7 +123,9 @@ public enum TypeSystemOptions /// 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 @@ public enum TypeSystemOptions /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods - | NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegersWithoutAttribute + | NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute } /// @@ -171,8 +173,8 @@ public static TypeSystemOptions GetOptions(DecompilerSettings settings) 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 4348e09297..aee0078a93 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -40,6 +40,14 @@ public struct LifetimeAnnotation /// /// 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 7becbdc5a9..cea8132506 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -253,7 +253,7 @@ internal bool IgnoreAttribute(TopLevelTypeName attributeType, SymbolKind target) 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 343cc8cfe4..c2cba9824e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -117,7 +117,7 @@ ReferenceKind DetectRefKind() public LifetimeAnnotation Lifetime { get { - if ((module.TypeSystemOptions & TypeSystemOptions.LifetimeAnnotations) == 0) + if ((module.TypeSystemOptions & TypeSystemOptions.ScopedRef) == 0) { return default; } @@ -126,7 +126,7 @@ public LifetimeAnnotation Lifetime { 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 9816546950..0e93e78f59 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1073,15 +1073,6 @@ public static string DecompilerSettings_IsUnmanagedAttributeOnTypeParametersShou } } - /// - /// 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 @@ public static string DecompilerSettings_RequiredMembers { } } + /// + /// 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 5a76bc5aa3..ffa61461d6 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 188/231] 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 49d191b08e..5450173574 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -111,4 +111,32 @@ private struct Entry 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 3d7d1e1150..b6b3bef3ad 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs @@ -64,7 +64,8 @@ public virtual string ReflectionName { 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 1cb4c5954b..3eff3c1423 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -308,7 +308,7 @@ public bool? IsReferenceType { 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 7a41c50c06..41ae7ee70b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -18,9 +18,8 @@ internal NullabilityAnnotatedType(IType type, Nullability nullability) 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 12601f9a4f..c7cf024284 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs @@ -115,7 +115,7 @@ public override int GetHashCode() 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 189/231] 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 384cebd831..86b9a2503b 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 190/231] Re-order packages.props --- packages.props | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages.props b/packages.props index 9b0177e171..a347475be6 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 191/231] 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 df63a36354..b99c9fb14d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs @@ -163,8 +163,13 @@ public static void BoxedLiteralsArray() 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 192/231] 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 41ae7ee70b..abfd5b184b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -20,6 +20,7 @@ internal NullabilityAnnotatedType(IType type, Nullability nullability) // 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 5db919db7b..28a0f33b7e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -107,7 +107,7 @@ public override bool? IsReferenceType { 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 193/231] 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 df2cb64020..39c2bcad3e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -72,6 +72,13 @@ public IType GetFunctionPointerType(SRM.MethodSignature signature) 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 194/231] 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 0330f079fc..79b79d70ca 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -88,7 +88,7 @@ public BlockContainer(ContainerKind kind = ContainerKind.Normal, StackType expec 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 195/231] #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 49501f5014..154e65c5ce 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 @@ public class ILReader 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 @@ public ILReader(MetadataModule module) 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 @@ void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, G 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 @@ void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, G 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 @@ ILVariable CreateILVariable(int index, IType type) 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 @@ void ReadInstructions(CancellationToken cancellationToken) 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 @@ public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, Ge 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 @@ ILInstruction InitObj(ILInstruction target, IType type) 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 196/231] 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 9c178a2ce7..47ebea33a1 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 75a7613bf8..be06a50348 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 @@ 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 @@ void CreateContainerStructure() 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 @@ void CreateContainerStructure() { 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 @@ void CreateContainerStructure() } 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 @@ public void CreateBlocks(BlockContainer mainContainer, List instr 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 @@ void ConnectBranches(ILInstruction inst, CancellationToken cancellationToken) 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 @@ void ConnectBranches(ILInstruction inst, CancellationToken cancellationToken) } } - Block FindBranchTarget(int targetILOffset) + Block? FindBranchTarget(int targetILOffset) { foreach (var container in containerStack) { @@ -291,7 +239,7 @@ Block FindBranchTarget(int targetILOffset) 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 @@ void CreateOnErrorDispatchers() { 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 @@ void CreateOnErrorDispatchers() 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 154e65c5ce..f53a56acc2 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 @@ public ILReader(MetadataModule module) 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 @@ void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, G 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 @@ void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, G 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 @@ void Warn(string message) 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 @@ private bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType /// /// 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 @@ void ReadInstructions(CancellationToken cancellationToken) { // 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 @@ void ReadInstructions(CancellationToken cancellationToken) // 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 @@ private void PrepareBranchTargetsAndStacksForExceptionHandlers() 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 @@ private void PrepareBranchTargetsAndStacksForExceptionHandlers() 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 @@ or OpCode.CallIndirect } } - 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 @@ public void WriteTypedIL(MethodDefinitionHandle method, MethodBodyBlock body, { 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 @@ public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, Ge 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 @@ StackType PeekStackType() return currentStack.Peek().StackType; } - class CollectStackVariablesVisitor : ILVisitor + sealed class CollectStackVariablesVisitor : ILVisitor { readonly UnionFind unionFind; internal readonly HashSet variables = new HashSet(); @@ -1216,7 +1244,7 @@ protected internal override ILInstruction VisitLdLoc(LdLoc inst) { 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 @@ protected internal override ILInstruction VisitStLoc(StLoc inst) { 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 @@ ILInstruction Pop(StackType expectedType) 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 @@ ILInstruction DecodeUnconditionalBranch(ILOpCode opCode, bool isLeave = false) } } - 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 @@ void MarkBranchTarget(int targetILOffset) /// private void FlushExpressionStack() { + Debug.Assert(currentBlock != null); foreach (var inst in expressionStack) { Debug.Assert(inst.ResultType != StackType.Void); @@ -1935,7 +1966,7 @@ private void FlushExpressionStack() 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 197/231] 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 c0b9de9850..af864827e5 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -258,7 +258,7 @@ static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILT 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 @@ static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILT /// 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 f53a56acc2..89d0739aed 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -514,14 +514,6 @@ void ReadBlock(ImportedBlock block, CancellationToken cancellationToken) 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 198/231] 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 237bdd4029..5338bb7c99 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -263,6 +263,12 @@ public async Task GuessAccessors() 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 0000000000..6945a1db14 --- /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 0000000000..bd14a188ca --- /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 89d0739aed..8b10729673 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -535,16 +535,25 @@ void ReadBlock(ImportedBlock block, CancellationToken cancellationToken) 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 199/231] 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 6945a1db14..73998e2bd4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs @@ -5,7 +5,7 @@ public static void RetVoid() } 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 be06a50348..c218816731 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -129,15 +129,6 @@ public void CreateBlocks(BlockContainer mainContainer, IEnumerable basicB { 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 8b10729673..901076a22b 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -457,6 +457,13 @@ void ReadInstructions(CancellationToken cancellationToken) { 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 @@ ILInstruction PopLdFldTarget(IField field) 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 200/231] 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 c0f5e37632..c341835e0e 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -312,6 +312,12 @@ public async Task LiftedOperators([ValueSource(nameof(defaultOptions))] Compiler 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 0000000000..35e4a4a186 --- /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 201/231] 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 e9c391ae32..abc6bb7e5f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -201,6 +201,12 @@ public struct CustomStruct { 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 @@ public static void CustomStructRightShiftTest(CustomStruct p, CustomClass c, Cus #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 35e4a4a186..0e73a79521 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs @@ -72,6 +72,13 @@ public class AllOperators 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 @@ public void Test() 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 86ae6bf22e..08d815d91e 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -530,6 +530,7 @@ static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings) 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 0972105f12..91a86192a5 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1729,6 +1729,7 @@ static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op) case BinaryOperatorType.ExclusiveOr: case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: return false; default: return true; @@ -1804,7 +1805,7 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign( 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 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign( } } - internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name) + internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -1846,6 +1847,8 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign( 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 2e04d0575a..2076139947 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -237,6 +237,7 @@ TypeSystemAstBuilder CreateAstBuilder() 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 ce48d69ca9..9a4788f704 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -826,6 +826,7 @@ public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binar 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 74e99d8c07..e7f372f96d 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs @@ -92,6 +92,9 @@ public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression bina 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 e279cf021c..2fb3a45460 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -122,6 +122,7 @@ static PrecedenceLevel GetPrecedence(Expression expr) 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 b3f453157c..fff06f1343 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs @@ -674,6 +674,27 @@ public OperatorMethod[] ShiftRightOperators { } } } + + 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 1e6d5df8b2..f235e0a98a 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -685,7 +685,7 @@ public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult { 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 @@ public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult 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 @@ static string GetOverloadableOperatorName(BinaryOperatorType op) 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 acd1038a87..378b33bfa5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -47,6 +47,7 @@ public class AssignmentExpression : Expression 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 @@ public static TokenRole GetOperatorRole(AssignmentOperatorType op) return ShiftLeftRole; case AssignmentOperatorType.ShiftRight: return ShiftRightRole; + case AssignmentOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case AssignmentOperatorType.BitwiseAnd: return BitwiseAndRole; case AssignmentOperatorType.BitwiseOr: @@ -164,6 +167,8 @@ public static TokenRole GetOperatorRole(AssignmentOperatorType op) 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 @@ public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool che 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 @@ public enum AssignmentOperatorType 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 f64752e063..294b9f5846 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -52,6 +52,7 @@ public class BinaryOperatorExpression : Expression 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 @@ public static TokenRole GetOperatorRole(BinaryOperatorType op) return ShiftLeftRole; case BinaryOperatorType.ShiftRight: return ShiftRightRole; + case BinaryOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case BinaryOperatorType.NullCoalescing: return NullCoalescingRole; case BinaryOperatorType.Range: @@ -262,6 +265,8 @@ public enum BinaryOperatorType 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 c0f8236b88..4d8677ca7d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs @@ -57,6 +57,7 @@ public enum OperatorType ExclusiveOr, LeftShift, RightShift, + UnsignedRightShift, Equality, Inequality, GreaterThan, @@ -94,6 +95,7 @@ public class OperatorDeclaration : EntityDeclaration 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 @@ static OperatorDeclaration() 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 @@ public static TokenRole GetRole(OperatorType type) 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 3751c4454c..17b8967102 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -225,6 +225,11 @@ void InitProperties() /// 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 @@ EntityDeclaration ConvertOperator(IMethod op) 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 f2f7a355f3..738fcd46ab 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -142,7 +142,7 @@ void ProcessInvocationExpression(InvocationExpression invocationExpression) 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 @@ static bool ToStringIsKnownEffectFree(IType type) } } - static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) + static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -374,6 +374,8 @@ static bool ToStringIsKnownEffectFree(IType type) 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 2a8e2a4aed..dac595efac 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -154,12 +154,13 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) 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 @@ public bool Utf8StringLiterals { } } + 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 7407179b4e..849ac88401 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -386,7 +386,7 @@ internal static bool HandleCompoundAssign(ILInstruction compoundStore, Statement 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 d496204b7a..74bbcf0725 100644 --- a/ICSharpCode.Decompiler/Output/IAmbience.cs +++ b/ICSharpCode.Decompiler/Output/IAmbience.cs @@ -109,6 +109,10 @@ public enum ConversionFlags /// 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 a5cd1e1a0d..4486366a9a 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -749,6 +749,10 @@ public override RichText GetRichTextTooltip(IEntity entity) { 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 0e93e78f59..f5396a9e28 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1280,6 +1280,15 @@ public static string DecompilerSettings_SwitchExpressions { } } + /// + /// 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 ffa61461d6..1368de08d9 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 202/231] 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 abc6bb7e5f..f0e437f495 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -1691,6 +1691,30 @@ public static void ShortRightShiftTest(short p, CustomClass c, CustomStruct2 s) #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 @@ public static void UshortRightShiftTest(ushort p, CustomClass c, CustomStruct2 s #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 @@ public static void IntRightShiftTest(int p, CustomClass c, CustomStruct2 s) #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 91a86192a5..20a1c7d8e9 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1745,10 +1745,25 @@ TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorTy 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 @@ protected internal override TranslatedExpression VisitNumericCompoundAssign(Nume 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 294b9f5846..1386e1029c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -208,6 +208,7 @@ public static ExpressionType GetLinqNodeType(BinaryOperatorType op, bool checkFo 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 d2ae9b2d5a..7fb22fadb9 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs @@ -98,6 +98,8 @@ public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(Bina 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 c35f986e0d..a25e39a96a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -215,8 +215,9 @@ internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, 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 @@ internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, } 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 203/231] 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 95f8e62697..7cd3368e63 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs @@ -106,9 +106,21 @@ public int RShiftByte(byte num) 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 @@ public int RShiftSByte(sbyte num) 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 20a1c7d8e9..6c0e302003 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1208,9 +1208,9 @@ protected internal override TranslatedExpression VisitBinaryNumericInstruction(B 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 @@ static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op) } } - 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 @@ TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorTy 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 @@ TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorTy 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 204/231] 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 adc10d6d5f..6b40f1ac14 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -38,6 +38,8 @@ static public void Main() #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 205/231] 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 980ba00c78..11faca8a45 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 5e6a1c7685..bf8fa6fe55 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 6b40f1ac14..adc10d6d5f 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -38,8 +38,6 @@ static public void Main() #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 92b843aaf3..c57753c148 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 206/231] 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 adc10d6d5f..ce15c30891 100644 --- a/ILSpy.Installer/setup.cs +++ b/ILSpy.Installer/setup.cs @@ -71,7 +71,41 @@ static public void Main() 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 207/231] 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 f6b0ea37cf..5511d5ad58 100644 --- a/ILSpy/Metadata/MetadataTreeNode.cs +++ b/ILSpy/Metadata/MetadataTreeNode.cs @@ -25,6 +25,7 @@ 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 @@ public MetadataTreeNode(PEFile module, AssemblyTreeNode assemblyTreeNode) 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 0e93e78f59..6ff0a2fda9 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1892,6 +1892,15 @@ public static string ManageAssemblyLists { } } + /// + /// 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 ffa61461d6..04fc39c006 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 208/231] 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 e9c391ae32..1a0f2b6b21 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -287,6 +287,18 @@ public struct CustomStruct2 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 @@ public void IncrementStaticPropertyShort() 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 @@ public static void ByteAddTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteSubtractTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteMultiplyTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteDivideTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteModulusTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteLeftShiftTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteRightShiftTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteBitAndTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteBitOrTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void ByteBitXorTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void BytePostIncTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void BytePreIncTest(byte p, CustomClass c, CustomStruct2 s) } 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 @@ public static void BytePostDecTest(byte p, CustomClass c, CustomStruct2 s) 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 @@ public static void BytePreDecTest(byte p, CustomClass c, CustomStruct2 s) } 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 @@ public static void SbyteAddTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteSubtractTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteMultiplyTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteDivideTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteModulusTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteLeftShiftTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteRightShiftTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteBitAndTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteBitOrTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbyteBitXorTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbytePostIncTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbytePreIncTest(sbyte p, CustomClass c, CustomStruct2 s) } 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 @@ public static void SbytePostDecTest(sbyte p, CustomClass c, CustomStruct2 s) 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 @@ public static void SbytePreDecTest(sbyte p, CustomClass c, CustomStruct2 s) } 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 @@ public static void ShortAddTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortSubtractTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortMultiplyTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortDivideTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortModulusTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortLeftShiftTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortRightShiftTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortBitOrTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortBitXorTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortPostIncTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortPreIncTest(short p, CustomClass c, CustomStruct2 s) } 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 @@ public static void ShortPostDecTest(short p, CustomClass c, CustomStruct2 s) 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 @@ public static void ShortPreDecTest(short p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UshortAddTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortSubtractTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortMultiplyTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortDivideTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortModulusTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortLeftShiftTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortRightShiftTest(ushort p, CustomClass c, CustomStruct2 s 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 @@ public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortBitOrTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortBitXorTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortPostIncTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortPreIncTest(ushort p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UshortPostDecTest(ushort p, CustomClass c, CustomStruct2 s) 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 @@ public static void UshortPreDecTest(ushort p, CustomClass c, CustomStruct2 s) } 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 @@ public static void IntAddTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntSubtractTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntMultiplyTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntDivideTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntModulusTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntLeftShiftTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntRightShiftTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntBitOrTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntBitXorTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntPostIncTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntPreIncTest(int p, CustomClass c, CustomStruct2 s) } 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 @@ public static void IntPostDecTest(int p, CustomClass c, CustomStruct2 s) 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 @@ public static void IntPreDecTest(int p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UintAddTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintSubtractTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintMultiplyTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintDivideTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintModulusTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintLeftShiftTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintRightShiftTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintBitAndTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintBitOrTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintBitXorTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintPostIncTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintPreIncTest(uint p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UintPostDecTest(uint p, CustomClass c, CustomStruct2 s) 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 @@ public static void UintPreDecTest(uint p, CustomClass c, CustomStruct2 s) } 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 @@ public static void LongAddTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongSubtractTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongMultiplyTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongDivideTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongModulusTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongLeftShiftTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongRightShiftTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongBitAndTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongBitOrTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongBitXorTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongPostIncTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongPreIncTest(long p, CustomClass c, CustomStruct2 s) } 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 @@ public static void LongPostDecTest(long p, CustomClass c, CustomStruct2 s) 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 @@ public static void LongPreDecTest(long p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UlongAddTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongSubtractTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongMultiplyTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongDivideTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongModulusTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongLeftShiftTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongRightShiftTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongBitAndTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongBitOrTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongBitXorTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongPostIncTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongPreIncTest(ulong p, CustomClass c, CustomStruct2 s) } 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 @@ public static void UlongPostDecTest(ulong p, CustomClass c, CustomStruct2 s) 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 @@ public static void UlongPreDecTest(ulong p, CustomClass c, CustomStruct2 s) } 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 @@ public static void CustomClassAddTest(CustomClass p, CustomClass c, CustomStruct 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 @@ public static void CustomClassSubtractTest(CustomClass p, CustomClass c, CustomS 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 @@ public static void CustomClassMultiplyTest(CustomClass p, CustomClass c, CustomS 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 @@ public static void CustomClassDivideTest(CustomClass p, CustomClass c, CustomStr 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 @@ public static void CustomClassModulusTest(CustomClass p, CustomClass c, CustomSt 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 @@ public static void CustomClassLeftShiftTest(CustomClass p, CustomClass c, Custom 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 @@ public static void CustomClassRightShiftTest(CustomClass p, CustomClass c, Custo 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 @@ public static void CustomClassBitAndTest(CustomClass p, CustomClass c, CustomStr 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 @@ public static void CustomClassBitOrTest(CustomClass p, CustomClass c, CustomStru 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 @@ public static void CustomClassBitXorTest(CustomClass p, CustomClass c, CustomStr 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 @@ public static void CustomClassPostIncTest(CustomClass p, CustomClass c, CustomSt 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 @@ public static void CustomClassPreIncTest(CustomClass p, CustomClass c, CustomStr } 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 @@ public static void CustomClassPostDecTest(CustomClass p, CustomClass c, CustomSt 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 @@ public static void CustomClassPreDecTest(CustomClass p, CustomClass c, CustomStr } 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 @@ public static void CustomStructAddTest(CustomStruct p, CustomClass c, CustomStru 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 @@ public static void CustomStructSubtractTest(CustomStruct p, CustomClass c, Custo 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 @@ public static void CustomStructMultiplyTest(CustomStruct p, CustomClass c, Custo 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 @@ public static void CustomStructDivideTest(CustomStruct p, CustomClass c, CustomS 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 @@ public static void CustomStructModulusTest(CustomStruct p, CustomClass c, Custom 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 @@ public static void CustomStructLeftShiftTest(CustomStruct p, CustomClass c, Cust 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 @@ public static void CustomStructRightShiftTest(CustomStruct p, CustomClass c, Cus 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 @@ public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomS 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 @@ public static void CustomStructBitOrTest(CustomStruct p, CustomClass c, CustomSt 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 @@ public static void CustomStructBitXorTest(CustomStruct p, CustomClass c, CustomS 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 @@ public static void CustomStructPostIncTest(CustomStruct p, CustomClass c, Custom 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 @@ public static void CustomStructPreIncTest(CustomStruct p, CustomClass c, CustomS } 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 @@ public static void CustomStructPostDecTest(CustomStruct p, CustomClass c, Custom 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 0972105f12..761a4d6b99 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1587,7 +1587,7 @@ TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOp 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 d2ae9b2d5a..d766045307 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 @@ public override void VisitAssignmentExpression(AssignmentExpression assignment) { 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 @@ public override void VisitAssignmentExpression(AssignmentExpression assignment) } } } + + 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 209/231] 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 d9e7b4c392..f3de4cb41f 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 210/231] 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 11faca8a45..2b6b3ed2cc 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 88f9c52942..4e959d0d58 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 3dbf7547fb..0000000000 --- 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 1754260579..8f710fffe6 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 0000000000..d086a027a3 --- /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 369386289c..692222f9ae 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 211/231] 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 4e959d0d58..e9bf4e1537 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 8f710fffe6..d210a29e95 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 212/231] 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 6759a54c12..13caafd9ce 100644 --- a/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn.Shared/Commands/OpenILSpyCommand.cs @@ -47,12 +47,6 @@ protected virtual void OnBeforeQueryStatus(object sender, EventArgs e) 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 8144eb1402..dc90e39a20 100644 --- a/ILSpy.AddIn.Shared/ILSpyInstance.cs +++ b/ILSpy.AddIn.Shared/ILSpyInstance.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -31,8 +32,15 @@ public ILSpyInstance(ILSpyParameters parameters = null) 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 e9bf4e1537..c992a08536 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 213/231] 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 db13ce77ec..be14883354 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 214/231] 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 c992a08536..b65918c3d8 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 215/231] 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 b65918c3d8..5d09db6572 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 d210a29e95..021cfed03e 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 216/231] 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 fabc14ebac..03432b95f5 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 217/231] 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 0ac9866bf2..1dcef578ab 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 @@ public override void Execute(object parameter) ); } - 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 @@ public static void Display(DecompilerTextView textView) 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 @@ static void AddUpdateCheckButton(StackPanel stackPanel, DecompilerTextView textV try { - AvailableVersionInfo vInfo = await GetLatestVersionAsync(); + AvailableVersionInfo vInfo = await NotifyOfUpdatesStrategy.GetLatestVersionAsync(); stackPanel.Children.Clear(); ShowAvailableVersion(vInfo, stackPanel); } @@ -155,11 +146,9 @@ static void AddUpdateCheckButton(StackPanel stackPanel, DecompilerTextView textV }; } - 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 @@ static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPan VerticalAlignment = VerticalAlignment.Bottom }); } - else if (currentVersion < availableVersion.Version) + else if (AppUpdateService.CurrentVersion < availableVersion.Version) { stackPanel.Children.Add( new TextBlock { @@ -197,149 +186,6 @@ static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPan 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 eaa65475ba..c31d35affe 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -50,6 +50,7 @@ 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 @@ public async Task ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, 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 @@ async void downloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e) 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 0000000000..04b3f1635b --- /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 0000000000..995619b5d5 --- /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 0000000000..1d5123c261 --- /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 0000000000..777f23eee1 --- /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 218/231] 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 cef3d91f31..e188f94780 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 219/231] 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 b51c2e9ae8..759dc2ee07 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -131,26 +131,10 @@ void WriteHeader(string name, string value) 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 220/231] 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 759dc2ee07..54e67fc985 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 @@ public ResXResourceWriter(string fileName) #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 @@ void WriteNiceBase64(byte[] value, int offset, int length) 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 @@ void WriteBytes(string name, Type type, byte[] value, int offset, int length, st writer.WriteEndElement(); - if (!(comment == null || comment.Equals(String.Empty))) + if (!string.IsNullOrEmpty(comment)) { writer.WriteStartElement("comment"); writer.WriteString(comment); @@ -171,29 +173,22 @@ void WriteBytes(string name, Type type, byte[] value, int offset, int length, st 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 @@ void WriteString(string name, string value, string type, string comment) 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 @@ private void AddResource(string name, object value, string comment) 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 @@ public void AddResource(string name, string value) 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 221/231] 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 54e67fc985..1417718aaf 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -294,162 +294,6 @@ public void AddResource(string name, string value) 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 222/231] 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 1417718aaf..7d18ef5517 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 @@ public ResXResourceWriter(string fileName) { 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 @@ protected virtual void Dispose(bool disposing) Close(); } - static string schema = @" + static readonly string schema = @" @@ -362,11 +354,9 @@ protected virtual void Dispose(bool disposing) ".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 223/231] 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 fabc14ebac..040170ca1b 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 0000000000..cad0afa930 --- /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 224/231] 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 2a9ae64408..8a540a601f 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 @@ internal class AutoProperties 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 d4d87d49ec..49a4fba3e0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -256,10 +256,13 @@ public CompilerFeatureRequiredAttribute(string featureName) 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 08d815d91e..9615359d3c 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1397,16 +1397,7 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun } 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 @@ EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, PEF 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 @@ EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeRe 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 @@ internal static bool RemoveCompilerFeatureRequiredAttribute(EntityDeclaration en 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 225/231] 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 2d832dfd74..6ddc23b437 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -53,8 +53,8 @@ public RecordDecompiler(IDecompilerTypeSystem dts, ITypeDefinition recordTypeDef 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 @@ public bool MethodIsGenerated(IMethod method) // 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 @@ private bool IsGeneratedEquals(IMethod method) 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 226/231] 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 c57753c148..9db6276d51 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 227/231] 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 8c469191fd..c50801c161 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 228/231] 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 353b49acfe..283818df5d 100644 --- a/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs +++ b/ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs @@ -10,11 +10,13 @@ public static class KnownGuids 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 f3e191de6d..1bb909b256 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -120,6 +120,7 @@ internal enum CustomDebugInformationKind DefaultNamespaces, EditAndContinueLocalSlotMap, EditAndContinueLambdaAndClosureMap, + EncStateMachineStateMap, EmbeddedSource, SourceLink, MethodSteppingInformation, @@ -154,6 +155,10 @@ static CustomDebugInformationKind GetKind(MetadataReader metadata, GuidHandle h) { return CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap; } + if (KnownGuids.EncStateMachineStateMap == guid) + { + return CustomDebugInformationKind.EncStateMachineStateMap; + } if (KnownGuids.EmbeddedSource == guid) { return CustomDebugInformationKind.EmbeddedSource; @@ -211,7 +216,6 @@ public string ParentTooltip { } string kindString; - public string Kind { get { if (kindString != null) @@ -226,52 +230,23 @@ public string Kind { { 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 229/231] 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 1bb909b256..981802be00 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -211,7 +211,7 @@ public string ParentTooltip { 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 @@ public string Kind { } } - [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 230/231] 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 4be8cd37a0..4ad56815d7 100644 --- a/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs @@ -87,14 +87,14 @@ struct AssemblyRefEntry 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 4c16f67137..5d8409657d 100644 --- a/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs @@ -72,14 +72,14 @@ struct AssemblyEntry + 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 97be915963..9da461b581 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -100,8 +100,7 @@ unsafe struct ClassLayoutEntry 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 @@ public string ParentTooltip { 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 0493abd8ff..d7b30b4b8c 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -87,13 +87,12 @@ struct ConstantEntry + 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 @@ public string ParentTooltip { } } - [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 4070ad9bec..7691be4b82 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -87,8 +87,7 @@ struct CustomAttributeEntry + 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 @@ public string ParentTooltip { } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Constructor => MetadataTokens.GetToken(customAttr.Constructor); public void OnConstructorClick() @@ -123,7 +121,7 @@ public string ConstructorTooltip { } } - [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 bcfb719ad4..8e3b823300 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -88,8 +88,7 @@ struct DeclSecurityEntry + 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 @@ public string ParentTooltip { } } - [StringFormat("X8")] + [ColumnInfo("X8", Kind = ColumnKind.Other)] public DeclarativeSecurityAction Action => declSecAttr.Action; public string ActionTooltip { @@ -115,7 +114,7 @@ public string ActionTooltip { } } - [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 d25738f72c..6cf0f93493 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct EventMapEntry 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 @@ public string ParentTooltip { } } - [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 4ed3b9a84c..dabcb7cc4a 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -90,7 +90,7 @@ struct EventDefEntry : IMemberTreeNode + 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 @@ struct EventDefEntry : IMemberTreeNode 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 fee2aff10c..878024a00d 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -86,7 +86,7 @@ struct ExportedTypeEntry + 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 @@ struct ExportedTypeEntry 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 d959a49b64..c5aa00d719 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct FieldLayoutEntry 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 @@ public string FieldTooltip { } } - [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 a4d74b9bc3..b85d3c5ec9 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct FieldMarshalEntry 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 @@ public string ParentTooltip { } } - [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 c0d54a0233..890b71cdfc 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -90,7 +90,7 @@ unsafe struct FieldRVAEntry { readonly PEFile module; readonly MetadataReader metadata; - readonly FieldRVA fieldLayout; + readonly FieldRVA fieldRVA; public int RID { get; } @@ -98,13 +98,12 @@ unsafe struct FieldRVAEntry 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 @@ public string FieldTooltip { } } - [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 bdc927b28c..73eceefa15 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -94,7 +94,7 @@ struct FieldDefEntry : IMemberTreeNode + 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 @@ struct FieldDefEntry : IMemberTreeNode 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 c48b8890bd..ddf0e51895 100644 --- a/ILSpy/Metadata/CorTables/FileTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FileTableTreeNode.cs @@ -85,7 +85,7 @@ struct FileEntry + 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 @@ struct FileEntry 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 760cb2df15..4fcf1bc661 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -86,8 +86,7 @@ struct GenericParamConstraintEntry + 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 @@ public string OwnerTooltip { } } - [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 5bb682d2b5..fd15908170 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -88,7 +88,7 @@ struct GenericParamEntry 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 @@ struct GenericParamEntry 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 ccfd7801b0..8ad12af715 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -104,7 +104,7 @@ unsafe struct ImplMapEntry 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 @@ unsafe struct ImplMapEntry 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 @@ public string MemberForwardedTooltip { } } - [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 6f50900325..8764c1c5aa 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct InterfaceImplEntry 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 @@ public string ClassTooltip { } } - [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 e720ee1c14..0d9619cd9d 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -88,7 +88,7 @@ struct ManifestResourceEntry + 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 @@ struct ManifestResourceEntry 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 5c49878465..e4701270fa 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -87,8 +87,7 @@ struct MemberRefEntry + 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 @@ public string ParentTooltip { 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 bd7292df82..8b761b27da 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -86,8 +86,7 @@ struct MethodImplEntry + 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 @@ public string MethodDeclarationTooltip { } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody); public void OnMethodBodyClick() @@ -120,8 +118,7 @@ public string MethodBodyTooltip { } } - [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 73316effc3..6956e0cc60 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -90,13 +90,12 @@ struct MethodSemanticsEntry + 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 @@ public string MethodTooltip { } } - [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 702f66594e..780b502ef3 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -87,8 +87,7 @@ struct MethodSpecEntry + 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 @@ public string MethodTooltip { } } - [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 c1b1160f28..639e6f025d 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -92,7 +92,7 @@ struct MethodDefEntry : IMemberTreeNode + 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 @@ struct MethodDefEntry : IMemberTreeNode 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 @@ struct MethodDefEntry : IMemberTreeNode 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 b86387ecdd..ba65094264 100644 --- a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs @@ -82,17 +82,17 @@ struct ModuleEntry 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 d9560ab7e3..dda3ad8e14 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct NestedClassEntry 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 @@ public string NestedClassTooltip { } } - [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 5aea6118d3..5fee320ee6 100644 --- a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs @@ -85,7 +85,7 @@ struct ParamEntry + 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 c4291171fc..b34023f2a4 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -98,8 +98,7 @@ unsafe struct PropertyMapEntry 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 @@ public string ParentTooltip { } } - [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 39c3808060..0b428934e9 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -90,7 +90,7 @@ struct PropertyDefEntry : IMemberTreeNode + 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 @@ struct PropertyDefEntry : IMemberTreeNode 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 ff6fbfb765..89e79afbca 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -86,7 +86,7 @@ struct StandAloneSigEntry + 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 078d8afb05..47b38af0a4 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -91,7 +91,7 @@ struct TypeDefEntry : IMemberTreeNode + 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 @@ struct TypeDefEntry : IMemberTreeNode 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 @@ public string BaseTypeTooltip { } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int FieldList => MetadataTokens.GetToken(typeDef.GetFields().FirstOrDefault()); public void OnFieldListClick() @@ -166,8 +164,7 @@ public string FieldListTooltip { } } - [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 24db219f93..6e34da4da5 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -86,8 +86,7 @@ struct TypeRefEntry + 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 1d19874e57..5b87adaa13 100644 --- a/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs @@ -86,7 +86,7 @@ struct TypeSpecEntry + 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 981802be00..329cc86b69 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -197,8 +197,7 @@ static CustomDebugInformationKind GetKind(MetadataReader metadata, GuidHandle h) 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 @@ public string Kind { } } - [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 c3cef36da6..2a7fdb8e4b 100644 --- a/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs @@ -90,7 +90,7 @@ struct DocumentEntry 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 @@ public string HashAlgorithmTooltip { } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Hash => MetadataTokens.GetHeapOffset(document.Hash); public string HashTooltip { @@ -118,7 +118,7 @@ public string HashTooltip { } } - [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 e7841965df..0e6ee99d0a 100644 --- a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs @@ -90,8 +90,7 @@ struct ImportScopeEntry 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 @@ public void OnParentClick() 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 2a095fee4c..9eaf8be2af 100644 --- a/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs @@ -92,7 +92,7 @@ struct LocalConstantEntry 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 546e5c3955..6854b64e0a 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -90,8 +90,7 @@ struct LocalScopeEntry 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 @@ public string MethodTooltip { } } - [StringFormat("X8")] - [LinkToTable] + [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(localScope.ImportScope); public void OnImportScopeClick() @@ -116,8 +114,7 @@ public void OnImportScopeClick() 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 @@ public void OnVariableListClick() 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 ed8744e2ef..9913e1a6b5 100644 --- a/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs @@ -84,7 +84,7 @@ struct LocalVariableEntry 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 46dc938855..d439308e44 100644 --- a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs @@ -88,8 +88,7 @@ struct MethodDebugInformationEntry 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 @@ public string DocumentTooltip { } } - [StringFormat("X")] + [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int SequencePoints => MetadataTokens.GetHeapOffset(debugInfo.SequencePointsBlob); public string SequencePointsTooltip { @@ -122,8 +121,7 @@ public string SequencePointsTooltip { } } - [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 5e30b145fc..4973f38d3a 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -92,8 +92,7 @@ struct StateMachineMethodEntry 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 @@ public string MoveNextMethodTooltip { } } - [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 b35db7d08d..d8963ce603 100644 --- a/ILSpy/Metadata/GoToTokenCommand.cs +++ b/ILSpy/Metadata/GoToTokenCommand.cs @@ -60,7 +60,7 @@ public bool IsVisible(TextViewContext context) 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 cd6558a178..da418b554a 100644 --- a/ILSpy/Metadata/Helpers.cs +++ b/ILSpy/Metadata/Helpers.cs @@ -143,7 +143,7 @@ DataGridColumn GetColumn() 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 @@ static void ApplyAttributes(PropertyDescriptor descriptor, Binding binding, Data 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 @@ public static string ReadUTF8StringNullTerminated(this ref BlobReader reader) } } - 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 231/231] 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 9da461b581..af78f30442 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -108,14 +108,8 @@ public void OnParentClick() 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 @@ public ClassLayoutEntry(PEFile module, byte* ptr, int metadataOffset, int row) + 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 d7b30b4b8c..3f9dc97c8a 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -100,14 +100,8 @@ public void OnParentClick() 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 @@ public ConstantEntry(PEFile module, ConstantHandle handle) 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 7691be4b82..30a6cf47f0 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -95,14 +95,8 @@ public void OnParentClick() 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 @@ public void OnConstructorClick() 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 @@ public CustomAttributeEntry(PEFile module, CustomAttributeHandle handle) 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 8e3b823300..601b7a8b13 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -96,14 +96,8 @@ public void OnParentClick() 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 @@ public DeclSecurityEntry(PEFile module, DeclarativeSecurityAttributeHandle handl 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 6cf0f93493..ae08337084 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnParentClick() 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 @@ public void OnEventListClick() 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 @@ public EventMapEntry(PEFile module, byte* ptr, int metadataOffset, int row) 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 dabcb7cc4a..b24f093dc6 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -111,13 +111,8 @@ public void OnTypeClick() 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 @@ public EventDefEntry(PEFile module, EventDefinitionHandle handle) 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 878024a00d..b95a5a9e8e 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -118,16 +118,8 @@ public void OnImplementationClick() 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 @@ public ExportedTypeEntry(int metadataOffset, PEFile module, ExportedTypeHandle h 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 c5aa00d719..8a80966b0c 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnFieldClick() 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 @@ public FieldLayoutEntry(PEFile module, byte* ptr, int metadataOffset, int row) 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 b85d3c5ec9..a9f26ff130 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnParentClick() 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 @@ public FieldMarshalEntry(PEFile module, byte* ptr, int metadataOffset, int row) 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 890b71cdfc..9317bb3367 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -106,14 +106,8 @@ public void OnFieldClick() 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 @@ public FieldRVAEntry(PEFile module, byte* ptr, int metadataOffset, int row) + 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 73eceefa15..f036661682 100644 --- a/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldTableTreeNode.cs @@ -113,14 +113,8 @@ struct FieldDefEntry : IMemberTreeNode [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 @@ public FieldDefEntry(PEFile module, FieldDefinitionHandle handle) 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 4fcf1bc661..1f97fdce6f 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -118,13 +118,8 @@ public void OnTypeClick() 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 @@ public GenericParamConstraintEntry(PEFile module, GenericParameterConstraintHand 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 fd15908170..789670a59f 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -99,18 +99,13 @@ struct GenericParamEntry [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 @@ public GenericParamEntry(PEFile module, GenericParameterHandle handle) 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 8ad12af715..c6c228ba69 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -123,14 +123,8 @@ public void OnMemberForwardedClick() 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 @@ public void OnImportScopeClick() 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 @@ public unsafe ImplMapEntry(PEFile module, byte* ptr, int metadataOffset, int row 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 8764c1c5aa..35c66ab850 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnClassClick() 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 @@ public void OnInterfaceClick() 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 @@ public InterfaceImplEntry(PEFile module, byte* ptr, int metadataOffset, int row) + 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 0d9619cd9d..79fd5b76b0 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -105,16 +105,8 @@ public void OnImplementationClick() 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 @@ public ManifestResourceEntry(PEFile module, ManifestResourceHandle handle) 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 e4701270fa..01e7806dce 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -95,13 +95,8 @@ public void OnParentClick() 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 @@ public string ParentTooltip { [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 @@ public MemberRefEntry(PEFile module, MemberReferenceHandle handle) 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 8b761b27da..24b93afd00 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -94,13 +94,8 @@ public void OnMethodDeclarationClick() 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 @@ public void OnMethodBodyClick() 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 @@ public void OnTypeClick() 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 @@ public MethodImplEntry(PEFile module, MethodImplementationHandle handle) 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 6956e0cc60..3f3ab38dfc 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -103,13 +103,8 @@ public void OnMethodClick() 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 @@ public void OnAssociationClick() 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 @@ public MethodSemanticsEntry(PEFile module, Handle handle, MethodSemanticsAttribu 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 780b502ef3..c050dbc58a 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -95,13 +95,8 @@ public void OnMethodClick() 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 @@ public MethodSpecEntry(PEFile module, MethodSpecificationHandle handle) 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 639e6f025d..46f81cde4f 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -122,18 +122,7 @@ struct MethodDefEntry : IMemberTreeNode 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 dda3ad8e14..de347fd332 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnNestedClassClick() 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 @@ public void OnEnclosingClassClick() 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 @@ public unsafe NestedClassEntry(PEFile module, byte* ptr, int metadataOffset, int 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 b34023f2a4..64d9bb19ad 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -106,14 +106,8 @@ public void OnParentClick() 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 @@ public void OnPropertyListClick() 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 @@ public PropertyMapEntry(PEFile module, byte* ptr, int metadataOffset, int row) 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 0b428934e9..8240c2ac59 100644 --- a/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs @@ -106,13 +106,8 @@ struct PropertyDefEntry : IMemberTreeNode [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 @@ public PropertyDefEntry(PEFile module, PropertyDefinitionHandle handle) 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 89e79afbca..0e618aad21 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -89,14 +89,8 @@ struct StandAloneSigEntry [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 @@ public StandAloneSigEntry(PEFile module, StandaloneSignatureHandle handle) 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 47b38af0a4..3388a244ae 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -152,15 +152,13 @@ public void OnFieldListClick() 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 @@ public void OnMethodListClick() 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 @@ public TypeDefEntry(PEFile module, TypeDefinitionHandle handle) 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 6e34da4da5..f0a773dc3d 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -94,31 +94,8 @@ public void OnResolutionScopeClick() 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 @@ public TypeRefEntry(PEFile module, TypeReferenceHandle handle) 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 329cc86b69..9e9d46c612 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -205,14 +205,8 @@ public void OnParentClick() 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 6854b64e0a..8b8ac8b1ab 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -98,13 +98,8 @@ public void OnMethodClick() 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 @@ public LocalScopeEntry(PEFile module, MetadataReader metadata, bool isEmbedded, 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 4973f38d3a..e93c0c3d9a 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -100,14 +100,8 @@ public void OnMoveNextMethodClick() 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 @@ public void OnKickofMethodClick() 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 @@ public StateMachineMethodEntry(PEFile module, ref BlobReader reader, bool isEmbe 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 dbd8a424c5..204270dc3e 100644 --- a/ILSpy/Metadata/MetadataTableTreeNode.cs +++ b/ILSpy/Metadata/MetadataTableTreeNode.cs @@ -22,11 +22,10 @@ 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 @@ private void View_Loaded(object sender, System.Windows.RoutedEventArgs e) 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