1 /* 2 * Copyright (c) 2010, 2015, 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 6199075 27 * 28 * @summary Unambiguous varargs method calls flagged as ambiguous 29 * @author mcimadamore 30 * 31 * @modules jdk.compiler/com.sun.tools.classfile 32 * jdk.compiler/com.sun.tools.javac.api 33 * jdk.compiler/com.sun.tools.javac.file 34 * jdk.compiler/com.sun.tools.javac.util 35 */ 36 37 import com.sun.source.util.JavacTask; 38 import com.sun.tools.classfile.Instruction; 39 import com.sun.tools.classfile.Attribute; 40 import com.sun.tools.classfile.ClassFile; 41 import com.sun.tools.classfile.Code_attribute; 42 import com.sun.tools.classfile.ConstantPool.*; 43 import com.sun.tools.classfile.Method; 44 import com.sun.tools.javac.api.JavacTool; 45 import com.sun.tools.javac.util.List; 46 47 import java.io.File; 48 import java.net.URI; 49 import java.util.Arrays; 50 import java.util.Locale; 51 import javax.tools.Diagnostic; 52 import javax.tools.JavaCompiler; 53 import javax.tools.JavaFileObject; 54 import javax.tools.SimpleJavaFileObject; 55 import javax.tools.StandardJavaFileManager; 56 import javax.tools.ToolProvider; 57 58 public class T6199075 { 59 60 int checkCount = 0; 61 int bytecodeCheckCount = 0; 62 63 enum TypeKind { 64 BYTE("byte", "(byte)1", "[B", 0), 65 CHAR("char", "'c'", "[C", 1), 66 SHORT("short", "(short)1", "[S", 2), 67 INT("int", "1", "[I", 3), 68 LONG("long", "1L", "[J", 4), 69 FLOAT("float", "1.0F", "[F", 5), 70 DOUBLE("double", "1.0D", "[D", 6), 71 BOOLEAN("boolean", "true", "[Z", -1); 72 73 String typeString; 74 String valueString; 75 String bytecodeString; 76 private int subtypeTag; 77 78 TypeKind(String typeString, String valueString, String bytecodeString, int subtypeTag) { 79 this.typeString = typeString; 80 this.valueString = valueString; 81 this.bytecodeString = bytecodeString; 82 this.subtypeTag = subtypeTag; 83 } 84 85 boolean isSubtypeOf(TypeKind that) { 86 switch (this) { 87 case BOOLEAN: 88 return that == BOOLEAN; 89 case BYTE: 90 case CHAR: 91 return this.subtypeTag == that.subtypeTag || 92 this.subtypeTag + 2 <= that.subtypeTag; 93 default: 94 return this.subtypeTag <= that.subtypeTag; 95 } 96 } 97 } 98 99 enum ArgumentsArity { 100 ZERO(0), 101 ONE(1), 102 TWO(2), 103 THREE(3); 104 105 int arity; 106 107 ArgumentsArity(int arity) { 108 this.arity = arity; 109 } 110 111 String asExpressionList(TypeKind type) { 112 StringBuilder buf = new StringBuilder(); 113 String sep = ""; 114 for (int i = 0; i < arity; i++) { 115 buf.append(sep); 116 buf.append(type.valueString); 117 sep = ","; 118 } 119 return buf.toString(); 120 } 121 } 122 123 static class VarargsMethod { 124 TypeKind varargsElement; 125 126 VarargsMethod(TypeKind varargsElement) { 127 this.varargsElement = varargsElement; 128 } 129 130 @Override 131 public String toString() { 132 return "void m("+ varargsElement.typeString+ "... args) {}"; 133 } 134 135 boolean isApplicable(TypeKind actual, ArgumentsArity argsArity) { 136 return argsArity == ArgumentsArity.ZERO || 137 actual.isSubtypeOf(varargsElement); 138 } 139 140 boolean isMoreSpecificThan(VarargsMethod that) { 141 return varargsElement.isSubtypeOf(that.varargsElement); 142 } 143 } 144 145 public static void main(String... args) throws Exception { 146 new T6199075().test(); 147 } 148 149 void test() throws Exception { 150 try { 151 for (TypeKind formal1 : TypeKind.values()) { 152 VarargsMethod m1 = new VarargsMethod(formal1); 153 for (TypeKind formal2 : TypeKind.values()) { 154 VarargsMethod m2 = new VarargsMethod(formal2); 155 for (TypeKind actual : TypeKind.values()) { 156 for (ArgumentsArity argsArity : ArgumentsArity.values()) { 157 compileAndCheck(m1, m2, actual, argsArity); 158 } 159 } 160 } 161 } 162 163 System.out.println("Total checks made: " + checkCount); 164 System.out.println("Bytecode checks made: " + bytecodeCheckCount); 165 } finally { 166 fm.close(); 167 } 168 } 169 170 // Create a single file manager and reuse it for each compile to save time. 171 StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); 172 173 void compileAndCheck(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) throws Exception { 174 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 175 JavaSource source = new JavaSource(m1, m2, actual, argsArity); 176 ErrorChecker ec = new ErrorChecker(); 177 JavacTask ct = (JavacTask)tool.getTask(null, fm, ec, 178 null, null, Arrays.asList(source)); 179 ct.generate(); 180 check(source, ec, m1, m2, actual, argsArity); 181 } 182 183 void check(JavaSource source, ErrorChecker ec, VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) { 184 checkCount++; 185 boolean resolutionError = false; 186 VarargsMethod selectedMethod = null; 187 188 boolean m1_applicable = m1.isApplicable(actual, argsArity); 189 boolean m2_applicable = m2.isApplicable(actual, argsArity); 190 191 if (!m1_applicable && !m2_applicable) { 192 resolutionError = true; 193 } else if (m1_applicable && m2_applicable) { 194 //most specific 195 boolean m1_moreSpecific = m1.isMoreSpecificThan(m2); 196 boolean m2_moreSpecific = m2.isMoreSpecificThan(m1); 197 resolutionError = m1_moreSpecific == m2_moreSpecific; 198 selectedMethod = m1_moreSpecific ? m1 : m2; 199 } else { 200 selectedMethod = m1_applicable ? 201 m1 : m2; 202 } 203 204 if (ec.errorFound != resolutionError) { 205 throw new Error("invalid diagnostics for source:\n" + 206 source.getCharContent(true) + 207 "\nExpected resolution error: " + resolutionError + 208 "\nFound error: " + ec.errorFound + 209 "\nCompiler diagnostics:\n" + ec.printDiags()); 210 } else if (!resolutionError) { 211 verifyBytecode(selectedMethod); 212 } 213 } 214 215 void verifyBytecode(VarargsMethod selected) { 216 bytecodeCheckCount++; 217 File compiledTest = new File("Test.class"); 218 try { 219 ClassFile cf = ClassFile.read(compiledTest); 220 Method testMethod = null; 221 for (Method m : cf.methods) { 222 if (m.getName(cf.constant_pool).equals("test")) { 223 testMethod = m; 224 break; 225 } 226 } 227 if (testMethod == null) { 228 throw new Error("Test method not found"); 229 } 230 Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code); 231 if (testMethod == null) { 232 throw new Error("Code attribute for test() method not found"); 233 } 234 235 for (Instruction i : ea.getInstructions()) { 236 if (i.getMnemonic().equals("invokevirtual")) { 237 int cp_entry = i.getUnsignedShort(1); 238 CONSTANT_Methodref_info methRef = 239 (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry); 240 String type = methRef.getNameAndTypeInfo().getType(); 241 if (!type.contains(selected.varargsElement.bytecodeString)) { 242 throw new Error("Unexpected type method call: " + type); 243 } 244 break; 245 } 246 } 247 } catch (Exception e) { 248 e.printStackTrace(); 249 throw new Error("error reading " + compiledTest +": " + e); 250 } 251 } 252 253 static class JavaSource extends SimpleJavaFileObject { 254 255 static final String source_template = "class Test {\n" + 256 " #V1\n" + 257 " #V2\n" + 258 " void test() { m(#E); }\n" + 259 "}"; 260 261 String source; 262 263 public JavaSource(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) { 264 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 265 source = source_template.replaceAll("#V1", m1.toString()). 266 replaceAll("#V2", m2.toString()). 267 replaceAll("#E", argsArity.asExpressionList(actual)); 268 } 269 270 @Override 271 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 272 return source; 273 } 274 } 275 276 static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 277 278 boolean errorFound; 279 List<String> errDiags = List.nil(); 280 281 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 282 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 283 errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault())); 284 errorFound = true; 285 } 286 } 287 288 String printDiags() { 289 StringBuilder buf = new StringBuilder(); 290 for (String s : errDiags) { 291 buf.append(s); 292 buf.append("\n"); 293 } 294 return buf.toString(); 295 } 296 } 297 }