Browse Source

Adjust NullableLiftingTransform to match new patterns due to RemoveInfeasiblePathTransform.

pull/2461/head
Siegfried Pammer 4 years ago
parent
commit
e50d221e06
  1. 2
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs
  3. 122
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

2
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -308,6 +308,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
if (flags.HasFlag(CompilerOptions.UseRoslyn2_10_0) if (flags.HasFlag(CompilerOptions.UseRoslyn2_10_0)
|| flags.HasFlag(CompilerOptions.UseRoslynLatest)) || flags.HasFlag(CompilerOptions.UseRoslynLatest))
{ {
preprocessorSymbols.Add("ROSLYN2");
preprocessorSymbols.Add("CS70"); preprocessorSymbols.Add("CS70");
preprocessorSymbols.Add("CS71"); preprocessorSymbols.Add("CS71");
preprocessorSymbols.Add("CS72"); preprocessorSymbols.Add("CS72");
@ -315,6 +316,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
} }
if (flags.HasFlag(CompilerOptions.UseRoslynLatest)) if (flags.HasFlag(CompilerOptions.UseRoslynLatest))
{ {
preprocessorSymbols.Add("ROSLYN3");
preprocessorSymbols.Add("CS73"); preprocessorSymbols.Add("CS73");
preprocessorSymbols.Add("CS80"); preprocessorSymbols.Add("CS80");
preprocessorSymbols.Add("VB16"); preprocessorSymbols.Add("VB16");

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs

@ -367,7 +367,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
Console.WriteLine(); Console.WriteLine();
} }
#if ROSLYN #if ROSLYN2
// Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b' // Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b'
if (!(a == b)) if (!(a == b))
{ {

122
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -137,6 +137,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return false; return false;
} }
bool AnalyzeNegatedCondition(ILInstruction condition)
{
return condition.MatchLogicNot(out var arg) && AnalyzeCondition(arg);
}
#endregion #endregion
#region Main lifting logic #region Main lifting logic
@ -211,11 +216,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => comp.lifted[C#](lhs, rhs) // => comp.lifted[C#](lhs, rhs)
return LiftCSharpComparison(comp, comp.Kind); return LiftCSharpComparison(comp, comp.Kind);
} }
else if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst)) if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst))
{ {
// comp(lhs, rhs) ? false : (v1 != null && ... && vn != null) // comp(lhs, rhs) ? false : (v1 != null && ... && vn != null)
return LiftCSharpComparison(comp, comp.Kind.Negate()); return LiftCSharpComparison(comp, comp.Kind.Negate());
} }
if (falseInst.MatchLdcI4(1) && AnalyzeNegatedCondition(trueInst))
{
// comp(lhs, rhs) ? !(v1 != null && ... && vn != null) : true
// => !comp.lifted[C#](lhs, rhs)
ILInstruction result = LiftCSharpComparison(comp, comp.Kind);
if (result == null)
return result;
return Comp.LogicNot(result);
}
if (trueInst.MatchLdcI4(1) && AnalyzeNegatedCondition(falseInst))
{
// comp(lhs, rhs) ? true : !(v1 != null && ... && vn != null)
ILInstruction result = LiftCSharpComparison(comp, comp.Kind.Negate());
if (result == null)
return result;
return Comp.LogicNot(result);
}
} }
} }
ILVariable v; ILVariable v;
@ -544,7 +566,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null; return null;
} }
Call LiftCSharpUserEqualityComparison(CompOrDecimal hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst) ILInstruction LiftCSharpUserEqualityComparison(CompOrDecimal hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst)
{ {
// User-defined equality operator: // User-defined equality operator:
// if (comp(call get_HasValue(ldloca nullable1) == call get_HasValue(ldloca nullable2))) // if (comp(call get_HasValue(ldloca nullable1) == call get_HasValue(ldloca nullable2)))
@ -576,11 +598,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null; return null;
if (!falseInst.MatchLdcI4(newComparisonKind == ComparisonKind.Equality ? 1 : 0)) if (!falseInst.MatchLdcI4(newComparisonKind == ComparisonKind.Equality ? 1 : 0))
return null; return null;
bool trueInstNegated = false;
while (trueInst.MatchLogicNot(out var arg))
{
trueInstNegated = !trueInstNegated;
trueInst = arg;
}
if (!(trueInst is Call call)) if (!(trueInst is Call call))
return null; return null;
if (!(call.Method.IsOperator && call.Arguments.Count == 2)) if (!(call.Method.IsOperator && call.Arguments.Count == 2))
return null; return null;
if (call.Method.Name != (newComparisonKind == ComparisonKind.Equality ? "op_Equality" : "op_Inequality")) bool expectEqualityOperator = (newComparisonKind == ComparisonKind.Equality) ^ trueInstNegated;
if (call.Method.Name != (expectEqualityOperator ? "op_Equality" : "op_Inequality"))
return null; return null;
var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method); var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
if (liftedOperator == null) if (liftedOperator == null)
@ -594,13 +623,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms
) )
{ {
context.Step("NullableLiftingTransform: C# user-defined (in)equality comparison", nestedIfInst); context.Step("NullableLiftingTransform: C# user-defined (in)equality comparison", nestedIfInst);
return new Call(liftedOperator) { ILInstruction replacement = new Call(liftedOperator) {
Arguments = { left, right }, Arguments = { left, right },
ConstrainedTo = call.ConstrainedTo, ConstrainedTo = call.ConstrainedTo,
ILStackWasEmpty = call.ILStackWasEmpty, ILStackWasEmpty = call.ILStackWasEmpty,
IsTail = call.IsTail, IsTail = call.IsTail,
}.WithILRange(call); }.WithILRange(call);
if (trueInstNegated)
{
replacement = Comp.LogicNot(replacement);
}
return replacement;
}
return null;
}
ILInstruction LiftCSharpUserComparison(ILInstruction trueInst, ILInstruction falseInst)
{
// (v1 != null && ... && vn != null) ? trueInst : falseInst
bool trueInstNegated = false;
while (trueInst.MatchLogicNot(out var arg))
{
trueInstNegated = !trueInstNegated;
trueInst = arg;
}
if (trueInst is Call call && !call.IsLifted
&& CSharp.Resolver.CSharpOperators.IsComparisonOperator(call.Method)
&& falseInst.MatchLdcI4((call.Method.Name == "op_Inequality") ^ trueInstNegated ? 1 : 0))
{
// (v1 != null && ... && vn != null) ? call op_LessThan(lhs, rhs) : ldc.i4(0)
var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
if ((call.Method.Name == "op_Equality" || call.Method.Name == "op_Inequality") && nullableVars.Count != 1)
{
// Equality is special (returns true if both sides are null), only handle it
// in the normal code path if we're dealing with only a single nullable var
// (comparing nullable with non-nullable).
return null;
}
if (liftedOperator == null)
{
return null;
}
context.Step("Lift user-defined comparison operator", trueInst);
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1],
call.Method.Parameters[0].Type, call.Method.Parameters[1].Type);
if (left != null && right != null && bits.All(0, nullableVars.Count))
{
ILInstruction result = new Call(liftedOperator) {
Arguments = { left, right },
ConstrainedTo = call.ConstrainedTo,
ILStackWasEmpty = call.ILStackWasEmpty,
IsTail = call.IsTail
}.WithILRange(call);
if (trueInstNegated)
{
result = Comp.LogicNot(result);
}
return result;
}
} }
return null; return null;
} }
#endregion #endregion
@ -641,35 +723,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType() UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType()
}; };
} }
else if (trueInst is Call call && !call.IsLifted ILInstruction result = LiftCSharpUserComparison(trueInst, falseInst);
&& CSharp.Resolver.CSharpOperators.IsComparisonOperator(call.Method) if (result != null)
&& falseInst.MatchLdcI4(call.Method.Name == "op_Inequality" ? 1 : 0)) return result;
{
// (v1 != null && ... && vn != null) ? call op_LessThan(lhs, rhs) : ldc.i4(0)
var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
if ((call.Method.Name == "op_Equality" || call.Method.Name == "op_Inequality") && nullableVars.Count != 1)
{
// Equality is special (returns true if both sides are null), only handle it
// in the normal code path if we're dealing with only a single nullable var
// (comparing nullable with non-nullable).
liftedOperator = null;
}
if (liftedOperator != null)
{
context.Step("Lift user-defined comparison operator", trueInst);
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1],
call.Method.Parameters[0].Type, call.Method.Parameters[1].Type);
if (left != null && right != null && bits.All(0, nullableVars.Count))
{
return new Call(liftedOperator) {
Arguments = { left, right },
ConstrainedTo = call.ConstrainedTo,
ILStackWasEmpty = call.ILStackWasEmpty,
IsTail = call.IsTail
}.WithILRange(call);
}
}
}
} }
ILInstruction lifted; ILInstruction lifted;
if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0])) if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))

Loading…
Cancel
Save