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