blob: 96eb5f57e548aed7eccb56993c9ad5378c823dd0 [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.BlockBuilder;
import org.apache.calcite.linq4j.tree.CatchBlock;
import org.apache.calcite.linq4j.tree.DeclarationStatement;
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.ParameterExpression;
import org.apache.calcite.linq4j.tree.Statement;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Modifier;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.ONE;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.TRUE;
import static org.apache.calcite.linq4j.test.BlockBuilderBase.TWO;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests expression inlining in BlockBuilder.
*/
class InlinerTest {
BlockBuilder b;
@BeforeEach
public void prepareBuilder() {
b = new BlockBuilder(true);
}
@Test void testInlineSingleUsage() {
DeclarationStatement decl = Expressions.declare(16, "x",
Expressions.add(ONE, TWO));
b.add(decl);
b.add(Expressions.return_(null, decl.parameter));
assertEquals("{\n return 1 + 2;\n}\n", b.toBlock().toString());
}
@Test void testInlineConstant() {
DeclarationStatement decl = Expressions.declare(16, "x", ONE);
b.add(decl);
b.add(
Expressions.return_(null,
Expressions.add(decl.parameter, decl.parameter)));
assertEquals("{\n return 1 + 1;\n}\n", b.toBlock().toString());
}
@Test void testInlineParameter() {
ParameterExpression pe = Expressions.parameter(int.class, "p");
DeclarationStatement decl = Expressions.declare(16, "x", pe);
b.add(decl);
b.add(
Expressions.return_(null,
Expressions.add(decl.parameter, decl.parameter)));
assertEquals("{\n return p + p;\n}\n", b.toBlock().toString());
}
@Test void testNoInlineMultipleUsage() {
ParameterExpression p1 = Expressions.parameter(int.class, "p1");
ParameterExpression p2 = Expressions.parameter(int.class, "p2");
DeclarationStatement decl = Expressions.declare(16, "x",
Expressions.subtract(p1, p2));
b.add(decl);
b.add(
Expressions.return_(null,
Expressions.add(decl.parameter, decl.parameter)));
assertEquals(
"{\n"
+ " final int x = p1 - p2;\n"
+ " return x + x;\n"
+ "}\n",
b.toBlock().toString());
}
@Test void testAssignInConditionMultipleUsage() {
// int t;
// return (t = 1) != a ? t : c
final BlockBuilder builder = new BlockBuilder(true);
final ParameterExpression t = Expressions.parameter(int.class, "t");
builder.add(Expressions.declare(0, t, null));
Expression v = builder.append("v",
Expressions.makeTernary(ExpressionType.Conditional,
Expressions.makeBinary(ExpressionType.NotEqual,
Expressions.assign(t, Expressions.constant(1)),
Expressions.parameter(int.class, "a")),
t,
Expressions.parameter(int.class, "c")));
builder.add(Expressions.return_(null, v));
assertEquals(
"{\n"
+ " int t;\n"
+ " return (t = 1) != a ? t : c;\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
@Test void testAssignInConditionOptimizedOut() {
checkAssignInConditionOptimizedOut(Modifier.FINAL,
"{\n"
+ " return 1 != a ? b : c;\n"
+ "}\n");
}
@Test void testAssignInConditionNotOptimizedWithoutFinal() {
checkAssignInConditionOptimizedOut(0,
"{\n"
+ " int t;\n"
+ " return (t = 1) != a ? b : c;\n"
+ "}\n");
}
void checkAssignInConditionOptimizedOut(int modifiers, String s) {
// int t;
// return (t = 1) != a ? b : c
final BlockBuilder builder = new BlockBuilder(true);
final ParameterExpression t =
Expressions.parameter(int.class, "t");
builder.add(Expressions.declare(modifiers, t, null));
Expression v = builder.append("v",
Expressions.makeTernary(ExpressionType.Conditional,
Expressions.makeBinary(ExpressionType.NotEqual,
Expressions.assign(t, Expressions.constant(1)),
Expressions.parameter(int.class, "a")),
Expressions.parameter(int.class, "b"),
Expressions.parameter(int.class, "c")));
builder.add(Expressions.return_(null, v));
assertThat(Expressions.toString(builder.toBlock()),
CoreMatchers.equalTo(s));
}
@Test void testAssignInConditionMultipleUsageNonOptimized() {
// int t = 2;
// return (t = 1) != a ? 1 : c
final BlockBuilder builder = new BlockBuilder(true);
final ParameterExpression t = Expressions.parameter(int.class, "t");
builder.add(Expressions.declare(0, t, TWO));
Expression v = builder.append("v",
Expressions.makeTernary(ExpressionType.Conditional,
Expressions.makeBinary(ExpressionType.NotEqual,
Expressions.assign(t, Expressions.constant(1)),
Expressions.parameter(int.class, "a")),
t,
Expressions.parameter(int.class, "c")));
builder.add(Expressions.return_(null, v));
assertEquals(
"{\n"
+ " int t = 2;\n"
+ " return (t = 1) != a ? t : c;\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
@Test void testMultiPassOptimization() {
// int t = u + v;
// boolean b = t > 1 ? true : true; -- optimized out, thus t can be inlined
// return b ? t : 2
final BlockBuilder builder = new BlockBuilder(true);
final ParameterExpression u = Expressions.parameter(int.class, "u");
final ParameterExpression v = Expressions.parameter(int.class, "v");
Expression t = builder.append("t", Expressions.add(u, v));
Expression b = builder.append("b",
Expressions.condition(Expressions.greaterThan(t, ONE), TRUE, TRUE));
builder.add(Expressions.return_(null, Expressions.condition(b, t, TWO)));
assertEquals(
"{\n"
+ " return u + v;\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
@Test void testInlineInTryCatchStatement() {
final BlockBuilder builder = new BlockBuilder(true);
final ParameterExpression t = Expressions.parameter(int.class, "t");
builder.add(Expressions.declare(Modifier.FINAL, t, ONE));
final ParameterExpression u = Expressions.parameter(int.class, "u");
builder.add(Expressions.declare(Modifier.FINAL, u, null));
Statement st = Expressions.statement(
Expressions.assign(u,
Expressions.makeBinary(ExpressionType.Add, t, TWO)));
ParameterExpression e = Expressions.parameter(0, Exception.class, "e");
CatchBlock cb = Expressions.catch_(e, Expressions.throw_(e));
builder.add(Expressions.tryCatch(st, cb));
builder.add(Expressions.return_(null, u));
assertEquals(
"{\n"
+ " final int u;\n"
+ " try {\n"
+ " u = 1 + 2;\n"
+ " } catch (Exception e) {\n"
+ " throw e;\n"
+ " }\n"
+ " return u;\n"
+ "}\n",
builder.toBlock().toString());
}
}