From 02dde92bcbbf8d57ad9096a56db955880eba08e8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 1 Jun 2018 21:37:11 +0200 Subject: [PATCH] Improve use of dynamic expressions in conditional context: Let "operator true" be invoked implicitly where possible. --- .../CSharp/ExpressionBuilder.cs | 14 +++++++++----- .../CSharp/OutputVisitor/CSharpOutputVisitor.cs | 2 +- .../OutputVisitor/InsertParenthesesVisitor.cs | 16 +++++++++++++--- .../Expressions/UnaryOperatorExpression.cs | 5 +++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 7afc472a2..fae864e50 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2527,12 +2527,16 @@ namespace ICSharpCode.Decompiler.CSharp return CreateUnaryOperator(UnaryOperatorType.Plus); case ExpressionType.IsTrue: var operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); - if (IfInstruction.IsInConditionSlot(inst)) { - // TODO + Expression expr; + if (inst.SlotInfo == IfInstruction.ConditionSlot) { + // We rely on the context implicitly invoking "operator true". + expr = new UnaryOperatorExpression(UnaryOperatorType.IsTrue, operand); + } else { + // Create a dummy conditional to ensure "operator true" will be invoked. + expr = new ConditionalExpression(operand, new PrimitiveExpression(true), new PrimitiveExpression(false)); } - return new ConditionalExpression(operand.Expression, new PrimitiveExpression(true), new PrimitiveExpression(false)) - .WithILInstruction(inst) - .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); + return expr.WithILInstruction(inst) + .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); default: return base.VisitDynamicUnaryOperatorInstruction(inst, context); } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 1e471358b..6ee369c5d 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1102,7 +1102,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); if (opType == UnaryOperatorType.Await) { WriteKeyword(opSymbol); - } else if (!IsPostfixOperator(opType) && opType != UnaryOperatorType.NullConditionalRewrap) { + } else if (!IsPostfixOperator(opType) && opSymbol != null) { WriteToken(opSymbol); } unaryOperatorExpression.Expression.AcceptVisitor(this); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 7d2345283..464538792 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -64,6 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return Primary; case UnaryOperatorType.NullConditionalRewrap: return NullableRewrap; + case UnaryOperatorType.IsTrue: + return Conditional; default: return Unary; } @@ -264,12 +266,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor if (InsertParenthesesForReadability && precedence < Equality) { // In readable mode, boost the priority of the left-hand side if the operator // there isn't the same as the operator on this expression. + int boostTo = IsBitwise(binaryOperatorExpression.Operator) ? Unary : Equality; if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) { ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); } else { - ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality); + ParenthesizeIfRequired(binaryOperatorExpression.Left, boostTo); } - ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality); + ParenthesizeIfRequired(binaryOperatorExpression.Right, boostTo); } else { // all other binary operators are left-associative ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); @@ -278,7 +281,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } base.VisitBinaryOperatorExpression(binaryOperatorExpression); } - + + static bool IsBitwise(BinaryOperatorType op) + { + return op == BinaryOperatorType.BitwiseAnd + || op == BinaryOperatorType.BitwiseOr + || op == BinaryOperatorType.ExclusiveOr; + } + BinaryOperatorType? GetBinaryOperatorType(Expression expr) { BinaryOperatorExpression boe = expr as BinaryOperatorExpression; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs index 551ba610c..5e7b5824f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs @@ -117,6 +117,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax case UnaryOperatorType.NullConditional: return NullConditionalRole; case UnaryOperatorType.NullConditionalRewrap: + case UnaryOperatorType.IsTrue: return null; // no syntax default: throw new NotSupportedException("Invalid value for UnaryOperatorType"); @@ -193,5 +194,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// This has no syntax in C#, but the node is used to ensure parentheses are inserted where necessary. /// NullConditionalRewrap, + /// + /// Implicit call of "operator true". + /// + IsTrue, } }