diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs index 5fdc53317..efeadf557 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs @@ -4,31 +4,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class PatternMatching { - public bool SimpleTypePattern(object x) + public void SimpleTypePattern(object x) { - Use(x is string y); - if (x is string z) + if (x is string value) { - Console.WriteLine(z); + Console.WriteLine(value); } - return x is string w; } - public bool SimpleTypePatternWithShortcircuit(object x) + public void TypePatternWithShortcircuit(object x) { - Use(F() && x is string y && y.Contains("a")); - if (F() && x is string z && z.Contains("a")) + Use(F() && x is string text && text.Contains("a")); + if (F() && x is string text2 && text2.Contains("a")) { - Console.WriteLine(z); + Console.WriteLine(text2); } - return F() && x is string w && w.Contains("a"); } - public void SimpleTypePatternWithShortcircuitAnd(object x) + public void TypePatternWithShortcircuitAnd(object x) { - if (x is string z && z.Contains("a")) + if (x is string text && text.Contains("a")) { - Console.WriteLine(z); + Console.WriteLine(text); } else { @@ -36,35 +33,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternWithShortcircuitOr(object x) + public void TypePatternWithShortcircuitOr(object x) { - if (!(x is string z) || z.Contains("a")) + if (!(x is string text) || text.Contains("a")) { Console.WriteLine(); } else { - Console.WriteLine(z); + Console.WriteLine(text); } } - public void SimpleTypePatternWithShortcircuitOr2(object x) + public void TypePatternWithShortcircuitOr2(object x) { - if (F() || !(x is string z)) + if (F() || !(x is string value)) { Console.WriteLine(); } else { - Console.WriteLine(z); + Console.WriteLine(value); } } - public void SimpleTypePatternValueTypesCondition(object x) + public void TypePatternValueTypesCondition(object x) { - if (x is int i) + if (x is int num) { - Console.WriteLine("Integer: " + i); + Console.WriteLine("Integer: " + num); } else { @@ -72,11 +69,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypesCondition2() + public void TypePatternValueTypesCondition2() { - if (GetObject() is int i) + if (GetObject() is int num) { - Console.WriteLine("Integer: " + i); + Console.WriteLine("Integer: " + num); } else { @@ -84,11 +81,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypesWithShortcircuitAnd(object x) + public void TypePatternValueTypesWithShortcircuitAnd(object x) { - if (x is int i && i.GetHashCode() > 0) + if (x is int num && num.GetHashCode() > 0) { - Console.WriteLine("Positive integer: " + i); + Console.WriteLine("Positive integer: " + num); } else { @@ -96,36 +93,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypesWithShortcircuitOr(object x) + public void TypePatternValueTypesWithShortcircuitOr(object x) { - if (!(x is int z) || z.GetHashCode() > 0) + if (!(x is int value) || value.GetHashCode() > 0) { Console.WriteLine(); } else { - Console.WriteLine(z); + Console.WriteLine(value); } } - public void SimpleTypePatternValueTypesWithShortcircuitOr2(object x) + public void TypePatternValueTypesWithShortcircuitOr2(object x) { - if (F() || !(x is int z)) + if (F() || !(x is int value)) { Console.WriteLine(); } else { - Console.WriteLine(z); + Console.WriteLine(value); } } -#if CS71 - public void SimpleTypePatternGenerics(object x) + public void TypePatternGenerics(object x) { - if (x is T t) + if (x is T val) { - Console.WriteLine(typeof(T).FullName + ": " + t); + Console.WriteLine(val.GetType().FullName); } else { @@ -133,11 +129,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternGenericRefType(object x) where T : class + public void TypePatternGenericRefType(object x) where T : class { - if (x is T t) + if (x is T val) { - Console.WriteLine(typeof(T).FullName + ": " + t); + Console.WriteLine(val.GetType().FullName); } else { @@ -145,24 +141,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternGenericValType(object x) where T : struct + public void TypePatternGenericValType(object x) where T : struct { - if (x is T t) + if (x is T val) { - Console.WriteLine(typeof(T).FullName + ": " + t); + Console.WriteLine(val.GetType().FullName); } else { Console.WriteLine("not a " + typeof(T).FullName); } } -#endif - public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse(object x) + public void TypePatternValueTypesWithShortcircuitAndMultiUse(object x) { - if (x is int i && i.GetHashCode() > 0 && i % 2 == 0) + if (x is int num && num.GetHashCode() > 0 && num % 2 == 0) { - Console.WriteLine("Positive integer: " + i); + Console.WriteLine("Positive integer: " + num); } else { @@ -170,9 +165,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse2(object x) + public void TypePatternValueTypesWithShortcircuitAndMultiUse2(object x) { - if ((x is int i && i.GetHashCode() > 0 && i % 2 == 0) || F()) + if ((x is int num && num.GetHashCode() > 0 && num % 2 == 0) || F()) { Console.WriteLine("true"); } @@ -182,9 +177,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse3(object x) + public void TypePatternValueTypesWithShortcircuitAndMultiUse3(object x) { - if (F() || (x is int i && i.GetHashCode() > 0 && i % 2 == 0)) + if (F() || (x is int num && num.GetHashCode() > 0 && num % 2 == 0)) { Console.WriteLine("true"); } @@ -194,9 +189,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public void SimpleTypePatternValueTypes() + public void TypePatternValueTypes() { - Use(F() && GetObject() is int y && y.GetHashCode() > 0 && y % 2 == 0); + Use(F() && GetObject() is int num && num.GetHashCode() > 0 && num % 2 == 0); } private bool F() diff --git a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs index f3f77108f..d83f62bf4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs @@ -18,6 +18,8 @@ #nullable enable +using ICSharpCode.Decompiler.TypeSystem; + namespace ICSharpCode.Decompiler.IL.Transforms { class PatternMatchingRefTypesTransform : IStatementTransform @@ -41,14 +43,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!context.Settings.PatternMatching) return; int startPos = pos; - if (block.Instructions[pos] is not StLoc - { - Variable: var s, - Value: IsInst { Argument: var testedOperand, Type: var type } - }) - { + if (!block.Instructions[pos].MatchStLoc(out var s, out var value)) return; + IType? unboxType; + if (value is UnboxAny unboxAny) + { + // stloc S(unbox.any T(isinst T(testedOperand))) + unboxType = unboxAny.Type; + value = unboxAny.Argument; } + else + { + unboxType = null; + } + if (value is not IsInst { Argument: var testedOperand, Type: var type }) + return; + if (type.IsReferenceType != true) + return; + if (!(unboxType == null || type.Equals(unboxType))) + return; if (!s.IsSingleDefinition) return; if (s.Kind is not (VariableKind.Local or VariableKind.StackSlot)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs index cf8234c8f..0da299ae0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs @@ -28,26 +28,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms { class PatternMatchingTransform : IILTransform { - - /// Block { - /// ... - /// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock - /// br unboxBlock - /// } - /// - /// Block unboxBlock (incoming: 1) { - /// stloc V(unbox.any T(ldloc testedOperand)) - /// if (nextCondition) br trueBlock - /// br falseBlock - /// } - /// => - /// Block { - /// ... - /// if (logic.and(match.type[T].notnull(V = testedOperand), nextCondition)) br trueBlock - /// br falseBlock - /// } - /// - /// -or- /// Block { /// ... /// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock @@ -77,28 +57,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms { continue; } - if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, falseBlock, - out var v, out var nextCondition, out var trueBlock, out var inverseNextCondition)) + if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, out var v)) { continue; } context.Step($"PatternMatching with {v.Name}", block); - if (inverseNextCondition) - { - nextCondition = Comp.LogicNot(nextCondition); - } var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; - ILInstruction logicAnd = IfInstruction.LogicAnd(new MatchInstruction(v, testedOperand) { + ifInst.Condition = new MatchInstruction(v, testedOperand) { CheckNotNull = true, CheckType = true - }, nextCondition); - ifInst.Condition = logicAnd; - ((Branch)ifInst.TrueInst).TargetBlock = trueBlock; + }; + ((Branch)ifInst.TrueInst).TargetBlock = unboxBlock; ((Branch)block.Instructions.Last()).TargetBlock = falseBlock; - unboxBlock.Instructions.Clear(); + unboxBlock.Instructions.RemoveAt(0); + // HACK: condition detection uses StartILOffset of blocks to decide which branch of if-else + // should become the then-branch. Change the unboxBlock StartILOffset from an offset inside + // the pattern matching machinery to an offset belonging to an instruction in the then-block. + unboxBlock.SetILRange(unboxBlock.Instructions[0]); v.Kind = VariableKind.PatternLocal; } - container.Blocks.RemoveAll(b => b.Instructions.Count == 0); } } @@ -140,20 +117,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Block unboxBlock (incoming: 1) { /// stloc V(unbox.any T(ldloc testedOperand)) - /// if (nextCondition) br trueBlock - /// br falseBlock + /// ... /// } - private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand, Block falseBlock, - [NotNullWhen(true)] out ILVariable? v, - [NotNullWhen(true)] out ILInstruction? nextCondition, - [NotNullWhen(true)] out Block? trueBlock, - out bool inverseCondition) + private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand, + [NotNullWhen(true)] out ILVariable? v) { v = null; - nextCondition = null; - trueBlock = null; - inverseCondition = false; - if (unboxBlock.IncomingEdgeCount != 1 || unboxBlock.Instructions.Count != 3) + if (unboxBlock.IncomingEdgeCount != 1) return false; if (!unboxBlock.Instructions[0].MatchStLoc(out v, out var value)) @@ -161,22 +131,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type) && arg.MatchLdLoc(testedOperand))) return false; - if (!unboxBlock.MatchIfAtEndOfBlock(out nextCondition, out var trueInst, out var falseInst)) - return false; - - if (trueInst.MatchBranch(out trueBlock) && falseInst.MatchBranch(falseBlock)) - { - return true; - } - else if (trueInst.MatchBranch(falseBlock) && falseInst.MatchBranch(out trueBlock)) - { - inverseCondition = true; - return true; - } - else - { - return false; - } + return true; } } }