Browse Source

Added support for isinst-unbox.any pattern with generic reference types and simplified value types pattern detection.

pull/2461/head
Siegfried Pammer 4 years ago
parent
commit
5fa8201533
  1. 103
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
  2. 25
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs
  3. 73
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

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

@ -4,31 +4,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public class PatternMatching public class PatternMatching
{ {
public bool SimpleTypePattern(object x) public void SimpleTypePattern(object x)
{ {
Use(x is string y); if (x is string value)
if (x is string z)
{ {
Console.WriteLine(z); Console.WriteLine(value);
} }
return x is string w;
} }
public bool SimpleTypePatternWithShortcircuit(object x) public void TypePatternWithShortcircuit(object x)
{ {
Use(F() && x is string y && y.Contains("a")); Use(F() && x is string text && text.Contains("a"));
if (F() && x is string z && z.Contains("a")) if (F() && x is string text2 && text2.Contains("a"))
{ {
Console.WriteLine(z); Console.WriteLine(text2);
} }
return F() && x is string w && w.Contains("a");
} }
public void SimpleTypePatternWithShortcircuitAnd(object x) public void TypePatternWithShortcircuitAnd(object x)
{ {
if (x is string z && z.Contains("a")) if (x is string text && text.Contains("a"))
{ {
Console.WriteLine(z); Console.WriteLine(text);
} }
else else
{ {
@ -36,35 +33,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternWithShortcircuitOr(object x) public void TypePatternWithShortcircuitOr(object x)
{ {
if (!(x is string z) || z.Contains("a")) if (!(x is string text) || text.Contains("a"))
{ {
Console.WriteLine(); Console.WriteLine();
} }
else else
{ {
Console.WriteLine(z); Console.WriteLine(text);
} }
} }
public void SimpleTypePatternWithShortcircuitOr2(object x) public void TypePatternWithShortcircuitOr2(object x)
{ {
if (F() || !(x is string z)) if (F() || !(x is string value))
{ {
Console.WriteLine(); Console.WriteLine();
} }
else else
{ {
Console.WriteLine(z); Console.WriteLine(value);
} }
} }
public void SimpleTypePatternValueTypesCondition(object x) public void TypePatternValueTypesCondition(object x)
{ {
if (x is int i) if (x is int num)
{ {
Console.WriteLine("Integer: " + i); Console.WriteLine("Integer: " + num);
} }
else else
{ {
@ -72,11 +69,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypesCondition2() public void TypePatternValueTypesCondition2()
{ {
if (GetObject() is int i) if (GetObject() is int num)
{ {
Console.WriteLine("Integer: " + i); Console.WriteLine("Integer: " + num);
} }
else else
{ {
@ -84,11 +81,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypesWithShortcircuitAnd(object x) public void TypePatternValueTypesWithShortcircuitAnd(object x)
{ {
if (x is int i && i.GetHashCode() > 0) if (x is int num && num.GetHashCode() > 0)
{ {
Console.WriteLine("Positive integer: " + i); Console.WriteLine("Positive integer: " + num);
} }
else else
{ {
@ -96,36 +93,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypesWithShortcircuitOr(object x) public void TypePatternValueTypesWithShortcircuitOr(object x)
{ {
if (!(x is int z) || z.GetHashCode() > 0) if (!(x is int value) || value.GetHashCode() > 0)
{ {
Console.WriteLine(); Console.WriteLine();
} }
else else
{ {
Console.WriteLine(z); Console.WriteLine(value);
} }
} }
public void SimpleTypePatternValueTypesWithShortcircuitOr2(object x) public void TypePatternValueTypesWithShortcircuitOr2(object x)
{ {
if (F() || !(x is int z)) if (F() || !(x is int value))
{ {
Console.WriteLine(); Console.WriteLine();
} }
else else
{ {
Console.WriteLine(z); Console.WriteLine(value);
} }
} }
#if CS71 public void TypePatternGenerics<T>(object x)
public void SimpleTypePatternGenerics<T>(object x)
{ {
if (x is T t) if (x is T val)
{ {
Console.WriteLine(typeof(T).FullName + ": " + t); Console.WriteLine(val.GetType().FullName);
} }
else else
{ {
@ -133,11 +129,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternGenericRefType<T>(object x) where T : class public void TypePatternGenericRefType<T>(object x) where T : class
{ {
if (x is T t) if (x is T val)
{ {
Console.WriteLine(typeof(T).FullName + ": " + t); Console.WriteLine(val.GetType().FullName);
} }
else else
{ {
@ -145,24 +141,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternGenericValType<T>(object x) where T : struct public void TypePatternGenericValType<T>(object x) where T : struct
{ {
if (x is T t) if (x is T val)
{ {
Console.WriteLine(typeof(T).FullName + ": " + t); Console.WriteLine(val.GetType().FullName);
} }
else else
{ {
Console.WriteLine("not a " + typeof(T).FullName); Console.WriteLine("not a " + typeof(T).FullName);
} }
} }
#endif
public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse(object x) public void TypePatternValueTypesWithShortcircuitAndMultiUse(object x)
{ {
if (x is int i && i.GetHashCode() > 0 && i % 2 == 0) if (x is int num && num.GetHashCode() > 0 && num % 2 == 0)
{ {
Console.WriteLine("Positive integer: " + i); Console.WriteLine("Positive integer: " + num);
} }
else else
{ {
@ -170,9 +165,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse2(object x) public void TypePatternValueTypesWithShortcircuitAndMultiUse2(object x)
{ {
if ((x is int i && i.GetHashCode() > 0 && i % 2 == 0) || F()) if ((x is int num && num.GetHashCode() > 0 && num % 2 == 0) || F())
{ {
Console.WriteLine("true"); Console.WriteLine("true");
} }
@ -182,9 +177,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypesWithShortcircuitAndMultiUse3(object x) public void TypePatternValueTypesWithShortcircuitAndMultiUse3(object x)
{ {
if (F() || (x is int i && i.GetHashCode() > 0 && i % 2 == 0)) if (F() || (x is int num && num.GetHashCode() > 0 && num % 2 == 0))
{ {
Console.WriteLine("true"); Console.WriteLine("true");
} }
@ -194,9 +189,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
public void SimpleTypePatternValueTypes() public void TypePatternValueTypes()
{ {
Use(F() && GetObject() is int y && y.GetHashCode() > 0 && y % 2 == 0); Use(F() && GetObject() is int num && num.GetHashCode() > 0 && num % 2 == 0);
} }
private bool F() private bool F()

25
ICSharpCode.Decompiler/IL/Transforms/PatternMatchingRefTypesTransform.cs

@ -18,6 +18,8 @@
#nullable enable #nullable enable
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class PatternMatchingRefTypesTransform : IStatementTransform class PatternMatchingRefTypesTransform : IStatementTransform
@ -41,14 +43,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!context.Settings.PatternMatching) if (!context.Settings.PatternMatching)
return; return;
int startPos = pos; int startPos = pos;
if (block.Instructions[pos] is not StLoc if (!block.Instructions[pos].MatchStLoc(out var s, out var value))
{
Variable: var s,
Value: IsInst { Argument: var testedOperand, Type: var type }
})
{
return; return;
IType? unboxType;
if (value is UnboxAny unboxAny)
{
// stloc S(unbox.any T(isinst T(testedOperand)))
unboxType = unboxAny.Type;
value = unboxAny.Argument;
} }
else
{
unboxType = null;
}
if (value is not IsInst { Argument: var testedOperand, Type: var type })
return;
if (type.IsReferenceType != true)
return;
if (!(unboxType == null || type.Equals(unboxType)))
return;
if (!s.IsSingleDefinition) if (!s.IsSingleDefinition)
return; return;
if (s.Kind is not (VariableKind.Local or VariableKind.StackSlot)) if (s.Kind is not (VariableKind.Local or VariableKind.StackSlot))

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

@ -28,26 +28,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class PatternMatchingTransform : IILTransform class PatternMatchingTransform : IILTransform
{ {
/// Block {
/// ...
/// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock
/// br unboxBlock
/// }
///
/// Block unboxBlock (incoming: 1) {
/// stloc V(unbox.any T(ldloc testedOperand))
/// if (nextCondition) br trueBlock
/// br falseBlock
/// }
/// =>
/// Block {
/// ...
/// if (logic.and(match.type[T].notnull(V = testedOperand), nextCondition)) br trueBlock
/// br falseBlock
/// }
///
/// -or-
/// Block { /// Block {
/// ... /// ...
/// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock /// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock
@ -77,28 +57,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
continue; continue;
} }
if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, falseBlock, if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, out var v))
out var v, out var nextCondition, out var trueBlock, out var inverseNextCondition))
{ {
continue; continue;
} }
context.Step($"PatternMatching with {v.Name}", block); context.Step($"PatternMatching with {v.Name}", block);
if (inverseNextCondition)
{
nextCondition = Comp.LogicNot(nextCondition);
}
var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!;
ILInstruction logicAnd = IfInstruction.LogicAnd(new MatchInstruction(v, testedOperand) { ifInst.Condition = new MatchInstruction(v, testedOperand) {
CheckNotNull = true, CheckNotNull = true,
CheckType = true CheckType = true
}, nextCondition); };
ifInst.Condition = logicAnd; ((Branch)ifInst.TrueInst).TargetBlock = unboxBlock;
((Branch)ifInst.TrueInst).TargetBlock = trueBlock;
((Branch)block.Instructions.Last()).TargetBlock = falseBlock; ((Branch)block.Instructions.Last()).TargetBlock = falseBlock;
unboxBlock.Instructions.Clear(); unboxBlock.Instructions.RemoveAt(0);
// HACK: condition detection uses StartILOffset of blocks to decide which branch of if-else
// should become the then-branch. Change the unboxBlock StartILOffset from an offset inside
// the pattern matching machinery to an offset belonging to an instruction in the then-block.
unboxBlock.SetILRange(unboxBlock.Instructions[0]);
v.Kind = VariableKind.PatternLocal; v.Kind = VariableKind.PatternLocal;
} }
container.Blocks.RemoveAll(b => b.Instructions.Count == 0);
} }
} }
@ -140,20 +117,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Block unboxBlock (incoming: 1) { /// Block unboxBlock (incoming: 1) {
/// stloc V(unbox.any T(ldloc testedOperand)) /// stloc V(unbox.any T(ldloc testedOperand))
/// if (nextCondition) br trueBlock /// ...
/// br falseBlock
/// } /// }
private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand, Block falseBlock, private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand,
[NotNullWhen(true)] out ILVariable? v, [NotNullWhen(true)] out ILVariable? v)
[NotNullWhen(true)] out ILInstruction? nextCondition,
[NotNullWhen(true)] out Block? trueBlock,
out bool inverseCondition)
{ {
v = null; v = null;
nextCondition = null; if (unboxBlock.IncomingEdgeCount != 1)
trueBlock = null;
inverseCondition = false;
if (unboxBlock.IncomingEdgeCount != 1 || unboxBlock.Instructions.Count != 3)
return false; return false;
if (!unboxBlock.Instructions[0].MatchStLoc(out v, out var value)) if (!unboxBlock.Instructions[0].MatchStLoc(out v, out var value))
@ -161,22 +131,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type) && arg.MatchLdLoc(testedOperand))) if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type) && arg.MatchLdLoc(testedOperand)))
return false; return false;
if (!unboxBlock.MatchIfAtEndOfBlock(out nextCondition, out var trueInst, out var falseInst)) return true;
return false;
if (trueInst.MatchBranch(out trueBlock) && falseInst.MatchBranch(falseBlock))
{
return true;
}
else if (trueInst.MatchBranch(falseBlock) && falseInst.MatchBranch(out trueBlock))
{
inverseCondition = true;
return true;
}
else
{
return false;
}
} }
} }
} }

Loading…
Cancel
Save