Thu, 25 Jul 2013 14:51:40 +0100
8020843: javac crashes on accessibility check with method reference with typevar receiver
Summary: method reference overload check doesn't walk through type-variable receivers
Reviewed-by: jjg
1 /*
2 * Copyright (c) 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 8013789
27 * @summary Compiler should emit bridges in interfaces
28 */
30 import com.sun.source.util.JavacTask;
31 import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper;
32 import com.sun.tools.javac.code.Symbol;
33 import com.sun.tools.javac.util.JCDiagnostic;
35 import java.io.File;
36 import java.io.PrintWriter;
37 import java.net.URI;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.EnumSet;
41 import java.util.List;
42 import java.util.Set;
44 import javax.tools.Diagnostic;
45 import javax.tools.Diagnostic.Kind;
46 import javax.tools.JavaCompiler;
47 import javax.tools.JavaFileObject;
48 import javax.tools.SimpleJavaFileObject;
49 import javax.tools.ToolProvider;
51 public class TestMetafactoryBridges {
53 static int checkCount = 0;
55 enum ClasspathKind {
56 NONE(),
57 B7(7, ClassKind.B),
58 A7(7, ClassKind.A),
59 B8(8, ClassKind.B),
60 A8(8, ClassKind.A);
62 int version;
63 ClassKind ck;
65 ClasspathKind() {
66 this(-1, null);
67 }
69 ClasspathKind(int version, ClassKind ck) {
70 this.version = version;
71 this.ck = ck;
72 }
73 }
75 enum PreferPolicy {
76 SOURCE("-Xprefer:source"),
77 NEWER("-Xprefer:newer");
79 String preferOpt;
81 PreferPolicy(String preferOpt) {
82 this.preferOpt = preferOpt;
83 }
84 }
86 enum SourcepathKind {
87 NONE,
88 A(ClassKind.A),
89 B(ClassKind.B),
90 C(ClassKind.C),
91 AB(ClassKind.A, ClassKind.B),
92 BC(ClassKind.B, ClassKind.C),
93 AC(ClassKind.A, ClassKind.C),
94 ABC(ClassKind.A, ClassKind.B, ClassKind.C);
96 List<ClassKind> sources;
98 SourcepathKind(ClassKind... sources) {
99 this.sources = Arrays.asList(sources);
100 }
101 }
103 enum SourceSet {
104 ALL() {
105 @Override
106 List<List<ClassKind>> permutations() {
107 return Arrays.asList(
108 Arrays.asList(ClassKind.A, ClassKind.B, ClassKind.C),
109 Arrays.asList(ClassKind.A, ClassKind.B, ClassKind.C),
110 Arrays.asList(ClassKind.B, ClassKind.A, ClassKind.C),
111 Arrays.asList(ClassKind.B, ClassKind.C, ClassKind.A),
112 Arrays.asList(ClassKind.C, ClassKind.A, ClassKind.B),
113 Arrays.asList(ClassKind.C, ClassKind.B, ClassKind.A)
114 );
115 }
116 },
117 AC() {
118 @Override
119 List<List<ClassKind>> permutations() {
120 return Arrays.asList(
121 Arrays.asList(ClassKind.A, ClassKind.C),
122 Arrays.asList(ClassKind.C, ClassKind.A)
123 );
124 }
125 },
126 C() {
127 @Override
128 List<List<ClassKind>> permutations() {
129 return Arrays.asList(Arrays.asList(ClassKind.C));
130 }
131 };
133 abstract List<List<ClassKind>> permutations();
134 }
136 enum ClassKind {
137 A("A", "interface A { Object m(); }"),
138 B("B", "interface B extends A { Integer m(); }", A),
139 C("C", "class C { B b = ()->42; }", A, B);
141 String name;
142 String source;
143 ClassKind[] deps;
145 ClassKind(String name, String source, ClassKind... deps) {
146 this.name = name;
147 this.source = source;
148 this.deps = deps;
149 }
150 }
152 public static void main(String... args) throws Exception {
153 String SCRATCH_DIR = System.getProperty("user.dir");
154 //create default shared JavaCompiler - reused across multiple compilations
155 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
157 int n = 0;
158 for (SourceSet ss : SourceSet.values()) {
159 for (List<ClassKind> sources : ss.permutations()) {
160 for (SourcepathKind spKind : SourcepathKind.values()) {
161 for (ClasspathKind cpKind : ClasspathKind.values()) {
162 for (PreferPolicy pp : PreferPolicy.values()) {
163 Set<ClassKind> deps = EnumSet.noneOf(ClassKind.class);
164 if (cpKind.ck != null) {
165 deps.add(cpKind.ck);
166 }
167 deps.addAll(sources);
168 if (deps.size() < 3) continue;
169 File testDir = new File(SCRATCH_DIR, "test" + n);
170 testDir.mkdir();
171 try (PrintWriter debugWriter = new PrintWriter(new File(testDir, "debug.txt"))) {
172 new TestMetafactoryBridges(testDir, sources, spKind, cpKind, pp, debugWriter).run(comp);
173 n++;
174 }
175 }
176 }
177 }
178 }
179 }
180 System.out.println("Total check executed: " + checkCount);
181 }
183 File testDir;
184 List<ClassKind> sources;
185 SourcepathKind spKind;
186 ClasspathKind cpKind;
187 PreferPolicy pp;
188 PrintWriter debugWriter;
189 DiagnosticChecker diagChecker;
191 TestMetafactoryBridges(File testDir, List<ClassKind>sources, SourcepathKind spKind,
192 ClasspathKind cpKind, PreferPolicy pp, PrintWriter debugWriter) {
193 this.testDir = testDir;
194 this.sources = sources;
195 this.spKind = spKind;
196 this.cpKind = cpKind;
197 this.pp = pp;
198 this.debugWriter = debugWriter;
199 this.diagChecker = new DiagnosticChecker();
200 }
202 class JavaSource extends SimpleJavaFileObject {
204 final String source;
206 public JavaSource(ClassKind ck) {
207 super(URI.create(String.format("myfo:/%s.java", ck.name)), JavaFileObject.Kind.SOURCE);
208 this.source = ck.source;
209 }
211 @Override
212 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
213 return source;
214 }
215 }
217 void run(JavaCompiler tool) throws Exception {
218 File classesDir = new File(testDir, "classes");
219 File outDir = new File(testDir, "out");
220 File srcDir = new File(testDir, "src");
221 classesDir.mkdir();
222 outDir.mkdir();
223 srcDir.mkdir();
225 debugWriter.append(testDir.getName() + "\n");
226 debugWriter.append("sources = " + sources + "\n");
227 debugWriter.append("spKind = " + spKind + "\n");
228 debugWriter.append("cpKind = " + cpKind + "\n");
229 debugWriter.append("preferPolicy = " + pp.preferOpt + "\n");
231 //step 1 - prepare sources (older!!)
232 debugWriter.append("Preparing sources\n");
233 for (ClassKind ck : spKind.sources) {
234 //skip sources explicitly provided on command line
235 if (!sources.contains(ck)) {
236 debugWriter.append("Copy " + ck.name + ".java to" + srcDir.getAbsolutePath() + "\n");
237 File dest = new File(srcDir, ck.name + ".java");
238 PrintWriter pw = new PrintWriter(dest);
239 pw.append(ck.source);
240 pw.close();
241 }
242 }
244 //step 2 - prepare classes
245 debugWriter.append("Preparing classes\n");
246 if (cpKind != ClasspathKind.NONE) {
247 List<JavaSource> sources = new ArrayList<>();
248 ClassKind toRemove = null;
249 sources.add(new JavaSource(cpKind.ck));
250 if (cpKind.ck.deps.length != 0) {
251 //at most only one dependency
252 toRemove = cpKind.ck.deps[0];
253 sources.add(new JavaSource(toRemove));
254 }
255 JavacTask ct = (JavacTask)tool.getTask(debugWriter, null, null,
256 Arrays.asList("-d", classesDir.getAbsolutePath(), "-source", String.valueOf(cpKind.version)), null, sources);
257 try {
258 ct.generate();
259 if (toRemove != null) {
260 debugWriter.append("Remove " + toRemove.name + ".class from" + classesDir.getAbsolutePath() + "\n");
261 File fileToRemove = new File(classesDir, toRemove.name + ".class");
262 fileToRemove.delete();
263 }
264 } catch (Throwable ex) {
265 throw new AssertionError("Error thrown when generating side-classes");
266 }
267 }
269 //step 3 - compile
270 debugWriter.append("Compiling test\n");
271 List<JavaSource> sourcefiles = new ArrayList<>();
272 for (ClassKind ck : sources) {
273 sourcefiles.add(new JavaSource(ck));
274 }
275 JavacTask ct = (JavacTask)tool.getTask(debugWriter, null, diagChecker,
276 Arrays.asList("-XDdumpLambdaToMethodStats", "-d", outDir.getAbsolutePath(),
277 "-sourcepath", srcDir.getAbsolutePath(),
278 "-classpath", classesDir.getAbsolutePath(),
279 pp.preferOpt), null, sourcefiles);
280 try {
281 ct.generate();
282 } catch (Throwable ex) {
283 throw new AssertionError("Error thrown when compiling test case");
284 }
285 check();
286 }
288 void check() {
289 checkCount++;
290 if (diagChecker.errorFound) {
291 throw new AssertionError("Unexpected compilation failure");
292 }
294 boolean altMetafactory =
295 cpKind == ClasspathKind.B7 &&
296 !sources.contains(ClassKind.B) &&
297 (pp == PreferPolicy.NEWER || !spKind.sources.contains(ClassKind.B));
299 if (altMetafactory != diagChecker.altMetafactory) {
300 throw new AssertionError("Bad metafactory detected - expected altMetafactory: " + altMetafactory +
301 "\ntest: " + testDir);
302 }
303 }
305 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
307 boolean altMetafactory = false;
308 boolean errorFound = false;
310 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
311 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
312 errorFound = true;
313 } else if (statProcessor.matches(diagnostic)) {
314 statProcessor.process(diagnostic);
315 }
316 }
318 abstract class DiagnosticProcessor {
320 List<String> codes;
321 Diagnostic.Kind kind;
323 public DiagnosticProcessor(Kind kind, String... codes) {
324 this.codes = Arrays.asList(codes);
325 this.kind = kind;
326 }
328 abstract void process(Diagnostic<? extends JavaFileObject> diagnostic);
330 boolean matches(Diagnostic<? extends JavaFileObject> diagnostic) {
331 return (codes.isEmpty() || codes.contains(diagnostic.getCode())) &&
332 diagnostic.getKind() == kind;
333 }
335 JCDiagnostic asJCDiagnostic(Diagnostic<? extends JavaFileObject> diagnostic) {
336 if (diagnostic instanceof JCDiagnostic) {
337 return (JCDiagnostic)diagnostic;
338 } else if (diagnostic instanceof DiagnosticSourceUnwrapper) {
339 return ((DiagnosticSourceUnwrapper)diagnostic).d;
340 } else {
341 throw new AssertionError("Cannot convert diagnostic to JCDiagnostic: " + diagnostic.getClass().getName());
342 }
343 }
344 }
346 DiagnosticProcessor statProcessor = new DiagnosticProcessor(Kind.NOTE,
347 "compiler.note.lambda.stat",
348 "compiler.note.mref.stat",
349 "compiler.note.mref.stat.1") {
350 @Override
351 void process(Diagnostic<? extends JavaFileObject> diagnostic) {
352 JCDiagnostic diag = asJCDiagnostic(diagnostic);
353 if ((Boolean)diag.getArgs()[0]) {
354 altMetafactory = true;
355 }
356 }
357 };
358 }
359 }