| /* |
| * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 7042566 8006694 8129962 |
| * @summary Unambiguous varargs method calls flagged as ambiguous |
| * temporarily workaround combo tests are causing time out in several platforms |
| * @library /tools/javac/lib |
| * @modules jdk.jdeps/com.sun.tools.classfile |
| * jdk.compiler/com.sun.tools.javac.api |
| * jdk.compiler/com.sun.tools.javac.code |
| * jdk.compiler/com.sun.tools.javac.comp |
| * jdk.compiler/com.sun.tools.javac.main |
| * jdk.compiler/com.sun.tools.javac.tree |
| * jdk.compiler/com.sun.tools.javac.util |
| * @build combo.ComboTestHelper |
| * @run main T7042566 |
| */ |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import javax.tools.JavaFileObject; |
| |
| import com.sun.tools.classfile.Instruction; |
| import com.sun.tools.classfile.Attribute; |
| import com.sun.tools.classfile.ClassFile; |
| import com.sun.tools.classfile.Code_attribute; |
| import com.sun.tools.classfile.ConstantPool.*; |
| import com.sun.tools.classfile.Method; |
| import com.sun.tools.javac.util.List; |
| |
| import combo.ComboInstance; |
| import combo.ComboParameter; |
| import combo.ComboTask.Result; |
| import combo.ComboTestHelper; |
| |
| public class T7042566 extends ComboInstance<T7042566> { |
| |
| enum TypeKind { |
| OBJECT("Object", "(Object)null", "Ljava/lang/Object;"), |
| STRING("String", "(String)null", "Ljava/lang/String;"); |
| |
| String typeString; |
| String valueString; |
| String bytecodeString; |
| |
| TypeKind(String typeString, String valueString, String bytecodeString) { |
| this.typeString = typeString; |
| this.valueString = valueString; |
| this.bytecodeString = bytecodeString; |
| } |
| |
| boolean isSubtypeOf(TypeKind that) { |
| return that == OBJECT || |
| (that == STRING && this == STRING); |
| } |
| } |
| |
| enum TypeConfiguration implements ComboParameter { |
| A(TypeKind.OBJECT), |
| B(TypeKind.STRING), |
| AA(TypeKind.OBJECT, TypeKind.OBJECT), |
| AB(TypeKind.OBJECT, TypeKind.STRING), |
| BA(TypeKind.STRING, TypeKind.OBJECT), |
| BB(TypeKind.STRING, TypeKind.STRING), |
| AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT), |
| AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING), |
| ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT), |
| ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING), |
| BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT), |
| BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING), |
| BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT), |
| BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING); |
| |
| List<TypeKind> typeKindList; |
| String expressionListStr; |
| String parameterListStr; |
| String bytecodeSigStr; |
| |
| TypeConfiguration(TypeKind... typeKindList) { |
| this.typeKindList = List.from(typeKindList); |
| expressionListStr = asExpressionList(); |
| parameterListStr = asParameterList(); |
| bytecodeSigStr = asBytecodeString(); |
| } |
| |
| private String asExpressionList() { |
| StringBuilder buf = new StringBuilder(); |
| String sep = ""; |
| for (TypeKind tk : typeKindList) { |
| buf.append(sep); |
| buf.append(tk.valueString); |
| sep = ","; |
| } |
| return buf.toString(); |
| } |
| |
| private String asParameterList() { |
| StringBuilder buf = new StringBuilder(); |
| String sep = ""; |
| int count = 0; |
| for (TypeKind arg : typeKindList) { |
| buf.append(sep); |
| buf.append(arg.typeString); |
| if (count == (typeKindList.size() - 1)) { |
| buf.append("..."); |
| } |
| buf.append(" "); |
| buf.append("arg" + count++); |
| sep = ","; |
| } |
| return buf.toString(); |
| } |
| |
| private String asBytecodeString() { |
| StringBuilder buf = new StringBuilder(); |
| int count = 0; |
| for (TypeKind arg : typeKindList) { |
| if (count == (typeKindList.size() - 1)) { |
| buf.append("["); |
| } |
| buf.append(arg.bytecodeString); |
| count++; |
| } |
| return buf.toString(); |
| } |
| |
| @Override |
| public String expand(String optParameter) { |
| return expressionListStr; |
| } |
| } |
| |
| static class VarargsMethod { |
| TypeConfiguration parameterTypes; |
| |
| public VarargsMethod(TypeConfiguration parameterTypes) { |
| this.parameterTypes = parameterTypes; |
| } |
| |
| @Override |
| public String toString() { |
| return "void m( " + parameterTypes.parameterListStr + ") {}"; |
| } |
| |
| boolean isApplicable(TypeConfiguration that) { |
| List<TypeKind> actuals = that.typeKindList; |
| List<TypeKind> formals = parameterTypes.typeKindList; |
| if ((actuals.size() - formals.size()) < -1) |
| return false; //not enough args |
| for (TypeKind actual : actuals) { |
| if (!actual.isSubtypeOf(formals.head)) |
| return false; //type mismatch |
| formals = formals.tail.isEmpty() ? |
| formals : |
| formals.tail; |
| } |
| return true; |
| } |
| |
| boolean isMoreSpecificThan(VarargsMethod that) { |
| List<TypeKind> actuals = parameterTypes.typeKindList; |
| List<TypeKind> formals = that.parameterTypes.typeKindList; |
| int checks = 0; |
| int expectedCheck = Math.max(actuals.size(), formals.size()); |
| while (checks < expectedCheck) { |
| if (!actuals.head.isSubtypeOf(formals.head)) |
| return false; //type mismatch |
| formals = formals.tail.isEmpty() ? |
| formals : |
| formals.tail; |
| actuals = actuals.tail.isEmpty() ? |
| actuals : |
| actuals.tail; |
| checks++; |
| } |
| return true; |
| } |
| } |
| |
| public static void main(String[] args) { |
| new ComboTestHelper<T7042566>() |
| .withArrayDimension("SIG", (x, sig, idx) -> x.methodSignatures[idx] = sig, 2, TypeConfiguration.values()) |
| .withDimension("ACTUALS", (x, actuals) -> x.actuals = actuals, TypeConfiguration.values()) |
| .run(T7042566::new, T7042566::setup); |
| } |
| |
| VarargsMethod m1; |
| VarargsMethod m2; |
| TypeConfiguration[] methodSignatures = new TypeConfiguration[2]; |
| TypeConfiguration actuals; |
| |
| void setup() { |
| this.m1 = new VarargsMethod(methodSignatures[0]); |
| this.m2 = new VarargsMethod(methodSignatures[1]); |
| } |
| |
| final String source_template = "class Test {\n" + |
| " #{METH.1}\n" + |
| " #{METH.2}\n" + |
| " void test() { m(#{ACTUALS}); }\n" + |
| "}"; |
| |
| @Override |
| public void doWork() throws IOException { |
| check(newCompilationTask() |
| .withSourceFromTemplate(source_template, this::getMethodDecl) |
| .generate()); |
| } |
| |
| ComboParameter getMethodDecl(String parameterName) { |
| switch (parameterName) { |
| case "METH": return optParameter -> { |
| return optParameter.equals("1") ? |
| m1.toString() : m2.toString(); |
| }; |
| default: |
| return null; |
| } |
| } |
| |
| void check(Result<Iterable<? extends JavaFileObject>> res) { |
| boolean resolutionError = false; |
| VarargsMethod selectedMethod = null; |
| |
| boolean m1_applicable = m1.isApplicable(actuals); |
| boolean m2_applicable = m2.isApplicable(actuals); |
| |
| if (!m1_applicable && !m2_applicable) { |
| resolutionError = true; |
| } else if (m1_applicable && m2_applicable) { |
| //most specific |
| boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); |
| boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); |
| |
| resolutionError = m1_moreSpecific == m2_moreSpecific; |
| selectedMethod = m1_moreSpecific ? m1 : m2; |
| } else { |
| selectedMethod = m1_applicable ? |
| m1 : m2; |
| } |
| |
| if (res.hasErrors() != resolutionError) { |
| fail("invalid diagnostics for source:\n" + |
| res.compilationInfo() + |
| "\nExpected resolution error: " + resolutionError + |
| "\nFound error: " + res.hasErrors()); |
| } else if (!resolutionError) { |
| verifyBytecode(res, selectedMethod); |
| } |
| } |
| |
| void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res, VarargsMethod selected) { |
| try (InputStream is = res.get().iterator().next().openInputStream()) { |
| ClassFile cf = ClassFile.read(is); |
| Method testMethod = null; |
| for (Method m : cf.methods) { |
| if (m.getName(cf.constant_pool).equals("test")) { |
| testMethod = m; |
| break; |
| } |
| } |
| if (testMethod == null) { |
| fail("Test method not found"); |
| return; |
| } |
| Code_attribute ea = |
| (Code_attribute)testMethod.attributes.get(Attribute.Code); |
| if (testMethod == null) { |
| fail("Code attribute for test() method not found"); |
| return; |
| } |
| |
| for (Instruction i : ea.getInstructions()) { |
| if (i.getMnemonic().equals("invokevirtual")) { |
| int cp_entry = i.getUnsignedShort(1); |
| CONSTANT_Methodref_info methRef = |
| (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); |
| String type = methRef.getNameAndTypeInfo().getType(); |
| String sig = selected.parameterTypes.bytecodeSigStr; |
| if (!type.contains(sig)) { |
| fail("Unexpected type method call: " + |
| type + "" + |
| "\nfound: " + sig + |
| "\n" + res.compilationInfo()); |
| return; |
| } |
| break; |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| fail("error reading classfile; " + res.compilationInfo() +": " + e); |
| } |
| } |
| } |