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