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