test/tools/javac/varargs/7042566/T7042566.java

Wed, 11 May 2011 13:10:57 +0200

author
mcimadamore
date
Wed, 11 May 2011 13:10:57 +0200
changeset 1006
a2d422d480cb
child 1482
954541f13717
permissions
-rw-r--r--

7042566: Regression: new ambiguity between varargs method
Summary: Erroneous ambiguity error when choosing most specific varargs method
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2011, 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  */
    24 /*
    25  * @test
    26  * @bug 7042566
    27  * @summary Unambiguous varargs method calls flagged as ambiguous
    28  */
    30 import com.sun.source.util.JavacTask;
    31 import com.sun.tools.classfile.Instruction;
    32 import com.sun.tools.classfile.Attribute;
    33 import com.sun.tools.classfile.ClassFile;
    34 import com.sun.tools.classfile.Code_attribute;
    35 import com.sun.tools.classfile.ConstantPool.*;
    36 import com.sun.tools.classfile.Method;
    37 import com.sun.tools.javac.api.JavacTool;
    38 import com.sun.tools.javac.util.List;
    40 import java.io.File;
    41 import java.net.URI;
    42 import java.util.Arrays;
    43 import java.util.Locale;
    44 import javax.tools.Diagnostic;
    45 import javax.tools.JavaCompiler;
    46 import javax.tools.JavaFileObject;
    47 import javax.tools.SimpleJavaFileObject;
    48 import javax.tools.StandardJavaFileManager;
    49 import javax.tools.ToolProvider;
    51 public class T7042566 {
    53     VarargsMethod m1;
    54     VarargsMethod m2;
    55     TypeConfiguration actuals;
    57     T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf, TypeConfiguration actuals) {
    58         this.m1 = new VarargsMethod(m1_conf);
    59         this.m2 = new VarargsMethod(m2_conf);
    60         this.actuals = actuals;
    61     }
    63     void compileAndCheck() throws Exception {
    64         final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
    65         JavaSource source = new JavaSource();
    66         ErrorChecker ec = new ErrorChecker();
    67         JavacTask ct = (JavacTask)tool.getTask(null, fm, ec,
    68                 null, null, Arrays.asList(source));
    69         ct.call();
    70         check(source, ec);
    71     }
    73     void check(JavaSource source, ErrorChecker ec) {
    74         checkCount++;
    75         boolean resolutionError = false;
    76         VarargsMethod selectedMethod = null;
    78         boolean m1_applicable = m1.isApplicable(actuals);
    79         boolean m2_applicable = m2.isApplicable(actuals);
    81         if (!m1_applicable && !m2_applicable) {
    82             resolutionError = true;
    83         } else if (m1_applicable && m2_applicable) {
    84             //most specific
    85             boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
    86             boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
    88             resolutionError = m1_moreSpecific == m2_moreSpecific;
    89             selectedMethod = m1_moreSpecific ? m1 : m2;
    90         } else {
    91             selectedMethod = m1_applicable ?
    92                 m1 : m2;
    93         }
    95         if (ec.errorFound != resolutionError) {
    96             throw new Error("invalid diagnostics for source:\n" +
    97                     source.getCharContent(true) +
    98                     "\nExpected resolution error: " + resolutionError +
    99                     "\nFound error: " + ec.errorFound +
   100                     "\nCompiler diagnostics:\n" + ec.printDiags());
   101         } else if (!resolutionError) {
   102             verifyBytecode(selectedMethod, source);
   103         }
   104     }
   106     void verifyBytecode(VarargsMethod selected, JavaSource source) {
   107         bytecodeCheckCount++;
   108         File compiledTest = new File("Test.class");
   109         try {
   110             ClassFile cf = ClassFile.read(compiledTest);
   111             Method testMethod = null;
   112             for (Method m : cf.methods) {
   113                 if (m.getName(cf.constant_pool).equals("test")) {
   114                     testMethod = m;
   115                     break;
   116                 }
   117             }
   118             if (testMethod == null) {
   119                 throw new Error("Test method not found");
   120             }
   121             Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
   122             if (testMethod == null) {
   123                 throw new Error("Code attribute for test() method not found");
   124             }
   126             for (Instruction i : ea.getInstructions()) {
   127                 if (i.getMnemonic().equals("invokevirtual")) {
   128                     int cp_entry = i.getUnsignedShort(1);
   129                     CONSTANT_Methodref_info methRef =
   130                             (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
   131                     String type = methRef.getNameAndTypeInfo().getType();
   132                     String sig = selected.parameterTypes.bytecodeSigStr;
   133                     if (!type.contains(sig)) {
   134                         throw new Error("Unexpected type method call: " + type + "" +
   135                                         "\nfound: " + sig +
   136                                         "\n" + source.getCharContent(true));
   137                     }
   138                     break;
   139                 }
   140             }
   141         } catch (Exception e) {
   142             e.printStackTrace();
   143             throw new Error("error reading " + compiledTest +": " + e);
   144         }
   145     }
   147     class JavaSource extends SimpleJavaFileObject {
   149         static final String source_template = "class Test {\n" +
   150                 "   #V1\n" +
   151                 "   #V2\n" +
   152                 "   void test() { m(#E); }\n" +
   153                 "}";
   155         String source;
   157         public JavaSource() {
   158             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   159             source = source_template.replaceAll("#V1", m1.toString()).
   160                     replaceAll("#V2", m2.toString()).
   161                     replaceAll("#E", actuals.expressionListStr);
   162         }
   164         @Override
   165         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   166             return source;
   167         }
   168     }
   170     /** global decls ***/
   172     // Create a single file manager and reuse it for each compile to save time.
   173     static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
   175     //statistics
   176     static int checkCount = 0;
   177     static int bytecodeCheckCount = 0;
   179     public static void main(String... args) throws Exception {
   180         for (TypeConfiguration tconf1 : TypeConfiguration.values()) {
   181             for (TypeConfiguration tconf2 : TypeConfiguration.values()) {
   182                 for (TypeConfiguration tconf3 : TypeConfiguration.values()) {
   183                     new T7042566(tconf1, tconf2, tconf3).compileAndCheck();
   184                 }
   185             }
   186         }
   188         System.out.println("Total checks made: " + checkCount);
   189         System.out.println("Bytecode checks made: " + bytecodeCheckCount);
   190     }
   192     enum TypeKind {
   193         OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
   194         STRING("String", "(String)null", "Ljava/lang/String;");
   196         String typeString;
   197         String valueString;
   198         String bytecodeString;
   200         TypeKind(String typeString, String valueString, String bytecodeString) {
   201             this.typeString = typeString;
   202             this.valueString = valueString;
   203             this.bytecodeString = bytecodeString;
   204         }
   206         boolean isSubtypeOf(TypeKind that) {
   207             return that == OBJECT ||
   208                     (that == STRING && this == STRING);
   209         }
   210     }
   212     enum TypeConfiguration {
   213         A(TypeKind.OBJECT),
   214         B(TypeKind.STRING),
   215         AA(TypeKind.OBJECT, TypeKind.OBJECT),
   216         AB(TypeKind.OBJECT, TypeKind.STRING),
   217         BA(TypeKind.STRING, TypeKind.OBJECT),
   218         BB(TypeKind.STRING, TypeKind.STRING),
   219         AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT),
   220         AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING),
   221         ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT),
   222         ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING),
   223         BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT),
   224         BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING),
   225         BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT),
   226         BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING);
   228         List<TypeKind> typeKindList;
   229         String expressionListStr;
   230         String parameterListStr;
   231         String bytecodeSigStr;
   233         private TypeConfiguration(TypeKind... typeKindList) {
   234             this.typeKindList = List.from(typeKindList);
   235             expressionListStr = asExpressionList();
   236             parameterListStr = asParameterList();
   237             bytecodeSigStr = asBytecodeString();
   238         }
   240         private String asExpressionList() {
   241             StringBuilder buf = new StringBuilder();
   242             String sep = "";
   243             for (TypeKind tk : typeKindList) {
   244                 buf.append(sep);
   245                 buf.append(tk.valueString);
   246                 sep = ",";
   247             }
   248             return buf.toString();
   249         }
   251         private String asParameterList() {
   252             StringBuilder buf = new StringBuilder();
   253             String sep = "";
   254             int count = 0;
   255             for (TypeKind arg : typeKindList) {
   256                 buf.append(sep);
   257                 buf.append(arg.typeString);
   258                 if (count == (typeKindList.size() - 1)) {
   259                     buf.append("...");
   260                 }
   261                 buf.append(" ");
   262                 buf.append("arg" + count++);
   263                 sep = ",";
   264             }
   265             return buf.toString();
   266         }
   268         private String asBytecodeString() {
   269             StringBuilder buf = new StringBuilder();
   270             int count = 0;
   271             for (TypeKind arg : typeKindList) {
   272                 if (count == (typeKindList.size() - 1)) {
   273                     buf.append("[");
   274                 }
   275                 buf.append(arg.bytecodeString);
   276                 count++;
   277             }
   278             return buf.toString();
   279         }
   280     }
   282     static class VarargsMethod {
   283         TypeConfiguration parameterTypes;
   285         public VarargsMethod(TypeConfiguration parameterTypes) {
   286             this.parameterTypes = parameterTypes;
   287         }
   289         @Override
   290         public String toString() {
   291             return "void m( " + parameterTypes.parameterListStr + ") {}";
   292         }
   294         boolean isApplicable(TypeConfiguration that) {
   295             List<TypeKind> actuals = that.typeKindList;
   296             List<TypeKind> formals = parameterTypes.typeKindList;
   297             if ((actuals.size() - formals.size()) < -1)
   298                 return false; //not enough args
   299             for (TypeKind actual : actuals) {
   300                 if (!actual.isSubtypeOf(formals.head))
   301                     return false; //type mismatch
   302                 formals = formals.tail.isEmpty() ?
   303                     formals :
   304                     formals.tail;
   305             }
   306             return true;
   307         }
   309         boolean isMoreSpecificThan(VarargsMethod that) {
   310             List<TypeKind> actuals = parameterTypes.typeKindList;
   311             List<TypeKind> formals = that.parameterTypes.typeKindList;
   312             int checks = 0;
   313             int expectedCheck = Math.max(actuals.size(), formals.size());
   314             while (checks < expectedCheck) {
   315                 if (!actuals.head.isSubtypeOf(formals.head))
   316                     return false; //type mismatch
   317                 formals = formals.tail.isEmpty() ?
   318                     formals :
   319                     formals.tail;
   320                 actuals = actuals.tail.isEmpty() ?
   321                     actuals :
   322                     actuals.tail;
   323                 checks++;
   324             }
   325             return true;
   326         }
   327     }
   329     static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
   331         boolean errorFound;
   332         List<String> errDiags = List.nil();
   334         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   335             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   336                 errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault()));
   337                 errorFound = true;
   338             }
   339         }
   341         String printDiags() {
   342             StringBuilder buf = new StringBuilder();
   343             for (String s : errDiags) {
   344                 buf.append(s);
   345                 buf.append("\n");
   346             }
   347             return buf.toString();
   348         }
   349     }
   350 }

mercurial