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
Console.WriteLine("not Test"); 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 #endif
private bool F() private bool F()
{ {

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

@ -127,9 +127,9 @@ namespace ICSharpCode.Decompiler.IL
testedOperand = m.testedOperand; testedOperand = m.testedOperand;
return true; return true;
case Comp comp: 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 else
{ {

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

@ -188,12 +188,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; 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 // if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037 // br IL_0037
if (MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst)) 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 (MatchInstruction.IsPatternMatch(condition, out var operand))
{ {
if (!PropertyOrFieldAccess(operand, out var target, out _)) if (!PropertyOrFieldAccess(operand, out var target, out _))
@ -204,24 +214,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return; 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); condition = Comp.LogicNot(condition);
} }
context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(condition); parentPattern.SubPatterns.Add(condition);
block.Instructions.Clear(); }
block.Instructions.Add(trueInst); else if (PropertyOrFieldAccess(condition, out var target, out _))
{
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == block.Parent) 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)) else if (block.Instructions[0].MatchStLoc(out var targetVariable, out var operand))
@ -248,10 +266,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
parentPattern.SubPatterns.Add(varPattern); parentPattern.SubPatterns.Add(varPattern);
block.Instructions.RemoveAt(0); block.Instructions.RemoveAt(0);
targetVariable.Kind = VariableKind.PatternLocal; 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) private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member)
{ {
if (operand is CallInstruction { if (operand is CallInstruction {
@ -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) ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg)
{ {
var targetBlock = trueInst as Block; var targetBlock = trueInst as Block;

Loading…
Cancel
Save