langtools/test/tools/javac/varargs/7042566/T7042566.java
changeset 9720 bc06a797f393
child 15040 99fd9483d3f0
equal deleted inserted replaced
9719:29adbf9e4cae 9720:bc06a797f393
       
     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  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 7042566
       
    27  * @summary Unambiguous varargs method calls flagged as ambiguous
       
    28  */
       
    29 
       
    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;
       
    39 
       
    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;
       
    50 
       
    51 public class T7042566 {
       
    52 
       
    53     VarargsMethod m1;
       
    54     VarargsMethod m2;
       
    55     TypeConfiguration actuals;
       
    56 
       
    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     }
       
    62 
       
    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     }
       
    72 
       
    73     void check(JavaSource source, ErrorChecker ec) {
       
    74         checkCount++;
       
    75         boolean resolutionError = false;
       
    76         VarargsMethod selectedMethod = null;
       
    77 
       
    78         boolean m1_applicable = m1.isApplicable(actuals);
       
    79         boolean m2_applicable = m2.isApplicable(actuals);
       
    80 
       
    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);
       
    87 
       
    88             resolutionError = m1_moreSpecific == m2_moreSpecific;
       
    89             selectedMethod = m1_moreSpecific ? m1 : m2;
       
    90         } else {
       
    91             selectedMethod = m1_applicable ?
       
    92                 m1 : m2;
       
    93         }
       
    94 
       
    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     }
       
   105 
       
   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             }
       
   125 
       
   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     }
       
   146 
       
   147     class JavaSource extends SimpleJavaFileObject {
       
   148 
       
   149         static final String source_template = "class Test {\n" +
       
   150                 "   #V1\n" +
       
   151                 "   #V2\n" +
       
   152                 "   void test() { m(#E); }\n" +
       
   153                 "}";
       
   154 
       
   155         String source;
       
   156 
       
   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         }
       
   163 
       
   164         @Override
       
   165         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
       
   166             return source;
       
   167         }
       
   168     }
       
   169 
       
   170     /** global decls ***/
       
   171 
       
   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);
       
   174 
       
   175     //statistics
       
   176     static int checkCount = 0;
       
   177     static int bytecodeCheckCount = 0;
       
   178 
       
   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         }
       
   187 
       
   188         System.out.println("Total checks made: " + checkCount);
       
   189         System.out.println("Bytecode checks made: " + bytecodeCheckCount);
       
   190     }
       
   191 
       
   192     enum TypeKind {
       
   193         OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
       
   194         STRING("String", "(String)null", "Ljava/lang/String;");
       
   195 
       
   196         String typeString;
       
   197         String valueString;
       
   198         String bytecodeString;
       
   199 
       
   200         TypeKind(String typeString, String valueString, String bytecodeString) {
       
   201             this.typeString = typeString;
       
   202             this.valueString = valueString;
       
   203             this.bytecodeString = bytecodeString;
       
   204         }
       
   205 
       
   206         boolean isSubtypeOf(TypeKind that) {
       
   207             return that == OBJECT ||
       
   208                     (that == STRING && this == STRING);
       
   209         }
       
   210     }
       
   211 
       
   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);
       
   227 
       
   228         List<TypeKind> typeKindList;
       
   229         String expressionListStr;
       
   230         String parameterListStr;
       
   231         String bytecodeSigStr;
       
   232 
       
   233         private TypeConfiguration(TypeKind... typeKindList) {
       
   234             this.typeKindList = List.from(typeKindList);
       
   235             expressionListStr = asExpressionList();
       
   236             parameterListStr = asParameterList();
       
   237             bytecodeSigStr = asBytecodeString();
       
   238         }
       
   239 
       
   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         }
       
   250 
       
   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         }
       
   267 
       
   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     }
       
   281 
       
   282     static class VarargsMethod {
       
   283         TypeConfiguration parameterTypes;
       
   284 
       
   285         public VarargsMethod(TypeConfiguration parameterTypes) {
       
   286             this.parameterTypes = parameterTypes;
       
   287         }
       
   288 
       
   289         @Override
       
   290         public String toString() {
       
   291             return "void m( " + parameterTypes.parameterListStr + ") {}";
       
   292         }
       
   293 
       
   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         }
       
   308 
       
   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     }
       
   328 
       
   329     static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
       
   330 
       
   331         boolean errorFound;
       
   332         List<String> errDiags = List.nil();
       
   333 
       
   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         }
       
   340 
       
   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 }