Index: src/parsing/parser.cc |
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc |
index e8de111f6495d6bde686770fb2a104f084e6c28d..f8092e6837430404afdf5a887af0b22610f5ec22 100644 |
--- a/src/parsing/parser.cc |
+++ b/src/parsing/parser.cc |
@@ -1620,19 +1620,88 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name, |
return factory()->NewEmptyStatement(kNoSourcePosition); |
} |
-static bool ContainsLabel(ZoneList<const AstRawString*>* labels, |
- const AstRawString* label) { |
- DCHECK(label != NULL); |
- if (labels != NULL) { |
- for (int i = labels->length(); i-- > 0; ) { |
- if (labels->at(i) == label) { |
- return true; |
- } |
+ZoneList<const AstRawString*>* Parser::DeclareLabel( |
+ ZoneList<const AstRawString*>* labels, VariableProxy* var, bool* ok) { |
+ const AstRawString* label = var->raw_name(); |
+ // TODO(1240780): We don't check for redeclaration of labels |
+ // during preparsing since keeping track of the set of active |
+ // labels requires nontrivial changes to the way scopes are |
+ // structured. However, these are probably changes we want to |
+ // make later anyway so we should go back and fix this then. |
+ if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { |
+ ReportMessage(MessageTemplate::kLabelRedeclaration, label); |
+ *ok = false; |
+ return nullptr; |
+ } |
+ if (labels == nullptr) { |
+ labels = new (zone()) ZoneList<const AstRawString*>(1, zone()); |
+ } |
+ labels->Add(label, zone()); |
+ // Remove the "ghost" variable that turned out to be a label |
+ // from the top scope. This way, we don't try to resolve it |
+ // during the scope processing. |
+ scope()->RemoveUnresolved(var); |
+ return labels; |
+} |
+ |
+bool Parser::ContainsLabel(ZoneList<const AstRawString*>* labels, |
+ const AstRawString* label) { |
+ DCHECK_NOT_NULL(label); |
+ if (labels != nullptr) { |
+ for (int i = labels->length(); i-- > 0;) { |
+ if (labels->at(i) == label) return true; |
} |
} |
return false; |
} |
+Expression* Parser::RewriteReturn(Expression* return_value, int pos) { |
+ if (IsSubclassConstructor(function_state_->kind())) { |
+ // For subclass constructors we need to return this in case of undefined |
+ // return a Smi (transformed into an exception in the ConstructStub) |
+ // for a non object. |
+ // |
+ // return expr; |
+ // |
+ // Is rewritten as: |
+ // |
+ // return (temp = expr) === undefined ? this : |
+ // %_IsJSReceiver(temp) ? temp : 1; |
+ |
+ // temp = expr |
+ Variable* temp = NewTemporary(ast_value_factory()->empty_string()); |
+ Assignment* assign = factory()->NewAssignment( |
+ Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos); |
+ |
+ // %_IsJSReceiver(temp) |
+ ZoneList<Expression*>* is_spec_object_args = |
+ new (zone()) ZoneList<Expression*>(1, zone()); |
+ is_spec_object_args->Add(factory()->NewVariableProxy(temp), zone()); |
+ Expression* is_spec_object_call = factory()->NewCallRuntime( |
+ Runtime::kInlineIsJSReceiver, is_spec_object_args, pos); |
+ |
+ // %_IsJSReceiver(temp) ? temp : 1; |
+ Expression* is_object_conditional = factory()->NewConditional( |
+ is_spec_object_call, factory()->NewVariableProxy(temp), |
+ factory()->NewSmiLiteral(1, pos), pos); |
+ |
+ // temp === undefined |
+ Expression* is_undefined = factory()->NewCompareOperation( |
+ Token::EQ_STRICT, assign, |
+ factory()->NewUndefinedLiteral(kNoSourcePosition), pos); |
+ |
+ // is_undefined ? this : is_object_conditional |
+ return_value = factory()->NewConditional(is_undefined, ThisExpression(pos), |
+ is_object_conditional, pos); |
+ } |
+ if (is_generator()) { |
+ return_value = BuildIteratorResult(return_value, true); |
+ } else if (is_async_function()) { |
+ return_value = BuildResolvePromise(return_value, return_value->position()); |
+ } |
+ return return_value; |
+} |
+ |
Statement* Parser::ParseFunctionDeclaration(bool* ok) { |
Consume(Token::FUNCTION); |
int pos = position(); |
@@ -1650,304 +1719,6 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) { |
return ParseHoistableDeclaration(pos, flags, nullptr, false, CHECK_OK); |
} |
-Statement* Parser::ParseExpressionOrLabelledStatement( |
- ZoneList<const AstRawString*>* labels, |
- AllowLabelledFunctionStatement allow_function, bool* ok) { |
- // ExpressionStatement | LabelledStatement :: |
- // Expression ';' |
- // Identifier ':' Statement |
- // |
- // ExpressionStatement[Yield] : |
- // [lookahead ∉ {{, function, class, let [}] Expression[In, ?Yield] ; |
- |
- int pos = peek_position(); |
- |
- switch (peek()) { |
- case Token::FUNCTION: |
- case Token::LBRACE: |
- UNREACHABLE(); // Always handled by the callers. |
- case Token::CLASS: |
- ReportUnexpectedToken(Next()); |
- *ok = false; |
- return nullptr; |
- default: |
- break; |
- } |
- |
- bool starts_with_idenfifier = peek_any_identifier(); |
- Expression* expr = ParseExpression(true, CHECK_OK); |
- 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 |
- // identifier. |
- VariableProxy* var = expr->AsVariableProxy(); |
- const AstRawString* label = var->raw_name(); |
- // TODO(1240780): We don't check for redeclaration of labels |
- // during preparsing since keeping track of the set of active |
- // labels requires nontrivial changes to the way scopes are |
- // structured. However, these are probably changes we want to |
- // make later anyway so we should go back and fix this then. |
- if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { |
- ReportMessage(MessageTemplate::kLabelRedeclaration, label); |
- *ok = false; |
- return NULL; |
- } |
- if (labels == NULL) { |
- labels = new(zone()) ZoneList<const AstRawString*>(4, zone()); |
- } |
- labels->Add(label, zone()); |
- // Remove the "ghost" variable that turned out to be a label |
- // from the top scope. This way, we don't try to resolve it |
- // during the scope processing. |
- scope()->RemoveUnresolved(var); |
- Expect(Token::COLON, CHECK_OK); |
- // ES#sec-labelled-function-declarations Labelled Function Declarations |
- if (peek() == Token::FUNCTION && is_sloppy(language_mode())) { |
- if (allow_function == kAllowLabelledFunctionStatement) { |
- return ParseFunctionDeclaration(ok); |
- } else { |
- return ParseScopedStatement(labels, true, ok); |
- } |
- } |
- return ParseStatement(labels, kDisallowLabelledFunctionStatement, 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()->raw_name() == |
- ast_value_factory()->native_string() && |
- !scanner()->literal_contains_escapes()) { |
- return ParseNativeDeclaration(ok); |
- } |
- |
- // Parsed expression statement, followed by semicolon. |
- ExpectSemicolon(CHECK_OK); |
- return factory()->NewExpressionStatement(expr, pos); |
-} |
- |
- |
-IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels, |
- bool* ok) { |
- // IfStatement :: |
- // 'if' '(' Expression ')' Statement ('else' Statement)? |
- |
- int pos = peek_position(); |
- Expect(Token::IF, CHECK_OK); |
- Expect(Token::LPAREN, CHECK_OK); |
- Expression* condition = ParseExpression(true, CHECK_OK); |
- Expect(Token::RPAREN, CHECK_OK); |
- Statement* then_statement = ParseScopedStatement(labels, false, CHECK_OK); |
- Statement* else_statement = NULL; |
- if (peek() == Token::ELSE) { |
- Next(); |
- else_statement = ParseScopedStatement(labels, false, CHECK_OK); |
- } else { |
- else_statement = factory()->NewEmptyStatement(kNoSourcePosition); |
- } |
- return factory()->NewIfStatement( |
- condition, then_statement, else_statement, pos); |
-} |
- |
- |
-Statement* Parser::ParseContinueStatement(bool* ok) { |
- // ContinueStatement :: |
- // 'continue' Identifier? ';' |
- |
- int pos = peek_position(); |
- Expect(Token::CONTINUE, CHECK_OK); |
- const AstRawString* label = NULL; |
- 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. |
- label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); |
- } |
- IterationStatement* target = LookupContinueTarget(label, CHECK_OK); |
- if (target == NULL) { |
- // Illegal continue statement. |
- MessageTemplate::Template message = MessageTemplate::kIllegalContinue; |
- if (label != NULL) { |
- message = MessageTemplate::kUnknownLabel; |
- } |
- ReportMessage(message, label); |
- *ok = false; |
- return NULL; |
- } |
- ExpectSemicolon(CHECK_OK); |
- return factory()->NewContinueStatement(target, pos); |
-} |
- |
- |
-Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels, |
- bool* ok) { |
- // BreakStatement :: |
- // 'break' Identifier? ';' |
- |
- int pos = peek_position(); |
- Expect(Token::BREAK, CHECK_OK); |
- const AstRawString* label = NULL; |
- 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. |
- label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); |
- } |
- // Parse labeled break statements that target themselves into |
- // empty statements, e.g. 'l1: l2: l3: break l2;' |
- if (label != NULL && ContainsLabel(labels, label)) { |
- ExpectSemicolon(CHECK_OK); |
- return factory()->NewEmptyStatement(pos); |
- } |
- BreakableStatement* target = NULL; |
- target = LookupBreakTarget(label, CHECK_OK); |
- if (target == NULL) { |
- // Illegal break statement. |
- MessageTemplate::Template message = MessageTemplate::kIllegalBreak; |
- if (label != NULL) { |
- message = MessageTemplate::kUnknownLabel; |
- } |
- ReportMessage(message, label); |
- *ok = false; |
- return NULL; |
- } |
- ExpectSemicolon(CHECK_OK); |
- return factory()->NewBreakStatement(target, pos); |
-} |
- |
- |
-Statement* Parser::ParseReturnStatement(bool* ok) { |
- // ReturnStatement :: |
- // 'return' Expression? ';' |
- |
- // Consume the return token. It is necessary to do that before |
- // reporting any errors on it, because of the way errors are |
- // reported (underlining). |
- Expect(Token::RETURN, CHECK_OK); |
- Scanner::Location loc = scanner()->location(); |
- |
- Token::Value tok = peek(); |
- Statement* result; |
- Expression* return_value; |
- if (scanner()->HasAnyLineTerminatorBeforeNext() || |
- tok == Token::SEMICOLON || |
- tok == Token::RBRACE || |
- tok == Token::EOS) { |
- if (IsSubclassConstructor(function_state_->kind())) { |
- return_value = ThisExpression(loc.beg_pos); |
- } else { |
- return_value = GetLiteralUndefined(position()); |
- } |
- } else { |
- int pos = peek_position(); |
- |
- if (IsSubclassConstructor(function_state_->kind())) { |
- // Because of the return code rewriting that happens in case of a subclass |
- // constructor we don't want to accept tail calls, therefore we don't set |
- // ReturnExprScope to kInsideValidReturnStatement here. |
- return_value = ParseExpression(true, CHECK_OK); |
- |
- // For subclass constructors we need to return this in case of undefined |
- // return a Smi (transformed into an exception in the ConstructStub) |
- // for a non object. |
- // |
- // return expr; |
- // |
- // Is rewritten as: |
- // |
- // return (temp = expr) === undefined ? this : |
- // %_IsJSReceiver(temp) ? temp : 1; |
- |
- // temp = expr |
- Variable* temp = NewTemporary(ast_value_factory()->empty_string()); |
- Assignment* assign = factory()->NewAssignment( |
- Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos); |
- |
- // %_IsJSReceiver(temp) |
- ZoneList<Expression*>* is_spec_object_args = |
- new (zone()) ZoneList<Expression*>(1, zone()); |
- is_spec_object_args->Add(factory()->NewVariableProxy(temp), zone()); |
- Expression* is_spec_object_call = factory()->NewCallRuntime( |
- Runtime::kInlineIsJSReceiver, is_spec_object_args, pos); |
- |
- // %_IsJSReceiver(temp) ? temp : 1; |
- Expression* is_object_conditional = factory()->NewConditional( |
- is_spec_object_call, factory()->NewVariableProxy(temp), |
- factory()->NewSmiLiteral(1, pos), pos); |
- |
- // temp === undefined |
- Expression* is_undefined = factory()->NewCompareOperation( |
- Token::EQ_STRICT, assign, |
- factory()->NewUndefinedLiteral(kNoSourcePosition), pos); |
- |
- // is_undefined ? this : is_object_conditional |
- return_value = factory()->NewConditional( |
- is_undefined, ThisExpression(pos), is_object_conditional, pos); |
- } else { |
- ReturnExprScope maybe_allow_tail_calls( |
- function_state_, ReturnExprContext::kInsideValidReturnStatement); |
- return_value = ParseExpression(true, CHECK_OK); |
- |
- if (allow_tailcalls() && !is_sloppy(language_mode()) && !is_resumable()) { |
- // ES6 14.6.1 Static Semantics: IsInTailPosition |
- function_state_->AddImplicitTailCallExpression(return_value); |
- } |
- } |
- } |
- ExpectSemicolon(CHECK_OK); |
- |
- if (is_generator()) { |
- return_value = BuildIteratorResult(return_value, true); |
- } else if (is_async_function()) { |
- return_value = BuildResolvePromise(return_value, return_value->position()); |
- } |
- |
- result = factory()->NewReturnStatement(return_value, loc.beg_pos); |
- |
- DeclarationScope* decl_scope = GetDeclarationScope(); |
- if (decl_scope->is_script_scope() || decl_scope->is_eval_scope()) { |
- ReportMessageAt(loc, MessageTemplate::kIllegalReturn); |
- *ok = false; |
- return NULL; |
- } |
- return result; |
-} |
- |
- |
-Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels, |
- bool* ok) { |
- // WithStatement :: |
- // 'with' '(' Expression ')' Statement |
- |
- Expect(Token::WITH, CHECK_OK); |
- int pos = position(); |
- |
- if (is_strict(language_mode())) { |
- ReportMessage(MessageTemplate::kStrictWith); |
- *ok = false; |
- return NULL; |
- } |
- |
- Expect(Token::LPAREN, CHECK_OK); |
- Expression* expr = ParseExpression(true, CHECK_OK); |
- Expect(Token::RPAREN, CHECK_OK); |
- |
- Scope* with_scope = NewScope(WITH_SCOPE); |
- Statement* body; |
- { |
- BlockState block_state(&scope_state_, with_scope); |
- with_scope->set_start_position(scanner()->peek_location().beg_pos); |
- body = ParseScopedStatement(labels, true, CHECK_OK); |
- with_scope->set_end_position(scanner()->location().end_pos); |
- } |
- return factory()->NewWithStatement(with_scope, expr, body, pos); |
-} |
- |
- |
CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) { |
// CaseClause :: |
// 'case' Expression ':' StatementList |