Tue, 04 Mar 2008 15:45:20 +0000
6663588: Compiler goes into infinite loop for Cyclic Inheritance test case
Summary: interplay between cyclic inheritance and tvar bounds hangs javac
Reviewed-by: jjg
1 /*
2 * Copyright 2001-2006 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 java.io.*;
30 import java.util.Collection;
32 import com.sun.tools.javac.code.*;
33 import com.sun.tools.javac.code.Symbol.*;
34 import com.sun.tools.javac.comp.*;
35 import com.sun.tools.javac.jvm.ClassReader;
36 import com.sun.tools.javac.jvm.ClassWriter;
37 import com.sun.tools.javac.parser.DocCommentScanner;
38 import com.sun.tools.javac.util.Paths;
39 import com.sun.tools.javac.tree.*;
40 import com.sun.tools.javac.tree.JCTree.*;
41 import com.sun.tools.javac.util.*;
43 import com.sun.javadoc.LanguageVersion;
44 import static com.sun.javadoc.LanguageVersion.*;
46 /**
47 * This class could be the main entry point for Javadoc when Javadoc is used as a
48 * component in a larger software system. It provides operations to
49 * construct a new javadoc processor, and to run it on a set of source
50 * files.
51 * @author Neal Gafter
52 */
53 public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
54 DocEnv docenv;
56 final Context context;
57 final Messager messager;
58 final JavadocClassReader reader;
59 final JavadocEnter enter;
60 final Annotate annotate;
61 private final Paths paths;
63 /**
64 * Construct a new JavaCompiler processor, using appropriately
65 * extended phases of the underlying compiler.
66 */
67 protected JavadocTool(Context context) {
68 super(context);
69 this.context = context;
70 messager = Messager.instance0(context);
71 reader = JavadocClassReader.instance0(context);
72 enter = JavadocEnter.instance0(context);
73 annotate = Annotate.instance(context);
74 paths = Paths.instance(context);
75 }
77 /**
78 * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler.
79 */
80 protected boolean keepComments() {
81 return true;
82 }
84 /**
85 * Construct a new javadoc tool.
86 */
87 public static JavadocTool make0(Context context) {
88 Messager messager = null;
89 try {
90 // force the use of Javadoc's class reader
91 JavadocClassReader.preRegister(context);
93 // force the use of Javadoc's own enter phase
94 JavadocEnter.preRegister(context);
96 // force the use of Javadoc's own member enter phase
97 JavadocMemberEnter.preRegister(context);
99 // force the use of Javadoc's own todo phase
100 JavadocTodo.preRegister(context);
102 // force the use of Messager as a Log
103 messager = Messager.instance0(context);
105 // force the use of the scanner that captures Javadoc comments
106 DocCommentScanner.Factory.preRegister(context);
108 return new JavadocTool(context);
109 } catch (CompletionFailure ex) {
110 messager.error(Position.NOPOS, ex.getMessage());
111 return null;
112 }
113 }
115 public RootDocImpl getRootDocImpl(String doclocale,
116 String encoding,
117 ModifierFilter filter,
118 List<String> javaNames,
119 List<String[]> options,
120 boolean breakiterator,
121 List<String> subPackages,
122 List<String> excludedPackages,
123 boolean docClasses,
124 boolean legacyDoclet,
125 boolean quiet) throws IOException {
126 docenv = DocEnv.instance(context);
127 docenv.showAccess = filter;
128 docenv.quiet = quiet;
129 docenv.breakiterator = breakiterator;
130 docenv.setLocale(doclocale);
131 docenv.setEncoding(encoding);
132 docenv.docClasses = docClasses;
133 docenv.legacyDoclet = legacyDoclet;
134 reader.sourceCompleter = docClasses ? null : this;
136 ListBuffer<String> names = new ListBuffer<String>();
137 ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<JCCompilationUnit>();
138 ListBuffer<JCCompilationUnit> packTrees = new ListBuffer<JCCompilationUnit>();
140 try {
141 for (List<String> it = javaNames; it.nonEmpty(); it = it.tail) {
142 String name = it.head;
143 if (!docClasses && name.endsWith(".java") && new File(name).exists()) {
144 docenv.notice("main.Loading_source_file", name);
145 JCCompilationUnit tree = parse(name);
146 classTrees.append(tree);
147 } else if (isValidPackageName(name)) {
148 names = names.append(name);
149 } else if (name.endsWith(".java")) {
150 docenv.error(null, "main.file_not_found", name);;
151 } else {
152 docenv.error(null, "main.illegal_package_name", name);
153 }
154 }
156 if (!docClasses) {
157 // Recursively search given subpackages. If any packages
158 //are found, add them to the list.
159 searchSubPackages(subPackages, names, excludedPackages);
161 // Parse the packages
162 for (List<String> packs = names.toList(); packs.nonEmpty(); packs = packs.tail) {
163 // Parse sources ostensibly belonging to package.
164 parsePackageClasses(packs.head, packTrees, excludedPackages);
165 }
167 if (messager.nerrors() != 0) return null;
169 // Enter symbols for all files
170 docenv.notice("main.Building_tree");
171 enter.main(classTrees.toList().appendList(packTrees.toList()));
172 }
173 } catch (Abort ex) {}
175 if (messager.nerrors() != 0) return null;
177 if (docClasses)
178 return new RootDocImpl(docenv, javaNames, options);
179 else
180 return new RootDocImpl(docenv, listClasses(classTrees.toList()), names.toList(), options);
181 }
183 /** Is the given string a valid package name? */
184 boolean isValidPackageName(String s) {
185 int index;
186 while ((index = s.indexOf('.')) != -1) {
187 if (!isValidClassName(s.substring(0, index))) return false;
188 s = s.substring(index+1);
189 }
190 return isValidClassName(s);
191 }
194 private final static char pathSep = File.pathSeparatorChar;
196 /**
197 * search all directories in path for subdirectory name. Add all
198 * .java files found in such a directory to args.
199 */
200 private void parsePackageClasses(String name,
201 ListBuffer<JCCompilationUnit> trees,
202 List<String> excludedPackages)
203 throws IOException {
204 if (excludedPackages.contains(name)) {
205 return;
206 }
207 boolean hasFiles = false;
208 docenv.notice("main.Loading_source_files_for_package", name);
209 name = name.replace('.', File.separatorChar);
210 for (File pathname : paths.sourceSearchPath()) {
211 File f = new File(pathname, name);
212 String names[] = f.list();
213 // if names not null, then found directory with source files
214 if (names != null) {
215 String dir = f.getAbsolutePath();
216 if (!dir.endsWith(File.separator))
217 dir = dir + File.separator;
218 for (int j = 0; j < names.length; j++) {
219 if (isValidJavaSourceFile(names[j])) {
220 String fn = dir + names[j];
221 // messager.notice("main.Loading_source_file", fn);
222 trees.append(parse(fn));
223 hasFiles = true;
224 }
225 }
226 }
227 }
228 if (!hasFiles)
229 messager.warning(null, "main.no_source_files_for_package",
230 name.replace(File.separatorChar, '.'));
231 }
233 /**
234 * Recursively search all directories in path for subdirectory name.
235 * Add all packages found in such a directory to packages list.
236 */
237 private void searchSubPackages(List<String> subPackages,
238 ListBuffer<String> packages,
239 List<String> excludedPackages) {
240 // FIXME: This search path is bogus.
241 // Only the effective source path should be searched for sources.
242 // Only the effective class path should be searched for classes.
243 // Should the bootclasspath/extdirs also be searched for classes?
244 java.util.List<File> pathnames = new java.util.ArrayList<File>();
245 if (paths.sourcePath() != null)
246 for (File elt : paths.sourcePath())
247 pathnames.add(elt);
248 for (File elt : paths.userClassPath())
249 pathnames.add(elt);
251 for (String subPackage : subPackages)
252 searchSubPackage(subPackage, packages, excludedPackages, pathnames);
253 }
255 /**
256 * Recursively search all directories in path for subdirectory name.
257 * Add all packages found in such a directory to packages list.
258 */
259 private void searchSubPackage(String packageName,
260 ListBuffer<String> packages,
261 List<String> excludedPackages,
262 Collection<File> pathnames) {
263 if (excludedPackages.contains(packageName))
264 return;
266 String packageFilename = packageName.replace('.', File.separatorChar);
267 boolean addedPackage = false;
268 for (File pathname : pathnames) {
269 File f = new File(pathname, packageFilename);
270 String filenames[] = f.list();
271 // if filenames not null, then found directory
272 if (filenames != null) {
273 for (String filename : filenames) {
274 if (!addedPackage
275 && (isValidJavaSourceFile(filename) ||
276 isValidJavaClassFile(filename))
277 && !packages.contains(packageName)) {
278 packages.append(packageName);
279 addedPackage = true;
280 } else if (isValidClassName(filename) &&
281 (new File(f, filename)).isDirectory()) {
282 searchSubPackage(packageName + "." + filename,
283 packages, excludedPackages, pathnames);
284 }
285 }
286 }
287 }
288 }
290 /**
291 * Return true if given file name is a valid class file name.
292 * @param file the name of the file to check.
293 * @return true if given file name is a valid class file name
294 * and false otherwise.
295 */
296 private static boolean isValidJavaClassFile(String file) {
297 if (!file.endsWith(".class")) return false;
298 String clazzName = file.substring(0, file.length() - ".class".length());
299 return isValidClassName(clazzName);
300 }
302 /**
303 * Return true if given file name is a valid Java source file name.
304 * @param file the name of the file to check.
305 * @return true if given file name is a valid Java source file name
306 * and false otherwise.
307 */
308 private static boolean isValidJavaSourceFile(String file) {
309 if (!file.endsWith(".java")) return false;
310 String clazzName = file.substring(0, file.length() - ".java".length());
311 return isValidClassName(clazzName);
312 }
314 /** Are surrogates supported?
315 */
316 final static boolean surrogatesSupported = surrogatesSupported();
317 private static boolean surrogatesSupported() {
318 try {
319 boolean b = Character.isHighSurrogate('a');
320 return true;
321 } catch (NoSuchMethodError ex) {
322 return false;
323 }
324 }
326 /**
327 * Return true if given file name is a valid class name
328 * (including "package-info").
329 * @param clazzname the name of the class to check.
330 * @return true if given class name is a valid class name
331 * and false otherwise.
332 */
333 public static boolean isValidClassName(String s) {
334 if (s.length() < 1) return false;
335 if (s.equals("package-info")) return true;
336 if (surrogatesSupported) {
337 int cp = s.codePointAt(0);
338 if (!Character.isJavaIdentifierStart(cp))
339 return false;
340 for (int j=Character.charCount(cp); j<s.length(); j+=Character.charCount(cp)) {
341 cp = s.codePointAt(j);
342 if (!Character.isJavaIdentifierPart(cp))
343 return false;
344 }
345 } else {
346 if (!Character.isJavaIdentifierStart(s.charAt(0)))
347 return false;
348 for (int j=1; j<s.length(); j++)
349 if (!Character.isJavaIdentifierPart(s.charAt(j)))
350 return false;
351 }
352 return true;
353 }
355 /**
356 * From a list of top level trees, return the list of contained class definitions
357 */
358 List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) {
359 ListBuffer<JCClassDecl> result = new ListBuffer<JCClassDecl>();
360 for (JCCompilationUnit t : trees) {
361 for (JCTree def : t.defs) {
362 if (def.getTag() == JCTree.CLASSDEF)
363 result.append((JCClassDecl)def);
364 }
365 }
366 return result.toList();
367 }
369 }