blob: 46c6912b1396bbb96e43ec78d3c74b39928f2eb5 [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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 net.hydromatic.linq4j.test;
import net.hydromatic.linq4j.expressions.*;
import net.hydromatic.linq4j.function.Function1;
import org.junit.Test;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import static org.junit.Assert.*;
/**
* Unit test for {@link net.hydromatic.linq4j.expressions.Expression}
* and subclasses.
*/
public class ExpressionTest {
@Test public void testLambdaCallsBinaryOp() {
// A parameter for the lambda expression.
ParameterExpression paramExpr =
Expressions.parameter(Double.TYPE, "arg");
// This expression represents a lambda expression
// that adds 1 to the parameter value.
FunctionExpression lambdaExpr = Expressions.lambda(
Expressions.add(
paramExpr,
Expressions.constant(2d)),
Arrays.asList(paramExpr));
// Print out the expression.
String s = Expressions.toString(lambdaExpr);
assertEquals(
"new net.hydromatic.linq4j.function.Function1() {\n"
+ " public double apply(double arg) {\n"
+ " return arg + 2.0D;\n"
+ " }\n"
+ " public Object apply(Double arg) {\n"
+ " return apply(\n"
+ " arg.doubleValue());\n"
+ " }\n"
+ " public Object apply(Object arg) {\n"
+ " return apply(\n"
+ " (Double) arg);\n"
+ " }\n"
+ "}\n",
s);
// Compile and run the lambda expression.
// The value of the parameter is 1.5.
double n = (Double) lambdaExpr.compile().dynamicInvoke(1.5d);
// This code example produces the following output:
//
// arg => (arg +2)
// 3
assertEquals(3.5D, n, 0d);
}
@Test public void testLambdaPrimitiveTwoArgs() {
// Parameters for the lambda expression.
ParameterExpression paramExpr =
Expressions.parameter(int.class, "key");
ParameterExpression param2Expr =
Expressions.parameter(int.class, "key2");
FunctionExpression lambdaExpr = Expressions.lambda(
Expressions.block(
(Type) null,
Expressions.return_(
null, paramExpr)),
Arrays.asList(paramExpr, param2Expr));
// Print out the expression.
String s = Expressions.toString(lambdaExpr);
assertEquals("new net.hydromatic.linq4j.function.Function2() {\n"
+ " public int apply(int key, int key2) {\n"
+ " return key;\n"
+ " }\n"
+ " public Integer apply(Integer key, Integer key2) {\n"
+ " return apply(\n"
+ " key.intValue(),\n"
+ " key2.intValue());\n"
+ " }\n"
+ " public Integer apply(Object key, Object key2) {\n"
+ " return apply(\n"
+ " (Integer) key,\n"
+ " (Integer) key2);\n"
+ " }\n"
+ "}\n",
s);
}
@Test public void testLambdaCallsTwoArgMethod() throws NoSuchMethodException {
// A parameter for the lambda expression.
ParameterExpression paramS =
Expressions.parameter(String.class, "s");
ParameterExpression paramBegin =
Expressions.parameter(Integer.TYPE, "begin");
ParameterExpression paramEnd =
Expressions.parameter(Integer.TYPE, "end");
// This expression represents a lambda expression
// that adds 1 to the parameter value.
FunctionExpression lambdaExpr =
Expressions.lambda(
Expressions.call(
paramS,
String.class.getMethod(
"substring", Integer.TYPE, Integer.TYPE),
paramBegin,
paramEnd), paramS, paramBegin, paramEnd);
// Compile and run the lambda expression.
String s =
(String) lambdaExpr.compile().dynamicInvoke("hello world", 3, 7);
assertEquals("lo w", s);
}
@Test public void testFoldAnd() {
// empty list yields true
final List<Expression> list0 = Collections.emptyList();
assertEquals(
"true",
Expressions.toString(
Expressions.foldAnd(list0)));
assertEquals(
"false",
Expressions.toString(
Expressions.foldOr(list0)));
final List<Expression> list1 =
Arrays.asList(
Expressions.equal(Expressions.constant(1), Expressions.constant(2)),
Expressions.equal(Expressions.constant(3), Expressions.constant(4)),
Expressions.constant(true),
Expressions.equal(Expressions.constant(5),
Expressions.constant(6)));
// true is eliminated from AND
assertEquals(
"1 == 2 && 3 == 4 && 5 == 6",
Expressions.toString(
Expressions.foldAnd(list1)));
// a single true makes OR true
assertEquals(
"true",
Expressions.toString(
Expressions.foldOr(list1)));
final List<Expression> list2 =
Collections.<Expression>singletonList(
Expressions.constant(true));
assertEquals(
"true",
Expressions.toString(
Expressions.foldAnd(list2)));
assertEquals(
"true",
Expressions.toString(
Expressions.foldOr(list2)));
final List<Expression> list3 =
Arrays.asList(
Expressions.equal(Expressions.constant(1), Expressions.constant(2)),
Expressions.constant(false),
Expressions.equal(Expressions.constant(5),
Expressions.constant(6)));
// false causes whole list to be false
assertEquals(
"false",
Expressions.toString(
Expressions.foldAnd(list3)));
assertEquals(
"1 == 2 || 5 == 6",
Expressions.toString(
Expressions.foldOr(list3)));
}
@Test public void testWrite() {
assertEquals(
"1 + 2.0F + 3L + Long.valueOf(4L)",
Expressions.toString(
Expressions.add(
Expressions.add(
Expressions.add(
Expressions.constant(1),
Expressions.constant(2F, Float.TYPE)),
Expressions.constant(3L, Long.TYPE)),
Expressions.constant(4L, Long.class))));
assertEquals(
"new java.math.BigDecimal(31415926L, 7)",
Expressions.toString(
Expressions.constant(
BigDecimal.valueOf(314159260, 8))));
// Parentheses needed, to override the left-associativity of +.
assertEquals(
"1 + (2 + 3)",
Expressions.toString(
Expressions.add(
Expressions.constant(1),
Expressions.add(
Expressions.constant(2),
Expressions.constant(3)))));
// No parentheses needed; higher precedence of * achieves the desired
// effect.
assertEquals(
"1 + 2 * 3",
Expressions.toString(
Expressions.add(
Expressions.constant(1),
Expressions.multiply(
Expressions.constant(2),
Expressions.constant(3)))));
assertEquals(
"1 * (2 + 3)",
Expressions.toString(
Expressions.multiply(
Expressions.constant(1),
Expressions.add(
Expressions.constant(2),
Expressions.constant(3)))));
// Parentheses needed, to overcome right-associativity of =.
assertEquals(
"(1 = 2) = 3",
Expressions.toString(
Expressions.assign(
Expressions.assign(
Expressions.constant(1), Expressions.constant(2)),
Expressions.constant(3))));
// Ternary operator.
assertEquals(
"1 < 2 ? (3 < 4 ? 5 : 6) : 7 < 8 ? 9 : 10",
Expressions.toString(
Expressions.condition(
Expressions.lessThan(
Expressions.constant(1),
Expressions.constant(2)),
Expressions.condition(
Expressions.lessThan(
Expressions.constant(3),
Expressions.constant(4)),
Expressions.constant(5),
Expressions.constant(6)),
Expressions.condition(
Expressions.lessThan(
Expressions.constant(7),
Expressions.constant(8)),
Expressions.constant(9),
Expressions.constant(10)))));
assertEquals(
"0 + (double) (2 + 3)",
Expressions.toString(
Expressions.add(
Expressions.constant(0),
Expressions.convert_(
Expressions.add(
Expressions.constant(2), Expressions.constant(3)),
Double.TYPE))));
// "--5" would be a syntax error
assertEquals(
"- - 5",
Expressions.toString(
Expressions.negate(
Expressions.negate(
Expressions.constant(5)))));
assertEquals(
"a.empno",
Expressions.toString(
Expressions.field(
Expressions.parameter(Linq4jTest.Employee.class, "a"),
"empno")));
assertEquals(
"a.length",
Expressions.toString(
Expressions.field(
Expressions.parameter(Object[].class, "a"),
"length")));
assertEquals(
"java.util.Collections.EMPTY_LIST",
Expressions.toString(
Expressions.field(
null, Collections.class, "EMPTY_LIST")));
final ParameterExpression paramX =
Expressions.parameter(String.class, "x");
assertEquals(
"new net.hydromatic.linq4j.function.Function1() {\n"
+ " public int apply(String x) {\n"
+ " return x.length();\n"
+ " }\n"
+ " public Object apply(Object x) {\n"
+ " return apply(\n"
+ " (String) x);\n"
+ " }\n"
+ "}\n",
Expressions.toString(
Expressions.lambda(
Function1.class,
Expressions.call(
paramX, "length", Collections.<Expression>emptyList()),
Arrays.asList(paramX))));
// 1-dimensional array with initializer
assertEquals(
"new String[] {\n"
+ " \"foo\",\n"
+ " null,\n"
+ " \"bar\\\"baz\"}",
Expressions.toString(
Expressions.newArrayInit(
String.class,
Expressions.constant("foo"),
Expressions.constant(null),
Expressions.constant("bar\"baz"))));
// 2-dimensional array with initializer
assertEquals(
"new String[][] {\n"
+ " new String[] {\n"
+ " \"foo\",\n"
+ " \"bar\"},\n"
+ " null,\n"
+ " new String[] {\n"
+ " null}}",
Expressions.toString(
Expressions.newArrayInit(
String.class,
2,
Expressions.constant(new String[] {"foo", "bar"}),
Expressions.constant(null),
Expressions.constant(new String[] {null}))));
// 1-dimensional array
assertEquals(
"new String[x + 1]",
Expressions.toString(
Expressions.newArrayBounds(
String.class,
1,
Expressions.add(
Expressions.parameter(0, int.class, "x"),
Expressions.constant(1)))));
// 3-dimensional array
assertEquals(
"new String[x + 1][][]",
Expressions.toString(
Expressions.newArrayBounds(
String.class,
3,
Expressions.add(
Expressions.parameter(0, int.class, "x"),
Expressions.constant(1)))));
assertEquals(
"(int) ((String) (Object) \"foo\").length()",
Expressions.toString(
Expressions.convert_(
Expressions.call(
Expressions.convert_(
Expressions.convert_(
Expressions.constant("foo"),
Object.class),
String.class),
"length",
Collections.<Expression>emptyList()),
Integer.TYPE)));
// resolving a static method
assertEquals(
"Integer.valueOf(\"0123\")",
Expressions.toString(
Expressions.call(
Integer.class,
"valueOf",
Collections.<Expression>singletonList(
Expressions.constant("0123")))));
// precedence of not and instanceof
assertEquals(
"!(o instanceof String)",
Expressions.toString(
Expressions.not(
Expressions.typeIs(
Expressions.parameter(Object.class, "o"),
String.class))));
// not not
assertEquals(
"!!(o instanceof String)",
Expressions.toString(
Expressions.not(
Expressions.not(
Expressions.typeIs(
Expressions.parameter(Object.class, "o"),
String.class)))));
}
@Test public void testWriteConstant() {
// array of primitives
assertEquals(
"new int[] {\n"
+ " 1,\n"
+ " 2,\n"
+ " -1}",
Expressions.toString(
Expressions.constant(new int[]{1, 2, -1})));
// primitive
assertEquals(
"-12",
Expressions.toString(
Expressions.constant(-12)));
assertEquals(
"(short)-12",
Expressions.toString(
Expressions.constant((short) -12)));
assertEquals(
"(byte)-12",
Expressions.toString(
Expressions.constant((byte) -12)));
// boxed primitives
assertEquals(
"Integer.valueOf(1)",
Expressions.toString(
Expressions.constant(1, Integer.class)));
assertEquals(
"Double.valueOf(-3.14D)",
Expressions.toString(
Expressions.constant(-3.14, Double.class)));
assertEquals(
"Boolean.valueOf(true)",
Expressions.toString(
Expressions.constant(true, Boolean.class)));
// primitive with explicit class
assertEquals(
"1",
Expressions.toString(
Expressions.constant(1, int.class)));
assertEquals(
"(short)1",
Expressions.toString(
Expressions.constant(1, short.class)));
assertEquals(
"(byte)1",
Expressions.toString(
Expressions.constant(1, byte.class)));
assertEquals(
"-3.14D",
Expressions.toString(
Expressions.constant(-3.14, double.class)));
assertEquals(
"true",
Expressions.toString(
Expressions.constant(true, boolean.class)));
// objects and nulls
assertEquals(
"new String[] {\n"
+ " \"foo\",\n"
+ " null}",
Expressions.toString(
Expressions.constant(new String[] {"foo", null})));
// string
assertEquals(
"\"hello, \\\"world\\\"!\"",
Expressions.toString(
Expressions.constant("hello, \"world\"!")));
// enum
assertEquals(
"net.hydromatic.linq4j.test.ExpressionTest.MyEnum.X",
Expressions.toString(
Expressions.constant(MyEnum.X)));
// array of enum
assertEquals(
"new net.hydromatic.linq4j.test.ExpressionTest.MyEnum[] {\n"
+ " net.hydromatic.linq4j.test.ExpressionTest.MyEnum.X,\n"
+ " net.hydromatic.linq4j.test.ExpressionTest.MyEnum.Y}",
Expressions.toString(
Expressions.constant(new MyEnum[]{MyEnum.X, MyEnum.Y})));
// class
assertEquals(
"java.lang.String.class",
Expressions.toString(
Expressions.constant(String.class)));
// array class
assertEquals(
"int[].class",
Expressions.toString(
Expressions.constant(int[].class)));
assertEquals(
"java.util.List[][].class",
Expressions.toString(
Expressions.constant(List[][].class)));
// automatically call constructor if it matches fields
assertEquals(
"new net.hydromatic.linq4j.test.Linq4jTest.Employee[] {\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest.Employee(\n"
+ " 100,\n"
+ " \"Fred\",\n"
+ " 10),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest.Employee(\n"
+ " 110,\n"
+ " \"Bill\",\n"
+ " 30),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest.Employee(\n"
+ " 120,\n"
+ " \"Eric\",\n"
+ " 10),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest.Employee(\n"
+ " 130,\n"
+ " \"Janet\",\n"
+ " 10)}",
Expressions.toString(
Expressions.constant(Linq4jTest.emps)));
}
@Test public void testWriteArray() {
assertEquals(
"1 + integers[2 + index]",
Expressions.toString(
Expressions.add(
Expressions.constant(1),
Expressions.arrayIndex(
Expressions.variable(int[].class, "integers"),
Expressions.add(
Expressions.constant(2),
Expressions.variable(int.class, "index"))))));
}
@Test public void testWriteAnonymousClass() {
// final List<String> baz = Arrays.asList("foo", "bar");
// new AbstractList<String>() {
// public int size() {
// return baz.size();
// }
// public String get(int index) {
// return ((String) baz.get(index)).toUpperCase();
// }
// }
final ParameterExpression bazParameter =
Expressions.parameter(
Types.of(List.class, String.class),
"baz");
final ParameterExpression indexParameter =
Expressions.parameter(
Integer.TYPE,
"index");
BlockStatement e =
Expressions.block(
Expressions.declare(
Modifier.FINAL,
bazParameter,
Expressions.call(
Arrays.class,
"asList",
Arrays.<Expression>asList(
Expressions.constant("foo"),
Expressions.constant("bar")))),
Expressions.statement(
Expressions.new_(
Types.of(AbstractList.class, String.class),
Collections.<Expression>emptyList(),
Arrays.<MemberDeclaration>asList(
Expressions.fieldDecl(
Modifier.PUBLIC | Modifier.FINAL,
Expressions.parameter(
String.class,
"qux"),
Expressions.constant("xyzzy")),
Expressions.methodDecl(
Modifier.PUBLIC,
Integer.TYPE,
"size",
Collections.<ParameterExpression>emptyList(),
Blocks.toFunctionBlock(
Expressions.call(
bazParameter,
"size",
Collections.<Expression>emptyList()))),
Expressions.methodDecl(
Modifier.PUBLIC,
String.class,
"get",
Arrays.asList(indexParameter),
Blocks.toFunctionBlock(
Expressions.call(
Expressions.convert_(
Expressions.call(
bazParameter,
"get",
Arrays.<Expression>asList(
indexParameter)),
String.class),
"toUpperCase",
Collections
.<Expression>emptyList())))))));
assertEquals(
"{\n"
+ " final java.util.List<String> baz = java.util.Arrays.asList(\"foo\", \"bar\");\n"
+ " new java.util.AbstractList<String>(){\n"
+ " public final String qux = \"xyzzy\";\n"
+ " public int size() {\n"
+ " return baz.size();\n"
+ " }\n"
+ "\n"
+ " public String get(int index) {\n"
+ " return ((String) baz.get(index)).toUpperCase();\n"
+ " }\n"
+ "\n"
+ " };\n"
+ "}\n",
Expressions.toString(e));
}
@Test public void testWriteWhile() {
DeclarationStatement xDecl;
DeclarationStatement yDecl;
Node node =
Expressions.block(
xDecl = Expressions.declare(
0,
"x",
Expressions.constant(10)),
yDecl = Expressions.declare(
0,
"y",
Expressions.constant(0)),
Expressions.while_(
Expressions.lessThan(
xDecl.parameter,
Expressions.constant(5)),
Expressions.statement(
Expressions.preIncrementAssign(yDecl.parameter))));
assertEquals(
"{\n"
+ " int x = 10;\n"
+ " int y = 0;\n"
+ " while (x < 5) {\n"
+ " ++y;\n"
+ " }\n"
+ "}\n",
Expressions.toString(node));
}
@Test public void testWriteTryCatchFinally() {
final ParameterExpression cce_ =
Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
final ParameterExpression re_ =
Expressions.parameter(0, RuntimeException.class, "re");
Node node =
Expressions.tryCatchFinally(
Expressions.block(
Expressions.return_(null,
Expressions.call(
Expressions.constant("foo"),
"length"))),
Expressions.statement(
Expressions.call(
Expressions.constant("foo"),
"toUpperCase")),
Expressions.catch_(cce_,
Expressions.return_(null, Expressions.constant(null))),
Expressions.catch_(re_,
Expressions.throw_(
Expressions.new_(IndexOutOfBoundsException.class))));
assertEquals(
"try {\n"
+ " return \"foo\".length();\n"
+ "} catch (final ClassCastException cce) {\n"
+ " return null;\n"
+ "} catch (RuntimeException re) {\n"
+ " throw new IndexOutOfBoundsException();\n"
+ "} finally {\n"
+ " \"foo\".toUpperCase();\n"
+ "}\n",
Expressions.toString(node));
}
@Test public void testWriteTryFinally() {
final ParameterExpression cce_ =
Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
final ParameterExpression re_ =
Expressions.parameter(0, RuntimeException.class, "re");
Node node =
Expressions.ifThen(
Expressions.constant(true),
Expressions.tryFinally(
Expressions.block(
Expressions.return_(null,
Expressions.call(
Expressions.constant("foo"),
"length"))),
Expressions.statement(
Expressions.call(
Expressions.constant("foo"),
"toUpperCase"))));
assertEquals(
"if (true) {\n"
+ " try {\n"
+ " return \"foo\".length();\n"
+ " } finally {\n"
+ " \"foo\".toUpperCase();\n"
+ " }\n"
+ "}\n",
Expressions.toString(node));
}
@Test public void testWriteTryCatch() {
final ParameterExpression cce_ =
Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
final ParameterExpression re_ =
Expressions.parameter(0, RuntimeException.class, "re");
Node node =
Expressions.tryCatch(
Expressions.block(
Expressions.return_(null,
Expressions.call(Expressions.constant("foo"), "length"))),
Expressions.catch_(cce_,
Expressions.return_(null, Expressions.constant(null))),
Expressions.catch_(re_,
Expressions.return_(null,
Expressions.call(re_, "toString"))));
assertEquals(
"try {\n"
+ " return \"foo\".length();\n"
+ "} catch (final ClassCastException cce) {\n"
+ " return null;\n"
+ "} catch (RuntimeException re) {\n"
+ " return re.toString();\n"
+ "}\n",
Expressions.toString(node));
}
@Test public void testType() {
// Type of ternary operator is the gcd of its arguments.
assertEquals(
long.class,
Expressions.condition(
Expressions.constant(true),
Expressions.constant(5),
Expressions.constant(6L)).getType());
assertEquals(
long.class,
Expressions.condition(
Expressions.constant(true),
Expressions.constant(5L),
Expressions.constant(6)).getType());
// If one of the arguments is null constant, it is implicitly coerced.
assertEquals(
String.class,
Expressions.condition(
Expressions.constant(true),
Expressions.constant("xxx"),
Expressions.constant(null)).getType());
assertEquals(
Integer.class,
Expressions.condition(
Expressions.constant(true),
Expressions.constant(0),
Expressions.constant(null)).getType());
}
@Test public void testCompile() throws NoSuchMethodException {
// Creating a parameter for the expression tree.
ParameterExpression param = Expressions.parameter(String.class);
// Creating an expression for the method call and specifying its
// parameter.
MethodCallExpression methodCall =
Expressions.call(
Integer.class,
"valueOf",
Collections.<Expression>singletonList(param));
// The following statement first creates an expression tree,
// then compiles it, and then runs it.
int x =
Expressions.<Function1<String, Integer>>lambda(
methodCall,
new ParameterExpression[] { param })
.getFunction()
.apply("1234");
assertEquals(1234, x);
}
@Test public void testBlockBuilder() {
checkBlockBuilder(
false,
"{\n"
+ " final int three = 1 + 2;\n"
+ " final int six = three * 2;\n"
+ " final int nine = three * three;\n"
+ " final int eighteen = three + six + nine;\n"
+ " return eighteen;\n"
+ "}\n");
checkBlockBuilder(
true,
"{\n"
+ " final int three = 1 + 2;\n"
+ " return three + three * 2 + three * three;\n"
+ "}\n");
}
public void checkBlockBuilder(boolean optimizing, String expected) {
BlockBuilder statements = new BlockBuilder(optimizing);
Expression one =
statements.append(
"one", Expressions.constant(1));
Expression two =
statements.append(
"two", Expressions.constant(2));
Expression three =
statements.append(
"three", Expressions.add(one, two));
Expression six =
statements.append(
"six",
Expressions.multiply(three, two));
Expression nine =
statements.append(
"nine",
Expressions.multiply(three, three));
Expression eighteen =
statements.append(
"eighteen",
Expressions.add(
Expressions.add(three, six),
nine));
statements.add(Expressions.return_(null, eighteen));
BlockStatement expression = statements.toBlock();
assertEquals(expected, Expressions.toString(expression));
expression.accept(new Visitor());
}
@Test public void testBlockBuilder2() {
BlockBuilder statements = new BlockBuilder();
Expression element =
statements.append(
"element", Expressions.constant(null));
Expression comparator =
statements.append(
"comparator", Expressions.constant(null, Comparator.class));
Expression treeSet =
statements.append(
"treeSet",
Expressions.new_(
TreeSet.class,
Arrays.asList(comparator)));
statements.add(
Expressions.return_(
null,
Expressions.call(
treeSet,
"add",
element)));
BlockStatement expression = statements.toBlock();
assertEquals(
"{\n"
+ " return new java.util.TreeSet(\n"
+ " (java.util.Comparator) null).add(null);\n"
+ "}\n",
Expressions.toString(expression));
expression.accept(new Visitor());
}
@Test public void testBlockBuilder3() {
/*
int a = 1;
int b = a + 2;
int c = a + 3;
int d = a + 4;
int e = {
int b = a + 3;
foo(b);
}
bar(a, b, c, d, e);
*/
BlockBuilder builder0 = new BlockBuilder();
final Expression a = builder0.append("_a", Expressions.constant(1));
final Expression b =
builder0.append("_b", Expressions.add(a, Expressions.constant(2)));
final Expression c =
builder0.append("_c", Expressions.add(a, Expressions.constant(3)));
final Expression d =
builder0.append("_d", Expressions.add(a, Expressions.constant(4)));
BlockBuilder builder1 = new BlockBuilder();
final Expression b1 =
builder1.append("_b", Expressions.add(a, Expressions.constant(3)));
builder1.add(
Expressions.statement(
Expressions.call(ExpressionTest.class, "foo", b1)));
final Expression e = builder0.append("e", builder1.toBlock());
builder0.add(
Expressions.statement(
Expressions.call(ExpressionTest.class, "bar", a, b, c, d, e)));
// With the bug in BlockBuilder.append(String, BlockExpression),
// bar(1, _b, _c, _d, foo(_d));
// Correct result is
// bar(1, _b, _c, _d, foo(_c));
// because _c has the same expression (a + 3) as inner b.
BlockStatement expression = builder0.toBlock();
assertEquals(
"{\n"
+ " final int _b = 1 + 2;\n"
+ " final int _c = 1 + 3;\n"
+ " final int _d = 1 + 4;\n"
+ " net.hydromatic.linq4j.test.ExpressionTest.bar(1, _b, _c, _d, net.hydromatic.linq4j.test.ExpressionTest.foo(_c));\n"
+ "}\n",
Expressions.toString(expression));
expression.accept(new Visitor());
}
@Test public void testConstantExpression() {
final Expression constant = Expressions.constant(
new Object[] {
1,
new Object[] {
(byte) 1, (short) 2, (int) 3, (long) 4,
(float) 5, (double) 6, (char) 7, true, "string", null
},
new AllType(true, (byte) 100, (char) 101, (short) 102, 103,
(long) 104, (float) 105, (double) 106, new BigDecimal(107),
new BigInteger("108"), "109", null)
});
assertEquals(
"new Object[] {\n"
+ " 1,\n"
+ " new Object[] {\n"
+ " (byte)1,\n"
+ " (short)2,\n"
+ " 3,\n"
+ " 4L,\n"
+ " 5.0F,\n"
+ " 6.0D,\n"
+ " (char)7,\n"
+ " true,\n"
+ " \"string\",\n"
+ " null},\n"
+ " new net.hydromatic.linq4j.test.ExpressionTest.AllType(\n"
+ " true,\n"
+ " (byte)100,\n"
+ " (char)101,\n"
+ " (short)102,\n"
+ " 103,\n"
+ " 104L,\n"
+ " 105.0F,\n"
+ " 106.0D,\n"
+ " new java.math.BigDecimal(107L),\n"
+ " new java.math.BigInteger(\"108\"),\n"
+ " \"109\",\n"
+ " null)}",
constant.toString());
constant.accept(new Visitor());
}
@Test public void testClassDecl() {
final NewExpression newExpression =
Expressions.new_(
Object.class,
Collections.<Expression>emptyList(),
Arrays.<MemberDeclaration>asList(
Expressions.fieldDecl(
Modifier.PUBLIC | Modifier.FINAL,
Expressions.parameter(String.class, "foo"),
Expressions.constant("bar")),
new ClassDeclaration(
Modifier.PUBLIC | Modifier.STATIC,
"MyClass",
null,
Collections.<Type>emptyList(),
Arrays.<MemberDeclaration>asList(
new FieldDeclaration(
0,
Expressions.parameter(int.class, "x"),
Expressions.constant(0)))),
Expressions.fieldDecl(
0,
Expressions.parameter(int.class, "i"))));
assertEquals(
"new Object(){\n"
+ " public final String foo = \"bar\";\n"
+ " public static class MyClass {\n"
+ " int x = 0;\n"
+ " }\n"
+ " int i;\n"
+ "}",
Expressions.toString(newExpression));
newExpression.accept(new Visitor());
}
@Test public void testReturn() {
assertEquals(
"if (true) {\n"
+ " return;\n"
+ "} else {\n"
+ " return 1;\n"
+ "}\n",
Expressions.toString(
Expressions.ifThenElse(
Expressions.constant(true),
Expressions.return_(null),
Expressions.return_(null, Expressions.constant(1)))));
}
@Test public void testIfElseIfElse() {
assertEquals(
"if (true) {\n"
+ " return;\n"
+ "} else if (false) {\n"
+ " return;\n"
+ "} else {\n"
+ " return 1;\n"
+ "}\n",
Expressions.toString(
Expressions.ifThenElse(
Expressions.constant(true),
Expressions.return_(null),
Expressions.constant(false),
Expressions.return_(null),
Expressions.return_(null, Expressions.constant(1)))));
}
/** Test for common sub-expression elimination. */
@Test public void testSubExpressionElimination() {
final BlockBuilder builder = new BlockBuilder(true);
ParameterExpression x = Expressions.parameter(Object.class, "p");
Expression current4 = builder.append(
"current4",
Expressions.convert_(x, Object[].class));
Expression v = builder.append(
"v",
Expressions.convert_(
Expressions.arrayIndex(
current4,
Expressions.constant(4)), Short.class));
Expression v0 = builder.append(
"v0",
Expressions.convert_(v, Number.class));
Expression v1 = builder.append(
"v1",
Expressions.convert_(
Expressions.arrayIndex(
current4,
Expressions.constant(4)), Short.class));
Expression v2 = builder.append(
"v2",
Expressions.convert_(v, Number.class));
Expression v3 = builder.append(
"v3",
Expressions.convert_(
Expressions.arrayIndex(
current4,
Expressions.constant(4)), Short.class));
Expression v4 = builder.append(
"v4",
Expressions.convert_(v3, Number.class));
Expression v5 = builder.append("v5", Expressions.call(v4, "intValue"));
Expression v6 = builder.append(
"v6",
Expressions.condition(
Expressions.equal(v2, Expressions.constant(null)),
Expressions.constant(null),
Expressions.equal(v5, Expressions.constant(1997))));
builder.add(Expressions.return_(null, v6));
assertEquals(
"{\n"
+ " final Short v = (Short) ((Object[]) p)[4];\n"
+ " return (Number) v == null ? (Boolean) null : ("
+ "(Number) v).intValue() == 1997;\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
@Test public void testFor() throws NoSuchFieldException {
final BlockBuilder builder = new BlockBuilder();
final ParameterExpression i_ = Expressions.parameter(int.class, "i");
builder.add(
Expressions.for_(
Expressions.declare(
0, i_, Expressions.constant(0)),
Expressions.lessThan(i_, Expressions.constant(10)),
Expressions.postIncrementAssign(i_),
Expressions.block(
Expressions.statement(
Expressions.call(
Expressions.field(
null, System.class.getField("out")),
"println",
i_)))));
assertEquals(
"{\n"
+ " for (int i = 0; i < 10; i++) {\n"
+ " System.out.println(i);\n"
+ " }\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
@Test public void testFor2() throws NoSuchFieldException {
final BlockBuilder builder = new BlockBuilder();
final ParameterExpression i_ = Expressions.parameter(int.class, "i");
final ParameterExpression j_ = Expressions.parameter(int.class, "j");
builder.add(
Expressions.for_(
Arrays.asList(
Expressions.declare(
0, i_, Expressions.constant(0)),
Expressions.declare(
0, j_, Expressions.constant(10))),
null,
null,
Expressions.block(
Expressions.ifThen(
Expressions.lessThan(
Expressions.preIncrementAssign(i_),
Expressions.preDecrementAssign(j_)),
Expressions.break_(null)))));
assertEquals(
"{\n"
+ " for (int i = 0, j = 10; ; ) {\n"
+ " if (++i < --j) {\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ "}\n",
Expressions.toString(builder.toBlock()));
}
enum MyEnum {
X,
Y {
public String toString() {
return "YYY";
}
}
}
public static int foo(int x) {
return 0;
}
public static int bar(int v, int w, int x, int y, int z) {
return 0;
}
public static class AllType {
public final boolean b;
public final byte y;
public final char c;
public final short s;
public final int i;
public final long l;
public final float f;
public final double d;
public final BigDecimal bd;
public final BigInteger bi;
public final String str;
public final Object o;
public AllType(boolean b, byte y, char c, short s, int i, long l, float f,
double d, BigDecimal bd, BigInteger bi, String str, Object o) {
this.b = b;
this.y = y;
this.c = c;
this.s = s;
this.i = i;
this.l = l;
this.f = f;
this.d = d;
this.bd = bd;
this.bi = bi;
this.str = str;
this.o = o;
}
}
}
// End ExpressionTest.java