Index: src/parser.cc |
=================================================================== |
--- src/parser.cc (revision 9808) |
+++ src/parser.cc (working copy) |
@@ -407,9 +407,9 @@ |
} |
-Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) { |
+Scope* Parser::NewScope(Scope* parent, ScopeType type) { |
Scope* result = new(zone()) Scope(parent, type); |
- result->Initialize(inside_with); |
+ result->Initialize(); |
return result; |
} |
@@ -459,14 +459,32 @@ |
// ---------------------------------------------------------------------------- |
-// LexicalScope is a support class to facilitate manipulation of the |
-// Parser's scope stack. The constructor sets the parser's top scope |
-// to the incoming scope, and the destructor resets it. |
-// |
-// Additionally, it stores transient information used during parsing. |
-// These scopes are not kept around after parsing or referenced by syntax |
-// trees so they can be stack-allocated and hence used by the pre-parser. |
+// LexicalScope and SaveScope are stack allocated support classes to facilitate |
+// anipulation of the Parser's scope stack. The constructor sets the parser's |
+// top scope to the incoming scope, and the destructor resets it. Additionally, |
+// LexicalScope stores transient information used during parsing. |
+ |
+class SaveScope BASE_EMBEDDED { |
+ public: |
+ SaveScope(Parser* parser, Scope* scope) |
+ : parser_(parser), |
+ previous_top_scope_(parser->top_scope_) { |
+ parser->top_scope_ = scope; |
+ } |
+ |
+ ~SaveScope() { |
+ parser_->top_scope_ = previous_top_scope_; |
+ } |
+ |
+ private: |
+ // Bookkeeping |
+ Parser* parser_; |
+ // Previous values |
+ Scope* previous_top_scope_; |
+}; |
+ |
+ |
class LexicalScope BASE_EMBEDDED { |
public: |
LexicalScope(Parser* parser, Scope* scope, Isolate* isolate); |
@@ -516,7 +534,6 @@ |
// Previous values |
LexicalScope* lexical_scope_parent_; |
Scope* previous_scope_; |
- int previous_with_nesting_level_; |
unsigned previous_ast_node_id_; |
}; |
@@ -529,11 +546,9 @@ |
parser_(parser), |
lexical_scope_parent_(parser->lexical_scope_), |
previous_scope_(parser->top_scope_), |
- previous_with_nesting_level_(parser->with_nesting_level_), |
previous_ast_node_id_(isolate->ast_node_id()) { |
parser->top_scope_ = scope; |
parser->lexical_scope_ = this; |
- parser->with_nesting_level_ = 0; |
isolate->set_ast_node_id(AstNode::kDeclarationsId + 1); |
} |
@@ -541,7 +556,6 @@ |
LexicalScope::~LexicalScope() { |
parser_->top_scope_ = previous_scope_; |
parser_->lexical_scope_ = lexical_scope_parent_; |
- parser_->with_nesting_level_ = previous_with_nesting_level_; |
parser_->isolate()->set_ast_node_id(previous_ast_node_id_); |
} |
@@ -578,7 +592,6 @@ |
script_(script), |
scanner_(isolate_->unicode_cache()), |
top_scope_(NULL), |
- with_nesting_level_(0), |
lexical_scope_(NULL), |
target_stack_(NULL), |
allow_natives_syntax_(allow_natives_syntax), |
@@ -623,6 +636,7 @@ |
bool in_global_context, |
StrictModeFlag strict_mode, |
ZoneScope* zone_scope) { |
+ ASSERT(top_scope_ == NULL); |
ASSERT(target_stack_ == NULL); |
if (pre_data_ != NULL) pre_data_->Initialize(); |
@@ -630,18 +644,16 @@ |
mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; |
if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; |
- Scope::Type type = |
- in_global_context |
- ? Scope::GLOBAL_SCOPE |
- : Scope::EVAL_SCOPE; |
+ ScopeType type = in_global_context ? GLOBAL_SCOPE : EVAL_SCOPE; |
Handle<String> no_name = isolate()->factory()->empty_symbol(); |
FunctionLiteral* result = NULL; |
- { Scope* scope = NewScope(top_scope_, type, inside_with()); |
+ { Scope* scope = NewScope(top_scope_, type); |
+ scope->set_start_position(0); |
+ scope->set_end_position(source->length()); |
LexicalScope lexical_scope(this, scope, isolate()); |
- if (strict_mode == kStrictMode) { |
- top_scope_->EnableStrictMode(); |
- } |
+ ASSERT(top_scope_->strict_mode_flag() == kNonStrictMode); |
+ top_scope_->SetStrictModeFlag(strict_mode); |
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16); |
bool ok = true; |
int beg_loc = scanner().location().beg_pos; |
@@ -665,8 +677,6 @@ |
lexical_scope.only_simple_this_property_assignments(), |
lexical_scope.this_property_assignments(), |
0, |
- 0, |
- source->length(), |
FunctionLiteral::ANONYMOUS_EXPRESSION, |
false); // Does not have duplicate parameters. |
} else if (stack_overflow_) { |
@@ -714,6 +724,7 @@ |
ZoneScope* zone_scope) { |
Handle<SharedFunctionInfo> shared_info = info->shared_info(); |
scanner_.Initialize(source); |
+ ASSERT(top_scope_ == NULL); |
ASSERT(target_stack_ == NULL); |
Handle<String> name(String::cast(shared_info->name())); |
@@ -727,16 +738,15 @@ |
{ |
// Parse the function literal. |
- Scope* scope = NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with()); |
+ Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE); |
if (!info->closure().is_null()) { |
scope = Scope::DeserializeScopeChain(info, scope); |
} |
LexicalScope lexical_scope(this, scope, isolate()); |
- |
- if (shared_info->strict_mode()) { |
- top_scope_->EnableStrictMode(); |
- } |
- |
+ ASSERT(scope->strict_mode_flag() == kNonStrictMode || |
+ scope->strict_mode_flag() == info->strict_mode_flag()); |
+ ASSERT(info->strict_mode_flag() == shared_info->strict_mode_flag()); |
+ scope->SetStrictModeFlag(shared_info->strict_mode_flag()); |
FunctionLiteral::Type type = shared_info->is_expression() |
? (shared_info->is_anonymous() |
? FunctionLiteral::ANONYMOUS_EXPRESSION |
@@ -1128,14 +1138,14 @@ |
// In harmony mode we allow additionally the following productions |
// SourceElement: |
// LetDeclaration |
+ // ConstDeclaration |
if (peek() == Token::FUNCTION) { |
return ParseFunctionDeclaration(ok); |
- } else if (peek() == Token::LET) { |
+ } else if (peek() == Token::LET || peek() == Token::CONST) { |
return ParseVariableStatement(kSourceElement, ok); |
- } else { |
- return ParseStatement(labels, ok); |
} |
+ return ParseStatement(labels, ok); |
} |
@@ -1183,7 +1193,7 @@ |
directive->Equals(isolate()->heap()->use_strict()) && |
token_loc.end_pos - token_loc.beg_pos == |
isolate()->heap()->use_strict()->length() + 2) { |
- top_scope_->EnableStrictMode(); |
+ top_scope_->SetStrictModeFlag(kStrictMode); |
// "use strict" is the only directive for now. |
directive_prologue = false; |
} |
@@ -1321,7 +1331,7 @@ |
// FunctionDeclaration |
// Common language extension is to allow function declaration in place |
// of any statement. This language extension is disabled in strict mode. |
- if (top_scope_->is_strict_mode()) { |
+ if (top_scope_->is_strict_mode() || harmony_scoping_) { |
ReportMessageAt(scanner().peek_location(), "strict_function", |
Vector<const char*>::empty()); |
*ok = false; |
@@ -1353,6 +1363,10 @@ |
// If we are inside a function, a declaration of a var/const variable is a |
// truly local variable, and the scope of the variable is always the function |
// scope. |
+ // Let/const variables in harmony mode are always added to the immediately |
+ // enclosing scope. |
+ Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY) |
+ ? top_scope_ : top_scope_->DeclarationScope(); |
// If a function scope exists, then we can statically declare this |
// variable and also set its mode. In any case, a Declaration node |
@@ -1362,9 +1376,8 @@ |
// to the calling function context. |
// Similarly, strict mode eval scope does not leak variable declarations to |
// the caller's scope so we declare all locals, too. |
- |
- Scope* declaration_scope = mode == LET ? top_scope_ |
- : top_scope_->DeclarationScope(); |
+ // Also for block scoped let/const bindings the variable can be |
+ // statically declared. |
if (declaration_scope->is_function_scope() || |
declaration_scope->is_strict_mode_eval_scope() || |
declaration_scope->is_block_scope()) { |
@@ -1389,6 +1402,7 @@ |
// We only have vars, consts and lets in declarations. |
ASSERT(var->mode() == VAR || |
var->mode() == CONST || |
+ var->mode() == CONST_HARMONY || |
var->mode() == LET); |
if (harmony_scoping_) { |
// In harmony mode we treat re-declarations as early errors. See |
@@ -1400,8 +1414,8 @@ |
*ok = false; |
return NULL; |
} |
- const char* type = (var->mode() == VAR) ? "var" : |
- (var->mode() == CONST) ? "const" : "let"; |
+ const char* type = (var->mode() == VAR) |
+ ? "var" : var->is_const_mode() ? "const" : "let"; |
Handle<String> type_string = |
isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED); |
Expression* expression = |
@@ -1429,12 +1443,13 @@ |
// a performance issue since it may lead to repeated |
// Runtime::DeclareContextSlot() calls. |
VariableProxy* proxy = declaration_scope->NewUnresolved( |
- name, false, scanner().location().beg_pos); |
+ name, scanner().location().beg_pos); |
declaration_scope->AddDeclaration( |
new(zone()) Declaration(proxy, mode, fun, top_scope_)); |
// For global const variables we bind the proxy to a variable. |
- if (mode == CONST && declaration_scope->is_global_scope()) { |
+ if ((mode == CONST || mode == CONST_HARMONY) && |
+ declaration_scope->is_global_scope()) { |
ASSERT(resolve); // should be set by all callers |
Variable::Kind kind = Variable::NORMAL; |
var = new(zone()) Variable(declaration_scope, name, CONST, true, kind); |
@@ -1582,20 +1597,14 @@ |
// Construct block expecting 16 statements. |
Block* body = new(zone()) Block(isolate(), labels, 16, false); |
- Scope* saved_scope = top_scope_; |
- Scope* block_scope = NewScope(top_scope_, |
- Scope::BLOCK_SCOPE, |
- inside_with()); |
- if (top_scope_->is_strict_mode()) { |
- block_scope->EnableStrictMode(); |
- } |
- top_scope_ = block_scope; |
+ Scope* block_scope = NewScope(top_scope_, BLOCK_SCOPE); |
// Parse the statements and collect escaping labels. |
- TargetCollector collector; |
- Target target(&this->target_stack_, &collector); |
Expect(Token::LBRACE, CHECK_OK); |
- { |
+ block_scope->set_start_position(scanner().location().beg_pos); |
+ { SaveScope save_scope(this, block_scope); |
+ TargetCollector collector; |
+ Target target(&this->target_stack_, &collector); |
Target target_body(&this->target_stack_, body); |
InitializationBlockFinder block_finder(top_scope_, target_stack_); |
@@ -1608,8 +1617,7 @@ |
} |
} |
Expect(Token::RBRACE, CHECK_OK); |
- top_scope_ = saved_scope; |
- |
+ block_scope->set_end_position(scanner().location().end_pos); |
block_scope = block_scope->FinalizeBlockScope(); |
body->set_block_scope(block_scope); |
return body; |
@@ -1623,6 +1631,7 @@ |
Handle<String> ignore; |
Block* result = ParseVariableDeclarations(var_context, |
+ NULL, |
&ignore, |
CHECK_OK); |
ExpectSemicolon(CHECK_OK); |
@@ -1641,12 +1650,24 @@ |
// *var is untouched; in particular, it is the caller's responsibility |
// to initialize it properly. This mechanism is used for the parsing |
// of 'for-in' loops. |
-Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, |
- Handle<String>* out, |
- bool* ok) { |
+Block* Parser::ParseVariableDeclarations( |
+ VariableDeclarationContext var_context, |
+ VariableDeclarationProperties* decl_props, |
+ Handle<String>* out, |
+ bool* ok) { |
// VariableDeclarations :: |
- // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] |
- |
+ // ('var' | 'const' | 'let') (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 |
VariableMode mode = VAR; |
// True if the binding needs initialization. 'let' and 'const' declared |
// bindings are created uninitialized by their declaration nodes and |
@@ -1659,19 +1680,32 @@ |
Consume(Token::VAR); |
} else if (peek() == Token::CONST) { |
Consume(Token::CONST); |
- if (top_scope_->is_strict_mode()) { |
+ if (harmony_scoping_) { |
+ if (var_context != kSourceElement && |
+ var_context != kForStatement) { |
+ // In harmony mode 'const' declarations are only allowed in source |
+ // element positions. |
+ ReportMessage("unprotected_const", Vector<const char*>::empty()); |
+ *ok = false; |
+ return NULL; |
+ } |
+ mode = CONST_HARMONY; |
+ init_op = Token::INIT_CONST_HARMONY; |
+ } else if (top_scope_->is_strict_mode()) { |
ReportMessage("strict_const", Vector<const char*>::empty()); |
*ok = false; |
return NULL; |
+ } else { |
+ mode = CONST; |
+ init_op = Token::INIT_CONST; |
} |
- mode = CONST; |
is_const = true; |
needs_init = true; |
- init_op = Token::INIT_CONST; |
} else if (peek() == Token::LET) { |
Consume(Token::LET); |
if (var_context != kSourceElement && |
var_context != kForStatement) { |
+ // Let declarations are only allowed in source element positions. |
ASSERT(var_context == kStatement); |
ReportMessage("unprotected_let", Vector<const char*>::empty()); |
*ok = false; |
@@ -1684,7 +1718,7 @@ |
UNREACHABLE(); // by current callers |
} |
- Scope* declaration_scope = (mode == LET) |
+ Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY) |
? top_scope_ : top_scope_->DeclarationScope(); |
// 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). Thus we can |
@@ -1729,8 +1763,10 @@ |
// If we have a const declaration, in an inner scope, the proxy is always |
// bound to the declared variable (independent of possibly surrounding with |
// statements). |
- Declare(name, mode, NULL, is_const /* always bound for CONST! */, |
- CHECK_OK); |
+ // For let/const declarations in harmony mode, we can also immediately |
+ // pre-resolve the proxy because it resides in the same scope as the |
+ // declaration. |
+ Declare(name, mode, NULL, mode != VAR, CHECK_OK); |
nvars++; |
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { |
ReportMessageAt(scanner().location(), "too_many_variables", |
@@ -1769,7 +1805,8 @@ |
Scope* initialization_scope = is_const ? declaration_scope : top_scope_; |
Expression* value = NULL; |
int position = -1; |
- if (peek() == Token::ASSIGN) { |
+ // Harmony consts have non-optional initializers. |
+ if (peek() == Token::ASSIGN || mode == CONST_HARMONY) { |
Expect(Token::ASSIGN, CHECK_OK); |
position = scanner().location().beg_pos; |
value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); |
@@ -1781,6 +1818,7 @@ |
} else { |
fni_->RemoveLastFunction(); |
} |
+ if (decl_props != NULL) *decl_props = kHasInitializers; |
} |
// Make sure that 'const x' and 'let x' initialize 'x' to undefined. |
@@ -1807,7 +1845,6 @@ |
// declaration statement has been executed. This is important in |
// browsers where the global object (window) has lots of |
// properties defined in prototype objects. |
- |
if (initialization_scope->is_global_scope()) { |
// Compute the arguments for the runtime call. |
ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3); |
@@ -1832,9 +1869,7 @@ |
} else { |
// Add strict mode. |
// We may want to pass singleton to avoid Literal allocations. |
- StrictModeFlag flag = initialization_scope->is_strict_mode() |
- ? kStrictMode |
- : kNonStrictMode; |
+ StrictModeFlag flag = initialization_scope->strict_mode_flag(); |
arguments->Add(NewNumberLiteral(flag)); |
// Be careful not to assign a value to the global variable if |
@@ -1871,18 +1906,14 @@ |
// dynamically looked-up variables and constants (the start context |
// for constant lookups is always the function context, while it is |
// the top context for var declared variables). Sigh... |
- // For 'let' declared variables the initialization is in the same scope |
- // as the declaration. Thus dynamic lookups are unnecessary even if the |
- // block scope is inside a with. |
+ // For 'let' and 'const' declared variables in harmony mode the |
+ // initialization is in the same scope as the declaration. Thus dynamic |
+ // lookups are unnecessary even if the block scope is inside a with. |
if (value != NULL) { |
- bool in_with = (mode == VAR) ? inside_with() : false; |
- VariableProxy* proxy = |
- initialization_scope->NewUnresolved(name, in_with); |
+ VariableProxy* proxy = initialization_scope->NewUnresolved(name); |
Assignment* assignment = |
new(zone()) Assignment(isolate(), init_op, proxy, value, position); |
- if (block) { |
- block->AddStatement(new(zone()) ExpressionStatement(assignment)); |
- } |
+ block->AddStatement(new(zone()) ExpressionStatement(assignment)); |
} |
if (fni_ != NULL) fni_->Leave(); |
@@ -2105,10 +2136,14 @@ |
Expression* expr = ParseExpression(true, CHECK_OK); |
Expect(Token::RPAREN, CHECK_OK); |
- ++with_nesting_level_; |
top_scope_->DeclarationScope()->RecordWithStatement(); |
- Statement* stmt = ParseStatement(labels, CHECK_OK); |
- --with_nesting_level_; |
+ Scope* with_scope = NewScope(top_scope_, WITH_SCOPE); |
+ Statement* stmt; |
+ { SaveScope save_scope(this, with_scope); |
+ with_scope->set_start_position(scanner().peek_location().beg_pos); |
+ stmt = ParseStatement(labels, CHECK_OK); |
+ with_scope->set_end_position(scanner().location().end_pos); |
+ } |
return new(zone()) WithStatement(expr, stmt); |
} |
@@ -2233,6 +2268,8 @@ |
Consume(Token::CATCH); |
Expect(Token::LPAREN, CHECK_OK); |
+ catch_scope = NewScope(top_scope_, CATCH_SCOPE); |
+ catch_scope->set_start_position(scanner().location().beg_pos); |
name = ParseIdentifier(CHECK_OK); |
if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) { |
@@ -2245,21 +2282,15 @@ |
if (peek() == Token::LBRACE) { |
Target target(&this->target_stack_, &catch_collector); |
- catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); |
- if (top_scope_->is_strict_mode()) { |
- catch_scope->EnableStrictMode(); |
- } |
VariableMode mode = harmony_scoping_ ? LET : VAR; |
catch_variable = catch_scope->DeclareLocal(name, mode); |
- Scope* saved_scope = top_scope_; |
- top_scope_ = catch_scope; |
+ SaveScope save_scope(this, catch_scope); |
catch_block = ParseBlock(NULL, CHECK_OK); |
- top_scope_ = saved_scope; |
} else { |
Expect(Token::LBRACE, CHECK_OK); |
} |
- |
+ catch_scope->set_end_position(scanner().location().end_pos); |
tok = peek(); |
} |
@@ -2365,16 +2396,22 @@ |
Statement* init = NULL; |
+ // Create an in-between scope for let-bound iteration variables. |
+ Scope* saved_scope = top_scope_; |
+ Scope* for_scope = NewScope(top_scope_, BLOCK_SCOPE); |
+ top_scope_ = for_scope; |
+ |
Expect(Token::FOR, CHECK_OK); |
Expect(Token::LPAREN, CHECK_OK); |
+ for_scope->set_start_position(scanner().location().beg_pos); |
if (peek() != Token::SEMICOLON) { |
if (peek() == Token::VAR || peek() == Token::CONST) { |
Handle<String> name; |
Block* variable_statement = |
- ParseVariableDeclarations(kForStatement, &name, CHECK_OK); |
+ ParseVariableDeclarations(kForStatement, NULL, &name, CHECK_OK); |
if (peek() == Token::IN && !name.is_null()) { |
- VariableProxy* each = top_scope_->NewUnresolved(name, inside_with()); |
+ VariableProxy* each = top_scope_->NewUnresolved(name); |
ForInStatement* loop = new(zone()) ForInStatement(isolate(), labels); |
Target target(&this->target_stack_, loop); |
@@ -2387,12 +2424,73 @@ |
Block* result = new(zone()) Block(isolate(), NULL, 2, false); |
result->AddStatement(variable_statement); |
result->AddStatement(loop); |
+ top_scope_ = saved_scope; |
+ for_scope->set_end_position(scanner().location().end_pos); |
+ for_scope = for_scope->FinalizeBlockScope(); |
+ ASSERT(for_scope == NULL); |
// Parsed for-in loop w/ variable/const declaration. |
return result; |
} else { |
init = variable_statement; |
} |
+ } else if (peek() == Token::LET) { |
+ Handle<String> name; |
+ VariableDeclarationProperties decl_props = kHasNoInitializers; |
+ Block* variable_statement = |
+ ParseVariableDeclarations(kForStatement, |
+ &decl_props, |
+ &name, |
+ CHECK_OK); |
+ bool accept_IN = !name.is_null() && decl_props != kHasInitializers; |
+ if (peek() == Token::IN && accept_IN) { |
+ // Rewrite a for-in statement of the form |
+ // |
+ // for (let x in e) b |
+ // |
+ // into |
+ // |
+ // <let x' be a temporary variable> |
+ // for (x' in e) { |
+ // let x; |
+ // x = x'; |
+ // b; |
+ // } |
+ // TODO(keuchel): Move the temporary variable to the block scope, after |
+ // implementing stack allocated block scoped variables. |
+ Variable* temp = top_scope_->DeclarationScope()->NewTemporary(name); |
+ VariableProxy* temp_proxy = new(zone()) VariableProxy(isolate(), temp); |
+ VariableProxy* each = top_scope_->NewUnresolved(name, inside_with()); |
+ ForInStatement* loop = new(zone()) ForInStatement(isolate(), labels); |
+ Target target(&this->target_stack_, loop); |
+ |
+ Expect(Token::IN, CHECK_OK); |
+ Expression* enumerable = ParseExpression(true, CHECK_OK); |
+ Expect(Token::RPAREN, CHECK_OK); |
+ |
+ Statement* body = ParseStatement(NULL, CHECK_OK); |
+ Block* body_block = new(zone()) Block(isolate(), NULL, 3, false); |
+ Assignment* assignment = new(zone()) Assignment(isolate(), |
+ Token::ASSIGN, |
+ each, |
+ temp_proxy, |
+ RelocInfo::kNoPosition); |
+ Statement* assignment_statement = |
+ new(zone()) ExpressionStatement(assignment); |
+ body_block->AddStatement(variable_statement); |
+ body_block->AddStatement(assignment_statement); |
+ body_block->AddStatement(body); |
+ loop->Initialize(temp_proxy, enumerable, body_block); |
+ top_scope_ = saved_scope; |
+ for_scope->set_end_position(scanner().location().end_pos); |
+ for_scope = for_scope->FinalizeBlockScope(); |
+ body_block->set_block_scope(for_scope); |
+ // Parsed for-in loop w/ let declaration. |
+ return loop; |
+ |
+ } else { |
+ init = variable_statement; |
+ } |
} else { |
Expression* expression = ParseExpression(false, CHECK_OK); |
if (peek() == Token::IN) { |
@@ -2414,6 +2512,10 @@ |
Statement* body = ParseStatement(NULL, CHECK_OK); |
if (loop) loop->Initialize(expression, enumerable, body); |
+ top_scope_ = saved_scope; |
+ for_scope->set_end_position(scanner().location().end_pos); |
+ for_scope = for_scope->FinalizeBlockScope(); |
+ ASSERT(for_scope == NULL); |
// Parsed for-in loop. |
return loop; |
@@ -2444,8 +2546,31 @@ |
Expect(Token::RPAREN, CHECK_OK); |
Statement* body = ParseStatement(NULL, CHECK_OK); |
- if (loop) loop->Initialize(init, cond, next, body); |
- return loop; |
+ top_scope_ = saved_scope; |
+ for_scope->set_end_position(scanner().location().end_pos); |
+ for_scope = for_scope->FinalizeBlockScope(); |
+ if (for_scope != NULL) { |
+ // Rewrite a for statement of the form |
+ // |
+ // for (let x = i; c; n) b |
+ // |
+ // into |
+ // |
+ // { |
+ // let x = i; |
+ // for (; c; n) b |
+ // } |
+ ASSERT(init != NULL); |
+ Block* result = new(zone()) Block(isolate(), NULL, 2, false); |
+ result->AddStatement(init); |
+ result->AddStatement(loop); |
+ result->set_block_scope(for_scope); |
+ if (loop) loop->Initialize(NULL, cond, next, body); |
+ return result; |
+ } else { |
+ if (loop) loop->Initialize(init, cond, next, body); |
+ return loop; |
+ } |
} |
@@ -3065,9 +3190,7 @@ |
case Token::FUTURE_STRICT_RESERVED_WORD: { |
Handle<String> name = ParseIdentifier(CHECK_OK); |
if (fni_ != NULL) fni_->PushVariableName(name); |
- result = top_scope_->NewUnresolved(name, |
- inside_with(), |
- scanner().location().beg_pos); |
+ result = top_scope_->NewUnresolved(name, scanner().location().beg_pos); |
break; |
} |
@@ -3184,9 +3307,11 @@ |
// Update the scope information before the pre-parsing bailout. |
int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); |
- // Allocate a fixed array with all the literals. |
- Handle<FixedArray> literals = |
+ // Allocate a fixed array to hold all the object literals. |
+ Handle<FixedArray> object_literals = |
isolate()->factory()->NewFixedArray(values->length(), TENURED); |
+ Handle<FixedDoubleArray> double_literals; |
+ ElementsKind elements_kind = FAST_SMI_ONLY_ELEMENTS; |
// Fill in the literals. |
bool is_simple = true; |
@@ -3198,19 +3323,75 @@ |
} |
Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i)); |
if (boilerplate_value->IsUndefined()) { |
- literals->set_the_hole(i); |
+ object_literals->set_the_hole(i); |
+ if (elements_kind == FAST_DOUBLE_ELEMENTS) { |
+ double_literals->set_the_hole(i); |
+ } |
is_simple = false; |
} else { |
- literals->set(i, *boilerplate_value); |
+ // Examine each literal element, and adjust the ElementsKind if the |
+ // literal element is not of a type that can be stored in the current |
+ // ElementsKind. Start with FAST_SMI_ONLY_ELEMENTS, and transition to |
+ // FAST_DOUBLE_ELEMENTS and FAST_ELEMENTS as necessary. Always remember |
+ // the tagged value, no matter what the ElementsKind is in case we |
+ // ultimately end up in FAST_ELEMENTS. |
+ object_literals->set(i, *boilerplate_value); |
+ if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
+ // Smi only elements. Notice if a transition to FAST_DOUBLE_ELEMENTS or |
+ // FAST_ELEMENTS is required. |
+ if (!boilerplate_value->IsSmi()) { |
+ if (boilerplate_value->IsNumber() && FLAG_smi_only_arrays) { |
+ // Allocate a double array on the FAST_DOUBLE_ELEMENTS transition to |
+ // avoid over-allocating in TENURED space. |
+ double_literals = isolate()->factory()->NewFixedDoubleArray( |
+ values->length(), TENURED); |
+ // Copy the contents of the FAST_SMI_ONLY_ELEMENT array to the |
+ // FAST_DOUBLE_ELEMENTS array so that they are in sync. |
+ for (int j = 0; j < i; ++j) { |
+ Object* smi_value = object_literals->get(j); |
+ if (smi_value->IsTheHole()) { |
+ double_literals->set_the_hole(j); |
+ } else { |
+ double_literals->set(j, Smi::cast(smi_value)->value()); |
+ } |
+ } |
+ double_literals->set(i, boilerplate_value->Number()); |
+ elements_kind = FAST_DOUBLE_ELEMENTS; |
+ } else { |
+ elements_kind = FAST_ELEMENTS; |
+ } |
+ } |
+ } else if (elements_kind == FAST_DOUBLE_ELEMENTS) { |
+ // Continue to store double values in to FAST_DOUBLE_ELEMENTS arrays |
+ // until the first value is seen that can't be stored as a double. |
+ if (boilerplate_value->IsNumber()) { |
+ double_literals->set(i, boilerplate_value->Number()); |
+ } else { |
+ elements_kind = FAST_ELEMENTS; |
+ } |
+ } |
} |
} |
// Simple and shallow arrays can be lazily copied, we transform the |
// elements array to a copy-on-write array. |
- if (is_simple && depth == 1 && values->length() > 0) { |
- literals->set_map(isolate()->heap()->fixed_cow_array_map()); |
+ if (is_simple && depth == 1 && values->length() > 0 && |
+ elements_kind != FAST_DOUBLE_ELEMENTS) { |
+ object_literals->set_map(isolate()->heap()->fixed_cow_array_map()); |
} |
+ Handle<FixedArrayBase> element_values = elements_kind == FAST_DOUBLE_ELEMENTS |
+ ? Handle<FixedArrayBase>(double_literals) |
+ : Handle<FixedArrayBase>(object_literals); |
+ |
+ // Remember both the literal's constant values as well as the ElementsKind |
+ // in a 2-element FixedArray. |
+ Handle<FixedArray> literals = |
+ isolate()->factory()->NewFixedArray(2, TENURED); |
+ |
+ literals->set(0, Smi::FromInt(elements_kind)); |
+ literals->set(1, *element_values); |
+ |
return new(zone()) ArrayLiteral( |
isolate(), literals, values, literal_index, is_simple, depth); |
} |
@@ -3715,13 +3896,11 @@ |
// hoisted. In harmony block scoping mode they are block scoped, so they |
// are not hoisted. |
Scope* scope = (type == FunctionLiteral::DECLARATION && !harmony_scoping_) |
- ? NewScope(top_scope_->DeclarationScope(), Scope::FUNCTION_SCOPE, false) |
- : NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); |
+ ? NewScope(top_scope_->DeclarationScope(), FUNCTION_SCOPE) |
+ : NewScope(top_scope_, FUNCTION_SCOPE); |
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8); |
int materialized_literal_count; |
int expected_property_count; |
- int start_pos; |
- int end_pos; |
bool only_simple_this_property_assignments; |
Handle<FixedArray> this_property_assignments; |
bool has_duplicate_parameters = false; |
@@ -3732,7 +3911,7 @@ |
// FormalParameterList :: |
// '(' (Identifier)*[','] ')' |
Expect(Token::LPAREN, CHECK_OK); |
- start_pos = scanner().location().beg_pos; |
+ scope->set_start_position(scanner().location().beg_pos); |
Scanner::Location name_loc = Scanner::Location::invalid(); |
Scanner::Location dupe_loc = Scanner::Location::invalid(); |
Scanner::Location reserved_loc = Scanner::Location::invalid(); |
@@ -3778,13 +3957,21 @@ |
// future we can change the AST to only refer to VariableProxies |
// instead of Variables and Proxis as is the case now. |
if (type == FunctionLiteral::NAMED_EXPRESSION) { |
- Variable* fvar = top_scope_->DeclareFunctionVar(function_name); |
- VariableProxy* fproxy = |
- top_scope_->NewUnresolved(function_name, inside_with()); |
+ VariableMode fvar_mode; |
+ Token::Value fvar_init_op; |
+ if (harmony_scoping_) { |
+ fvar_mode = CONST_HARMONY; |
+ fvar_init_op = Token::INIT_CONST_HARMONY; |
+ } else { |
+ fvar_mode = CONST; |
+ fvar_init_op = Token::INIT_CONST; |
+ } |
+ Variable* fvar = top_scope_->DeclareFunctionVar(function_name, fvar_mode); |
+ VariableProxy* fproxy = top_scope_->NewUnresolved(function_name); |
fproxy->BindTo(fvar); |
body->Add(new(zone()) ExpressionStatement( |
new(zone()) Assignment(isolate(), |
- Token::INIT_CONST, |
+ fvar_init_op, |
fproxy, |
new(zone()) ThisFunction(isolate()), |
RelocInfo::kNoPosition))); |
@@ -3808,18 +3995,18 @@ |
// compile after all. |
is_lazily_compiled = false; |
} else { |
- end_pos = entry.end_pos(); |
- if (end_pos <= function_block_pos) { |
+ scope->set_end_position(entry.end_pos()); |
+ if (scope->end_position() <= function_block_pos) { |
// End position greater than end of stream is safe, and hard to check. |
ReportInvalidPreparseData(function_name, CHECK_OK); |
} |
isolate()->counters()->total_preparse_skipped()->Increment( |
- end_pos - function_block_pos); |
+ scope->end_position() - function_block_pos); |
// Seek to position just before terminal '}'. |
- scanner().SeekForward(end_pos - 1); |
+ scanner().SeekForward(scope->end_position() - 1); |
materialized_literal_count = entry.literal_count(); |
expected_property_count = entry.property_count(); |
- if (entry.strict_mode()) top_scope_->EnableStrictMode(); |
+ if (entry.strict_mode()) top_scope_->SetStrictModeFlag(kStrictMode); |
only_simple_this_property_assignments = false; |
this_property_assignments = isolate()->factory()->empty_fixed_array(); |
Expect(Token::RBRACE, CHECK_OK); |
@@ -3836,12 +4023,13 @@ |
this_property_assignments = lexical_scope.this_property_assignments(); |
Expect(Token::RBRACE, CHECK_OK); |
- end_pos = scanner().location().end_pos; |
+ scope->set_end_position(scanner().location().end_pos); |
} |
// Validate strict mode. |
if (top_scope_->is_strict_mode()) { |
if (IsEvalOrArguments(function_name)) { |
+ int start_pos = scope->start_position(); |
int position = function_token_position != RelocInfo::kNoPosition |
? function_token_position |
: (start_pos > 0 ? start_pos - 1 : start_pos); |
@@ -3864,6 +4052,7 @@ |
return NULL; |
} |
if (name_is_strict_reserved) { |
+ int start_pos = scope->start_position(); |
int position = function_token_position != RelocInfo::kNoPosition |
? function_token_position |
: (start_pos > 0 ? start_pos - 1 : start_pos); |
@@ -3879,7 +4068,9 @@ |
*ok = false; |
return NULL; |
} |
- CheckOctalLiteral(start_pos, end_pos, CHECK_OK); |
+ CheckOctalLiteral(scope->start_position(), |
+ scope->end_position(), |
+ CHECK_OK); |
} |
} |
@@ -3897,8 +4088,6 @@ |
only_simple_this_property_assignments, |
this_property_assignments, |
num_parameters, |
- start_pos, |
- end_pos, |
type, |
has_duplicate_parameters); |
function_literal->set_function_token_position(function_token_position); |
@@ -5119,17 +5308,16 @@ |
// Create a Scanner for the preparser to use as input, and preparse the source. |
static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, |
- bool allow_lazy, |
- ParserRecorder* recorder, |
- bool harmony_scoping) { |
+ int flags, |
+ ParserRecorder* recorder) { |
Isolate* isolate = Isolate::Current(); |
JavaScriptScanner scanner(isolate->unicode_cache()); |
- scanner.SetHarmonyScoping(harmony_scoping); |
+ scanner.SetHarmonyScoping((flags & kHarmonyScoping) != 0); |
scanner.Initialize(source); |
intptr_t stack_limit = isolate->stack_guard()->real_climit(); |
if (!preparser::PreParser::PreParseProgram(&scanner, |
recorder, |
- allow_lazy, |
+ flags, |
stack_limit)) { |
isolate->StackOverflow(); |
return NULL; |
@@ -5146,25 +5334,28 @@ |
// even if the preparser data is only used once. |
ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, |
v8::Extension* extension, |
- bool harmony_scoping) { |
+ int flags) { |
bool allow_lazy = FLAG_lazy && (extension == NULL); |
if (!allow_lazy) { |
// Partial preparsing is only about lazily compiled functions. |
// If we don't allow lazy compilation, the log data will be empty. |
return NULL; |
} |
+ flags |= kAllowLazy; |
PartialParserRecorder recorder; |
- return DoPreParse(source, allow_lazy, &recorder, harmony_scoping); |
+ return DoPreParse(source, flags, &recorder); |
} |
ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source, |
v8::Extension* extension, |
- bool harmony_scoping) { |
+ int flags) { |
Handle<Script> no_script; |
- bool allow_lazy = FLAG_lazy && (extension == NULL); |
+ if (FLAG_lazy && (extension == NULL)) { |
+ flags |= kAllowLazy; |
+ } |
CompleteParserRecorder recorder; |
- return DoPreParse(source, allow_lazy, &recorder, harmony_scoping); |
+ return DoPreParse(source, flags, &recorder); |
} |
@@ -5227,7 +5418,7 @@ |
Handle<String> source = Handle<String>(String::cast(script->source())); |
result = parser.ParseProgram(source, |
info->is_global(), |
- info->StrictMode()); |
+ info->strict_mode_flag()); |
} |
} |
info->SetFunction(result); |