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