From ad4a87b9a3c054afaf0b159c9c86175e48559016 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Thu, 9 Oct 2025 02:12:48 -0700 Subject: [PATCH 1/2] Improve inlining of boxed values --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 +++ .../TestCases/Pretty/Issue3584.cs | 47 +++++++++++++++++++ ICSharpCode.Decompiler/IL/ILTypeExtensions.cs | 2 + ICSharpCode.Decompiler/IL/Instructions.cs | 12 ++++- 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3584.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index b6d2af5ad..cafcf2dad 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -163,6 +163,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index ffedb0b0e..ce1793293 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -718,6 +718,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task Issue3584([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3584.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3584.cs new file mode 100644 index 000000000..3c583d827 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3584.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal abstract class Issue4000 : IEnumerable, IEnumerable + { + public int Length; + + protected T[] results; + +#if !ROSLYN4 + public T this[int i] { + get { + if (i >= Length || i < 0) + { + return default(T); + } + if (results[i] != null && results[i].Equals(default(T))) + { + results[i] = CreateIthElement(i); + } + return results[i]; + } + } +#endif + + protected abstract T CreateIthElement(int i); + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Length; i++) + { + if (results[i] != null && results[i].Equals(default(T))) + { + results[i] = CreateIthElement(i); + } + yield return results[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index fda8cb533..4977eba37 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -238,6 +238,8 @@ namespace ICSharpCode.Decompiler.IL default: return SpecialType.UnknownType; } + case DefaultValue defaultValue: + return defaultValue.Type; case ILFunction func when func.DelegateType != null: return func.DelegateType; default: diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index b3a73d336..96a6bd948 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -318,6 +318,7 @@ namespace ICSharpCode.Decompiler.IL set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); + InvalidateFlags(); } } protected sealed override int GetChildCount() @@ -4357,10 +4358,19 @@ namespace ICSharpCode.Decompiler.IL public override StackType ResultType { get { return StackType.O; } } protected override InstructionFlags ComputeFlags() { - return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow; + var baseFlags = base.ComputeFlags(); + if (baseFlags == InstructionFlags.None && Type.Equals(Argument.InferType(null))) + { + return InstructionFlags.None; + } + return baseFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { + if (Flags == InstructionFlags.None) + { + return InstructionFlags.None; + } return base.DirectFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } } From b9d6ba7dff71e5e2fa3e782e8c37e293ec264430 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 11 Oct 2025 21:35:04 -0700 Subject: [PATCH 2/2] Remove flags from `box` that indicate memory access and ability to throw --- ICSharpCode.Decompiler/IL/Instructions.cs | 19 ------------------- ICSharpCode.Decompiler/IL/Instructions.tt | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 96a6bd948..490bc19db 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -318,7 +318,6 @@ namespace ICSharpCode.Decompiler.IL set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); - InvalidateFlags(); } } protected sealed override int GetChildCount() @@ -4356,24 +4355,6 @@ namespace ICSharpCode.Decompiler.IL set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } - protected override InstructionFlags ComputeFlags() - { - var baseFlags = base.ComputeFlags(); - if (baseFlags == InstructionFlags.None && Type.Equals(Argument.InferType(null))) - { - return InstructionFlags.None; - } - return baseFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; - } - public override InstructionFlags DirectFlags { - get { - if (Flags == InstructionFlags.None) - { - return InstructionFlags.None; - } - return base.DirectFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; - } - } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index ed6081243..74963251b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -266,7 +266,7 @@ CustomInvariant("CheckTargetSlot();")), new OpCode("box", "Boxes a value.", - Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("O")), + Unary, HasTypeOperand, ResultType("O")), new OpCode("unbox", "Compute address inside box.", Unary, HasTypeOperand, MayThrow, ResultType("Ref")), new OpCode("unbox.any", "Unbox a value.",