Browse Source

Support null check without type check in sub patterns.

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
e475af7822
  1. 24
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 4
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs
  3. 83
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -387,6 +387,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -387,6 +387,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NonTypePattern(object obj)
{
if (obj is X { A: 42, B: { Length: 0 } } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_VarLengthPattern(object obj)
{
if (obj is X { A: 42, B: { Length: var length } } x)
{
Console.WriteLine("Test " + x.A + ": " + length);
}
else
{
Console.WriteLine("not Test");
}
}
#endif
private bool F()
{

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

@ -127,9 +127,9 @@ namespace ICSharpCode.Decompiler.IL @@ -127,9 +127,9 @@ namespace ICSharpCode.Decompiler.IL
testedOperand = m.testedOperand;
return true;
case Comp comp:
if (comp.MatchLogicNot(out var operand))
if (comp.MatchLogicNot(out var operand) && IsPatternMatch(operand, out testedOperand))
{
return IsPatternMatch(operand, out testedOperand);
return true;
}
else
{

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

@ -188,12 +188,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -188,12 +188,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
private void DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
private static void DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
{
// if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037
if (MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
bool negate = false;
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
{
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
{
return;
}
ExtensionMethods.Swap(ref trueInst, ref falseInst);
negate = true;
}
if (MatchInstruction.IsPatternMatch(condition, out var operand))
{
if (!PropertyOrFieldAccess(operand, out var target, out _))
@ -204,24 +214,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -204,24 +214,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
return;
}
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
context.Step("Move property sub pattern", condition);
if (negate)
{
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
{
return;
}
ExtensionMethods.Swap(ref trueInst, ref falseInst);
condition = Comp.LogicNot(condition);
}
context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(condition);
block.Instructions.Clear();
block.Instructions.Add(trueInst);
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent)
}
else if (PropertyOrFieldAccess(condition, out var target, out _))
{
if (!target.MatchLdLocRef(parentPattern.Variable))
{
DetectPropertySubPatterns(parentPattern, trueBlock, falseInst, context, ref cfg);
return;
}
context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(new Comp(negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdcI4(0)));
}
else
{
return;
}
block.Instructions.Clear();
block.Instructions.Add(trueInst);
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent)
{
DetectPropertySubPatterns(parentPattern, trueBlock, falseInst, context, ref cfg);
}
}
else if (block.Instructions[0].MatchStLoc(out var targetVariable, out var operand))
@ -248,10 +266,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -248,10 +266,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
parentPattern.SubPatterns.Add(varPattern);
block.Instructions.RemoveAt(0);
targetVariable.Kind = VariableKind.PatternLocal;
DetectPropertySubPatterns(parentPattern, block, parentFalseInst, context, ref cfg);
if (!MatchNullCheckPattern(block, varPattern, parentFalseInst, context, ref cfg))
{
DetectPropertySubPatterns(parentPattern, block, parentFalseInst, context, ref cfg);
}
}
}
private static bool MatchNullCheckPattern(Block block, MatchInstruction varPattern, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
{
if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
return false;
}
if (condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(varPattern.Variable))
{
ExtensionMethods.Swap(ref trueInst, ref falseInst);
}
else if (condition.MatchCompNotEqualsNull(out arg) && arg.MatchLdLoc(varPattern.Variable))
{
}
else
{
return false;
}
if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst))
{
return false;
}
context.Step("Null check pattern", block);
varPattern.CheckNotNull = true;
block.Instructions.Clear();
block.Instructions.Add(trueInst);
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent)
{
DetectPropertySubPatterns(varPattern, trueBlock, falseInst, context, ref cfg);
}
return true;
}
private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member)
{
if (operand is CallInstruction {
@ -300,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -300,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst,
private static bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst,
ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg)
{
var targetBlock = trueInst as Block;

Loading…
Cancel
Save