From 9d22e3ab41f5817c98262a6120b04eb7c41c7a68 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 20 Jul 2024 10:40:31 +0200 Subject: [PATCH] NullableLiftingTransform: Undo new compiler optimization (omitting get_HasValue for comparisions with constants) --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 14 ++++++++++ .../Transforms/PatternStatementTransform.cs | 11 ++++++++ .../IL/Transforms/ExpressionTransforms.cs | 4 +-- .../IL/Transforms/NullableLiftingTransform.cs | 27 +++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3ceeeaf60d..39cdba5381 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -373,6 +373,20 @@ public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, return HandleImplicitConversion(method, argumentList.Arguments[0]); } + if (settings.LiftNullables && method.Name == "GetValueOrDefault" + && method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT) + && method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Boolean) + && argumentList.Length == 0) + { + argumentList.CheckNoNamedOrOptionalArguments(); + return new BinaryOperatorExpression( + target.Expression, + BinaryOperatorType.Equality, + new PrimitiveExpression(true)) + .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, + argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); + } + var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target, ref argumentList, CallTransformation.All, out IParameterizedMember foundMethod); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index f9a6050123..3605d968d7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -1167,6 +1167,17 @@ public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression e } return base.VisitBinaryOperatorExpression(expr); } + + public override AstNode VisitUnaryOperatorExpression(UnaryOperatorExpression expr) + { + if (expr.Operator == UnaryOperatorType.Not && expr.Expression is BinaryOperatorExpression { Operator: BinaryOperatorType.Equality } binary) + { + binary.Operator = BinaryOperatorType.InEquality; + expr.ReplaceWith(binary.Detach()); + return VisitBinaryOperatorExpression(binary); + } + return base.VisitUnaryOperatorExpression(expr); + } #endregion #region C# 7.3 pattern based fixed (for value types) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 5385bbaae6..b9b99ad427 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -96,8 +96,7 @@ protected internal override void VisitComp(Comp inst) return; } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None - && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp) - ) + && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp)) { // if (comp(x != 0)) ==> if (x) // comp(comp(...) != 0) => comp(...) @@ -107,6 +106,7 @@ protected internal override void VisitComp(Comp inst) inst.Left.AcceptVisitor(this); return; } + new NullableLiftingTransform(context).Run(inst); base.VisitComp(inst); if (inst.IsLifted) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 4e297b00d9..3d517e4633 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -85,6 +85,33 @@ public bool Run(BinaryNumericInstruction bni) return false; } + /// + /// VS2022.10 / Roslyn 4.10.0 adds an optimization that turns + /// a == 42 into a.GetValueOrDefault() == 42 without any HasValue check. + /// + public void Run(Comp comp) + { + if (!comp.IsLifted && comp.Kind.IsEqualityOrInequality()) + { + var left = comp.Left; + var right = comp.Right; + if (MatchGetValueOrDefault(left, out ILInstruction arg) + && right.MatchLdcI(out var value) && value != 0) + { + context.Step("comp(a.GetValueOrDefault() == const) -> comp.lifted(a == const)", comp); + comp.LiftingKind = ComparisonLiftingKind.CSharp; + comp.Left = new LdObj(arg, ((Call)left).Method.DeclaringType); + } + else if (MatchGetValueOrDefault(right, out arg) + && left.MatchLdcI(out value) && value != 0) + { + context.Step("comp(const == a.GetValueOrDefault()) -> comp.lifted(const == a)", comp); + comp.LiftingKind = ComparisonLiftingKind.CSharp; + comp.Right = new LdObj(arg, ((Call)right).Method.DeclaringType); + } + } + } + public bool RunStatements(Block block, int pos) { // e.g.: