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 8129962 27 * @summary Unambiguous varargs method calls flagged as ambiguous 28 * temporarily workaround combo tests are causing time out in several platforms 29 * @library /tools/javac/lib 30 * @modules jdk.jdeps/com.sun.tools.classfile 31 * jdk.compiler/com.sun.tools.javac.api 32 * jdk.compiler/com.sun.tools.javac.code 33 * jdk.compiler/com.sun.tools.javac.comp 34 * jdk.compiler/com.sun.tools.javac.main 35 * jdk.compiler/com.sun.tools.javac.tree 36 * jdk.compiler/com.sun.tools.javac.util 37 * @build combo.ComboTestHelper 38 * @run main T7042566 39 */ 40 41import java.io.IOException; 42import java.io.InputStream; 43import javax.tools.JavaFileObject; 44 45import com.sun.tools.classfile.Instruction; 46import com.sun.tools.classfile.Attribute; 47import com.sun.tools.classfile.ClassFile; 48import com.sun.tools.classfile.Code_attribute; 49import com.sun.tools.classfile.ConstantPool.*; 50import com.sun.tools.classfile.Method; 51import com.sun.tools.javac.util.List; 52 53import combo.ComboInstance; 54import combo.ComboParameter; 55import combo.ComboTask.Result; 56import combo.ComboTestHelper; 57 58public class T7042566 extends ComboInstance<T7042566> { 59 60 enum TypeKind { 61 OBJECT("Object", "(Object)null", "Ljava/lang/Object;"), 62 STRING("String", "(String)null", "Ljava/lang/String;"); 63 64 String typeString; 65 String valueString; 66 String bytecodeString; 67 68 TypeKind(String typeString, String valueString, String bytecodeString) { 69 this.typeString = typeString; 70 this.valueString = valueString; 71 this.bytecodeString = bytecodeString; 72 } 73 74 boolean isSubtypeOf(TypeKind that) { 75 return that == OBJECT || 76 (that == STRING && this == STRING); 77 } 78 } 79 80 enum TypeConfiguration implements ComboParameter { 81 A(TypeKind.OBJECT), 82 B(TypeKind.STRING), 83 AA(TypeKind.OBJECT, TypeKind.OBJECT), 84 AB(TypeKind.OBJECT, TypeKind.STRING), 85 BA(TypeKind.STRING, TypeKind.OBJECT), 86 BB(TypeKind.STRING, TypeKind.STRING), 87 AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT), 88 AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING), 89 ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT), 90 ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING), 91 BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT), 92 BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING), 93 BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT), 94 BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING); 95 96 List<TypeKind> typeKindList; 97 String expressionListStr; 98 String parameterListStr; 99 String bytecodeSigStr; 100 101 TypeConfiguration(TypeKind... typeKindList) { 102 this.typeKindList = List.from(typeKindList); 103 expressionListStr = asExpressionList(); 104 parameterListStr = asParameterList(); 105 bytecodeSigStr = asBytecodeString(); 106 } 107 108 private String asExpressionList() { 109 StringBuilder buf = new StringBuilder(); 110 String sep = ""; 111 for (TypeKind tk : typeKindList) { 112 buf.append(sep); 113 buf.append(tk.valueString); 114 sep = ","; 115 } 116 return buf.toString(); 117 } 118 119 private String asParameterList() { 120 StringBuilder buf = new StringBuilder(); 121 String sep = ""; 122 int count = 0; 123 for (TypeKind arg : typeKindList) { 124 buf.append(sep); 125 buf.append(arg.typeString); 126 if (count == (typeKindList.size() - 1)) { 127 buf.append("..."); 128 } 129 buf.append(" "); 130 buf.append("arg" + count++); 131 sep = ","; 132 } 133 return buf.toString(); 134 } 135 136 private String asBytecodeString() { 137 StringBuilder buf = new StringBuilder(); 138 int count = 0; 139 for (TypeKind arg : typeKindList) { 140 if (count == (typeKindList.size() - 1)) { 141 buf.append("["); 142 } 143 buf.append(arg.bytecodeString); 144 count++; 145 } 146 return buf.toString(); 147 } 148 149 @Override 150 public String expand(String optParameter) { 151 return expressionListStr; 152 } 153 } 154 155 static class VarargsMethod { 156 TypeConfiguration parameterTypes; 157 158 public VarargsMethod(TypeConfiguration parameterTypes) { 159 this.parameterTypes = parameterTypes; 160 } 161 162 @Override 163 public String toString() { 164 return "void m( " + parameterTypes.parameterListStr + ") {}"; 165 } 166 167 boolean isApplicable(TypeConfiguration that) { 168 List<TypeKind> actuals = that.typeKindList; 169 List<TypeKind> formals = parameterTypes.typeKindList; 170 if ((actuals.size() - formals.size()) < -1) 171 return false; //not enough args 172 for (TypeKind actual : actuals) { 173 if (!actual.isSubtypeOf(formals.head)) 174 return false; //type mismatch 175 formals = formals.tail.isEmpty() ? 176 formals : 177 formals.tail; 178 } 179 return true; 180 } 181 182 boolean isMoreSpecificThan(VarargsMethod that) { 183 List<TypeKind> actuals = parameterTypes.typeKindList; 184 List<TypeKind> formals = that.parameterTypes.typeKindList; 185 int checks = 0; 186 int expectedCheck = Math.max(actuals.size(), formals.size()); 187 while (checks < expectedCheck) { 188 if (!actuals.head.isSubtypeOf(formals.head)) 189 return false; //type mismatch 190 formals = formals.tail.isEmpty() ? 191 formals : 192 formals.tail; 193 actuals = actuals.tail.isEmpty() ? 194 actuals : 195 actuals.tail; 196 checks++; 197 } 198 return true; 199 } 200 } 201 202 public static void main(String[] args) { 203 new ComboTestHelper<T7042566>() 204 .withArrayDimension("SIG", (x, sig, idx) -> x.methodSignatures[idx] = sig, 2, TypeConfiguration.values()) 205 .withDimension("ACTUALS", (x, actuals) -> x.actuals = actuals, TypeConfiguration.values()) 206 .run(T7042566::new, T7042566::setup); 207 } 208 209 VarargsMethod m1; 210 VarargsMethod m2; 211 TypeConfiguration[] methodSignatures = new TypeConfiguration[2]; 212 TypeConfiguration actuals; 213 214 void setup() { 215 this.m1 = new VarargsMethod(methodSignatures[0]); 216 this.m2 = new VarargsMethod(methodSignatures[1]); 217 } 218 219 final String source_template = "class Test {\n" + 220 " #{METH.1}\n" + 221 " #{METH.2}\n" + 222 " void test() { m(#{ACTUALS}); }\n" + 223 "}"; 224 225 @Override 226 public void doWork() throws IOException { 227 check(newCompilationTask() 228 .withSourceFromTemplate(source_template, this::getMethodDecl) 229 .generate()); 230 } 231 232 ComboParameter getMethodDecl(String parameterName) { 233 switch (parameterName) { 234 case "METH": return optParameter -> { 235 return optParameter.equals("1") ? 236 m1.toString() : m2.toString(); 237 }; 238 default: 239 return null; 240 } 241 } 242 243 void check(Result<Iterable<? extends JavaFileObject>> res) { 244 boolean resolutionError = false; 245 VarargsMethod selectedMethod = null; 246 247 boolean m1_applicable = m1.isApplicable(actuals); 248 boolean m2_applicable = m2.isApplicable(actuals); 249 250 if (!m1_applicable && !m2_applicable) { 251 resolutionError = true; 252 } else if (m1_applicable && m2_applicable) { 253 //most specific 254 boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); 255 boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); 256 257 resolutionError = m1_moreSpecific == m2_moreSpecific; 258 selectedMethod = m1_moreSpecific ? m1 : m2; 259 } else { 260 selectedMethod = m1_applicable ? 261 m1 : m2; 262 } 263 264 if (res.hasErrors() != resolutionError) { 265 fail("invalid diagnostics for source:\n" + 266 res.compilationInfo() + 267 "\nExpected resolution error: " + resolutionError + 268 "\nFound error: " + res.hasErrors()); 269 } else if (!resolutionError) { 270 verifyBytecode(res, selectedMethod); 271 } 272 } 273 274 void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res, VarargsMethod selected) { 275 try (InputStream is = res.get().iterator().next().openInputStream()) { 276 ClassFile cf = ClassFile.read(is); 277 Method testMethod = null; 278 for (Method m : cf.methods) { 279 if (m.getName(cf.constant_pool).equals("test")) { 280 testMethod = m; 281 break; 282 } 283 } 284 if (testMethod == null) { 285 fail("Test method not found"); 286 return; 287 } 288 Code_attribute ea = 289 (Code_attribute)testMethod.attributes.get(Attribute.Code); 290 if (testMethod == null) { 291 fail("Code attribute for test() method not found"); 292 return; 293 } 294 295 for (Instruction i : ea.getInstructions()) { 296 if (i.getMnemonic().equals("invokevirtual")) { 297 int cp_entry = i.getUnsignedShort(1); 298 CONSTANT_Methodref_info methRef = 299 (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); 300 String type = methRef.getNameAndTypeInfo().getType(); 301 String sig = selected.parameterTypes.bytecodeSigStr; 302 if (!type.contains(sig)) { 303 fail("Unexpected type method call: " + 304 type + "" + 305 "\nfound: " + sig + 306 "\n" + res.compilationInfo()); 307 return; 308 } 309 break; 310 } 311 } 312 } catch (Exception e) { 313 e.printStackTrace(); 314 fail("error reading classfile; " + res.compilationInfo() +": " + e); 315 } 316 } 317} 318