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) 2000, 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 java.lang.reflect.Modifier;
29 import java.util.*;
30 import javax.tools.JavaFileManager;
32 import com.sun.javadoc.*;
34 import com.sun.tools.javac.code.*;
35 import com.sun.tools.javac.code.Symbol.*;
36 import com.sun.tools.javac.code.Type.ClassType;
37 import com.sun.tools.javac.comp.Check;
38 import com.sun.tools.javac.tree.JCTree.*;
39 import com.sun.tools.javac.util.Context;
40 import com.sun.tools.javac.util.Names;
41 import com.sun.tools.javac.util.Position;
43 /**
44 * Holds the environment for a run of javadoc.
45 * Holds only the information needed throughout the
46 * run and not the compiler info that could be GC'ed
47 * or ported.
48 *
49 * @since 1.4
50 * @author Robert Field
51 * @author Neal Gafter (rewrite)
52 * @author Scott Seligman (generics)
53 */
54 public class DocEnv {
55 protected static final Context.Key<DocEnv> docEnvKey =
56 new Context.Key<DocEnv>();
58 public static DocEnv instance(Context context) {
59 DocEnv instance = context.get(docEnvKey);
60 if (instance == null)
61 instance = new DocEnv(context);
62 return instance;
63 }
65 private Messager messager;
67 DocLocale doclocale;
69 /** Predefined symbols known to the compiler. */
70 Symtab syms;
72 /** Referenced directly in RootDocImpl. */
73 JavadocClassReader reader;
75 /** Javadoc's own version of the compiler's enter phase. */
76 JavadocEnter enter;
78 /** The name table. */
79 Names names;
81 /** The encoding name. */
82 private String encoding;
84 final Symbol externalizableSym;
86 /** Access filter (public, protected, ...). */
87 protected ModifierFilter showAccess;
89 /** True if we are using a sentence BreakIterator. */
90 boolean breakiterator;
92 /**
93 * True if we do not want to print any notifications at all.
94 */
95 boolean quiet = false;
97 Check chk;
98 Types types;
99 JavaFileManager fileManager;
101 /** Allow documenting from class files? */
102 boolean docClasses = false;
104 /** Does the doclet only expect pre-1.5 doclet API? */
105 protected boolean legacyDoclet = true;
107 /**
108 * Set this to true if you would like to not emit any errors, warnings and
109 * notices.
110 */
111 private boolean silent = false;
113 /**
114 * Constructor
115 *
116 * @param context Context for this javadoc instance.
117 */
118 protected DocEnv(Context context) {
119 context.put(docEnvKey, this);
121 messager = Messager.instance0(context);
122 syms = Symtab.instance(context);
123 reader = JavadocClassReader.instance0(context);
124 enter = JavadocEnter.instance0(context);
125 names = Names.instance(context);
126 externalizableSym = reader.enterClass(names.fromString("java.io.Externalizable"));
127 chk = Check.instance(context);
128 types = Types.instance(context);
129 fileManager = context.get(JavaFileManager.class);
131 // Default. Should normally be reset with setLocale.
132 this.doclocale = new DocLocale(this, "", breakiterator);
133 }
135 public void setSilent(boolean silent) {
136 this.silent = silent;
137 }
139 /**
140 * Look up ClassDoc by qualified name.
141 */
142 public ClassDocImpl lookupClass(String name) {
143 ClassSymbol c = getClassSymbol(name);
144 if (c != null) {
145 return getClassDoc(c);
146 } else {
147 return null;
148 }
149 }
151 /**
152 * Load ClassDoc by qualified name.
153 */
154 public ClassDocImpl loadClass(String name) {
155 try {
156 ClassSymbol c = reader.loadClass(names.fromString(name));
157 return getClassDoc(c);
158 } catch (CompletionFailure ex) {
159 chk.completionError(null, ex);
160 return null;
161 }
162 }
164 /**
165 * Look up PackageDoc by qualified name.
166 */
167 public PackageDocImpl lookupPackage(String name) {
168 //### Jing alleges that class check is needed
169 //### to avoid a compiler bug. Most likely
170 //### instead a dummy created for error recovery.
171 //### Should investigate this.
172 PackageSymbol p = syms.packages.get(names.fromString(name));
173 ClassSymbol c = getClassSymbol(name);
174 if (p != null && c == null) {
175 return getPackageDoc(p);
176 } else {
177 return null;
178 }
179 }
180 // where
181 /** Retrieve class symbol by fully-qualified name.
182 */
183 ClassSymbol getClassSymbol(String name) {
184 // Name may contain nested class qualification.
185 // Generate candidate flatnames with successively shorter
186 // package qualifiers and longer nested class qualifiers.
187 int nameLen = name.length();
188 char[] nameChars = name.toCharArray();
189 int idx = name.length();
190 for (;;) {
191 ClassSymbol s = syms.classes.get(names.fromChars(nameChars, 0, nameLen));
192 if (s != null)
193 return s; // found it!
194 idx = name.substring(0, idx).lastIndexOf('.');
195 if (idx < 0) break;
196 nameChars[idx] = '$';
197 }
198 return null;
199 }
201 /**
202 * Set the locale.
203 */
204 public void setLocale(String localeName) {
205 // create locale specifics
206 doclocale = new DocLocale(this, localeName, breakiterator);
207 // reset Messager if locale has changed.
208 messager.reset();
209 }
211 /** Check whether this member should be documented. */
212 public boolean shouldDocument(VarSymbol sym) {
213 long mod = sym.flags();
215 if ((mod & Flags.SYNTHETIC) != 0) {
216 return false;
217 }
219 return showAccess.checkModifier(translateModifiers(mod));
220 }
222 /** Check whether this member should be documented. */
223 public boolean shouldDocument(MethodSymbol sym) {
224 long mod = sym.flags();
226 if ((mod & Flags.SYNTHETIC) != 0) {
227 return false;
228 }
230 return showAccess.checkModifier(translateModifiers(mod));
231 }
233 /** check whether this class should be documented. */
234 public boolean shouldDocument(ClassSymbol sym) {
235 return
236 (sym.flags_field&Flags.SYNTHETIC) == 0 && // no synthetics
237 (docClasses || getClassDoc(sym).tree != null) &&
238 isVisible(sym);
239 }
241 //### Comment below is inaccurate wrt modifier filter testing
242 /**
243 * Check the visibility if this is an nested class.
244 * if this is not a nested class, return true.
245 * if this is an static visible nested class,
246 * return true.
247 * if this is an visible nested class
248 * if the outer class is visible return true.
249 * else return false.
250 * IMPORTANT: This also allows, static nested classes
251 * to be defined inside an nested class, which is not
252 * allowed by the compiler. So such an test case will
253 * not reach upto this method itself, but if compiler
254 * allows it, then that will go through.
255 */
256 protected boolean isVisible(ClassSymbol sym) {
257 long mod = sym.flags_field;
258 if (!showAccess.checkModifier(translateModifiers(mod))) {
259 return false;
260 }
261 ClassSymbol encl = sym.owner.enclClass();
262 return (encl == null || (mod & Flags.STATIC) != 0 || isVisible(encl));
263 }
265 //---------------- print forwarders ----------------//
267 /**
268 * Print error message, increment error count.
269 *
270 * @param msg message to print.
271 */
272 public void printError(String msg) {
273 if (silent)
274 return;
275 messager.printError(msg);
276 }
278 /**
279 * Print error message, increment error count.
280 *
281 * @param key selects message from resource
282 */
283 public void error(DocImpl doc, String key) {
284 if (silent)
285 return;
286 messager.error(doc==null ? null : doc.position(), key);
287 }
289 /**
290 * Print error message, increment error count.
291 *
292 * @param key selects message from resource
293 */
294 public void error(SourcePosition pos, String key) {
295 if (silent)
296 return;
297 messager.error(pos, key);
298 }
300 /**
301 * Print error message, increment error count.
302 *
303 * @param msg message to print.
304 */
305 public void printError(SourcePosition pos, String msg) {
306 if (silent)
307 return;
308 messager.printError(pos, msg);
309 }
311 /**
312 * Print error message, increment error count.
313 *
314 * @param key selects message from resource
315 * @param a1 first argument
316 */
317 public void error(DocImpl doc, String key, String a1) {
318 if (silent)
319 return;
320 messager.error(doc==null ? null : doc.position(), key, a1);
321 }
323 /**
324 * Print error message, increment error count.
325 *
326 * @param key selects message from resource
327 * @param a1 first argument
328 * @param a2 second argument
329 */
330 public void error(DocImpl doc, String key, String a1, String a2) {
331 if (silent)
332 return;
333 messager.error(doc==null ? null : doc.position(), key, a1, a2);
334 }
336 /**
337 * Print error message, increment error count.
338 *
339 * @param key selects message from resource
340 * @param a1 first argument
341 * @param a2 second argument
342 * @param a3 third argument
343 */
344 public void error(DocImpl doc, String key, String a1, String a2, String a3) {
345 if (silent)
346 return;
347 messager.error(doc==null ? null : doc.position(), key, a1, a2, a3);
348 }
350 /**
351 * Print warning message, increment warning count.
352 *
353 * @param msg message to print.
354 */
355 public void printWarning(String msg) {
356 if (silent)
357 return;
358 messager.printWarning(msg);
359 }
361 /**
362 * Print warning message, increment warning count.
363 *
364 * @param key selects message from resource
365 */
366 public void warning(DocImpl doc, String key) {
367 if (silent)
368 return;
369 messager.warning(doc==null ? null : doc.position(), key);
370 }
372 /**
373 * Print warning message, increment warning count.
374 *
375 * @param msg message to print.
376 */
377 public void printWarning(SourcePosition pos, String msg) {
378 if (silent)
379 return;
380 messager.printWarning(pos, msg);
381 }
383 /**
384 * Print warning message, increment warning count.
385 *
386 * @param key selects message from resource
387 * @param a1 first argument
388 */
389 public void warning(DocImpl doc, String key, String a1) {
390 if (silent)
391 return;
392 messager.warning(doc==null ? null : doc.position(), key, a1);
393 }
395 /**
396 * Print warning message, increment warning count.
397 *
398 * @param key selects message from resource
399 * @param a1 first argument
400 * @param a2 second argument
401 */
402 public void warning(DocImpl doc, String key, String a1, String a2) {
403 if (silent)
404 return;
405 messager.warning(doc==null ? null : doc.position(), key, a1, a2);
406 }
408 /**
409 * Print warning message, increment warning count.
410 *
411 * @param key selects message from resource
412 * @param a1 first argument
413 * @param a2 second argument
414 * @param a3 third argument
415 */
416 public void warning(DocImpl doc, String key, String a1, String a2, String a3) {
417 if (silent)
418 return;
419 messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3);
420 }
422 /**
423 * Print warning message, increment warning count.
424 *
425 * @param key selects message from resource
426 * @param a1 first argument
427 * @param a2 second argument
428 * @param a3 third argument
429 */
430 public void warning(DocImpl doc, String key, String a1, String a2, String a3,
431 String a4) {
432 if (silent)
433 return;
434 messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3, a4);
435 }
437 /**
438 * Print a message.
439 *
440 * @param msg message to print.
441 */
442 public void printNotice(String msg) {
443 if (silent || quiet)
444 return;
445 messager.printNotice(msg);
446 }
449 /**
450 * Print a message.
451 *
452 * @param key selects message from resource
453 */
454 public void notice(String key) {
455 if (silent || quiet)
456 return;
457 messager.notice(key);
458 }
460 /**
461 * Print a message.
462 *
463 * @param msg message to print.
464 */
465 public void printNotice(SourcePosition pos, String msg) {
466 if (silent || quiet)
467 return;
468 messager.printNotice(pos, msg);
469 }
471 /**
472 * Print a message.
473 *
474 * @param key selects message from resource
475 * @param a1 first argument
476 */
477 public void notice(String key, String a1) {
478 if (silent || quiet)
479 return;
480 messager.notice(key, a1);
481 }
483 /**
484 * Print a message.
485 *
486 * @param key selects message from resource
487 * @param a1 first argument
488 * @param a2 second argument
489 */
490 public void notice(String key, String a1, String a2) {
491 if (silent || quiet)
492 return;
493 messager.notice(key, a1, a2);
494 }
496 /**
497 * Print a message.
498 *
499 * @param key selects message from resource
500 * @param a1 first argument
501 * @param a2 second argument
502 * @param a3 third argument
503 */
504 public void notice(String key, String a1, String a2, String a3) {
505 if (silent || quiet)
506 return;
507 messager.notice(key, a1, a2, a3);
508 }
510 /**
511 * Exit, reporting errors and warnings.
512 */
513 public void exit() {
514 // Messager should be replaced by a more general
515 // compilation environment. This can probably
516 // subsume DocEnv as well.
517 messager.exit();
518 }
520 protected Map<PackageSymbol, PackageDocImpl> packageMap =
521 new HashMap<PackageSymbol, PackageDocImpl>();
522 /**
523 * Return the PackageDoc of this package symbol.
524 */
525 public PackageDocImpl getPackageDoc(PackageSymbol pack) {
526 PackageDocImpl result = packageMap.get(pack);
527 if (result != null) return result;
528 result = new PackageDocImpl(this, pack);
529 packageMap.put(pack, result);
530 return result;
531 }
533 /**
534 * Create the PackageDoc (or a subtype) for a package symbol.
535 */
536 void makePackageDoc(PackageSymbol pack, String docComment, JCCompilationUnit tree) {
537 PackageDocImpl result = packageMap.get(pack);
538 if (result != null) {
539 if (docComment != null) result.setRawCommentText(docComment);
540 if (tree != null) result.setTree(tree);
541 } else {
542 result = new PackageDocImpl(this, pack, docComment, tree);
543 packageMap.put(pack, result);
544 }
545 }
548 protected Map<ClassSymbol, ClassDocImpl> classMap =
549 new HashMap<ClassSymbol, ClassDocImpl>();
550 /**
551 * Return the ClassDoc (or a subtype) of this class symbol.
552 */
553 public ClassDocImpl getClassDoc(ClassSymbol clazz) {
554 ClassDocImpl result = classMap.get(clazz);
555 if (result != null) return result;
556 if (isAnnotationType(clazz)) {
557 result = new AnnotationTypeDocImpl(this, clazz);
558 } else {
559 result = new ClassDocImpl(this, clazz);
560 }
561 classMap.put(clazz, result);
562 return result;
563 }
565 /**
566 * Create the ClassDoc (or a subtype) for a class symbol.
567 */
568 protected void makeClassDoc(ClassSymbol clazz, String docComment, JCClassDecl tree, Position.LineMap lineMap) {
569 ClassDocImpl result = classMap.get(clazz);
570 if (result != null) {
571 if (docComment != null) result.setRawCommentText(docComment);
572 if (tree != null) result.setTree(tree);
573 return;
574 }
575 if (isAnnotationType(tree)) { // flags of clazz may not yet be set
576 result = new AnnotationTypeDocImpl(this, clazz, docComment, tree, lineMap);
577 } else {
578 result = new ClassDocImpl(this, clazz, docComment, tree, lineMap);
579 }
580 classMap.put(clazz, result);
581 }
583 protected static boolean isAnnotationType(ClassSymbol clazz) {
584 return ClassDocImpl.isAnnotationType(clazz);
585 }
587 protected static boolean isAnnotationType(JCClassDecl tree) {
588 return (tree.mods.flags & Flags.ANNOTATION) != 0;
589 }
591 protected Map<VarSymbol, FieldDocImpl> fieldMap =
592 new HashMap<VarSymbol, FieldDocImpl>();
593 /**
594 * Return the FieldDoc of this var symbol.
595 */
596 public FieldDocImpl getFieldDoc(VarSymbol var) {
597 FieldDocImpl result = fieldMap.get(var);
598 if (result != null) return result;
599 result = new FieldDocImpl(this, var);
600 fieldMap.put(var, result);
601 return result;
602 }
603 /**
604 * Create a FieldDoc for a var symbol.
605 */
606 protected void makeFieldDoc(VarSymbol var, String docComment, JCVariableDecl tree, Position.LineMap lineMap) {
607 FieldDocImpl result = fieldMap.get(var);
608 if (result != null) {
609 if (docComment != null) result.setRawCommentText(docComment);
610 if (tree != null) result.setTree(tree);
611 } else {
612 result = new FieldDocImpl(this, var, docComment, tree, lineMap);
613 fieldMap.put(var, result);
614 }
615 }
617 protected Map<MethodSymbol, ExecutableMemberDocImpl> methodMap =
618 new HashMap<MethodSymbol, ExecutableMemberDocImpl>();
619 /**
620 * Create a MethodDoc for this MethodSymbol.
621 * Should be called only on symbols representing methods.
622 */
623 protected void makeMethodDoc(MethodSymbol meth, String docComment,
624 JCMethodDecl tree, Position.LineMap lineMap) {
625 MethodDocImpl result = (MethodDocImpl)methodMap.get(meth);
626 if (result != null) {
627 if (docComment != null) result.setRawCommentText(docComment);
628 if (tree != null) result.setTree(tree);
629 } else {
630 result = new MethodDocImpl(this, meth, docComment, tree, lineMap);
631 methodMap.put(meth, result);
632 }
633 }
635 /**
636 * Return the MethodDoc for a MethodSymbol.
637 * Should be called only on symbols representing methods.
638 */
639 public MethodDocImpl getMethodDoc(MethodSymbol meth) {
640 assert !meth.isConstructor() : "not expecting a constructor symbol";
641 MethodDocImpl result = (MethodDocImpl)methodMap.get(meth);
642 if (result != null) return result;
643 result = new MethodDocImpl(this, meth);
644 methodMap.put(meth, result);
645 return result;
646 }
648 /**
649 * Create the ConstructorDoc for a MethodSymbol.
650 * Should be called only on symbols representing constructors.
651 */
652 protected void makeConstructorDoc(MethodSymbol meth, String docComment,
653 JCMethodDecl tree, Position.LineMap lineMap) {
654 ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth);
655 if (result != null) {
656 if (docComment != null) result.setRawCommentText(docComment);
657 if (tree != null) result.setTree(tree);
658 } else {
659 result = new ConstructorDocImpl(this, meth, docComment, tree, lineMap);
660 methodMap.put(meth, result);
661 }
662 }
664 /**
665 * Return the ConstructorDoc for a MethodSymbol.
666 * Should be called only on symbols representing constructors.
667 */
668 public ConstructorDocImpl getConstructorDoc(MethodSymbol meth) {
669 assert meth.isConstructor() : "expecting a constructor symbol";
670 ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth);
671 if (result != null) return result;
672 result = new ConstructorDocImpl(this, meth);
673 methodMap.put(meth, result);
674 return result;
675 }
677 /**
678 * Create the AnnotationTypeElementDoc for a MethodSymbol.
679 * Should be called only on symbols representing annotation type elements.
680 */
681 protected void makeAnnotationTypeElementDoc(MethodSymbol meth,
682 String docComment, JCMethodDecl tree, Position.LineMap lineMap) {
683 AnnotationTypeElementDocImpl result =
684 (AnnotationTypeElementDocImpl)methodMap.get(meth);
685 if (result != null) {
686 if (docComment != null) result.setRawCommentText(docComment);
687 if (tree != null) result.setTree(tree);
688 } else {
689 result =
690 new AnnotationTypeElementDocImpl(this, meth, docComment, tree, lineMap);
691 methodMap.put(meth, result);
692 }
693 }
695 /**
696 * Return the AnnotationTypeElementDoc for a MethodSymbol.
697 * Should be called only on symbols representing annotation type elements.
698 */
699 public AnnotationTypeElementDocImpl getAnnotationTypeElementDoc(
700 MethodSymbol meth) {
702 AnnotationTypeElementDocImpl result =
703 (AnnotationTypeElementDocImpl)methodMap.get(meth);
704 if (result != null) return result;
705 result = new AnnotationTypeElementDocImpl(this, meth);
706 methodMap.put(meth, result);
707 return result;
708 }
710 // private Map<ClassType, ParameterizedTypeImpl> parameterizedTypeMap =
711 // new HashMap<ClassType, ParameterizedTypeImpl>();
712 /**
713 * Return the ParameterizedType of this instantiation.
714 // * ### Could use Type.sameTypeAs() instead of equality matching in hashmap
715 // * ### to avoid some duplication.
716 */
717 ParameterizedTypeImpl getParameterizedType(ClassType t) {
718 return new ParameterizedTypeImpl(this, t);
719 // ParameterizedTypeImpl result = parameterizedTypeMap.get(t);
720 // if (result != null) return result;
721 // result = new ParameterizedTypeImpl(this, t);
722 // parameterizedTypeMap.put(t, result);
723 // return result;
724 }
726 /**
727 * Set the encoding.
728 */
729 public void setEncoding(String encoding) {
730 this.encoding = encoding;
731 }
733 /**
734 * Get the encoding.
735 */
736 public String getEncoding() {
737 return encoding;
738 }
740 /**
741 * Convert modifier bits from private coding used by
742 * the compiler to that of java.lang.reflect.Modifier.
743 */
744 static int translateModifiers(long flags) {
745 int result = 0;
746 if ((flags & Flags.ABSTRACT) != 0)
747 result |= Modifier.ABSTRACT;
748 if ((flags & Flags.FINAL) != 0)
749 result |= Modifier.FINAL;
750 if ((flags & Flags.INTERFACE) != 0)
751 result |= Modifier.INTERFACE;
752 if ((flags & Flags.NATIVE) != 0)
753 result |= Modifier.NATIVE;
754 if ((flags & Flags.PRIVATE) != 0)
755 result |= Modifier.PRIVATE;
756 if ((flags & Flags.PROTECTED) != 0)
757 result |= Modifier.PROTECTED;
758 if ((flags & Flags.PUBLIC) != 0)
759 result |= Modifier.PUBLIC;
760 if ((flags & Flags.STATIC) != 0)
761 result |= Modifier.STATIC;
762 if ((flags & Flags.SYNCHRONIZED) != 0)
763 result |= Modifier.SYNCHRONIZED;
764 if ((flags & Flags.TRANSIENT) != 0)
765 result |= Modifier.TRANSIENT;
766 if ((flags & Flags.VOLATILE) != 0)
767 result |= Modifier.VOLATILE;
768 return result;
769 }
770 }