Browse Source

Add support for simple constant patterns.

pull/3049/head
Siegfried Pammer 3 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. 37
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -310,6 +310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
#if CS80
public void RecursivePattern_Type(object x) public void RecursivePattern_Type(object x)
{ {
if (x is X { C: string text }) if (x is X { C: string text })
@ -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() private bool F()
{ {
return true; return true;

31
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -4546,14 +4546,14 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
left = left.UnwrapChild(castExpr.Expression); left = left.UnwrapChild(castExpr.Expression);
} }
var right = TranslatePattern(inst); var right = TranslatePattern(inst, left.Type);
return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right) return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)))
.WithILInstruction(inst); .WithILInstruction(inst);
} }
ExpressionWithILInstruction TranslatePattern(ILInstruction pattern) ExpressionWithILInstruction TranslatePattern(ILInstruction pattern, IType leftHandType)
{ {
switch (pattern) switch (pattern)
{ {
@ -4590,7 +4590,7 @@ namespace ICSharpCode.Decompiler.CSharp
continue; continue;
} }
recursivePatternExpression.SubPatterns.Add( 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)) .WithRR(new MemberResolveResult(null, member))
); );
} }
@ -4616,6 +4616,31 @@ namespace ICSharpCode.Decompiler.CSharp
return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type)) return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type))
.WithILInstruction(matchInstruction); .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: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }

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

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

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

@ -183,17 +183,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container) 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; 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 // if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037 // 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)) if (MatchInstruction.IsPatternMatch(condition, out var operand))
{ {
@ -212,18 +212,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst)) if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
{
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
{ {
return false; return false;
} }
ExtensionMethods.Swap(ref trueInst, ref falseInst);
condition = Comp.LogicNot(condition);
}
context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(condition); parentPattern.SubPatterns.Add(condition);
block.Instructions.RemoveAt(0); block.Instructions.Clear();
block.Instructions[0] = trueInst; block.Instructions.Add(trueInst);
return true; return true;
} }
} }
return false; 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, private 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)
{ {

Loading…
Cancel
Save