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