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