T7042566.java revision 1519:5c956be64b9e
1/* 2 * Copyright (c) 2011, 2013, 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 7042566 8006694 27 * @summary Unambiguous varargs method calls flagged as ambiguous 28 * temporarily workaround combo tests are causing time out in several platforms 29 * @library ../../lib 30 * @build JavacTestingAbstractThreadedTest 31 * @run main/othervm T7042566 32 */ 33 34// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) 35// see JDK-8006746 36 37import java.io.File; 38import java.net.URI; 39import java.util.Arrays; 40import java.util.Locale; 41import java.util.concurrent.atomic.AtomicInteger; 42import javax.tools.Diagnostic; 43import javax.tools.JavaCompiler; 44import javax.tools.JavaFileObject; 45import javax.tools.SimpleJavaFileObject; 46import javax.tools.ToolProvider; 47 48import com.sun.source.util.JavacTask; 49import com.sun.tools.classfile.Instruction; 50import com.sun.tools.classfile.Attribute; 51import com.sun.tools.classfile.ClassFile; 52import com.sun.tools.classfile.Code_attribute; 53import com.sun.tools.classfile.ConstantPool.*; 54import com.sun.tools.classfile.Method; 55import com.sun.tools.javac.util.List; 56 57public class T7042566 58 extends JavacTestingAbstractThreadedTest 59 implements Runnable { 60 61 VarargsMethod m1; 62 VarargsMethod m2; 63 TypeConfiguration actuals; 64 65 T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf, 66 TypeConfiguration actuals) { 67 this.m1 = new VarargsMethod(m1_conf); 68 this.m2 = new VarargsMethod(m2_conf); 69 this.actuals = actuals; 70 } 71 72 @Override 73 public void run() { 74 int id = checkCount.incrementAndGet(); 75 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 76 JavaSource source = new JavaSource(id); 77 ErrorChecker ec = new ErrorChecker(); 78 JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), ec, 79 null, null, Arrays.asList(source)); 80 ct.call(); 81 check(source, ec, id); 82 } 83 84 void check(JavaSource source, ErrorChecker ec, int id) { 85 boolean resolutionError = false; 86 VarargsMethod selectedMethod = null; 87 88 boolean m1_applicable = m1.isApplicable(actuals); 89 boolean m2_applicable = m2.isApplicable(actuals); 90 91 if (!m1_applicable && !m2_applicable) { 92 resolutionError = true; 93 } else if (m1_applicable && m2_applicable) { 94 //most specific 95 boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); 96 boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); 97 98 resolutionError = m1_moreSpecific == m2_moreSpecific; 99 selectedMethod = m1_moreSpecific ? m1 : m2; 100 } else { 101 selectedMethod = m1_applicable ? 102 m1 : m2; 103 } 104 105 if (ec.errorFound != resolutionError) { 106 throw new Error("invalid diagnostics for source:\n" + 107 source.getCharContent(true) + 108 "\nExpected resolution error: " + resolutionError + 109 "\nFound error: " + ec.errorFound + 110 "\nCompiler diagnostics:\n" + ec.printDiags()); 111 } else if (!resolutionError) { 112 verifyBytecode(selectedMethod, source, id); 113 } 114 } 115 116 void verifyBytecode(VarargsMethod selected, JavaSource source, int id) { 117 bytecodeCheckCount.incrementAndGet(); 118 File compiledTest = new File(String.format("Test%d.class", id)); 119 try { 120 ClassFile cf = ClassFile.read(compiledTest); 121 Method testMethod = null; 122 for (Method m : cf.methods) { 123 if (m.getName(cf.constant_pool).equals("test")) { 124 testMethod = m; 125 break; 126 } 127 } 128 if (testMethod == null) { 129 throw new Error("Test method not found"); 130 } 131 Code_attribute ea = 132 (Code_attribute)testMethod.attributes.get(Attribute.Code); 133 if (testMethod == null) { 134 throw new Error("Code attribute for test() method not found"); 135 } 136 137 for (Instruction i : ea.getInstructions()) { 138 if (i.getMnemonic().equals("invokevirtual")) { 139 int cp_entry = i.getUnsignedShort(1); 140 CONSTANT_Methodref_info methRef = 141 (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); 142 String type = methRef.getNameAndTypeInfo().getType(); 143 String sig = selected.parameterTypes.bytecodeSigStr; 144 if (!type.contains(sig)) { 145 throw new Error("Unexpected type method call: " + 146 type + "" + 147 "\nfound: " + sig + 148 "\n" + source.getCharContent(true)); 149 } 150 break; 151 } 152 } 153 } catch (Exception e) { 154 e.printStackTrace(); 155 throw new Error("error reading " + compiledTest +": " + e); 156 } 157 } 158 159 class JavaSource extends SimpleJavaFileObject { 160 161 static final String source_template = "class Test#ID {\n" + 162 " #V1\n" + 163 " #V2\n" + 164 " void test() { m(#E); }\n" + 165 "}"; 166 167 String source; 168 169 public JavaSource(int id) { 170 super(URI.create(String.format("myfo:/Test%d.java", id)), 171 JavaFileObject.Kind.SOURCE); 172 source = source_template.replaceAll("#V1", m1.toString()) 173 .replaceAll("#V2", m2.toString()) 174 .replaceAll("#E", actuals.expressionListStr) 175 .replaceAll("#ID", String.valueOf(id)); 176 } 177 178 @Override 179 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 180 return source; 181 } 182 } 183 184 public static void main(String... args) throws Exception { 185 for (TypeConfiguration tconf1 : TypeConfiguration.values()) { 186 for (TypeConfiguration tconf2 : TypeConfiguration.values()) { 187 for (TypeConfiguration tconf3 : TypeConfiguration.values()) { 188 pool.execute(new T7042566(tconf1, tconf2, tconf3)); 189 } 190 } 191 } 192 193 outWriter.println("Bytecode checks made: " + bytecodeCheckCount.get()); 194 checkAfterExec(); 195 } 196 197 enum TypeKind { 198 OBJECT("Object", "(Object)null", "Ljava/lang/Object;"), 199 STRING("String", "(String)null", "Ljava/lang/String;"); 200 201 String typeString; 202 String valueString; 203 String bytecodeString; 204 205 TypeKind(String typeString, String valueString, String bytecodeString) { 206 this.typeString = typeString; 207 this.valueString = valueString; 208 this.bytecodeString = bytecodeString; 209 } 210 211 boolean isSubtypeOf(TypeKind that) { 212 return that == OBJECT || 213 (that == STRING && this == STRING); 214 } 215 } 216 217 enum TypeConfiguration { 218 A(TypeKind.OBJECT), 219 B(TypeKind.STRING), 220 AA(TypeKind.OBJECT, TypeKind.OBJECT), 221 AB(TypeKind.OBJECT, TypeKind.STRING), 222 BA(TypeKind.STRING, TypeKind.OBJECT), 223 BB(TypeKind.STRING, TypeKind.STRING), 224 AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT), 225 AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING), 226 ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT), 227 ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING), 228 BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT), 229 BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING), 230 BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT), 231 BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING); 232 233 List<TypeKind> typeKindList; 234 String expressionListStr; 235 String parameterListStr; 236 String bytecodeSigStr; 237 238 private TypeConfiguration(TypeKind... typeKindList) { 239 this.typeKindList = List.from(typeKindList); 240 expressionListStr = asExpressionList(); 241 parameterListStr = asParameterList(); 242 bytecodeSigStr = asBytecodeString(); 243 } 244 245 private String asExpressionList() { 246 StringBuilder buf = new StringBuilder(); 247 String sep = ""; 248 for (TypeKind tk : typeKindList) { 249 buf.append(sep); 250 buf.append(tk.valueString); 251 sep = ","; 252 } 253 return buf.toString(); 254 } 255 256 private String asParameterList() { 257 StringBuilder buf = new StringBuilder(); 258 String sep = ""; 259 int count = 0; 260 for (TypeKind arg : typeKindList) { 261 buf.append(sep); 262 buf.append(arg.typeString); 263 if (count == (typeKindList.size() - 1)) { 264 buf.append("..."); 265 } 266 buf.append(" "); 267 buf.append("arg" + count++); 268 sep = ","; 269 } 270 return buf.toString(); 271 } 272 273 private String asBytecodeString() { 274 StringBuilder buf = new StringBuilder(); 275 int count = 0; 276 for (TypeKind arg : typeKindList) { 277 if (count == (typeKindList.size() - 1)) { 278 buf.append("["); 279 } 280 buf.append(arg.bytecodeString); 281 count++; 282 } 283 return buf.toString(); 284 } 285 } 286 287 static class VarargsMethod { 288 TypeConfiguration parameterTypes; 289 290 public VarargsMethod(TypeConfiguration parameterTypes) { 291 this.parameterTypes = parameterTypes; 292 } 293 294 @Override 295 public String toString() { 296 return "void m( " + parameterTypes.parameterListStr + ") {}"; 297 } 298 299 boolean isApplicable(TypeConfiguration that) { 300 List<TypeKind> actuals = that.typeKindList; 301 List<TypeKind> formals = parameterTypes.typeKindList; 302 if ((actuals.size() - formals.size()) < -1) 303 return false; //not enough args 304 for (TypeKind actual : actuals) { 305 if (!actual.isSubtypeOf(formals.head)) 306 return false; //type mismatch 307 formals = formals.tail.isEmpty() ? 308 formals : 309 formals.tail; 310 } 311 return true; 312 } 313 314 boolean isMoreSpecificThan(VarargsMethod that) { 315 List<TypeKind> actuals = parameterTypes.typeKindList; 316 List<TypeKind> formals = that.parameterTypes.typeKindList; 317 int checks = 0; 318 int expectedCheck = Math.max(actuals.size(), formals.size()); 319 while (checks < expectedCheck) { 320 if (!actuals.head.isSubtypeOf(formals.head)) 321 return false; //type mismatch 322 formals = formals.tail.isEmpty() ? 323 formals : 324 formals.tail; 325 actuals = actuals.tail.isEmpty() ? 326 actuals : 327 actuals.tail; 328 checks++; 329 } 330 return true; 331 } 332 } 333 334 static class ErrorChecker 335 implements javax.tools.DiagnosticListener<JavaFileObject> { 336 337 boolean errorFound; 338 List<String> errDiags = List.nil(); 339 340 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 341 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 342 errDiags = errDiags 343 .append(diagnostic.getMessage(Locale.getDefault())); 344 errorFound = true; 345 } 346 } 347 348 String printDiags() { 349 StringBuilder buf = new StringBuilder(); 350 for (String s : errDiags) { 351 buf.append(s); 352 buf.append("\n"); 353 } 354 return buf.toString(); 355 } 356 } 357 358 //number of bytecode checks made while running combo tests 359 static AtomicInteger bytecodeCheckCount = new AtomicInteger(); 360 361} 362