Browse Source

[nullables] support equality comparisons on System.Decimal

pull/892/head
Daniel Grunwald 8 years ago
parent
commit
33e5011367
  1. 4
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  2. 99
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

4
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -285,8 +285,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -285,8 +285,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
name = "array";
} else if (type is PointerType) {
name = "ptr";
} else if (type.Kind == TypeKind.TypeParameter) {
} else if (type.Kind == TypeKind.TypeParameter || type.Kind == TypeKind.Unknown) {
name = "val";
} else if (type.Kind == TypeKind.ByReference) {
name = "reference";
} else if (type.IsAnonymousType()) {
name = "anon";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {

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

@ -113,7 +113,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -113,7 +113,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
#endregion
#region Lift / DoLift
#region Main lifting logic
/// <summary>
/// Main entry point for lifting; called by both the expression-transform
/// and the block transform.
/// </summary>
ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst)
{
ILInstruction condition = ifInst.Condition;
@ -126,7 +130,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -126,7 +130,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => normal lifting
return LiftNormal(trueInst, falseInst, ilrange: ifInst.ILRange);
}
if (condition is Comp comp && !comp.IsLifted) {
if (MatchCompOrDecimal(condition, out var comp)) {
// This might be a C#-style lifted comparison
// (C# checks the underlying value before checking the HasValue bits)
if (comp.Kind.IsEqualityOrInequality()) {
@ -164,6 +168,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -164,6 +168,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
ILVariable v;
// Handle equality comparisons with bool?:
if (MatchGetValueOrDefault(condition, out v)
&& NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean))
{
@ -205,6 +210,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -205,6 +210,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
) { ILRange = ifInst.ILRange };
}
}
// Handle & and | on bool?:
if (trueInst.MatchLdLoc(out v)) {
if (MatchNullableCtor(falseInst, out var utype, out var arg)
&& utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(0))
@ -281,8 +287,81 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -281,8 +287,81 @@ namespace ICSharpCode.Decompiler.IL.Transforms
a = b;
b = tmp;
}
#endregion
Comp LiftCSharpEqualityComparison(Comp valueComp, ComparisonKind newComparisonKind, ILInstruction hasValueTest)
#region CSharpComp
static bool MatchCompOrDecimal(ILInstruction inst, out CompOrDecimal result)
{
result = default(CompOrDecimal);
result.Instruction = inst;
if (inst is Comp comp && !comp.IsLifted) {
result.Kind = comp.Kind;
result.Left = comp.Left;
result.Right = comp.Right;
return true;
} else if (inst is Call call && call.Method.IsOperator && call.Arguments.Count == 2 && !call.IsLifted) {
switch (call.Method.Name) {
case "op_Equality":
result.Kind = ComparisonKind.Equality;
break;
case "op_Inequality":
result.Kind = ComparisonKind.Inequality;
break;
case "op_LessThan":
result.Kind = ComparisonKind.LessThan;
break;
case "op_LessThanOrEqual":
result.Kind = ComparisonKind.LessThanOrEqual;
break;
case "op_GreaterThan":
result.Kind = ComparisonKind.GreaterThan;
break;
case "op_GreaterThanOrEqual":
result.Kind = ComparisonKind.GreaterThanOrEqual;
break;
default:
return false;
}
result.Left = call.Arguments[0];
result.Right = call.Arguments[1];
return call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal);
}
return false;
}
/// <summary>
/// Represents either non-lifted IL `Comp` or a call to one of the (non-lifted) 6 comparison operators on `System.Decimal`.
/// </summary>
struct CompOrDecimal
{
public ILInstruction Instruction;
public ComparisonKind Kind;
public ILInstruction Left;
public ILInstruction Right;
internal ILInstruction MakeLifted(ComparisonKind newComparisonKind, ILInstruction left, ILInstruction right)
{
if (Instruction is Comp comp) {
return new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, comp.InputType, comp.Sign, left, right) {
ILRange = Instruction.ILRange
};
} else if (Instruction is Call call && newComparisonKind == Kind) {
return new Call(CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method)) {
Arguments = { left, right },
ConstrainedTo = call.ConstrainedTo,
ILRange = call.ILRange,
ILStackWasEmpty = call.ILStackWasEmpty,
IsTail = call.IsTail
};
} else {
return null;
}
}
}
#endregion
#region Lift...Comparison
ILInstruction LiftCSharpEqualityComparison(CompOrDecimal valueComp, ComparisonKind newComparisonKind, ILInstruction hasValueTest)
{
Debug.Assert(newComparisonKind.IsEqualityOrInequality());
bool hasValueTestNegated = false;
@ -306,8 +385,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -306,8 +385,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (left != null && right != null && leftBits[0] && rightBits[0]
&& SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags)
) {
context.Step("NullableLiftingTransform: C# (in)equality comparison", valueComp);
return new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, valueComp.InputType, valueComp.Sign, left, right);
context.Step("NullableLiftingTransform: C# (in)equality comparison", valueComp.Instruction);
return valueComp.MakeLifted(newComparisonKind, left, right);
}
} else if (newComparisonKind == ComparisonKind.Equality && !hasValueTestNegated && MatchHasValueCall(hasValueTest, out var v)) {
// Comparing nullable with non-nullable -> we can fall back to the normal comparison code.
@ -333,7 +412,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -333,7 +412,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// This means unlike LiftNormal(), we cannot rely on the input instruction not being evaluated if
/// a variable is <c>null</c>.
/// </summary>
Comp LiftCSharpComparison(Comp comp, ComparisonKind newComparisonKind)
ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind)
{
var (left, right, bits) = DoLiftBinary(comp.Left, comp.Right);
// due to the restrictions on side effects, we only allow instructions that are pure after lifting.
@ -343,13 +422,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -343,13 +422,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// don't lift if a nullableVar doesn't contribute to the result
return null;
}
context.Step("NullableLiftingTransform: C# comparison", comp);
return new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, comp.InputType, comp.Sign, left, right);
context.Step("NullableLiftingTransform: C# comparison", comp.Instruction);
return comp.MakeLifted(newComparisonKind, left, right);
}
return null;
}
ILInstruction LiftCSharpUserEqualityComparison(Comp hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst)
Call LiftCSharpUserEqualityComparison(CompOrDecimal hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst)
{
// User-defined equality operator:
// if (comp(call get_HasValue(ldloca nullable1) == call get_HasValue(ldloca nullable2)))
@ -408,7 +487,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -408,7 +487,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return null;
}
#endregion
#region LiftNormal / DoLift
/// <summary>
/// Performs nullable lifting.
///

Loading…
Cancel
Save