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) 1997, 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 com.sun.tools.javac.main.CommandLine;
31 import com.sun.tools.javac.util.Context;
32 import com.sun.tools.javac.util.List;
33 import com.sun.tools.javac.util.ListBuffer;
34 import com.sun.tools.javac.util.Log;
35 import com.sun.tools.javac.util.Options;
37 import java.io.IOException;
38 import java.io.File;
39 import java.io.FileNotFoundException;
40 import java.io.PrintWriter;
42 import java.util.StringTokenizer;
44 import static com.sun.tools.javac.code.Flags.*;
46 /**
47 * Main program of Javadoc.
48 * Previously named "Main".
49 *
50 * @since 1.2
51 * @author Robert Field
52 * @author Neal Gafter (rewrite)
53 */
54 class Start {
56 private final String defaultDocletClassName;
57 private final ClassLoader docletParentClassLoader;
59 private static final String javadocName = "javadoc";
61 private static final String standardDocletClassName =
62 "com.sun.tools.doclets.standard.Standard";
64 private ListBuffer<String[]> options = new ListBuffer<String[]>();
66 private ModifierFilter showAccess = null;
68 private long defaultFilter = PUBLIC | PROTECTED;
70 private Messager messager;
72 String docLocale = "";
74 boolean breakiterator = false;
75 boolean quiet = false;
76 String encoding = null;
78 private DocletInvoker docletInvoker;
80 /* Treat warnings as errors. */
81 private boolean rejectWarnings = false;
83 Start(String programName,
84 PrintWriter errWriter,
85 PrintWriter warnWriter,
86 PrintWriter noticeWriter,
87 String defaultDocletClassName) {
88 this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
89 }
91 Start(String programName,
92 PrintWriter errWriter,
93 PrintWriter warnWriter,
94 PrintWriter noticeWriter,
95 String defaultDocletClassName,
96 ClassLoader docletParentClassLoader) {
97 Context tempContext = new Context(); // interim context until option decoding completed
98 messager = new Messager(tempContext, programName, errWriter, warnWriter, noticeWriter);
99 this.defaultDocletClassName = defaultDocletClassName;
100 this.docletParentClassLoader = docletParentClassLoader;
101 }
103 Start(String programName, String defaultDocletClassName) {
104 this(programName, defaultDocletClassName, null);
105 }
107 Start(String programName, String defaultDocletClassName,
108 ClassLoader docletParentClassLoader) {
109 Context tempContext = new Context(); // interim context until option decoding completed
110 messager = new Messager(tempContext, programName);
111 this.defaultDocletClassName = defaultDocletClassName;
112 this.docletParentClassLoader = docletParentClassLoader;
113 }
115 Start(String programName, ClassLoader docletParentClassLoader) {
116 this(programName, standardDocletClassName, docletParentClassLoader);
117 }
119 Start(String programName) {
120 this(programName, standardDocletClassName);
121 }
123 Start(ClassLoader docletParentClassLoader) {
124 this(javadocName, docletParentClassLoader);
125 }
127 Start() {
128 this(javadocName);
129 }
131 /**
132 * Usage
133 */
134 private void usage() {
135 messager.notice("main.usage");
137 // let doclet print usage information (does nothing on error)
138 if (docletInvoker != null) {
139 docletInvoker.optionLength("-help");
140 }
141 }
143 /**
144 * Usage
145 */
146 private void Xusage() {
147 messager.notice("main.Xusage");
148 }
150 /**
151 * Exit
152 */
153 private void exit() {
154 messager.exit();
155 }
158 /**
159 * Main program - external wrapper
160 */
161 int begin(String... argv) {
162 boolean failed = false;
164 try {
165 failed = !parseAndExecute(argv);
166 } catch(Messager.ExitJavadoc exc) {
167 // ignore, we just exit this way
168 } catch (OutOfMemoryError ee) {
169 messager.error(null, "main.out.of.memory");
170 failed = true;
171 } catch (Error ee) {
172 ee.printStackTrace(System.err);
173 messager.error(null, "main.fatal.error");
174 failed = true;
175 } catch (Exception ee) {
176 ee.printStackTrace(System.err);
177 messager.error(null, "main.fatal.exception");
178 failed = true;
179 } finally {
180 messager.exitNotice();
181 messager.flush();
182 }
183 failed |= messager.nerrors() > 0;
184 failed |= rejectWarnings && messager.nwarnings() > 0;
185 return failed ? 1 : 0;
186 }
188 private void addToList(ListBuffer<String> list, String str){
189 StringTokenizer st = new StringTokenizer(str, ":");
190 String current;
191 while(st.hasMoreTokens()){
192 current = st.nextToken();
193 list.append(current);
194 }
195 }
197 /**
198 * Main program - internal
199 */
200 private boolean parseAndExecute(String... argv) throws IOException {
201 long tm = System.currentTimeMillis();
203 ListBuffer<String> javaNames = new ListBuffer<String>();
205 // Preprocess @file arguments
206 try {
207 argv = CommandLine.parse(argv);
208 } catch (FileNotFoundException e) {
209 messager.error(null, "main.cant.read", e.getMessage());
210 exit();
211 } catch (IOException e) {
212 e.printStackTrace(System.err);
213 exit();
214 }
216 setDocletInvoker(argv);
217 ListBuffer<String> subPackages = new ListBuffer<String>();
218 ListBuffer<String> excludedPackages = new ListBuffer<String>();
220 Context context = new Context();
221 // Setup a new Messager, using the same initial parameters as the
222 // existing Messager, except that this one will be able to use any
223 // options that may be set up below.
224 Messager.preRegister(context,
225 messager.programName,
226 messager.getWriter(Log.WriterKind.ERROR),
227 messager.getWriter(Log.WriterKind.WARNING),
228 messager.getWriter(Log.WriterKind.NOTICE));
230 Options compOpts = Options.instance(context);
231 boolean docClasses = false;
233 // Parse arguments
234 for (int i = 0 ; i < argv.length ; i++) {
235 String arg = argv[i];
236 if (arg.equals("-subpackages")) {
237 oneArg(argv, i++);
238 addToList(subPackages, argv[i]);
239 } else if (arg.equals("-exclude")){
240 oneArg(argv, i++);
241 addToList(excludedPackages, argv[i]);
242 } else if (arg.equals("-verbose")) {
243 setOption(arg);
244 compOpts.put("-verbose", "");
245 } else if (arg.equals("-encoding")) {
246 oneArg(argv, i++);
247 encoding = argv[i];
248 compOpts.put("-encoding", argv[i]);
249 } else if (arg.equals("-breakiterator")) {
250 breakiterator = true;
251 setOption("-breakiterator");
252 } else if (arg.equals("-quiet")) {
253 quiet = true;
254 setOption("-quiet");
255 } else if (arg.equals("-help")) {
256 usage();
257 exit();
258 } else if (arg.equals("-Xclasses")) {
259 setOption(arg);
260 docClasses = true;
261 } else if (arg.equals("-Xwerror")) {
262 setOption(arg);
263 rejectWarnings = true;
264 } else if (arg.equals("-private")) {
265 setOption(arg);
266 setFilter(ModifierFilter.ALL_ACCESS);
267 } else if (arg.equals("-package")) {
268 setOption(arg);
269 setFilter(PUBLIC | PROTECTED |
270 ModifierFilter.PACKAGE );
271 } else if (arg.equals("-protected")) {
272 setOption(arg);
273 setFilter(PUBLIC | PROTECTED );
274 } else if (arg.equals("-public")) {
275 setOption(arg);
276 setFilter(PUBLIC);
277 } else if (arg.equals("-source")) {
278 oneArg(argv, i++);
279 if (compOpts.get("-source") != null) {
280 usageError("main.option.already.seen", arg);
281 }
282 compOpts.put("-source", argv[i]);
283 } else if (arg.equals("-prompt")) {
284 compOpts.put("-prompt", "-prompt");
285 messager.promptOnError = true;
286 } else if (arg.equals("-sourcepath")) {
287 oneArg(argv, i++);
288 if (compOpts.get("-sourcepath") != null) {
289 usageError("main.option.already.seen", arg);
290 }
291 compOpts.put("-sourcepath", argv[i]);
292 } else if (arg.equals("-classpath")) {
293 oneArg(argv, i++);
294 if (compOpts.get("-classpath") != null) {
295 usageError("main.option.already.seen", arg);
296 }
297 compOpts.put("-classpath", argv[i]);
298 } else if (arg.equals("-sysclasspath")) {
299 oneArg(argv, i++);
300 if (compOpts.get("-bootclasspath") != null) {
301 usageError("main.option.already.seen", arg);
302 }
303 compOpts.put("-bootclasspath", argv[i]);
304 } else if (arg.equals("-bootclasspath")) {
305 oneArg(argv, i++);
306 if (compOpts.get("-bootclasspath") != null) {
307 usageError("main.option.already.seen", arg);
308 }
309 compOpts.put("-bootclasspath", argv[i]);
310 } else if (arg.equals("-extdirs")) {
311 oneArg(argv, i++);
312 if (compOpts.get("-extdirs") != null) {
313 usageError("main.option.already.seen", arg);
314 }
315 compOpts.put("-extdirs", argv[i]);
316 } else if (arg.equals("-overview")) {
317 oneArg(argv, i++);
318 } else if (arg.equals("-doclet")) {
319 i++; // handled in setDocletInvoker
320 } else if (arg.equals("-docletpath")) {
321 i++; // handled in setDocletInvoker
322 } else if (arg.equals("-locale")) {
323 if (i != 0)
324 usageError("main.locale_first");
325 oneArg(argv, i++);
326 docLocale = argv[i];
327 } else if (arg.equals("-Xmaxerrs") || arg.equals("-Xmaxwarns")) {
328 oneArg(argv, i++);
329 if (compOpts.get(arg) != null) {
330 usageError("main.option.already.seen", arg);
331 }
332 compOpts.put(arg, argv[i]);
333 } else if (arg.equals("-X")) {
334 Xusage();
335 exit();
336 } else if (arg.startsWith("-XD")) {
337 String s = arg.substring("-XD".length());
338 int eq = s.indexOf('=');
339 String key = (eq < 0) ? s : s.substring(0, eq);
340 String value = (eq < 0) ? s : s.substring(eq+1);
341 compOpts.put(key, value);
342 }
343 // call doclet for its options
344 // other arg starts with - is invalid
345 else if ( arg.startsWith("-") ) {
346 int optionLength;
347 optionLength = docletInvoker.optionLength(arg);
348 if (optionLength < 0) {
349 // error already displayed
350 exit();
351 } else if (optionLength == 0) {
352 // option not found
353 usageError("main.invalid_flag", arg);
354 } else {
355 // doclet added option
356 if ((i + optionLength) > argv.length) {
357 usageError("main.requires_argument", arg);
358 }
359 ListBuffer<String> args = new ListBuffer<String>();
360 for (int j = 0; j < optionLength-1; ++j) {
361 args.append(argv[++i]);
362 }
363 setOption(arg, args.toList());
364 }
365 } else {
366 javaNames.append(arg);
367 }
368 }
370 if (javaNames.isEmpty() && subPackages.isEmpty()) {
371 usageError("main.No_packages_or_classes_specified");
372 }
374 if (!docletInvoker.validOptions(options.toList())) {
375 // error message already displayed
376 exit();
377 }
379 JavadocTool comp = JavadocTool.make0(context);
380 if (comp == null) return false;
382 if (showAccess == null) {
383 setFilter(defaultFilter);
384 }
386 LanguageVersion languageVersion = docletInvoker.languageVersion();
387 RootDocImpl root = comp.getRootDocImpl(
388 docLocale, encoding, showAccess,
389 javaNames.toList(), options.toList(), breakiterator,
390 subPackages.toList(), excludedPackages.toList(),
391 docClasses,
392 // legacy?
393 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1, quiet);
395 // pass off control to the doclet
396 boolean ok = root != null;
397 if (ok) ok = docletInvoker.start(root);
399 Messager docletMessager = Messager.instance0(context);
400 messager.nwarnings += docletMessager.nwarnings;
401 messager.nerrors += docletMessager.nerrors;
403 // We're done.
404 if (compOpts.get("-verbose") != null) {
405 tm = System.currentTimeMillis() - tm;
406 messager.notice("main.done_in", Long.toString(tm));
407 }
409 return ok;
410 }
412 private void setDocletInvoker(String[] argv) {
413 String docletClassName = null;
414 String docletPath = null;
416 // Parse doclet specifying arguments
417 for (int i = 0 ; i < argv.length ; i++) {
418 String arg = argv[i];
419 if (arg.equals("-doclet")) {
420 oneArg(argv, i++);
421 if (docletClassName != null) {
422 usageError("main.more_than_one_doclet_specified_0_and_1",
423 docletClassName, argv[i]);
424 }
425 docletClassName = argv[i];
426 } else if (arg.equals("-docletpath")) {
427 oneArg(argv, i++);
428 if (docletPath == null) {
429 docletPath = argv[i];
430 } else {
431 docletPath += File.pathSeparator + argv[i];
432 }
433 }
434 }
436 if (docletClassName == null) {
437 docletClassName = defaultDocletClassName;
438 }
440 // attempt to find doclet
441 docletInvoker = new DocletInvoker(messager,
442 docletClassName, docletPath,
443 docletParentClassLoader);
444 }
446 private void setFilter(long filterBits) {
447 if (showAccess != null) {
448 messager.error(null, "main.incompatible.access.flags");
449 usage();
450 exit();
451 }
452 showAccess = new ModifierFilter(filterBits);
453 }
455 /**
456 * Set one arg option.
457 * Error and exit if one argument is not provided.
458 */
459 private void oneArg(String[] args, int index) {
460 if ((index + 1) < args.length) {
461 setOption(args[index], args[index+1]);
462 } else {
463 usageError("main.requires_argument", args[index]);
464 }
465 }
467 private void usageError(String key) {
468 messager.error(null, key);
469 usage();
470 exit();
471 }
473 private void usageError(String key, String a1) {
474 messager.error(null, key, a1);
475 usage();
476 exit();
477 }
479 private void usageError(String key, String a1, String a2) {
480 messager.error(null, key, a1, a2);
481 usage();
482 exit();
483 }
485 /**
486 * indicate an option with no arguments was given.
487 */
488 private void setOption(String opt) {
489 String[] option = { opt };
490 options.append(option);
491 }
493 /**
494 * indicate an option with one argument was given.
495 */
496 private void setOption(String opt, String argument) {
497 String[] option = { opt, argument };
498 options.append(option);
499 }
501 /**
502 * indicate an option with the specified list of arguments was given.
503 */
504 private void setOption(String opt, List<String> arguments) {
505 String[] args = new String[arguments.length() + 1];
506 int k = 0;
507 args[k++] = opt;
508 for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
509 args[k++] = i.head;
510 }
511 options = options.append(args);
512 }
514 }