Tue, 11 Aug 2009 01:13:14 +0100
6521805: Regression: JDK5/JDK6 javac allows write access to outer class reference
Summary: javac should warn/complain about identifiers with the same name as synthetic symbol
Reviewed-by: jjg
1 /*
2 * Copyright 1998-2009 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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.net.*;
35 import java.lang.reflect.Method;
36 import java.lang.reflect.Modifier;
37 import java.lang.reflect.InvocationTargetException;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.StringTokenizer;
43 /**
44 * Class creates, controls and invokes doclets.
45 * @author Neal Gafter (rewrite)
46 */
47 public class DocletInvoker {
49 private final Class<?> docletClass;
51 private final String docletClassName;
53 private final ClassLoader appClassLoader;
55 private final Messager messager;
57 private static class DocletInvokeException extends Exception {
58 private static final long serialVersionUID = 0;
59 }
61 private String appendPath(String path1, String path2) {
62 if (path1 == null || path1.length() == 0) {
63 return path2 == null ? "." : path2;
64 } else if (path2 == null || path2.length() == 0) {
65 return path1;
66 } else {
67 return path1 + File.pathSeparator + path2;
68 }
69 }
71 public DocletInvoker(Messager messager,
72 String docletClassName, String docletPath,
73 ClassLoader docletParentClassLoader) {
74 this.messager = messager;
75 this.docletClassName = docletClassName;
77 // construct class loader
78 String cpString = null; // make sure env.class.path defaults to dot
80 // do prepends to get correct ordering
81 cpString = appendPath(System.getProperty("env.class.path"), cpString);
82 cpString = appendPath(System.getProperty("java.class.path"), cpString);
83 cpString = appendPath(docletPath, cpString);
84 URL[] urls = pathToURLs(cpString);
85 if (docletParentClassLoader == null)
86 appClassLoader = new URLClassLoader(urls, getDelegationClassLoader(docletClassName));
87 else
88 appClassLoader = new URLClassLoader(urls, docletParentClassLoader);
90 // attempt to find doclet
91 Class<?> dc = null;
92 try {
93 dc = appClassLoader.loadClass(docletClassName);
94 } catch (ClassNotFoundException exc) {
95 messager.error(null, "main.doclet_class_not_found", docletClassName);
96 messager.exit();
97 }
98 docletClass = dc;
99 }
101 /*
102 * Returns the delegation class loader to use when creating
103 * appClassLoader (used to load the doclet). The context class
104 * loader is the best choice, but legacy behavior was to use the
105 * default delegation class loader (aka system class loader).
106 *
107 * Here we favor using the context class loader. To ensure
108 * compatibility with existing apps, we revert to legacy
109 * behavior if either or both of the following conditions hold:
110 *
111 * 1) the doclet is loadable from the system class loader but not
112 * from the context class loader,
113 *
114 * 2) this.getClass() is loadable from the system class loader but not
115 * from the context class loader.
116 */
117 private ClassLoader getDelegationClassLoader(String docletClassName) {
118 ClassLoader ctxCL = Thread.currentThread().getContextClassLoader();
119 ClassLoader sysCL = ClassLoader.getSystemClassLoader();
120 if (sysCL == null)
121 return ctxCL;
122 if (ctxCL == null)
123 return sysCL;
125 // Condition 1.
126 try {
127 sysCL.loadClass(docletClassName);
128 try {
129 ctxCL.loadClass(docletClassName);
130 } catch (ClassNotFoundException e) {
131 return sysCL;
132 }
133 } catch (ClassNotFoundException e) {
134 }
136 // Condition 2.
137 try {
138 if (getClass() == sysCL.loadClass(getClass().getName())) {
139 try {
140 if (getClass() != ctxCL.loadClass(getClass().getName()))
141 return sysCL;
142 } catch (ClassNotFoundException e) {
143 return sysCL;
144 }
145 }
146 } catch (ClassNotFoundException e) {
147 }
149 return ctxCL;
150 }
152 /**
153 * Generate documentation here. Return true on success.
154 */
155 public boolean start(RootDoc root) {
156 Object retVal;
157 String methodName = "start";
158 Class<?>[] paramTypes = new Class<?>[1];
159 Object[] params = new Object[1];
160 paramTypes[0] = RootDoc.class;
161 params[0] = root;
162 try {
163 retVal = invoke(methodName, null, paramTypes, params);
164 } catch (DocletInvokeException exc) {
165 return false;
166 }
167 if (retVal instanceof Boolean) {
168 return ((Boolean)retVal).booleanValue();
169 } else {
170 messager.error(null, "main.must_return_boolean",
171 docletClassName, methodName);
172 return false;
173 }
174 }
176 /**
177 * Check for doclet added options here. Zero return means
178 * option not known. Positive value indicates number of
179 * arguments to option. Negative value means error occurred.
180 */
181 public int optionLength(String option) {
182 Object retVal;
183 String methodName = "optionLength";
184 Class<?>[] paramTypes = new Class<?>[1];
185 Object[] params = new Object[1];
186 paramTypes[0] = option.getClass();
187 params[0] = option;
188 try {
189 retVal = invoke(methodName, new Integer(0), paramTypes, params);
190 } catch (DocletInvokeException exc) {
191 return -1;
192 }
193 if (retVal instanceof Integer) {
194 return ((Integer)retVal).intValue();
195 } else {
196 messager.error(null, "main.must_return_int",
197 docletClassName, methodName);
198 return -1;
199 }
200 }
202 /**
203 * Let doclet check that all options are OK. Returning true means
204 * options are OK. If method does not exist, assume true.
205 */
206 public boolean validOptions(List<String[]> optlist) {
207 Object retVal;
208 String options[][] = optlist.toArray(new String[optlist.length()][]);
209 String methodName = "validOptions";
210 DocErrorReporter reporter = messager;
211 Class<?>[] paramTypes = new Class<?>[2];
212 Object[] params = new Object[2];
213 paramTypes[0] = options.getClass();
214 paramTypes[1] = DocErrorReporter.class;
215 params[0] = options;
216 params[1] = reporter;
217 try {
218 retVal = invoke(methodName, Boolean.TRUE, paramTypes, params);
219 } catch (DocletInvokeException exc) {
220 return false;
221 }
222 if (retVal instanceof Boolean) {
223 return ((Boolean)retVal).booleanValue();
224 } else {
225 messager.error(null, "main.must_return_boolean",
226 docletClassName, methodName);
227 return false;
228 }
229 }
231 /**
232 * Return the language version supported by this doclet.
233 * If the method does not exist in the doclet, assume version 1.1.
234 */
235 public LanguageVersion languageVersion() {
236 try {
237 Object retVal;
238 String methodName = "languageVersion";
239 Class<?>[] paramTypes = new Class<?>[0];
240 Object[] params = new Object[0];
241 try {
242 retVal = invoke(methodName, JAVA_1_1, paramTypes, params);
243 } catch (DocletInvokeException exc) {
244 return JAVA_1_1;
245 }
246 if (retVal instanceof LanguageVersion) {
247 return (LanguageVersion)retVal;
248 } else {
249 messager.error(null, "main.must_return_languageversion",
250 docletClassName, methodName);
251 return JAVA_1_1;
252 }
253 } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class.
254 return null;
255 }
256 }
258 /**
259 * Utility method for calling doclet functionality
260 */
261 private Object invoke(String methodName, Object returnValueIfNonExistent,
262 Class<?>[] paramTypes, Object[] params)
263 throws DocletInvokeException {
264 Method meth;
265 try {
266 meth = docletClass.getMethod(methodName, paramTypes);
267 } catch (NoSuchMethodException exc) {
268 if (returnValueIfNonExistent == null) {
269 messager.error(null, "main.doclet_method_not_found",
270 docletClassName, methodName);
271 throw new DocletInvokeException();
272 } else {
273 return returnValueIfNonExistent;
274 }
275 } catch (SecurityException exc) {
276 messager.error(null, "main.doclet_method_not_accessible",
277 docletClassName, methodName);
278 throw new DocletInvokeException();
279 }
280 if (!Modifier.isStatic(meth.getModifiers())) {
281 messager.error(null, "main.doclet_method_must_be_static",
282 docletClassName, methodName);
283 throw new DocletInvokeException();
284 }
285 ClassLoader savedCCL =
286 Thread.currentThread().getContextClassLoader();
287 try {
288 Thread.currentThread().setContextClassLoader(appClassLoader);
289 return meth.invoke(null , params);
290 } catch (IllegalArgumentException exc) {
291 messager.error(null, "main.internal_error_exception_thrown",
292 docletClassName, methodName, exc.toString());
293 throw new DocletInvokeException();
294 } catch (IllegalAccessException exc) {
295 messager.error(null, "main.doclet_method_not_accessible",
296 docletClassName, methodName);
297 throw new DocletInvokeException();
298 } catch (NullPointerException exc) {
299 messager.error(null, "main.internal_error_exception_thrown",
300 docletClassName, methodName, exc.toString());
301 throw new DocletInvokeException();
302 } catch (InvocationTargetException exc) {
303 Throwable err = exc.getTargetException();
304 if (err instanceof java.lang.OutOfMemoryError) {
305 messager.error(null, "main.out.of.memory");
306 } else {
307 messager.error(null, "main.exception_thrown",
308 docletClassName, methodName, exc.toString());
309 exc.getTargetException().printStackTrace();
310 }
311 throw new DocletInvokeException();
312 } finally {
313 Thread.currentThread().setContextClassLoader(savedCCL);
314 }
315 }
317 /**
318 * Utility method for converting a search path string to an array
319 * of directory and JAR file URLs.
320 *
321 * @param path the search path string
322 * @return the resulting array of directory and JAR file URLs
323 */
324 static URL[] pathToURLs(String path) {
325 StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
326 URL[] urls = new URL[st.countTokens()];
327 int count = 0;
328 while (st.hasMoreTokens()) {
329 URL url = fileToURL(new File(st.nextToken()));
330 if (url != null) {
331 urls[count++] = url;
332 }
333 }
334 if (urls.length != count) {
335 URL[] tmp = new URL[count];
336 System.arraycopy(urls, 0, tmp, 0, count);
337 urls = tmp;
338 }
339 return urls;
340 }
342 /**
343 * Returns the directory or JAR file URL corresponding to the specified
344 * local file name.
345 *
346 * @param file the File object
347 * @return the resulting directory or JAR file URL, or null if unknown
348 */
349 static URL fileToURL(File file) {
350 String name;
351 try {
352 name = file.getCanonicalPath();
353 } catch (IOException e) {
354 name = file.getAbsolutePath();
355 }
356 name = name.replace(File.separatorChar, '/');
357 if (!name.startsWith("/")) {
358 name = "/" + name;
359 }
360 // If the file does not exist, then assume that it's a directory
361 if (!file.isFile()) {
362 name = name + "/";
363 }
364 try {
365 return new URL("file", "", name);
366 } catch (MalformedURLException e) {
367 throw new IllegalArgumentException("file");
368 }
369 }
370 }