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