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 }