aoqi@0: /* aoqi@0: * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * @test aoqi@0: * @bug 7115050 8003280 8005852 8006694 aoqi@0: * @summary Add lambda tests aoqi@0: * Add parser support for lambda expressions aoqi@0: * temporarily workaround combo tests are causing time out in several platforms aoqi@0: * @library ../lib aoqi@0: * @build JavacTestingAbstractThreadedTest aoqi@0: * @run main/othervm LambdaParserTest aoqi@0: */ aoqi@0: aoqi@0: // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) aoqi@0: // see JDK-8006746 aoqi@0: aoqi@0: import java.net.URI; aoqi@0: import java.util.Arrays; aoqi@0: import javax.tools.Diagnostic; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.SimpleJavaFileObject; aoqi@0: import com.sun.source.util.JavacTask; aoqi@0: aoqi@0: public class LambdaParserTest aoqi@0: extends JavacTestingAbstractThreadedTest aoqi@0: implements Runnable { aoqi@0: aoqi@0: enum LambdaKind { aoqi@0: NILARY_EXPR("()->x"), aoqi@0: NILARY_STMT("()->{ return x; }"), aoqi@0: ONEARY_SHORT_EXPR("#PN->x"), aoqi@0: ONEARY_SHORT_STMT("#PN->{ return x; }"), aoqi@0: ONEARY_EXPR("(#M1 #T1 #PN)->x"), aoqi@0: ONEARY_STMT("(#M1 #T1 #PN)->{ return x; }"), aoqi@0: TWOARY_EXPR("(#M1 #T1 #PN, #M2 #T2 y)->x"), aoqi@0: TWOARY_STMT("(#M1 #T1 #PN, #M2 #T2 y)->{ return x; }"); aoqi@0: aoqi@0: String lambdaTemplate; aoqi@0: aoqi@0: LambdaKind(String lambdaTemplate) { aoqi@0: this.lambdaTemplate = lambdaTemplate; aoqi@0: } aoqi@0: aoqi@0: String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2, aoqi@0: ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn) { aoqi@0: return lambdaTemplate.replaceAll("#M1", mk1.modifier) aoqi@0: .replaceAll("#M2", mk2.modifier) aoqi@0: .replaceAll("#T1", pk1.parameterType) aoqi@0: .replaceAll("#T2", pk2.parameterType) aoqi@0: .replaceAll("#PN", pn.nameStr); aoqi@0: } aoqi@0: aoqi@0: int arity() { aoqi@0: switch (this) { aoqi@0: case NILARY_EXPR: aoqi@0: case NILARY_STMT: return 0; aoqi@0: case ONEARY_SHORT_EXPR: aoqi@0: case ONEARY_SHORT_STMT: aoqi@0: case ONEARY_EXPR: aoqi@0: case ONEARY_STMT: return 1; aoqi@0: case TWOARY_EXPR: aoqi@0: case TWOARY_STMT: return 2; aoqi@0: default: throw new AssertionError("Invalid lambda kind " + this); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: boolean isShort() { aoqi@0: return this == ONEARY_SHORT_EXPR || aoqi@0: this == ONEARY_SHORT_STMT; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum LambdaParameterName { aoqi@0: IDENT("x"), aoqi@0: UNDERSCORE("_"); aoqi@0: aoqi@0: String nameStr; aoqi@0: aoqi@0: LambdaParameterName(String nameStr) { aoqi@0: this.nameStr = nameStr; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum LambdaParameterKind { aoqi@0: IMPLICIT(""), aoqi@0: EXPLIICT_SIMPLE("A"), aoqi@0: EXPLIICT_SIMPLE_ARR1("A[]"), aoqi@0: EXPLIICT_SIMPLE_ARR2("A[][]"), aoqi@0: EXPLICIT_VARARGS("A..."), aoqi@0: EXPLICIT_GENERIC1("A"), aoqi@0: EXPLICIT_GENERIC2("A"), aoqi@0: EXPLICIT_GENERIC2_VARARGS("A..."), aoqi@0: EXPLICIT_GENERIC2_ARR1("A[]"), aoqi@0: EXPLICIT_GENERIC2_ARR2("A[][]"); aoqi@0: aoqi@0: String parameterType; aoqi@0: aoqi@0: LambdaParameterKind(String parameterType) { aoqi@0: this.parameterType = parameterType; aoqi@0: } aoqi@0: aoqi@0: boolean explicit() { aoqi@0: return this != IMPLICIT; aoqi@0: } aoqi@0: aoqi@0: boolean isVarargs() { aoqi@0: return this == EXPLICIT_VARARGS || aoqi@0: this == EXPLICIT_GENERIC2_VARARGS; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum ModifierKind { aoqi@0: NONE(""), aoqi@0: FINAL("final"), aoqi@0: PUBLIC("public"); aoqi@0: aoqi@0: String modifier; aoqi@0: aoqi@0: ModifierKind(String modifier) { aoqi@0: this.modifier = modifier; aoqi@0: } aoqi@0: aoqi@0: boolean compatibleWith(LambdaParameterKind pk) { aoqi@0: switch (this) { aoqi@0: case PUBLIC: return false; aoqi@0: case FINAL: return pk != LambdaParameterKind.IMPLICIT; aoqi@0: case NONE: return true; aoqi@0: default: throw new AssertionError("Invalid modifier kind " + this); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum ExprKind { aoqi@0: NONE("#L#S"), aoqi@0: SINGLE_PAREN1("(#L#S)"), aoqi@0: SINGLE_PAREN2("(#L)#S"), aoqi@0: DOUBLE_PAREN1("((#L#S))"), aoqi@0: DOUBLE_PAREN2("((#L)#S)"), aoqi@0: DOUBLE_PAREN3("((#L))#S"); aoqi@0: aoqi@0: String expressionTemplate; aoqi@0: aoqi@0: ExprKind(String expressionTemplate) { aoqi@0: this.expressionTemplate = expressionTemplate; aoqi@0: } aoqi@0: aoqi@0: String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2, aoqi@0: ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk) { aoqi@0: return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2, pn)) aoqi@0: .replaceAll("#S", sk.subExpression); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum SubExprKind { aoqi@0: NONE(""), aoqi@0: SELECT_FIELD(".f"), aoqi@0: SELECT_METHOD(".f()"), aoqi@0: SELECT_NEW(".new Foo()"), aoqi@0: POSTINC("++"), aoqi@0: POSTDEC("--"); aoqi@0: aoqi@0: String subExpression; aoqi@0: aoqi@0: SubExprKind(String subExpression) { aoqi@0: this.subExpression = subExpression; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static void main(String... args) throws Exception { aoqi@0: for (LambdaKind lk : LambdaKind.values()) { aoqi@0: for (LambdaParameterName pn : LambdaParameterName.values()) { aoqi@0: for (LambdaParameterKind pk1 : LambdaParameterKind.values()) { aoqi@0: if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) aoqi@0: continue; aoqi@0: for (LambdaParameterKind pk2 : LambdaParameterKind.values()) { aoqi@0: if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) aoqi@0: continue; aoqi@0: for (ModifierKind mk1 : ModifierKind.values()) { aoqi@0: if (mk1 != ModifierKind.NONE && lk.isShort()) aoqi@0: continue; aoqi@0: if (lk.arity() < 1 && mk1 != ModifierKind.NONE) aoqi@0: continue; aoqi@0: for (ModifierKind mk2 : ModifierKind.values()) { aoqi@0: if (lk.arity() < 2 && mk2 != ModifierKind.NONE) aoqi@0: continue; aoqi@0: for (SubExprKind sk : SubExprKind.values()) { aoqi@0: for (ExprKind ek : ExprKind.values()) { aoqi@0: pool.execute( aoqi@0: new LambdaParserTest(pk1, pk2, mk1, aoqi@0: mk2, lk, sk, ek, pn)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: checkAfterExec(); aoqi@0: } aoqi@0: aoqi@0: LambdaParameterKind pk1; aoqi@0: LambdaParameterKind pk2; aoqi@0: ModifierKind mk1; aoqi@0: ModifierKind mk2; aoqi@0: LambdaKind lk; aoqi@0: LambdaParameterName pn; aoqi@0: SubExprKind sk; aoqi@0: ExprKind ek; aoqi@0: JavaSource source; aoqi@0: DiagnosticChecker diagChecker; aoqi@0: aoqi@0: LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, aoqi@0: ModifierKind mk1, ModifierKind mk2, LambdaKind lk, aoqi@0: SubExprKind sk, ExprKind ek, LambdaParameterName pn) { aoqi@0: this.pk1 = pk1; aoqi@0: this.pk2 = pk2; aoqi@0: this.mk1 = mk1; aoqi@0: this.mk2 = mk2; aoqi@0: this.lk = lk; aoqi@0: this.pn = pn; aoqi@0: this.sk = sk; aoqi@0: this.ek = ek; aoqi@0: this.source = new JavaSource(); aoqi@0: this.diagChecker = new DiagnosticChecker(); aoqi@0: } aoqi@0: aoqi@0: class JavaSource extends SimpleJavaFileObject { aoqi@0: aoqi@0: String template = "class Test {\n" + aoqi@0: " SAM s = #E;\n" + aoqi@0: "}"; aoqi@0: aoqi@0: String source; aoqi@0: aoqi@0: public JavaSource() { aoqi@0: super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); aoqi@0: source = template.replaceAll("#E", aoqi@0: ek.expressionString(pk1, pk2, mk1, mk2, lk, pn, sk)); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public CharSequence getCharContent(boolean ignoreEncodingErrors) { aoqi@0: return source; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public void run() { aoqi@0: JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker, aoqi@0: null, null, Arrays.asList(source)); aoqi@0: try { aoqi@0: ct.parse(); aoqi@0: } catch (Throwable ex) { aoqi@0: processException(ex); aoqi@0: return; aoqi@0: } aoqi@0: check(); aoqi@0: } aoqi@0: aoqi@0: void check() { aoqi@0: checkCount.incrementAndGet(); aoqi@0: aoqi@0: boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) || aoqi@0: (lk.arity() > 1 && !mk2.compatibleWith(pk2)); aoqi@0: aoqi@0: if (lk.arity() == 2 && aoqi@0: (pk1.explicit() != pk2.explicit() || aoqi@0: pk1.isVarargs())) { aoqi@0: errorExpected = true; aoqi@0: } aoqi@0: aoqi@0: errorExpected |= pn == LambdaParameterName.UNDERSCORE && aoqi@0: lk.arity() > 0; aoqi@0: aoqi@0: if (errorExpected != diagChecker.errorFound) { aoqi@0: throw new Error("invalid diagnostics for source:\n" + aoqi@0: source.getCharContent(true) + aoqi@0: "\nFound error: " + diagChecker.errorFound + aoqi@0: "\nExpected error: " + errorExpected); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static class DiagnosticChecker aoqi@0: implements javax.tools.DiagnosticListener { aoqi@0: aoqi@0: boolean errorFound; aoqi@0: aoqi@0: public void report(Diagnostic diagnostic) { aoqi@0: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { aoqi@0: errorFound = true; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }