blob: e05f903772e6147fad2da97ef808b06d80335787 [file] [log] [blame]
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001// Copyright 2010 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "../include/v8stdint.h"
29#include "unicode.h"
30#include "globals.h"
31#include "checks.h"
32#include "allocation.h"
33#include "utils.h"
34#include "list.h"
35#include "scanner-base.h"
36#include "preparse-data.h"
37#include "preparser.h"
38
39namespace v8 {
40namespace preparser {
41
42// Preparsing checks a JavaScript program and emits preparse-data that helps
43// a later parsing to be faster.
44// See preparser-data.h for the data.
45
46// The PreParser checks that the syntax follows the grammar for JavaScript,
47// and collects some information about the program along the way.
48// The grammar check is only performed in order to understand the program
49// sufficiently to deduce some information about it, that can be used
50// to speed up later parsing. Finding errors is not the goal of pre-parsing,
51// rather it is to speed up properly written and correct programs.
52// That means that contextual checks (like a label being declared where
53// it is used) are generally omitted.
54
55namespace i = ::v8::internal;
56
57#define CHECK_OK ok); \
58 if (!*ok) return -1; \
59 ((void)0
60#define DUMMY ) // to make indentation work
61#undef DUMMY
62
63
64void PreParser::ReportUnexpectedToken(i::Token::Value token) {
65 // We don't report stack overflows here, to avoid increasing the
66 // stack depth even further. Instead we report it after parsing is
67 // over, in ParseProgram.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000068 if (token == i::Token::ILLEGAL && stack_overflow_) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +000069 return;
70 }
71 i::JavaScriptScanner::Location source_location = scanner_->location();
72
73 // Four of the tokens are treated specially
74 switch (token) {
75 case i::Token::EOS:
76 return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
77 "unexpected_eos", NULL);
78 case i::Token::NUMBER:
79 return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
80 "unexpected_token_number", NULL);
81 case i::Token::STRING:
82 return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
83 "unexpected_token_string", NULL);
84 case i::Token::IDENTIFIER:
85 return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
86 "unexpected_token_identifier", NULL);
87 default:
88 const char* name = i::Token::String(token);
89 ReportMessageAt(source_location.beg_pos, source_location.end_pos,
90 "unexpected_token", name);
91 }
92}
93
94
kasperl@chromium.orga5551262010-12-07 12:49:48 +000095PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
96 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +000097 // SourceElements ::
98 // (Statement)* <end_token>
99
100 while (peek() != end_token) {
101 ParseStatement(CHECK_OK);
102 }
103 return kUnknownSourceElements;
104}
105
106
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000107PreParser::Statement PreParser::ParseStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000108 // Statement ::
109 // Block
110 // VariableStatement
111 // EmptyStatement
112 // ExpressionStatement
113 // IfStatement
114 // IterationStatement
115 // ContinueStatement
116 // BreakStatement
117 // ReturnStatement
118 // WithStatement
119 // LabelledStatement
120 // SwitchStatement
121 // ThrowStatement
122 // TryStatement
123 // DebuggerStatement
124
125 // Note: Since labels can only be used by 'break' and 'continue'
126 // statements, which themselves are only valid within blocks,
127 // iterations or 'switch' statements (i.e., BreakableStatements),
128 // labels can be simply ignored in all other cases; except for
129 // trivial labeled break statements 'label: break label' which is
130 // parsed into an empty statement.
131
132 // Keep the source position of the statement
133 switch (peek()) {
134 case i::Token::LBRACE:
135 return ParseBlock(ok);
136
137 case i::Token::CONST:
138 case i::Token::VAR:
139 return ParseVariableStatement(ok);
140
141 case i::Token::SEMICOLON:
142 Next();
143 return kUnknownStatement;
144
145 case i::Token::IF:
146 return ParseIfStatement(ok);
147
148 case i::Token::DO:
149 return ParseDoWhileStatement(ok);
150
151 case i::Token::WHILE:
152 return ParseWhileStatement(ok);
153
154 case i::Token::FOR:
155 return ParseForStatement(ok);
156
157 case i::Token::CONTINUE:
158 return ParseContinueStatement(ok);
159
160 case i::Token::BREAK:
161 return ParseBreakStatement(ok);
162
163 case i::Token::RETURN:
164 return ParseReturnStatement(ok);
165
166 case i::Token::WITH:
167 return ParseWithStatement(ok);
168
169 case i::Token::SWITCH:
170 return ParseSwitchStatement(ok);
171
172 case i::Token::THROW:
173 return ParseThrowStatement(ok);
174
175 case i::Token::TRY:
176 return ParseTryStatement(ok);
177
178 case i::Token::FUNCTION:
179 return ParseFunctionDeclaration(ok);
180
181 case i::Token::NATIVE:
182 return ParseNativeDeclaration(ok);
183
184 case i::Token::DEBUGGER:
185 return ParseDebuggerStatement(ok);
186
187 default:
188 return ParseExpressionOrLabelledStatement(ok);
189 }
190}
191
192
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000193PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000194 // FunctionDeclaration ::
195 // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
196 Expect(i::Token::FUNCTION, CHECK_OK);
197 ParseIdentifier(CHECK_OK);
198 ParseFunctionLiteral(CHECK_OK);
199 return kUnknownStatement;
200}
201
202
203// Language extension which is only enabled for source files loaded
204// through the API's extension mechanism. A native function
205// declaration is resolved by looking up the function through a
206// callback provided by the extension.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000207PreParser::Statement PreParser::ParseNativeDeclaration(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000208 Expect(i::Token::NATIVE, CHECK_OK);
209 Expect(i::Token::FUNCTION, CHECK_OK);
210 ParseIdentifier(CHECK_OK);
211 Expect(i::Token::LPAREN, CHECK_OK);
212 bool done = (peek() == i::Token::RPAREN);
213 while (!done) {
214 ParseIdentifier(CHECK_OK);
215 done = (peek() == i::Token::RPAREN);
216 if (!done) {
217 Expect(i::Token::COMMA, CHECK_OK);
218 }
219 }
220 Expect(i::Token::RPAREN, CHECK_OK);
221 Expect(i::Token::SEMICOLON, CHECK_OK);
222 return kUnknownStatement;
223}
224
225
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000226PreParser::Statement PreParser::ParseBlock(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000227 // Block ::
228 // '{' Statement* '}'
229
230 // Note that a Block does not introduce a new execution scope!
231 // (ECMA-262, 3rd, 12.2)
232 //
233 Expect(i::Token::LBRACE, CHECK_OK);
234 while (peek() != i::Token::RBRACE) {
235 ParseStatement(CHECK_OK);
236 }
237 Expect(i::Token::RBRACE, CHECK_OK);
238 return kUnknownStatement;
239}
240
241
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000242PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000243 // VariableStatement ::
244 // VariableDeclarations ';'
245
246 Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK);
247 ExpectSemicolon(CHECK_OK);
248 return result;
249}
250
251
252// If the variable declaration declares exactly one non-const
253// variable, then *var is set to that variable. In all other cases,
254// *var is untouched; in particular, it is the caller's responsibility
255// to initialize it properly. This mechanism is also used for the parsing
256// of 'for-in' loops.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000257PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
258 int* num_decl,
259 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000260 // VariableDeclarations ::
261 // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
262
263 if (peek() == i::Token::VAR) {
264 Consume(i::Token::VAR);
265 } else if (peek() == i::Token::CONST) {
266 Consume(i::Token::CONST);
267 } else {
268 *ok = false;
269 return 0;
270 }
271
272 // The scope of a variable/const declared anywhere inside a function
273 // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). .
274 int nvars = 0; // the number of variables declared
275 do {
276 // Parse variable name.
277 if (nvars > 0) Consume(i::Token::COMMA);
278 ParseIdentifier(CHECK_OK);
279 nvars++;
280 if (peek() == i::Token::ASSIGN) {
281 Expect(i::Token::ASSIGN, CHECK_OK);
282 ParseAssignmentExpression(accept_IN, CHECK_OK);
283 }
284 } while (peek() == i::Token::COMMA);
285
286 if (num_decl != NULL) *num_decl = nvars;
287 return kUnknownStatement;
288}
289
290
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000291PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000292 bool* ok) {
293 // ExpressionStatement | LabelledStatement ::
294 // Expression ';'
295 // Identifier ':' Statement
296
297 Expression expr = ParseExpression(true, CHECK_OK);
298 if (peek() == i::Token::COLON && expr == kIdentifierExpression) {
299 Consume(i::Token::COLON);
300 return ParseStatement(ok);
301 }
302 // Parsed expression statement.
303 ExpectSemicolon(CHECK_OK);
304 return kUnknownStatement;
305}
306
307
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000308PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000309 // IfStatement ::
310 // 'if' '(' Expression ')' Statement ('else' Statement)?
311
312 Expect(i::Token::IF, CHECK_OK);
313 Expect(i::Token::LPAREN, CHECK_OK);
314 ParseExpression(true, CHECK_OK);
315 Expect(i::Token::RPAREN, CHECK_OK);
316 ParseStatement(CHECK_OK);
317 if (peek() == i::Token::ELSE) {
318 Next();
319 ParseStatement(CHECK_OK);
320 }
321 return kUnknownStatement;
322}
323
324
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000325PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000326 // ContinueStatement ::
327 // 'continue' [no line terminator] Identifier? ';'
328
329 Expect(i::Token::CONTINUE, CHECK_OK);
330 i::Token::Value tok = peek();
331 if (!scanner_->has_line_terminator_before_next() &&
332 tok != i::Token::SEMICOLON &&
333 tok != i::Token::RBRACE &&
334 tok != i::Token::EOS) {
335 ParseIdentifier(CHECK_OK);
336 }
337 ExpectSemicolon(CHECK_OK);
338 return kUnknownStatement;
339}
340
341
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000342PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000343 // BreakStatement ::
344 // 'break' [no line terminator] Identifier? ';'
345
346 Expect(i::Token::BREAK, CHECK_OK);
347 i::Token::Value tok = peek();
348 if (!scanner_->has_line_terminator_before_next() &&
349 tok != i::Token::SEMICOLON &&
350 tok != i::Token::RBRACE &&
351 tok != i::Token::EOS) {
352 ParseIdentifier(CHECK_OK);
353 }
354 ExpectSemicolon(CHECK_OK);
355 return kUnknownStatement;
356}
357
358
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000359PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000360 // ReturnStatement ::
361 // 'return' [no line terminator] Expression? ';'
362
363 // Consume the return token. It is necessary to do the before
364 // reporting any errors on it, because of the way errors are
365 // reported (underlining).
366 Expect(i::Token::RETURN, CHECK_OK);
367
368 // An ECMAScript program is considered syntactically incorrect if it
369 // contains a return statement that is not within the body of a
370 // function. See ECMA-262, section 12.9, page 67.
371 // This is not handled during preparsing.
372
373 i::Token::Value tok = peek();
374 if (!scanner_->has_line_terminator_before_next() &&
375 tok != i::Token::SEMICOLON &&
376 tok != i::Token::RBRACE &&
377 tok != i::Token::EOS) {
378 ParseExpression(true, CHECK_OK);
379 }
380 ExpectSemicolon(CHECK_OK);
381 return kUnknownStatement;
382}
383
384
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000385PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000386 // WithStatement ::
387 // 'with' '(' Expression ')' Statement
388 Expect(i::Token::WITH, CHECK_OK);
389 Expect(i::Token::LPAREN, CHECK_OK);
390 ParseExpression(true, CHECK_OK);
391 Expect(i::Token::RPAREN, CHECK_OK);
392
393 scope_->EnterWith();
394 ParseStatement(CHECK_OK);
395 scope_->LeaveWith();
396 return kUnknownStatement;
397}
398
399
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000400PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000401 // SwitchStatement ::
402 // 'switch' '(' Expression ')' '{' CaseClause* '}'
403
404 Expect(i::Token::SWITCH, CHECK_OK);
405 Expect(i::Token::LPAREN, CHECK_OK);
406 ParseExpression(true, CHECK_OK);
407 Expect(i::Token::RPAREN, CHECK_OK);
408
409 Expect(i::Token::LBRACE, CHECK_OK);
410 i::Token::Value token = peek();
411 while (token != i::Token::RBRACE) {
412 if (token == i::Token::CASE) {
413 Expect(i::Token::CASE, CHECK_OK);
414 ParseExpression(true, CHECK_OK);
415 Expect(i::Token::COLON, CHECK_OK);
416 } else if (token == i::Token::DEFAULT) {
417 Expect(i::Token::DEFAULT, CHECK_OK);
418 Expect(i::Token::COLON, CHECK_OK);
419 } else {
420 ParseStatement(CHECK_OK);
421 }
422 token = peek();
423 }
424 Expect(i::Token::RBRACE, CHECK_OK);
425
426 return kUnknownStatement;
427}
428
429
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000430PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000431 // DoStatement ::
432 // 'do' Statement 'while' '(' Expression ')' ';'
433
434 Expect(i::Token::DO, CHECK_OK);
435 ParseStatement(CHECK_OK);
436 Expect(i::Token::WHILE, CHECK_OK);
437 Expect(i::Token::LPAREN, CHECK_OK);
438 ParseExpression(true, CHECK_OK);
439 Expect(i::Token::RPAREN, CHECK_OK);
440 return kUnknownStatement;
441}
442
443
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000444PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000445 // WhileStatement ::
446 // 'while' '(' Expression ')' Statement
447
448 Expect(i::Token::WHILE, CHECK_OK);
449 Expect(i::Token::LPAREN, CHECK_OK);
450 ParseExpression(true, CHECK_OK);
451 Expect(i::Token::RPAREN, CHECK_OK);
452 ParseStatement(CHECK_OK);
453 return kUnknownStatement;
454}
455
456
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000457PreParser::Statement PreParser::ParseForStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000458 // ForStatement ::
459 // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
460
461 Expect(i::Token::FOR, CHECK_OK);
462 Expect(i::Token::LPAREN, CHECK_OK);
463 if (peek() != i::Token::SEMICOLON) {
464 if (peek() == i::Token::VAR || peek() == i::Token::CONST) {
465 int decl_count;
466 ParseVariableDeclarations(false, &decl_count, CHECK_OK);
467 if (peek() == i::Token::IN && decl_count == 1) {
468 Expect(i::Token::IN, CHECK_OK);
469 ParseExpression(true, CHECK_OK);
470 Expect(i::Token::RPAREN, CHECK_OK);
471
472 ParseStatement(CHECK_OK);
473 return kUnknownStatement;
474 }
475 } else {
476 ParseExpression(false, CHECK_OK);
477 if (peek() == i::Token::IN) {
478 Expect(i::Token::IN, CHECK_OK);
479 ParseExpression(true, CHECK_OK);
480 Expect(i::Token::RPAREN, CHECK_OK);
481
482 ParseStatement(CHECK_OK);
483 return kUnknownStatement;
484 }
485 }
486 }
487
488 // Parsed initializer at this point.
489 Expect(i::Token::SEMICOLON, CHECK_OK);
490
491 if (peek() != i::Token::SEMICOLON) {
492 ParseExpression(true, CHECK_OK);
493 }
494 Expect(i::Token::SEMICOLON, CHECK_OK);
495
496 if (peek() != i::Token::RPAREN) {
497 ParseExpression(true, CHECK_OK);
498 }
499 Expect(i::Token::RPAREN, CHECK_OK);
500
501 ParseStatement(CHECK_OK);
502 return kUnknownStatement;
503}
504
505
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000506PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000507 // ThrowStatement ::
508 // 'throw' [no line terminator] Expression ';'
509
510 Expect(i::Token::THROW, CHECK_OK);
511 if (scanner_->has_line_terminator_before_next()) {
512 i::JavaScriptScanner::Location pos = scanner_->location();
513 ReportMessageAt(pos.beg_pos, pos.end_pos,
514 "newline_after_throw", NULL);
515 *ok = false;
516 return kUnknownStatement;
517 }
518 ParseExpression(true, CHECK_OK);
519 ExpectSemicolon(CHECK_OK);
520
521 return kUnknownStatement;
522}
523
524
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000525PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000526 // TryStatement ::
527 // 'try' Block Catch
528 // 'try' Block Finally
529 // 'try' Block Catch Finally
530 //
531 // Catch ::
532 // 'catch' '(' Identifier ')' Block
533 //
534 // Finally ::
535 // 'finally' Block
536
537 // In preparsing, allow any number of catch/finally blocks, including zero
538 // of both.
539
540 Expect(i::Token::TRY, CHECK_OK);
541
542 ParseBlock(CHECK_OK);
543
544 bool catch_or_finally_seen = false;
545 if (peek() == i::Token::CATCH) {
546 Consume(i::Token::CATCH);
547 Expect(i::Token::LPAREN, CHECK_OK);
548 ParseIdentifier(CHECK_OK);
549 Expect(i::Token::RPAREN, CHECK_OK);
550 scope_->EnterWith();
551 ParseBlock(ok);
552 scope_->LeaveWith();
553 if (!*ok) return kUnknownStatement;
554 catch_or_finally_seen = true;
555 }
556 if (peek() == i::Token::FINALLY) {
557 Consume(i::Token::FINALLY);
558 ParseBlock(CHECK_OK);
559 catch_or_finally_seen = true;
560 }
561 if (!catch_or_finally_seen) {
562 *ok = false;
563 }
564 return kUnknownStatement;
565}
566
567
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000568PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000569 // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
570 // contexts this is used as a statement which invokes the debugger as if a
571 // break point is present.
572 // DebuggerStatement ::
573 // 'debugger' ';'
574
575 Expect(i::Token::DEBUGGER, CHECK_OK);
576 ExpectSemicolon(CHECK_OK);
577 return kUnknownStatement;
578}
579
580
581// Precedence = 1
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000582PreParser::Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000583 // Expression ::
584 // AssignmentExpression
585 // Expression ',' AssignmentExpression
586
587 Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK);
588 while (peek() == i::Token::COMMA) {
589 Expect(i::Token::COMMA, CHECK_OK);
590 ParseAssignmentExpression(accept_IN, CHECK_OK);
591 result = kUnknownExpression;
592 }
593 return result;
594}
595
596
597// Precedence = 2
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000598PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
599 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000600 // AssignmentExpression ::
601 // ConditionalExpression
602 // LeftHandSideExpression AssignmentOperator AssignmentExpression
603
604 Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK);
605
606 if (!i::Token::IsAssignmentOp(peek())) {
607 // Parsed conditional expression only (no assignment).
608 return expression;
609 }
610
611 i::Token::Value op = Next(); // Get assignment operator.
612 ParseAssignmentExpression(accept_IN, CHECK_OK);
613
614 if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) {
615 scope_->AddProperty();
616 }
617
618 return kUnknownExpression;
619}
620
621
622// Precedence = 3
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000623PreParser::Expression PreParser::ParseConditionalExpression(bool accept_IN,
624 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000625 // ConditionalExpression ::
626 // LogicalOrExpression
627 // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression
628
629 // We start using the binary expression parser for prec >= 4 only!
630 Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK);
631 if (peek() != i::Token::CONDITIONAL) return expression;
632 Consume(i::Token::CONDITIONAL);
633 // In parsing the first assignment expression in conditional
634 // expressions we always accept the 'in' keyword; see ECMA-262,
635 // section 11.12, page 58.
636 ParseAssignmentExpression(true, CHECK_OK);
637 Expect(i::Token::COLON, CHECK_OK);
638 ParseAssignmentExpression(accept_IN, CHECK_OK);
639 return kUnknownExpression;
640}
641
642
643int PreParser::Precedence(i::Token::Value tok, bool accept_IN) {
644 if (tok == i::Token::IN && !accept_IN)
645 return 0; // 0 precedence will terminate binary expression parsing
646
647 return i::Token::Precedence(tok);
648}
649
650
651// Precedence >= 4
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000652PreParser::Expression PreParser::ParseBinaryExpression(int prec,
653 bool accept_IN,
654 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000655 Expression result = ParseUnaryExpression(CHECK_OK);
656 for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
657 // prec1 >= 4
658 while (Precedence(peek(), accept_IN) == prec1) {
659 Next();
660 ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK);
661 result = kUnknownExpression;
662 }
663 }
664 return result;
665}
666
667
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000668PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000669 // UnaryExpression ::
670 // PostfixExpression
671 // 'delete' UnaryExpression
672 // 'void' UnaryExpression
673 // 'typeof' UnaryExpression
674 // '++' UnaryExpression
675 // '--' UnaryExpression
676 // '+' UnaryExpression
677 // '-' UnaryExpression
678 // '~' UnaryExpression
679 // '!' UnaryExpression
680
681 i::Token::Value op = peek();
682 if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) {
683 op = Next();
684 ParseUnaryExpression(ok);
685 return kUnknownExpression;
686 } else {
687 return ParsePostfixExpression(ok);
688 }
689}
690
691
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000692PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000693 // PostfixExpression ::
694 // LeftHandSideExpression ('++' | '--')?
695
696 Expression expression = ParseLeftHandSideExpression(CHECK_OK);
697 if (!scanner_->has_line_terminator_before_next() &&
698 i::Token::IsCountOp(peek())) {
699 Next();
700 return kUnknownExpression;
701 }
702 return expression;
703}
704
705
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000706PreParser::Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000707 // LeftHandSideExpression ::
708 // (NewExpression | MemberExpression) ...
709
710 Expression result;
711 if (peek() == i::Token::NEW) {
712 result = ParseNewExpression(CHECK_OK);
713 } else {
714 result = ParseMemberExpression(CHECK_OK);
715 }
716
717 while (true) {
718 switch (peek()) {
719 case i::Token::LBRACK: {
720 Consume(i::Token::LBRACK);
721 ParseExpression(true, CHECK_OK);
722 Expect(i::Token::RBRACK, CHECK_OK);
723 if (result == kThisExpression) {
724 result = kThisPropertyExpression;
725 } else {
726 result = kUnknownExpression;
727 }
728 break;
729 }
730
731 case i::Token::LPAREN: {
732 ParseArguments(CHECK_OK);
733 result = kUnknownExpression;
734 break;
735 }
736
737 case i::Token::PERIOD: {
738 Consume(i::Token::PERIOD);
739 ParseIdentifierName(CHECK_OK);
740 if (result == kThisExpression) {
741 result = kThisPropertyExpression;
742 } else {
743 result = kUnknownExpression;
744 }
745 break;
746 }
747
748 default:
749 return result;
750 }
751 }
752}
753
754
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000755PreParser::Expression PreParser::ParseNewExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000756 // NewExpression ::
757 // ('new')+ MemberExpression
758
759 // The grammar for new expressions is pretty warped. The keyword
760 // 'new' can either be a part of the new expression (where it isn't
761 // followed by an argument list) or a part of the member expression,
762 // where it must be followed by an argument list. To accommodate
763 // this, we parse the 'new' keywords greedily and keep track of how
764 // many we have parsed. This information is then passed on to the
765 // member expression parser, which is only allowed to match argument
766 // lists as long as it has 'new' prefixes left
767 unsigned new_count = 0;
768 do {
769 Consume(i::Token::NEW);
770 new_count++;
771 } while (peek() == i::Token::NEW);
772
773 return ParseMemberWithNewPrefixesExpression(new_count, ok);
774}
775
776
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000777PreParser::Expression PreParser::ParseMemberExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000778 return ParseMemberWithNewPrefixesExpression(0, ok);
779}
780
781
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000782PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000783 unsigned new_count, bool* ok) {
784 // MemberExpression ::
785 // (PrimaryExpression | FunctionLiteral)
786 // ('[' Expression ']' | '.' Identifier | Arguments)*
787
788 // Parse the initial primary or function expression.
789 Expression result = kUnknownExpression;
790 if (peek() == i::Token::FUNCTION) {
791 Consume(i::Token::FUNCTION);
792 if (peek() == i::Token::IDENTIFIER) {
793 ParseIdentifier(CHECK_OK);
794 }
795 result = ParseFunctionLiteral(CHECK_OK);
796 } else {
797 result = ParsePrimaryExpression(CHECK_OK);
798 }
799
800 while (true) {
801 switch (peek()) {
802 case i::Token::LBRACK: {
803 Consume(i::Token::LBRACK);
804 ParseExpression(true, CHECK_OK);
805 Expect(i::Token::RBRACK, CHECK_OK);
806 if (result == kThisExpression) {
807 result = kThisPropertyExpression;
808 } else {
809 result = kUnknownExpression;
810 }
811 break;
812 }
813 case i::Token::PERIOD: {
814 Consume(i::Token::PERIOD);
815 ParseIdentifierName(CHECK_OK);
816 if (result == kThisExpression) {
817 result = kThisPropertyExpression;
818 } else {
819 result = kUnknownExpression;
820 }
821 break;
822 }
823 case i::Token::LPAREN: {
824 if (new_count == 0) return result;
825 // Consume one of the new prefixes (already parsed).
826 ParseArguments(CHECK_OK);
827 new_count--;
828 result = kUnknownExpression;
829 break;
830 }
831 default:
832 return result;
833 }
834 }
835}
836
837
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000838PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000839 // PrimaryExpression ::
840 // 'this'
841 // 'null'
842 // 'true'
843 // 'false'
844 // Identifier
845 // Number
846 // String
847 // ArrayLiteral
848 // ObjectLiteral
849 // RegExpLiteral
850 // '(' Expression ')'
851
852 Expression result = kUnknownExpression;
853 switch (peek()) {
854 case i::Token::THIS: {
855 Next();
856 result = kThisExpression;
857 break;
858 }
859
860 case i::Token::IDENTIFIER: {
861 ParseIdentifier(CHECK_OK);
862 result = kIdentifierExpression;
863 break;
864 }
865
866 case i::Token::NULL_LITERAL:
867 case i::Token::TRUE_LITERAL:
868 case i::Token::FALSE_LITERAL:
869 case i::Token::NUMBER: {
870 Next();
871 break;
872 }
873 case i::Token::STRING: {
874 Next();
875 result = GetStringSymbol();
876 break;
877 }
878
879 case i::Token::ASSIGN_DIV:
880 result = ParseRegExpLiteral(true, CHECK_OK);
881 break;
882
883 case i::Token::DIV:
884 result = ParseRegExpLiteral(false, CHECK_OK);
885 break;
886
887 case i::Token::LBRACK:
888 result = ParseArrayLiteral(CHECK_OK);
889 break;
890
891 case i::Token::LBRACE:
892 result = ParseObjectLiteral(CHECK_OK);
893 break;
894
895 case i::Token::LPAREN:
896 Consume(i::Token::LPAREN);
897 result = ParseExpression(true, CHECK_OK);
898 Expect(i::Token::RPAREN, CHECK_OK);
899 if (result == kIdentifierExpression) result = kUnknownExpression;
900 break;
901
902 case i::Token::MOD:
903 result = ParseV8Intrinsic(CHECK_OK);
904 break;
905
906 default: {
907 Next();
908 *ok = false;
909 return kUnknownExpression;
910 }
911 }
912
913 return result;
914}
915
916
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000917PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000918 // ArrayLiteral ::
919 // '[' Expression? (',' Expression?)* ']'
920 Expect(i::Token::LBRACK, CHECK_OK);
921 while (peek() != i::Token::RBRACK) {
922 if (peek() != i::Token::COMMA) {
923 ParseAssignmentExpression(true, CHECK_OK);
924 }
925 if (peek() != i::Token::RBRACK) {
926 Expect(i::Token::COMMA, CHECK_OK);
927 }
928 }
929 Expect(i::Token::RBRACK, CHECK_OK);
930
931 scope_->NextMaterializedLiteralIndex();
932 return kUnknownExpression;
933}
934
935
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000936PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000937 // ObjectLiteral ::
938 // '{' (
939 // ((IdentifierName | String | Number) ':' AssignmentExpression)
940 // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
941 // )*[','] '}'
942
943 Expect(i::Token::LBRACE, CHECK_OK);
944 while (peek() != i::Token::RBRACE) {
945 i::Token::Value next = peek();
946 switch (next) {
947 case i::Token::IDENTIFIER: {
948 bool is_getter = false;
949 bool is_setter = false;
950 ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
951 if ((is_getter || is_setter) && peek() != i::Token::COLON) {
952 i::Token::Value name = Next();
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000953 bool is_keyword = i::Token::IsKeyword(name);
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000954 if (name != i::Token::IDENTIFIER &&
955 name != i::Token::NUMBER &&
956 name != i::Token::STRING &&
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000957 !is_keyword) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000958 *ok = false;
959 return kUnknownExpression;
960 }
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000961 if (!is_keyword) {
962 LogSymbol();
963 }
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000964 ParseFunctionLiteral(CHECK_OK);
965 if (peek() != i::Token::RBRACE) {
966 Expect(i::Token::COMMA, CHECK_OK);
967 }
968 continue; // restart the while
969 }
970 break;
971 }
972 case i::Token::STRING:
973 Consume(next);
974 GetStringSymbol();
975 break;
976 case i::Token::NUMBER:
977 Consume(next);
978 break;
979 default:
980 if (i::Token::IsKeyword(next)) {
981 Consume(next);
982 } else {
983 // Unexpected token.
984 *ok = false;
985 return kUnknownExpression;
986 }
987 }
988
989 Expect(i::Token::COLON, CHECK_OK);
990 ParseAssignmentExpression(true, CHECK_OK);
991
992 // TODO(1240767): Consider allowing trailing comma.
993 if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK);
994 }
995 Expect(i::Token::RBRACE, CHECK_OK);
996
997 scope_->NextMaterializedLiteralIndex();
998 return kUnknownExpression;
999}
1000
1001
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001002PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal,
1003 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001004 if (!scanner_->ScanRegExpPattern(seen_equal)) {
1005 Next();
1006 i::JavaScriptScanner::Location location = scanner_->location();
1007 ReportMessageAt(location.beg_pos, location.end_pos,
1008 "unterminated_regexp", NULL);
1009 *ok = false;
1010 return kUnknownExpression;
1011 }
1012
1013 scope_->NextMaterializedLiteralIndex();
1014
1015 if (!scanner_->ScanRegExpFlags()) {
1016 Next();
1017 i::JavaScriptScanner::Location location = scanner_->location();
1018 ReportMessageAt(location.beg_pos, location.end_pos,
1019 "invalid_regexp_flags", NULL);
1020 *ok = false;
1021 return kUnknownExpression;
1022 }
1023 Next();
1024 return kUnknownExpression;
1025}
1026
1027
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001028PreParser::Arguments PreParser::ParseArguments(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001029 // Arguments ::
1030 // '(' (AssignmentExpression)*[','] ')'
1031
1032 Expect(i::Token::LPAREN, CHECK_OK);
1033 bool done = (peek() == i::Token::RPAREN);
1034 int argc = 0;
1035 while (!done) {
1036 ParseAssignmentExpression(true, CHECK_OK);
1037 argc++;
1038 done = (peek() == i::Token::RPAREN);
1039 if (!done) Expect(i::Token::COMMA, CHECK_OK);
1040 }
1041 Expect(i::Token::RPAREN, CHECK_OK);
1042 return argc;
1043}
1044
1045
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001046PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001047 // Function ::
1048 // '(' FormalParameterList? ')' '{' FunctionBody '}'
1049
1050 // Parse function body.
1051 ScopeType outer_scope_type = scope_->type();
1052 bool inside_with = scope_->IsInsideWith();
1053 Scope function_scope(&scope_, kFunctionScope);
1054
1055 // FormalParameterList ::
1056 // '(' (Identifier)*[','] ')'
1057 Expect(i::Token::LPAREN, CHECK_OK);
1058 bool done = (peek() == i::Token::RPAREN);
1059 while (!done) {
1060 ParseIdentifier(CHECK_OK);
1061 done = (peek() == i::Token::RPAREN);
1062 if (!done) {
1063 Expect(i::Token::COMMA, CHECK_OK);
1064 }
1065 }
1066 Expect(i::Token::RPAREN, CHECK_OK);
1067
1068 Expect(i::Token::LBRACE, CHECK_OK);
1069 int function_block_pos = scanner_->location().beg_pos;
1070
1071 // Determine if the function will be lazily compiled.
1072 // Currently only happens to top-level functions.
1073 // Optimistically assume that all top-level functions are lazily compiled.
1074 bool is_lazily_compiled =
1075 (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_);
1076
1077 if (is_lazily_compiled) {
1078 log_->PauseRecording();
1079 ParseSourceElements(i::Token::RBRACE, ok);
1080 log_->ResumeRecording();
1081 if (!*ok) return kUnknownExpression;
1082
1083 Expect(i::Token::RBRACE, CHECK_OK);
1084
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001085 // Position right after terminal '}'.
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001086 int end_pos = scanner_->location().end_pos;
1087 log_->LogFunction(function_block_pos, end_pos,
1088 function_scope.materialized_literal_count(),
1089 function_scope.expected_properties());
1090 } else {
1091 ParseSourceElements(i::Token::RBRACE, CHECK_OK);
1092 Expect(i::Token::RBRACE, CHECK_OK);
1093 }
1094 return kUnknownExpression;
1095}
1096
1097
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001098PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001099 // CallRuntime ::
1100 // '%' Identifier Arguments
1101
1102 Expect(i::Token::MOD, CHECK_OK);
1103 ParseIdentifier(CHECK_OK);
1104 ParseArguments(CHECK_OK);
1105
1106 return kUnknownExpression;
1107}
1108
1109
1110void PreParser::ExpectSemicolon(bool* ok) {
1111 // Check for automatic semicolon insertion according to
1112 // the rules given in ECMA-262, section 7.9, page 21.
1113 i::Token::Value tok = peek();
1114 if (tok == i::Token::SEMICOLON) {
1115 Next();
1116 return;
1117 }
1118 if (scanner_->has_line_terminator_before_next() ||
1119 tok == i::Token::RBRACE ||
1120 tok == i::Token::EOS) {
1121 return;
1122 }
1123 Expect(i::Token::SEMICOLON, ok);
1124}
1125
1126
lrn@chromium.org5d00b602011-01-05 09:51:43 +00001127void PreParser::LogSymbol() {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001128 int identifier_pos = scanner_->location().beg_pos;
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00001129 if (scanner_->is_literal_ascii()) {
1130 log_->LogAsciiSymbol(identifier_pos, scanner_->literal_ascii_string());
1131 } else {
1132 log_->LogUC16Symbol(identifier_pos, scanner_->literal_uc16_string());
1133 }
lrn@chromium.org5d00b602011-01-05 09:51:43 +00001134}
1135
1136
1137PreParser::Identifier PreParser::GetIdentifierSymbol() {
1138 LogSymbol();
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00001139 return kUnknownIdentifier;
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001140}
1141
1142
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001143PreParser::Expression PreParser::GetStringSymbol() {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00001144 LogSymbol();
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001145 return kUnknownExpression;
1146}
1147
1148
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001149PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001150 Expect(i::Token::IDENTIFIER, ok);
1151 if (!*ok) return kUnknownIdentifier;
1152 return GetIdentifierSymbol();
1153}
1154
1155
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001156PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001157 i::Token::Value next = Next();
1158 if (i::Token::IsKeyword(next)) {
1159 int pos = scanner_->location().beg_pos;
1160 const char* keyword = i::Token::String(next);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00001161 log_->LogAsciiSymbol(pos, i::Vector<const char>(keyword,
1162 i::StrLength(keyword)));
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001163 return kUnknownExpression;
1164 }
1165 if (next == i::Token::IDENTIFIER) {
1166 return GetIdentifierSymbol();
1167 }
1168 *ok = false;
1169 return kUnknownIdentifier;
1170}
1171
1172
1173// This function reads an identifier and determines whether or not it
1174// is 'get' or 'set'. The reason for not using ParseIdentifier and
1175// checking on the output is that this involves heap allocation which
1176// we can't do during preparsing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001177PreParser::Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
1178 bool* is_set,
1179 bool* ok) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001180 Expect(i::Token::IDENTIFIER, CHECK_OK);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00001181 if (scanner_->is_literal_ascii() && scanner_->literal_length() == 3) {
1182 const char* token = scanner_->literal_ascii_string().start();
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001183 *is_get = strncmp(token, "get", 3) == 0;
1184 *is_set = !*is_get && strncmp(token, "set", 3) == 0;
1185 }
1186 return GetIdentifierSymbol();
1187}
1188
1189#undef CHECK_OK
1190} } // v8::preparser