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.code.Symbol;
31 import com.sun.tools.javac.code.Symbol.ClassSymbol;
32 import com.sun.tools.javac.code.Type;
33 import com.sun.tools.javac.code.Type.ClassType;
34 import com.sun.tools.javac.code.Type.TypeVar;
35 import com.sun.tools.javac.code.Type.ArrayType;
36 import com.sun.tools.javac.util.List;
38 import static com.sun.tools.javac.code.TypeTags.*;
41 public class TypeMaker {
43 public static com.sun.javadoc.Type getType(DocEnv env, Type t) {
44 return getType(env, t, true);
45 }
47 /**
48 * @param errToClassDoc if true, ERROR type results in a ClassDoc;
49 * false preserves legacy behavior
50 */
51 @SuppressWarnings("fallthrough")
52 public static com.sun.javadoc.Type getType(DocEnv env, Type t,
53 boolean errToClassDoc) {
54 if (env.legacyDoclet) {
55 t = env.types.erasure(t);
56 }
57 switch (t.tag) {
58 case CLASS:
59 if (ClassDocImpl.isGeneric((ClassSymbol)t.tsym)) {
60 return env.getParameterizedType((ClassType)t);
61 } else {
62 return env.getClassDoc((ClassSymbol)t.tsym);
63 }
64 case WILDCARD:
65 Type.WildcardType a = (Type.WildcardType)t;
66 return new WildcardTypeImpl(env, a);
67 case TYPEVAR: return new TypeVariableImpl(env, (TypeVar)t);
68 case ARRAY: return new ArrayTypeImpl(env, t);
69 case BYTE: return PrimitiveType.byteType;
70 case CHAR: return PrimitiveType.charType;
71 case SHORT: return PrimitiveType.shortType;
72 case INT: return PrimitiveType.intType;
73 case LONG: return PrimitiveType.longType;
74 case FLOAT: return PrimitiveType.floatType;
75 case DOUBLE: return PrimitiveType.doubleType;
76 case BOOLEAN: return PrimitiveType.booleanType;
77 case VOID: return PrimitiveType.voidType;
78 case ERROR:
79 if (errToClassDoc)
80 return env.getClassDoc((ClassSymbol)t.tsym);
81 // FALLTHRU
82 default:
83 return new PrimitiveType(t.tsym.getQualifiedName().toString());
84 }
85 }
87 /**
88 * Convert a list of javac types into an array of javadoc types.
89 */
90 public static com.sun.javadoc.Type[] getTypes(DocEnv env, List<Type> ts) {
91 return getTypes(env, ts, new com.sun.javadoc.Type[ts.length()]);
92 }
94 /**
95 * Like the above version, but use and return the array given.
96 */
97 public static com.sun.javadoc.Type[] getTypes(DocEnv env, List<Type> ts,
98 com.sun.javadoc.Type res[]) {
99 int i = 0;
100 for (Type t : ts) {
101 res[i++] = getType(env, t);
102 }
103 return res;
104 }
106 public static String getTypeName(Type t, boolean full) {
107 switch (t.tag) {
108 case ARRAY:
109 StringBuilder s = new StringBuilder();
110 while (t.tag == ARRAY) {
111 s.append("[]");
112 t = ((ArrayType)t).elemtype;
113 }
114 s.insert(0, getTypeName(t, full));
115 return s.toString();
116 case CLASS:
117 return ClassDocImpl.getClassName((ClassSymbol)t.tsym, full);
118 default:
119 return t.tsym.getQualifiedName().toString();
120 }
121 }
123 /**
124 * Return the string representation of a type use. Bounds of type
125 * variables are not included; bounds of wildcard types are.
126 * Class names are qualified if "full" is true.
127 */
128 static String getTypeString(DocEnv env, Type t, boolean full) {
129 switch (t.tag) {
130 case ARRAY:
131 StringBuilder s = new StringBuilder();
132 while (t.tag == ARRAY) {
133 s.append("[]");
134 t = env.types.elemtype(t);
135 }
136 s.insert(0, getTypeString(env, t, full));
137 return s.toString();
138 case CLASS:
139 return ParameterizedTypeImpl.
140 parameterizedTypeToString(env, (ClassType)t, full);
141 case WILDCARD:
142 Type.WildcardType a = (Type.WildcardType)t;
143 return WildcardTypeImpl.wildcardTypeToString(env, a, full);
144 default:
145 return t.tsym.getQualifiedName().toString();
146 }
147 }
149 /**
150 * Return the formal type parameters of a class or method as an
151 * angle-bracketed string. Each parameter is a type variable with
152 * optional bounds. Class names are qualified if "full" is true.
153 * Return "" if there are no type parameters or we're hiding generics.
154 */
155 static String typeParametersString(DocEnv env, Symbol sym, boolean full) {
156 if (env.legacyDoclet || sym.type.getTypeArguments().isEmpty()) {
157 return "";
158 }
159 StringBuilder s = new StringBuilder();
160 for (Type t : sym.type.getTypeArguments()) {
161 s.append(s.length() == 0 ? "<" : ", ");
162 s.append(TypeVariableImpl.typeVarToString(env, (TypeVar)t, full));
163 }
164 s.append(">");
165 return s.toString();
166 }
168 /**
169 * Return the actual type arguments of a parameterized type as an
170 * angle-bracketed string. Class name are qualified if "full" is true.
171 * Return "" if there are no type arguments or we're hiding generics.
172 */
173 static String typeArgumentsString(DocEnv env, ClassType cl, boolean full) {
174 if (env.legacyDoclet || cl.getTypeArguments().isEmpty()) {
175 return "";
176 }
177 StringBuilder s = new StringBuilder();
178 for (Type t : cl.getTypeArguments()) {
179 s.append(s.length() == 0 ? "<" : ", ");
180 s.append(getTypeString(env, t, full));
181 }
182 s.append(">");
183 return s.toString();
184 }
187 private static class ArrayTypeImpl implements com.sun.javadoc.Type {
189 Type arrayType;
191 DocEnv env;
193 ArrayTypeImpl(DocEnv env, Type arrayType) {
194 this.env = env;
195 this.arrayType = arrayType;
196 }
198 private com.sun.javadoc.Type skipArraysCache = null;
200 private com.sun.javadoc.Type skipArrays() {
201 if (skipArraysCache == null) {
202 Type t;
203 for (t = arrayType; t.tag == ARRAY; t = env.types.elemtype(t)) { }
204 skipArraysCache = TypeMaker.getType(env, t);
205 }
206 return skipArraysCache;
207 }
209 /**
210 * Return the type's dimension information, as a string.
211 * <p>
212 * For example, a two dimensional array of String returns '[][]'.
213 */
214 public String dimension() {
215 StringBuilder dimension = new StringBuilder();
216 for (Type t = arrayType; t.tag == ARRAY; t = env.types.elemtype(t)) {
217 dimension.append("[]");
218 }
219 return dimension.toString();
220 }
222 /**
223 * Return unqualified name of type excluding any dimension information.
224 * <p>
225 * For example, a two dimensional array of String returns 'String'.
226 */
227 public String typeName() {
228 return skipArrays().typeName();
229 }
231 /**
232 * Return qualified name of type excluding any dimension information.
233 *<p>
234 * For example, a two dimensional array of String
235 * returns 'java.lang.String'.
236 */
237 public String qualifiedTypeName() {
238 return skipArrays().qualifiedTypeName();
239 }
241 /**
242 * Return the simple name of this type excluding any dimension information.
243 */
244 public String simpleTypeName() {
245 return skipArrays().simpleTypeName();
246 }
248 /**
249 * Return this type as a class. Array dimensions are ignored.
250 *
251 * @return a ClassDocImpl if the type is a Class.
252 * Return null if it is a primitive type..
253 */
254 public ClassDoc asClassDoc() {
255 return skipArrays().asClassDoc();
256 }
258 /**
259 * Return this type as a <code>ParameterizedType</code> if it
260 * represents a parameterized type. Array dimensions are ignored.
261 */
262 public ParameterizedType asParameterizedType() {
263 return skipArrays().asParameterizedType();
264 }
266 /**
267 * Return this type as a <code>TypeVariable</code> if it represents
268 * a type variable. Array dimensions are ignored.
269 */
270 public TypeVariable asTypeVariable() {
271 return skipArrays().asTypeVariable();
272 }
274 /**
275 * Return null, as there are no arrays of wildcard types.
276 */
277 public WildcardType asWildcardType() {
278 return null;
279 }
281 /**
282 * Return this type as an <code>AnnotationTypeDoc</code> if it
283 * represents an annotation type. Array dimensions are ignored.
284 */
285 public AnnotationTypeDoc asAnnotationTypeDoc() {
286 return skipArrays().asAnnotationTypeDoc();
287 }
289 /**
290 * Return true if this is an array of a primitive type.
291 */
292 public boolean isPrimitive() {
293 return skipArrays().isPrimitive();
294 }
296 /**
297 * Return a string representation of the type.
298 *
299 * Return name of type including any dimension information.
300 * <p>
301 * For example, a two dimensional array of String returns
302 * <code>String[][]</code>.
303 *
304 * @return name of type including any dimension information.
305 */
306 @Override
307 public String toString() {
308 return qualifiedTypeName() + dimension();
309 }
310 }
311 }