Mon, 11 Feb 2013 21:26:06 +0530
8007915: Nashorn IR, codegen, parser packages and Context instance should be inaccessible to user code
Reviewed-by: lagergren, jlaskey, attila
1 /*
2 * Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.parser;
28 import static jdk.nashorn.internal.parser.TokenType.EOF;
29 import static jdk.nashorn.internal.parser.TokenType.EOL;
30 import static jdk.nashorn.internal.parser.TokenType.IDENT;
32 import jdk.nashorn.internal.ir.IdentNode;
33 import jdk.nashorn.internal.ir.LiteralNode;
34 import jdk.nashorn.internal.parser.Lexer.LexerToken;
35 import jdk.nashorn.internal.parser.Lexer.RegexToken;
36 import jdk.nashorn.internal.runtime.ECMAErrors;
37 import jdk.nashorn.internal.runtime.ErrorManager;
38 import jdk.nashorn.internal.runtime.JSErrorType;
39 import jdk.nashorn.internal.runtime.ParserException;
40 import jdk.nashorn.internal.runtime.RegExp;
41 import jdk.nashorn.internal.runtime.Source;
43 /**
44 * Base class for parsers.
45 */
46 public abstract class AbstractParser {
47 /** Source to parse. */
48 protected final Source source;
50 /** Error manager to report errors. */
51 protected final ErrorManager errors;
53 /** Stream of lex tokens to parse. */
54 protected TokenStream stream;
56 /** Index of current token. */
57 protected int k;
59 /** Descriptor of current token. */
60 protected long token;
62 /** Type of current token. */
63 protected TokenType type;
65 /** Type of last token. */
66 protected TokenType last;
68 /** Start position of current token. */
69 protected int start;
71 /** Finish position of previous token. */
72 protected int finish;
74 /** Current line number. */
75 protected int line;
77 /** Position of last EOL + 1. */
78 protected int linePosition;
80 /** Lexer used to scan source content. */
81 protected Lexer lexer;
83 /** Is this parser running under strict mode? */
84 protected boolean isStrictMode;
86 /**
87 * Construct a parser.
88 *
89 * @param source Source to parse.
90 * @param errors Error reporting manager.
91 * @param strict True if we are in strict mode
92 */
93 protected AbstractParser(final Source source, final ErrorManager errors, final boolean strict) {
94 this.source = source;
95 this.errors = errors;
96 this.k = -1;
97 this.token = Token.toDesc(EOL, 0, 1);
98 this.type = EOL;
99 this.last = EOL;
100 this.isStrictMode = strict;
101 }
103 /**
104 * Get the Source
105 *
106 * @return the Source
107 */
108 public Source getSource() {
109 return source;
110 }
112 /**
113 * Get the ith token.
114 *
115 * @param i Index of token.
116 *
117 * @return the token
118 */
119 protected final long getToken(final int i) {
120 // Make sure there are enough tokens available.
121 while (i > stream.last()) {
122 // If we need to buffer more for lookahead.
123 if (stream.isFull()) {
124 stream.grow();
125 }
127 // Get more tokens.
128 lexer.lexify();
129 }
131 return stream.get(i);
132 }
134 /**
135 * Return the tokenType of the ith token.
136 *
137 * @param i Index of token
138 *
139 * @return the token type
140 */
141 protected final TokenType T(final int i) {
142 // Get token descriptor and extract tokenType.
143 return Token.descType(getToken(i));
144 }
146 /**
147 * Seek next token that is not an EOL.
148 *
149 * @return tokenType of next token.
150 */
151 protected final TokenType next() {
152 do {
153 nextOrEOL();
154 } while (type == EOL);
156 return type;
157 }
159 /**
160 * Seek next token.
161 *
162 * @return tokenType of next token.
163 */
164 protected final TokenType nextOrEOL() {
165 // Capture last token tokenType.
166 last = type;
167 if (type != EOF) {
169 // Set up next token.
170 k++;
171 final long lastToken = token;
172 token = getToken(k);
173 type = Token.descType(token);
175 // do this before the start is changed below
176 if (last != EOL) {
177 finish = start + Token.descLength(lastToken);
178 }
180 if (type == EOL) {
181 line = Token.descLength(token);
182 linePosition = Token.descPosition(token);
183 } else {
184 start = Token.descPosition(token);
185 }
187 }
189 return type;
190 }
192 /**
193 * Get the message string for a message ID and arguments
194 *
195 * @param msgId The Message ID
196 * @param args The arguments
197 *
198 * @return The message string
199 */
200 protected static String message(final String msgId, final String... args) {
201 return ECMAErrors.getMessage("parser.error." + msgId, args);
202 }
204 /**
205 * Report an error.
206 *
207 * @param message Error message.
208 * @param errorToken Offending token.
209 */
210 protected final void error(final String message, final long errorToken) {
211 error(JSErrorType.SYNTAX_ERROR, message, errorToken);
212 }
214 /**
215 * Report an error.
216 *
217 * @param errorType The error type
218 * @param message Error message.
219 * @param errorToken Offending token.
220 */
221 protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
222 final int position = Token.descPosition(errorToken);
223 final int lineNum = source.getLine(position);
224 final int columnNum = source.getColumn(position);
225 final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
226 final ParserException exp = new ParserException(formatted, source, lineNum, columnNum, errorToken);
227 exp.setErrorType(errorType);
228 throw exp;
229 }
231 /**
232 * Report an error.
233 *
234 * @param message Error message.
235 */
236 protected final void error(final String message) {
237 error(JSErrorType.SYNTAX_ERROR, message);
238 }
240 /**
241 * Report an error.
242 *
243 * @param errorType The error type
244 * @param message Error message.
245 */
246 protected final void error(final JSErrorType errorType, final String message) {
247 // TODO - column needs to account for tabs.
248 final int position = Token.descPosition(token);
249 final int column = position - linePosition;
250 final String formatted = ErrorManager.format(message, source, line, column, token);
251 final ParserException exp = new ParserException(formatted, source, line, column, token);
252 exp.setErrorType(errorType);
253 throw exp;
254 }
256 /**
257 * Generate 'expected' message.
258 *
259 * @param expected Expected tokenType.
260 *
261 * @return the message string
262 */
263 protected final String expectMessage(final TokenType expected) {
264 final String tokenString = Token.toString(source, token, false);
265 String msg;
267 if (expected == null) {
268 msg = AbstractParser.message("expected.stmt", tokenString);
269 } else {
270 final String expectedName = expected.getNameOrType();
271 msg = AbstractParser.message("expected", expectedName, tokenString);
272 }
274 return msg;
275 }
277 /**
278 * Check next token and advance.
279 *
280 * @param expected Expected tokenType.
281 *
282 * @throws ParserException on unexpected token type
283 */
284 protected final void expect(final TokenType expected) throws ParserException {
285 if (type != expected) {
286 error(expectMessage(expected));
287 }
289 next();
290 }
292 /**
293 * Check next token, get its value and advance.
294 *
295 * @param expected Expected tokenType.
296 * @return The JavaScript value of the token
297 * @throws ParserException on unexpected token type
298 */
299 protected final Object expectValue(final TokenType expected) throws ParserException {
300 if (type != expected) {
301 error(expectMessage(expected));
302 }
304 final Object value = getValue();
306 next();
308 return value;
309 }
311 /**
312 * Get the value of the current token.
313 *
314 * @return JavaScript value of the token.
315 */
316 protected final Object getValue() {
317 return getValue(token);
318 }
320 /**
321 * Get the value of a specific token
322 *
323 * @param valueToken the token
324 *
325 * @return JavaScript value of the token
326 */
327 protected final Object getValue(final long valueToken) {
328 try {
329 return lexer.getValueOf(valueToken, isStrictMode);
330 } catch (final ParserException e) {
331 errors.error(e);
332 }
334 return null;
335 }
337 /**
338 * Certain future reserved words can be used as identifiers in
339 * non-strict mode. Check if the current token is one such.
340 *
341 * @return true if non strict mode identifier
342 */
343 protected final boolean isNonStrictModeIdent() {
344 return !isStrictMode && type.getKind() == TokenKind.FUTURESTRICT;
345 }
347 /**
348 * Get ident.
349 *
350 * @return Ident node.
351 */
352 protected final IdentNode getIdent() {
353 // Capture IDENT token.
354 long identToken = token;
356 if (isNonStrictModeIdent()) {
357 // Fake out identifier.
358 identToken = Token.recast(token, IDENT);
359 // Get IDENT.
360 final String ident = (String)getValue(identToken);
362 next();
364 // Create IDENT node.
365 return new IdentNode(source, identToken, finish, ident);
366 }
368 // Get IDENT.
369 final String ident = (String)expectValue(IDENT);
370 if (ident == null) {
371 return null;
372 }
373 // Create IDENT node.
374 return new IdentNode(source, identToken, finish, ident);
375 }
377 /**
378 * Check if current token is in identifier name
379 *
380 * @return true if current token is an identifier name
381 */
382 protected final boolean isIdentifierName() {
383 final TokenKind kind = type.getKind();
384 if (kind == TokenKind.KEYWORD || kind == TokenKind.FUTURE || kind == TokenKind.FUTURESTRICT) {
385 return true;
386 }
387 // Fake out identifier.
388 final long identToken = Token.recast(token, IDENT);
389 // Get IDENT.
390 final String ident = (String)getValue(identToken);
391 return !ident.isEmpty() && Character.isJavaIdentifierStart(ident.charAt(0));
392 }
394 /**
395 * Create an IdentNode from the current token
396 *
397 * @return an IdentNode representing the current token
398 */
399 protected final IdentNode getIdentifierName() {
400 if (type == IDENT) {
401 return getIdent();
402 } else if (isIdentifierName()) {
403 // Fake out identifier.
404 final long identToken = Token.recast(token, IDENT);
405 // Get IDENT.
406 final String ident = (String)getValue(identToken);
407 next();
408 // Create IDENT node.
409 return new IdentNode(source, identToken, finish, ident);
410 } else {
411 expect(IDENT);
412 return null;
413 }
414 }
416 /**
417 * Create a LiteralNode from the current token
418 *
419 * @return LiteralNode representing the current token
420 * @throws ParserException if any literals fails to parse
421 */
422 protected final LiteralNode<?> getLiteral() throws ParserException {
423 // Capture LITERAL token.
424 final long literalToken = token;
426 // Create literal node.
427 final Object value = getValue();
428 // Advance to have a correct finish
429 next();
431 LiteralNode<?> node = null;
433 if (value == null) {
434 node = LiteralNode.newInstance(source, literalToken, finish);
435 } else if (value instanceof Number) {
436 node = LiteralNode.newInstance(source, literalToken, finish, (Number)value);
437 } else if (value instanceof String) {
438 node = LiteralNode.newInstance(source, literalToken, finish, (String)value);
439 } else if (value instanceof LexerToken) {
440 if (value instanceof RegexToken) {
441 final RegexToken regex = (RegexToken)value;
442 try {
443 RegExp.validate(regex.getExpression(), regex.getOptions());
444 } catch (final ParserException e) {
445 error(e.getMessage());
446 }
447 }
448 node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
449 } else {
450 assert false : "unknown type for LiteralNode: " + value.getClass();
451 }
453 return node;
454 }
455 }