1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 7115050 8003280 8005852 8006694 8129962
27  * @summary Add lambda tests
28  *  Add parser support for lambda expressions
29  *  temporarily workaround combo tests are causing time out in several platforms
30  * @library /tools/javac/lib
31  * @modules jdk.compiler/com.sun.tools.javac.api
32  *          jdk.compiler/com.sun.tools.javac.code
33  *          jdk.compiler/com.sun.tools.javac.comp
34  *          jdk.compiler/com.sun.tools.javac.main
35  *          jdk.compiler/com.sun.tools.javac.tree
36  *          jdk.compiler/com.sun.tools.javac.util
37  * @build combo.ComboTestHelper
38 
39  * @run main LambdaParserTest
40  */
41 
42 import java.io.IOException;
43 import java.util.Arrays;
44 
45 import combo.ComboInstance;
46 import combo.ComboParameter;
47 import combo.ComboTask.Result;
48 import combo.ComboTestHelper;
49 
50 public class LambdaParserTest extends ComboInstance<LambdaParserTest> {
51 
52     enum LambdaKind implements ComboParameter {
53         NILARY_EXPR("()->x"),
54         NILARY_STMT("()->{ return x; }"),
55         ONEARY_SHORT_EXPR("#{NAME}->x"),
56         ONEARY_SHORT_STMT("#{NAME}->{ return x; }"),
57         ONEARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME})->x"),
58         ONEARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME})->{ return x; }"),
59         TWOARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->x"),
60         TWOARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->{ return x; }");
61 
62         String lambdaTemplate;
63 
LambdaKind(String lambdaTemplate)64         LambdaKind(String lambdaTemplate) {
65             this.lambdaTemplate = lambdaTemplate;
66         }
67 
68         @Override
expand(String optParameter)69         public String expand(String optParameter) {
70             return lambdaTemplate;
71         }
72 
arity()73         int arity() {
74             switch (this) {
75                 case NILARY_EXPR:
76                 case NILARY_STMT: return 0;
77                 case ONEARY_SHORT_EXPR:
78                 case ONEARY_SHORT_STMT:
79                 case ONEARY_EXPR:
80                 case ONEARY_STMT: return 1;
81                 case TWOARY_EXPR:
82                 case TWOARY_STMT: return 2;
83                 default: throw new AssertionError("Invalid lambda kind " + this);
84             }
85         }
86 
isShort()87         boolean isShort() {
88             return this == ONEARY_SHORT_EXPR ||
89                     this == ONEARY_SHORT_STMT;
90         }
91     }
92 
93     enum LambdaParameterName implements ComboParameter {
94         IDENT("x"),
95         UNDERSCORE("_");
96 
97         String nameStr;
98 
LambdaParameterName(String nameStr)99         LambdaParameterName(String nameStr) {
100             this.nameStr = nameStr;
101         }
102 
103         @Override
expand(String optParameter)104         public String expand(String optParameter) {
105             return nameStr;
106         }
107     }
108 
109     enum SourceKind {
110         SOURCE_9("9"),
111         SOURCE_10("10");
112 
113         String sourceNumber;
114 
SourceKind(String sourceNumber)115         SourceKind(String sourceNumber) {
116             this.sourceNumber = sourceNumber;
117         }
118     }
119 
120     enum LambdaParameterKind implements ComboParameter {
121 
122         IMPLICIT_1("", ExplicitKind.IMPLICIT),
123         IMPLICIT_2("var", ExplicitKind.IMPLICIT_VAR),
124         EXPLIICT_SIMPLE("A", ExplicitKind.EXPLICIT),
125         EXPLIICT_SIMPLE_ARR1("A[]", ExplicitKind.EXPLICIT),
126         EXPLIICT_SIMPLE_ARR2("A[][]", ExplicitKind.EXPLICIT),
127         EXPLICIT_VARARGS("A...", ExplicitKind.EXPLICIT),
128         EXPLICIT_GENERIC1("A<X>", ExplicitKind.EXPLICIT),
129         EXPLICIT_GENERIC2("A<? extends X, ? super Y>", ExplicitKind.EXPLICIT),
130         EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>...", ExplicitKind.EXPLICIT),
131         EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]", ExplicitKind.EXPLICIT),
132         EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]", ExplicitKind.EXPLICIT);
133 
134         enum ExplicitKind {
135             IMPLICIT,
136             IMPLICIT_VAR,
137             EXPLICIT;
138         }
139 
140         String parameterType;
141         ExplicitKind explicitKind;
142 
143 
LambdaParameterKind(String parameterType, ExplicitKind ekind)144         LambdaParameterKind(String parameterType, ExplicitKind ekind) {
145             this.parameterType = parameterType;
146             this.explicitKind = ekind;
147         }
148 
isVarargs()149         boolean isVarargs() {
150             return this == EXPLICIT_VARARGS ||
151                     this == EXPLICIT_GENERIC2_VARARGS;
152         }
153 
154         @Override
expand(String optParameter)155         public String expand(String optParameter) {
156             return parameterType;
157         }
158 
explicitKind(SourceKind sk)159         ExplicitKind explicitKind(SourceKind sk) {
160             switch (explicitKind) {
161                 case IMPLICIT_VAR:
162                     return (sk == SourceKind.SOURCE_9) ?
163                             ExplicitKind.EXPLICIT : ExplicitKind.IMPLICIT_VAR;
164                 default:
165                     return explicitKind;
166             }
167         }
168     }
169 
170     enum ModifierKind implements ComboParameter {
171         NONE(""),
172         FINAL("final"),
173         PUBLIC("public"),
174         ANNO("@A");
175 
176         String modifier;
177 
ModifierKind(String modifier)178         ModifierKind(String modifier) {
179             this.modifier = modifier;
180         }
181 
compatibleWith(LambdaParameterKind pk)182         boolean compatibleWith(LambdaParameterKind pk) {
183             switch (this) {
184                 case PUBLIC: return false;
185                 case ANNO:
186                 case FINAL: return pk != LambdaParameterKind.IMPLICIT_1;
187                 case NONE: return true;
188                 default: throw new AssertionError("Invalid modifier kind " + this);
189             }
190         }
191 
192         @Override
expand(String optParameter)193         public String expand(String optParameter) {
194             return modifier;
195         }
196     }
197 
198     enum ExprKind implements ComboParameter {
199         NONE("#{LAMBDA}#{SUBEXPR}"),
200         SINGLE_PAREN1("(#{LAMBDA}#{SUBEXPR})"),
201         SINGLE_PAREN2("(#{LAMBDA})#{SUBEXPR}"),
202         DOUBLE_PAREN1("((#{LAMBDA}#{SUBEXPR}))"),
203         DOUBLE_PAREN2("((#{LAMBDA})#{SUBEXPR})"),
204         DOUBLE_PAREN3("((#{LAMBDA}))#{SUBEXPR}");
205 
206         String expressionTemplate;
207 
ExprKind(String expressionTemplate)208         ExprKind(String expressionTemplate) {
209             this.expressionTemplate = expressionTemplate;
210         }
211 
212         @Override
expand(String optParameter)213         public String expand(String optParameter) {
214             return expressionTemplate;
215         }
216     }
217 
218     enum SubExprKind implements ComboParameter {
219         NONE(""),
220         SELECT_FIELD(".f"),
221         SELECT_METHOD(".f()"),
222         SELECT_NEW(".new Foo()"),
223         POSTINC("++"),
224         POSTDEC("--");
225 
226         String subExpression;
227 
SubExprKind(String subExpression)228         SubExprKind(String subExpression) {
229             this.subExpression = subExpression;
230         }
231 
232         @Override
expand(String optParameter)233         public String expand(String optParameter) {
234             return subExpression;
235         }
236     }
237 
main(String... args)238     public static void main(String... args) throws Exception {
239         new ComboTestHelper<LambdaParserTest>()
240                 .withFilter(LambdaParserTest::redundantTestFilter)
241                 .withFilter(LambdaParserTest::badImplicitFilter)
242                 .withDimension("SOURCE", (x, sk) -> x.sk = sk, SourceKind.values())
243                 .withDimension("LAMBDA", (x, lk) -> x.lk = lk, LambdaKind.values())
244                 .withDimension("NAME", (x, name) -> x.pn = name, LambdaParameterName.values())
245                 .withArrayDimension("TYPE", (x, type, idx) -> x.pks[idx] = type, 2, LambdaParameterKind.values())
246                 .withArrayDimension("MOD", (x, mod, idx) -> x.mks[idx] = mod, 2, ModifierKind.values())
247                 .withDimension("EXPR", ExprKind.values())
248                 .withDimension("SUBEXPR", SubExprKind.values())
249                 .run(LambdaParserTest::new);
250     }
251 
252     LambdaParameterKind[] pks = new LambdaParameterKind[2];
253     ModifierKind[] mks = new ModifierKind[2];
254     LambdaKind lk;
255     LambdaParameterName pn;
256     SourceKind sk;
257 
badImplicitFilter()258     boolean badImplicitFilter() {
259         return !(mks[0] != ModifierKind.NONE && lk.isShort());
260     }
261 
redundantTestFilter()262     boolean redundantTestFilter() {
263         for (int i = lk.arity(); i < mks.length ; i++) {
264             if (mks[i].ordinal() != 0) {
265                 return false;
266             }
267         }
268         for (int i = lk.arity(); i < pks.length ; i++) {
269             if (pks[i].ordinal() != 0) {
270                 return false;
271             }
272         }
273         return true;
274     }
275 
276     String template = "@interface A { }\n" +
277             "class Test {\n" +
278             "   SAM s = #{EXPR};\n" +
279             "}";
280 
281     @Override
doWork()282     public void doWork() throws IOException {
283         newCompilationTask()
284                 .withOptions(Arrays.asList("-source", sk.sourceNumber))
285                 .withSourceFromTemplate(template)
286                 .parse(this::check);
287     }
288 
check(Result<?> res)289     void check(Result<?> res) {
290         boolean errorExpected = (lk.arity() > 0 && !mks[0].compatibleWith(pks[0])) ||
291                 (lk.arity() > 1 && !mks[1].compatibleWith(pks[1]));
292 
293         if (lk.arity() == 2 &&
294                 (pks[0].explicitKind(sk) != pks[1].explicitKind(sk) ||
295                 pks[0].isVarargs())) {
296             errorExpected = true;
297         }
298 
299         errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
300                 lk.arity() > 0;
301 
302         if (errorExpected != res.hasErrors()) {
303             fail("invalid diagnostics for source:\n" +
304                 res.compilationInfo() +
305                 "\nFound error: " + res.hasErrors() +
306                 "\nExpected error: " + errorExpected);
307         }
308     }
309 }
310