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