Tue, 08 Jan 2013 13:47:57 +0000
8005167: execution time of combo tests in javac should be improved
Reviewed-by: jjg, jjh
1 /*
2 * Copyright (c) 2011, 2013, 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 * @library ../../lib
29 * @build JavacTestingAbstractThreadedTest
30 * @run main T7042566
31 */
33 import java.io.File;
34 import java.net.URI;
35 import java.util.Arrays;
36 import java.util.Locale;
37 import java.util.concurrent.atomic.AtomicInteger;
38 import javax.tools.Diagnostic;
39 import javax.tools.JavaCompiler;
40 import javax.tools.JavaFileObject;
41 import javax.tools.SimpleJavaFileObject;
42 import javax.tools.ToolProvider;
44 import com.sun.source.util.JavacTask;
45 import com.sun.tools.classfile.Instruction;
46 import com.sun.tools.classfile.Attribute;
47 import com.sun.tools.classfile.ClassFile;
48 import com.sun.tools.classfile.Code_attribute;
49 import com.sun.tools.classfile.ConstantPool.*;
50 import com.sun.tools.classfile.Method;
51 import com.sun.tools.javac.util.List;
53 public class T7042566
54 extends JavacTestingAbstractThreadedTest
55 implements Runnable {
57 VarargsMethod m1;
58 VarargsMethod m2;
59 TypeConfiguration actuals;
61 T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf,
62 TypeConfiguration actuals) {
63 this.m1 = new VarargsMethod(m1_conf);
64 this.m2 = new VarargsMethod(m2_conf);
65 this.actuals = actuals;
66 }
68 @Override
69 public void run() {
70 int id = checkCount.incrementAndGet();
71 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
72 JavaSource source = new JavaSource(id);
73 ErrorChecker ec = new ErrorChecker();
74 JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), ec,
75 null, null, Arrays.asList(source));
76 ct.call();
77 check(source, ec, id);
78 }
80 void check(JavaSource source, ErrorChecker ec, int id) {
81 boolean resolutionError = false;
82 VarargsMethod selectedMethod = null;
84 boolean m1_applicable = m1.isApplicable(actuals);
85 boolean m2_applicable = m2.isApplicable(actuals);
87 if (!m1_applicable && !m2_applicable) {
88 resolutionError = true;
89 } else if (m1_applicable && m2_applicable) {
90 //most specific
91 boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
92 boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
94 resolutionError = m1_moreSpecific == m2_moreSpecific;
95 selectedMethod = m1_moreSpecific ? m1 : m2;
96 } else {
97 selectedMethod = m1_applicable ?
98 m1 : m2;
99 }
101 if (ec.errorFound != resolutionError) {
102 throw new Error("invalid diagnostics for source:\n" +
103 source.getCharContent(true) +
104 "\nExpected resolution error: " + resolutionError +
105 "\nFound error: " + ec.errorFound +
106 "\nCompiler diagnostics:\n" + ec.printDiags());
107 } else if (!resolutionError) {
108 verifyBytecode(selectedMethod, source, id);
109 }
110 }
112 void verifyBytecode(VarargsMethod selected, JavaSource source, int id) {
113 bytecodeCheckCount.incrementAndGet();
114 File compiledTest = new File(String.format("Test%d.class", id));
115 try {
116 ClassFile cf = ClassFile.read(compiledTest);
117 Method testMethod = null;
118 for (Method m : cf.methods) {
119 if (m.getName(cf.constant_pool).equals("test")) {
120 testMethod = m;
121 break;
122 }
123 }
124 if (testMethod == null) {
125 throw new Error("Test method not found");
126 }
127 Code_attribute ea =
128 (Code_attribute)testMethod.attributes.get(Attribute.Code);
129 if (testMethod == null) {
130 throw new Error("Code attribute for test() method not found");
131 }
133 for (Instruction i : ea.getInstructions()) {
134 if (i.getMnemonic().equals("invokevirtual")) {
135 int cp_entry = i.getUnsignedShort(1);
136 CONSTANT_Methodref_info methRef =
137 (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
138 String type = methRef.getNameAndTypeInfo().getType();
139 String sig = selected.parameterTypes.bytecodeSigStr;
140 if (!type.contains(sig)) {
141 throw new Error("Unexpected type method call: " +
142 type + "" +
143 "\nfound: " + sig +
144 "\n" + source.getCharContent(true));
145 }
146 break;
147 }
148 }
149 } catch (Exception e) {
150 e.printStackTrace();
151 throw new Error("error reading " + compiledTest +": " + e);
152 }
153 }
155 class JavaSource extends SimpleJavaFileObject {
157 static final String source_template = "class Test#ID {\n" +
158 " #V1\n" +
159 " #V2\n" +
160 " void test() { m(#E); }\n" +
161 "}";
163 String source;
165 public JavaSource(int id) {
166 super(URI.create(String.format("myfo:/Test%d.java", id)),
167 JavaFileObject.Kind.SOURCE);
168 source = source_template.replaceAll("#V1", m1.toString())
169 .replaceAll("#V2", m2.toString())
170 .replaceAll("#E", actuals.expressionListStr)
171 .replaceAll("#ID", String.valueOf(id));
172 }
174 @Override
175 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
176 return source;
177 }
178 }
180 public static void main(String... args) throws Exception {
181 for (TypeConfiguration tconf1 : TypeConfiguration.values()) {
182 for (TypeConfiguration tconf2 : TypeConfiguration.values()) {
183 for (TypeConfiguration tconf3 : TypeConfiguration.values()) {
184 pool.execute(new T7042566(tconf1, tconf2, tconf3));
185 }
186 }
187 }
189 outWriter.println("Bytecode checks made: " + bytecodeCheckCount.get());
190 checkAfterExec();
191 }
193 enum TypeKind {
194 OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
195 STRING("String", "(String)null", "Ljava/lang/String;");
197 String typeString;
198 String valueString;
199 String bytecodeString;
201 TypeKind(String typeString, String valueString, String bytecodeString) {
202 this.typeString = typeString;
203 this.valueString = valueString;
204 this.bytecodeString = bytecodeString;
205 }
207 boolean isSubtypeOf(TypeKind that) {
208 return that == OBJECT ||
209 (that == STRING && this == STRING);
210 }
211 }
213 enum TypeConfiguration {
214 A(TypeKind.OBJECT),
215 B(TypeKind.STRING),
216 AA(TypeKind.OBJECT, TypeKind.OBJECT),
217 AB(TypeKind.OBJECT, TypeKind.STRING),
218 BA(TypeKind.STRING, TypeKind.OBJECT),
219 BB(TypeKind.STRING, TypeKind.STRING),
220 AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT),
221 AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING),
222 ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT),
223 ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING),
224 BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT),
225 BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING),
226 BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT),
227 BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING);
229 List<TypeKind> typeKindList;
230 String expressionListStr;
231 String parameterListStr;
232 String bytecodeSigStr;
234 private TypeConfiguration(TypeKind... typeKindList) {
235 this.typeKindList = List.from(typeKindList);
236 expressionListStr = asExpressionList();
237 parameterListStr = asParameterList();
238 bytecodeSigStr = asBytecodeString();
239 }
241 private String asExpressionList() {
242 StringBuilder buf = new StringBuilder();
243 String sep = "";
244 for (TypeKind tk : typeKindList) {
245 buf.append(sep);
246 buf.append(tk.valueString);
247 sep = ",";
248 }
249 return buf.toString();
250 }
252 private String asParameterList() {
253 StringBuilder buf = new StringBuilder();
254 String sep = "";
255 int count = 0;
256 for (TypeKind arg : typeKindList) {
257 buf.append(sep);
258 buf.append(arg.typeString);
259 if (count == (typeKindList.size() - 1)) {
260 buf.append("...");
261 }
262 buf.append(" ");
263 buf.append("arg" + count++);
264 sep = ",";
265 }
266 return buf.toString();
267 }
269 private String asBytecodeString() {
270 StringBuilder buf = new StringBuilder();
271 int count = 0;
272 for (TypeKind arg : typeKindList) {
273 if (count == (typeKindList.size() - 1)) {
274 buf.append("[");
275 }
276 buf.append(arg.bytecodeString);
277 count++;
278 }
279 return buf.toString();
280 }
281 }
283 static class VarargsMethod {
284 TypeConfiguration parameterTypes;
286 public VarargsMethod(TypeConfiguration parameterTypes) {
287 this.parameterTypes = parameterTypes;
288 }
290 @Override
291 public String toString() {
292 return "void m( " + parameterTypes.parameterListStr + ") {}";
293 }
295 boolean isApplicable(TypeConfiguration that) {
296 List<TypeKind> actuals = that.typeKindList;
297 List<TypeKind> formals = parameterTypes.typeKindList;
298 if ((actuals.size() - formals.size()) < -1)
299 return false; //not enough args
300 for (TypeKind actual : actuals) {
301 if (!actual.isSubtypeOf(formals.head))
302 return false; //type mismatch
303 formals = formals.tail.isEmpty() ?
304 formals :
305 formals.tail;
306 }
307 return true;
308 }
310 boolean isMoreSpecificThan(VarargsMethod that) {
311 List<TypeKind> actuals = parameterTypes.typeKindList;
312 List<TypeKind> formals = that.parameterTypes.typeKindList;
313 int checks = 0;
314 int expectedCheck = Math.max(actuals.size(), formals.size());
315 while (checks < expectedCheck) {
316 if (!actuals.head.isSubtypeOf(formals.head))
317 return false; //type mismatch
318 formals = formals.tail.isEmpty() ?
319 formals :
320 formals.tail;
321 actuals = actuals.tail.isEmpty() ?
322 actuals :
323 actuals.tail;
324 checks++;
325 }
326 return true;
327 }
328 }
330 static class ErrorChecker
331 implements javax.tools.DiagnosticListener<JavaFileObject> {
333 boolean errorFound;
334 List<String> errDiags = List.nil();
336 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
337 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
338 errDiags = errDiags
339 .append(diagnostic.getMessage(Locale.getDefault()));
340 errorFound = true;
341 }
342 }
344 String printDiags() {
345 StringBuilder buf = new StringBuilder();
346 for (String s : errDiags) {
347 buf.append(s);
348 buf.append("\n");
349 }
350 return buf.toString();
351 }
352 }
354 //number of bytecode checks made while running combo tests
355 static AtomicInteger bytecodeCheckCount = new AtomicInteger();
357 }