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