1 /* 2 * Copyright (c) 2011, 2013, 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 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 ../lib 31 * @build JavacTestingAbstractThreadedTest 32 * @run main/othervm LambdaParserTest 33 */ 34 35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) 36 // see JDK-8006746 37 38 import java.net.URI; 39 import java.util.Arrays; 40 import javax.tools.Diagnostic; 41 import javax.tools.JavaFileObject; 42 import javax.tools.SimpleJavaFileObject; 43 import com.sun.source.util.JavacTask; 44 45 public class LambdaParserTest 46 extends JavacTestingAbstractThreadedTest 47 implements Runnable { 48 49 enum LambdaKind { 50 NILARY_EXPR("()->x"), 51 NILARY_STMT("()->{ return x; }"), 52 ONEARY_SHORT_EXPR("#PN->x"), 53 ONEARY_SHORT_STMT("#PN->{ return x; }"), 54 ONEARY_EXPR("(#M1 #T1 #PN)->x"), 55 ONEARY_STMT("(#M1 #T1 #PN)->{ return x; }"), 56 TWOARY_EXPR("(#M1 #T1 #PN, #M2 #T2 y)->x"), 57 TWOARY_STMT("(#M1 #T1 #PN, #M2 #T2 y)->{ return x; }"); 58 59 String lambdaTemplate; 60 LambdaKind(String lambdaTemplate)61 LambdaKind(String lambdaTemplate) { 62 this.lambdaTemplate = lambdaTemplate; 63 } 64 getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn)65 String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2, 66 ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn) { 67 return lambdaTemplate.replaceAll("#M1", mk1.modifier) 68 .replaceAll("#M2", mk2.modifier) 69 .replaceAll("#T1", pk1.parameterType) 70 .replaceAll("#T2", pk2.parameterType) 71 .replaceAll("#PN", pn.nameStr); 72 } 73 arity()74 int arity() { 75 switch (this) { 76 case NILARY_EXPR: 77 case NILARY_STMT: return 0; 78 case ONEARY_SHORT_EXPR: 79 case ONEARY_SHORT_STMT: 80 case ONEARY_EXPR: 81 case ONEARY_STMT: return 1; 82 case TWOARY_EXPR: 83 case TWOARY_STMT: return 2; 84 default: throw new AssertionError("Invalid lambda kind " + this); 85 } 86 } 87 isShort()88 boolean isShort() { 89 return this == ONEARY_SHORT_EXPR || 90 this == ONEARY_SHORT_STMT; 91 } 92 } 93 94 enum LambdaParameterName { 95 IDENT("x"), 96 UNDERSCORE("_"); 97 98 String nameStr; 99 LambdaParameterName(String nameStr)100 LambdaParameterName(String nameStr) { 101 this.nameStr = nameStr; 102 } 103 } 104 105 enum LambdaParameterKind { 106 IMPLICIT(""), 107 EXPLIICT_SIMPLE("A"), 108 EXPLIICT_SIMPLE_ARR1("A[]"), 109 EXPLIICT_SIMPLE_ARR2("A[][]"), 110 EXPLICIT_VARARGS("A..."), 111 EXPLICIT_GENERIC1("A<X>"), 112 EXPLICIT_GENERIC2("A<? extends X, ? super Y>"), 113 EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>..."), 114 EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]"), 115 EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]"); 116 117 String parameterType; 118 LambdaParameterKind(String parameterType)119 LambdaParameterKind(String parameterType) { 120 this.parameterType = parameterType; 121 } 122 explicit()123 boolean explicit() { 124 return this != IMPLICIT; 125 } 126 isVarargs()127 boolean isVarargs() { 128 return this == EXPLICIT_VARARGS || 129 this == EXPLICIT_GENERIC2_VARARGS; 130 } 131 } 132 133 enum ModifierKind { 134 NONE(""), 135 FINAL("final"), 136 PUBLIC("public"); 137 138 String modifier; 139 ModifierKind(String modifier)140 ModifierKind(String modifier) { 141 this.modifier = modifier; 142 } 143 compatibleWith(LambdaParameterKind pk)144 boolean compatibleWith(LambdaParameterKind pk) { 145 switch (this) { 146 case PUBLIC: return false; 147 case FINAL: return pk != LambdaParameterKind.IMPLICIT; 148 case NONE: return true; 149 default: throw new AssertionError("Invalid modifier kind " + this); 150 } 151 } 152 } 153 154 enum ExprKind { 155 NONE("#L#S"), 156 SINGLE_PAREN1("(#L#S)"), 157 SINGLE_PAREN2("(#L)#S"), 158 DOUBLE_PAREN1("((#L#S))"), 159 DOUBLE_PAREN2("((#L)#S)"), 160 DOUBLE_PAREN3("((#L))#S"); 161 162 String expressionTemplate; 163 ExprKind(String expressionTemplate)164 ExprKind(String expressionTemplate) { 165 this.expressionTemplate = expressionTemplate; 166 } 167 expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk)168 String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2, 169 ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk) { 170 return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2, pn)) 171 .replaceAll("#S", sk.subExpression); 172 } 173 } 174 175 enum SubExprKind { 176 NONE(""), 177 SELECT_FIELD(".f"), 178 SELECT_METHOD(".f()"), 179 SELECT_NEW(".new Foo()"), 180 POSTINC("++"), 181 POSTDEC("--"); 182 183 String subExpression; 184 SubExprKind(String subExpression)185 SubExprKind(String subExpression) { 186 this.subExpression = subExpression; 187 } 188 } 189 main(String... args)190 public static void main(String... args) throws Exception { 191 for (LambdaKind lk : LambdaKind.values()) { 192 for (LambdaParameterName pn : LambdaParameterName.values()) { 193 for (LambdaParameterKind pk1 : LambdaParameterKind.values()) { 194 if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) 195 continue; 196 for (LambdaParameterKind pk2 : LambdaParameterKind.values()) { 197 if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) 198 continue; 199 for (ModifierKind mk1 : ModifierKind.values()) { 200 if (mk1 != ModifierKind.NONE && lk.isShort()) 201 continue; 202 if (lk.arity() < 1 && mk1 != ModifierKind.NONE) 203 continue; 204 for (ModifierKind mk2 : ModifierKind.values()) { 205 if (lk.arity() < 2 && mk2 != ModifierKind.NONE) 206 continue; 207 for (SubExprKind sk : SubExprKind.values()) { 208 for (ExprKind ek : ExprKind.values()) { 209 pool.execute( 210 new LambdaParserTest(pk1, pk2, mk1, 211 mk2, lk, sk, ek, pn)); 212 } 213 } 214 } 215 } 216 } 217 } 218 } 219 } 220 221 checkAfterExec(); 222 } 223 224 LambdaParameterKind pk1; 225 LambdaParameterKind pk2; 226 ModifierKind mk1; 227 ModifierKind mk2; 228 LambdaKind lk; 229 LambdaParameterName pn; 230 SubExprKind sk; 231 ExprKind ek; 232 JavaSource source; 233 DiagnosticChecker diagChecker; 234 LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, ModifierKind mk2, LambdaKind lk, SubExprKind sk, ExprKind ek, LambdaParameterName pn)235 LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, 236 ModifierKind mk1, ModifierKind mk2, LambdaKind lk, 237 SubExprKind sk, ExprKind ek, LambdaParameterName pn) { 238 this.pk1 = pk1; 239 this.pk2 = pk2; 240 this.mk1 = mk1; 241 this.mk2 = mk2; 242 this.lk = lk; 243 this.pn = pn; 244 this.sk = sk; 245 this.ek = ek; 246 this.source = new JavaSource(); 247 this.diagChecker = new DiagnosticChecker(); 248 } 249 250 class JavaSource extends SimpleJavaFileObject { 251 252 String template = "class Test {\n" + 253 " SAM s = #E;\n" + 254 "}"; 255 256 String source; 257 JavaSource()258 public JavaSource() { 259 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 260 source = template.replaceAll("#E", 261 ek.expressionString(pk1, pk2, mk1, mk2, lk, pn, sk)); 262 } 263 264 @Override getCharContent(boolean ignoreEncodingErrors)265 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 266 return source; 267 } 268 } 269 run()270 public void run() { 271 JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker, 272 null, null, Arrays.asList(source)); 273 try { 274 ct.parse(); 275 } catch (Throwable ex) { 276 processException(ex); 277 return; 278 } 279 check(); 280 } 281 check()282 void check() { 283 checkCount.incrementAndGet(); 284 285 boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) || 286 (lk.arity() > 1 && !mk2.compatibleWith(pk2)); 287 288 if (lk.arity() == 2 && 289 (pk1.explicit() != pk2.explicit() || 290 pk1.isVarargs())) { 291 errorExpected = true; 292 } 293 294 errorExpected |= pn == LambdaParameterName.UNDERSCORE && 295 lk.arity() > 0; 296 297 if (errorExpected != diagChecker.errorFound) { 298 throw new Error("invalid diagnostics for source:\n" + 299 source.getCharContent(true) + 300 "\nFound error: " + diagChecker.errorFound + 301 "\nExpected error: " + errorExpected); 302 } 303 } 304 305 static class DiagnosticChecker 306 implements javax.tools.DiagnosticListener<JavaFileObject> { 307 308 boolean errorFound; 309 report(Diagnostic<? extends JavaFileObject> diagnostic)310 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 311 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 312 errorFound = true; 313 } 314 } 315 } 316 317 } 318