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) 2012, 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 8003280 8006694
27 * @summary Add lambda tests
28 * Automatic test for checking correctness of structural most specific test routine
29 * temporarily workaround combo tests are causing time out in several platforms
30 * @library ../../lib
31 * @build JavacTestingAbstractThreadedTest
32 * @run main/othervm/timeout=600 StructuralMostSpecificTest
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;
44 import com.sun.tools.javac.api.ClientCodeWrapper;
45 import com.sun.tools.javac.util.JCDiagnostic;
47 public class StructuralMostSpecificTest
48 extends JavacTestingAbstractThreadedTest
49 implements Runnable {
51 enum RetTypeKind {
52 SHORT("short"),
53 INT("int"),
54 OBJECT("Object"),
55 INTEGER("Integer"),
56 VOID("void"),
57 J_L_VOID("Void");
59 String retTypeStr;
61 RetTypeKind(String retTypeStr) {
62 this.retTypeStr = retTypeStr;
63 }
65 boolean moreSpecificThan(RetTypeKind rk) {
66 return moreSpecificThan[this.ordinal()][rk.ordinal()];
67 }
69 static boolean[][] moreSpecificThan = {
70 // SHORT | INT | OBJECT | INTEGER | VOID | J_L_VOID
71 /* SHORT */ { true , true , true , false , false , false },
72 /* INT */ { false , true , true , true , false , false },
73 /* OBJECT */ { false , false , true , false , false , false },
74 /* INTEGER */ { false , false , true , true , false , false },
75 /* VOID */ { false , false , false , false , true , true },
76 /* J_L_VOID */{ false , false , true , false , false , true } };
77 }
79 enum ArgTypeKind {
80 SHORT("short"),
81 INT("int"),
82 BOOLEAN("boolean"),
83 OBJECT("Object"),
84 INTEGER("Integer"),
85 DOUBLE("Double");
87 String argTypeStr;
89 ArgTypeKind(String typeStr) {
90 this.argTypeStr = typeStr;
91 }
92 }
94 enum ExceptionKind {
95 NONE(""),
96 EXCEPTION("throws Exception"),
97 SQL_EXCEPTION("throws java.sql.SQLException"),
98 IO_EXCEPTION("throws java.io.IOException");
100 String exceptionStr;
102 ExceptionKind(String exceptionStr) {
103 this.exceptionStr = exceptionStr;
104 }
105 }
107 enum LambdaReturnKind {
108 VOID("return;"),
109 SHORT("return (short)0;"),
110 INT("return 0;"),
111 INTEGER("return (Integer)null;"),
112 NULL("return null;");
114 String retStr;
116 LambdaReturnKind(String retStr) {
117 this.retStr = retStr;
118 }
120 boolean compatibleWith(RetTypeKind rk) {
121 return compatibleWith[rk.ordinal()][ordinal()];
122 }
124 static boolean[][] compatibleWith = {
125 // VOID | SHORT | INT | INTEGER | NULL
126 /* SHORT */ { false , true , false , false , false },
127 /* INT */ { false , true , true , true , false },
128 /* OBJECT */ { false , true , true , true , true },
129 /* INTEGER */ { false , false , true , true , true },
130 /* VOID */ { true , false , false , false , false },
131 /* J_L_VOID */{ false , false , false , false , true } };
133 boolean needsConversion(RetTypeKind rk) {
134 return needsConversion[rk.ordinal()][ordinal()];
135 }
137 static boolean[][] needsConversion = {
138 // VOID | SHORT | INT | INTEGER | NULL
139 /* SHORT */ { false , false , false , false , false },
140 /* INT */ { false , false , false , true , false },
141 /* OBJECT */ { false , true , true , false , false },
142 /* INTEGER */ { false , false , true , false , false },
143 /* VOID */ { false , false , false , false , false },
144 /* J_L_VOID */{ true , false , false , false , false } };
145 }
147 public static void main(String... args) throws Exception {
148 for (LambdaReturnKind lrk : LambdaReturnKind.values()) {
149 for (RetTypeKind rk1 : RetTypeKind.values()) {
150 for (RetTypeKind rk2 : RetTypeKind.values()) {
151 for (ExceptionKind ek1 : ExceptionKind.values()) {
152 for (ExceptionKind ek2 : ExceptionKind.values()) {
153 for (ArgTypeKind ak11 : ArgTypeKind.values()) {
154 for (ArgTypeKind ak12 : ArgTypeKind.values()) {
155 pool.execute(
156 new StructuralMostSpecificTest(lrk, rk1,
157 rk2, ek1, ek2, ak11, ak12));
158 }
159 }
160 }
161 }
162 }
163 }
164 }
166 checkAfterExec();
167 }
169 LambdaReturnKind lrk;
170 RetTypeKind rt1, rt2;
171 ArgTypeKind ak1, ak2;
172 ExceptionKind ek1, ek2;
173 JavaSource source;
174 DiagnosticChecker diagChecker;
176 StructuralMostSpecificTest(LambdaReturnKind lrk, RetTypeKind rt1, RetTypeKind rt2,
177 ExceptionKind ek1, ExceptionKind ek2, ArgTypeKind ak1, ArgTypeKind ak2) {
178 this.lrk = lrk;
179 this.rt1 = rt1;
180 this.rt2 = rt2;
181 this.ek1 = ek1;
182 this.ek2 = ek2;
183 this.ak1 = ak1;
184 this.ak2 = ak2;
185 this.source = new JavaSource();
186 this.diagChecker = new DiagnosticChecker();
187 }
189 class JavaSource extends SimpleJavaFileObject {
191 String template = "interface SAM1 {\n" +
192 " #R1 m(#A1 a1) #E1;\n" +
193 "}\n" +
194 "interface SAM2 {\n" +
195 " #R2 m(#A2 a1) #E2;\n" +
196 "}\n" +
197 "class Test {\n" +
198 " void m(SAM1 s) { }\n" +
199 " void m(SAM2 s) { }\n" +
200 " { m(x->{ #LR }); }\n" +
201 "}\n";
203 String source;
205 public JavaSource() {
206 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
207 source = template.replaceAll("#LR", lrk.retStr)
208 .replaceAll("#R1", rt1.retTypeStr)
209 .replaceAll("#R2", rt2.retTypeStr)
210 .replaceAll("#A1", ak1.argTypeStr)
211 .replaceAll("#A2", ak2.argTypeStr)
212 .replaceAll("#E1", ek1.exceptionStr)
213 .replaceAll("#E2", ek2.exceptionStr);
214 }
216 @Override
217 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
218 return source;
219 }
220 }
222 public void run() {
223 JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
224 Arrays.asList("-XDverboseResolution=all,-predef,-internal,-object-init"),
225 null, Arrays.asList(source));
226 try {
227 ct.analyze();
228 } catch (Throwable ex) {
229 throw new
230 AssertionError("Error thron when analyzing the following source:\n" +
231 source.getCharContent(true));
232 }
233 check();
234 }
236 void check() {
237 checkCount.incrementAndGet();
239 if (!lrk.compatibleWith(rt1) || !lrk.compatibleWith(rt2))
240 return;
242 if (lrk.needsConversion(rt1) != lrk.needsConversion(rt2))
243 return;
245 boolean m1MoreSpecific = moreSpecific(rt1, rt2, ek1, ek2, ak1, ak2);
246 boolean m2MoreSpecific = moreSpecific(rt2, rt1, ek2, ek1, ak2, ak1);
248 boolean ambiguous = (m1MoreSpecific == m2MoreSpecific);
250 if (ambiguous != diagChecker.ambiguityFound) {
251 throw new Error("invalid diagnostics for source:\n" +
252 source.getCharContent(true) +
253 "\nAmbiguity found: " + diagChecker.ambiguityFound +
254 "\nm1 more specific: " + m1MoreSpecific +
255 "\nm2 more specific: " + m2MoreSpecific +
256 "\nexpected ambiguity: " + ambiguous);
257 }
259 if (!ambiguous) {
260 String sigToCheck = m1MoreSpecific ? "m(SAM1)" : "m(SAM2)";
261 if (!sigToCheck.equals(diagChecker.mostSpecificSig)) {
262 throw new Error("invalid most specific method selected:\n" +
263 source.getCharContent(true) +
264 "\nMost specific found: " + diagChecker.mostSpecificSig +
265 "\nm1 more specific: " + m1MoreSpecific +
266 "\nm2 more specific: " + m2MoreSpecific);
267 }
268 }
269 }
271 boolean moreSpecific(RetTypeKind rk1, RetTypeKind rk2, ExceptionKind ek1,
272 ExceptionKind ek2, ArgTypeKind ak1, ArgTypeKind ak2) {
273 if (!rk1.moreSpecificThan(rk2))
274 return false;
276 if (ak1 != ak2)
277 return false;
279 return true;
280 }
282 static class DiagnosticChecker
283 implements javax.tools.DiagnosticListener<JavaFileObject> {
285 boolean ambiguityFound;
286 String mostSpecificSig;
288 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
289 try {
290 if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
291 diagnostic.getCode().equals("compiler.err.ref.ambiguous")) {
292 ambiguityFound = true;
293 } else if (diagnostic.getKind() == Diagnostic.Kind.NOTE &&
294 diagnostic.getCode()
295 .equals("compiler.note.verbose.resolve.multi")) {
296 ClientCodeWrapper.DiagnosticSourceUnwrapper dsu =
297 (ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic;
298 JCDiagnostic.MultilineDiagnostic mdiag =
299 (JCDiagnostic.MultilineDiagnostic)dsu.d;
300 int mostSpecificIndex = (Integer)mdiag.getArgs()[2];
301 mostSpecificSig =
302 ((JCDiagnostic)mdiag.getSubdiagnostics()
303 .get(mostSpecificIndex)).getArgs()[1].toString();
304 }
305 } catch (RuntimeException t) {
306 t.printStackTrace();
307 throw t;
308 }
309 }
310 }
312 }