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) 1999, 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.javac.parser;
28 import java.util.Locale;
30 import com.sun.tools.javac.api.Formattable;
31 import com.sun.tools.javac.api.Messages;
32 import com.sun.tools.javac.parser.Tokens.Token.Tag;
33 import com.sun.tools.javac.util.List;
34 import com.sun.tools.javac.util.Name;
35 import com.sun.tools.javac.util.Context;
36 import com.sun.tools.javac.util.ListBuffer;
37 import com.sun.tools.javac.util.Names;
39 /** A class that defines codes/utilities for Java source tokens
40 * returned from lexical analysis.
41 *
42 * <p><b>This is NOT part of any supported API.
43 * If you write code that depends on this, you do so at your own risk.
44 * This code and its internal interfaces are subject to change or
45 * deletion without notice.</b>
46 */
47 public class Tokens {
49 private final Names names;
51 /**
52 * Keyword array. Maps name indices to Token.
53 */
54 private final TokenKind[] key;
56 /** The number of the last entered keyword.
57 */
58 private int maxKey = 0;
60 /** The names of all tokens.
61 */
62 private Name[] tokenName = new Name[TokenKind.values().length];
64 public static final Context.Key<Tokens> tokensKey =
65 new Context.Key<Tokens>();
67 public static Tokens instance(Context context) {
68 Tokens instance = context.get(tokensKey);
69 if (instance == null)
70 instance = new Tokens(context);
71 return instance;
72 }
74 protected Tokens(Context context) {
75 context.put(tokensKey, this);
76 names = Names.instance(context);
78 for (TokenKind t : TokenKind.values()) {
79 if (t.name != null)
80 enterKeyword(t.name, t);
81 else
82 tokenName[t.ordinal()] = null;
83 }
85 key = new TokenKind[maxKey+1];
86 for (int i = 0; i <= maxKey; i++) key[i] = TokenKind.IDENTIFIER;
87 for (TokenKind t : TokenKind.values()) {
88 if (t.name != null)
89 key[tokenName[t.ordinal()].getIndex()] = t;
90 }
91 }
93 private void enterKeyword(String s, TokenKind token) {
94 Name n = names.fromString(s);
95 tokenName[token.ordinal()] = n;
96 if (n.getIndex() > maxKey) maxKey = n.getIndex();
97 }
99 /**
100 * Create a new token given a name; if the name corresponds to a token name,
101 * a new token of the corresponding kind is returned; otherwise, an
102 * identifier token is returned.
103 */
104 TokenKind lookupKind(Name name) {
105 return (name.getIndex() > maxKey) ? TokenKind.IDENTIFIER : key[name.getIndex()];
106 }
108 TokenKind lookupKind(String name) {
109 return lookupKind(names.fromString(name));
110 }
112 /**
113 * This enum defines all tokens used by the javac scanner. A token is
114 * optionally associated with a name.
115 */
116 public enum TokenKind implements Formattable {
117 EOF(),
118 ERROR(),
119 IDENTIFIER(Tag.NAMED),
120 ABSTRACT("abstract"),
121 ASSERT("assert", Tag.NAMED),
122 BOOLEAN("boolean", Tag.NAMED),
123 BREAK("break"),
124 BYTE("byte", Tag.NAMED),
125 CASE("case"),
126 CATCH("catch"),
127 CHAR("char", Tag.NAMED),
128 CLASS("class"),
129 CONST("const"),
130 CONTINUE("continue"),
131 DEFAULT("default"),
132 DO("do"),
133 DOUBLE("double", Tag.NAMED),
134 ELSE("else"),
135 ENUM("enum", Tag.NAMED),
136 EXTENDS("extends"),
137 FINAL("final"),
138 FINALLY("finally"),
139 FLOAT("float", Tag.NAMED),
140 FOR("for"),
141 GOTO("goto"),
142 IF("if"),
143 IMPLEMENTS("implements"),
144 IMPORT("import"),
145 INSTANCEOF("instanceof"),
146 INT("int", Tag.NAMED),
147 INTERFACE("interface"),
148 LONG("long", Tag.NAMED),
149 NATIVE("native"),
150 NEW("new"),
151 PACKAGE("package"),
152 PRIVATE("private"),
153 PROTECTED("protected"),
154 PUBLIC("public"),
155 RETURN("return"),
156 SHORT("short", Tag.NAMED),
157 STATIC("static"),
158 STRICTFP("strictfp"),
159 SUPER("super", Tag.NAMED),
160 SWITCH("switch"),
161 SYNCHRONIZED("synchronized"),
162 THIS("this", Tag.NAMED),
163 THROW("throw"),
164 THROWS("throws"),
165 TRANSIENT("transient"),
166 TRY("try"),
167 VOID("void", Tag.NAMED),
168 VOLATILE("volatile"),
169 WHILE("while"),
170 INTLITERAL(Tag.NUMERIC),
171 LONGLITERAL(Tag.NUMERIC),
172 FLOATLITERAL(Tag.NUMERIC),
173 DOUBLELITERAL(Tag.NUMERIC),
174 CHARLITERAL(Tag.NUMERIC),
175 STRINGLITERAL(Tag.STRING),
176 TRUE("true", Tag.NAMED),
177 FALSE("false", Tag.NAMED),
178 NULL("null", Tag.NAMED),
179 ARROW("->"),
180 HASH("#"),
181 LPAREN("("),
182 RPAREN(")"),
183 LBRACE("{"),
184 RBRACE("}"),
185 LBRACKET("["),
186 RBRACKET("]"),
187 SEMI(";"),
188 COMMA(","),
189 DOT("."),
190 ELLIPSIS("..."),
191 EQ("="),
192 GT(">"),
193 LT("<"),
194 BANG("!"),
195 TILDE("~"),
196 QUES("?"),
197 COLON(":"),
198 EQEQ("=="),
199 LTEQ("<="),
200 GTEQ(">="),
201 BANGEQ("!="),
202 AMPAMP("&&"),
203 BARBAR("||"),
204 PLUSPLUS("++"),
205 SUBSUB("--"),
206 PLUS("+"),
207 SUB("-"),
208 STAR("*"),
209 SLASH("/"),
210 AMP("&"),
211 BAR("|"),
212 CARET("^"),
213 PERCENT("%"),
214 LTLT("<<"),
215 GTGT(">>"),
216 GTGTGT(">>>"),
217 PLUSEQ("+="),
218 SUBEQ("-="),
219 STAREQ("*="),
220 SLASHEQ("/="),
221 AMPEQ("&="),
222 BAREQ("|="),
223 CARETEQ("^="),
224 PERCENTEQ("%="),
225 LTLTEQ("<<="),
226 GTGTEQ(">>="),
227 GTGTGTEQ(">>>="),
228 MONKEYS_AT("@"),
229 CUSTOM;
231 public final String name;
232 public final Tag tag;
234 TokenKind() {
235 this(null, Tag.DEFAULT);
236 }
238 TokenKind(String name) {
239 this(name, Tag.DEFAULT);
240 }
242 TokenKind(Tag tag) {
243 this(null, tag);
244 }
246 TokenKind(String name, Tag tag) {
247 this.name = name;
248 this.tag = tag;
249 }
251 public String toString() {
252 switch (this) {
253 case IDENTIFIER:
254 return "token.identifier";
255 case CHARLITERAL:
256 return "token.character";
257 case STRINGLITERAL:
258 return "token.string";
259 case INTLITERAL:
260 return "token.integer";
261 case LONGLITERAL:
262 return "token.long-integer";
263 case FLOATLITERAL:
264 return "token.float";
265 case DOUBLELITERAL:
266 return "token.double";
267 case ERROR:
268 return "token.bad-symbol";
269 case EOF:
270 return "token.end-of-input";
271 case DOT: case COMMA: case SEMI: case LPAREN: case RPAREN:
272 case LBRACKET: case RBRACKET: case LBRACE: case RBRACE:
273 return "'" + name + "'";
274 default:
275 return name;
276 }
277 }
279 public String getKind() {
280 return "Token";
281 }
283 public String toString(Locale locale, Messages messages) {
284 return name != null ? toString() : messages.getLocalizedString(locale, "compiler.misc." + toString());
285 }
286 }
288 public interface Comment {
290 enum CommentStyle {
291 LINE,
292 BLOCK,
293 JAVADOC,
294 }
296 String getText();
297 CommentStyle getStyle();
298 boolean isDeprecated();
299 }
301 /**
302 * This is the class representing a javac token. Each token has several fields
303 * that are set by the javac lexer (i.e. start/end position, string value, etc).
304 */
305 public static class Token {
307 /** tags constants **/
308 enum Tag {
309 DEFAULT,
310 NAMED,
311 STRING,
312 NUMERIC;
313 }
315 /** The token kind */
316 public final TokenKind kind;
318 /** The start position of this token */
319 public final int pos;
321 /** The end position of this token */
322 public final int endPos;
324 /** Comment reader associated with this token */
325 public final List<Comment> comments;
327 Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
328 this.kind = kind;
329 this.pos = pos;
330 this.endPos = endPos;
331 this.comments = comments;
332 checkKind();
333 }
335 Token[] split(Tokens tokens) {
336 if (kind.name.length() < 2 || kind.tag != Tag.DEFAULT) {
337 throw new AssertionError("Cant split" + kind);
338 }
340 TokenKind t1 = tokens.lookupKind(kind.name.substring(0, 1));
341 TokenKind t2 = tokens.lookupKind(kind.name.substring(1));
343 if (t1 == null || t2 == null) {
344 throw new AssertionError("Cant split - bad subtokens");
345 }
346 return new Token[] {
347 new Token(t1, pos, pos + t1.name.length(), comments),
348 new Token(t2, pos + t1.name.length(), endPos, null)
349 };
350 }
352 protected void checkKind() {
353 if (kind.tag != Tag.DEFAULT) {
354 throw new AssertionError("Bad token kind - expected " + Tag.STRING);
355 }
356 }
358 public Name name() {
359 throw new UnsupportedOperationException();
360 }
362 public String stringVal() {
363 throw new UnsupportedOperationException();
364 }
366 public int radix() {
367 throw new UnsupportedOperationException();
368 }
370 /**
371 * Preserve classic semantics - if multiple javadocs are found on the token
372 * the last one is returned
373 */
374 public String comment(Comment.CommentStyle style) {
375 List<Comment> readers = getReaders(Comment.CommentStyle.JAVADOC);
376 return readers.isEmpty() ?
377 null :
378 readers.head.getText();
379 }
381 /**
382 * Preserve classic semantics - deprecated should be set if at least one
383 * javadoc comment attached to this token contains the '@deprecated' string
384 */
385 public boolean deprecatedFlag() {
386 for (Comment r : getReaders(Comment.CommentStyle.JAVADOC)) {
387 if (r.isDeprecated()) {
388 return true;
389 }
390 }
391 return false;
392 }
394 private List<Comment> getReaders(Comment.CommentStyle style) {
395 if (comments == null) {
396 return List.nil();
397 } else {
398 ListBuffer<Comment> buf = ListBuffer.lb();
399 for (Comment r : comments) {
400 if (r.getStyle() == style) {
401 buf.add(r);
402 }
403 }
404 return buf.toList();
405 }
406 }
407 }
409 final static class NamedToken extends Token {
410 /** The name of this token */
411 public final Name name;
413 public NamedToken(TokenKind kind, int pos, int endPos, Name name, List<Comment> comments) {
414 super(kind, pos, endPos, comments);
415 this.name = name;
416 }
418 protected void checkKind() {
419 if (kind.tag != Tag.NAMED) {
420 throw new AssertionError("Bad token kind - expected " + Tag.NAMED);
421 }
422 }
424 @Override
425 public Name name() {
426 return name;
427 }
428 }
430 static class StringToken extends Token {
431 /** The string value of this token */
432 public final String stringVal;
434 public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
435 super(kind, pos, endPos, comments);
436 this.stringVal = stringVal;
437 }
439 protected void checkKind() {
440 if (kind.tag != Tag.STRING) {
441 throw new AssertionError("Bad token kind - expected " + Tag.STRING);
442 }
443 }
445 @Override
446 public String stringVal() {
447 return stringVal;
448 }
449 }
451 final static class NumericToken extends StringToken {
452 /** The 'radix' value of this token */
453 public final int radix;
455 public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
456 super(kind, pos, endPos, stringVal, comments);
457 this.radix = radix;
458 }
460 protected void checkKind() {
461 if (kind.tag != Tag.NUMERIC) {
462 throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC);
463 }
464 }
466 @Override
467 public int radix() {
468 return radix;
469 }
470 }
472 public static final Token DUMMY =
473 new Token(TokenKind.ERROR, 0, 0, null);
474 }