diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 4bd62a7f1..6d512e570 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -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)) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index cc307247d..e09216067 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -113,7 +113,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms } #endregion - #region Lift / DoLift + #region Main lifting logic + /// + /// Main entry point for lifting; called by both the expression-transform + /// and the block transform. + /// ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst) { ILInstruction condition = ifInst.Condition; @@ -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 } } 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 ) { 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 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; + } + + /// + /// Represents either non-lifted IL `Comp` or a call to one of the (non-lifted) 6 comparison operators on `System.Decimal`. + /// + 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 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 /// This means unlike LiftNormal(), we cannot rely on the input instruction not being evaluated if /// a variable is null. /// - 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 // 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 } return null; } + #endregion + #region LiftNormal / DoLift /// /// Performs nullable lifting. ///