Browse Source

Add support for nullable structs

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
a93731ad3a
  1. 16
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 50
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -6,10 +6,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -6,10 +6,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class X
{
public int? NullableIntField;
public S? NullableCustomStructField;
public int I { get; set; }
public string Text { get; set; }
public object Obj { get; set; }
public S CustomStruct { get; set; }
public int? NullableIntProp { get; set; }
public S? NullableCustomStructProp { get; set; }
}
public struct S
@ -486,6 +490,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -486,6 +490,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("not Test");
}
}
public void RecursivePattern_NullableCustomStructProp_Const(object obj)
{
if (obj is X { NullableCustomStructProp: { I: 42, Obj: not null } nullableCustomStructProp })
{
Console.WriteLine("Test " + nullableCustomStructProp.Text);
}
else
{
Console.WriteLine("not Test");
}
}
#endif
private bool F()
{

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

@ -285,7 +285,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -285,7 +285,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
block.Instructions.RemoveAt(0);
targetVariable.Kind = VariableKind.PatternLocal;
var instructionAfterNullCheck = MatchNullCheckPattern(block, varPattern, parentFalseInst, context, ref cfg);
if (targetVariable.Type.IsKnownType(KnownTypeCode.NullableOfT))
{
var instructionAfterHasValueCheck = MatchNullableHasValueCheckPattern(block, varPattern, parentFalseInst, context);
return DetectPropertySubPatterns(varPattern, instructionAfterHasValueCheck, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg);
}
var instructionAfterNullCheck = MatchNullCheckPattern(block, varPattern, parentFalseInst, context);
if (instructionAfterNullCheck != null)
{
return DetectPropertySubPatterns(varPattern, instructionAfterNullCheck, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg);
@ -306,7 +312,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -306,7 +312,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
private static ILInstruction? MatchNullCheckPattern(Block block, MatchInstruction varPattern,
ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg)
ILInstruction parentFalseInst, ILTransformContext context)
{
if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
@ -333,6 +339,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -333,6 +339,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms
block.Instructions.Add(trueInst);
return trueInst;
}
private static ILInstruction? MatchNullableHasValueCheckPattern(Block block, MatchInstruction varPattern,
ILInstruction parentFalseInst, ILTransformContext context)
{
if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
return null;
}
if (!NullableLiftingTransform.MatchHasValueCall(condition, varPattern.Variable))
{
return null;
}
if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst))
{
return null;
}
if (!(trueInst.MatchBranch(out var trueBlock) && trueBlock.Parent == block.Parent && trueBlock.IncomingEdgeCount == 1))
{
return null;
}
if (!trueBlock.Instructions[0].MatchStLoc(out var newTargetVariable, out var getValueOrDefaultCall))
{
return null;
}
if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefaultCall, varPattern.Variable))
{
return null;
}
if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0 && varPattern.Variable.AddressCount == 2))
{
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