Mon, 01 Jul 2013 14:57:03 +0100
7034798: Ambiguity error for abstract method call is too eager
Summary: Javac should wait and see if ambiguous methods can be reconciled at the end of an overload resolution round
Reviewed-by: jjg, vromero
jjg@1569 | 1 | /* |
darcy@1646 | 2 | * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. |
jjg@1569 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@1569 | 4 | * |
jjg@1569 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@1569 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@1569 | 7 | * published by the Free Software Foundation. |
jjg@1569 | 8 | * |
jjg@1569 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@1569 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@1569 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@1569 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@1569 | 13 | * accompanied this code). |
jjg@1569 | 14 | * |
jjg@1569 | 15 | * You should have received a copy of the GNU General Public License version |
jjg@1569 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@1569 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@1569 | 18 | * |
jjg@1569 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jjg@1569 | 20 | * or visit www.oracle.com if you need additional information or have any |
jjg@1569 | 21 | * questions. |
jjg@1569 | 22 | */ |
jjg@1569 | 23 | |
jjg@1569 | 24 | /* |
jjg@1569 | 25 | * @test |
jjg@1569 | 26 | * @bug 8004182 |
jjg@1569 | 27 | * @summary Add support for profiles in javac |
jjg@1569 | 28 | */ |
jjg@1569 | 29 | |
jjg@1569 | 30 | import java.io.PrintWriter; |
jjg@1569 | 31 | import java.io.StringWriter; |
jjg@1569 | 32 | import java.lang.annotation.Annotation; |
jjg@1569 | 33 | import java.lang.annotation.Retention; |
jjg@1569 | 34 | import java.lang.annotation.RetentionPolicy; |
jjg@1569 | 35 | import java.lang.reflect.InvocationTargetException; |
jjg@1569 | 36 | import java.lang.reflect.Method; |
jjg@1569 | 37 | import java.net.URI; |
jjg@1569 | 38 | import java.util.ArrayList; |
jjg@1569 | 39 | import java.util.Arrays; |
jjg@1569 | 40 | import java.util.Collections; |
jjg@1569 | 41 | import java.util.EnumMap; |
jjg@1569 | 42 | import java.util.List; |
jjg@1569 | 43 | import java.util.Map; |
jjg@1569 | 44 | |
jjg@1569 | 45 | import javax.tools.Diagnostic; |
jjg@1569 | 46 | import javax.tools.DiagnosticCollector; |
jjg@1569 | 47 | import javax.tools.JavaCompiler; |
jjg@1569 | 48 | import javax.tools.JavaFileObject; |
jjg@1569 | 49 | import javax.tools.SimpleJavaFileObject; |
jjg@1569 | 50 | import javax.tools.StandardJavaFileManager; |
jjg@1569 | 51 | |
jjg@1569 | 52 | import com.sun.source.util.JavacTask; |
jjg@1569 | 53 | import com.sun.tools.javac.api.JavacTool; |
jjg@1569 | 54 | import com.sun.tools.javac.jvm.Profile; |
jjg@1569 | 55 | import com.sun.tools.javac.jvm.Target; |
jjg@1569 | 56 | |
jjg@1569 | 57 | |
jjg@1569 | 58 | public class ProfileOptionTest { |
jjg@1569 | 59 | public static void main(String... args) throws Exception { |
jjg@1569 | 60 | new ProfileOptionTest().run(); |
jjg@1569 | 61 | } |
jjg@1569 | 62 | |
jjg@1569 | 63 | private final JavaCompiler javac = JavacTool.create(); |
jjg@1569 | 64 | private final StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null); |
jjg@1569 | 65 | |
jjg@1569 | 66 | |
jjg@1569 | 67 | // ---------- Test cases, invoked reflectively via run. ---------- |
jjg@1569 | 68 | |
jjg@1569 | 69 | @Test |
jjg@1569 | 70 | void testInvalidProfile_CommandLine() throws Exception { |
jjg@1569 | 71 | JavaFileObject fo = new StringJavaFileObject("Test.java", "class Test { }"); |
jjg@1569 | 72 | String badName = "foo"; |
jjg@1569 | 73 | List<String> opts = Arrays.asList("-profile", badName); |
jjg@1569 | 74 | StringWriter sw = new StringWriter(); |
jjg@1569 | 75 | try { |
jjg@1569 | 76 | JavacTask task = (JavacTask) javac.getTask(sw, fm, null, opts, null, |
jjg@1569 | 77 | Arrays.asList(fo)); |
jjg@1569 | 78 | throw new Exception("expected exception not thrown"); |
jjg@1569 | 79 | } catch (IllegalArgumentException e) { |
jjg@1569 | 80 | // expected |
jjg@1569 | 81 | } |
jjg@1569 | 82 | } |
jjg@1569 | 83 | |
jjg@1569 | 84 | @Test |
jjg@1569 | 85 | void testInvalidProfile_API() throws Exception { |
jjg@1569 | 86 | String badName = "foo"; |
jjg@1569 | 87 | String[] opts = { "-profile", badName }; |
jjg@1569 | 88 | StringWriter sw = new StringWriter(); |
jjg@1569 | 89 | PrintWriter pw = new PrintWriter(sw); |
jjg@1569 | 90 | int rc = com.sun.tools.javac.Main.compile(opts, pw); |
jjg@1569 | 91 | |
jjg@1569 | 92 | // sadly, command line errors are not (yet?) reported to |
jjg@1569 | 93 | // the diag listener |
jjg@1569 | 94 | String out = sw.toString(); |
jjg@1569 | 95 | if (!out.isEmpty()) |
jjg@1569 | 96 | System.err.println(out.trim()); |
jjg@1569 | 97 | |
jjg@1569 | 98 | if (!out.contains("invalid profile: " + badName)) { |
jjg@1569 | 99 | error("expected message not found"); |
jjg@1569 | 100 | } |
jjg@1569 | 101 | } |
jjg@1569 | 102 | |
jjg@1569 | 103 | @Test |
jjg@1569 | 104 | void testTargetProfileCombinations() throws Exception { |
jjg@1569 | 105 | JavaFileObject fo = new StringJavaFileObject("Test.java", "class Test { }"); |
jjg@1569 | 106 | for (Target t: Target.values()) { |
jjg@1569 | 107 | switch (t) { |
jjg@1569 | 108 | case JDK1_1: case JDK1_2: // no equivalent -source |
jjg@1569 | 109 | continue; |
jjg@1569 | 110 | } |
jjg@1569 | 111 | |
jjg@1569 | 112 | for (Profile p: Profile.values()) { |
jjg@1569 | 113 | List<String> opts = new ArrayList<String>(); |
jjg@1569 | 114 | opts.addAll(Arrays.asList("-source", t.name, "-target", t.name)); |
jjg@1569 | 115 | opts.add("-Xlint:-options"); // dont warn about no -bootclasspath |
jjg@1569 | 116 | if (p != Profile.DEFAULT) |
jjg@1569 | 117 | opts.addAll(Arrays.asList("-profile", p.name)); |
jjg@1569 | 118 | StringWriter sw = new StringWriter(); |
jjg@1569 | 119 | JavacTask task = (JavacTask) javac.getTask(sw, fm, null, opts, null, |
jjg@1569 | 120 | Arrays.asList(fo)); |
jjg@1569 | 121 | task.analyze(); |
jjg@1569 | 122 | |
jjg@1569 | 123 | // sadly, command line errors are not (yet?) reported to |
jjg@1569 | 124 | // the diag listener |
jjg@1569 | 125 | String out = sw.toString(); |
jjg@1569 | 126 | if (!out.isEmpty()) |
jjg@1569 | 127 | System.err.println(out.trim()); |
jjg@1569 | 128 | |
jjg@1569 | 129 | switch (t) { |
jjg@1569 | 130 | case JDK1_8: |
jjg@1569 | 131 | if (!out.isEmpty()) |
jjg@1569 | 132 | error("unexpected output from compiler"); |
jjg@1569 | 133 | break; |
jjg@1569 | 134 | default: |
jjg@1569 | 135 | if (p != Profile.DEFAULT |
jjg@1569 | 136 | && !out.contains("profile " + p.name |
jjg@1569 | 137 | + " is not valid for target release " + t.name)) { |
jjg@1569 | 138 | error("expected message not found"); |
jjg@1569 | 139 | } |
jjg@1569 | 140 | } |
jjg@1569 | 141 | } |
jjg@1569 | 142 | } |
jjg@1569 | 143 | } |
jjg@1569 | 144 | |
jjg@1569 | 145 | @Test |
jjg@1569 | 146 | void testClassesInProfiles() throws Exception { |
jjg@1569 | 147 | for (Profile p: Profile.values()) { |
jjg@1569 | 148 | for (Map.Entry<Profile, List<JavaFileObject>> e: testClasses.entrySet()) { |
jjg@1569 | 149 | for (JavaFileObject fo: e.getValue()) { |
jjg@1569 | 150 | DiagnosticCollector<JavaFileObject> dl = |
jjg@1569 | 151 | new DiagnosticCollector<JavaFileObject>(); |
jjg@1569 | 152 | List<String> opts = (p == Profile.DEFAULT) |
jjg@1569 | 153 | ? Collections.<String>emptyList() |
jjg@1569 | 154 | : Arrays.asList("-profile", p.name); |
jjg@1569 | 155 | JavacTask task = (JavacTask) javac.getTask(null, fm, dl, opts, null, |
jjg@1569 | 156 | Arrays.asList(fo)); |
jjg@1569 | 157 | task.analyze(); |
jjg@1569 | 158 | |
jjg@1569 | 159 | List<String> expectDiagCodes = (p.value >= e.getKey().value) |
jjg@1569 | 160 | ? Collections.<String>emptyList() |
jjg@1569 | 161 | : Arrays.asList("compiler.err.not.in.profile"); |
jjg@1569 | 162 | |
jjg@1569 | 163 | checkDiags(opts + " " + fo.getName(), dl.getDiagnostics(), expectDiagCodes); |
jjg@1569 | 164 | } |
jjg@1569 | 165 | } |
jjg@1569 | 166 | } |
jjg@1569 | 167 | } |
jjg@1569 | 168 | |
jjg@1569 | 169 | Map<Profile, List<JavaFileObject>> testClasses = |
jjg@1569 | 170 | new EnumMap<Profile, List<JavaFileObject>>(Profile.class); |
jjg@1569 | 171 | |
jjg@1569 | 172 | void initTestClasses() { |
jjg@1569 | 173 | // The following table assumes the existence of specific classes |
jjg@1569 | 174 | // in specific profiles, as defined in the Java SE 8 spec. |
jjg@1569 | 175 | init(Profile.COMPACT1, |
jjg@1569 | 176 | java.lang.String.class); |
jjg@1569 | 177 | |
jjg@1569 | 178 | init(Profile.COMPACT2, |
jjg@1569 | 179 | javax.xml.XMLConstants.class); |
jjg@1569 | 180 | |
jjg@1569 | 181 | init(Profile.COMPACT3, |
alanb@1731 | 182 | javax.sql.rowset.Predicate.class, |
jjg@1569 | 183 | com.sun.security.auth.PolicyFile.class); // specifically included in 3 |
jjg@1569 | 184 | |
jjg@1569 | 185 | init(Profile.DEFAULT, |
jjg@1569 | 186 | java.beans.BeanInfo.class, |
jjg@1569 | 187 | javax.management.remote.rmi._RMIServer_Stub.class); // specifically excluded in 3 |
jjg@1569 | 188 | } |
jjg@1569 | 189 | |
jjg@1569 | 190 | void init(Profile p, Class<?>... classes) { |
jjg@1569 | 191 | List<JavaFileObject> srcs = new ArrayList<JavaFileObject>(); |
jjg@1569 | 192 | for (Class<?> c: classes) { |
jjg@1569 | 193 | String name = "T" + c.getSimpleName(); |
jjg@1569 | 194 | String src = |
jjg@1569 | 195 | "class T" + name + "{" + "\n" + |
jjg@1569 | 196 | " Class<?> c = " + c.getName() + ".class;\n" + |
jjg@1569 | 197 | "}"; |
jjg@1569 | 198 | srcs.add(new StringJavaFileObject(name + ".java", src)); |
jjg@1569 | 199 | } |
jjg@1569 | 200 | testClasses.put(p, srcs); |
jjg@1569 | 201 | } |
jjg@1569 | 202 | |
jjg@1569 | 203 | void checkDiags(String msg, List<Diagnostic<? extends JavaFileObject>> diags, List<String> expectDiagCodes) { |
jjg@1569 | 204 | System.err.print(msg); |
jjg@1569 | 205 | if (diags.isEmpty()) |
jjg@1569 | 206 | System.err.println(" OK"); |
jjg@1569 | 207 | else { |
jjg@1569 | 208 | System.err.println(); |
jjg@1569 | 209 | System.err.println(diags); |
jjg@1569 | 210 | } |
jjg@1569 | 211 | |
jjg@1569 | 212 | List<String> foundDiagCodes = new ArrayList<String>(); |
jjg@1569 | 213 | for (Diagnostic<? extends JavaFileObject> d: diags) |
jjg@1569 | 214 | foundDiagCodes.add(d.getCode()); |
jjg@1569 | 215 | |
jjg@1569 | 216 | if (!foundDiagCodes.equals(expectDiagCodes)) { |
jjg@1569 | 217 | System.err.println("Found diag codes: " + foundDiagCodes); |
jjg@1569 | 218 | System.err.println("Expected diag codes: " + expectDiagCodes); |
jjg@1569 | 219 | error("expected diagnostics not found"); |
jjg@1569 | 220 | } |
jjg@1569 | 221 | } |
jjg@1569 | 222 | |
jjg@1569 | 223 | /** Marker annotation for test cases. */ |
jjg@1569 | 224 | @Retention(RetentionPolicy.RUNTIME) |
jjg@1569 | 225 | @interface Test { } |
jjg@1569 | 226 | |
jjg@1569 | 227 | /** Run all test cases. */ |
jjg@1569 | 228 | void run() throws Exception { |
jjg@1569 | 229 | initTestClasses(); |
jjg@1569 | 230 | |
jjg@1569 | 231 | for (Method m: getClass().getDeclaredMethods()) { |
jjg@1569 | 232 | Annotation a = m.getAnnotation(Test.class); |
jjg@1569 | 233 | if (a != null) { |
jjg@1569 | 234 | System.err.println(m.getName()); |
jjg@1569 | 235 | try { |
jjg@1569 | 236 | m.invoke(this, new Object[] { }); |
jjg@1569 | 237 | } catch (InvocationTargetException e) { |
jjg@1569 | 238 | Throwable cause = e.getCause(); |
jjg@1569 | 239 | throw (cause instanceof Exception) ? ((Exception) cause) : e; |
jjg@1569 | 240 | } |
jjg@1569 | 241 | System.err.println(); |
jjg@1569 | 242 | } |
jjg@1569 | 243 | } |
jjg@1569 | 244 | |
jjg@1569 | 245 | if (errors > 0) |
jjg@1569 | 246 | throw new Exception(errors + " errors occurred"); |
jjg@1569 | 247 | } |
jjg@1569 | 248 | |
jjg@1569 | 249 | void error(String msg) { |
jjg@1569 | 250 | System.err.println("Error: " + msg); |
jjg@1569 | 251 | errors++; |
jjg@1569 | 252 | } |
jjg@1569 | 253 | |
jjg@1569 | 254 | int errors; |
jjg@1569 | 255 | |
jjg@1569 | 256 | private static class StringJavaFileObject extends SimpleJavaFileObject { |
jjg@1569 | 257 | StringJavaFileObject(String name, String text) { |
jjg@1569 | 258 | super(URI.create(name), JavaFileObject.Kind.SOURCE); |
jjg@1569 | 259 | this.text = text; |
jjg@1569 | 260 | } |
jjg@1569 | 261 | @Override |
jjg@1569 | 262 | public CharSequence getCharContent(boolean b) { |
jjg@1569 | 263 | return text; |
jjg@1569 | 264 | } |
jjg@1569 | 265 | private String text; |
jjg@1569 | 266 | } |
jjg@1569 | 267 | } |