From a3191f19e2d6ccf66fba596be9fb8013cef57b01 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 16 Sep 2022 23:46:49 +0200 Subject: [PATCH] Fix #2763: Improve decompilation of switch-on-enum by preserving enum type information when inlining local variables into SwitchInstruction.Value. --- .../TestCases/Pretty/Switch.cs | 35 +++++++++++++++++++ .../CSharp/ExpressionBuilder.cs | 4 +++ .../CSharp/StatementBuilder.cs | 4 +++ .../IL/Instructions/SwitchInstruction.cs | 11 +++++- .../IL/Transforms/ILInlining.cs | 6 ++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index f49d3ea682..374752ba39 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -74,6 +74,16 @@ public enum State Null } + public enum KnownColor + { + DarkBlue, + DarkCyan, + DarkGoldenrod, + DarkGray, + DarkGreen, + DarkKhaki + } + private static char ch1767; #if !ROSLYN @@ -1474,5 +1484,30 @@ public static void Issue1767(string s) break; } } + + public static void Issue2763(int value) + { + switch ((KnownColor)value) + { + case KnownColor.DarkBlue: + Console.WriteLine("DarkBlue"); + break; + case KnownColor.DarkCyan: + Console.WriteLine("DarkCyan"); + break; + case KnownColor.DarkGoldenrod: + Console.WriteLine("DarkGoldenrod"); + break; + case KnownColor.DarkGray: + Console.WriteLine("DarkGray"); + break; + case KnownColor.DarkGreen: + Console.WriteLine("DarkGreen"); + break; + case KnownColor.DarkKhaki: + Console.WriteLine("DarkKhaki"); + break; + } + } } } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0c167800bf..e964073951 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3819,6 +3819,10 @@ protected internal override TranslatedExpression VisitSwitchInstruction(SwitchIn { strToInt = null; value = Translate(inst.Value); + if (inst.Type != null) + { + value = value.ConvertTo(inst.Type, this, allowImplicitConversion: true); + } type = value.Type; } diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 32d46b6433..1db19abe8a 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -226,6 +226,10 @@ SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstructio { strToInt = null; value = exprBuilder.Translate(inst.Value); + if (inst.Type != null) + { + value = value.ConvertTo(inst.Type, exprBuilder, allowImplicitConversion: true); + } type = value.Type; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index aa3a55a564..ad0d0a9a4b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL @@ -42,6 +43,12 @@ partial class SwitchInstruction /// public bool IsLifted; + /// + /// Additional type information used to interpret the value instruction. + /// Set by ILInlining to preserve stack information that would otherwise be lost. + /// + public IType? Type; + public SwitchInstruction(ILInstruction value) : base(OpCode.SwitchInstruction) { @@ -82,7 +89,9 @@ public override void WriteTo(ITextOutput output, ILAstWritingOptions options) output.Write("switch"); if (IsLifted) output.Write(".lifted"); - output.Write(" ("); + output.Write(' '); + Type?.WriteTo(output); + output.Write('('); value.WriteTo(output, options); output.Write(") "); output.MarkFoldStart("{...}"); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d9326c8419..b77494f465 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -600,6 +600,12 @@ static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, I case OpCode.YieldReturn: return true; case OpCode.SwitchInstruction: + // Preserve type info on switch instruction, if we're inlining a local variable into the switch-value slot. + if (v.Kind != VariableKind.StackSlot && loadInst.SlotInfo == SwitchInstruction.ValueSlot) + { + ((SwitchInstruction)parent).Type ??= v.Type; + } + return true; //case OpCode.BinaryNumericInstruction when parent.SlotInfo == SwitchInstruction.ValueSlot: case OpCode.StringToInt when parent.SlotInfo == SwitchInstruction.ValueSlot: return true;