| Index: src/parser.cc
|
| ===================================================================
|
| --- src/parser.cc (revision 8618)
|
| +++ src/parser.cc (working copy)
|
| @@ -411,6 +411,7 @@
|
| return result;
|
| }
|
|
|
| +
|
| // ----------------------------------------------------------------------------
|
| // Target is a support class to facilitate manipulation of the
|
| // Parser's target_stack_ (the stack of potential 'break' and
|
| @@ -536,7 +537,6 @@
|
|
|
|
|
| LexicalScope::~LexicalScope() {
|
| - parser_->top_scope_->Leave();
|
| parser_->top_scope_ = previous_scope_;
|
| parser_->lexical_scope_ = lexical_scope_parent_;
|
| parser_->with_nesting_level_ = previous_with_nesting_level_;
|
| @@ -592,11 +592,11 @@
|
| FunctionLiteral* Parser::ParseProgram(Handle<String> source,
|
| bool in_global_context,
|
| StrictModeFlag strict_mode) {
|
| - CompilationZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
|
| + ZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
|
|
|
| HistogramTimerScope timer(isolate()->counters()->parse());
|
| isolate()->counters()->total_parse_size()->Increment(source->length());
|
| - fni_ = new(zone()) FuncNameInferrer();
|
| + fni_ = new(zone()) FuncNameInferrer(isolate());
|
|
|
| // Initialize parser state.
|
| source->TryFlatten();
|
| @@ -658,6 +658,7 @@
|
| 0,
|
| 0,
|
| source->length(),
|
| + false,
|
| false);
|
| } else if (stack_overflow_) {
|
| isolate()->StackOverflow();
|
| @@ -674,7 +675,7 @@
|
| }
|
|
|
| FunctionLiteral* Parser::ParseLazy(CompilationInfo* info) {
|
| - CompilationZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
|
| + ZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
|
| HistogramTimerScope timer(isolate()->counters()->parse_lazy());
|
| Handle<String> source(String::cast(script_->source()));
|
| isolate()->counters()->total_parse_size()->Increment(source->length());
|
| @@ -707,7 +708,7 @@
|
| ASSERT(target_stack_ == NULL);
|
|
|
| Handle<String> name(String::cast(shared_info->name()));
|
| - fni_ = new(zone()) FuncNameInferrer();
|
| + fni_ = new(zone()) FuncNameInferrer(isolate());
|
| fni_->PushEnclosingName(name);
|
|
|
| mode_ = PARSE_EAGERLY;
|
| @@ -822,14 +823,24 @@
|
| // form expr.a = ...; expr.b = ...; etc.
|
| class InitializationBlockFinder : public ParserFinder {
|
| public:
|
| - InitializationBlockFinder()
|
| - : first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {}
|
| + // We find and mark the initialization blocks in top level
|
| + // non-looping code only. This is because the optimization prevents
|
| + // reuse of the map transitions, so it should be used only for code
|
| + // that will only be run once.
|
| + InitializationBlockFinder(Scope* top_scope, Target* target)
|
| + : enabled_(top_scope->DeclarationScope()->is_global_scope() &&
|
| + !IsLoopTarget(target)),
|
| + first_in_block_(NULL),
|
| + last_in_block_(NULL),
|
| + block_size_(0) {}
|
|
|
| ~InitializationBlockFinder() {
|
| + if (!enabled_) return;
|
| if (InBlock()) EndBlock();
|
| }
|
|
|
| void Update(Statement* stat) {
|
| + if (!enabled_) return;
|
| Assignment* assignment = AsAssignment(stat);
|
| if (InBlock()) {
|
| if (BlockContinues(assignment)) {
|
| @@ -850,6 +861,14 @@
|
| // the overhead exceeds the savings below this limit.
|
| static const int kMinInitializationBlock = 3;
|
|
|
| + static bool IsLoopTarget(Target* target) {
|
| + while (target != NULL) {
|
| + if (target->node()->AsIterationStatement() != NULL) return true;
|
| + target = target->previous();
|
| + }
|
| + return false;
|
| + }
|
| +
|
| // Returns true if the expressions appear to denote the same object.
|
| // In the context of initialization blocks, we only consider expressions
|
| // of the form 'expr.x' or expr["x"].
|
| @@ -912,6 +931,7 @@
|
|
|
| bool InBlock() { return first_in_block_ != NULL; }
|
|
|
| + const bool enabled_;
|
| Assignment* first_in_block_;
|
| Assignment* last_in_block_;
|
| int block_size_;
|
| @@ -1077,7 +1097,7 @@
|
| TargetScope scope(&this->target_stack_);
|
|
|
| ASSERT(processor != NULL);
|
| - InitializationBlockFinder block_finder;
|
| + InitializationBlockFinder block_finder(top_scope_, target_stack_);
|
| ThisNamedPropertyAssigmentFinder this_property_assignment_finder(isolate());
|
| bool directive_prologue = true; // Parsing directive prologue.
|
|
|
| @@ -1132,12 +1152,7 @@
|
| }
|
| }
|
|
|
| - // We find and mark the initialization blocks on top level code only.
|
| - // This is because the optimization prevents reuse of the map transitions,
|
| - // so it should be used only for code that will only be run once.
|
| - if (top_scope_->is_global_scope()) {
|
| - block_finder.Update(stat);
|
| - }
|
| + block_finder.Update(stat);
|
| // Find and mark all assignments to named properties in this (this.x =)
|
| if (top_scope_->is_function_scope()) {
|
| this_property_assignment_finder.Update(top_scope_, stat);
|
| @@ -1269,9 +1284,6 @@
|
| return ParseFunctionDeclaration(ok);
|
| }
|
|
|
| - case Token::NATIVE:
|
| - return ParseNativeDeclaration(ok);
|
| -
|
| case Token::DEBUGGER:
|
| stmt = ParseDebuggerStatement(ok);
|
| break;
|
| @@ -1304,13 +1316,14 @@
|
| // 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.
|
| - if (top_scope_->is_function_scope() ||
|
| - top_scope_->is_strict_mode_eval_scope()) {
|
| + Scope* declaration_scope = top_scope_->DeclarationScope();
|
| + if (declaration_scope->is_function_scope() ||
|
| + declaration_scope->is_strict_mode_eval_scope()) {
|
| // Declare the variable in the function scope.
|
| - var = top_scope_->LocalLookup(name);
|
| + var = declaration_scope->LocalLookup(name);
|
| if (var == NULL) {
|
| // Declare the name.
|
| - var = top_scope_->DeclareLocal(name, mode, Scope::VAR_OR_CONST);
|
| + var = declaration_scope->DeclareLocal(name, mode);
|
| } else {
|
| // The name was declared before; check for conflicting
|
| // re-declarations. If the previous declaration was a const or the
|
| @@ -1326,7 +1339,7 @@
|
| Expression* expression =
|
| NewThrowTypeError(isolate()->factory()->redeclaration_symbol(),
|
| type_string, name);
|
| - top_scope_->SetIllegalRedeclaration(expression);
|
| + declaration_scope->SetIllegalRedeclaration(expression);
|
| }
|
| }
|
| }
|
| @@ -1347,14 +1360,18 @@
|
| // semantic issue as long as we keep the source order, but it may be
|
| // a performance issue since it may lead to repeated
|
| // Runtime::DeclareContextSlot() calls.
|
| - VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with());
|
| - top_scope_->AddDeclaration(new(zone()) Declaration(proxy, mode, fun));
|
| + VariableProxy* proxy = declaration_scope->NewUnresolved(name, false);
|
| + declaration_scope->AddDeclaration(new(zone()) Declaration(proxy, mode, fun));
|
|
|
| // For global const variables we bind the proxy to a variable.
|
| - if (mode == Variable::CONST && top_scope_->is_global_scope()) {
|
| + if (mode == Variable::CONST && declaration_scope->is_global_scope()) {
|
| ASSERT(resolve); // should be set by all callers
|
| Variable::Kind kind = Variable::NORMAL;
|
| - var = new(zone()) Variable(top_scope_, name, Variable::CONST, true, kind);
|
| + var = new(zone()) Variable(declaration_scope,
|
| + name,
|
| + Variable::CONST,
|
| + true,
|
| + kind);
|
| }
|
|
|
| // If requested and we have a local variable, bind the proxy to the variable
|
| @@ -1392,13 +1409,6 @@
|
| // declaration is resolved by looking up the function through a
|
| // callback provided by the extension.
|
| Statement* Parser::ParseNativeDeclaration(bool* ok) {
|
| - if (extension_ == NULL) {
|
| - ReportUnexpectedToken(Token::NATIVE);
|
| - *ok = false;
|
| - return NULL;
|
| - }
|
| -
|
| - Expect(Token::NATIVE, CHECK_OK);
|
| Expect(Token::FUNCTION, CHECK_OK);
|
| Handle<String> name = ParseIdentifier(CHECK_OK);
|
| Expect(Token::LPAREN, CHECK_OK);
|
| @@ -1417,7 +1427,7 @@
|
| // isn't lazily compiled. The extension structures are only
|
| // accessible while parsing the first time not when reparsing
|
| // because of lazy compilation.
|
| - top_scope_->ForceEagerCompilation();
|
| + top_scope_->DeclarationScope()->ForceEagerCompilation();
|
|
|
| // Compute the function template for the native function.
|
| v8::Handle<v8::FunctionTemplate> fun_template =
|
| @@ -1455,10 +1465,11 @@
|
| // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
|
| Expect(Token::FUNCTION, CHECK_OK);
|
| int function_token_position = scanner().location().beg_pos;
|
| - bool is_reserved = false;
|
| - Handle<String> name = ParseIdentifierOrReservedWord(&is_reserved, CHECK_OK);
|
| + bool is_strict_reserved = false;
|
| + Handle<String> name = ParseIdentifierOrStrictReservedWord(
|
| + &is_strict_reserved, CHECK_OK);
|
| FunctionLiteral* fun = ParseFunctionLiteral(name,
|
| - is_reserved,
|
| + is_strict_reserved,
|
| function_token_position,
|
| DECLARATION,
|
| CHECK_OK);
|
| @@ -1481,9 +1492,13 @@
|
| Block* result = new(zone()) Block(labels, 16, false);
|
| Target target(&this->target_stack_, result);
|
| Expect(Token::LBRACE, CHECK_OK);
|
| + InitializationBlockFinder block_finder(top_scope_, target_stack_);
|
| while (peek() != Token::RBRACE) {
|
| Statement* stat = ParseStatement(NULL, CHECK_OK);
|
| - if (stat && !stat->IsEmpty()) result->AddStatement(stat);
|
| + if (stat && !stat->IsEmpty()) {
|
| + result->AddStatement(stat);
|
| + block_finder.Update(stat);
|
| + }
|
| }
|
| Expect(Token::RBRACE, CHECK_OK);
|
| return result;
|
| @@ -1494,8 +1509,8 @@
|
| // VariableStatement ::
|
| // VariableDeclarations ';'
|
|
|
| - Expression* dummy; // to satisfy the ParseVariableDeclarations() signature
|
| - Block* result = ParseVariableDeclarations(true, &dummy, CHECK_OK);
|
| + Handle<String> ignore;
|
| + Block* result = ParseVariableDeclarations(true, &ignore, CHECK_OK);
|
| ExpectSemicolon(CHECK_OK);
|
| return result;
|
| }
|
| @@ -1513,18 +1528,19 @@
|
| // to initialize it properly. This mechanism is used for the parsing
|
| // of 'for-in' loops.
|
| Block* Parser::ParseVariableDeclarations(bool accept_IN,
|
| - Expression** var,
|
| + Handle<String>* out,
|
| bool* ok) {
|
| // VariableDeclarations ::
|
| // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
|
|
|
| Variable::Mode mode = Variable::VAR;
|
| bool is_const = false;
|
| + Scope* declaration_scope = top_scope_->DeclarationScope();
|
| if (peek() == Token::VAR) {
|
| Consume(Token::VAR);
|
| } else if (peek() == Token::CONST) {
|
| Consume(Token::CONST);
|
| - if (top_scope_->is_strict_mode()) {
|
| + if (declaration_scope->is_strict_mode()) {
|
| ReportMessage("strict_const", Vector<const char*>::empty());
|
| *ok = false;
|
| return NULL;
|
| @@ -1549,18 +1565,18 @@
|
| //
|
| // Create new block with one expected declaration.
|
| Block* block = new(zone()) Block(NULL, 1, true);
|
| - VariableProxy* last_var = NULL; // the last variable declared
|
| int nvars = 0; // the number of variables declared
|
| + Handle<String> name;
|
| do {
|
| if (fni_ != NULL) fni_->Enter();
|
|
|
| // Parse variable name.
|
| if (nvars > 0) Consume(Token::COMMA);
|
| - Handle<String> name = ParseIdentifier(CHECK_OK);
|
| + name = ParseIdentifier(CHECK_OK);
|
| if (fni_ != NULL) fni_->PushVariableName(name);
|
|
|
| // Strict mode variables may not be named eval or arguments
|
| - if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) {
|
| + if (declaration_scope->is_strict_mode() && IsEvalOrArguments(name)) {
|
| ReportMessage("strict_var_name", Vector<const char*>::empty());
|
| *ok = false;
|
| return NULL;
|
| @@ -1578,11 +1594,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).
|
| - last_var = Declare(name, mode, NULL,
|
| - is_const /* always bound for CONST! */,
|
| - CHECK_OK);
|
| + Declare(name, mode, NULL, is_const /* always bound for CONST! */,
|
| + CHECK_OK);
|
| nvars++;
|
| - if (top_scope_->num_var_or_const() > kMaxNumFunctionLocals) {
|
| + if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
| ReportMessageAt(scanner().location(), "too_many_variables",
|
| Vector<const char*>::empty());
|
| *ok = false;
|
| @@ -1598,10 +1613,10 @@
|
| //
|
| // var v; v = x;
|
| //
|
| - // In particular, we need to re-lookup 'v' as it may be a
|
| - // different 'v' than the 'v' in the declaration (if we are inside
|
| - // a 'with' statement that makes a object property with name 'v'
|
| - // visible).
|
| + // In particular, we need to re-lookup 'v' (in top_scope_, not
|
| + // declaration_scope) as it may be a different 'v' than the 'v' in the
|
| + // declaration (e.g., if we are inside a 'with' statement or 'catch'
|
| + // block).
|
| //
|
| // However, note that const declarations are different! A const
|
| // declaration of the form:
|
| @@ -1616,6 +1631,7 @@
|
| // one - there is no re-lookup (see the last parameter of the
|
| // Declare() call above).
|
|
|
| + Scope* initialization_scope = is_const ? declaration_scope : top_scope_;
|
| Expression* value = NULL;
|
| int position = -1;
|
| if (peek() == Token::ASSIGN) {
|
| @@ -1623,7 +1639,11 @@
|
| position = scanner().location().beg_pos;
|
| value = ParseAssignmentExpression(accept_IN, CHECK_OK);
|
| // Don't infer if it is "a = function(){...}();"-like expression.
|
| - if (fni_ != NULL && value->AsCall() == NULL) fni_->Infer();
|
| + if (fni_ != NULL &&
|
| + value->AsCall() == NULL &&
|
| + value->AsCallNew() == NULL) {
|
| + fni_->Infer();
|
| + }
|
| }
|
|
|
| // Make sure that 'const c' actually initializes 'c' to undefined
|
| @@ -1652,7 +1672,7 @@
|
| // browsers where the global object (window) has lots of
|
| // properties defined in prototype objects.
|
|
|
| - if (top_scope_->is_global_scope()) {
|
| + if (initialization_scope->is_global_scope()) {
|
| // Compute the arguments for the runtime call.
|
| ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3);
|
| // We have at least 1 parameter.
|
| @@ -1675,8 +1695,10 @@
|
| } else {
|
| // Add strict mode.
|
| // We may want to pass singleton to avoid Literal allocations.
|
| - arguments->Add(NewNumberLiteral(
|
| - top_scope_->is_strict_mode() ? kStrictMode : kNonStrictMode));
|
| + StrictModeFlag flag = initialization_scope->is_strict_mode()
|
| + ? kStrictMode
|
| + : kNonStrictMode;
|
| + arguments->Add(NewNumberLiteral(flag));
|
|
|
| // Be careful not to assign a value to the global variable if
|
| // we're in a with. The initialization value should not
|
| @@ -1713,8 +1735,11 @@
|
| // the top context for variables). Sigh...
|
| if (value != NULL) {
|
| Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR);
|
| + bool in_with = is_const ? false : inside_with();
|
| + VariableProxy* proxy =
|
| + initialization_scope->NewUnresolved(name, in_with);
|
| Assignment* assignment =
|
| - new(zone()) Assignment(op, last_var, value, position);
|
| + new(zone()) Assignment(op, proxy, value, position);
|
| if (block) {
|
| block->AddStatement(new(zone()) ExpressionStatement(assignment));
|
| }
|
| @@ -1723,10 +1748,10 @@
|
| if (fni_ != NULL) fni_->Leave();
|
| } while (peek() == Token::COMMA);
|
|
|
| - if (!is_const && nvars == 1) {
|
| - // We have a single, non-const variable.
|
| - ASSERT(last_var != NULL);
|
| - *var = last_var;
|
| + // If there was a single non-const declaration, return it in the output
|
| + // parameter for possible use by for/in.
|
| + if (nvars == 1 && !is_const) {
|
| + *out = name;
|
| }
|
|
|
| return block;
|
| @@ -1751,7 +1776,7 @@
|
| // Identifier ':' Statement
|
| bool starts_with_idenfifier = peek_any_identifier();
|
| Expression* expr = ParseExpression(true, CHECK_OK);
|
| - if (peek() == Token::COLON && starts_with_idenfifier && expr &&
|
| + if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL &&
|
| expr->AsVariableProxy() != NULL &&
|
| !expr->AsVariableProxy()->is_this()) {
|
| // Expression is a single identifier, and not, e.g., a parenthesized
|
| @@ -1781,6 +1806,20 @@
|
| return ParseStatement(labels, ok);
|
| }
|
|
|
| + // If we have an extension, we allow a native function declaration.
|
| + // A native function declaration starts with "native function" with
|
| + // no line-terminator between the two words.
|
| + if (extension_ != NULL &&
|
| + peek() == Token::FUNCTION &&
|
| + !scanner().HasAnyLineTerminatorBeforeNext() &&
|
| + expr != NULL &&
|
| + expr->AsVariableProxy() != NULL &&
|
| + expr->AsVariableProxy()->name()->Equals(
|
| + isolate()->heap()->native_symbol()) &&
|
| + !scanner().literal_contains_escapes()) {
|
| + return ParseNativeDeclaration(ok);
|
| + }
|
| +
|
| // Parsed expression statement.
|
| ExpectSemicolon(CHECK_OK);
|
| return new(zone()) ExpressionStatement(expr);
|
| @@ -1814,7 +1853,7 @@
|
| Expect(Token::CONTINUE, CHECK_OK);
|
| Handle<String> label = Handle<String>::null();
|
| Token::Value tok = peek();
|
| - if (!scanner().has_line_terminator_before_next() &&
|
| + if (!scanner().HasAnyLineTerminatorBeforeNext() &&
|
| tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
| label = ParseIdentifier(CHECK_OK);
|
| }
|
| @@ -1844,7 +1883,7 @@
|
| Expect(Token::BREAK, CHECK_OK);
|
| Handle<String> label;
|
| Token::Value tok = peek();
|
| - if (!scanner().has_line_terminator_before_next() &&
|
| + if (!scanner().HasAnyLineTerminatorBeforeNext() &&
|
| tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
| label = ParseIdentifier(CHECK_OK);
|
| }
|
| @@ -1886,14 +1925,16 @@
|
| // function. See ECMA-262, section 12.9, page 67.
|
| //
|
| // To be consistent with KJS we report the syntax error at runtime.
|
| - if (!top_scope_->is_function_scope()) {
|
| + Scope* declaration_scope = top_scope_->DeclarationScope();
|
| + if (declaration_scope->is_global_scope() ||
|
| + declaration_scope->is_eval_scope()) {
|
| Handle<String> type = isolate()->factory()->illegal_return_symbol();
|
| Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null());
|
| return new(zone()) ExpressionStatement(throw_error);
|
| }
|
|
|
| Token::Value tok = peek();
|
| - if (scanner().has_line_terminator_before_next() ||
|
| + if (scanner().HasAnyLineTerminatorBeforeNext() ||
|
| tok == Token::SEMICOLON ||
|
| tok == Token::RBRACE ||
|
| tok == Token::EOS) {
|
| @@ -1907,17 +1948,13 @@
|
| }
|
|
|
|
|
| -Block* Parser::WithHelper(Expression* obj,
|
| - ZoneStringList* labels,
|
| - bool is_catch_block,
|
| - bool* ok) {
|
| +Block* Parser::WithHelper(Expression* obj, ZoneStringList* labels, bool* ok) {
|
| // Parse the statement and collect escaping labels.
|
| - ZoneList<Label*>* target_list = new(zone()) ZoneList<Label*>(0);
|
| - TargetCollector collector(target_list);
|
| + TargetCollector collector;
|
| Statement* stat;
|
| { Target target(&this->target_stack_, &collector);
|
| with_nesting_level_++;
|
| - top_scope_->RecordWithStatement();
|
| + top_scope_->DeclarationScope()->RecordWithStatement();
|
| stat = ParseStatement(labels, CHECK_OK);
|
| with_nesting_level_--;
|
| }
|
| @@ -1927,7 +1964,7 @@
|
| Block* result = new(zone()) Block(NULL, 2, false);
|
|
|
| if (result != NULL) {
|
| - result->AddStatement(new(zone()) WithEnterStatement(obj, is_catch_block));
|
| + result->AddStatement(new(zone()) EnterWithContextStatement(obj));
|
|
|
| // Create body block.
|
| Block* body = new(zone()) Block(NULL, 1, false);
|
| @@ -1935,7 +1972,7 @@
|
|
|
| // Create exit block.
|
| Block* exit = new(zone()) Block(NULL, 1, false);
|
| - exit->AddStatement(new(zone()) WithExitStatement());
|
| + exit->AddStatement(new(zone()) ExitContextStatement());
|
|
|
| // Return a try-finally statement.
|
| TryFinallyStatement* wrapper = new(zone()) TryFinallyStatement(body, exit);
|
| @@ -1962,7 +1999,7 @@
|
| Expression* expr = ParseExpression(true, CHECK_OK);
|
| Expect(Token::RPAREN, CHECK_OK);
|
|
|
| - return WithHelper(expr, labels, false, CHECK_OK);
|
| + return WithHelper(expr, labels, CHECK_OK);
|
| }
|
|
|
|
|
| @@ -2032,7 +2069,7 @@
|
|
|
| Expect(Token::THROW, CHECK_OK);
|
| int pos = scanner().location().beg_pos;
|
| - if (scanner().has_line_terminator_before_next()) {
|
| + if (scanner().HasAnyLineTerminatorBeforeNext()) {
|
| ReportMessage("newline_after_throw", Vector<const char*>::empty());
|
| *ok = false;
|
| return NULL;
|
| @@ -2058,18 +2095,13 @@
|
|
|
| Expect(Token::TRY, CHECK_OK);
|
|
|
| - ZoneList<Label*>* target_list = new(zone()) ZoneList<Label*>(0);
|
| - TargetCollector collector(target_list);
|
| + TargetCollector try_collector;
|
| Block* try_block;
|
|
|
| - { Target target(&this->target_stack_, &collector);
|
| + { Target target(&this->target_stack_, &try_collector);
|
| try_block = ParseBlock(NULL, CHECK_OK);
|
| }
|
|
|
| - Block* catch_block = NULL;
|
| - Variable* catch_var = NULL;
|
| - Block* finally_block = NULL;
|
| -
|
| Token::Value tok = peek();
|
| if (tok != Token::CATCH && tok != Token::FINALLY) {
|
| ReportMessage("no_catch_or_finally", Vector<const char*>::empty());
|
| @@ -2078,18 +2110,19 @@
|
| }
|
|
|
| // If we can break out from the catch block and there is a finally block,
|
| - // then we will need to collect jump targets from the catch block. Since
|
| - // we don't know yet if there will be a finally block, we always collect
|
| - // the jump targets.
|
| - ZoneList<Label*>* catch_target_list = new(zone()) ZoneList<Label*>(0);
|
| - TargetCollector catch_collector(catch_target_list);
|
| - bool has_catch = false;
|
| + // then we will need to collect escaping targets from the catch
|
| + // block. Since we don't know yet if there will be a finally block, we
|
| + // always collect the targets.
|
| + TargetCollector catch_collector;
|
| + Scope* catch_scope = NULL;
|
| + Variable* catch_variable = NULL;
|
| + Block* catch_block = NULL;
|
| + Handle<String> name;
|
| if (tok == Token::CATCH) {
|
| - has_catch = true;
|
| Consume(Token::CATCH);
|
|
|
| Expect(Token::LPAREN, CHECK_OK);
|
| - Handle<String> name = ParseIdentifier(CHECK_OK);
|
| + name = ParseIdentifier(CHECK_OK);
|
|
|
| if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) {
|
| ReportMessage("strict_catch_variable", Vector<const char*>::empty());
|
| @@ -2100,17 +2133,39 @@
|
| Expect(Token::RPAREN, CHECK_OK);
|
|
|
| if (peek() == Token::LBRACE) {
|
| - // Allocate a temporary for holding the finally state while
|
| - // executing the finally block.
|
| - catch_var =
|
| - top_scope_->NewTemporary(isolate()->factory()->catch_var_symbol());
|
| - Literal* name_literal = new(zone()) Literal(name);
|
| - VariableProxy* catch_var_use = new(zone()) VariableProxy(catch_var);
|
| - Expression* obj =
|
| - new(zone()) CatchExtensionObject(name_literal, catch_var_use);
|
| + // Rewrite the catch body B to a single statement block
|
| + // { try B finally { PopContext }}.
|
| + Block* inner_body;
|
| + // We need to collect escapes from the body for both the inner
|
| + // try/finally used to pop the catch context and any possible outer
|
| + // try/finally.
|
| + TargetCollector inner_collector;
|
| { Target target(&this->target_stack_, &catch_collector);
|
| - catch_block = WithHelper(obj, NULL, true, CHECK_OK);
|
| + { Target target(&this->target_stack_, &inner_collector);
|
| + catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with());
|
| + if (top_scope_->is_strict_mode()) {
|
| + catch_scope->EnableStrictMode();
|
| + }
|
| + catch_variable = catch_scope->DeclareLocal(name, Variable::VAR);
|
| +
|
| + Scope* saved_scope = top_scope_;
|
| + top_scope_ = catch_scope;
|
| + inner_body = ParseBlock(NULL, CHECK_OK);
|
| + top_scope_ = saved_scope;
|
| + }
|
| }
|
| +
|
| + // Create exit block.
|
| + Block* inner_finally = new(zone()) Block(NULL, 1, false);
|
| + inner_finally->AddStatement(new(zone()) ExitContextStatement());
|
| +
|
| + // Create a try/finally statement.
|
| + TryFinallyStatement* inner_try_finally =
|
| + new(zone()) TryFinallyStatement(inner_body, inner_finally);
|
| + inner_try_finally->set_escaping_targets(inner_collector.targets());
|
| +
|
| + catch_block = new(zone()) Block(NULL, 1, false);
|
| + catch_block->AddStatement(inner_try_finally);
|
| } else {
|
| Expect(Token::LBRACE, CHECK_OK);
|
| }
|
| @@ -2118,45 +2173,48 @@
|
| tok = peek();
|
| }
|
|
|
| - if (tok == Token::FINALLY || !has_catch) {
|
| + Block* finally_block = NULL;
|
| + if (tok == Token::FINALLY || catch_block == NULL) {
|
| Consume(Token::FINALLY);
|
| - // Declare a variable for holding the finally state while
|
| - // executing the finally block.
|
| finally_block = ParseBlock(NULL, CHECK_OK);
|
| }
|
|
|
| // Simplify the AST nodes by converting:
|
| - // 'try { } catch { } finally { }'
|
| + // 'try B0 catch B1 finally B2'
|
| // to:
|
| - // 'try { try { } catch { } } finally { }'
|
| + // 'try { try B0 catch B1 } finally B2'
|
|
|
| if (catch_block != NULL && finally_block != NULL) {
|
| - VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var);
|
| + // If we have both, create an inner try/catch.
|
| + ASSERT(catch_scope != NULL && catch_variable != NULL);
|
| TryCatchStatement* statement =
|
| - new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block);
|
| - statement->set_escaping_targets(collector.targets());
|
| + new(zone()) TryCatchStatement(try_block,
|
| + catch_scope,
|
| + catch_variable,
|
| + catch_block);
|
| + statement->set_escaping_targets(try_collector.targets());
|
| try_block = new(zone()) Block(NULL, 1, false);
|
| try_block->AddStatement(statement);
|
| - catch_block = NULL;
|
| + catch_block = NULL; // Clear to indicate it's been handled.
|
| }
|
|
|
| TryStatement* result = NULL;
|
| if (catch_block != NULL) {
|
| ASSERT(finally_block == NULL);
|
| - VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var);
|
| + ASSERT(catch_scope != NULL && catch_variable != NULL);
|
| result =
|
| - new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block);
|
| - result->set_escaping_targets(collector.targets());
|
| + new(zone()) TryCatchStatement(try_block,
|
| + catch_scope,
|
| + catch_variable,
|
| + catch_block);
|
| } else {
|
| ASSERT(finally_block != NULL);
|
| result = new(zone()) TryFinallyStatement(try_block, finally_block);
|
| - // Add the jump targets of the try block and the catch block.
|
| - for (int i = 0; i < collector.targets()->length(); i++) {
|
| - catch_collector.AddTarget(collector.targets()->at(i));
|
| - }
|
| - result->set_escaping_targets(catch_collector.targets());
|
| + // Combine the jump targets of the try block and the possible catch block.
|
| + try_collector.targets()->AddAll(*catch_collector.targets());
|
| }
|
|
|
| + result->set_escaping_targets(try_collector.targets());
|
| return result;
|
| }
|
|
|
| @@ -2221,10 +2279,12 @@
|
| Expect(Token::LPAREN, CHECK_OK);
|
| if (peek() != Token::SEMICOLON) {
|
| if (peek() == Token::VAR || peek() == Token::CONST) {
|
| - Expression* each = NULL;
|
| + Handle<String> name;
|
| Block* variable_statement =
|
| - ParseVariableDeclarations(false, &each, CHECK_OK);
|
| - if (peek() == Token::IN && each != NULL) {
|
| + ParseVariableDeclarations(false, &name, CHECK_OK);
|
| +
|
| + if (peek() == Token::IN && !name.is_null()) {
|
| + VariableProxy* each = top_scope_->NewUnresolved(name, inside_with());
|
| ForInStatement* loop = new(zone()) ForInStatement(labels);
|
| Target target(&this->target_stack_, loop);
|
|
|
| @@ -2376,7 +2436,7 @@
|
| if ((op == Token::INIT_VAR
|
| || op == Token::INIT_CONST
|
| || op == Token::ASSIGN)
|
| - && (right->AsCall() == NULL)) {
|
| + && (right->AsCall() == NULL && right->AsCallNew() == NULL)) {
|
| fni_->Infer();
|
| }
|
| fni_->Leave();
|
| @@ -2545,18 +2605,26 @@
|
| int position = scanner().location().beg_pos;
|
| Expression* expression = ParseUnaryExpression(CHECK_OK);
|
|
|
| - // Compute some expressions involving only number literals.
|
| - if (expression != NULL && expression->AsLiteral() &&
|
| - expression->AsLiteral()->handle()->IsNumber()) {
|
| - double value = expression->AsLiteral()->handle()->Number();
|
| - switch (op) {
|
| - case Token::ADD:
|
| - return expression;
|
| - case Token::SUB:
|
| - return NewNumberLiteral(-value);
|
| - case Token::BIT_NOT:
|
| - return NewNumberLiteral(~DoubleToInt32(value));
|
| - default: break;
|
| + if (expression != NULL && (expression->AsLiteral() != NULL)) {
|
| + Handle<Object> literal = expression->AsLiteral()->handle();
|
| + if (op == Token::NOT) {
|
| + // Convert the literal to a boolean condition and negate it.
|
| + bool condition = literal->ToBoolean()->IsTrue();
|
| + Handle<Object> result(isolate()->heap()->ToBoolean(!condition));
|
| + return new(zone()) Literal(result);
|
| + } else if (literal->IsNumber()) {
|
| + // Compute some expressions involving only number literals.
|
| + double value = literal->Number();
|
| + switch (op) {
|
| + case Token::ADD:
|
| + return expression;
|
| + case Token::SUB:
|
| + return NewNumberLiteral(-value);
|
| + case Token::BIT_NOT:
|
| + return NewNumberLiteral(~DoubleToInt32(value));
|
| + default:
|
| + break;
|
| + }
|
| }
|
| }
|
|
|
| @@ -2607,7 +2675,7 @@
|
| // LeftHandSideExpression ('++' | '--')?
|
|
|
| Expression* expression = ParseLeftHandSideExpression(CHECK_OK);
|
| - if (!scanner().has_line_terminator_before_next() &&
|
| + if (!scanner().HasAnyLineTerminatorBeforeNext() &&
|
| Token::IsCountOp(peek())) {
|
| // Signal a reference error if the expression is an invalid
|
| // left-hand side expression. We could report this as a syntax
|
| @@ -2758,11 +2826,12 @@
|
| Expect(Token::FUNCTION, CHECK_OK);
|
| int function_token_position = scanner().location().beg_pos;
|
| Handle<String> name;
|
| - bool is_reserved_name = false;
|
| + bool is_strict_reserved_name = false;
|
| if (peek_any_identifier()) {
|
| - name = ParseIdentifierOrReservedWord(&is_reserved_name, CHECK_OK);
|
| + name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name,
|
| + CHECK_OK);
|
| }
|
| - result = ParseFunctionLiteral(name, is_reserved_name,
|
| + result = ParseFunctionLiteral(name, is_strict_reserved_name,
|
| function_token_position, NESTED, CHECK_OK);
|
| } else {
|
| result = ParsePrimaryExpression(CHECK_OK);
|
| @@ -2775,6 +2844,14 @@
|
| int pos = scanner().location().beg_pos;
|
| Expression* index = ParseExpression(true, CHECK_OK);
|
| result = new(zone()) Property(result, index, pos);
|
| + if (fni_ != NULL) {
|
| + if (index->IsPropertyName()) {
|
| + fni_->PushLiteralName(index->AsLiteral()->AsPropertyName());
|
| + } else {
|
| + fni_->PushLiteralName(
|
| + isolate()->factory()->anonymous_function_symbol());
|
| + }
|
| + }
|
| Expect(Token::RBRACK, CHECK_OK);
|
| break;
|
| }
|
| @@ -2833,6 +2910,9 @@
|
| return ReportMessage("unexpected_token_identifier",
|
| Vector<const char*>::empty());
|
| case Token::FUTURE_RESERVED_WORD:
|
| + return ReportMessage("unexpected_reserved",
|
| + Vector<const char*>::empty());
|
| + case Token::FUTURE_STRICT_RESERVED_WORD:
|
| return ReportMessage(top_scope_->is_strict_mode() ?
|
| "unexpected_strict_reserved" :
|
| "unexpected_token_identifier",
|
| @@ -2872,8 +2952,7 @@
|
| switch (peek()) {
|
| case Token::THIS: {
|
| Consume(Token::THIS);
|
| - VariableProxy* recv = top_scope_->receiver();
|
| - result = recv;
|
| + result = new(zone()) VariableProxy(top_scope_->receiver());
|
| break;
|
| }
|
|
|
| @@ -2893,7 +2972,7 @@
|
| break;
|
|
|
| case Token::IDENTIFIER:
|
| - case Token::FUTURE_RESERVED_WORD: {
|
| + case Token::FUTURE_STRICT_RESERVED_WORD: {
|
| Handle<String> name = ParseIdentifier(CHECK_OK);
|
| if (fni_ != NULL) fni_->PushVariableName(name);
|
| result = top_scope_->NewUnresolved(name,
|
| @@ -3304,6 +3383,7 @@
|
| bool is_keyword = Token::IsKeyword(next);
|
| if (next == Token::IDENTIFIER || next == Token::NUMBER ||
|
| next == Token::FUTURE_RESERVED_WORD ||
|
| + next == Token::FUTURE_STRICT_RESERVED_WORD ||
|
| next == Token::STRING || is_keyword) {
|
| Handle<String> name;
|
| if (is_keyword) {
|
| @@ -3358,11 +3438,12 @@
|
|
|
| switch (next) {
|
| case Token::FUTURE_RESERVED_WORD:
|
| + case Token::FUTURE_STRICT_RESERVED_WORD:
|
| case Token::IDENTIFIER: {
|
| bool is_getter = false;
|
| bool is_setter = false;
|
| Handle<String> id =
|
| - ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
|
| + ParseIdentifierNameOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
|
| if (fni_ != NULL) fni_->PushLiteralName(id);
|
|
|
| if ((is_getter || is_setter) && peek() != Token::COLON) {
|
| @@ -3506,6 +3587,12 @@
|
| while (!done) {
|
| Expression* argument = ParseAssignmentExpression(true, CHECK_OK);
|
| result->Add(argument);
|
| + if (result->length() > kMaxNumFunctionParameters) {
|
| + ReportMessageAt(scanner().location(), "too_many_arguments",
|
| + Vector<const char*>::empty());
|
| + *ok = false;
|
| + return NULL;
|
| + }
|
| done = (peek() == Token::RPAREN);
|
| if (!done) Expect(Token::COMMA, CHECK_OK);
|
| }
|
| @@ -3515,7 +3602,7 @@
|
|
|
|
|
| FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
|
| - bool name_is_reserved,
|
| + bool name_is_strict_reserved,
|
| int function_token_position,
|
| FunctionLiteralType type,
|
| bool* ok) {
|
| @@ -3544,6 +3631,7 @@
|
| int end_pos;
|
| bool only_simple_this_property_assignments;
|
| Handle<FixedArray> this_property_assignments;
|
| + bool has_duplicate_parameters = false;
|
| // Parse function body.
|
| { LexicalScope lexical_scope(this, scope, isolate());
|
| top_scope_->SetScopeName(name);
|
| @@ -3558,25 +3646,24 @@
|
|
|
| bool done = (peek() == Token::RPAREN);
|
| while (!done) {
|
| - bool is_reserved = false;
|
| + bool is_strict_reserved = false;
|
| Handle<String> param_name =
|
| - ParseIdentifierOrReservedWord(&is_reserved, CHECK_OK);
|
| + ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
|
| + CHECK_OK);
|
|
|
| // Store locations for possible future error reports.
|
| if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) {
|
| name_loc = scanner().location();
|
| }
|
| if (!dupe_loc.IsValid() && top_scope_->IsDeclared(param_name)) {
|
| + has_duplicate_parameters = true;
|
| dupe_loc = scanner().location();
|
| }
|
| - if (!reserved_loc.IsValid() && is_reserved) {
|
| + if (!reserved_loc.IsValid() && is_strict_reserved) {
|
| reserved_loc = scanner().location();
|
| }
|
|
|
| - Variable* parameter = top_scope_->DeclareLocal(param_name,
|
| - Variable::VAR,
|
| - Scope::PARAMETER);
|
| - top_scope_->AddParameter(parameter);
|
| + top_scope_->DeclareParameter(param_name);
|
| num_parameters++;
|
| if (num_parameters > kMaxNumFunctionParameters) {
|
| ReportMessageAt(scanner().location(), "too_many_parameters",
|
| @@ -3633,6 +3720,7 @@
|
| scanner().SeekForward(end_pos - 1);
|
| materialized_literal_count = entry.literal_count();
|
| expected_property_count = entry.property_count();
|
| + if (entry.strict_mode()) top_scope_->EnableStrictMode();
|
| only_simple_this_property_assignments = false;
|
| this_property_assignments = isolate()->factory()->empty_fixed_array();
|
| Expect(Token::RBRACE, CHECK_OK);
|
| @@ -3673,7 +3761,7 @@
|
| *ok = false;
|
| return NULL;
|
| }
|
| - if (name_is_reserved) {
|
| + if (name_is_strict_reserved) {
|
| int position = function_token_position != RelocInfo::kNoPosition
|
| ? function_token_position
|
| : (start_pos > 0 ? start_pos - 1 : start_pos);
|
| @@ -3704,7 +3792,8 @@
|
| num_parameters,
|
| start_pos,
|
| end_pos,
|
| - (function_name->length() > 0));
|
| + (function_name->length() > 0),
|
| + has_duplicate_parameters);
|
| function_literal->set_function_token_position(function_token_position);
|
|
|
| if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal);
|
| @@ -3723,7 +3812,7 @@
|
| if (extension_ != NULL) {
|
| // The extension structures are only accessible while parsing the
|
| // very first time not when reparsing because of lazy compilation.
|
| - top_scope_->ForceEagerCompilation();
|
| + top_scope_->DeclarationScope()->ForceEagerCompilation();
|
| }
|
|
|
| const Runtime::Function* function = Runtime::FunctionForSymbol(name);
|
| @@ -3761,7 +3850,8 @@
|
| bool Parser::peek_any_identifier() {
|
| Token::Value next = peek();
|
| return next == Token::IDENTIFIER ||
|
| - next == Token::FUTURE_RESERVED_WORD;
|
| + next == Token::FUTURE_RESERVED_WORD ||
|
| + next == Token::FUTURE_STRICT_RESERVED_WORD;
|
| }
|
|
|
|
|
| @@ -3799,7 +3889,7 @@
|
| Next();
|
| return;
|
| }
|
| - if (scanner().has_line_terminator_before_next() ||
|
| + if (scanner().HasAnyLineTerminatorBeforeNext() ||
|
| tok == Token::RBRACE ||
|
| tok == Token::EOS) {
|
| return;
|
| @@ -3823,22 +3913,27 @@
|
| }
|
|
|
|
|
| +// Parses and identifier that is valid for the current scope, in particular it
|
| +// fails on strict mode future reserved keywords in a strict scope.
|
| Handle<String> Parser::ParseIdentifier(bool* ok) {
|
| - bool is_reserved;
|
| - return ParseIdentifierOrReservedWord(&is_reserved, ok);
|
| + if (top_scope_->is_strict_mode()) {
|
| + Expect(Token::IDENTIFIER, ok);
|
| + } else if (!Check(Token::IDENTIFIER)) {
|
| + Expect(Token::FUTURE_STRICT_RESERVED_WORD, ok);
|
| + }
|
| + if (!*ok) return Handle<String>();
|
| + return GetSymbol(ok);
|
| }
|
|
|
|
|
| -Handle<String> Parser::ParseIdentifierOrReservedWord(bool* is_reserved,
|
| - bool* ok) {
|
| - *is_reserved = false;
|
| - if (top_scope_->is_strict_mode()) {
|
| - Expect(Token::IDENTIFIER, ok);
|
| - } else {
|
| - if (!Check(Token::IDENTIFIER)) {
|
| - Expect(Token::FUTURE_RESERVED_WORD, ok);
|
| - *is_reserved = true;
|
| - }
|
| +// Parses and identifier or a strict mode future reserved word, and indicate
|
| +// whether it is strict mode future reserved.
|
| +Handle<String> Parser::ParseIdentifierOrStrictReservedWord(
|
| + bool* is_strict_reserved, bool* ok) {
|
| + *is_strict_reserved = false;
|
| + if (!Check(Token::IDENTIFIER)) {
|
| + Expect(Token::FUTURE_STRICT_RESERVED_WORD, ok);
|
| + *is_strict_reserved = true;
|
| }
|
| if (!*ok) return Handle<String>();
|
| return GetSymbol(ok);
|
| @@ -3848,8 +3943,9 @@
|
| Handle<String> Parser::ParseIdentifierName(bool* ok) {
|
| Token::Value next = Next();
|
| if (next != Token::IDENTIFIER &&
|
| - next != Token::FUTURE_RESERVED_WORD &&
|
| - !Token::IsKeyword(next)) {
|
| + next != Token::FUTURE_RESERVED_WORD &&
|
| + next != Token::FUTURE_STRICT_RESERVED_WORD &&
|
| + !Token::IsKeyword(next)) {
|
| ReportUnexpectedToken(next);
|
| *ok = false;
|
| return Handle<String>();
|
| @@ -3890,12 +3986,12 @@
|
| }
|
|
|
|
|
| -// This function reads an identifier and determines whether or not it
|
| +// This function reads an identifier name and determines whether or not it
|
| // is 'get' or 'set'.
|
| -Handle<String> Parser::ParseIdentifierOrGetOrSet(bool* is_get,
|
| - bool* is_set,
|
| - bool* ok) {
|
| - Handle<String> result = ParseIdentifier(ok);
|
| +Handle<String> Parser::ParseIdentifierNameOrGetOrSet(bool* is_get,
|
| + bool* is_set,
|
| + bool* ok) {
|
| + Handle<String> result = ParseIdentifierName(ok);
|
| if (!*ok) return Handle<String>();
|
| if (scanner().is_literal_ascii() && scanner().literal_length() == 3) {
|
| const char* token = scanner().literal_ascii_string().start();
|
| @@ -4895,7 +4991,7 @@
|
| bool allow_lazy,
|
| ParserRecorder* recorder) {
|
| Isolate* isolate = Isolate::Current();
|
| - V8JavaScriptScanner scanner(isolate->unicode_cache());
|
| + JavaScriptScanner scanner(isolate->unicode_cache());
|
| scanner.Initialize(source);
|
| intptr_t stack_limit = isolate->stack_guard()->real_climit();
|
| if (!preparser::PreParser::PreParseProgram(&scanner,
|
| @@ -4967,6 +5063,7 @@
|
| Parser parser(script, true, NULL, NULL);
|
| result = parser.ParseLazy(info);
|
| } else {
|
| + // Whether we allow %identifier(..) syntax.
|
| bool allow_natives_syntax =
|
| info->allows_natives_syntax() || FLAG_allow_natives_syntax;
|
| ScriptDataImpl* pre_data = info->pre_parse_data();
|
|
|