Browse Source

Add support for nullable int const patterns

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
cb62cac9d3
  1. 12
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  3. 2
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs
  4. 69
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -491,6 +491,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -491,6 +491,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public void RecursivePattern_NullableIntField_Const(object obj)
{
if (obj is X { NullableIntField: 42 } x)
{
Console.WriteLine("Test " + x);
}
else
{
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructProp_Const(object obj)
{
if (obj is X { NullableCustomStructProp: { I: 42, Obj: not null } nullableCustomStructProp })

2
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.IL @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.IL
}
}
public readonly ComparisonLiftingKind LiftingKind;
public ComparisonLiftingKind LiftingKind;
/// <summary>
/// Gets the stack type of the comparison inputs.

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

@ -152,7 +152,7 @@ namespace ICSharpCode.Decompiler.IL @@ -152,7 +152,7 @@ namespace ICSharpCode.Decompiler.IL
&& call.Arguments.Count == 2;
}
private static bool IsConstant(ILInstruction inst)
internal static bool IsConstant(ILInstruction inst)
{
return inst.OpCode switch {
OpCode.LdcDecimal => true,

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

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
continue;
}
}
container.Blocks.RemoveAll(b => b.Instructions.Count == 0);
}
}
@ -287,8 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -287,8 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (targetVariable.Type.IsKnownType(KnownTypeCode.NullableOfT))
{
var instructionAfterHasValueCheck = MatchNullableHasValueCheckPattern(block, varPattern, parentFalseInst, context);
return DetectPropertySubPatterns(varPattern, instructionAfterHasValueCheck, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg);
return MatchNullableHasValueCheckPattern(block, varPattern, parentFalseInst, context, ref cfg);
}
var instructionAfterNullCheck = MatchNullCheckPattern(block, varPattern, parentFalseInst, context);
@ -340,7 +340,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -340,7 +340,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return trueInst;
}
private static ILInstruction? MatchNullableHasValueCheckPattern(Block block, MatchInstruction varPattern,
ILInstruction parentFalseInst, ILTransformContext context)
ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
{
if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
@ -358,26 +358,65 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -358,26 +358,65 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
return null;
}
if (!trueBlock.Instructions[0].MatchStLoc(out var newTargetVariable, out var getValueOrDefaultCall))
if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0 && varPattern.Variable.AddressCount == 2))
{
return null;
}
if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefaultCall, varPattern.Variable))
if (trueBlock.Instructions[0].MatchStLoc(out var newTargetVariable, out var getValueOrDefaultCall)
&& NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefaultCall, varPattern.Variable))
{
return null;
context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block);
varPattern.CheckNotNull = true;
varPattern.Variable = newTargetVariable;
newTargetVariable.Kind = VariableKind.PatternLocal;
block.Instructions.Clear();
block.Instructions.Add(trueInst);
trueBlock.Instructions.RemoveAt(0);
return DetectPropertySubPatterns(varPattern, trueBlock, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg);
}
if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0 && varPattern.Variable.AddressCount == 2))
else if (MatchBlockContainingOneCondition(trueBlock, out condition, out trueInst, out falseInst))
{
if (!(condition is Comp comp
&& MatchInstruction.IsConstant(comp.Right)
&& NullableLiftingTransform.MatchGetValueOrDefault(comp.Left, varPattern.Variable)))
{
return null;
}
bool negated = false;
if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst))
{
if (!DetectExitPoints.CompatibleExitInstruction(trueInst, parentFalseInst))
{
return null;
}
ExtensionMethods.Swap(ref trueInst, ref falseInst);
negated = true;
}
if (comp.Kind == (negated ? ComparisonKind.Equality : ComparisonKind.Inequality))
{
return null;
}
context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block);
// varPattern: match (v = testedOperand)
// comp: comp.i4(call GetValueOrDefault(ldloca v) != ldc.i4 42)
// =>
// comp.i4.lifted(testedOperand != ldc.i4 42)
block.Instructions.Clear();
block.Instructions.Add(trueInst);
trueBlock.Instructions.Clear();
comp.Left = varPattern.TestedOperand;
comp.LiftingKind = ComparisonLiftingKind.CSharp;
if (negated)
{
comp = Comp.LogicNot(comp);
}
varPattern.ReplaceWith(comp);
return trueInst;
}
else
{
return null;
}
context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block);
varPattern.CheckNotNull = true;
varPattern.Variable = newTargetVariable;
newTargetVariable.Kind = VariableKind.PatternLocal;
block.Instructions.Clear();
block.Instructions.Add(trueInst);
trueBlock.Instructions.RemoveAt(0);
return trueBlock;
}
private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member)

Loading…
Cancel
Save