/* * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 7115050 8003280 8005852 8006694 8129962 * @summary Add lambda tests * Add parser support for lambda expressions * temporarily workaround combo tests are causing time out in several platforms * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.util * @build combo.ComboTestHelper * @run main LambdaParserTest */ import java.io.IOException; import java.util.Arrays; import combo.ComboInstance; import combo.ComboParameter; import combo.ComboTask.Result; import combo.ComboTestHelper; public class LambdaParserTest extends ComboInstance { enum LambdaKind implements ComboParameter { NILARY_EXPR("()->x"), NILARY_STMT("()->{ return x; }"), ONEARY_SHORT_EXPR("#{NAME}->x"), ONEARY_SHORT_STMT("#{NAME}->{ return x; }"), ONEARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME})->x"), ONEARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME})->{ return x; }"), TWOARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->x"), TWOARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->{ return x; }"); String lambdaTemplate; LambdaKind(String lambdaTemplate) { this.lambdaTemplate = lambdaTemplate; } @Override public String expand(String optParameter) { return lambdaTemplate; } int arity() { switch (this) { case NILARY_EXPR: case NILARY_STMT: return 0; case ONEARY_SHORT_EXPR: case ONEARY_SHORT_STMT: case ONEARY_EXPR: case ONEARY_STMT: return 1; case TWOARY_EXPR: case TWOARY_STMT: return 2; default: throw new AssertionError("Invalid lambda kind " + this); } } boolean isShort() { return this == ONEARY_SHORT_EXPR || this == ONEARY_SHORT_STMT; } } enum LambdaParameterName implements ComboParameter { IDENT("x"), UNDERSCORE("_"); String nameStr; LambdaParameterName(String nameStr) { this.nameStr = nameStr; } @Override public String expand(String optParameter) { return nameStr; } } enum SourceKind { SOURCE_10("10"), SOURCE_11("11"); String sourceNumber; SourceKind(String sourceNumber) { this.sourceNumber = sourceNumber; } } enum LambdaParameterKind implements ComboParameter { IMPLICIT_1("", ExplicitKind.IMPLICIT), IMPLICIT_2("var", ExplicitKind.IMPLICIT_VAR), EXPLICIT_SIMPLE("A", ExplicitKind.EXPLICIT), EXPLICIT_SIMPLE_ARR1("A[]", ExplicitKind.EXPLICIT), EXPLICIT_SIMPLE_ARR2("A[][]", ExplicitKind.EXPLICIT), EXPLICIT_VARARGS("A...", ExplicitKind.EXPLICIT), EXPLICIT_GENERIC1("A", ExplicitKind.EXPLICIT), EXPLICIT_GENERIC2("A", ExplicitKind.EXPLICIT), EXPLICIT_GENERIC2_VARARGS("A...", ExplicitKind.EXPLICIT), EXPLICIT_GENERIC2_ARR1("A[]", ExplicitKind.EXPLICIT), EXPLICIT_GENERIC2_ARR2("A[][]", ExplicitKind.EXPLICIT); enum ExplicitKind { IMPLICIT, IMPLICIT_VAR, EXPLICIT; } String parameterType; ExplicitKind explicitKind; LambdaParameterKind(String parameterType, ExplicitKind ekind) { this.parameterType = parameterType; this.explicitKind = ekind; } boolean isVarargs() { return this == EXPLICIT_VARARGS || this == EXPLICIT_GENERIC2_VARARGS; } @Override public String expand(String optParameter) { return parameterType; } ExplicitKind explicitKind(SourceKind sk) { return explicitKind; } } enum ModifierKind implements ComboParameter { NONE(""), FINAL("final"), PUBLIC("public"), ANNO("@A"); String modifier; ModifierKind(String modifier) { this.modifier = modifier; } boolean compatibleWith(LambdaParameterKind pk) { switch (this) { case PUBLIC: return false; case ANNO: case FINAL: return pk != LambdaParameterKind.IMPLICIT_1; case NONE: return true; default: throw new AssertionError("Invalid modifier kind " + this); } } @Override public String expand(String optParameter) { return modifier; } } enum ExprKind implements ComboParameter { NONE("#{LAMBDA}#{SUBEXPR}"), SINGLE_PAREN1("(#{LAMBDA}#{SUBEXPR})"), SINGLE_PAREN2("(#{LAMBDA})#{SUBEXPR}"), DOUBLE_PAREN1("((#{LAMBDA}#{SUBEXPR}))"), DOUBLE_PAREN2("((#{LAMBDA})#{SUBEXPR})"), DOUBLE_PAREN3("((#{LAMBDA}))#{SUBEXPR}"); String expressionTemplate; ExprKind(String expressionTemplate) { this.expressionTemplate = expressionTemplate; } @Override public String expand(String optParameter) { return expressionTemplate; } } enum SubExprKind implements ComboParameter { NONE(""), SELECT_FIELD(".f"), SELECT_METHOD(".f()"), SELECT_NEW(".new Foo()"), POSTINC("++"), POSTDEC("--"); String subExpression; SubExprKind(String subExpression) { this.subExpression = subExpression; } @Override public String expand(String optParameter) { return subExpression; } } public static void main(String... args) throws Exception { new ComboTestHelper() .withFilter(LambdaParserTest::redundantTestFilter) .withDimension("SOURCE", (x, sk) -> x.sk = sk, SourceKind.values()) .withDimension("LAMBDA", (x, lk) -> x.lk = lk, LambdaKind.values()) .withDimension("NAME", (x, name) -> x.pn = name, LambdaParameterName.values()) .withArrayDimension("TYPE", (x, type, idx) -> x.pks[idx] = type, 2, LambdaParameterKind.values()) .withArrayDimension("MOD", (x, mod, idx) -> x.mks[idx] = mod, 2, ModifierKind.values()) .withDimension("EXPR", (x, exp) -> x.exp = exp, ExprKind.values()) .withDimension("SUBEXPR", (x, sub) -> x.sub = sub, SubExprKind.values()) .run(LambdaParserTest::new); } LambdaParameterKind[] pks = new LambdaParameterKind[2]; ModifierKind[] mks = new ModifierKind[2]; LambdaKind lk; LambdaParameterName pn; SourceKind sk; ExprKind exp; SubExprKind sub; boolean redundantTestFilter() { if (sub == SubExprKind.NONE) { switch (exp) { //followings combinations with empty sub-expressions produces the same source case SINGLE_PAREN2, DOUBLE_PAREN2, DOUBLE_PAREN3: return false; } } else { switch (lk) { //any non-empty subexpression does not combine with lambda statements case NILARY_STMT, ONEARY_SHORT_STMT, ONEARY_STMT, TWOARY_STMT: return false; } } switch (lk) { //parameters not present in the expression are redundant case NILARY_EXPR, NILARY_STMT: if (pn.ordinal() != 0) return false; case ONEARY_SHORT_EXPR, ONEARY_SHORT_STMT: if (pks[0].ordinal() != 0 || mks[0].ordinal() != 0) return false; case ONEARY_EXPR, ONEARY_STMT : if (pks[1].ordinal() != 0 || mks[1].ordinal() != 0) return false; } return true; } String template = "@interface A { }\n" + "class Test {\n" + " SAM s = #{EXPR};\n" + "}"; @Override public void doWork() throws IOException { newCompilationTask() .withOptions(Arrays.asList("-source", sk.sourceNumber)) .withSourceFromTemplate(template) .parse(this::check); } void check(Result res) { boolean errorExpected = (lk.arity() > 0 && !mks[0].compatibleWith(pks[0])) || (lk.arity() > 1 && !mks[1].compatibleWith(pks[1])); if (lk.arity() == 2 && (pks[0].explicitKind(sk) != pks[1].explicitKind(sk) || pks[0].isVarargs())) { errorExpected = true; } errorExpected |= pn == LambdaParameterName.UNDERSCORE && lk.arity() > 0; for (int i = 0; i < lk.arity(); i++) { if (!lk.isShort() && pks[i].explicitKind(sk) == LambdaParameterKind.ExplicitKind.IMPLICIT_VAR && sk == SourceKind.SOURCE_10) { errorExpected = true; break; } } if (errorExpected != res.hasErrors()) { fail("invalid diagnostics for source:\n" + res.compilationInfo() + "\nFound error: " + res.hasErrors() + "\nExpected error: " + errorExpected); } } }