Browse Source

Fix pattern matching with fields and value types.

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
4e62fea07a
  1. 29
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 5
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs
  3. 64
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

29
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs

@ -11,6 +11,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public object C { get; set; } public object C { get; set; }
} }
public struct S
{
public int I;
}
public void SimpleTypePattern(object x) public void SimpleTypePattern(object x)
{ {
if (x is string value) if (x is string value)
@ -346,6 +351,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test"); Console.WriteLine("not Test");
} }
} }
public void RecursivePattern_MultipleConstants(object obj)
{
if (obj is X { A: 42, B: "Hello" } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_ValueTypeWithField(object obj)
{
if (obj is S { I: 42 } s)
{
Console.WriteLine("Test " + s);
}
else
{
Console.WriteLine("not Test");
}
}
#endif #endif
private bool F() private bool F()
{ {

5
ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs

@ -22,6 +22,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
partial class MatchInstruction : ILInstruction partial class MatchInstruction : ILInstruction
@ -211,12 +212,12 @@ namespace ICSharpCode.Decompiler.IL
} }
else if (operand.MatchLdFld(out var target, out _)) else if (operand.MatchLdFld(out var target, out _))
{ {
Debug.Assert(target.MatchLdLoc(variable)); Debug.Assert(target.MatchLdLocRef(variable));
} }
else if (operand is CallInstruction call) else if (operand is CallInstruction call)
{ {
Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter); Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter);
Debug.Assert(call.Arguments[0].MatchLdLoc(variable)); Debug.Assert(call.Arguments[0].MatchLdLocRef(variable));
} }
else else
{ {

64
ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

@ -19,7 +19,6 @@
#nullable enable #nullable enable
using System; using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -189,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
private bool DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context) private void DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context)
{ {
// if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022 // if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037 // br IL_0037
@ -197,25 +196,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (MatchInstruction.IsPatternMatch(condition, out var operand)) if (MatchInstruction.IsPatternMatch(condition, out var operand))
{ {
if (operand is not CallInstruction { if (!PropertyOrFieldAccess(operand, out var target))
Method: {
SymbolKind: SymbolKind.Accessor,
AccessorKind: MethodSemanticsAttributes.Getter
},
Arguments: [LdLoc ldloc]
} call)
{ {
return false; return;
} }
if (ldloc.Variable != parentPattern.Variable) if (!target.MatchLdLocRef(parentPattern.Variable))
{ {
return false; return;
} }
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst)) if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
{ {
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst)) if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
{ {
return false; return;
} }
ExtensionMethods.Swap(ref trueInst, ref falseInst); ExtensionMethods.Swap(ref trueInst, ref falseInst);
condition = Comp.LogicNot(condition); condition = Comp.LogicNot(condition);
@ -224,10 +217,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms
parentPattern.SubPatterns.Add(condition); parentPattern.SubPatterns.Add(condition);
block.Instructions.Clear(); block.Instructions.Clear();
block.Instructions.Add(trueInst); block.Instructions.Add(trueInst);
return true;
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent)
{
DetectPropertySubPatterns(parentPattern, trueBlock, falseInst, context);
}
} }
} }
return false; }
private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target)
{
if (operand is CallInstruction {
Method: {
SymbolKind: SymbolKind.Accessor,
AccessorKind: MethodSemanticsAttributes.Getter
},
Arguments: [var _target]
})
{
target = _target;
return true;
}
else if (operand.MatchLdFld(out target, out _))
{
return true;
}
else
{
return false;
}
} }
private static bool MatchBlockContainingOneCondition(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst) private static bool MatchBlockContainingOneCondition(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst)
@ -310,7 +329,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
private bool PatternMatchValueTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) private bool PatternMatchValueTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg)
{ {
if (!MatchIsInstBlock(block, out var type, out var testedOperand, out var testedVariable, if (!MatchIsInstBlock(block, out var type, out var testedOperand, out var testedVariable,
out var boxType1, out var unboxBlock, out var falseBlock)) out var boxType1, out var unboxBlock, out var falseInst))
{ {
return false; return false;
} }
@ -348,8 +367,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
CheckNotNull = true, CheckNotNull = true,
CheckType = true CheckType = true
}; };
((Branch)ifInst.TrueInst).TargetBlock = unboxBlock; ifInst.TrueInst = new Branch(unboxBlock);
((Branch)block.Instructions.Last()).TargetBlock = falseBlock; block.Instructions[^1] = falseInst;
unboxBlock.Instructions.RemoveAt(0); unboxBlock.Instructions.RemoveAt(0);
if (unboxOperand == tempStore?.Variable) if (unboxOperand == tempStore?.Variable)
{ {
@ -360,6 +379,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// the pattern matching machinery to an offset belonging to an instruction in the then-block. // the pattern matching machinery to an offset belonging to an instruction in the then-block.
unboxBlock.SetILRange(unboxBlock.Instructions[0]); unboxBlock.SetILRange(unboxBlock.Instructions[0]);
storeToV.Variable.Kind = VariableKind.PatternLocal; storeToV.Variable.Kind = VariableKind.PatternLocal;
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, context);
return true; return true;
} }
@ -376,15 +396,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
[NotNullWhen(true)] out ILVariable? testedVariable, [NotNullWhen(true)] out ILVariable? testedVariable,
out IType? boxType, out IType? boxType,
[NotNullWhen(true)] out Block? unboxBlock, [NotNullWhen(true)] out Block? unboxBlock,
[NotNullWhen(true)] out Block? falseBlock) [NotNullWhen(true)] out ILInstruction? falseInst)
{ {
type = null; type = null;
testedOperand = null; testedOperand = null;
testedVariable = null; testedVariable = null;
boxType = null; boxType = null;
unboxBlock = null; unboxBlock = null;
falseBlock = null; if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out falseInst))
if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst))
{ {
return false; return false;
} }
@ -412,8 +431,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return false; return false;
} }
return trueInst.MatchBranch(out unboxBlock) && falseInst.MatchBranch(out falseBlock) return trueInst.MatchBranch(out unboxBlock) && unboxBlock.Parent == block.Parent;
&& unboxBlock.Parent == block.Parent && falseBlock.Parent == block.Parent;
} }
/// Block unboxBlock (incoming: 1) { /// Block unboxBlock (incoming: 1) {

Loading…
Cancel
Save