1 /*
  2  * Copyright (c) 2011, 2015, 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 8003280 8006694 8129962
 27  * @summary Add lambda tests
 28  *  perform automated checks in type inference in lambda expressions
 29  *  in different contexts
 30  *  temporarily workaround combo tests are causing time out in several platforms
 31  * @enablePreview
 32  * @library /tools/javac/lib
 33  * @modules jdk.compiler/com.sun.tools.javac.api
 34  *          jdk.compiler/com.sun.tools.javac.file
 35  *          jdk.compiler/com.sun.tools.javac.util
 36  * @build combo.ComboTestHelper
 37  * @compile  TypeInferenceComboTest.java
 38  * @run main TypeInferenceComboTest
 39  */
 40 
 41 import java.io.IOException;
 42 
 43 import combo.ComboInstance;
 44 import combo.ComboParameter;
 45 import combo.ComboTask.Result;
 46 import combo.ComboTestHelper;
 47 
 48 public class TypeInferenceComboTest extends ComboInstance<TypeInferenceComboTest> {
 49     enum Context {
 50         ASSIGNMENT("SAM#Type s = #LBody;"),
 51         METHOD_CALL("#GenericDeclKind void method1(SAM#Type s) { }\n" +
 52                     "void method2() {\n" +
 53                     "    method1(#LBody);\n" +
 54                     "}"),
 55         RETURN_OF_METHOD("SAM#Type method1() {\n" +
 56                 "    return #LBody;\n" +
 57                 "}"),
 58         LAMBDA_RETURN_EXPRESSION("SAM2 s2 = () -> {return (SAM#Type)#LBody;};\n"),
 59         ARRAY_INITIALIZER("Object[] oarray = {\"a\", 1, (SAM#Type)#LBody};");
 60 
 61         String context;
 62 
 63         Context(String context) {
 64             this.context = context;
 65         }
 66 
 67         String getContext(SamKind sk, TypeKind samTargetT, Keyword kw,
 68                 TypeKind parameterT, TypeKind returnT, LambdaKind lk,
 69                 ParameterKind pk, GenericDeclKind gdk, LambdaBody lb) {
 70             String result = context;
 71             if (sk == SamKind.GENERIC) {
 72                 if(this == Context.METHOD_CALL) {
 73                     result = result.replaceAll("#GenericDeclKind",
 74                             gdk.getGenericDeclKind(samTargetT));
 75                     if(gdk == GenericDeclKind.NON_GENERIC)
 76                         result = result.replaceAll("#Type", "<" +
 77                                 samTargetT.typeStr + ">");
 78                     else //#GenericDeclKind is <T> or <T extends xxx>
 79                         result = result.replaceAll("#Type", "<T>");
 80                 }
 81                 else {
 82                     if(kw == Keyword.VOID)
 83                         result = result.replaceAll("#Type", "<" +
 84                                 samTargetT.typeStr + ">");
 85                     else
 86                         result = result.replaceAll("#Type", "<? " + kw.keyStr +
 87                                 " " + samTargetT.typeStr + ">");
 88                 }
 89             }
 90             else
 91                 result = result.replaceAll("#Type", "").
 92                         replaceAll("#GenericDeclKind", "");
 93 
 94             return result.replaceAll("#LBody",
 95                     lb.getLambdaBody(samTargetT, parameterT, returnT, lk, pk));
 96         }
 97     }
 98 
 99     enum SamKind {
100         GENERIC("interface SAM<T> { #R m(#ARG); }"),
101         NON_GENERIC("interface SAM { #R m(#ARG); }");
102 
103         String sam_str;
104 
105         SamKind(String sam_str) {
106             this.sam_str = sam_str;
107         }
108 
109         String getSam(TypeKind parameterT, TypeKind returnT) {
110             return sam_str.replaceAll("#ARG",
111                     parameterT == TypeKind.VOID ?
112                         "" : parameterT.typeStr + " arg")
113                     .replaceAll("#R", returnT.typeStr);
114         }
115     }
116 
117     enum TypeKind {
118         VOID("void", ""),
119         STRING("String", "\"hello\""),
120         INTEGER("Integer", "1"),
121         INT("int", "0"),
122         COMPARATOR("java.util.Comparator<String>",
123                 "(java.util.Comparator<String>)(a, b) -> a.length()-b.length()"),
124         SAM("SAM2", "null"),
125         GENERIC("T", null);
126 
127         String typeStr;
128         String valStr;
129 
130         TypeKind(String typeStr, String valStr) {
131             this.typeStr = typeStr;
132             this.valStr = valStr;
133         }
134     }
135 
136     enum LambdaKind {
137         EXPRESSION("#VAL"),
138         STATEMENT("{return #VAL;}");
139 
140         String stmt;
141 
142         LambdaKind(String stmt) {
143             this.stmt = stmt;
144         }
145     }
146 
147     enum ParameterKind {
148         EXPLICIT("#TYPE"),
149         IMPLICIT("");
150 
151         String paramTemplate;
152 
153         ParameterKind(String paramTemplate) {
154              this.paramTemplate = paramTemplate;
155         }
156     }
157 
158     enum Keyword {
159         SUPER("super"),
160         EXTENDS("extends"),
161         VOID("");
162 
163         String keyStr;
164 
165         Keyword(String keyStr) {
166             this.keyStr = keyStr;
167         }
168     }
169 
170     enum LambdaBody {
171         //no parameters, return type is one of the TypeKind
172         RETURN_VOID("() -> #RET"),
173         //has parameters, return type is one of the TypeKind
174         RETURN_ARG("(#PK arg) -> #RET");
175 
176         String bodyStr;
177 
178         LambdaBody(String bodyStr) {
179             this.bodyStr = bodyStr;
180         }
181 
182         String getLambdaBody(TypeKind samTargetT, TypeKind parameterT,
183                 TypeKind returnT, LambdaKind lk, ParameterKind pk) {
184             String result = bodyStr.replaceAll("#PK", pk.paramTemplate);
185 
186             if(result.contains("#TYPE")) {
187                 if (parameterT == TypeKind.GENERIC && this != RETURN_VOID)
188                     result = result.replaceAll("#TYPE",
189                             samTargetT == null? "": samTargetT.typeStr);
190                 else
191                     result = result.replaceAll("#TYPE", parameterT.typeStr);
192             }
193             if (this == RETURN_ARG && parameterT == returnT)
194                 return result.replaceAll("#RET", lk.stmt.replaceAll("#VAL", "arg"));
195             else {
196                 if(returnT != TypeKind.GENERIC)
197                     return result.replaceAll("#RET", lk.stmt.replaceAll("#VAL",
198                             (returnT==TypeKind.VOID &&
199                             lk==LambdaKind.EXPRESSION) ? "{}" : returnT.valStr));
200                 else
201                     return result.replaceAll("#RET",
202                             lk.stmt.replaceAll("#VAL", samTargetT.valStr));
203             }
204         }
205     }
206 
207     enum GenericDeclKind {
208         NON_GENERIC(""),
209         GENERIC_NOBOUND("<T>"),
210         GENERIC_BOUND("<T extends #ExtendedType>");
211         String typeStr;
212 
213         GenericDeclKind(String typeStr) {
214             this.typeStr = typeStr;
215         }
216 
217         String getGenericDeclKind(TypeKind et) {
218             return typeStr.replaceAll("#ExtendedType", et==null? "":et.typeStr);
219         }
220     }
221 
222     public static void main(String[] args) {
223         new ComboTestHelper<TypeInferenceComboTest>()
224                 .withFilter(TypeInferenceComboTest::badTestFilter)
225                 .withFilter(TypeInferenceComboTest::redundantTestFilter)
226                 .withDimension("SAM", (x, sam) -> x.samKind = sam, SamKind.values())
227                 .withDimension("SAMTARGET", (x, target) -> x.samTargetType = target, TypeKind.values())
228                 .withDimension("PARAMTYPE", (x, param) -> x.parameterType = param, TypeKind.values())
229                 .withDimension("RETTYPE", (x, ret) -> x.returnType = ret, TypeKind.values())
230                 .withDimension("CTX", (x, ctx) -> x.context = ctx, Context.values())
231                 .withDimension("LAMBDABODY", (x, body) -> x.lambdaBodyType = body, LambdaBody.values())
232                 .withDimension("LAMBDAKIND", (x, lambda) -> x.lambdaKind = lambda, LambdaKind.values())
233                 .withDimension("PARAMKIND", (x, param) -> x.parameterKind = param, ParameterKind.values())
234                 .withDimension("KEYWORD", (x, kw) -> x.keyword = kw, Keyword.values())
235                 .withDimension("GENDECL", (x, gk) -> x.genericDeclKind = gk, GenericDeclKind.values())
236                 .run(TypeInferenceComboTest::new);
237     }
238 
239     SamKind samKind;
240     TypeKind samTargetType;
241     TypeKind parameterType;
242     TypeKind returnType;
243     Context context;
244     LambdaBody lambdaBodyType;
245     LambdaKind lambdaKind;
246     ParameterKind parameterKind;
247     Keyword keyword;
248     GenericDeclKind genericDeclKind;
249 
250     boolean badTestFilter() {
251         if (samKind == SamKind.NON_GENERIC) {
252             return (parameterType != TypeKind.GENERIC && returnType != TypeKind.GENERIC);
253         } else {
254             return (samTargetType != TypeKind.VOID &&
255                    samTargetType != TypeKind.INT &&
256                    samTargetType != TypeKind.GENERIC &&
257                    (parameterType == TypeKind.GENERIC ||
258                    returnType == TypeKind.GENERIC));
259         }
260     }
261 
262     boolean redundantTestFilter() {
263         if (samKind == SamKind.NON_GENERIC) {
264             return keyword.ordinal() == 0 && samTargetType.ordinal() == 0 && genericDeclKind.ordinal() == 0;
265         } else {
266             return context == Context.METHOD_CALL || genericDeclKind.ordinal() == 0;
267         }
268     }
269 
270     String sam_template = "#{SAM}\n" +
271                          "interface SAM2 {\n" +
272                          "    SAM m();\n" +
273                          "}\n";
274 
275 
276     String client_template = "class Client { \n" +
277                              "    #{CONTEXT}\n" +
278                              "}";
279 
280     @Override
281     public void doWork() throws IOException {
282         newCompilationTask()
283                 .withSourceFromTemplate("Sam", sam_template, this::samClass)
284                 .withSourceFromTemplate("Client", client_template, this::clientContext)
285                 .analyze(res -> {
286             if (res.hasErrors() == checkTypeInference()) {
287                 fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
288             }
289         });
290     }
291 
292     ComboParameter samClass(String parameterName) {
293         switch (parameterName) {
294             case "SAM":
295                 return new ComboParameter.Constant<>(samKind.getSam(parameterType, returnType));
296             default:
297                 return null;
298         }
299     }
300 
301     ComboParameter clientContext(String parameterName) {
302         switch (parameterName) {
303             case "CONTEXT":
304                 return new ComboParameter.Constant<>(context.getContext(samKind, samTargetType,
305                         keyword, parameterType, returnType, lambdaKind, parameterKind, genericDeclKind, lambdaBodyType));
306             default:
307                 return null;
308         }
309     }
310 
311     boolean checkTypeInference() {
312         if (parameterType == TypeKind.VOID) {
313             if (lambdaBodyType != LambdaBody.RETURN_VOID)
314                 return false;
315         }
316         else if (lambdaBodyType != LambdaBody.RETURN_ARG)
317             return false;
318 
319         return true;
320     }
321 }