1 /*
2  * Copyright (c) 2012, 2014, 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 8005166
27  * @summary Add support for static interface methods
28  *          Smoke test for static interface method hiding
29  * @run main/timeout=600 InterfaceMethodHidingTest
30  */
31 
32 import com.sun.source.util.JavacTask;
33 import java.net.URI;
34 import java.util.Arrays;
35 import javax.tools.Diagnostic;
36 import javax.tools.JavaCompiler;
37 import javax.tools.JavaFileObject;
38 import javax.tools.SimpleJavaFileObject;
39 import javax.tools.StandardJavaFileManager;
40 import javax.tools.ToolProvider;
41 
42 
43 public class InterfaceMethodHidingTest {
44 
45     static int checkCount = 0;
46 
47     enum SignatureKind {
48         VOID_INTEGER("void m(Integer s)", "return;"),
49         STRING_INTEGER("String m(Integer s)", "return null;"),
50         VOID_STRING("void m(String s)", "return;"),
51         STRING_STRING("String m(String s)", "return null;");
52 
53         String sigStr;
54         String retStr;
55 
SignatureKind(String sigStr, String retStr)56         SignatureKind(String sigStr, String retStr) {
57             this.sigStr = sigStr;
58             this.retStr = retStr;
59         }
60 
overrideEquivalentWith(SignatureKind s2)61         boolean overrideEquivalentWith(SignatureKind s2) {
62             switch (this) {
63                 case VOID_INTEGER:
64                 case STRING_INTEGER:
65                     return s2 == VOID_INTEGER || s2 == STRING_INTEGER;
66                 case VOID_STRING:
67                 case STRING_STRING:
68                     return s2 == VOID_STRING || s2 == STRING_STRING;
69                 default:
70                     throw new AssertionError("bad signature kind");
71             }
72         }
73     }
74 
75     enum MethodKind {
76         VIRTUAL("", "#M #S;"),
77         STATIC("static", "#M #S { #BE; #R }"),
78         DEFAULT("default", "#M #S { #BE; #R }");
79 
80         String modStr;
81         String methTemplate;
82 
MethodKind(String modStr, String methTemplate)83         MethodKind(String modStr, String methTemplate) {
84             this.modStr = modStr;
85             this.methTemplate = methTemplate;
86         }
87 
inherithed()88         boolean inherithed() {
89             return this != STATIC;
90         }
91 
overrides(MethodKind mk1, SignatureKind sk1, MethodKind mk2, SignatureKind sk2)92         static boolean overrides(MethodKind mk1, SignatureKind sk1, MethodKind mk2, SignatureKind sk2) {
93             return sk1 == sk2 &&
94                     mk2.inherithed() &&
95                     mk1 != STATIC;
96         }
97 
getBody(BodyExpr be, SignatureKind sk)98         String getBody(BodyExpr be, SignatureKind sk) {
99             return methTemplate.replaceAll("#BE", be.bodyExprStr)
100                     .replaceAll("#R", sk.retStr)
101                     .replaceAll("#M", modStr)
102                     .replaceAll("#S", sk.sigStr);
103         }
104     }
105 
106     enum BodyExpr {
107         NONE(""),
108         THIS("Object o = this");
109 
110         String bodyExprStr;
111 
BodyExpr(String bodyExprStr)112         BodyExpr(String bodyExprStr) {
113             this.bodyExprStr = bodyExprStr;
114         }
115 
allowed(MethodKind mk)116         boolean allowed(MethodKind mk) {
117             return this == NONE ||
118                     mk != MethodKind.STATIC;
119         }
120     }
121 
main(String... args)122     public static void main(String... args) throws Exception {
123 
124         //create default shared JavaCompiler - reused across multiple compilations
125         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
126         StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
127 
128         for (MethodKind mk1 : MethodKind.values()) {
129             for (SignatureKind sk1 : SignatureKind.values()) {
130                 for (BodyExpr be1 : BodyExpr.values()) {
131                     for (MethodKind mk2 : MethodKind.values()) {
132                         for (SignatureKind sk2 : SignatureKind.values()) {
133                             for (BodyExpr be2 : BodyExpr.values()) {
134                                 for (MethodKind mk3 : MethodKind.values()) {
135                                     for (SignatureKind sk3 : SignatureKind.values()) {
136                                         for (BodyExpr be3 : BodyExpr.values()) {
137                                             new InterfaceMethodHidingTest(mk1, mk2, mk3, sk1, sk2, sk3, be1, be2, be3).run(comp, fm);
138                                         }
139                                     }
140                                 }
141                             }
142                         }
143                     }
144                 }
145             }
146         }
147         System.out.println("Total check executed: " + checkCount);
148     }
149 
150     MethodKind mk1, mk2, mk3;
151     SignatureKind sk1, sk2, sk3;
152     BodyExpr be1, be2, be3;
153     JavaSource source;
154     DiagnosticChecker diagChecker;
155 
InterfaceMethodHidingTest(MethodKind mk1, MethodKind mk2, MethodKind mk3, SignatureKind sk1, SignatureKind sk2, SignatureKind sk3, BodyExpr be1, BodyExpr be2, BodyExpr be3)156     InterfaceMethodHidingTest(MethodKind mk1, MethodKind mk2, MethodKind mk3,
157             SignatureKind sk1, SignatureKind sk2, SignatureKind sk3, BodyExpr be1, BodyExpr be2, BodyExpr be3) {
158         this.mk1 = mk1;
159         this.mk2 = mk2;
160         this.mk3 = mk3;
161         this.sk1 = sk1;
162         this.sk2 = sk2;
163         this.sk3 = sk3;
164         this.be1 = be1;
165         this.be2 = be2;
166         this.be3 = be3;
167         this.source = new JavaSource();
168         this.diagChecker = new DiagnosticChecker();
169     }
170 
171     class JavaSource extends SimpleJavaFileObject {
172 
173         String template = "interface Sup {\n" +
174                           "   default void sup() { }\n" +
175                           "}\n" +
176                           "interface A extends Sup {\n" +
177                           "   #M1\n" +
178                           "}\n" +
179                           "interface B extends A, Sup {\n" +
180                           "   #M2\n" +
181                           "}\n" +
182                           "interface C extends B, Sup {\n" +
183                           "   #M3\n" +
184                           "}\n";
185 
186         String source;
187 
JavaSource()188         public JavaSource() {
189             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
190             source = template.replaceAll("#M1", mk1.getBody(be1, sk1))
191                     .replaceAll("#M2", mk2.getBody(be2, sk2))
192                     .replaceAll("#M3", mk3.getBody(be3, sk3));
193         }
194 
195         @Override
getCharContent(boolean ignoreEncodingErrors)196         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
197             return source;
198         }
199     }
200 
run(JavaCompiler tool, StandardJavaFileManager fm)201     void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
202         JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
203                 Arrays.asList("-XDallowStaticInterfaceMethods"), null, Arrays.asList(source));
204         try {
205             ct.analyze();
206         } catch (Throwable ex) {
207             throw new AssertionError("Error thrown when analyzing the following source:\n" + source.getCharContent(true));
208         }
209         check();
210     }
211 
check()212     void check() {
213         boolean errorExpected =
214                 !be1.allowed(mk1) || !be2.allowed(mk2) || !be3.allowed(mk3);
215 
216         if (mk1.inherithed()) {
217             errorExpected |=
218                     sk2.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk2, sk2, mk1, sk1) ||
219                     sk3.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk3, sk3, mk1, sk1);
220         }
221 
222         if (mk2.inherithed()) {
223             errorExpected |=
224                     sk3.overrideEquivalentWith(sk2) && !MethodKind.overrides(mk3, sk3, mk2, sk2);
225         }
226 
227         checkCount++;
228         if (diagChecker.errorFound != errorExpected) {
229             throw new AssertionError("Problem when compiling source:\n" + source.getCharContent(true) +
230                     "\nfound error: " + diagChecker.errorFound);
231         }
232     }
233 
234     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
235 
236         boolean errorFound;
237 
report(Diagnostic<? extends JavaFileObject> diagnostic)238         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
239             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
240                 errorFound = true;
241             }
242         }
243     }
244 }
245