com.strobel.expressions.Optimizer Maven / Gradle / Ivy
/*
* Optimizer.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is based on the Dynamic Language Runtime from Microsoft,
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.expressions;
import com.strobel.reflection.PrimitiveTypes;
import com.strobel.reflection.Type;
import com.strobel.util.TypeUtils;
import static com.strobel.expressions.Expression.*;
/**
* @author strobelm
*/
final class Optimizer extends ExpressionVisitor {
private final static Optimizer OPTIMIZER = new Optimizer();
static Expression optimize(final Expression node) {
return OPTIMIZER.visit(node);
}
static LambdaExpression optimize(final LambdaExpression node) {
return OPTIMIZER.visitLambda(node);
}
@Override
protected Expression visitBinary(final BinaryExpression node) {
Expression reduced = reduceNullConstantComparison(node);
if (reduced != null) {
return visit(reduced);
}
reduced = reduceBooleanConstantComparison(node);
if (reduced != null) {
return visit(reduced);
}
return super.visitBinary(node);
}
@Override
protected Expression visitUnary(final UnaryExpression node) {
Expression reduced = reduceNullConstantCheck(node);
if (reduced != null) {
return visit(reduced);
}
reduced = reduceDoubleNot(node);
if (reduced != null) {
return visit(reduced);
}
return super.visitUnary(node);
}
private Expression reduceNullConstantCheck(final UnaryExpression node) {
final Expression operand = node.getOperand();
final ExpressionType nodeType = node.getNodeType();
final ExpressionType operandNodeType = operand.getNodeType();
if (nodeType == ExpressionType.IsNull) {
if (ConstantCheck.isNull(operand)) {
if (operandNodeType == ExpressionType.Parameter ||
operandNodeType == ExpressionType.Constant) {
return constant(Boolean.TRUE);
}
return block(operand, constant(Boolean.TRUE));
}
}
else if (nodeType == ExpressionType.IsNotNull) {
if (ConstantCheck.isNull(operand)) {
if (operandNodeType == ExpressionType.Parameter ||
operandNodeType == ExpressionType.Constant) {
return constant(Boolean.FALSE);
}
return block(operand, constant(Boolean.FALSE));
}
}
return null;
}
private Expression reduceDoubleNot(final UnaryExpression node) {
final Type> type = node.getType();
final Expression operand = node.getOperand();
if (type != PrimitiveTypes.Boolean || operand.getType() != PrimitiveTypes.Boolean) {
return null;
}
final ExpressionType nodeType = node.getNodeType();
final ExpressionType operandNodeType = operand.getNodeType();
if ((nodeType == ExpressionType.Not || nodeType == ExpressionType.IsFalse) &&
(operandNodeType == ExpressionType.Not || operandNodeType == ExpressionType.IsFalse)) {
return ((UnaryExpression)operand).getOperand();
}
return null;
}
private Expression reduceNullConstantComparison(final BinaryExpression node) {
final Expression left = visit(node.getLeft());
final Expression right = visit(node.getRight());
if (node.getType() != PrimitiveTypes.Boolean) {
return null;
}
final ExpressionType nodeType = node.getNodeType();
switch (nodeType) {
case Equal:
case NotEqual:
case ReferenceEqual:
case ReferenceNotEqual:
break;
default:
return null;
}
if (ConstantCheck.isNull(right)) {
if (ConstantCheck.isNull(left)) {
return constant(nodeType == ExpressionType.Equal || nodeType == ExpressionType.ReferenceEqual);
}
return nodeType == ExpressionType.Equal || nodeType == ExpressionType.ReferenceEqual
? isNull(left)
: isNotNull(left);
}
if (ConstantCheck.isNull(left)) {
return nodeType == ExpressionType.Equal || nodeType == ExpressionType.ReferenceEqual
? isNull(right)
: isNotNull(right);
}
return null;
}
private Expression reduceBooleanConstantComparison(final BinaryExpression node) {
final Expression left = visit(node.getLeft());
final Expression right = visit(node.getRight());
final ExpressionType nodeType = node.getNodeType();
if (node.getType() != PrimitiveTypes.Boolean ||
nodeType != ExpressionType.Equal && nodeType != ExpressionType.NotEqual) {
return null;
}
if (ConstantCheck.isTrue(right)) {
// true [op] true
if (ConstantCheck.isTrue(left)) {
return constant(nodeType == ExpressionType.Equal);
}
// false [op] true
if (ConstantCheck.isFalse(left)) {
return constant(nodeType == ExpressionType.NotEqual);
}
// expr [op] true
if (TypeUtils.getUnderlyingPrimitiveOrSelf(left.getType()) == PrimitiveTypes.Boolean) {
return nodeType == ExpressionType.Equal ? left : isFalse(left);
}
return null;
}
if (ConstantCheck.isFalse(right)) {
// false [op] false
if (ConstantCheck.isFalse(left)) {
return constant(nodeType == ExpressionType.Equal);
}
// true [op] false
if (ConstantCheck.isTrue(left)) {
return constant(nodeType == ExpressionType.NotEqual);
}
// expr [op] false
if (TypeUtils.getUnderlyingPrimitiveOrSelf(left.getType()) == PrimitiveTypes.Boolean) {
return nodeType == ExpressionType.Equal ? isFalse(left) : left;
}
return null;
}
if (ConstantCheck.isTrue(left)) {
// true [op] expr
if (TypeUtils.getUnderlyingPrimitiveOrSelf(right.getType()) == PrimitiveTypes.Boolean) {
return nodeType == ExpressionType.Equal ? right : isFalse(right);
}
}
else if (ConstantCheck.isFalse(left)) {
// false [op] expr
if (TypeUtils.getUnderlyingPrimitiveOrSelf(right.getType()) == PrimitiveTypes.Boolean) {
return nodeType == ExpressionType.NotEqual ? right : isFalse(right);
}
}
return null;
}
}