summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs')
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs164
1 files changed, 164 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
new file mode 100644
index 00000000..193c5e69
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform
+ {
+ sealed class LiftedOperator { }
+ /// <summary>
+ /// Annotation for lifted operators that cannot be transformed by PushNegation
+ /// </summary>
+ public static readonly object LiftedOperatorAnnotation = new LiftedOperator();
+
+ public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data)
+ {
+ // lifted operators can't be transformed
+ if (unary.Annotation<LiftedOperator>() != null || unary.Expression.Annotation<LiftedOperator>() != null)
+ return base.VisitUnaryOperatorExpression(unary, data);
+
+ // Remove double negation
+ // !!a
+ if (unary.Operator == UnaryOperatorType.Not &&
+ unary.Expression is UnaryOperatorExpression &&
+ (unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not)
+ {
+ AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression;
+ unary.ReplaceWith(newNode);
+ return newNode.AcceptVisitor(this, data);
+ }
+
+ // Push through binary operation
+ // !((a) op (b))
+ BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression;
+ if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) {
+ bool successful = true;
+ switch (binaryOp.Operator) {
+ case BinaryOperatorType.Equality:
+ binaryOp.Operator = BinaryOperatorType.InEquality;
+ break;
+ case BinaryOperatorType.InEquality:
+ binaryOp.Operator = BinaryOperatorType.Equality;
+ break;
+ case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN)
+ binaryOp.Operator = BinaryOperatorType.LessThanOrEqual;
+ break;
+ case BinaryOperatorType.GreaterThanOrEqual:
+ binaryOp.Operator = BinaryOperatorType.LessThan;
+ break;
+ case BinaryOperatorType.LessThanOrEqual:
+ binaryOp.Operator = BinaryOperatorType.GreaterThan;
+ break;
+ case BinaryOperatorType.LessThan:
+ binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
+ break;
+ default:
+ successful = false;
+ break;
+ }
+ if (successful) {
+ unary.ReplaceWith(binaryOp);
+ return binaryOp.AcceptVisitor(this, data);
+ }
+
+ successful = true;
+ switch (binaryOp.Operator) {
+ case BinaryOperatorType.ConditionalAnd:
+ binaryOp.Operator = BinaryOperatorType.ConditionalOr;
+ break;
+ case BinaryOperatorType.ConditionalOr:
+ binaryOp.Operator = BinaryOperatorType.ConditionalAnd;
+ break;
+ default:
+ successful = false;
+ break;
+ }
+ if (successful) {
+ binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
+ binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
+ unary.ReplaceWith(binaryOp);
+ return binaryOp.AcceptVisitor(this, data);
+ }
+ }
+ return base.VisitUnaryOperatorExpression(unary, data);
+ }
+
+ readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression(
+ new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
+ BinaryOperatorType.Equality,
+ new NullReferenceExpression()
+ );
+
+ readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression(
+ new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
+ BinaryOperatorType.InEquality,
+ new NullReferenceExpression()
+ );
+
+ public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
+ {
+ // lifted operators can't be transformed
+ if (binaryOperatorExpression.Annotation<LiftedOperator>() != null)
+ return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
+
+ BinaryOperatorType op = binaryOperatorExpression.Operator;
+ bool? rightOperand = null;
+ if (binaryOperatorExpression.Right is PrimitiveExpression)
+ rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?;
+ if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) {
+ // 'b == true' or 'b != false' is useless
+ binaryOperatorExpression.Left.AcceptVisitor(this, data);
+ binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left);
+ return null;
+ } else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) {
+ // 'b == false' or 'b != true' is a negation:
+ Expression left = binaryOperatorExpression.Left;
+ left.Remove();
+ UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left);
+ binaryOperatorExpression.ReplaceWith(uoe);
+ return uoe.AcceptVisitor(this, data);
+ } else {
+ bool negate = false;
+ Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression);
+ if (!m.Success) {
+ m = asCastIsNullPattern.Match(binaryOperatorExpression);
+ negate = true;
+ }
+ if (m.Success) {
+ Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach());
+ if (negate)
+ expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr);
+ binaryOperatorExpression.ReplaceWith(expr);
+ return expr.AcceptVisitor(this, data);
+ } else {
+ return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
+ }
+ }
+ }
+ void IAstTransform.Run(AstNode node)
+ {
+ node.AcceptVisitor(this, null);
+ }
+ }
+}