v8/src/preparser.cc
adamk 33a373985b Fix lazy compilation of eval() under nosnap/--use-strict
When running without a snapshot, the GlobalEval function gets lazy compiled.
By the time we compile it, its name is "eval", which causes the parser to
choke (functions named "eval" aren't allowed in strict mode!).

Instead, we now always skip checking the function name when lazy-parsing,
as the name has already been checked appropriately by the preparser.

Also cleaned up other cases that don't require name checking by introducing
FunctionNameValidity enum and passing appropriate values throughout the
parser and preparser.

This lets us pass an additional 18 test262 tests.

BUG=v8:4198
LOG=n

Review URL: https://codereview.chromium.org/1227093005

Cr-Commit-Position: refs/heads/master@{#29559}
2015-07-09 21:31:18 +00:00

1205 lines
40 KiB
C++

// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include "src/allocation.h"
#include "src/base/logging.h"
#include "src/conversions-inl.h"
#include "src/conversions.h"
#include "src/globals.h"
#include "src/hashmap.h"
#include "src/list.h"
#include "src/preparse-data.h"
#include "src/preparse-data-format.h"
#include "src/preparser.h"
#include "src/unicode.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
void PreParserTraits::ReportMessageAt(Scanner::Location location,
MessageTemplate::Template message,
const char* arg,
ParseErrorType error_type) {
ReportMessageAt(location.beg_pos, location.end_pos, message, arg, error_type);
}
void PreParserTraits::ReportMessageAt(int start_pos, int end_pos,
MessageTemplate::Template message,
const char* arg,
ParseErrorType error_type) {
pre_parser_->log_->LogMessage(start_pos, end_pos, message, arg, error_type);
}
PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) {
if (scanner->current_token() == Token::FUTURE_RESERVED_WORD) {
return PreParserIdentifier::FutureReserved();
} else if (scanner->current_token() ==
Token::FUTURE_STRICT_RESERVED_WORD) {
return PreParserIdentifier::FutureStrictReserved();
} else if (scanner->current_token() == Token::LET) {
return PreParserIdentifier::Let();
} else if (scanner->current_token() == Token::STATIC) {
return PreParserIdentifier::Static();
} else if (scanner->current_token() == Token::YIELD) {
return PreParserIdentifier::Yield();
}
if (scanner->UnescapedLiteralMatches("eval", 4)) {
return PreParserIdentifier::Eval();
}
if (scanner->UnescapedLiteralMatches("arguments", 9)) {
return PreParserIdentifier::Arguments();
}
if (scanner->UnescapedLiteralMatches("undefined", 9)) {
return PreParserIdentifier::Undefined();
}
if (scanner->LiteralMatches("prototype", 9)) {
return PreParserIdentifier::Prototype();
}
if (scanner->LiteralMatches("constructor", 11)) {
return PreParserIdentifier::Constructor();
}
return PreParserIdentifier::Default();
}
PreParserIdentifier PreParserTraits::GetNumberAsSymbol(Scanner* scanner) {
return PreParserIdentifier::Default();
}
PreParserExpression PreParserTraits::ExpressionFromString(
int pos, Scanner* scanner, PreParserFactory* factory) {
if (scanner->UnescapedLiteralMatches("use strict", 10)) {
return PreParserExpression::UseStrictStringLiteral();
} else if (scanner->UnescapedLiteralMatches("use strong", 10)) {
return PreParserExpression::UseStrongStringLiteral();
}
return PreParserExpression::StringLiteral();
}
PreParserExpression PreParserTraits::ParseV8Intrinsic(bool* ok) {
return pre_parser_->ParseV8Intrinsic(ok);
}
PreParserExpression PreParserTraits::ParseFunctionLiteral(
PreParserIdentifier name, Scanner::Location function_name_location,
FunctionNameValidity function_name_validity, FunctionKind kind,
int function_token_position, FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction, bool* ok) {
return pre_parser_->ParseFunctionLiteral(
name, function_name_location, function_name_validity, kind,
function_token_position, type, arity_restriction, ok);
}
PreParser::PreParseResult PreParser::PreParseLazyFunction(
LanguageMode language_mode, FunctionKind kind, ParserRecorder* log,
Scanner::BookmarkScope* bookmark) {
log_ = log;
// Lazy functions always have trivial outer scopes (no with/catch scopes).
Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE);
PreParserFactory top_factory(NULL);
FunctionState top_state(&function_state_, &scope_, top_scope, kNormalFunction,
&top_factory);
scope_->SetLanguageMode(language_mode);
Scope* function_scope = NewScope(
scope_, IsArrowFunction(kind) ? ARROW_SCOPE : FUNCTION_SCOPE, kind);
PreParserFactory function_factory(NULL);
FunctionState function_state(&function_state_, &scope_, function_scope, kind,
&function_factory);
DCHECK_EQ(Token::LBRACE, scanner()->current_token());
bool ok = true;
int start_position = peek_position();
ParseLazyFunctionLiteralBody(&ok, bookmark);
if (bookmark && bookmark->HasBeenReset()) {
; // Do nothing, as we've just aborted scanning this function.
} else if (stack_overflow()) {
return kPreParseStackOverflow;
} else if (!ok) {
ReportUnexpectedToken(scanner()->current_token());
} else {
DCHECK_EQ(Token::RBRACE, scanner()->peek());
if (is_strict(scope_->language_mode())) {
int end_pos = scanner()->location().end_pos;
CheckStrictOctalLiteral(start_position, end_pos, &ok);
if (!ok) return kPreParseSuccess;
if (is_strong(scope_->language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_location().IsValid()) {
ReportMessageAt(Scanner::Location(start_position, start_position + 1),
MessageTemplate::kStrongSuperCallMissing,
kReferenceError);
return kPreParseSuccess;
}
}
}
}
return kPreParseSuccess;
}
PreParserExpression PreParserTraits::ParseClassLiteral(
PreParserIdentifier name, Scanner::Location class_name_location,
bool name_is_strict_reserved, int pos, bool* ok) {
return pre_parser_->ParseClassLiteral(name, class_name_location,
name_is_strict_reserved, pos, ok);
}
// Preparsing checks a JavaScript program and emits preparse-data that helps
// a later parsing to be faster.
// See preparser-data.h for the data.
// The PreParser checks that the syntax follows the grammar for JavaScript,
// and collects some information about the program along the way.
// The grammar check is only performed in order to understand the program
// sufficiently to deduce some information about it, that can be used
// to speed up later parsing. Finding errors is not the goal of pre-parsing,
// rather it is to speed up properly written and correct programs.
// That means that contextual checks (like a label being declared where
// it is used) are generally omitted.
PreParser::Statement PreParser::ParseStatementListItem(bool* ok) {
// ECMA 262 6th Edition
// StatementListItem[Yield, Return] :
// Statement[?Yield, ?Return]
// Declaration[?Yield]
//
// Declaration[Yield] :
// HoistableDeclaration[?Yield]
// ClassDeclaration[?Yield]
// LexicalDeclaration[In, ?Yield]
//
// HoistableDeclaration[Yield, Default] :
// FunctionDeclaration[?Yield, ?Default]
// GeneratorDeclaration[?Yield, ?Default]
//
// LexicalDeclaration[In, Yield] :
// LetOrConst BindingList[?In, ?Yield] ;
switch (peek()) {
case Token::FUNCTION:
return ParseFunctionDeclaration(ok);
case Token::CLASS:
return ParseClassDeclaration(ok);
case Token::CONST:
if (allow_const()) {
return ParseVariableStatement(kStatementListItem, ok);
}
break;
case Token::LET:
if (allow_let()) {
return ParseVariableStatement(kStatementListItem, ok);
}
break;
default:
break;
}
return ParseStatement(ok);
}
void PreParser::ParseStatementList(int end_token, bool* ok,
Scanner::BookmarkScope* bookmark) {
// SourceElements ::
// (Statement)* <end_token>
// Bookkeeping for trial parse if bookmark is set:
DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet());
bool maybe_reset = bookmark != nullptr;
int count_statements = 0;
bool directive_prologue = true;
while (peek() != end_token) {
if (directive_prologue && peek() != Token::STRING) {
directive_prologue = false;
}
bool starts_with_identifier = peek() == Token::IDENTIFIER;
Scanner::Location token_loc = scanner()->peek_location();
Scanner::Location old_this_loc = function_state_->this_location();
Scanner::Location old_super_loc = function_state_->super_location();
Statement statement = ParseStatementListItem(ok);
if (!*ok) return;
if (is_strong(language_mode()) &&
scope_->is_function_scope() &&
i::IsConstructor(function_state_->kind())) {
Scanner::Location this_loc = function_state_->this_location();
Scanner::Location super_loc = function_state_->super_location();
if (this_loc.beg_pos != old_this_loc.beg_pos &&
this_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(this_loc, MessageTemplate::kStrongConstructorThis);
*ok = false;
return;
}
if (super_loc.beg_pos != old_super_loc.beg_pos &&
super_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(super_loc, MessageTemplate::kStrongConstructorSuper);
*ok = false;
return;
}
}
if (directive_prologue) {
if (statement.IsUseStrictLiteral()) {
scope_->SetLanguageMode(
static_cast<LanguageMode>(scope_->language_mode() | STRICT_BIT));
} else if (statement.IsUseStrongLiteral() && allow_strong_mode()) {
scope_->SetLanguageMode(static_cast<LanguageMode>(
scope_->language_mode() | STRICT_BIT | STRONG_BIT));
} else if (!statement.IsStringLiteral()) {
directive_prologue = false;
}
}
// If we're allowed to reset to a bookmark, we will do so when we see a long
// and trivial function.
// Our current definition of 'long and trivial' is:
// - over 200 statements
// - all starting with an identifier (i.e., no if, for, while, etc.)
if (maybe_reset && (!starts_with_identifier ||
++count_statements > kLazyParseTrialLimit)) {
if (count_statements > kLazyParseTrialLimit) {
bookmark->Reset();
return;
}
maybe_reset = false;
}
}
}
#define CHECK_OK ok); \
if (!*ok) return Statement::Default(); \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
PreParser::Statement PreParser::ParseStatement(bool* ok) {
// Statement ::
// EmptyStatement
// ...
if (peek() == Token::SEMICOLON) {
Next();
return Statement::Default();
}
return ParseSubStatement(ok);
}
PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
// Statement ::
// Block
// VariableStatement
// EmptyStatement
// ExpressionStatement
// IfStatement
// IterationStatement
// ContinueStatement
// BreakStatement
// ReturnStatement
// WithStatement
// LabelledStatement
// SwitchStatement
// ThrowStatement
// TryStatement
// DebuggerStatement
// Note: Since labels can only be used by 'break' and 'continue'
// statements, which themselves are only valid within blocks,
// iterations or 'switch' statements (i.e., BreakableStatements),
// labels can be simply ignored in all other cases; except for
// trivial labeled break statements 'label: break label' which is
// parsed into an empty statement.
// Keep the source position of the statement
switch (peek()) {
case Token::LBRACE:
return ParseBlock(ok);
case Token::SEMICOLON:
if (is_strong(language_mode())) {
PreParserTraits::ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kStrongEmpty);
*ok = false;
return Statement::Default();
}
Next();
return Statement::Default();
case Token::IF:
return ParseIfStatement(ok);
case Token::DO:
return ParseDoWhileStatement(ok);
case Token::WHILE:
return ParseWhileStatement(ok);
case Token::FOR:
return ParseForStatement(ok);
case Token::CONTINUE:
return ParseContinueStatement(ok);
case Token::BREAK:
return ParseBreakStatement(ok);
case Token::RETURN:
return ParseReturnStatement(ok);
case Token::WITH:
return ParseWithStatement(ok);
case Token::SWITCH:
return ParseSwitchStatement(ok);
case Token::THROW:
return ParseThrowStatement(ok);
case Token::TRY:
return ParseTryStatement(ok);
case Token::FUNCTION: {
Scanner::Location start_location = scanner()->peek_location();
Statement statement = ParseFunctionDeclaration(CHECK_OK);
Scanner::Location end_location = scanner()->location();
if (is_strict(language_mode())) {
PreParserTraits::ReportMessageAt(start_location.beg_pos,
end_location.end_pos,
MessageTemplate::kStrictFunction);
*ok = false;
return Statement::Default();
} else {
return statement;
}
}
case Token::DEBUGGER:
return ParseDebuggerStatement(ok);
case Token::VAR:
return ParseVariableStatement(kStatement, ok);
case Token::CONST:
// In ES6 CONST is not allowed as a Statement, only as a
// LexicalDeclaration, however we continue to allow it in sloppy mode for
// backwards compatibility.
if (is_sloppy(language_mode()) && allow_legacy_const()) {
return ParseVariableStatement(kStatement, ok);
}
// Fall through.
default:
return ParseExpressionOrLabelledStatement(ok);
}
}
PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
// GeneratorDeclaration ::
// 'function' '*' Identifier '(' FormalParameterListopt ')'
// '{' FunctionBody '}'
Expect(Token::FUNCTION, CHECK_OK);
int pos = position();
bool is_generator = Check(Token::MUL);
bool is_strict_reserved = false;
Identifier name = ParseIdentifierOrStrictReservedWord(
&is_strict_reserved, CHECK_OK);
ParseFunctionLiteral(name, scanner()->location(),
is_strict_reserved ? kFunctionNameIsStrictReserved
: kFunctionNameValidityUnknown,
is_generator ? FunctionKind::kGeneratorFunction
: FunctionKind::kNormalFunction,
pos, FunctionLiteral::DECLARATION,
FunctionLiteral::NORMAL_ARITY, CHECK_OK);
return Statement::FunctionDeclaration();
}
PreParser::Statement PreParser::ParseClassDeclaration(bool* ok) {
Expect(Token::CLASS, CHECK_OK);
if (!allow_harmony_sloppy() && is_sloppy(language_mode())) {
ReportMessage(MessageTemplate::kSloppyLexical);
*ok = false;
return Statement::Default();
}
int pos = position();
bool is_strict_reserved = false;
Identifier name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
ParseClassLiteral(name, scanner()->location(), is_strict_reserved, pos,
CHECK_OK);
return Statement::Default();
}
PreParser::Statement PreParser::ParseBlock(bool* ok) {
// Block ::
// '{' Statement* '}'
// Note that a Block does not introduce a new execution scope!
// (ECMA-262, 3rd, 12.2)
//
Expect(Token::LBRACE, CHECK_OK);
Statement final = Statement::Default();
while (peek() != Token::RBRACE) {
if (is_strict(language_mode()) || allow_harmony_sloppy()) {
final = ParseStatementListItem(CHECK_OK);
} else {
final = ParseStatement(CHECK_OK);
}
}
Expect(Token::RBRACE, ok);
return final;
}
PreParser::Statement PreParser::ParseVariableStatement(
VariableDeclarationContext var_context,
bool* ok) {
// VariableStatement ::
// VariableDeclarations ';'
Statement result = ParseVariableDeclarations(var_context, nullptr, nullptr,
nullptr, CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
// If the variable declaration declares exactly one non-const
// variable, then *var is set to that variable. In all other cases,
// *var is untouched; in particular, it is the caller's responsibility
// to initialize it properly. This mechanism is also used for the parsing
// of 'for-in' loops.
PreParser::Statement PreParser::ParseVariableDeclarations(
VariableDeclarationContext var_context, int* num_decl,
Scanner::Location* first_initializer_loc, Scanner::Location* bindings_loc,
bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
//
// The ES6 Draft Rev3 specifies the following grammar for const declarations
//
// ConstDeclaration ::
// const ConstBinding (',' ConstBinding)* ';'
// ConstBinding ::
// Identifier '=' AssignmentExpression
//
// TODO(ES6):
// ConstBinding ::
// BindingPattern '=' AssignmentExpression
bool require_initializer = false;
bool is_strict_const = false;
if (peek() == Token::VAR) {
if (is_strong(language_mode())) {
Scanner::Location location = scanner()->peek_location();
ReportMessageAt(location, MessageTemplate::kStrongVar);
*ok = false;
return Statement::Default();
}
Consume(Token::VAR);
} else if (peek() == Token::CONST && allow_const()) {
// TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads:
//
// ConstDeclaration : const ConstBinding (',' ConstBinding)* ';'
//
// * It is a Syntax Error if the code that matches this production is not
// contained in extended code.
//
// However disallowing const in sloppy mode will break compatibility with
// existing pages. Therefore we keep allowing const with the old
// non-harmony semantics in sloppy mode.
Consume(Token::CONST);
if (is_strict(language_mode()) ||
(allow_harmony_sloppy() && !allow_legacy_const())) {
DCHECK(var_context != kStatement);
is_strict_const = true;
require_initializer = var_context != kForStatement;
}
} else if (peek() == Token::LET && allow_let()) {
Consume(Token::LET);
DCHECK(var_context != kStatement);
} else {
*ok = false;
return Statement::Default();
}
// The scope of a var/const declared variable anywhere inside a function
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). The scope
// of a let declared variable is the scope of the immediately enclosing
// block.
int nvars = 0; // the number of variables declared
int bindings_start = peek_position();
do {
// Parse binding pattern.
if (nvars > 0) Consume(Token::COMMA);
{
ExpressionClassifier pattern_classifier;
Token::Value next = peek();
PreParserExpression pattern =
ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
if (!allow_harmony_destructuring() && !pattern.IsIdentifier()) {
ReportUnexpectedToken(next);
*ok = false;
return Statement::Default();
}
}
Scanner::Location variable_loc = scanner()->location();
nvars++;
if (peek() == Token::ASSIGN || require_initializer ||
// require initializers for multiple consts.
(is_strict_const && peek() == Token::COMMA)) {
Expect(Token::ASSIGN, CHECK_OK);
ExpressionClassifier classifier;
ParseAssignmentExpression(var_context != kForStatement, &classifier,
CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
variable_loc.end_pos = scanner()->location().end_pos;
if (first_initializer_loc && !first_initializer_loc->IsValid()) {
*first_initializer_loc = variable_loc;
}
}
} while (peek() == Token::COMMA);
if (bindings_loc) {
*bindings_loc =
Scanner::Location(bindings_start, scanner()->location().end_pos);
}
if (num_decl != NULL) *num_decl = nvars;
return Statement::Default();
}
PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
// ExpressionStatement | LabelledStatement ::
// Expression ';'
// Identifier ':' Statement
switch (peek()) {
case Token::FUNCTION:
case Token::LBRACE:
UNREACHABLE(); // Always handled by the callers.
case Token::CLASS:
ReportUnexpectedToken(Next());
*ok = false;
return Statement::Default();
case Token::THIS:
if (!FLAG_strong_this) break;
// Fall through.
case Token::SUPER:
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind())) {
bool is_this = peek() == Token::THIS;
Expression expr = Expression::Default();
ExpressionClassifier classifier;
if (is_this) {
expr = ParseStrongInitializationExpression(&classifier, CHECK_OK);
} else {
expr = ParseStrongSuperCallExpression(&classifier, CHECK_OK);
}
ValidateExpression(&classifier, CHECK_OK);
switch (peek()) {
case Token::SEMICOLON:
Consume(Token::SEMICOLON);
break;
case Token::RBRACE:
case Token::EOS:
break;
default:
if (!scanner()->HasAnyLineTerminatorBeforeNext()) {
ReportMessageAt(function_state_->this_location(),
is_this
? MessageTemplate::kStrongConstructorThis
: MessageTemplate::kStrongConstructorSuper);
*ok = false;
return Statement::Default();
}
}
return Statement::ExpressionStatement(expr);
}
break;
// TODO(arv): Handle `let [`
// https://code.google.com/p/v8/issues/detail?id=3847
default:
break;
}
bool starts_with_identifier = peek_any_identifier();
ExpressionClassifier classifier;
Expression expr = ParseExpression(true, &classifier, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
// Even if the expression starts with an identifier, it is not necessarily an
// identifier. For example, "foo + bar" starts with an identifier but is not
// an identifier.
if (starts_with_identifier && expr.IsIdentifier() && peek() == Token::COLON) {
// Expression is a single identifier, and not, e.g., a parenthesized
// identifier.
DCHECK(!expr.AsIdentifier().IsFutureReserved());
DCHECK(is_sloppy(language_mode()) ||
!IsFutureStrictReserved(expr.AsIdentifier()));
Consume(Token::COLON);
Statement statement = ParseStatement(ok);
return statement.IsJumpStatement() ? Statement::Default() : statement;
// Preparsing is disabled for extensions (because the extension details
// aren't passed to lazily compiled functions), so we don't
// accept "native function" in the preparser.
}
// Parsed expression statement.
// Detect attempts at 'let' declarations in sloppy mode.
if (peek() == Token::IDENTIFIER && is_sloppy(language_mode()) &&
expr.IsIdentifier() && expr.AsIdentifier().IsLet()) {
ReportMessage(MessageTemplate::kSloppyLexical, NULL);
*ok = false;
return Statement::Default();
}
ExpectSemicolon(CHECK_OK);
return Statement::ExpressionStatement(expr);
}
PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
// IfStatement ::
// 'if' '(' Expression ')' Statement ('else' Statement)?
Expect(Token::IF, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Statement stat = ParseSubStatement(CHECK_OK);
if (peek() == Token::ELSE) {
Next();
Statement else_stat = ParseSubStatement(CHECK_OK);
stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ?
Statement::Jump() : Statement::Default();
} else {
stat = Statement::Default();
}
return stat;
}
PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
// ContinueStatement ::
// 'continue' [no line terminator] Identifier? ';'
Expect(Token::CONTINUE, CHECK_OK);
Token::Value tok = peek();
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
return Statement::Jump();
}
PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
// BreakStatement ::
// 'break' [no line terminator] Identifier? ';'
Expect(Token::BREAK, CHECK_OK);
Token::Value tok = peek();
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
return Statement::Jump();
}
PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
// ReturnStatement ::
// 'return' [no line terminator] Expression? ';'
// Consume the return token. It is necessary to do before
// reporting any errors on it, because of the way errors are
// reported (underlining).
Expect(Token::RETURN, CHECK_OK);
function_state_->set_return_location(scanner()->location());
// An ECMAScript program is considered syntactically incorrect if it
// contains a return statement that is not within the body of a
// function. See ECMA-262, section 12.9, page 67.
// This is not handled during preparsing.
Token::Value tok = peek();
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind())) {
int pos = peek_position();
ReportMessageAt(Scanner::Location(pos, pos + 1),
MessageTemplate::kStrongConstructorReturnValue);
*ok = false;
return Statement::Default();
}
ParseExpression(true, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
return Statement::Jump();
}
PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
// WithStatement ::
// 'with' '(' Expression ')' Statement
Expect(Token::WITH, CHECK_OK);
if (is_strict(language_mode())) {
ReportMessageAt(scanner()->location(), MessageTemplate::kStrictWith);
*ok = false;
return Statement::Default();
}
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
BlockState block_state(&scope_, with_scope);
ParseSubStatement(CHECK_OK);
return Statement::Default();
}
PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
// SwitchStatement ::
// 'switch' '(' Expression ')' '{' CaseClause* '}'
Expect(Token::SWITCH, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Expect(Token::LBRACE, CHECK_OK);
Token::Value token = peek();
while (token != Token::RBRACE) {
if (token == Token::CASE) {
Expect(Token::CASE, CHECK_OK);
ParseExpression(true, CHECK_OK);
} else {
Expect(Token::DEFAULT, CHECK_OK);
}
Expect(Token::COLON, CHECK_OK);
token = peek();
Statement statement = Statement::Jump();
while (token != Token::CASE &&
token != Token::DEFAULT &&
token != Token::RBRACE) {
statement = ParseStatementListItem(CHECK_OK);
token = peek();
}
if (is_strong(language_mode()) && !statement.IsJumpStatement() &&
token != Token::RBRACE) {
ReportMessageAt(scanner()->location(),
MessageTemplate::kStrongSwitchFallthrough);
*ok = false;
return Statement::Default();
}
}
Expect(Token::RBRACE, ok);
return Statement::Default();
}
PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
// DoStatement ::
// 'do' Statement 'while' '(' Expression ')' ';'
Expect(Token::DO, CHECK_OK);
ParseSubStatement(CHECK_OK);
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, ok);
if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON);
return Statement::Default();
}
PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
// WhileStatement ::
// 'while' '(' Expression ')' Statement
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok);
return Statement::Default();
}
PreParser::Statement PreParser::ParseForStatement(bool* ok) {
// ForStatement ::
// 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
Expect(Token::FOR, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
bool is_let_identifier_expression = false;
if (peek() != Token::SEMICOLON) {
ForEachStatement::VisitMode mode;
if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) ||
(peek() == Token::LET && allow_let())) {
int decl_count;
Scanner::Location first_initializer_loc = Scanner::Location::invalid();
Scanner::Location bindings_loc = Scanner::Location::invalid();
ParseVariableDeclarations(kForStatement, &decl_count,
&first_initializer_loc, &bindings_loc,
CHECK_OK);
bool accept_IN = decl_count >= 1;
bool accept_OF = true;
if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return Statement::Default();
if (decl_count != 1) {
const char* loop_type =
mode == ForEachStatement::ITERATE ? "for-of" : "for-in";
PreParserTraits::ReportMessageAt(
bindings_loc, MessageTemplate::kForInOfLoopMultiBindings,
loop_type);
*ok = false;
return Statement::Default();
}
if (first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(first_initializer_loc,
MessageTemplate::kForOfLoopInitializer);
} else {
// TODO(caitp): This should be an error in sloppy mode, too.
ReportMessageAt(first_initializer_loc,
MessageTemplate::kForInLoopInitializer);
}
*ok = false;
return Statement::Default();
}
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
return Statement::Default();
}
} else {
Expression lhs = ParseExpression(false, CHECK_OK);
is_let_identifier_expression =
lhs.IsIdentifier() && lhs.AsIdentifier().IsLet();
if (CheckInOrOf(lhs.IsIdentifier(), &mode, ok)) {
if (!*ok) return Statement::Default();
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
return Statement::Default();
}
}
}
// Parsed initializer at this point.
// Detect attempts at 'let' declarations in sloppy mode.
if (peek() == Token::IDENTIFIER && is_sloppy(language_mode()) &&
is_let_identifier_expression) {
ReportMessage(MessageTemplate::kSloppyLexical, NULL);
*ok = false;
return Statement::Default();
}
Expect(Token::SEMICOLON, CHECK_OK);
if (peek() != Token::SEMICOLON) {
ParseExpression(true, CHECK_OK);
}
Expect(Token::SEMICOLON, CHECK_OK);
if (peek() != Token::RPAREN) {
ParseExpression(true, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok);
return Statement::Default();
}
PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
// ThrowStatement ::
// 'throw' [no line terminator] Expression ';'
Expect(Token::THROW, CHECK_OK);
if (scanner()->HasAnyLineTerminatorBeforeNext()) {
ReportMessageAt(scanner()->location(), MessageTemplate::kNewlineAfterThrow);
*ok = false;
return Statement::Default();
}
ParseExpression(true, CHECK_OK);
ExpectSemicolon(ok);
return Statement::Jump();
}
PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
// TryStatement ::
// 'try' Block Catch
// 'try' Block Finally
// 'try' Block Catch Finally
//
// Catch ::
// 'catch' '(' Identifier ')' Block
//
// Finally ::
// 'finally' Block
Expect(Token::TRY, CHECK_OK);
ParseBlock(CHECK_OK);
Token::Value tok = peek();
if (tok != Token::CATCH && tok != Token::FINALLY) {
ReportMessageAt(scanner()->location(), MessageTemplate::kNoCatchOrFinally);
*ok = false;
return Statement::Default();
}
if (tok == Token::CATCH) {
Consume(Token::CATCH);
Expect(Token::LPAREN, CHECK_OK);
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
{
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
BlockState block_state(&scope_, with_scope);
ParseBlock(CHECK_OK);
}
tok = peek();
}
if (tok == Token::FINALLY) {
Consume(Token::FINALLY);
ParseBlock(CHECK_OK);
}
return Statement::Default();
}
PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) {
// In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
// contexts this is used as a statement which invokes the debugger as if a
// break point is present.
// DebuggerStatement ::
// 'debugger' ';'
Expect(Token::DEBUGGER, CHECK_OK);
ExpectSemicolon(ok);
return Statement::Default();
}
#undef CHECK_OK
#define CHECK_OK ok); \
if (!*ok) return Expression::Default(); \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
PreParser::Expression PreParser::ParseFunctionLiteral(
Identifier function_name, Scanner::Location function_name_location,
FunctionNameValidity function_name_validity, FunctionKind kind,
int function_token_pos, FunctionLiteral::FunctionType function_type,
FunctionLiteral::ArityRestriction arity_restriction, bool* ok) {
// Function ::
// '(' FormalParameterList? ')' '{' FunctionBody '}'
// Parse function body.
bool outer_is_script_scope = scope_->is_script_scope();
Scope* function_scope = NewScope(scope_, FUNCTION_SCOPE, kind);
PreParserFactory factory(NULL);
FunctionState function_state(&function_state_, &scope_, function_scope, kind,
&factory);
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
ExpressionClassifier formals_classifier(&duplicate_finder);
Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos;
function_scope->set_start_position(start_position);
PreParserFormalParameterParsingState parsing_state(nullptr);
int num_parameters =
ParseFormalParameterList(&parsing_state, &formals_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
CheckArityRestrictions(num_parameters, arity_restriction,
parsing_state.has_rest, start_position,
formals_end_position, CHECK_OK);
// See Parser::ParseFunctionLiteral for more information about lazy parsing
// and lazy compilation.
bool is_lazily_parsed =
(outer_is_script_scope && allow_lazy() && !parenthesized_function_);
parenthesized_function_ = false;
Expect(Token::LBRACE, CHECK_OK);
if (is_lazily_parsed) {
ParseLazyFunctionLiteralBody(CHECK_OK);
} else {
ParseStatementList(Token::RBRACE, CHECK_OK);
}
Expect(Token::RBRACE, CHECK_OK);
// Validate name and parameter names. We can do this only after parsing the
// function, since the function can declare itself strict.
CheckFunctionName(language_mode(), function_name, function_name_validity,
function_name_location, CHECK_OK);
const bool strict_formal_parameters =
!parsing_state.is_simple_parameter_list || IsConciseMethod(kind);
const bool allow_duplicate_parameters =
is_sloppy(language_mode()) && !strict_formal_parameters;
ValidateFormalParameters(&formals_classifier, language_mode(),
allow_duplicate_parameters, CHECK_OK);
if (is_strict(language_mode())) {
int end_position = scanner()->location().end_pos;
CheckStrictOctalLiteral(start_position, end_position, CHECK_OK);
}
if (is_strong(language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_location().IsValid()) {
ReportMessageAt(function_name_location,
MessageTemplate::kStrongSuperCallMissing,
kReferenceError);
*ok = false;
return Expression::Default();
}
}
return Expression::Default();
}
void PreParser::ParseLazyFunctionLiteralBody(bool* ok,
Scanner::BookmarkScope* bookmark) {
int body_start = position();
ParseStatementList(Token::RBRACE, ok, bookmark);
if (!*ok) return;
if (bookmark && bookmark->HasBeenReset()) return;
// Position right after terminal '}'.
DCHECK_EQ(Token::RBRACE, scanner()->peek());
int body_end = scanner()->peek_location().end_pos;
log_->LogFunction(body_start, body_end,
function_state_->materialized_literal_count(),
function_state_->expected_property_count(), language_mode(),
scope_->uses_super_property(), scope_->calls_eval());
}
PreParserExpression PreParser::ParseClassLiteral(
PreParserIdentifier name, Scanner::Location class_name_location,
bool name_is_strict_reserved, int pos, bool* ok) {
// All parts of a ClassDeclaration and ClassExpression are strict code.
if (name_is_strict_reserved) {
ReportMessageAt(class_name_location,
MessageTemplate::kUnexpectedStrictReserved);
*ok = false;
return EmptyExpression();
}
if (IsEvalOrArguments(name)) {
ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments);
*ok = false;
return EmptyExpression();
}
LanguageMode class_language_mode = language_mode();
if (is_strong(class_language_mode) && IsUndefined(name)) {
ReportMessageAt(class_name_location, MessageTemplate::kStrongUndefined);
*ok = false;
return EmptyExpression();
}
Scope* scope = NewScope(scope_, BLOCK_SCOPE);
BlockState block_state(&scope_, scope);
scope_->SetLanguageMode(
static_cast<LanguageMode>(class_language_mode | STRICT_BIT));
// TODO(marja): Make PreParser use scope names too.
// scope_->SetScopeName(name);
bool has_extends = Check(Token::EXTENDS);
if (has_extends) {
ExpressionClassifier classifier;
ParseLeftHandSideExpression(&classifier, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
}
ClassLiteralChecker checker(this);
bool has_seen_constructor = false;
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
if (Check(Token::SEMICOLON)) continue;
const bool in_class = true;
const bool is_static = false;
bool is_computed_name = false; // Classes do not care about computed
// property names here.
ExpressionClassifier classifier;
ParsePropertyDefinition(&checker, in_class, has_extends, is_static,
&is_computed_name, &has_seen_constructor,
&classifier, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
}
Expect(Token::RBRACE, CHECK_OK);
return Expression::Default();
}
PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
// CallRuntime ::
// '%' Identifier Arguments
Expect(Token::MOD, CHECK_OK);
if (!allow_natives()) {
*ok = false;
return Expression::Default();
}
// Allow "eval" or "arguments" for backward compatibility.
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
Scanner::Location spread_pos;
ExpressionClassifier classifier;
ParseArguments(&spread_pos, &classifier, ok);
ValidateExpression(&classifier, CHECK_OK);
DCHECK(!spread_pos.IsValid());
return Expression::Default();
}
#undef CHECK_OK
} } // v8::internal