IntersectionTypeCastTest.java revision 1481:954541f13717
12726Sjlahoda/* 22311Sjlahoda * 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 */ 23 24/* 25 * @test 26 * @bug 8002099 27 * @summary Add support for intersection types in cast expression 28 * @library ../../lib 29 * @build JavacTestingAbstractThreadedTest 30 * @run main/timeout=360 IntersectionTypeCastTest 31 */ 32 33import java.net.URI; 34import java.util.Arrays; 35import javax.tools.Diagnostic; 36import javax.tools.JavaCompiler; 37import javax.tools.JavaFileObject; 38import javax.tools.SimpleJavaFileObject; 39import javax.tools.ToolProvider; 40 41import com.sun.source.util.JavacTask; 42import com.sun.tools.javac.util.List; 43import com.sun.tools.javac.util.ListBuffer; 44 45public class IntersectionTypeCastTest 46 extends JavacTestingAbstractThreadedTest 47 implements Runnable { 48 49 interface Type { 50 boolean subtypeOf(Type that); 51 String asString(); 52 boolean isClass(); 53 boolean isInterface(); 54 } 55 56 enum InterfaceKind implements Type { 57 A("interface A { }\n", "A", null), 58 B("interface B { }\n", "B", null), 59 C("interface C extends A { }\n", "C", A); 60 61 String declStr; 62 String typeStr; 63 InterfaceKind superInterface; 64 65 InterfaceKind(String declStr, String typeStr, 66 InterfaceKind superInterface) { 67 this.declStr = declStr; 68 this.typeStr = typeStr; 69 this.superInterface = superInterface; 70 } 71 72 @Override 73 public boolean subtypeOf(Type that) { 74 return this == that || superInterface == that || 75 that == ClassKind.OBJECT; 76 } 77 78 @Override 79 public String asString() { 80 return typeStr; 81 } 82 83 @Override 84 public boolean isClass() { 85 return false; 86 } 87 88 @Override 89 public boolean isInterface() { 90 return true; 91 } 92 } 93 94 enum ClassKind implements Type { 95 OBJECT(null, "Object"), 96 CA("#M class CA implements A { }\n", "CA", 97 InterfaceKind.A), 98 CB("#M class CB implements B { }\n", "CB", 99 InterfaceKind.B), 100 CAB("#M class CAB implements A, B { }\n", "CAB", 101 InterfaceKind.A, InterfaceKind.B), 102 CC("#M class CC implements C { }\n", "CC", 103 InterfaceKind.C, InterfaceKind.A), 104 CCA("#M class CCA implements C, A { }\n", "CCA", 105 InterfaceKind.C, InterfaceKind.A), 106 CCB("#M class CCB implements C, B { }\n", "CCB", 107 InterfaceKind.C, InterfaceKind.A, InterfaceKind.B), 108 CCAB("#M class CCAB implements C, A, B { }\n", "CCAB", 109 InterfaceKind.C, InterfaceKind.A, InterfaceKind.B); 110 111 String declTemplate; 112 String typeStr; 113 List<InterfaceKind> superInterfaces; 114 115 ClassKind(String declTemplate, String typeStr, 116 InterfaceKind... superInterfaces) { 117 this.declTemplate = declTemplate; 118 this.typeStr = typeStr; 119 this.superInterfaces = List.from(superInterfaces); 120 } 121 122 String getDecl(ModifierKind mod) { 123 return declTemplate != null ? 124 declTemplate.replaceAll("#M", mod.modStr) : 125 ""; 126 } 127 128 @Override 129 public boolean subtypeOf(Type that) { 130 return this == that || superInterfaces.contains(that) || 131 that == OBJECT; 132 } 133 134 @Override 135 public String asString() { 136 return typeStr; 137 } 138 139 @Override 140 public boolean isClass() { 141 return true; 142 } 143 144 @Override 145 public boolean isInterface() { 146 return false; 147 } 148 } 149 150 enum ModifierKind { 151 NONE(""), 152 FINAL("final"); 153 154 String modStr; 155 156 ModifierKind(String modStr) { 157 this.modStr = modStr; 158 } 159 } 160 161 enum CastKind { 162 CLASS("(#C)", 0), 163 INTERFACE("(#I0)", 1), 164 INTERSECTION2("(#C & #I0)", 1), 165 INTERSECTION3("(#C & #I0 & #I1)", 2); 166 //INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3); 167 168 String castTemplate; 169 int interfaceBounds; 170 171 CastKind(String castTemplate, int interfaceBounds) { 172 this.castTemplate = castTemplate; 173 this.interfaceBounds = interfaceBounds; 174 } 175 } 176 177 static class CastInfo { 178 CastKind kind; 179 Type[] types; 180 181 CastInfo(CastKind kind, Type... types) { 182 this.kind = kind; 183 this.types = types; 184 } 185 186 String getCast() { 187 String temp = kind.castTemplate.replaceAll("#C", 188 types[0].asString()); 189 for (int i = 0; i < kind.interfaceBounds ; i++) { 190 temp = temp.replace(String.format("#I%d", i), 191 types[i + 1].asString()); 192 } 193 return temp; 194 } 195 196 boolean hasDuplicateTypes() { 197 for (int i = 0 ; i < types.length ; i++) { 198 for (int j = 0 ; j < types.length ; j++) { 199 if (i != j && types[i] == types[j]) { 200 return true; 201 } 202 } 203 } 204 return false; 205 } 206 207 boolean compatibleWith(ModifierKind mod, CastInfo that) { 208 for (Type t1 : types) { 209 for (Type t2 : that.types) { 210 boolean compat = 211 t1.subtypeOf(t2) || 212 t2.subtypeOf(t1) || 213 (t1.isInterface() && t2.isInterface()) || //side-cast (1) 214 (mod == ModifierKind.NONE && 215 (t1.isInterface() != t2.isInterface())); //side-cast (2) 216 if (!compat) return false; 217 } 218 } 219 return true; 220 } 221 } 222 223 public static void main(String... args) throws Exception { 224 for (ModifierKind mod : ModifierKind.values()) { 225 for (CastInfo cast1 : allCastInfo()) { 226 for (CastInfo cast2 : allCastInfo()) { 227 pool.execute( 228 new IntersectionTypeCastTest(mod, cast1, cast2)); 229 } 230 } 231 } 232 checkAfterExec(); 233 } 234 235 static List<CastInfo> allCastInfo() { 236 ListBuffer<CastInfo> buf = ListBuffer.lb(); 237 for (CastKind kind : CastKind.values()) { 238 for (ClassKind clazz : ClassKind.values()) { 239 if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) { 240 continue; 241 } else if (kind.interfaceBounds == 0) { 242 buf.append(new CastInfo(kind, clazz)); 243 continue; 244 } else { 245 for (InterfaceKind intf1 : InterfaceKind.values()) { 246 if (kind.interfaceBounds == 1) { 247 buf.append(new CastInfo(kind, clazz, intf1)); 248 continue; 249 } else { 250 for (InterfaceKind intf2 : InterfaceKind.values()) { 251 if (kind.interfaceBounds == 2) { 252 buf.append( 253 new CastInfo(kind, clazz, intf1, intf2)); 254 continue; 255 } else { 256 for (InterfaceKind intf3 : InterfaceKind.values()) { 257 buf.append( 258 new CastInfo(kind, clazz, intf1, 259 intf2, intf3)); 260 continue; 261 } 262 } 263 } 264 } 265 } 266 } 267 } 268 } 269 return buf.toList(); 270 } 271 272 ModifierKind mod; 273 CastInfo cast1, cast2; 274 JavaSource source; 275 DiagnosticChecker diagChecker; 276 277 IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) { 278 this.mod = mod; 279 this.cast1 = cast1; 280 this.cast2 = cast2; 281 this.source = new JavaSource(); 282 this.diagChecker = new DiagnosticChecker(); 283 } 284 285 @Override 286 public void run() { 287 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 288 289 JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker, 290 Arrays.asList("-XDallowIntersectionTypes"), 291 null, Arrays.asList(source)); 292 try { 293 ct.analyze(); 294 } catch (Throwable ex) { 295 throw new AssertionError("Error thrown when compiling the following code:\n" + 296 source.getCharContent(true)); 297 } 298 check(); 299 } 300 301 class JavaSource extends SimpleJavaFileObject { 302 303 String bodyTemplate = "class Test {\n" + 304 " void test() {\n" + 305 " Object o = #C1#C2null;\n" + 306 " } }"; 307 308 String source = ""; 309 310 public JavaSource() { 311 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 312 for (ClassKind ck : ClassKind.values()) { 313 source += ck.getDecl(mod); 314 } 315 for (InterfaceKind ik : InterfaceKind.values()) { 316 source += ik.declStr; 317 } 318 source += bodyTemplate.replaceAll("#C1", cast1.getCast()). 319 replaceAll("#C2", cast2.getCast()); 320 } 321 322 @Override 323 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 324 return source; 325 } 326 } 327 328 void check() { 329 checkCount.incrementAndGet(); 330 331 boolean errorExpected = cast1.hasDuplicateTypes() || 332 cast2.hasDuplicateTypes(); 333 334 errorExpected |= !cast2.compatibleWith(mod, cast1); 335 336 if (errorExpected != diagChecker.errorFound) { 337 throw new Error("invalid diagnostics for source:\n" + 338 source.getCharContent(true) + 339 "\nFound error: " + diagChecker.errorFound + 340 "\nExpected error: " + errorExpected); 341 } 342 } 343 344 static class DiagnosticChecker 345 implements javax.tools.DiagnosticListener<JavaFileObject> { 346 347 boolean errorFound; 348 349 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 350 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 351 errorFound = true; 352 } 353 } 354 } 355 356} 357