1 /*
2  * Copyright (c) 2011, 2013, 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
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 ../lib
31  * @build JavacTestingAbstractThreadedTest
32  * @run main/othervm LambdaParserTest
33  */
34 
35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
36 // see JDK-8006746
37 
38 import java.net.URI;
39 import java.util.Arrays;
40 import javax.tools.Diagnostic;
41 import javax.tools.JavaFileObject;
42 import javax.tools.SimpleJavaFileObject;
43 import com.sun.source.util.JavacTask;
44 
45 public class LambdaParserTest
46     extends JavacTestingAbstractThreadedTest
47     implements Runnable {
48 
49     enum LambdaKind {
50         NILARY_EXPR("()->x"),
51         NILARY_STMT("()->{ return x; }"),
52         ONEARY_SHORT_EXPR("#PN->x"),
53         ONEARY_SHORT_STMT("#PN->{ return x; }"),
54         ONEARY_EXPR("(#M1 #T1 #PN)->x"),
55         ONEARY_STMT("(#M1 #T1 #PN)->{ return x; }"),
56         TWOARY_EXPR("(#M1 #T1 #PN, #M2 #T2 y)->x"),
57         TWOARY_STMT("(#M1 #T1 #PN, #M2 #T2 y)->{ return x; }");
58 
59         String lambdaTemplate;
60 
LambdaKind(String lambdaTemplate)61         LambdaKind(String lambdaTemplate) {
62             this.lambdaTemplate = lambdaTemplate;
63         }
64 
getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn)65         String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2,
66                 ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn) {
67             return lambdaTemplate.replaceAll("#M1", mk1.modifier)
68                     .replaceAll("#M2", mk2.modifier)
69                     .replaceAll("#T1", pk1.parameterType)
70                     .replaceAll("#T2", pk2.parameterType)
71                     .replaceAll("#PN", pn.nameStr);
72         }
73 
arity()74         int arity() {
75             switch (this) {
76                 case NILARY_EXPR:
77                 case NILARY_STMT: return 0;
78                 case ONEARY_SHORT_EXPR:
79                 case ONEARY_SHORT_STMT:
80                 case ONEARY_EXPR:
81                 case ONEARY_STMT: return 1;
82                 case TWOARY_EXPR:
83                 case TWOARY_STMT: return 2;
84                 default: throw new AssertionError("Invalid lambda kind " + this);
85             }
86         }
87 
isShort()88         boolean isShort() {
89             return this == ONEARY_SHORT_EXPR ||
90                     this == ONEARY_SHORT_STMT;
91         }
92     }
93 
94     enum LambdaParameterName {
95         IDENT("x"),
96         UNDERSCORE("_");
97 
98         String nameStr;
99 
LambdaParameterName(String nameStr)100         LambdaParameterName(String nameStr) {
101             this.nameStr = nameStr;
102         }
103     }
104 
105     enum LambdaParameterKind {
106         IMPLICIT(""),
107         EXPLIICT_SIMPLE("A"),
108         EXPLIICT_SIMPLE_ARR1("A[]"),
109         EXPLIICT_SIMPLE_ARR2("A[][]"),
110         EXPLICIT_VARARGS("A..."),
111         EXPLICIT_GENERIC1("A<X>"),
112         EXPLICIT_GENERIC2("A<? extends X, ? super Y>"),
113         EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>..."),
114         EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]"),
115         EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]");
116 
117         String parameterType;
118 
LambdaParameterKind(String parameterType)119         LambdaParameterKind(String parameterType) {
120             this.parameterType = parameterType;
121         }
122 
explicit()123         boolean explicit() {
124             return this != IMPLICIT;
125         }
126 
isVarargs()127         boolean isVarargs() {
128             return this == EXPLICIT_VARARGS ||
129                     this == EXPLICIT_GENERIC2_VARARGS;
130         }
131     }
132 
133     enum ModifierKind {
134         NONE(""),
135         FINAL("final"),
136         PUBLIC("public");
137 
138         String modifier;
139 
ModifierKind(String modifier)140         ModifierKind(String modifier) {
141             this.modifier = modifier;
142         }
143 
compatibleWith(LambdaParameterKind pk)144         boolean compatibleWith(LambdaParameterKind pk) {
145             switch (this) {
146                 case PUBLIC: return false;
147                 case FINAL: return pk != LambdaParameterKind.IMPLICIT;
148                 case NONE: return true;
149                 default: throw new AssertionError("Invalid modifier kind " + this);
150             }
151         }
152     }
153 
154     enum ExprKind {
155         NONE("#L#S"),
156         SINGLE_PAREN1("(#L#S)"),
157         SINGLE_PAREN2("(#L)#S"),
158         DOUBLE_PAREN1("((#L#S))"),
159         DOUBLE_PAREN2("((#L)#S)"),
160         DOUBLE_PAREN3("((#L))#S");
161 
162         String expressionTemplate;
163 
ExprKind(String expressionTemplate)164         ExprKind(String expressionTemplate) {
165             this.expressionTemplate = expressionTemplate;
166         }
167 
expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk)168         String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2,
169                 ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk) {
170             return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2, pn))
171                     .replaceAll("#S", sk.subExpression);
172         }
173     }
174 
175     enum SubExprKind {
176         NONE(""),
177         SELECT_FIELD(".f"),
178         SELECT_METHOD(".f()"),
179         SELECT_NEW(".new Foo()"),
180         POSTINC("++"),
181         POSTDEC("--");
182 
183         String subExpression;
184 
SubExprKind(String subExpression)185         SubExprKind(String subExpression) {
186             this.subExpression = subExpression;
187         }
188     }
189 
main(String... args)190     public static void main(String... args) throws Exception {
191         for (LambdaKind lk : LambdaKind.values()) {
192             for (LambdaParameterName pn : LambdaParameterName.values()) {
193                 for (LambdaParameterKind pk1 : LambdaParameterKind.values()) {
194                     if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT)
195                         continue;
196                     for (LambdaParameterKind pk2 : LambdaParameterKind.values()) {
197                         if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT)
198                             continue;
199                         for (ModifierKind mk1 : ModifierKind.values()) {
200                             if (mk1 != ModifierKind.NONE && lk.isShort())
201                                 continue;
202                             if (lk.arity() < 1 && mk1 != ModifierKind.NONE)
203                                 continue;
204                             for (ModifierKind mk2 : ModifierKind.values()) {
205                                 if (lk.arity() < 2 && mk2 != ModifierKind.NONE)
206                                     continue;
207                                 for (SubExprKind sk : SubExprKind.values()) {
208                                     for (ExprKind ek : ExprKind.values()) {
209                                         pool.execute(
210                                             new LambdaParserTest(pk1, pk2, mk1,
211                                                                  mk2, lk, sk, ek, pn));
212                                     }
213                                 }
214                             }
215                         }
216                     }
217                 }
218             }
219         }
220 
221         checkAfterExec();
222     }
223 
224     LambdaParameterKind pk1;
225     LambdaParameterKind pk2;
226     ModifierKind mk1;
227     ModifierKind mk2;
228     LambdaKind lk;
229     LambdaParameterName pn;
230     SubExprKind sk;
231     ExprKind ek;
232     JavaSource source;
233     DiagnosticChecker diagChecker;
234 
LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaKind lk, SubExprKind sk, ExprKind ek, LambdaParameterName pn)235     LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2,
236             ModifierKind mk1, ModifierKind mk2, LambdaKind lk,
237             SubExprKind sk, ExprKind ek, LambdaParameterName pn) {
238         this.pk1 = pk1;
239         this.pk2 = pk2;
240         this.mk1 = mk1;
241         this.mk2 = mk2;
242         this.lk = lk;
243         this.pn = pn;
244         this.sk = sk;
245         this.ek = ek;
246         this.source = new JavaSource();
247         this.diagChecker = new DiagnosticChecker();
248     }
249 
250     class JavaSource extends SimpleJavaFileObject {
251 
252         String template = "class Test {\n" +
253                           "   SAM s = #E;\n" +
254                           "}";
255 
256         String source;
257 
JavaSource()258         public JavaSource() {
259             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
260             source = template.replaceAll("#E",
261                     ek.expressionString(pk1, pk2, mk1, mk2, lk, pn, sk));
262         }
263 
264         @Override
getCharContent(boolean ignoreEncodingErrors)265         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
266             return source;
267         }
268     }
269 
run()270     public void run() {
271         JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
272                 null, null, Arrays.asList(source));
273         try {
274             ct.parse();
275         } catch (Throwable ex) {
276             processException(ex);
277             return;
278         }
279         check();
280     }
281 
check()282     void check() {
283         checkCount.incrementAndGet();
284 
285         boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) ||
286                 (lk.arity() > 1 && !mk2.compatibleWith(pk2));
287 
288         if (lk.arity() == 2 &&
289                 (pk1.explicit() != pk2.explicit() ||
290                 pk1.isVarargs())) {
291             errorExpected = true;
292         }
293 
294         errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
295                 lk.arity() > 0;
296 
297         if (errorExpected != diagChecker.errorFound) {
298             throw new Error("invalid diagnostics for source:\n" +
299                 source.getCharContent(true) +
300                 "\nFound error: " + diagChecker.errorFound +
301                 "\nExpected error: " + errorExpected);
302         }
303     }
304 
305     static class DiagnosticChecker
306         implements javax.tools.DiagnosticListener<JavaFileObject> {
307 
308         boolean errorFound;
309 
report(Diagnostic<? extends JavaFileObject> diagnostic)310         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
311             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
312                 errorFound = true;
313             }
314         }
315     }
316 
317 }
318