Thu, 12 Jan 2012 15:28:34 +0000
7123100: javac fails with java.lang.StackOverflowError
Summary: Inference of under-constrained type-variables creates erroneous recursive wildcard types
Reviewed-by: jjg
1 /*
2 * Copyright (c) 1998, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javadoc;
28 import com.sun.javadoc.*;
30 import static com.sun.javadoc.LanguageVersion.*;
32 import com.sun.tools.javac.util.List;
34 import java.io.File;
35 import java.lang.reflect.Method;
36 import java.lang.reflect.Modifier;
37 import java.lang.reflect.InvocationTargetException;
38 import java.net.URL;
39 import java.net.URLClassLoader;
42 /**
43 * Class creates, controls and invokes doclets.
44 * @author Neal Gafter (rewrite)
45 */
46 public class DocletInvoker {
48 private final Class<?> docletClass;
50 private final String docletClassName;
52 private final ClassLoader appClassLoader;
54 private final Messager messager;
56 private static class DocletInvokeException extends Exception {
57 private static final long serialVersionUID = 0;
58 }
60 private String appendPath(String path1, String path2) {
61 if (path1 == null || path1.length() == 0) {
62 return path2 == null ? "." : path2;
63 } else if (path2 == null || path2.length() == 0) {
64 return path1;
65 } else {
66 return path1 + File.pathSeparator + path2;
67 }
68 }
70 public DocletInvoker(Messager messager,
71 String docletClassName, String docletPath,
72 ClassLoader docletParentClassLoader) {
73 this.messager = messager;
74 this.docletClassName = docletClassName;
76 // construct class loader
77 String cpString = null; // make sure env.class.path defaults to dot
79 // do prepends to get correct ordering
80 cpString = appendPath(System.getProperty("env.class.path"), cpString);
81 cpString = appendPath(System.getProperty("java.class.path"), cpString);
82 cpString = appendPath(docletPath, cpString);
83 URL[] urls = com.sun.tools.javac.file.Locations.pathToURLs(cpString);
84 if (docletParentClassLoader == null)
85 appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
86 else
87 appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
89 // attempt to find doclet
90 Class<?> dc = null;
91 try {
92 dc = appClassLoader.loadClass(docletClassName);
93 } catch (ClassNotFoundException exc) {
94 messager.error(null, "main.doclet_class_not_found", docletClassName);
95 messager.exit();
96 }
97 docletClass = dc;
98 }
100 /*
101 * Returns the delegation class loader to use when creating
102 * appClassLoader (used to load the doclet). The context class
103 * loader is the best choice, but legacy behavior was to use the
104 * default delegation class loader (aka system class loader).
105 *
106 * Here we favor using the context class loader. To ensure
107 * compatibility with existing apps, we revert to legacy
108 * behavior if either or both of the following conditions hold:
109 *
110 * 1) the doclet is loadable from the system class loader but not
111 * from the context class loader,
112 *
113 * 2) this.getClass() is loadable from the system class loader but not
114 * from the context class loader.
115 */
116 private ClassLoader getDelegationClassLoader(String docletClassName) {
117 ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
118 ClassLoader sysCL = ClassLoader.getSystemClassLoader();
119 if (sysCL == null)
120 return ctxCL;
121 if (ctxCL == null)
122 return sysCL;
124 // Condition 1.
125 try {
126 sysCL.loadClass(docletClassName);
127 try {
128 ctxCL.loadClass(docletClassName);
129 } catch (ClassNotFoundException e) {
130 return sysCL;
131 }
132 } catch (ClassNotFoundException e) {
133 }
135 // Condition 2.
136 try {
137 if (getClass() == sysCL.loadClass(getClass().getName())) {
138 try {
139 if (getClass() != ctxCL.loadClass(getClass().getName()))
140 return sysCL;
141 } catch (ClassNotFoundException e) {
142 return sysCL;
143 }
144 }
145 } catch (ClassNotFoundException e) {
146 }
148 return ctxCL;
149 }
151 /**
152 * Generate documentation here. Return true on success.
153 */
154 public boolean start(RootDoc root) {
155 Object retVal;
156 String methodName = "start";
157 Class<?>[] paramTypes = { RootDoc.class };
158 Object[] params = { root };
159 try {
160 retVal = invoke(methodName, null, paramTypes, params);
161 } catch (DocletInvokeException exc) {
162 return false;
163 }
164 if (retVal instanceof Boolean) {
165 return ((Boolean)retVal).booleanValue();
166 } else {
167 messager.error(null, "main.must_return_boolean",
168 docletClassName, methodName);
169 return false;
170 }
171 }
173 /**
174 * Check for doclet added options here. Zero return means
175 * option not known. Positive value indicates number of
176 * arguments to option. Negative value means error occurred.
177 */
178 public int optionLength(String option) {
179 Object retVal;
180 String methodName = "optionLength";
181 Class<?>[] paramTypes = { String.class };
182 Object[] params = { option };
183 try {
184 retVal = invoke(methodName, new Integer(0), paramTypes, params);
185 } catch (DocletInvokeException exc) {
186 return -1;
187 }
188 if (retVal instanceof Integer) {
189 return ((Integer)retVal).intValue();
190 } else {
191 messager.error(null, "main.must_return_int",
192 docletClassName, methodName);
193 return -1;
194 }
195 }
197 /**
198 * Let doclet check that all options are OK. Returning true means
199 * options are OK. If method does not exist, assume true.
200 */
201 public boolean validOptions(List<String[]> optlist) {
202 Object retVal;
203 String options[][] = optlist.toArray(new String[optlist.length()][]);
204 String methodName = "validOptions";
205 DocErrorReporter reporter = messager;
206 Class<?>[] paramTypes = { String[][].class, DocErrorReporter.class };
207 Object[] params = { options, reporter };
208 try {
209 retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
210 } catch (DocletInvokeException exc) {
211 return false;
212 }
213 if (retVal instanceof Boolean) {
214 return ((Boolean)retVal).booleanValue();
215 } else {
216 messager.error(null, "main.must_return_boolean",
217 docletClassName, methodName);
218 return false;
219 }
220 }
222 /**
223 * Return the language version supported by this doclet.
224 * If the method does not exist in the doclet, assume version 1.1.
225 */
226 public LanguageVersion languageVersion() {
227 try {
228 Object retVal;
229 String methodName = "languageVersion";
230 Class<?>[] paramTypes = new Class<?>[0];
231 Object[] params = new Object[0];
232 try {
233 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
234 } catch (DocletInvokeException exc) {
235 return JAVA_1_1;
236 }
237 if (retVal instanceof LanguageVersion) {
238 return (LanguageVersion)retVal;
239 } else {
240 messager.error(null, "main.must_return_languageversion",
241 docletClassName, methodName);
242 return JAVA_1_1;
243 }
244 } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
245 return null;
246 }
247 }
249 /**
250 * Utility method for calling doclet functionality
251 */
252 private Object invoke(String methodName, Object returnValueIfNonExistent,
253 Class<?>[] paramTypes, Object[] params)
254 throws DocletInvokeException {
255 Method meth;
256 try {
257 meth = docletClass.getMethod(methodName, paramTypes);
258 } catch (NoSuchMethodException exc) {
259 if (returnValueIfNonExistent == null) {
260 messager.error(null, "main.doclet_method_not_found",
261 docletClassName, methodName);
262 throw new DocletInvokeException();
263 } else {
264 return returnValueIfNonExistent;
265 }
266 } catch (SecurityException exc) {
267 messager.error(null, "main.doclet_method_not_accessible",
268 docletClassName, methodName);
269 throw new DocletInvokeException();
270 }
271 if (!Modifier.isStatic(meth.getModifiers())) {
272 messager.error(null, "main.doclet_method_must_be_static",
273 docletClassName, methodName);
274 throw new DocletInvokeException();
275 }
276 ClassLoader savedCCL =
277 Thread.currentThread().getContextClassLoader();
278 try {
279 Thread.currentThread().setContextClassLoader(appClassLoader);
280 return meth.invoke(null , params);
281 } catch (IllegalArgumentException exc) {
282 messager.error(null, "main.internal_error_exception_thrown",
283 docletClassName, methodName, exc.toString());
284 throw new DocletInvokeException();
285 } catch (IllegalAccessException exc) {
286 messager.error(null, "main.doclet_method_not_accessible",
287 docletClassName, methodName);
288 throw new DocletInvokeException();
289 } catch (NullPointerException exc) {
290 messager.error(null, "main.internal_error_exception_thrown",
291 docletClassName, methodName, exc.toString());
292 throw new DocletInvokeException();
293 } catch (InvocationTargetException exc) {
294 Throwable err = exc.getTargetException();
295 if (err instanceof java.lang.OutOfMemoryError) {
296 messager.error(null, "main.out.of.memory");
297 } else {
298 messager.error(null, "main.exception_thrown",
299 docletClassName, methodName, exc.toString());
300 exc.getTargetException().printStackTrace();
301 }
302 throw new DocletInvokeException();
303 } finally {
304 Thread.currentThread().setContextClassLoader(savedCCL);
305 }
306 }
307 }