1 /*
  2  * Copyright (c) 2012, 2015, 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 8129962
 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 /tools/javac/lib
 30  * @modules jdk.compiler/com.sun.tools.javac.api
 31  *          jdk.compiler/com.sun.tools.javac.file
 32  *          jdk.compiler/com.sun.tools.javac.util
 33  * @build combo.ComboTestHelper
 34  * @run main TestDefaultSuperCall
 35  */
 36 
 37 import java.io.IOException;
 38 import java.util.ArrayList;
 39 import java.util.List;
 40 
 41 import combo.ComboInstance;
 42 import combo.ComboParameter;
 43 import combo.ComboTask.Result;
 44 import combo.ComboTestHelper;
 45 
 46 public class TestDefaultSuperCall extends ComboInstance<TestDefaultSuperCall> {
 47 
 48     enum InterfaceKind implements ComboParameter {
 49         DEFAULT("interface A extends B { default void m() { } }"),
 50         ABSTRACT("interface A extends B { void m(); }"),
 51         NONE("interface A extends B { }");
 52 
 53         String interfaceStr;
 54 
 55         InterfaceKind(String interfaceStr) {
 56             this.interfaceStr = interfaceStr;
 57         }
 58 
 59         boolean methodDefined() {
 60             return this == DEFAULT;
 61         }
 62 
 63         @Override
 64         public String expand(String optParameter) {
 65             return interfaceStr;
 66         }
 67     }
 68 
 69     enum PruneKind implements ComboParameter {
 70         NO_PRUNE("interface C { }"),
 71         PRUNE("interface C extends A { }");
 72 
 73         boolean methodDefined(InterfaceKind ik) {
 74             return this == PRUNE &&
 75                     ik.methodDefined();
 76         }
 77 
 78         String interfaceStr;
 79 
 80         PruneKind(String interfaceStr) {
 81             this.interfaceStr = interfaceStr;
 82         }
 83 
 84         @Override
 85         public String expand(String optParameter) {
 86             return interfaceStr;
 87         }
 88     }
 89 
 90     enum QualifierKind implements ComboParameter {
 91         DIRECT_1("C"),
 92         DIRECT_2("A"),
 93         INDIRECT("B"),
 94         UNRELATED("E"),
 95         ENCLOSING_1("name0"),
 96         ENCLOSING_2("name1");
 97 
 98         String qualifierStr;
 99 
100         QualifierKind(String qualifierStr) {
101             this.qualifierStr = qualifierStr;
102         }
103 
104         boolean isEnclosing() {
105             return this == ENCLOSING_1 ||
106                     this == ENCLOSING_2;
107         }
108 
109         boolean allowSuperCall(InterfaceKind ik, PruneKind pk) {
110             switch (this) {
111                 case DIRECT_1:
112                     return pk.methodDefined(ik);
113                 case DIRECT_2:
114                     return ik.methodDefined() && pk == PruneKind.NO_PRUNE;
115                 default:
116                     return false;
117             }
118         }
119 
120         @Override
121         public String expand(String optParameter) {
122             return qualifierStr;
123         }
124     }
125 
126     enum ExprKind implements ComboParameter {
127         THIS("this"),
128         SUPER("super");
129 
130         String exprStr;
131 
132         ExprKind(String exprStr) {
133             this.exprStr = exprStr;
134         }
135 
136         @Override
137         public String expand(String optParameter) {
138             return exprStr;
139         }
140     }
141 
142     enum ElementKind implements ComboParameter {
143         INTERFACE("interface name#CURR { #BODY }", true),
144         INTERFACE_EXTENDS("interface name#CURR extends A, C { #BODY }", true),
145         CLASS("class name#CURR { #BODY }", false),
146         CLASS_EXTENDS("abstract class name#CURR implements A, C { #BODY }", false),
147         STATIC_CLASS("static class name#CURR { #BODY }", true),
148         STATIC_CLASS_EXTENDS("abstract static class name#CURR implements A, C { #BODY }", true),
149         ANON_CLASS("new Object() { #BODY };", false),
150         METHOD("void test() { #BODY }", false),
151         STATIC_METHOD("static void test() { #BODY }", true),
152         DEFAULT_METHOD("default void test() { #BODY }", false);
153 
154         String templateDecl;
155         boolean isStatic;
156 
157         ElementKind(String templateDecl, boolean isStatic) {
158             this.templateDecl = templateDecl;
159             this.isStatic = isStatic;
160         }
161 
162         boolean isClassDecl() {
163             switch(this) {
164                 case METHOD:
165                 case STATIC_METHOD:
166                 case DEFAULT_METHOD:
167                     return false;
168                 default:
169                     return true;
170             }
171         }
172 
173         boolean isAllowedEnclosing(ElementKind ek, boolean isTop) {
174             switch (this) {
175                 case CLASS:
176                 case CLASS_EXTENDS:
177                     //class is implicitly static inside interface, so skip this combo
178                     return ek.isClassDecl() &&
179                             ek != INTERFACE && ek != INTERFACE_EXTENDS;
180                 case ANON_CLASS:
181                     return !ek.isClassDecl();
182                 case METHOD:
183                     return ek == CLASS || ek == CLASS_EXTENDS ||
184                             ek == STATIC_CLASS || ek == STATIC_CLASS_EXTENDS ||
185                             ek == ANON_CLASS;
186                 case INTERFACE:
187                 case INTERFACE_EXTENDS:
188                 case STATIC_CLASS:
189                 case STATIC_CLASS_EXTENDS:
190                 case STATIC_METHOD:
191                     return (isTop && (ek == CLASS || ek == CLASS_EXTENDS)) ||
192                             ek == STATIC_CLASS || ek == STATIC_CLASS_EXTENDS;
193                 case DEFAULT_METHOD:
194                     return ek == INTERFACE || ek == INTERFACE_EXTENDS;
195                 default:
196                     throw new AssertionError("Bad enclosing element kind" + this);
197             }
198         }
199 
200         boolean isAllowedTop() {
201             switch (this) {
202                 case CLASS:
203                 case CLASS_EXTENDS:
204                 case INTERFACE:
205                 case INTERFACE_EXTENDS:
206                     return true;
207                 default:
208                     return false;
209             }
210         }
211 
212         boolean hasSuper() {
213             return this == INTERFACE_EXTENDS ||
214                     this == STATIC_CLASS_EXTENDS ||
215                     this == CLASS_EXTENDS;
216         }
217 
218         @Override
219         public String expand(String optParameter) {
220             int nextDepth = new Integer(optParameter) + 1;
221             String replStr = (nextDepth <= 4) ?
222                     String.format("#{ELEM[%d].%d}", nextDepth, nextDepth) :
223                     "#{QUAL}.#{EXPR}.#{METH}();";
224             return templateDecl
225                     .replaceAll("#CURR", optParameter)
226                     .replaceAll("#BODY", replStr);
227         }
228     }
229 
230     static class Shape {
231 
232         List<ElementKind> enclosingElements;
233         List<String> enclosingNames;
234         List<String> elementsWithMethod;
235 
236         Shape(ElementKind... elements) {
237             enclosingElements = new ArrayList<>();
238             enclosingNames = new ArrayList<>();
239             elementsWithMethod = new ArrayList<>();
240             int count = 0;
241             String prevName = null;
242             for (ElementKind ek : elements) {
243                 String name = "name"+count++;
244                 if (ek.isStatic) {
245                     enclosingElements = new ArrayList<>();
246                     enclosingNames = new ArrayList<>();
247                 }
248                 if (ek.isClassDecl()) {
249                     enclosingElements.add(ek);
250                     enclosingNames.add(name);
251                 } else {
252                     elementsWithMethod.add(prevName);
253                 }
254                 prevName = name;
255             }
256         }
257     }
258 
259     public static void main(String... args) throws Exception {
260         new ComboTestHelper<TestDefaultSuperCall>()
261                 .withFilter(TestDefaultSuperCall::filterBadTopElement)
262                 .withFilter(TestDefaultSuperCall::filterBadIntermediateElement)
263                 .withFilter(TestDefaultSuperCall::filterBadTerminalElement)
264                 .withDimension("INTF1", (x, ik) -> x.ik = ik, InterfaceKind.values())
265                 .withDimension("INTF2", (x, pk) -> x.pk = pk, PruneKind.values())
266                 .withArrayDimension("ELEM", (x, elem, idx) -> x.elements[idx] = elem, 5, ElementKind.values())
267                 .withDimension("QUAL", (x, qk) -> x.qk = qk, QualifierKind.values())
268                 .withDimension("EXPR", (x, ek) -> x.ek = ek, ExprKind.values())
269                 .run(TestDefaultSuperCall::new);
270     }
271 
272     InterfaceKind ik;
273     PruneKind pk;
274     ElementKind[] elements = new ElementKind[5];
275     QualifierKind qk;
276     ExprKind ek;
277 
278     boolean filterBadTopElement() {
279         return elements[0].isAllowedTop();
280     }
281 
282     boolean filterBadIntermediateElement() {
283         for (int i = 1 ; i < 4 ; i++) {
284             if (!elements[i].isAllowedEnclosing(elements[i - 1], i == 1)) {
285                 return false;
286             }
287         }
288         return true;
289     }
290 
291     boolean filterBadTerminalElement() {
292         return elements[4].isAllowedEnclosing(elements[3], false) && !elements[4].isClassDecl();
293     }
294 
295     String template = "interface E {}\n" +
296                       "interface B { }\n" +
297                       "#{INTF1}\n" +
298                       "#{INTF2}\n" +
299                       "#{ELEM[0].0}";
300 
301     @Override
302     public void doWork() throws IOException {
303         newCompilationTask()
304                 .withSourceFromTemplate(template, this::methodName)
305                 .analyze(this::check);
306     }
307 
308     ComboParameter methodName(String parameterName) {
309         switch (parameterName) {
310             case "METH":
311                 String methodName = ek == ExprKind.THIS ? "test" : "m";
312                 return new ComboParameter.Constant<String>(methodName);
313             default:
314                 return null;
315         }
316     }
317 
318     void check(Result<?> res) {
319         Shape sh = new Shape(elements);
320 
321         boolean errorExpected = false;
322 
323         boolean badEnclosing = false;
324         boolean badThis = false;
325         boolean badSuper = false;
326 
327         if (qk == QualifierKind.ENCLOSING_1 &&
328                 sh.enclosingNames.size() < 1) {
329             errorExpected |= true;
330             badEnclosing = true;
331         }
332 
333         if (qk == QualifierKind.ENCLOSING_2 &&
334                 sh.enclosingNames.size() < 2) {
335             errorExpected |= true;
336             badEnclosing = true;
337         }
338 
339         if (ek == ExprKind.THIS) {
340             boolean found = false;
341             for (int i = 0; i < sh.enclosingElements.size(); i++) {
342                 if (sh.enclosingElements.get(i) == ElementKind.ANON_CLASS) continue;
343                 if (sh.enclosingNames.get(i).equals(qk.qualifierStr)) {
344                     found = sh.elementsWithMethod.contains(sh.enclosingNames.get(i));
345                     break;
346                 }
347             }
348             errorExpected |= !found;
349             if (!found) {
350                 badThis = true;
351             }
352         }
353 
354         if (ek == ExprKind.SUPER) {
355 
356             int lastIdx = sh.enclosingElements.size() - 1;
357             boolean found = lastIdx == -1 ? false :
358                     sh.enclosingElements.get(lastIdx).hasSuper() &&
359                     qk.allowSuperCall(ik, pk);
360 
361             errorExpected |= !found;
362             if (!found) {
363                 badSuper = true;
364             }
365         }
366 
367         if (res.hasErrors() != errorExpected) {
368             fail("Problem when compiling source:\n" +
369                     res.compilationInfo() +
370                     "\nenclosingElems: " + sh.enclosingElements +
371                     "\nenclosingNames: " + sh.enclosingNames +
372                     "\nelementsWithMethod: " + sh.elementsWithMethod +
373                     "\nbad encl: " + badEnclosing +
374                     "\nbad this: " + badThis +
375                     "\nbad super: " + badSuper +
376                     "\nqual kind: " + qk +
377                     "\nfound error: " + res.hasErrors());
378         }
379     }
380 }