Tue, 14 May 2013 11:11:09 -0700
8012556: Implement lambda methods on interfaces as static
8006140: Javac NPE compiling Lambda expression on initialization expression of static field in interface
Summary: Lambdas occurring in static contexts or those not needing instance information should be generated into static methods. This has long been the case for classes. However, as a work-around to the lack of support for statics on interfaces, interface lambda methods have been generated into default methods. For lambdas in interface static contexts (fields and static methods) this causes an NPE in javac because there is no 'this'. MethodHandles now support static methods on interfaces. This changeset allows lambda methods to be generated as static interface methods. An existing bug in Hotspot (8013875) is exposed in a test when the "-esa" flag is used. This test and another test that already exposed this bug have been marked with @ignore.
Reviewed-by: mcimadamore
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 */
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 */
35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
36 // see JDK-8006746
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;
45 public class LambdaParserTest
46 extends JavacTestingAbstractThreadedTest
47 implements Runnable {
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; }");
59 String lambdaTemplate;
61 LambdaKind(String lambdaTemplate) {
62 this.lambdaTemplate = lambdaTemplate;
63 }
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 }
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 }
88 boolean isShort() {
89 return this == ONEARY_SHORT_EXPR ||
90 this == ONEARY_SHORT_STMT;
91 }
92 }
94 enum LambdaParameterName {
95 IDENT("x"),
96 UNDERSCORE("_");
98 String nameStr;
100 LambdaParameterName(String nameStr) {
101 this.nameStr = nameStr;
102 }
103 }
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>[][]");
117 String parameterType;
119 LambdaParameterKind(String parameterType) {
120 this.parameterType = parameterType;
121 }
123 boolean explicit() {
124 return this != IMPLICIT;
125 }
127 boolean isVarargs() {
128 return this == EXPLICIT_VARARGS ||
129 this == EXPLICIT_GENERIC2_VARARGS;
130 }
131 }
133 enum ModifierKind {
134 NONE(""),
135 FINAL("final"),
136 PUBLIC("public");
138 String modifier;
140 ModifierKind(String modifier) {
141 this.modifier = modifier;
142 }
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 }
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");
162 String expressionTemplate;
164 ExprKind(String expressionTemplate) {
165 this.expressionTemplate = expressionTemplate;
166 }
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 }
175 enum SubExprKind {
176 NONE(""),
177 SELECT_FIELD(".f"),
178 SELECT_METHOD(".f()"),
179 SELECT_NEW(".new Foo()"),
180 POSTINC("++"),
181 POSTDEC("--");
183 String subExpression;
185 SubExprKind(String subExpression) {
186 this.subExpression = subExpression;
187 }
188 }
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 }
221 checkAfterExec();
222 }
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;
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 }
250 class JavaSource extends SimpleJavaFileObject {
252 String template = "class Test {\n" +
253 " SAM s = #E;\n" +
254 "}";
256 String source;
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 }
264 @Override
265 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
266 return source;
267 }
268 }
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 }
282 void check() {
283 checkCount.incrementAndGet();
285 boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) ||
286 (lk.arity() > 1 && !mk2.compatibleWith(pk2));
288 if (lk.arity() == 2 &&
289 (pk1.explicit() != pk2.explicit() ||
290 pk1.isVarargs())) {
291 errorExpected = true;
292 }
294 errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
295 lk.arity() > 0;
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 }
305 static class DiagnosticChecker
306 implements javax.tools.DiagnosticListener<JavaFileObject> {
308 boolean errorFound;
310 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
311 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
312 errorFound = true;
313 }
314 }
315 }
317 }