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 */ 23 24 /* 25 * @test 26 * @bug 7192246 8006694 27 * @summary Automatic test for checking correctness of default super/this resolution 28 * temporarily workaround combo tests are causing time out in several platforms 29 * @library ../../lib 30 * @build JavacTestingAbstractThreadedTest 31 * @run main/othervm TestDefaultSuperCall 32 */ 33 34 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) 35 // see JDK-8006746 36 37 import java.net.URI; 38 import java.util.Arrays; 39 import java.util.ArrayList; 40 import java.util.List; 41 import javax.tools.Diagnostic; 42 import javax.tools.JavaFileObject; 43 import javax.tools.SimpleJavaFileObject; 44 45 import com.sun.source.util.JavacTask; 46 47 public class TestDefaultSuperCall 48 extends JavacTestingAbstractThreadedTest 49 implements Runnable { 50 51 enum InterfaceKind { 52 DEFAULT("interface A extends B { default void m() { } }"), 53 ABSTRACT("interface A extends B { void m(); }"), 54 NONE("interface A extends B { }"); 55 56 String interfaceStr; 57 InterfaceKind(String interfaceStr)58 InterfaceKind(String interfaceStr) { 59 this.interfaceStr = interfaceStr; 60 } 61 methodDefined()62 boolean methodDefined() { 63 return this == DEFAULT; 64 } 65 } 66 67 enum PruneKind { 68 NO_PRUNE("interface C { }"), 69 PRUNE("interface C extends A { }"); 70 methodDefined(InterfaceKind ik)71 boolean methodDefined(InterfaceKind ik) { 72 return this == PRUNE && 73 ik.methodDefined(); 74 } 75 76 String interfaceStr; 77 PruneKind(String interfaceStr)78 PruneKind(String interfaceStr) { 79 this.interfaceStr = interfaceStr; 80 } 81 } 82 83 enum QualifierKind { 84 DIRECT_1("C"), 85 DIRECT_2("A"), 86 INDIRECT("B"), 87 UNRELATED("E"), 88 ENCLOSING_1(null), 89 ENCLOSING_2(null); 90 91 String qualifierStr; 92 QualifierKind(String qualifierStr)93 QualifierKind(String qualifierStr) { 94 this.qualifierStr = qualifierStr; 95 } 96 getQualifier(Shape sh)97 String getQualifier(Shape sh) { 98 switch (this) { 99 case ENCLOSING_1: return sh.enclosingAt(0); 100 case ENCLOSING_2: return sh.enclosingAt(1); 101 default: 102 return qualifierStr; 103 } 104 } 105 isEnclosing()106 boolean isEnclosing() { 107 return this == ENCLOSING_1 || 108 this == ENCLOSING_2; 109 } 110 allowSuperCall(InterfaceKind ik, PruneKind pk)111 boolean allowSuperCall(InterfaceKind ik, PruneKind pk) { 112 switch (this) { 113 case DIRECT_1: 114 return pk.methodDefined(ik); 115 case DIRECT_2: 116 return ik.methodDefined() && pk == PruneKind.NO_PRUNE; 117 default: 118 return false; 119 } 120 } 121 } 122 123 enum ExprKind { 124 THIS("this"), 125 SUPER("super"); 126 127 String exprStr; 128 ExprKind(String exprStr)129 ExprKind(String exprStr) { 130 this.exprStr = exprStr; 131 } 132 } 133 134 enum ElementKind { 135 INTERFACE("interface #N { #B }", true), 136 INTERFACE_EXTENDS("interface #N extends A, C { #B }", true), 137 CLASS("class #N { #B }", false), 138 CLASS_EXTENDS("abstract class #N implements A, C { #B }", false), 139 STATIC_CLASS("static class #N { #B }", true), 140 STATIC_CLASS_EXTENDS("abstract static class #N implements A, C { #B }", true), 141 ANON_CLASS("new Object() { #B };", false), 142 METHOD("void test() { #B }", false), 143 STATIC_METHOD("static void test() { #B }", true), 144 DEFAULT_METHOD("default void test() { #B }", false); 145 146 String templateDecl; 147 boolean isStatic; 148 ElementKind(String templateDecl, boolean isStatic)149 ElementKind(String templateDecl, boolean isStatic) { 150 this.templateDecl = templateDecl; 151 this.isStatic = isStatic; 152 } 153 isClassDecl()154 boolean isClassDecl() { 155 switch(this) { 156 case METHOD: 157 case STATIC_METHOD: 158 case DEFAULT_METHOD: 159 return false; 160 default: 161 return true; 162 } 163 } 164 isAllowedEnclosing(ElementKind ek, boolean isTop)165 boolean isAllowedEnclosing(ElementKind ek, boolean isTop) { 166 switch (this) { 167 case CLASS: 168 case CLASS_EXTENDS: 169 //class is implicitly static inside interface, so skip this combo 170 return ek.isClassDecl() && 171 ek != INTERFACE && ek != INTERFACE_EXTENDS; 172 case ANON_CLASS: 173 return !ek.isClassDecl(); 174 case METHOD: 175 return ek == CLASS || ek == CLASS_EXTENDS || 176 ek == STATIC_CLASS || ek == STATIC_CLASS_EXTENDS || 177 ek == ANON_CLASS; 178 case INTERFACE: 179 case INTERFACE_EXTENDS: 180 case STATIC_CLASS: 181 case STATIC_CLASS_EXTENDS: 182 case STATIC_METHOD: 183 return (isTop && (ek == CLASS || ek == CLASS_EXTENDS)) || 184 ek == STATIC_CLASS || ek == STATIC_CLASS_EXTENDS; 185 case DEFAULT_METHOD: 186 return ek == INTERFACE || ek == INTERFACE_EXTENDS; 187 default: 188 throw new AssertionError("Bad enclosing element kind" + this); 189 } 190 } 191 isAllowedTop()192 boolean isAllowedTop() { 193 switch (this) { 194 case CLASS: 195 case CLASS_EXTENDS: 196 case INTERFACE: 197 case INTERFACE_EXTENDS: 198 return true; 199 default: 200 return false; 201 } 202 } 203 hasSuper()204 boolean hasSuper() { 205 return this == INTERFACE_EXTENDS || 206 this == STATIC_CLASS_EXTENDS || 207 this == CLASS_EXTENDS; 208 } 209 } 210 211 static class Shape { 212 213 String shapeStr; 214 List<ElementKind> enclosingElements; 215 List<String> enclosingNames; 216 List<String> elementsWithMethod; 217 Shape(ElementKind... elements)218 Shape(ElementKind... elements) { 219 enclosingElements = new ArrayList<>(); 220 enclosingNames = new ArrayList<>(); 221 elementsWithMethod = new ArrayList<>(); 222 int count = 0; 223 String prevName = null; 224 for (ElementKind ek : elements) { 225 String name = "name"+count++; 226 if (ek.isStatic) { 227 enclosingElements = new ArrayList<>(); 228 enclosingNames = new ArrayList<>(); 229 } 230 if (ek.isClassDecl()) { 231 enclosingElements.add(ek); 232 enclosingNames.add(name); 233 } else { 234 elementsWithMethod.add(prevName); 235 } 236 String element = ek.templateDecl.replaceAll("#N", name); 237 shapeStr = shapeStr == 238 null ? element : shapeStr.replaceAll("#B", element); 239 prevName = name; 240 } 241 } 242 getShape(QualifierKind qk, ExprKind ek)243 String getShape(QualifierKind qk, ExprKind ek) { 244 String methName = ek == ExprKind.THIS ? "test" : "m"; 245 String call = qk.getQualifier(this) + "." + 246 ek.exprStr + "." + methName + "();"; 247 return shapeStr.replaceAll("#B", call); 248 } 249 enclosingAt(int index)250 String enclosingAt(int index) { 251 return index < enclosingNames.size() ? 252 enclosingNames.get(index) : "BAD"; 253 } 254 } 255 main(String... args)256 public static void main(String... args) throws Exception { 257 for (InterfaceKind ik : InterfaceKind.values()) { 258 for (PruneKind pk : PruneKind.values()) { 259 for (ElementKind ek1 : ElementKind.values()) { 260 if (!ek1.isAllowedTop()) continue; 261 for (ElementKind ek2 : ElementKind.values()) { 262 if (!ek2.isAllowedEnclosing(ek1, true)) continue; 263 for (ElementKind ek3 : ElementKind.values()) { 264 if (!ek3.isAllowedEnclosing(ek2, false)) continue; 265 for (ElementKind ek4 : ElementKind.values()) { 266 if (!ek4.isAllowedEnclosing(ek3, false)) continue; 267 for (ElementKind ek5 : ElementKind.values()) { 268 if (!ek5.isAllowedEnclosing(ek4, false) || 269 ek5.isClassDecl()) continue; 270 for (QualifierKind qk : QualifierKind.values()) { 271 for (ExprKind ek : ExprKind.values()) { 272 pool.execute( 273 new TestDefaultSuperCall(ik, pk, 274 new Shape(ek1, ek2, ek3, 275 ek4, ek5), qk, ek)); 276 } 277 } 278 } 279 } 280 } 281 } 282 } 283 } 284 } 285 286 checkAfterExec(); 287 } 288 289 InterfaceKind ik; 290 PruneKind pk; 291 Shape sh; 292 QualifierKind qk; 293 ExprKind ek; 294 JavaSource source; 295 DiagnosticChecker diagChecker; 296 TestDefaultSuperCall(InterfaceKind ik, PruneKind pk, Shape sh, QualifierKind qk, ExprKind ek)297 TestDefaultSuperCall(InterfaceKind ik, PruneKind pk, Shape sh, 298 QualifierKind qk, ExprKind ek) { 299 this.ik = ik; 300 this.pk = pk; 301 this.sh = sh; 302 this.qk = qk; 303 this.ek = ek; 304 this.source = new JavaSource(); 305 this.diagChecker = new DiagnosticChecker(); 306 } 307 308 class JavaSource extends SimpleJavaFileObject { 309 310 String template = "interface E {}\n" + 311 "interface B { }\n" + 312 "#I\n" + 313 "#P\n" + 314 "#C"; 315 316 String source; 317 JavaSource()318 public JavaSource() { 319 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 320 source = template.replaceAll("#I", ik.interfaceStr) 321 .replaceAll("#P", pk.interfaceStr) 322 .replaceAll("#C", sh.getShape(qk, ek)); 323 } 324 325 @Override getCharContent(boolean ignoreEncodingErrors)326 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 327 return source; 328 } 329 } 330 run()331 public void run() { 332 JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker, 333 null, null, Arrays.asList(source)); 334 try { 335 ct.analyze(); 336 } catch (Throwable ex) { 337 processException(ex); 338 return; 339 } 340 check(); 341 } 342 check()343 void check() { 344 boolean errorExpected = false; 345 346 boolean badEnclosing = false; 347 boolean badThis = false; 348 boolean badSuper = false; 349 350 if (qk == QualifierKind.ENCLOSING_1 && 351 sh.enclosingNames.size() < 1) { 352 errorExpected |= true; 353 badEnclosing = true; 354 } 355 356 if (qk == QualifierKind.ENCLOSING_2 && 357 sh.enclosingNames.size() < 2) { 358 errorExpected |= true; 359 badEnclosing = true; 360 } 361 362 if (ek == ExprKind.THIS) { 363 boolean found = false; 364 for (int i = 0; i < sh.enclosingElements.size(); i++) { 365 if (sh.enclosingElements.get(i) == ElementKind.ANON_CLASS) continue; 366 if (sh.enclosingNames.get(i).equals(qk.getQualifier(sh))) { 367 found = sh.elementsWithMethod.contains(sh.enclosingNames.get(i)); 368 break; 369 } 370 } 371 errorExpected |= !found; 372 if (!found) { 373 badThis = true; 374 } 375 } 376 377 if (ek == ExprKind.SUPER) { 378 379 int lastIdx = sh.enclosingElements.size() - 1; 380 boolean found = lastIdx == -1 ? false : 381 sh.enclosingElements.get(lastIdx).hasSuper() && 382 qk.allowSuperCall(ik, pk); 383 384 errorExpected |= !found; 385 if (!found) { 386 badSuper = true; 387 } 388 } 389 390 checkCount.incrementAndGet(); 391 if (diagChecker.errorFound != errorExpected) { 392 throw new AssertionError("Problem when compiling source:\n" + 393 source.getCharContent(true) + 394 "\nenclosingElems: " + sh.enclosingElements + 395 "\nenclosingNames: " + sh.enclosingNames + 396 "\nelementsWithMethod: " + sh.elementsWithMethod + 397 "\nbad encl: " + badEnclosing + 398 "\nbad this: " + badThis + 399 "\nbad super: " + badSuper + 400 "\nqual kind: " + qk + 401 "\nfound error: " + diagChecker.errorFound); 402 } 403 } 404 405 static class DiagnosticChecker 406 implements javax.tools.DiagnosticListener<JavaFileObject> { 407 408 boolean errorFound; 409 report(Diagnostic<? extends JavaFileObject> diagnostic)410 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 411 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 412 errorFound = true; 413 } 414 } 415 } 416 417 } 418