mcimadamore@787: /* mcimadamore@787: * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. mcimadamore@787: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@787: * mcimadamore@787: * This code is free software; you can redistribute it and/or modify it mcimadamore@787: * under the terms of the GNU General Public License version 2 only, as mcimadamore@787: * published by the Free Software Foundation. mcimadamore@787: * mcimadamore@787: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@787: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@787: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@787: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@787: * accompanied this code). mcimadamore@787: * mcimadamore@787: * You should have received a copy of the GNU General Public License version mcimadamore@787: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@787: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@787: * mcimadamore@787: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mcimadamore@787: * or visit www.oracle.com if you need additional information or have any mcimadamore@787: * questions. mcimadamore@787: */ mcimadamore@787: mcimadamore@787: /* mcimadamore@787: * @test mcimadamore@787: * @bug 6199075 mcimadamore@787: * mcimadamore@787: * @summary Unambiguous varargs method calls flagged as ambiguous mcimadamore@787: * @author mcimadamore mcimadamore@787: * mcimadamore@787: */ mcimadamore@787: mcimadamore@787: import com.sun.source.util.JavacTask; mcimadamore@787: import com.sun.tools.classfile.Instruction; mcimadamore@787: import com.sun.tools.classfile.Attribute; mcimadamore@787: import com.sun.tools.classfile.ClassFile; mcimadamore@787: import com.sun.tools.classfile.Code_attribute; mcimadamore@787: import com.sun.tools.classfile.ConstantPool.*; mcimadamore@787: import com.sun.tools.classfile.Method; jjh@892: import com.sun.tools.javac.api.JavacTool; mcimadamore@787: import com.sun.tools.javac.util.List; mcimadamore@787: mcimadamore@787: import java.io.File; mcimadamore@787: import java.net.URI; mcimadamore@787: import java.util.Arrays; mcimadamore@787: import java.util.Locale; mcimadamore@787: import javax.tools.Diagnostic; mcimadamore@787: import javax.tools.JavaCompiler; mcimadamore@787: import javax.tools.JavaFileObject; mcimadamore@787: import javax.tools.SimpleJavaFileObject; jjh@892: import javax.tools.StandardJavaFileManager; mcimadamore@787: import javax.tools.ToolProvider; mcimadamore@787: mcimadamore@787: public class T6199075 { mcimadamore@787: mcimadamore@787: int checkCount = 0; mcimadamore@787: int bytecodeCheckCount = 0; mcimadamore@787: mcimadamore@787: enum TypeKind { mcimadamore@787: BYTE("byte", "(byte)1", "[B", 0), mcimadamore@787: CHAR("char", "'c'", "[C", 1), mcimadamore@787: SHORT("short", "(short)1", "[S", 2), mcimadamore@787: INT("int", "1", "[I", 3), mcimadamore@787: LONG("long", "1L", "[J", 4), mcimadamore@787: FLOAT("float", "1.0F", "[F", 5), mcimadamore@787: DOUBLE("double", "1.0D", "[D", 6), mcimadamore@787: BOOLEAN("boolean", "true", "[Z", -1); mcimadamore@787: mcimadamore@787: String typeString; mcimadamore@787: String valueString; mcimadamore@787: String bytecodeString; mcimadamore@787: private int subtypeTag; mcimadamore@787: mcimadamore@787: TypeKind(String typeString, String valueString, String bytecodeString, int subtypeTag) { mcimadamore@787: this.typeString = typeString; mcimadamore@787: this.valueString = valueString; mcimadamore@787: this.bytecodeString = bytecodeString; mcimadamore@787: this.subtypeTag = subtypeTag; mcimadamore@787: } mcimadamore@787: mcimadamore@787: boolean isSubtypeOf(TypeKind that) { mcimadamore@787: switch (this) { mcimadamore@787: case BOOLEAN: mcimadamore@787: return that == BOOLEAN; mcimadamore@787: case BYTE: mcimadamore@787: case CHAR: mcimadamore@787: return this.subtypeTag == that.subtypeTag || mcimadamore@787: this.subtypeTag + 2 <= that.subtypeTag; mcimadamore@787: default: mcimadamore@787: return this.subtypeTag <= that.subtypeTag; mcimadamore@787: } mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: enum ArgumentsArity { mcimadamore@787: ZERO(0), mcimadamore@787: ONE(1), mcimadamore@787: TWO(2), mcimadamore@787: THREE(3); mcimadamore@787: mcimadamore@787: int arity; mcimadamore@787: mcimadamore@787: ArgumentsArity(int arity) { mcimadamore@787: this.arity = arity; mcimadamore@787: } mcimadamore@787: mcimadamore@787: String asExpressionList(TypeKind type) { mcimadamore@787: StringBuilder buf = new StringBuilder(); mcimadamore@787: String sep = ""; mcimadamore@787: for (int i = 0; i < arity; i++) { mcimadamore@787: buf.append(sep); mcimadamore@787: buf.append(type.valueString); mcimadamore@787: sep = ","; mcimadamore@787: } mcimadamore@787: return buf.toString(); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: static class VarargsMethod { mcimadamore@787: TypeKind varargsElement; mcimadamore@787: mcimadamore@787: VarargsMethod(TypeKind varargsElement) { mcimadamore@787: this.varargsElement = varargsElement; mcimadamore@787: } mcimadamore@787: mcimadamore@787: @Override mcimadamore@787: public String toString() { mcimadamore@787: return "void m("+ varargsElement.typeString+ "... args) {}"; mcimadamore@787: } mcimadamore@787: mcimadamore@787: boolean isApplicable(TypeKind actual, ArgumentsArity argsArity) { mcimadamore@787: return argsArity == ArgumentsArity.ZERO || mcimadamore@787: actual.isSubtypeOf(varargsElement); mcimadamore@787: } mcimadamore@787: mcimadamore@787: boolean isMoreSpecificThan(VarargsMethod that) { mcimadamore@787: return varargsElement.isSubtypeOf(that.varargsElement); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: public static void main(String... args) throws Exception { mcimadamore@787: new T6199075().test(); mcimadamore@787: } mcimadamore@787: mcimadamore@787: void test() throws Exception { mcimadamore@787: for (TypeKind formal1 : TypeKind.values()) { mcimadamore@787: VarargsMethod m1 = new VarargsMethod(formal1); mcimadamore@787: for (TypeKind formal2 : TypeKind.values()) { mcimadamore@787: VarargsMethod m2 = new VarargsMethod(formal2); mcimadamore@787: for (TypeKind actual : TypeKind.values()) { mcimadamore@787: for (ArgumentsArity argsArity : ArgumentsArity.values()) { mcimadamore@787: compileAndCheck(m1, m2, actual, argsArity); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: System.out.println("Total checks made: " + checkCount); mcimadamore@787: System.out.println("Bytecode checks made: " + bytecodeCheckCount); mcimadamore@787: } mcimadamore@787: jjh@892: // Create a single file manager and reuse it for each compile to save time. jjh@892: StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); jjh@892: mcimadamore@787: void compileAndCheck(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) throws Exception { mcimadamore@787: final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); mcimadamore@787: JavaSource source = new JavaSource(m1, m2, actual, argsArity); mcimadamore@787: ErrorChecker ec = new ErrorChecker(); jjh@892: JavacTask ct = (JavacTask)tool.getTask(null, fm, ec, mcimadamore@787: null, null, Arrays.asList(source)); mcimadamore@787: ct.generate(); mcimadamore@787: check(source, ec, m1, m2, actual, argsArity); mcimadamore@787: } mcimadamore@787: mcimadamore@787: void check(JavaSource source, ErrorChecker ec, VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) { mcimadamore@787: checkCount++; mcimadamore@787: boolean resolutionError = false; mcimadamore@787: VarargsMethod selectedMethod = null; mcimadamore@787: mcimadamore@787: boolean m1_applicable = m1.isApplicable(actual, argsArity); mcimadamore@787: boolean m2_applicable = m2.isApplicable(actual, argsArity); mcimadamore@787: mcimadamore@787: if (!m1_applicable && !m2_applicable) { mcimadamore@787: resolutionError = true; mcimadamore@787: } else if (m1_applicable && m2_applicable) { mcimadamore@787: //most specific mcimadamore@787: boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); mcimadamore@787: boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); mcimadamore@787: resolutionError = m1_moreSpecific == m2_moreSpecific; mcimadamore@787: selectedMethod = m1_moreSpecific ? m1 : m2; mcimadamore@787: } else { mcimadamore@787: selectedMethod = m1_applicable ? mcimadamore@787: m1 : m2; mcimadamore@787: } mcimadamore@787: mcimadamore@787: if (ec.errorFound != resolutionError) { mcimadamore@787: throw new Error("invalid diagnostics for source:\n" + mcimadamore@787: source.getCharContent(true) + mcimadamore@787: "\nExpected resolution error: " + resolutionError + mcimadamore@787: "\nFound error: " + ec.errorFound + mcimadamore@787: "\nCompiler diagnostics:\n" + ec.printDiags()); mcimadamore@787: } else if (!resolutionError) { mcimadamore@787: verifyBytecode(selectedMethod); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: void verifyBytecode(VarargsMethod selected) { mcimadamore@787: bytecodeCheckCount++; mcimadamore@787: File compiledTest = new File("Test.class"); mcimadamore@787: try { mcimadamore@787: ClassFile cf = ClassFile.read(compiledTest); mcimadamore@787: Method testMethod = null; mcimadamore@787: for (Method m : cf.methods) { mcimadamore@787: if (m.getName(cf.constant_pool).equals("test")) { mcimadamore@787: testMethod = m; mcimadamore@787: break; mcimadamore@787: } mcimadamore@787: } mcimadamore@787: if (testMethod == null) { mcimadamore@787: throw new Error("Test method not found"); mcimadamore@787: } mcimadamore@787: Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code); mcimadamore@787: if (testMethod == null) { mcimadamore@787: throw new Error("Code attribute for test() method not found"); mcimadamore@787: } mcimadamore@787: mcimadamore@787: for (Instruction i : ea.getInstructions()) { mcimadamore@787: if (i.getMnemonic().equals("invokevirtual")) { mcimadamore@787: int cp_entry = i.getUnsignedShort(1); mcimadamore@787: CONSTANT_Methodref_info methRef = mcimadamore@787: (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); mcimadamore@787: String type = methRef.getNameAndTypeInfo().getType(); mcimadamore@787: if (!type.contains(selected.varargsElement.bytecodeString)) { mcimadamore@787: throw new Error("Unexpected type method call: " + type); mcimadamore@787: } mcimadamore@787: break; mcimadamore@787: } mcimadamore@787: } mcimadamore@787: } catch (Exception e) { mcimadamore@787: e.printStackTrace(); mcimadamore@787: throw new Error("error reading " + compiledTest +": " + e); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: static class JavaSource extends SimpleJavaFileObject { mcimadamore@787: mcimadamore@787: static final String source_template = "class Test {\n" + mcimadamore@787: " #V1\n" + mcimadamore@787: " #V2\n" + mcimadamore@787: " void test() { m(#E); }\n" + mcimadamore@787: "}"; mcimadamore@787: mcimadamore@787: String source; mcimadamore@787: mcimadamore@787: public JavaSource(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) { mcimadamore@787: super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); mcimadamore@787: source = source_template.replaceAll("#V1", m1.toString()). mcimadamore@787: replaceAll("#V2", m2.toString()). mcimadamore@787: replaceAll("#E", argsArity.asExpressionList(actual)); mcimadamore@787: } mcimadamore@787: mcimadamore@787: @Override mcimadamore@787: public CharSequence getCharContent(boolean ignoreEncodingErrors) { mcimadamore@787: return source; mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: static class ErrorChecker implements javax.tools.DiagnosticListener { mcimadamore@787: mcimadamore@787: boolean errorFound; mcimadamore@787: List errDiags = List.nil(); mcimadamore@787: mcimadamore@787: public void report(Diagnostic diagnostic) { mcimadamore@787: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { mcimadamore@787: errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault())); mcimadamore@787: errorFound = true; mcimadamore@787: } mcimadamore@787: } mcimadamore@787: mcimadamore@787: String printDiags() { mcimadamore@787: StringBuilder buf = new StringBuilder(); mcimadamore@787: for (String s : errDiags) { mcimadamore@787: buf.append(s); mcimadamore@787: buf.append("\n"); mcimadamore@787: } mcimadamore@787: return buf.toString(); mcimadamore@787: } mcimadamore@787: } mcimadamore@787: }