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