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 }