Browse Source

Add support for simple constant patterns.

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
65b4c928c0
  1. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 31
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 36
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  4. 39
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -310,6 +310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -310,6 +310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
#if CS80
public void RecursivePattern_Type(object x)
{
if (x is X { C: string text })
@ -322,6 +323,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -322,6 +323,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public void RecursivePattern_Constant(object obj)
{
if (obj is X { C: null } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
#endif
private bool F()
{
return true;

31
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -4546,14 +4546,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -4546,14 +4546,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
left = left.UnwrapChild(castExpr.Expression);
}
var right = TranslatePattern(inst);
var right = TranslatePattern(inst, left.Type);
return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)))
.WithILInstruction(inst);
}
ExpressionWithILInstruction TranslatePattern(ILInstruction pattern)
ExpressionWithILInstruction TranslatePattern(ILInstruction pattern, IType leftHandType)
{
switch (pattern)
{
@ -4590,7 +4590,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -4590,7 +4590,7 @@ namespace ICSharpCode.Decompiler.CSharp
continue;
}
recursivePatternExpression.SubPatterns.Add(
new NamedArgumentExpression { Name = member.Name, Expression = TranslatePattern(subPattern) }
new NamedArgumentExpression { Name = member.Name, Expression = TranslatePattern(subPattern, member.ReturnType) }
.WithRR(new MemberResolveResult(null, member))
);
}
@ -4616,6 +4616,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -4616,6 +4616,31 @@ namespace ICSharpCode.Decompiler.CSharp
return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type))
.WithILInstruction(matchInstruction);
}
case Comp comp:
var constantValue = Translate(comp.Right, leftHandType);
switch (comp.Kind)
{
case ComparisonKind.Equality:
return constantValue
.WithILInstruction(comp);
case ComparisonKind.Inequality:
return new UnaryOperatorExpression(UnaryOperatorType.PatternNot, constantValue)
.WithILInstruction(comp);
case ComparisonKind.LessThan:
return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThan, constantValue)
.WithILInstruction(comp);
case ComparisonKind.LessThanOrEqual:
return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThanOrEqual, constantValue)
.WithILInstruction(comp);
case ComparisonKind.GreaterThan:
return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThan, constantValue)
.WithILInstruction(comp);
case ComparisonKind.GreaterThanOrEqual:
return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThanOrEqual, constantValue)
.WithILInstruction(comp);
default:
throw new InvalidOperationException("Unexpected comparison kind: " + comp.Kind);
}
default:
throw new NotImplementedException();
}

36
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -46,6 +46,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -46,6 +46,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole NullConditionalRole = new TokenRole("?");
public readonly static TokenRole SuppressNullableWarningRole = new TokenRole("!");
public readonly static TokenRole IndexFromEndRole = new TokenRole("^");
public readonly static TokenRole PatternNotRole = new TokenRole("not");
public UnaryOperatorExpression()
{
@ -126,6 +127,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -126,6 +127,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return SuppressNullableWarningRole;
case UnaryOperatorType.IndexFromEnd:
return IndexFromEndRole;
case UnaryOperatorType.PatternNot:
return PatternNotRole;
case UnaryOperatorType.PatternRelationalLessThan:
return BinaryOperatorExpression.LessThanRole;
case UnaryOperatorType.PatternRelationalLessThanOrEqual:
return BinaryOperatorExpression.LessThanOrEqualRole;
case UnaryOperatorType.PatternRelationalGreaterThan:
return BinaryOperatorExpression.GreaterThanRole;
case UnaryOperatorType.PatternRelationalGreaterThanOrEqual:
return BinaryOperatorExpression.GreaterThanOrEqualRole;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
}
@ -156,6 +167,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -156,6 +167,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case UnaryOperatorType.Await:
case UnaryOperatorType.SuppressNullableWarning:
case UnaryOperatorType.IndexFromEnd:
case UnaryOperatorType.PatternNot:
case UnaryOperatorType.PatternRelationalLessThan:
case UnaryOperatorType.PatternRelationalLessThanOrEqual:
case UnaryOperatorType.PatternRelationalGreaterThan:
case UnaryOperatorType.PatternRelationalGreaterThanOrEqual:
return ExpressionType.Extension;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
@ -216,5 +232,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -216,5 +232,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// C# 8 prefix ^ operator
/// </summary>
IndexFromEnd,
/// <summary>
/// C# 9 not pattern
/// </summary>
PatternNot,
/// <summary>
/// C# 9 relational pattern
/// </summary>
PatternRelationalLessThan,
/// <summary>
/// C# 9 relational pattern
/// </summary>
PatternRelationalLessThanOrEqual,
/// <summary>
/// C# 9 relational pattern
/// </summary>
PatternRelationalGreaterThan,
/// <summary>
/// C# 9 relational pattern
/// </summary>
PatternRelationalGreaterThanOrEqual,
}
}

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

@ -183,17 +183,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -183,17 +183,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container)
{
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst);
DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst, context);
}
return true;
}
private bool DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst)
private bool 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
// br IL_0037
if (block.Instructions.Count == 2 && block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst))
if (MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
if (MatchInstruction.IsPatternMatch(condition, out var operand))
{
@ -213,17 +213,44 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -213,17 +213,44 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
{
return false;
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
{
return false;
}
ExtensionMethods.Swap(ref trueInst, ref falseInst);
condition = Comp.LogicNot(condition);
}
context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(condition);
block.Instructions.RemoveAt(0);
block.Instructions[0] = trueInst;
block.Instructions.Clear();
block.Instructions.Add(trueInst);
return true;
}
}
return false;
}
private static bool MatchBlockContainingOneCondition(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst)
{
switch (block.Instructions.Count)
{
case 2:
return block.MatchIfAtEndOfBlock(out condition, out trueInst, out falseInst);
case 3:
condition = null;
if (!block.MatchIfAtEndOfBlock(out var loadTemp, out trueInst, out falseInst))
return false;
if (!(loadTemp.MatchLdLoc(out var tempVar) && tempVar.IsSingleDefinition && tempVar.LoadCount == 1))
return false;
return block.Instructions[0].MatchStLoc(tempVar, out condition);
default:
condition = null;
trueInst = null;
falseInst = null;
return false;
}
}
private bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst,
ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg)
{

Loading…
Cancel
Save