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