Optimize a ? true/false : b and a ? b : true/false patterns coming from CASE
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/OptimizeVisitor.java b/src/main/java/net/hydromatic/linq4j/expressions/OptimizeVisitor.java
index e0567eb..42e87b1 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/OptimizeVisitor.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/OptimizeVisitor.java
@@ -17,8 +17,9 @@
*/
package net.hydromatic.linq4j.expressions;
-import java.util.ArrayList;
-import java.util.List;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
import static net.hydromatic.linq4j.expressions.ExpressionType.Equal;
import static net.hydromatic.linq4j.expressions.ExpressionType.NotEqual;
@@ -42,6 +43,21 @@
Expressions.field(null, Boolean.class, "TRUE");
public static final Statement EMPTY_STATEMENT = Expressions.statement(null);
+ private static final Set<Method> KNOWN_NON_NULL_METHODS
+ = new HashSet<Method>();
+
+ static {
+ for (Class aClass : new Class[]{Boolean.class, Byte.class, Short.class,
+ Integer.class, Long.class, String.class}) {
+ for (Method method : aClass.getMethods()) {
+ if ("valueOf".equals(method.getName())
+ && Modifier.isStatic(method.getModifiers())) {
+ KNOWN_NON_NULL_METHODS.add(method);
+ }
+ }
+ }
+ }
+
@Override
public Expression visit(
TernaryExpression ternary,
@@ -71,6 +87,26 @@
}
}
+ // a ? true : b === a || b
+ // a ? false : b === !a && b
+ always = always(expression1);
+ if (always != null && isKnownNotNull(expression2)) {
+ return (always
+ ? Expressions.orElse(expression0, expression2)
+ : Expressions.andAlso(Expressions.not(expression0),
+ expression2)).accept(this);
+ }
+
+ // a ? b : true === !a || b
+ // a ? b : false === a && b
+ always = always(expression2);
+ if (always != null && isKnownNotNull(expression1)) {
+ return (always
+ ? Expressions.orElse(Expressions.not(expression0),
+ expression1)
+ : Expressions.andAlso(expression0, expression1)).accept(this);
+ }
+
if (expression0 instanceof BinaryExpression
&& (expression0.getNodeType() == ExpressionType.Equal
|| expression0.getNodeType() == ExpressionType.NotEqual)) {
@@ -308,6 +344,21 @@
}
/**
+ * Verifies if the expression always returns non-null result.
+ * For instance, primitive types cannot contain null values.
+ *
+ * @param expression expression to test
+ * @return true when the expression is known to be not-null
+ */
+ protected boolean isKnownNotNull(Expression expression) {
+ return Primitive.is(expression.getType())
+ || always(expression) != null
+ || (expression instanceof MethodCallExpression
+ && KNOWN_NON_NULL_METHODS.contains(((MethodCallExpression)
+ expression).method));
+ }
+
+ /**
* Treats two expressions equal even if they represent different null types
*/
private static boolean eq(Expression a, Expression b) {
diff --git a/src/test/java/net/hydromatic/linq4j/test/BlockBuilderBase.java b/src/test/java/net/hydromatic/linq4j/test/BlockBuilderBase.java
index f4f9750..4565143 100644
--- a/src/test/java/net/hydromatic/linq4j/test/BlockBuilderBase.java
+++ b/src/test/java/net/hydromatic/linq4j/test/BlockBuilderBase.java
@@ -36,6 +36,11 @@
public static final Expression TRUE = Expressions.constant(true);
public static final Expression FALSE = Expressions.constant(false);
+ public static final Expression TRUE_B =
+ Expressions.field(null, Boolean.class, "TRUE");
+ public static final Expression FALSE_B =
+ Expressions.field(null, Boolean.class, "FALSE");
+
public static String optimize(Expression expr) {
return optimize(Expressions.return_(null, expr));
}
diff --git a/src/test/java/net/hydromatic/linq4j/test/OptimizerTest.java b/src/test/java/net/hydromatic/linq4j/test/OptimizerTest.java
index c840843..5d73fc2 100644
--- a/src/test/java/net/hydromatic/linq4j/test/OptimizerTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/OptimizerTest.java
@@ -83,6 +83,61 @@
}
@Test
+ public void optimizeTernaryAtrueB() {
+ // a ? true : b === a || b
+ assertEquals("{\n return a || b;\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ TRUE, Expressions.parameter(boolean.class, "b"))));
+ }
+
+ @Test
+ public void optimizeTernaryAtrueNull() {
+ // a ? Boolean.TRUE : null === a ? Boolean.TRUE : (Boolean) null
+ assertEquals("{\n return a ? Boolean.TRUE : (Boolean) null;\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ TRUE_B, Expressions.constant(null, Boolean.class))));
+ }
+
+ @Test
+ public void optimizeTernaryAtrueBoxed() {
+ // a ? Boolean.TRUE : Boolean.valueOf(b) === a || b
+ assertEquals("{\n return a || Boolean.valueOf(b);\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ TRUE_B, Expressions.call(Boolean.class, "valueOf",
+ Expressions.parameter(boolean.class, "b")))));
+ }
+
+ @Test
+ public void optimizeTernaryABtrue() {
+ // a ? b : true === !a || b
+ assertEquals("{\n return !a || b;\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ Expressions.parameter(boolean.class, "b"), TRUE)));
+ }
+
+ @Test
+ public void optimizeTernaryAfalseB() {
+ // a ? false : b === !a && b
+ assertEquals("{\n return !a && b;\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ FALSE, Expressions.parameter(boolean.class, "b"))));
+ }
+
+ @Test
+ public void optimizeTernaryABfalse() {
+ // a ? b : false === a && b
+ assertEquals("{\n return a && b;\n}\n",
+ optimize(Expressions.condition(
+ Expressions.parameter(boolean.class, "a"),
+ Expressions.parameter(boolean.class, "b"), FALSE)));
+ }
+
+ @Test
public void optimizeTernaryInEqualABCeqB() {
// (v ? (Integer) null : inp0_) == null
assertEquals("{\n return v || inp0_ == null;\n}\n",
@@ -115,7 +170,7 @@
@Test
public void optimizeTernaryAeqBAB() {
- // a == b ? b : a
+ // a == b ? a : b
ParameterExpression a = Expressions.parameter(boolean.class, "a");
ParameterExpression b = Expressions.parameter(boolean.class, "b");
assertEquals("{\n return b;\n}\n",