Tue, 10 Sep 2013 13:47:51 +0200
8005222: Fixed bugs should have tests with bugid in @bug tag
Reviewed-by: jfranck, jjg
Contributed-by: Andreas Lundblad <andreas.lundblad@oracle.com>
1 /*
2 * Copyright (c) 2012, 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 8002099
27 * @summary Add support for intersection types in cast expression
28 */
30 import com.sun.source.util.JavacTask;
31 import com.sun.tools.javac.util.ListBuffer;
32 import java.net.URI;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import javax.tools.Diagnostic;
37 import javax.tools.JavaCompiler;
38 import javax.tools.JavaFileObject;
39 import javax.tools.SimpleJavaFileObject;
40 import javax.tools.StandardJavaFileManager;
41 import javax.tools.ToolProvider;
43 public class IntersectionTargetTypeTest {
45 static int checkCount = 0;
47 enum BoundKind {
48 INTF,
49 CLASS;
50 }
52 enum MethodKind {
53 NONE(false),
54 ABSTRACT_M(true),
55 DEFAULT_M(false),
56 ABSTRACT_G(true),
57 DEFAULT_G(false);
59 boolean isAbstract;
61 MethodKind(boolean isAbstract) {
62 this.isAbstract = isAbstract;
63 }
64 }
66 enum TypeKind {
67 A("interface A { }\n", "A", BoundKind.INTF, MethodKind.NONE),
68 B("interface B { default void m() { } }\n", "B", BoundKind.INTF, MethodKind.DEFAULT_M),
69 C("interface C { void m(); }\n", "C", BoundKind.INTF, MethodKind.ABSTRACT_M),
70 D("interface D extends B { }\n", "D", BoundKind.INTF, MethodKind.DEFAULT_M),
71 E("interface E extends C { }\n", "E", BoundKind.INTF, MethodKind.ABSTRACT_M),
72 F("interface F extends C { void g(); }\n", "F", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.ABSTRACT_M),
73 G("interface G extends B { void g(); }\n", "G", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.DEFAULT_M),
74 H("interface H extends A { void g(); }\n", "H", BoundKind.INTF, MethodKind.ABSTRACT_G),
75 OBJECT("", "Object", BoundKind.CLASS),
76 STRING("", "String", BoundKind.CLASS);
78 String declStr;
79 String typeStr;
80 BoundKind boundKind;
81 MethodKind[] methodKinds;
83 private TypeKind(String declStr, String typeStr, BoundKind boundKind, MethodKind... methodKinds) {
84 this.declStr = declStr;
85 this.typeStr = typeStr;
86 this.boundKind = boundKind;
87 this.methodKinds = methodKinds;
88 }
90 boolean compatibleSupertype(TypeKind tk) {
91 if (tk == this) return true;
92 switch (tk) {
93 case B:
94 return this != C && this != E && this != F;
95 case C:
96 return this != B && this != C && this != D && this != G;
97 case D: return compatibleSupertype(B);
98 case E:
99 case F: return compatibleSupertype(C);
100 case G: return compatibleSupertype(B);
101 case H: return compatibleSupertype(A);
102 default:
103 return true;
104 }
105 }
106 }
108 enum CastKind {
109 ONE_ARY("(#B0)", 1),
110 TWO_ARY("(#B0 & #B1)", 2),
111 THREE_ARY("(#B0 & #B1 & #B2)", 3);
113 String castTemplate;
114 int nbounds;
116 CastKind(String castTemplate, int nbounds) {
117 this.castTemplate = castTemplate;
118 this.nbounds = nbounds;
119 }
120 }
122 enum ExpressionKind {
123 LAMBDA("()->{}", true),
124 MREF("this::m", true),
125 //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
126 //COND_MREF("(true ? this::m : this::m)", true),
127 STANDALONE("null", false);
129 String exprString;
130 boolean isFunctional;
132 private ExpressionKind(String exprString, boolean isFunctional) {
133 this.exprString = exprString;
134 this.isFunctional = isFunctional;
135 }
136 }
138 static class CastInfo {
139 CastKind kind;
140 TypeKind[] types;
142 CastInfo(CastKind kind, TypeKind... types) {
143 this.kind = kind;
144 this.types = types;
145 }
147 String getCast() {
148 String temp = kind.castTemplate;
149 for (int i = 0; i < kind.nbounds ; i++) {
150 temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
151 }
152 return temp;
153 }
155 boolean wellFormed() {
156 //check for duplicate types
157 for (int i = 0 ; i < types.length ; i++) {
158 for (int j = 0 ; j < types.length ; j++) {
159 if (i != j && types[i] == types[j]) {
160 return false;
161 }
162 }
163 }
164 //check that classes only appear as first bound
165 boolean classOk = true;
166 for (int i = 0 ; i < types.length ; i++) {
167 if (types[i].boundKind == BoundKind.CLASS &&
168 !classOk) {
169 return false;
170 }
171 classOk = false;
172 }
173 //check that supertypes are mutually compatible
174 for (int i = 0 ; i < types.length ; i++) {
175 for (int j = 0 ; j < types.length ; j++) {
176 if (!types[i].compatibleSupertype(types[j]) && i != j) {
177 return false;
178 }
179 }
180 }
181 return true;
182 }
183 }
185 public static void main(String... args) throws Exception {
186 //create default shared JavaCompiler - reused across multiple compilations
187 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
188 StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
190 for (CastInfo cInfo : allCastInfo()) {
191 for (ExpressionKind ek : ExpressionKind.values()) {
192 new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm);
193 }
194 }
195 System.out.println("Total check executed: " + checkCount);
196 }
198 static List<CastInfo> allCastInfo() {
199 ListBuffer<CastInfo> buf = ListBuffer.lb();
200 for (CastKind kind : CastKind.values()) {
201 for (TypeKind b1 : TypeKind.values()) {
202 if (kind.nbounds == 1) {
203 buf.append(new CastInfo(kind, b1));
204 continue;
205 } else {
206 for (TypeKind b2 : TypeKind.values()) {
207 if (kind.nbounds == 2) {
208 buf.append(new CastInfo(kind, b1, b2));
209 continue;
210 } else {
211 for (TypeKind b3 : TypeKind.values()) {
212 buf.append(new CastInfo(kind, b1, b2, b3));
213 }
214 }
215 }
216 }
217 }
218 }
219 return buf.toList();
220 }
222 CastInfo cInfo;
223 ExpressionKind ek;
224 JavaSource source;
225 DiagnosticChecker diagChecker;
227 IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
228 this.cInfo = cInfo;
229 this.ek = ek;
230 this.source = new JavaSource();
231 this.diagChecker = new DiagnosticChecker();
232 }
234 class JavaSource extends SimpleJavaFileObject {
236 String bodyTemplate = "class Test {\n" +
237 " void m() { }\n" +
238 " void test() {\n" +
239 " Object o = #C#E;\n" +
240 " } }";
242 String source = "";
244 public JavaSource() {
245 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
246 for (TypeKind tk : TypeKind.values()) {
247 source += tk.declStr;
248 }
249 source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
250 }
252 @Override
253 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
254 return source;
255 }
256 }
258 void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
259 JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
260 null, null, Arrays.asList(source));
261 try {
262 ct.analyze();
263 } catch (Throwable ex) {
264 throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
265 }
266 check();
267 }
269 void check() {
270 checkCount++;
272 boolean errorExpected = !cInfo.wellFormed();
274 if (ek.isFunctional) {
275 List<MethodKind> mks = new ArrayList<>();
276 for (TypeKind tk : cInfo.types) {
277 if (tk.boundKind == BoundKind.CLASS) {
278 errorExpected = true;
279 break;
280 } else {
281 mks = mergeMethods(mks, Arrays.asList(tk.methodKinds));
282 }
283 }
284 int abstractCount = 0;
285 for (MethodKind mk : mks) {
286 if (mk.isAbstract) {
287 abstractCount++;
288 }
289 }
290 errorExpected |= abstractCount != 1;
291 }
293 if (errorExpected != diagChecker.errorFound) {
294 throw new Error("invalid diagnostics for source:\n" +
295 source.getCharContent(true) +
296 "\nFound error: " + diagChecker.errorFound +
297 "\nExpected error: " + errorExpected);
298 }
299 }
301 List<MethodKind> mergeMethods(List<MethodKind> l1, List<MethodKind> l2) {
302 List<MethodKind> mergedMethods = new ArrayList<>(l1);
303 for (MethodKind mk2 : l2) {
304 boolean add = !mergedMethods.contains(mk2);
305 switch (mk2) {
306 case ABSTRACT_G:
307 add = add && !mergedMethods.contains(MethodKind.DEFAULT_G);
308 break;
309 case ABSTRACT_M:
310 add = add && !mergedMethods.contains(MethodKind.DEFAULT_M);
311 break;
312 case DEFAULT_G:
313 mergedMethods.remove(MethodKind.ABSTRACT_G);
314 case DEFAULT_M:
315 mergedMethods.remove(MethodKind.ABSTRACT_M);
316 case NONE:
317 add = false;
318 break;
319 }
320 if (add) {
321 mergedMethods.add(mk2);
322 }
323 }
324 return mergedMethods;
325 }
327 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
329 boolean errorFound;
331 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
332 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
333 errorFound = true;
334 }
335 }
336 }
337 }