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 @@ -4,31 +4,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class PatternMatching
{
public bool SimpleTypePattern(object x)
public void SimpleTypePattern(object x)
{
Use(x is string y);
if (x is string z)
if (x is string value)
{
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"));
if (F() && x is string z && z.Contains("a"))
Use(F() && x is string text && text.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
{
@ -36,35 +33,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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();
}
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();
}
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
{
@ -72,11 +69,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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
{
@ -84,11 +81,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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
{
@ -96,36 +93,35 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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();
}
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();
}
else
{
Console.WriteLine(z);
Console.WriteLine(value);
}
}
#if CS71
public void SimpleTypePatternGenerics<T>(object x)
public void TypePatternGenerics<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
{
@ -133,11 +129,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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
{
@ -145,24 +141,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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
{
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
{
@ -170,9 +165,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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");
}
@ -182,9 +177,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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");
}
@ -194,9 +189,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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()

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

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
#nullable enable
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class PatternMatchingRefTypesTransform : IStatementTransform
@ -41,14 +43,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -41,14 +43,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!context.Settings.PatternMatching)
return;
int startPos = pos;
if (block.Instructions[pos] is not StLoc
{
Variable: var s,
Value: IsInst { Argument: var testedOperand, Type: var type }
})
{
if (!block.Instructions[pos].MatchStLoc(out var s, out var value))
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)
return;
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 @@ -28,26 +28,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
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 {
/// ...
/// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock
@ -77,28 +57,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -77,28 +57,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
continue;
}
if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, falseBlock,
out var v, out var nextCondition, out var trueBlock, out var inverseNextCondition))
if (!MatchUnboxBlock(unboxBlock, type, testedOperand.Variable, out var v))
{
continue;
}
context.Step($"PatternMatching with {v.Name}", block);
if (inverseNextCondition)
{
nextCondition = Comp.LogicNot(nextCondition);
}
var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!;
ILInstruction logicAnd = IfInstruction.LogicAnd(new MatchInstruction(v, testedOperand) {
ifInst.Condition = new MatchInstruction(v, testedOperand) {
CheckNotNull = true,
CheckType = true
}, nextCondition);
ifInst.Condition = logicAnd;
((Branch)ifInst.TrueInst).TargetBlock = trueBlock;
};
((Branch)ifInst.TrueInst).TargetBlock = unboxBlock;
((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;
}
container.Blocks.RemoveAll(b => b.Instructions.Count == 0);
}
}
@ -140,20 +117,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -140,20 +117,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Block unboxBlock (incoming: 1) {
/// stloc V(unbox.any T(ldloc testedOperand))
/// if (nextCondition) br trueBlock
/// br falseBlock
/// ...
/// }
private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand, Block falseBlock,
[NotNullWhen(true)] out ILVariable? v,
[NotNullWhen(true)] out ILInstruction? nextCondition,
[NotNullWhen(true)] out Block? trueBlock,
out bool inverseCondition)
private bool MatchUnboxBlock(Block unboxBlock, IType type, ILVariable testedOperand,
[NotNullWhen(true)] out ILVariable? v)
{
v = null;
nextCondition = null;
trueBlock = null;
inverseCondition = false;
if (unboxBlock.IncomingEdgeCount != 1 || unboxBlock.Instructions.Count != 3)
if (unboxBlock.IncomingEdgeCount != 1)
return false;
if (!unboxBlock.Instructions[0].MatchStLoc(out v, out var value))
@ -161,22 +131,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -161,22 +131,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type) && arg.MatchLdLoc(testedOperand)))
return false;
if (!unboxBlock.MatchIfAtEndOfBlock(out nextCondition, out var trueInst, out var falseInst))
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;
}
return true;
}
}
}

Loading…
Cancel
Save