1 /*
  2  * Copyright (c) 2011, 2020, 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.file
 33  *          jdk.compiler/com.sun.tools.javac.util
 34  * @build combo.ComboTestHelper
 35 
 36  * @run main LambdaParserTest
 37  */
 38 
 39 import java.io.IOException;
 40 import java.util.Arrays;
 41 
 42 import combo.ComboInstance;
 43 import combo.ComboParameter;
 44 import combo.ComboTask.Result;
 45 import combo.ComboTestHelper;
 46 
 47 public class LambdaParserTest extends ComboInstance<LambdaParserTest> {
 48 
 49     enum LambdaKind implements ComboParameter {
 50         NILARY_EXPR("()->x"),
 51         NILARY_STMT("()->{ return x; }"),
 52         ONEARY_SHORT_EXPR("#{NAME}->x"),
 53         ONEARY_SHORT_STMT("#{NAME}->{ return x; }"),
 54         ONEARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME})->x"),
 55         ONEARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME})->{ return x; }"),
 56         TWOARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->x"),
 57         TWOARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->{ return x; }");
 58 
 59         String lambdaTemplate;
 60 
 61         LambdaKind(String lambdaTemplate) {
 62             this.lambdaTemplate = lambdaTemplate;
 63         }
 64 
 65         @Override
 66         public String expand(String optParameter) {
 67             return lambdaTemplate;
 68         }
 69 
 70         int arity() {
 71             switch (this) {
 72                 case NILARY_EXPR:
 73                 case NILARY_STMT: return 0;
 74                 case ONEARY_SHORT_EXPR:
 75                 case ONEARY_SHORT_STMT:
 76                 case ONEARY_EXPR:
 77                 case ONEARY_STMT: return 1;
 78                 case TWOARY_EXPR:
 79                 case TWOARY_STMT: return 2;
 80                 default: throw new AssertionError("Invalid lambda kind " + this);
 81             }
 82         }
 83 
 84         boolean isShort() {
 85             return this == ONEARY_SHORT_EXPR ||
 86                     this == ONEARY_SHORT_STMT;
 87         }
 88     }
 89 
 90     enum LambdaParameterName implements ComboParameter {
 91         IDENT("x"),
 92         UNDERSCORE("_");
 93 
 94         String nameStr;
 95 
 96         LambdaParameterName(String nameStr) {
 97             this.nameStr = nameStr;
 98         }
 99 
100         @Override
101         public String expand(String optParameter) {
102             return nameStr;
103         }
104     }
105 
106     enum SourceKind {
107         SOURCE_10("10"),
108         SOURCE_11("11");
109 
110         String sourceNumber;
111 
112         SourceKind(String sourceNumber) {
113             this.sourceNumber = sourceNumber;
114         }
115     }
116 
117     enum LambdaParameterKind implements ComboParameter {
118 
119         IMPLICIT_1("", ExplicitKind.IMPLICIT),
120         IMPLICIT_2("var", ExplicitKind.IMPLICIT_VAR),
121         EXPLICIT_SIMPLE("A", ExplicitKind.EXPLICIT),
122         EXPLICIT_SIMPLE_ARR1("A[]", ExplicitKind.EXPLICIT),
123         EXPLICIT_SIMPLE_ARR2("A[][]", ExplicitKind.EXPLICIT),
124         EXPLICIT_VARARGS("A...", ExplicitKind.EXPLICIT),
125         EXPLICIT_GENERIC1("A<X>", ExplicitKind.EXPLICIT),
126         EXPLICIT_GENERIC2("A<? extends X, ? super Y>", ExplicitKind.EXPLICIT),
127         EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>...", ExplicitKind.EXPLICIT),
128         EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]", ExplicitKind.EXPLICIT),
129         EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]", ExplicitKind.EXPLICIT);
130 
131         enum ExplicitKind {
132             IMPLICIT,
133             IMPLICIT_VAR,
134             EXPLICIT;
135         }
136 
137         String parameterType;
138         ExplicitKind explicitKind;
139 
140 
141         LambdaParameterKind(String parameterType, ExplicitKind ekind) {
142             this.parameterType = parameterType;
143             this.explicitKind = ekind;
144         }
145 
146         boolean isVarargs() {
147             return this == EXPLICIT_VARARGS ||
148                     this == EXPLICIT_GENERIC2_VARARGS;
149         }
150 
151         @Override
152         public String expand(String optParameter) {
153             return parameterType;
154         }
155 
156         ExplicitKind explicitKind(SourceKind sk) {
157             return explicitKind;
158         }
159     }
160 
161     enum ModifierKind implements ComboParameter {
162         NONE(""),
163         FINAL("final"),
164         PUBLIC("public"),
165         ANNO("@A");
166 
167         String modifier;
168 
169         ModifierKind(String modifier) {
170             this.modifier = modifier;
171         }
172 
173         boolean compatibleWith(LambdaParameterKind pk) {
174             switch (this) {
175                 case PUBLIC: return false;
176                 case ANNO:
177                 case FINAL: return pk != LambdaParameterKind.IMPLICIT_1;
178                 case NONE: return true;
179                 default: throw new AssertionError("Invalid modifier kind " + this);
180             }
181         }
182 
183         @Override
184         public String expand(String optParameter) {
185             return modifier;
186         }
187     }
188 
189     enum ExprKind implements ComboParameter {
190         NONE("#{LAMBDA}#{SUBEXPR}"),
191         SINGLE_PAREN1("(#{LAMBDA}#{SUBEXPR})"),
192         SINGLE_PAREN2("(#{LAMBDA})#{SUBEXPR}"),
193         DOUBLE_PAREN1("((#{LAMBDA}#{SUBEXPR}))"),
194         DOUBLE_PAREN2("((#{LAMBDA})#{SUBEXPR})"),
195         DOUBLE_PAREN3("((#{LAMBDA}))#{SUBEXPR}");
196 
197         String expressionTemplate;
198 
199         ExprKind(String expressionTemplate) {
200             this.expressionTemplate = expressionTemplate;
201         }
202 
203         @Override
204         public String expand(String optParameter) {
205             return expressionTemplate;
206         }
207     }
208 
209     enum SubExprKind implements ComboParameter {
210         NONE(""),
211         SELECT_FIELD(".f"),
212         SELECT_METHOD(".f()"),
213         SELECT_NEW(".new Foo()"),
214         POSTINC("++"),
215         POSTDEC("--");
216 
217         String subExpression;
218 
219         SubExprKind(String subExpression) {
220             this.subExpression = subExpression;
221         }
222 
223         @Override
224         public String expand(String optParameter) {
225             return subExpression;
226         }
227     }
228 
229     public static void main(String... args) throws Exception {
230         new ComboTestHelper<LambdaParserTest>()
231                 .withFilter(LambdaParserTest::redundantTestFilter)
232                 .withDimension("SOURCE", (x, sk) -> x.sk = sk, SourceKind.values())
233                 .withDimension("LAMBDA", (x, lk) -> x.lk = lk, LambdaKind.values())
234                 .withDimension("NAME", (x, name) -> x.pn = name, LambdaParameterName.values())
235                 .withArrayDimension("TYPE", (x, type, idx) -> x.pks[idx] = type, 2, LambdaParameterKind.values())
236                 .withArrayDimension("MOD", (x, mod, idx) -> x.mks[idx] = mod, 2, ModifierKind.values())
237                 .withDimension("EXPR", (x, exp) -> x.exp = exp, ExprKind.values())
238                 .withDimension("SUBEXPR", (x, sub) -> x.sub = sub, SubExprKind.values())
239                 .run(LambdaParserTest::new);
240     }
241 
242     LambdaParameterKind[] pks = new LambdaParameterKind[2];
243     ModifierKind[] mks = new ModifierKind[2];
244     LambdaKind lk;
245     LambdaParameterName pn;
246     SourceKind sk;
247     ExprKind exp;
248     SubExprKind sub;
249 
250     boolean redundantTestFilter() {
251         if (sub == SubExprKind.NONE) {
252             switch (exp) {
253                 //followings combinations with empty sub-expressions produces the same source
254                 case SINGLE_PAREN2, DOUBLE_PAREN2, DOUBLE_PAREN3: return false;
255             }
256         } else {
257             switch (lk) {
258                 //any non-empty subexpression does not combine with lambda statements
259                 case NILARY_STMT, ONEARY_SHORT_STMT, ONEARY_STMT, TWOARY_STMT: return false;
260             }
261         }
262         switch (lk) {
263             //parameters not present in the expression are redundant
264             case NILARY_EXPR, NILARY_STMT:
265                 if (pn.ordinal() != 0) return false;
266             case ONEARY_SHORT_EXPR, ONEARY_SHORT_STMT:
267                 if (pks[0].ordinal() != 0 || mks[0].ordinal() != 0) return false;
268             case ONEARY_EXPR, ONEARY_STMT :
269                 if (pks[1].ordinal() != 0 || mks[1].ordinal() != 0) return false;
270         }
271         return true;
272     }
273 
274     String template = "@interface A { }\n" +
275             "class Test {\n" +
276             "   SAM s = #{EXPR};\n" +
277             "}";
278 
279     @Override
280     public void doWork() throws IOException {
281         newCompilationTask()
282                 .withOptions(Arrays.asList("-source", sk.sourceNumber))
283                 .withSourceFromTemplate(template)
284                 .parse(this::check);
285     }
286 
287     void check(Result<?> res) {
288         boolean errorExpected = (lk.arity() > 0 && !mks[0].compatibleWith(pks[0])) ||
289                 (lk.arity() > 1 && !mks[1].compatibleWith(pks[1]));
290 
291         if (lk.arity() == 2 &&
292                 (pks[0].explicitKind(sk) != pks[1].explicitKind(sk) ||
293                 pks[0].isVarargs())) {
294             errorExpected = true;
295         }
296 
297         errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
298                 lk.arity() > 0;
299 
300         for (int i = 0; i < lk.arity(); i++) {
301             if (!lk.isShort() &&
302                     pks[i].explicitKind(sk) == LambdaParameterKind.ExplicitKind.IMPLICIT_VAR &&
303                     sk == SourceKind.SOURCE_10) {
304                 errorExpected = true;
305                 break;
306             }
307         }
308 
309         if (errorExpected != res.hasErrors()) {
310             fail("invalid diagnostics for source:\n" +
311                 res.compilationInfo() +
312                 "\nFound error: " + res.hasErrors() +
313                 "\nExpected error: " + errorExpected);
314         }
315     }
316 }