blob: b12aeba6d1fa6e8c7fbe74d20d8896a718304f4e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.linq4j.test;
import org.apache.calcite.linq4j.tree.BinaryExpression;
import org.apache.calcite.linq4j.tree.BlockBuilder;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.ExpressionType;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.OptimizeShuttle;
import org.apache.calcite.linq4j.tree.ParameterExpression;
import org.apache.calcite.linq4j.tree.Shuttle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.function.Function;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.FOUR;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.ONE;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.TWO;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests BlockBuilder.
*/
class BlockBuilderTest {
BlockBuilder b;
@BeforeEach
public void prepareBuilder() {
b = new BlockBuilder(true);
}
@Test void testReuseExpressionsFromUpperLevel() {
Expression x = b.append("x", Expressions.add(ONE, TWO));
BlockBuilder nested = new BlockBuilder(true, b);
Expression y = nested.append("y", Expressions.add(ONE, TWO));
nested.add(Expressions.return_(null, Expressions.add(y, y)));
b.add(nested.toBlock());
assertEquals(
"{\n"
+ " final int x = 1 + 2;\n"
+ " {\n"
+ " return x + x;\n"
+ " }\n"
+ "}\n",
b.toBlock().toString());
}
@Test void testTestCustomOptimizer() {
BlockBuilder b = new BlockBuilder() {
@Override protected Shuttle createOptimizeShuttle() {
return new OptimizeShuttle() {
@Override public Expression visit(BinaryExpression binary,
Expression expression0, Expression expression1) {
if (binary.getNodeType() == ExpressionType.Add
&& ONE.equals(expression0) && TWO.equals(expression1)) {
return FOUR;
}
return super.visit(binary, expression0, expression1);
}
};
}
};
b.add(Expressions.return_(null, Expressions.add(ONE, TWO)));
assertEquals("{\n return 4;\n}\n", b.toBlock().toString());
}
private BlockBuilder appendBlockWithSameVariable(
Expression initializer1, Expression initializer2) {
BlockBuilder outer = new BlockBuilder();
ParameterExpression outerX = Expressions.parameter(int.class, "x");
outer.add(Expressions.declare(0, outerX, initializer1));
outer.add(Expressions.statement(Expressions.assign(outerX, Expressions.constant(1))));
BlockBuilder inner = new BlockBuilder();
ParameterExpression innerX = Expressions.parameter(int.class, "x");
inner.add(Expressions.declare(0, innerX, initializer2));
inner.add(Expressions.statement(Expressions.assign(innerX, Expressions.constant(42))));
inner.add(Expressions.return_(null, innerX));
outer.append("x", inner.toBlock());
return outer;
}
@Test void testRenameVariablesWithEmptyInitializer() {
BlockBuilder outer = appendBlockWithSameVariable(null, null);
assertEquals("{\n"
+ " int x;\n"
+ " x = 1;\n"
+ " int x0;\n"
+ " x0 = 42;\n"
+ "}\n", Expressions.toString(outer.toBlock()),
"x in the second block should be renamed to avoid name clash");
}
@Test void testRenameVariablesWithInitializer() {
BlockBuilder outer = appendBlockWithSameVariable(
Expressions.constant(7), Expressions.constant(8));
assertEquals("{\n"
+ " int x = 7;\n"
+ " x = 1;\n"
+ " int x0 = 8;\n"
+ " x0 = 42;\n"
+ "}\n", Expressions.toString(outer.toBlock()),
"x in the second block should be renamed to avoid name clash");
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2413">[CALCITE-2413]
* RexToLixTranslator does not generate correct declaration of Methods with
* generic return types</a>. */
@Test void genericMethodCall() throws NoSuchMethodException {
BlockBuilder bb = new BlockBuilder();
bb.append("_i",
Expressions.call(
Expressions.new_(Identity.class),
Identity.class.getMethod("apply", Object.class),
Expressions.constant("test")));
assertEquals(
"{\n"
+ " final Object _i = new org.apache.calcite.linq4j.test.BlockBuilderTest.Identity()"
+ ".apply(\"test\");\n"
+ "}\n",
Expressions.toString(bb.toBlock()));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2611">[CALCITE-2611]
* Linq4j code generation failure if one side of an OR contains
* UNKNOWN</a>. */
@Test void testOptimizeBoxedFalseEqNull() {
BlockBuilder outer = new BlockBuilder();
outer.append(
Expressions.equal(
OptimizeShuttle.BOXED_FALSE_EXPR,
Expressions.constant(null)));
assertEquals("{\n"
+ " return false;\n"
+ "}\n", Expressions.toString(outer.toBlock()),
"Expected to optimize Boolean.FALSE = null to false");
}
/**
* Class with generics to validate if {@link Expressions#call(Method, Expression...)} works.
* @param <I> result type
*/
static class Identity<I> implements Function<I, I> {
@Override public I apply(I i) {
return i;
}
}
}