mcimadamore@1006: /* vromero@1482: * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. mcimadamore@1006: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@1006: * mcimadamore@1006: * This code is free software; you can redistribute it and/or modify it mcimadamore@1006: * under the terms of the GNU General Public License version 2 only, as mcimadamore@1006: * published by the Free Software Foundation. mcimadamore@1006: * mcimadamore@1006: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@1006: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@1006: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@1006: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@1006: * accompanied this code). mcimadamore@1006: * mcimadamore@1006: * You should have received a copy of the GNU General Public License version mcimadamore@1006: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@1006: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@1006: * mcimadamore@1006: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mcimadamore@1006: * or visit www.oracle.com if you need additional information or have any mcimadamore@1006: * questions. mcimadamore@1006: */ mcimadamore@1006: mcimadamore@1006: /* mcimadamore@1006: * @test vromero@1520: * @bug 7042566 8006694 mcimadamore@1006: * @summary Unambiguous varargs method calls flagged as ambiguous vromero@1520: * temporarily workaround combo tests are causing time out in several platforms vromero@1482: * @library ../../lib vromero@1482: * @build JavacTestingAbstractThreadedTest vromero@1520: * @run main/othervm T7042566 mcimadamore@1006: */ mcimadamore@1006: vromero@1520: // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) vromero@1520: // see JDK-8006746 vromero@1520: vromero@1482: import java.io.File; vromero@1482: import java.net.URI; vromero@1482: import java.util.Arrays; vromero@1482: import java.util.Locale; vromero@1482: import java.util.concurrent.atomic.AtomicInteger; vromero@1482: import javax.tools.Diagnostic; vromero@1482: import javax.tools.JavaCompiler; vromero@1482: import javax.tools.JavaFileObject; vromero@1482: import javax.tools.SimpleJavaFileObject; vromero@1482: import javax.tools.ToolProvider; vromero@1482: mcimadamore@1006: import com.sun.source.util.JavacTask; mcimadamore@1006: import com.sun.tools.classfile.Instruction; mcimadamore@1006: import com.sun.tools.classfile.Attribute; mcimadamore@1006: import com.sun.tools.classfile.ClassFile; mcimadamore@1006: import com.sun.tools.classfile.Code_attribute; mcimadamore@1006: import com.sun.tools.classfile.ConstantPool.*; mcimadamore@1006: import com.sun.tools.classfile.Method; mcimadamore@1006: import com.sun.tools.javac.util.List; mcimadamore@1006: vromero@1482: public class T7042566 vromero@1482: extends JavacTestingAbstractThreadedTest vromero@1482: implements Runnable { mcimadamore@1006: mcimadamore@1006: VarargsMethod m1; mcimadamore@1006: VarargsMethod m2; mcimadamore@1006: TypeConfiguration actuals; mcimadamore@1006: vromero@1482: T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf, vromero@1482: TypeConfiguration actuals) { mcimadamore@1006: this.m1 = new VarargsMethod(m1_conf); mcimadamore@1006: this.m2 = new VarargsMethod(m2_conf); mcimadamore@1006: this.actuals = actuals; mcimadamore@1006: } mcimadamore@1006: vromero@1482: @Override vromero@1482: public void run() { vromero@1482: int id = checkCount.incrementAndGet(); mcimadamore@1006: final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); vromero@1482: JavaSource source = new JavaSource(id); mcimadamore@1006: ErrorChecker ec = new ErrorChecker(); vromero@1482: JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), ec, mcimadamore@1006: null, null, Arrays.asList(source)); mcimadamore@1006: ct.call(); vromero@1482: check(source, ec, id); mcimadamore@1006: } mcimadamore@1006: vromero@1482: void check(JavaSource source, ErrorChecker ec, int id) { mcimadamore@1006: boolean resolutionError = false; mcimadamore@1006: VarargsMethod selectedMethod = null; mcimadamore@1006: mcimadamore@1006: boolean m1_applicable = m1.isApplicable(actuals); mcimadamore@1006: boolean m2_applicable = m2.isApplicable(actuals); mcimadamore@1006: mcimadamore@1006: if (!m1_applicable && !m2_applicable) { mcimadamore@1006: resolutionError = true; mcimadamore@1006: } else if (m1_applicable && m2_applicable) { mcimadamore@1006: //most specific mcimadamore@1006: boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); mcimadamore@1006: boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); mcimadamore@1006: mcimadamore@1006: resolutionError = m1_moreSpecific == m2_moreSpecific; mcimadamore@1006: selectedMethod = m1_moreSpecific ? m1 : m2; mcimadamore@1006: } else { mcimadamore@1006: selectedMethod = m1_applicable ? mcimadamore@1006: m1 : m2; mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: if (ec.errorFound != resolutionError) { mcimadamore@1006: throw new Error("invalid diagnostics for source:\n" + mcimadamore@1006: source.getCharContent(true) + mcimadamore@1006: "\nExpected resolution error: " + resolutionError + mcimadamore@1006: "\nFound error: " + ec.errorFound + mcimadamore@1006: "\nCompiler diagnostics:\n" + ec.printDiags()); mcimadamore@1006: } else if (!resolutionError) { vromero@1482: verifyBytecode(selectedMethod, source, id); mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: vromero@1482: void verifyBytecode(VarargsMethod selected, JavaSource source, int id) { vromero@1482: bytecodeCheckCount.incrementAndGet(); vromero@1482: File compiledTest = new File(String.format("Test%d.class", id)); mcimadamore@1006: try { mcimadamore@1006: ClassFile cf = ClassFile.read(compiledTest); mcimadamore@1006: Method testMethod = null; mcimadamore@1006: for (Method m : cf.methods) { mcimadamore@1006: if (m.getName(cf.constant_pool).equals("test")) { mcimadamore@1006: testMethod = m; mcimadamore@1006: break; mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: if (testMethod == null) { mcimadamore@1006: throw new Error("Test method not found"); mcimadamore@1006: } vromero@1482: Code_attribute ea = vromero@1482: (Code_attribute)testMethod.attributes.get(Attribute.Code); mcimadamore@1006: if (testMethod == null) { mcimadamore@1006: throw new Error("Code attribute for test() method not found"); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: for (Instruction i : ea.getInstructions()) { mcimadamore@1006: if (i.getMnemonic().equals("invokevirtual")) { mcimadamore@1006: int cp_entry = i.getUnsignedShort(1); mcimadamore@1006: CONSTANT_Methodref_info methRef = vromero@1482: (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); mcimadamore@1006: String type = methRef.getNameAndTypeInfo().getType(); mcimadamore@1006: String sig = selected.parameterTypes.bytecodeSigStr; mcimadamore@1006: if (!type.contains(sig)) { vromero@1482: throw new Error("Unexpected type method call: " + vromero@1482: type + "" + mcimadamore@1006: "\nfound: " + sig + mcimadamore@1006: "\n" + source.getCharContent(true)); mcimadamore@1006: } mcimadamore@1006: break; mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: } catch (Exception e) { mcimadamore@1006: e.printStackTrace(); mcimadamore@1006: throw new Error("error reading " + compiledTest +": " + e); mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: class JavaSource extends SimpleJavaFileObject { mcimadamore@1006: vromero@1482: static final String source_template = "class Test#ID {\n" + mcimadamore@1006: " #V1\n" + mcimadamore@1006: " #V2\n" + mcimadamore@1006: " void test() { m(#E); }\n" + mcimadamore@1006: "}"; mcimadamore@1006: mcimadamore@1006: String source; mcimadamore@1006: vromero@1482: public JavaSource(int id) { vromero@1482: super(URI.create(String.format("myfo:/Test%d.java", id)), vromero@1482: JavaFileObject.Kind.SOURCE); vromero@1482: source = source_template.replaceAll("#V1", m1.toString()) vromero@1482: .replaceAll("#V2", m2.toString()) vromero@1482: .replaceAll("#E", actuals.expressionListStr) vromero@1482: .replaceAll("#ID", String.valueOf(id)); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: @Override mcimadamore@1006: public CharSequence getCharContent(boolean ignoreEncodingErrors) { mcimadamore@1006: return source; mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: public static void main(String... args) throws Exception { mcimadamore@1006: for (TypeConfiguration tconf1 : TypeConfiguration.values()) { mcimadamore@1006: for (TypeConfiguration tconf2 : TypeConfiguration.values()) { mcimadamore@1006: for (TypeConfiguration tconf3 : TypeConfiguration.values()) { vromero@1482: pool.execute(new T7042566(tconf1, tconf2, tconf3)); mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: vromero@1482: outWriter.println("Bytecode checks made: " + bytecodeCheckCount.get()); vromero@1482: checkAfterExec(); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: enum TypeKind { mcimadamore@1006: OBJECT("Object", "(Object)null", "Ljava/lang/Object;"), mcimadamore@1006: STRING("String", "(String)null", "Ljava/lang/String;"); mcimadamore@1006: mcimadamore@1006: String typeString; mcimadamore@1006: String valueString; mcimadamore@1006: String bytecodeString; mcimadamore@1006: mcimadamore@1006: TypeKind(String typeString, String valueString, String bytecodeString) { mcimadamore@1006: this.typeString = typeString; mcimadamore@1006: this.valueString = valueString; mcimadamore@1006: this.bytecodeString = bytecodeString; mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: boolean isSubtypeOf(TypeKind that) { mcimadamore@1006: return that == OBJECT || mcimadamore@1006: (that == STRING && this == STRING); mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: enum TypeConfiguration { mcimadamore@1006: A(TypeKind.OBJECT), mcimadamore@1006: B(TypeKind.STRING), mcimadamore@1006: AA(TypeKind.OBJECT, TypeKind.OBJECT), mcimadamore@1006: AB(TypeKind.OBJECT, TypeKind.STRING), mcimadamore@1006: BA(TypeKind.STRING, TypeKind.OBJECT), mcimadamore@1006: BB(TypeKind.STRING, TypeKind.STRING), mcimadamore@1006: AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT), mcimadamore@1006: AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING), mcimadamore@1006: ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT), mcimadamore@1006: ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING), mcimadamore@1006: BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT), mcimadamore@1006: BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING), mcimadamore@1006: BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT), mcimadamore@1006: BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING); mcimadamore@1006: mcimadamore@1006: List typeKindList; mcimadamore@1006: String expressionListStr; mcimadamore@1006: String parameterListStr; mcimadamore@1006: String bytecodeSigStr; mcimadamore@1006: mcimadamore@1006: private TypeConfiguration(TypeKind... typeKindList) { mcimadamore@1006: this.typeKindList = List.from(typeKindList); mcimadamore@1006: expressionListStr = asExpressionList(); mcimadamore@1006: parameterListStr = asParameterList(); mcimadamore@1006: bytecodeSigStr = asBytecodeString(); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: private String asExpressionList() { mcimadamore@1006: StringBuilder buf = new StringBuilder(); mcimadamore@1006: String sep = ""; mcimadamore@1006: for (TypeKind tk : typeKindList) { mcimadamore@1006: buf.append(sep); mcimadamore@1006: buf.append(tk.valueString); mcimadamore@1006: sep = ","; mcimadamore@1006: } mcimadamore@1006: return buf.toString(); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: private String asParameterList() { mcimadamore@1006: StringBuilder buf = new StringBuilder(); mcimadamore@1006: String sep = ""; mcimadamore@1006: int count = 0; mcimadamore@1006: for (TypeKind arg : typeKindList) { mcimadamore@1006: buf.append(sep); mcimadamore@1006: buf.append(arg.typeString); mcimadamore@1006: if (count == (typeKindList.size() - 1)) { mcimadamore@1006: buf.append("..."); mcimadamore@1006: } mcimadamore@1006: buf.append(" "); mcimadamore@1006: buf.append("arg" + count++); mcimadamore@1006: sep = ","; mcimadamore@1006: } mcimadamore@1006: return buf.toString(); mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: private String asBytecodeString() { mcimadamore@1006: StringBuilder buf = new StringBuilder(); mcimadamore@1006: int count = 0; mcimadamore@1006: for (TypeKind arg : typeKindList) { mcimadamore@1006: if (count == (typeKindList.size() - 1)) { mcimadamore@1006: buf.append("["); mcimadamore@1006: } mcimadamore@1006: buf.append(arg.bytecodeString); mcimadamore@1006: count++; mcimadamore@1006: } mcimadamore@1006: return buf.toString(); mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: static class VarargsMethod { mcimadamore@1006: TypeConfiguration parameterTypes; mcimadamore@1006: mcimadamore@1006: public VarargsMethod(TypeConfiguration parameterTypes) { mcimadamore@1006: this.parameterTypes = parameterTypes; mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: @Override mcimadamore@1006: public String toString() { mcimadamore@1006: return "void m( " + parameterTypes.parameterListStr + ") {}"; mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: boolean isApplicable(TypeConfiguration that) { mcimadamore@1006: List actuals = that.typeKindList; mcimadamore@1006: List formals = parameterTypes.typeKindList; mcimadamore@1006: if ((actuals.size() - formals.size()) < -1) mcimadamore@1006: return false; //not enough args mcimadamore@1006: for (TypeKind actual : actuals) { mcimadamore@1006: if (!actual.isSubtypeOf(formals.head)) mcimadamore@1006: return false; //type mismatch mcimadamore@1006: formals = formals.tail.isEmpty() ? mcimadamore@1006: formals : mcimadamore@1006: formals.tail; mcimadamore@1006: } mcimadamore@1006: return true; mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: boolean isMoreSpecificThan(VarargsMethod that) { mcimadamore@1006: List actuals = parameterTypes.typeKindList; mcimadamore@1006: List formals = that.parameterTypes.typeKindList; mcimadamore@1006: int checks = 0; mcimadamore@1006: int expectedCheck = Math.max(actuals.size(), formals.size()); mcimadamore@1006: while (checks < expectedCheck) { mcimadamore@1006: if (!actuals.head.isSubtypeOf(formals.head)) mcimadamore@1006: return false; //type mismatch mcimadamore@1006: formals = formals.tail.isEmpty() ? mcimadamore@1006: formals : mcimadamore@1006: formals.tail; mcimadamore@1006: actuals = actuals.tail.isEmpty() ? mcimadamore@1006: actuals : mcimadamore@1006: actuals.tail; mcimadamore@1006: checks++; mcimadamore@1006: } mcimadamore@1006: return true; mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: vromero@1482: static class ErrorChecker vromero@1482: implements javax.tools.DiagnosticListener { mcimadamore@1006: mcimadamore@1006: boolean errorFound; mcimadamore@1006: List errDiags = List.nil(); mcimadamore@1006: mcimadamore@1006: public void report(Diagnostic diagnostic) { mcimadamore@1006: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { vromero@1482: errDiags = errDiags vromero@1482: .append(diagnostic.getMessage(Locale.getDefault())); mcimadamore@1006: errorFound = true; mcimadamore@1006: } mcimadamore@1006: } mcimadamore@1006: mcimadamore@1006: String printDiags() { mcimadamore@1006: StringBuilder buf = new StringBuilder(); mcimadamore@1006: for (String s : errDiags) { mcimadamore@1006: buf.append(s); mcimadamore@1006: buf.append("\n"); mcimadamore@1006: } mcimadamore@1006: return buf.toString(); mcimadamore@1006: } mcimadamore@1006: } vromero@1482: vromero@1482: //number of bytecode checks made while running combo tests vromero@1482: static AtomicInteger bytecodeCheckCount = new AtomicInteger(); vromero@1482: mcimadamore@1006: }