diff options
author | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-04-07 14:06:55 -0700 |
---|---|---|
committer | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-04-14 10:03:39 -0700 |
commit | 52af5c4eebf4de8638aef0338bd826656312a02a (patch) | |
tree | 628dc9fb0b558c3a73a2160706fef368876fe548 /deps/v8/src/parsing/parser.cc | |
parent | 6e3e8acc7cc7ebd3d67db5ade1247b8b558efe09 (diff) | |
download | android-node-v8-52af5c4eebf4de8638aef0338bd826656312a02a.tar.gz android-node-v8-52af5c4eebf4de8638aef0338bd826656312a02a.tar.bz2 android-node-v8-52af5c4eebf4de8638aef0338bd826656312a02a.zip |
deps: upgrade V8 to 5.0.71.32
* Pick up the branch head for V8 5.0 stable [1]
* Edit v8 gitignore to allow trace_event copy
* Update V8 DEP trace_event as per deps/v8/DEPS [2]
[1] https://chromium.googlesource.com/v8/v8.git/+/3c67831
[2] https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/4b09207e447ae5bd34643b4c6321bee7b76d35f9
Ref: https://github.com/nodejs/node/pull/5945
PR-URL: https://github.com/nodejs/node/pull/6111
Reviewed-By: targos - Michaƫl Zasso <mic.besace@gmail.com>
Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: indutny - Fedor Indutny <fedor.indutny@gmail.com>
Diffstat (limited to 'deps/v8/src/parsing/parser.cc')
-rw-r--r-- | deps/v8/src/parsing/parser.cc | 2461 |
1 files changed, 1911 insertions, 550 deletions
diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc index b1b8c1316b..8005479a32 100644 --- a/deps/v8/src/parsing/parser.cc +++ b/deps/v8/src/parsing/parser.cc @@ -6,6 +6,7 @@ #include "src/api.h" #include "src/ast/ast.h" +#include "src/ast/ast-expression-rewriter.h" #include "src/ast/ast-expression-visitor.h" #include "src/ast/ast-literal-reindexer.h" #include "src/ast/scopeinfo.h" @@ -22,6 +23,7 @@ #include "src/parsing/scanner-character-streams.h" #include "src/runtime/runtime.h" #include "src/string-stream.h" +#include "src/tracing/trace-event.h" namespace v8 { namespace internal { @@ -178,15 +180,14 @@ void Parser::SetCachedData(ParseInfo* info) { } } - -FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope, +FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, + bool call_super, Scope* scope, int pos, int end_pos, LanguageMode language_mode) { int materialized_literal_count = -1; int expected_property_count = -1; int parameter_count = 0; - const AstRawString* name = ast_value_factory()->empty_string(); - + if (name == nullptr) name = ast_value_factory()->empty_string(); FunctionKind kind = call_super ? FunctionKind::kDefaultSubclassConstructor : FunctionKind::kDefaultBaseConstructor; @@ -642,10 +643,16 @@ Expression* ParserTraits::NewTargetExpression(Scope* scope, } -Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope, - int pos, int end_pos, - LanguageMode mode) { - return parser_->DefaultConstructor(call_super, scope, pos, end_pos, mode); +Expression* ParserTraits::FunctionSentExpression(Scope* scope, + AstNodeFactory* factory, + int pos) { + // We desugar function.sent into %GeneratorGetInput(generator). + Zone* zone = parser_->zone(); + ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(1, zone); + VariableProxy* generator = factory->NewVariableProxy( + parser_->function_state_->generator_object_variable()); + args->Add(generator, zone); + return factory->NewCallRuntime(Runtime::kGeneratorGetInput, args, pos); } @@ -721,11 +728,10 @@ FunctionLiteral* ParserTraits::ParseFunctionLiteral( const AstRawString* name, Scanner::Location function_name_location, FunctionNameValidity function_name_validity, FunctionKind kind, int function_token_position, FunctionLiteral::FunctionType type, - FunctionLiteral::ArityRestriction arity_restriction, LanguageMode language_mode, bool* ok) { return parser_->ParseFunctionLiteral( name, function_name_location, function_name_validity, kind, - function_token_position, type, arity_restriction, language_mode, ok); + function_token_position, type, language_mode, ok); } @@ -767,6 +773,7 @@ Parser::Parser(ParseInfo* info) set_allow_legacy_const(FLAG_legacy_const); set_allow_harmony_do_expressions(FLAG_harmony_do_expressions); set_allow_harmony_function_name(FLAG_harmony_function_name); + set_allow_harmony_function_sent(FLAG_harmony_function_sent); for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; ++feature) { use_counts_[feature] = 0; @@ -789,6 +796,7 @@ FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { DCHECK(parsing_on_main_thread_); HistogramTimerScope timer_scope(isolate->counters()->parse(), true); + TRACE_EVENT0("v8", "V8.Parse"); Handle<String> source(String::cast(info->script()->source())); isolate->counters()->total_parse_size()->Increment(source->length()); base::ElapsedTimer timer; @@ -935,13 +943,9 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) { if (ok) { ParserTraits::RewriteDestructuringAssignments(); - result = factory()->NewFunctionLiteral( - ast_value_factory()->empty_string(), scope_, body, - function_state.materialized_literal_count(), - function_state.expected_property_count(), 0, - FunctionLiteral::kNoDuplicateParameters, - FunctionLiteral::kGlobalOrEval, FunctionLiteral::kShouldLazyCompile, - FunctionKind::kNormalFunction, 0); + result = factory()->NewScriptOrEvalFunctionLiteral( + scope_, body, function_state.materialized_literal_count(), + function_state.expected_property_count()); } } @@ -957,6 +961,7 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info) { // called in the main thread. DCHECK(parsing_on_main_thread_); HistogramTimerScope timer_scope(isolate->counters()->parse_lazy()); + TRACE_EVENT0("v8", "V8.ParseLazy"); Handle<String> source(String::cast(info->script()->source())); isolate->counters()->total_parse_size()->Increment(source->length()); base::ElapsedTimer timer; @@ -990,6 +995,18 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info) { return result; } +static FunctionLiteral::FunctionType ComputeFunctionType( + Handle<SharedFunctionInfo> shared_info) { + if (shared_info->is_declaration()) { + return FunctionLiteral::kDeclaration; + } else if (shared_info->is_named_expression()) { + return FunctionLiteral::kNamedExpression; + } else if (IsConciseMethod(shared_info->kind()) || + IsAccessorFunction(shared_info->kind())) { + return FunctionLiteral::kAccessorOrMethod; + } + return FunctionLiteral::kAnonymousExpression; +} FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, Utf16CharacterStream* source) { @@ -1028,11 +1045,7 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, is_strict(info->language_mode())); DCHECK(info->language_mode() == shared_info->language_mode()); FunctionLiteral::FunctionType function_type = - shared_info->is_expression() - ? (shared_info->is_anonymous() - ? FunctionLiteral::kAnonymousExpression - : FunctionLiteral::kNamedExpression) - : FunctionLiteral::kDeclaration; + ComputeFunctionType(shared_info); bool ok = true; if (shared_info->is_arrow()) { @@ -1050,7 +1063,7 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, SetLanguageMode(scope, shared_info->language_mode()); scope->set_start_position(shared_info->start_position()); - ExpressionClassifier formals_classifier; + ExpressionClassifier formals_classifier(this); ParserFormalParameters formals(scope); Checkpoint checkpoint(this); { @@ -1096,15 +1109,15 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, } } } else if (shared_info->is_default_constructor()) { - result = DefaultConstructor(IsSubclassConstructor(shared_info->kind()), - scope, shared_info->start_position(), - shared_info->end_position(), - shared_info->language_mode()); + result = DefaultConstructor( + raw_name, IsSubclassConstructor(shared_info->kind()), scope, + shared_info->start_position(), shared_info->end_position(), + shared_info->language_mode()); } else { - result = ParseFunctionLiteral( - raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, - shared_info->kind(), RelocInfo::kNoPosition, function_type, - FunctionLiteral::kNormalArity, shared_info->language_mode(), &ok); + result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(), + kSkipFunctionNameCheck, shared_info->kind(), + RelocInfo::kNoPosition, function_type, + shared_info->language_mode(), &ok); } // Make sure the results agree. DCHECK(ok == (result != NULL)); @@ -1260,20 +1273,11 @@ Statement* Parser::ParseStatementListItem(bool* ok) { // Statement // Declaration - if (peek() != Token::CLASS) { - // No more classes follow; reset the start position for the consecutive - // class declaration group. - scope_->set_class_declaration_group_start(-1); - } - switch (peek()) { case Token::FUNCTION: return ParseFunctionDeclaration(NULL, ok); case Token::CLASS: - if (scope_->class_declaration_group_start() < 0) { - scope_->set_class_declaration_group_start( - scanner()->peek_location().beg_pos); - } + Consume(Token::CLASS); return ParseClassDeclaration(NULL, ok); case Token::CONST: if (allow_const()) { @@ -1345,7 +1349,6 @@ void* Parser::ParseModuleItemList(ZoneList<Statement*>* body, bool* ok) { } } - scope_->module()->Freeze(); return NULL; } @@ -1558,24 +1561,53 @@ Statement* Parser::ParseExportDefault(bool* ok) { Expect(Token::DEFAULT, CHECK_OK); Scanner::Location default_loc = scanner()->location(); + const AstRawString* default_string = ast_value_factory()->default_string(); ZoneList<const AstRawString*> names(1, zone()); - Statement* result = NULL; + Statement* result = nullptr; + Expression* default_export = nullptr; switch (peek()) { - case Token::FUNCTION: - // TODO(ES6): Support parsing anonymous function declarations here. - result = ParseFunctionDeclaration(&names, CHECK_OK); + case Token::FUNCTION: { + Consume(Token::FUNCTION); + int pos = position(); + bool is_generator = Check(Token::MUL); + if (peek() == Token::LPAREN) { + // FunctionDeclaration[+Default] :: + // 'function' '(' FormalParameters ')' '{' FunctionBody '}' + // + // GeneratorDeclaration[+Default] :: + // 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}' + default_export = ParseFunctionLiteral( + default_string, Scanner::Location::invalid(), + kSkipFunctionNameCheck, + is_generator ? FunctionKind::kGeneratorFunction + : FunctionKind::kNormalFunction, + pos, FunctionLiteral::kDeclaration, language_mode(), CHECK_OK); + result = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + } else { + result = ParseFunctionDeclaration(pos, is_generator, &names, CHECK_OK); + } break; + } case Token::CLASS: - // TODO(ES6): Support parsing anonymous class declarations here. - result = ParseClassDeclaration(&names, CHECK_OK); + Consume(Token::CLASS); + if (peek() == Token::EXTENDS || peek() == Token::LBRACE) { + // ClassDeclaration[+Default] :: + // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}' + default_export = + ParseClassLiteral(default_string, Scanner::Location::invalid(), + false, position(), CHECK_OK); + result = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + } else { + result = ParseClassDeclaration(&names, CHECK_OK); + } break; default: { int pos = peek_position(); - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); Expression* expr = ParseAssignmentExpression(true, &classifier, CHECK_OK); - expr = ParserTraits::RewriteNonPattern(expr, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); ExpectSemicolon(CHECK_OK); result = factory()->NewExpressionStatement(expr, pos); @@ -1583,19 +1615,18 @@ Statement* Parser::ParseExportDefault(bool* ok) { } } - const AstRawString* default_string = ast_value_factory()->default_string(); - DCHECK_LE(names.length(), 1); if (names.length() == 1) { scope_->module()->AddLocalExport(default_string, names.first(), zone(), ok); if (!*ok) { ParserTraits::ReportMessageAt( default_loc, MessageTemplate::kDuplicateExport, default_string); - return NULL; + return nullptr; } } else { // TODO(ES6): Assign result to a const binding with the name "*default*" // and add an export entry with "*default*" as the local name. + USE(default_export); } return result; @@ -1686,6 +1717,7 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { break; case Token::CLASS: + Consume(Token::CLASS); result = ParseClassDeclaration(&names, CHECK_OK); break; @@ -1921,42 +1953,44 @@ Variable* Parser::Declare(Declaration* declaration, if (var == NULL) { // Declare the name. Variable::Kind kind = Variable::NORMAL; - int declaration_group_start = -1; if (is_function_declaration) { kind = Variable::FUNCTION; - } else if (declaration->IsVariableDeclaration() && - declaration->AsVariableDeclaration()->is_class_declaration()) { - kind = Variable::CLASS; - declaration_group_start = - declaration->AsVariableDeclaration()->declaration_group_start(); } var = declaration_scope->DeclareLocal( - name, mode, declaration->initialization(), kind, kNotAssigned, - declaration_group_start); - } else if (((IsLexicalVariableMode(mode) || - IsLexicalVariableMode(var->mode())) && - // Allow duplicate function decls for web compat, see bug 4693. - (is_strict(language_mode()) || !is_function_declaration || - !var->is_function())) || - ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) && - !declaration_scope->is_script_scope())) { - // The name was declared in this scope before; check for conflicting - // re-declarations. We have a conflict if either of the declarations is - // not a var (in script scope, we also have to ignore legacy const for - // compatibility). There is similar code in runtime.cc in the Declare - // functions. The function CheckConflictingVarDeclarations checks for - // var and let bindings from different scopes whereas this is a check for - // conflicting declarations within the same scope. This check also covers - // the special case - // - // function () { let x; { var x; } } - // - // because the var declaration is hoisted to the function scope where 'x' - // is already bound. - DCHECK(IsDeclaredVariableMode(var->mode())); - if (is_strict(language_mode()) || - (allow_harmony_sloppy() && mode != CONST_LEGACY && - var->mode() != CONST_LEGACY)) { + name, mode, declaration->initialization(), kind, kNotAssigned); + } else if ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) && + !declaration_scope->is_script_scope()) { + // Duplicate legacy const definitions throw at runtime. + DCHECK(is_sloppy(language_mode())); + Expression* expression = NewThrowSyntaxError( + MessageTemplate::kVarRedeclaration, name, declaration->position()); + declaration_scope->SetIllegalRedeclaration(expression); + } else if ((IsLexicalVariableMode(mode) || + IsLexicalVariableMode(var->mode())) && + // Lexical bindings may appear for some parameters in sloppy + // mode even with --harmony-sloppy off. + (is_strict(language_mode()) || allow_harmony_sloppy())) { + // Allow duplicate function decls for web compat, see bug 4693. + if (is_sloppy(language_mode()) && is_function_declaration && + var->is_function()) { + DCHECK(IsLexicalVariableMode(mode) && + IsLexicalVariableMode(var->mode())); + ++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition]; + } else { + // The name was declared in this scope before; check for conflicting + // re-declarations. We have a conflict if either of the declarations + // is not a var (in script scope, we also have to ignore legacy const + // for compatibility). There is similar code in runtime.cc in the + // Declare functions. The function CheckConflictingVarDeclarations + // checks for var and let bindings from different scopes whereas this + // is a check for conflicting declarations within the same scope. This + // check also covers the special case + // + // function () { let x; { var x; } } + // + // because the var declaration is hoisted to the function scope where + // 'x' is already bound. + DCHECK(IsDeclaredVariableMode(var->mode())); // In harmony we treat re-declarations as early errors. See // ES5 16 for a definition of early errors. if (declaration_kind == DeclarationDescriptor::NORMAL) { @@ -1967,9 +2001,6 @@ Variable* Parser::Declare(Declaration* declaration, *ok = false; return nullptr; } - Expression* expression = NewThrowSyntaxError( - MessageTemplate::kVarRedeclaration, name, declaration->position()); - declaration_scope->SetIllegalRedeclaration(expression); } else if (mode == VAR) { var->set_maybe_assigned(); } @@ -2093,14 +2124,22 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { Statement* Parser::ParseFunctionDeclaration( ZoneList<const AstRawString*>* names, 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); + return ParseFunctionDeclaration(pos, is_generator, names, ok); +} + + +Statement* Parser::ParseFunctionDeclaration( + int pos, bool is_generator, ZoneList<const AstRawString*>* names, + bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}' + // GeneratorDeclaration :: + // 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}' + // + // 'function' and '*' (if present) have been consumed by the caller. bool is_strict_reserved = false; const AstRawString* name = ParseIdentifierOrStrictReservedWord( &is_strict_reserved, CHECK_OK); @@ -2113,8 +2152,7 @@ Statement* Parser::ParseFunctionDeclaration( : kFunctionNameValidityUnknown, is_generator ? FunctionKind::kGeneratorFunction : FunctionKind::kNormalFunction, - pos, FunctionLiteral::kDeclaration, FunctionLiteral::kNormalArity, - language_mode(), CHECK_OK); + pos, FunctionLiteral::kDeclaration, language_mode(), CHECK_OK); // Even if we're not at the top-level of the global or a function // scope, we treat it as such and introduce the function with its @@ -2151,6 +2189,8 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names, // ClassDeclaration :: // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' // + // 'class' is expected to be consumed by the caller. + // // A ClassDeclaration // // class C { ... } @@ -2161,7 +2201,6 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names, // // so rewrite it as such. - Expect(Token::CLASS, CHECK_OK); if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { ReportMessage(MessageTemplate::kSloppyLexical); *ok = false; @@ -2177,30 +2216,10 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names, VariableMode mode = is_strong(language_mode()) ? CONST : LET; VariableProxy* proxy = NewUnresolved(name, mode); - const bool is_class_declaration = true; - Declaration* declaration = factory()->NewVariableDeclaration( - proxy, mode, scope_, pos, is_class_declaration, - scope_->class_declaration_group_start()); - Variable* outer_class_variable = - Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + Declaration* declaration = + factory()->NewVariableDeclaration(proxy, mode, scope_, pos); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); proxy->var()->set_initializer_position(position()); - // This is needed because a class ("class Name { }") creates two bindings (one - // in the outer scope, and one in the class scope). The method is a function - // scope inside the inner scope (class scope). The consecutive class - // declarations are in the outer scope. - if (value->class_variable_proxy() && value->class_variable_proxy()->var() && - outer_class_variable->is_class()) { - // In some cases, the outer variable is not detected as a class variable; - // this happens e.g., for lazy methods. They are excluded from strong mode - // checks for now. TODO(marja, rossberg): re-create variables with the - // correct Kind and remove this hack. - value->class_variable_proxy() - ->var() - ->AsClassVariable() - ->set_declaration_group_start( - outer_class_variable->AsClassVariable()->declaration_group_start()); - } - Assignment* assignment = factory()->NewAssignment(Token::INIT, proxy, value, pos); Statement* assignment_statement = @@ -2281,17 +2300,16 @@ Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context, // is inside an initializer block, it is ignored. DeclarationParsingResult parsing_result; - ParseVariableDeclarations(var_context, &parsing_result, CHECK_OK); + Block* result = + ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK); ExpectSemicolon(CHECK_OK); - - Block* result = parsing_result.BuildInitializationBlock(names, CHECK_OK); return result; } - -void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, - DeclarationParsingResult* parsing_result, - bool* ok) { +Block* Parser::ParseVariableDeclarations( + VariableDeclarationContext var_context, + DeclarationParsingResult* parsing_result, + ZoneList<const AstRawString*>* names, bool* ok) { // VariableDeclarations :: // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] // @@ -2311,17 +2329,19 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, parsing_result->descriptor.declaration_pos = peek_position(); parsing_result->descriptor.initialization_pos = peek_position(); parsing_result->descriptor.mode = VAR; - // True if the binding needs initialization. 'let' and 'const' declared - // bindings are created uninitialized by their declaration nodes and - // need initialization. 'var' declared bindings are always initialized - // immediately by their declaration nodes. - parsing_result->descriptor.needs_init = false; + + Block* init_block = nullptr; + if (var_context != kForStatement) { + init_block = factory()->NewBlock( + NULL, 1, true, parsing_result->descriptor.declaration_pos); + } + if (peek() == Token::VAR) { if (is_strong(language_mode())) { Scanner::Location location = scanner()->peek_location(); ReportMessageAt(location, MessageTemplate::kStrongVar); *ok = false; - return; + return nullptr; } Consume(Token::VAR); } else if (peek() == Token::CONST && allow_const()) { @@ -2334,12 +2354,10 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, DCHECK(var_context != kStatement); parsing_result->descriptor.mode = CONST; } - parsing_result->descriptor.needs_init = true; } else if (peek() == Token::LET && allow_let()) { Consume(Token::LET); DCHECK(var_context != kStatement); parsing_result->descriptor.mode = LET; - parsing_result->descriptor.needs_init = true; } else { UNREACHABLE(); // by current callers } @@ -2350,7 +2368,6 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, bool first_declaration = true; int bindings_start = peek_position(); - bool is_for_iteration_variable; do { FuncNameInferrer::State fni_state(fni_); @@ -2360,27 +2377,20 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, Expression* pattern; int decl_pos = peek_position(); { - ExpressionClassifier pattern_classifier; + ExpressionClassifier pattern_classifier(this); Token::Value next = peek(); - pattern = ParsePrimaryExpression(&pattern_classifier, ok); - if (!*ok) return; - ValidateBindingPattern(&pattern_classifier, ok); - if (!*ok) return; + pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK); + ValidateBindingPattern(&pattern_classifier, CHECK_OK); if (IsLexicalVariableMode(parsing_result->descriptor.mode)) { - ValidateLetPattern(&pattern_classifier, ok); - if (!*ok) return; + ValidateLetPattern(&pattern_classifier, CHECK_OK); } if (!allow_harmony_destructuring_bind() && !pattern->IsVariableProxy()) { ReportUnexpectedToken(next); *ok = false; - return; + return nullptr; } } - bool is_pattern = - (pattern->IsObjectLiteral() || pattern->IsArrayLiteral()) && - !pattern->is_parenthesized(); - Scanner::Location variable_loc = scanner()->location(); const AstRawString* single_name = pattern->IsVariableProxy() ? pattern->AsVariableProxy()->raw_name() @@ -2389,25 +2399,13 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, if (fni_ != NULL) fni_->PushVariableName(single_name); } - is_for_iteration_variable = - var_context == kForStatement && - (peek() == Token::IN || PeekContextualKeyword(CStrVector("of"))); - if (is_for_iteration_variable && - (parsing_result->descriptor.mode == CONST || - parsing_result->descriptor.mode == CONST_LEGACY)) { - parsing_result->descriptor.needs_init = false; - } - Expression* value = NULL; - // Harmony consts have non-optional initializers. int initializer_position = RelocInfo::kNoPosition; if (Check(Token::ASSIGN)) { - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); value = ParseAssignmentExpression(var_context != kForStatement, - &classifier, ok); - if (!*ok) return; - value = ParserTraits::RewriteNonPattern(value, &classifier, ok); - if (!*ok) return; + &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); variable_loc.end_pos = scanner()->location().end_pos; if (!parsing_result->first_initializer_loc.IsValid()) { @@ -2424,48 +2422,60 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, } } - if (allow_harmony_function_name() && single_name) { - if (value->IsFunctionLiteral()) { - auto function_literal = value->AsFunctionLiteral(); - if (function_literal->is_anonymous()) { - function_literal->set_raw_name(single_name); - } - } else if (value->IsClassLiteral()) { - auto class_literal = value->AsClassLiteral(); - if (class_literal->raw_name() == nullptr) { - class_literal->set_raw_name(single_name); - } - } + if (allow_harmony_function_name()) { + ParserTraits::SetFunctionNameFromIdentifierRef(value, pattern); } // End position of the initializer is after the assignment expression. initializer_position = scanner()->location().end_pos; } else { - if ((parsing_result->descriptor.mode == CONST || is_pattern) && - !is_for_iteration_variable) { - ParserTraits::ReportMessageAt( - Scanner::Location(decl_pos, scanner()->location().end_pos), - MessageTemplate::kDeclarationMissingInitializer, - is_pattern ? "destructuring" : "const"); - *ok = false; - return; + // Initializers may be either required or implied unless this is a + // for-in/of iteration variable. + if (var_context != kForStatement || !PeekInOrOf()) { + // ES6 'const' and binding patterns require initializers. + if (parsing_result->descriptor.mode == CONST || + !pattern->IsVariableProxy()) { + ParserTraits::ReportMessageAt( + Scanner::Location(decl_pos, scanner()->location().end_pos), + MessageTemplate::kDeclarationMissingInitializer, + !pattern->IsVariableProxy() ? "destructuring" : "const"); + *ok = false; + return nullptr; + } + + // 'let x' and (legacy) 'const x' initialize 'x' to undefined. + if (parsing_result->descriptor.mode == LET || + parsing_result->descriptor.mode == CONST_LEGACY) { + value = GetLiteralUndefined(position()); + } } + // End position of the initializer is after the variable. initializer_position = position(); } - // Make sure that 'const x' and 'let x' initialize 'x' to undefined. - if (value == NULL && parsing_result->descriptor.needs_init) { - value = GetLiteralUndefined(position()); + DeclarationParsingResult::Declaration decl(pattern, initializer_position, + value); + if (var_context == kForStatement) { + // Save the declaration for further handling in ParseForStatement. + parsing_result->declarations.Add(decl); + } else { + // Immediately declare the variable otherwise. This avoids O(N^2) + // behavior (where N is the number of variables in a single + // declaration) in the PatternRewriter having to do with removing + // and adding VariableProxies to the Scope (see bug 4699). + DCHECK_NOT_NULL(init_block); + PatternRewriter::DeclareAndInitializeVariables( + init_block, &parsing_result->descriptor, &decl, names, CHECK_OK); } - - parsing_result->declarations.Add(DeclarationParsingResult::Declaration( - pattern, initializer_position, value)); first_declaration = false; } while (peek() == Token::COMMA); parsing_result->bindings_loc = Scanner::Location(bindings_start, scanner()->location().end_pos); + + DCHECK(*ok); + return init_block; } @@ -2511,13 +2521,13 @@ Statement* Parser::ParseExpressionOrLabelledStatement( IsClassConstructor(function_state_->kind())) { bool is_this = peek() == Token::THIS; Expression* expr; - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); if (is_this) { expr = ParseStrongInitializationExpression(&classifier, CHECK_OK); } else { expr = ParseStrongSuperCallExpression(&classifier, CHECK_OK); } - expr = ParserTraits::RewriteNonPattern(expr, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); switch (peek()) { case Token::SEMICOLON: Consume(Token::SEMICOLON); @@ -2728,23 +2738,22 @@ Statement* Parser::ParseReturnStatement(bool* ok) { if (IsSubclassConstructor(function_state_->kind())) { // For subclass constructors we need to return this in case of undefined - // and throw an exception in case of a non object. + // 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 : throw new TypeError(...); + // %_IsJSReceiver(temp) ? temp : 1; + + // temp = expr Variable* temp = scope_->NewTemporary( ast_value_factory()->empty_string()); Assignment* assign = factory()->NewAssignment( Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos); - Expression* throw_expression = - NewThrowTypeError(MessageTemplate::kDerivedConstructorReturn, - ast_value_factory()->empty_string(), pos); - // %_IsJSReceiver(temp) ZoneList<Expression*>* is_spec_object_args = new (zone()) ZoneList<Expression*>(1, zone()); @@ -2755,7 +2764,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) { // %_IsJSReceiver(temp) ? temp : throw_expression Expression* is_object_conditional = factory()->NewConditional( is_spec_object_call, factory()->NewVariableProxy(temp), - throw_expression, pos); + factory()->NewSmiLiteral(1, pos), pos); // temp === undefined Expression* is_undefined = factory()->NewCompareOperation( @@ -2768,7 +2777,10 @@ Statement* Parser::ParseReturnStatement(bool* ok) { is_object_conditional, pos); } - return_value->MarkTail(); + // ES6 14.6.1 Static Semantics: IsInTailPosition + if (FLAG_harmony_tailcalls && !is_sloppy(language_mode())) { + function_state_->AddExpressionInTailPosition(return_value); + } } ExpectSemicolon(CHECK_OK); @@ -2974,6 +2986,40 @@ Statement* Parser::ParseThrowStatement(bool* ok) { factory()->NewThrow(exception, pos), pos); } +class Parser::DontCollectExpressionsInTailPositionScope { + public: + DontCollectExpressionsInTailPositionScope( + Parser::FunctionState* function_state) + : function_state_(function_state), + old_value_(function_state->collect_expressions_in_tail_position()) { + function_state->set_collect_expressions_in_tail_position(false); + } + ~DontCollectExpressionsInTailPositionScope() { + function_state_->set_collect_expressions_in_tail_position(old_value_); + } + + private: + Parser::FunctionState* function_state_; + bool old_value_; +}; + +// Collects all return expressions at tail call position in this scope +// to a separate list. +class Parser::CollectExpressionsInTailPositionToListScope { + public: + CollectExpressionsInTailPositionToListScope( + Parser::FunctionState* function_state, List<Expression*>* list) + : function_state_(function_state), list_(list) { + function_state->expressions_in_tail_position().Swap(list_); + } + ~CollectExpressionsInTailPositionToListScope() { + function_state_->expressions_in_tail_position().Swap(list_); + } + + private: + Parser::FunctionState* function_state_; + List<Expression*>* list_; +}; TryStatement* Parser::ParseTryStatement(bool* ok) { // TryStatement :: @@ -2990,7 +3036,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Expect(Token::TRY, CHECK_OK); int pos = position(); - Block* try_block = ParseBlock(NULL, CHECK_OK); + Block* try_block; + { + DontCollectExpressionsInTailPositionScope no_tail_calls(function_state_); + try_block = ParseBlock(NULL, CHECK_OK); + } Token::Value tok = peek(); if (tok != Token::CATCH && tok != Token::FINALLY) { @@ -3002,6 +3052,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Scope* catch_scope = NULL; Variable* catch_variable = NULL; Block* catch_block = NULL; + List<Expression*> expressions_in_tail_position_in_catch_block; if (tok == Token::CATCH) { Consume(Token::CATCH); @@ -3009,7 +3060,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { catch_scope = NewScope(scope_, CATCH_SCOPE); catch_scope->set_start_position(scanner()->location().beg_pos); - ExpressionClassifier pattern_classifier; + ExpressionClassifier pattern_classifier(this); Expression* pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK); ValidateBindingPattern(&pattern_classifier, CHECK_OK); @@ -3027,6 +3078,9 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Expect(Token::RPAREN, CHECK_OK); { + CollectExpressionsInTailPositionToListScope + collect_expressions_in_tail_position_scope( + function_state_, &expressions_in_tail_position_in_catch_block); BlockState block_state(&scope_, catch_scope); // TODO(adamk): Make a version of ParseBlock that takes a scope and @@ -3047,7 +3101,6 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { descriptor.scope = scope_; descriptor.hoist_scope = nullptr; descriptor.mode = LET; - descriptor.needs_init = true; descriptor.declaration_pos = pattern->position(); descriptor.initialization_pos = pattern->position(); @@ -3102,6 +3155,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { TryStatement* result = NULL; if (catch_block != NULL) { + // For a try-catch construct append return expressions from the catch block + // to the list of return expressions. + function_state_->expressions_in_tail_position().AddAll( + expressions_in_tail_position_in_catch_block); + DCHECK(finally_block == NULL); DCHECK(catch_scope != NULL && catch_variable != NULL); result = factory()->NewTryCatchStatement(try_block, catch_scope, @@ -3262,6 +3320,7 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, } for_of->Initialize(each, subject, body, + iterator, assign_iterator, next_result, result_done, @@ -3288,9 +3347,8 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, } } - Statement* Parser::DesugarLexicalBindingsInForStatement( - Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names, + Scope* inner_scope, VariableMode mode, ZoneList<const AstRawString*>* names, ForStatement* loop, Statement* init, Expression* cond, Statement* next, Statement* body, bool* ok) { // ES6 13.7.4.8 specifies that on each loop iteration the let variables are @@ -3331,7 +3389,6 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( // } DCHECK(names->length() > 0); - Scope* for_scope = scope_; ZoneList<Variable*> temps(names->length(), zone()); Block* outer_block = factory()->NewBlock(NULL, names->length() + 4, false, @@ -3384,150 +3441,155 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( ForStatement* outer_loop = factory()->NewForStatement(NULL, RelocInfo::kNoPosition); outer_block->statements()->Add(outer_loop, zone()); - - outer_block->set_scope(for_scope); - scope_ = inner_scope; + outer_block->set_scope(scope_); Block* inner_block = factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition); - Block* ignore_completion_block = factory()->NewBlock( - NULL, names->length() + 3, true, RelocInfo::kNoPosition); - ZoneList<Variable*> inner_vars(names->length(), zone()); - // For each let variable x: - // make statement: let/const x = temp_x. - VariableMode mode = is_const ? CONST : LET; - for (int i = 0; i < names->length(); i++) { - VariableProxy* proxy = NewUnresolved(names->at(i), mode); - Declaration* declaration = factory()->NewVariableDeclaration( - proxy, mode, scope_, RelocInfo::kNoPosition); - Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); - inner_vars.Add(declaration->proxy()->var(), zone()); - VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); - Assignment* assignment = factory()->NewAssignment( - Token::INIT, proxy, temp_proxy, RelocInfo::kNoPosition); - Statement* assignment_statement = - factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); - DCHECK(init->position() != RelocInfo::kNoPosition); - proxy->var()->set_initializer_position(init->position()); - ignore_completion_block->statements()->Add(assignment_statement, zone()); - } + { + BlockState block_state(&scope_, inner_scope); - // Make statement: if (first == 1) { first = 0; } else { next; } - if (next) { - DCHECK(first); - Expression* compare = NULL; - // Make compare expression: first == 1. - { - Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); - VariableProxy* first_proxy = factory()->NewVariableProxy(first); - compare = factory()->NewCompareOperation(Token::EQ, first_proxy, const1, - RelocInfo::kNoPosition); - } - Statement* clear_first = NULL; - // Make statement: first = 0. - { - VariableProxy* first_proxy = factory()->NewVariableProxy(first); - Expression* const0 = factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); + Block* ignore_completion_block = factory()->NewBlock( + NULL, names->length() + 3, true, RelocInfo::kNoPosition); + ZoneList<Variable*> inner_vars(names->length(), zone()); + // For each let variable x: + // make statement: let/const x = temp_x. + for (int i = 0; i < names->length(); i++) { + VariableProxy* proxy = NewUnresolved(names->at(i), mode); + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, mode, scope_, RelocInfo::kNoPosition); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + inner_vars.Add(declaration->proxy()->var(), zone()); + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); Assignment* assignment = factory()->NewAssignment( - Token::ASSIGN, first_proxy, const0, RelocInfo::kNoPosition); - clear_first = + Token::INIT, proxy, temp_proxy, RelocInfo::kNoPosition); + Statement* assignment_statement = factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + DCHECK(init->position() != RelocInfo::kNoPosition); + proxy->var()->set_initializer_position(init->position()); + ignore_completion_block->statements()->Add(assignment_statement, zone()); } - Statement* clear_first_or_next = factory()->NewIfStatement( - compare, clear_first, next, RelocInfo::kNoPosition); - ignore_completion_block->statements()->Add(clear_first_or_next, zone()); - } - Variable* flag = scope_->NewTemporary(temp_name); - // Make statement: flag = 1. - { - VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); - Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); - Assignment* assignment = factory()->NewAssignment( - Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition); - Statement* assignment_statement = - factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); - ignore_completion_block->statements()->Add(assignment_statement, zone()); - } + // Make statement: if (first == 1) { first = 0; } else { next; } + if (next) { + DCHECK(first); + Expression* compare = NULL; + // Make compare expression: first == 1. + { + Expression* const1 = + factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + VariableProxy* first_proxy = factory()->NewVariableProxy(first); + compare = factory()->NewCompareOperation(Token::EQ, first_proxy, const1, + RelocInfo::kNoPosition); + } + Statement* clear_first = NULL; + // Make statement: first = 0. + { + VariableProxy* first_proxy = factory()->NewVariableProxy(first); + Expression* const0 = + factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, first_proxy, const0, RelocInfo::kNoPosition); + clear_first = factory()->NewExpressionStatement(assignment, + RelocInfo::kNoPosition); + } + Statement* clear_first_or_next = factory()->NewIfStatement( + compare, clear_first, next, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(clear_first_or_next, zone()); + } - // Make statement: if (!cond) break. - if (cond) { - Statement* stop = - factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); - Statement* noop = factory()->NewEmptyStatement(RelocInfo::kNoPosition); - ignore_completion_block->statements()->Add( - factory()->NewIfStatement(cond, noop, stop, cond->position()), zone()); - } + Variable* flag = scope_->NewTemporary(temp_name); + // Make statement: flag = 1. + { + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(assignment_statement, zone()); + } - inner_block->statements()->Add(ignore_completion_block, zone()); - // Make cond expression for main loop: flag == 1. - Expression* flag_cond = NULL; - { - Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); - VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); - flag_cond = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, - RelocInfo::kNoPosition); - } + // Make statement: if (!cond) break. + if (cond) { + Statement* stop = + factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); + Statement* noop = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add( + factory()->NewIfStatement(cond, noop, stop, cond->position()), + zone()); + } - // Create chain of expressions "flag = 0, temp_x = x, ..." - Statement* compound_next_statement = NULL; - { - Expression* compound_next = NULL; - // Make expression: flag = 0. + inner_block->statements()->Add(ignore_completion_block, zone()); + // Make cond expression for main loop: flag == 1. + Expression* flag_cond = NULL; { + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); - Expression* const0 = factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); - compound_next = factory()->NewAssignment(Token::ASSIGN, flag_proxy, - const0, RelocInfo::kNoPosition); + flag_cond = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, + RelocInfo::kNoPosition); } - // Make the comma-separated list of temp_x = x assignments. - int inner_var_proxy_pos = scanner()->location().beg_pos; - for (int i = 0; i < names->length(); i++) { - VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); - VariableProxy* proxy = - factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos); - Assignment* assignment = factory()->NewAssignment( - Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition); - compound_next = factory()->NewBinaryOperation( - Token::COMMA, compound_next, assignment, RelocInfo::kNoPosition); - } + // Create chain of expressions "flag = 0, temp_x = x, ..." + Statement* compound_next_statement = NULL; + { + Expression* compound_next = NULL; + // Make expression: flag = 0. + { + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + Expression* const0 = + factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); + compound_next = factory()->NewAssignment( + Token::ASSIGN, flag_proxy, const0, RelocInfo::kNoPosition); + } - compound_next_statement = factory()->NewExpressionStatement( - compound_next, RelocInfo::kNoPosition); - } + // Make the comma-separated list of temp_x = x assignments. + int inner_var_proxy_pos = scanner()->location().beg_pos; + for (int i = 0; i < names->length(); i++) { + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); + VariableProxy* proxy = + factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition); + compound_next = factory()->NewBinaryOperation( + Token::COMMA, compound_next, assignment, RelocInfo::kNoPosition); + } - // Make statement: labels: for (; flag == 1; flag = 0, temp_x = x) - // Note that we re-use the original loop node, which retains its labels - // and ensures that any break or continue statements in body point to - // the right place. - loop->Initialize(NULL, flag_cond, compound_next_statement, body); - inner_block->statements()->Add(loop, zone()); + compound_next_statement = factory()->NewExpressionStatement( + compound_next, RelocInfo::kNoPosition); + } - // Make statement: {{if (flag == 1) break;}} - { - Expression* compare = NULL; - // Make compare expresion: flag == 1. + // Make statement: labels: for (; flag == 1; flag = 0, temp_x = x) + // Note that we re-use the original loop node, which retains its labels + // and ensures that any break or continue statements in body point to + // the right place. + loop->Initialize(NULL, flag_cond, compound_next_statement, body); + inner_block->statements()->Add(loop, zone()); + + // Make statement: {{if (flag == 1) break;}} { - Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); - VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); - compare = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, - RelocInfo::kNoPosition); - } - Statement* stop = - factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); - Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition); - Statement* if_flag_break = - factory()->NewIfStatement(compare, stop, empty, RelocInfo::kNoPosition); - Block* ignore_completion_block = - factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition); - ignore_completion_block->statements()->Add(if_flag_break, zone()); - inner_block->statements()->Add(ignore_completion_block, zone()); - } + Expression* compare = NULL; + // Make compare expresion: flag == 1. + { + Expression* const1 = + factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + compare = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, + RelocInfo::kNoPosition); + } + Statement* stop = + factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); + Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + Statement* if_flag_break = factory()->NewIfStatement( + compare, stop, empty, RelocInfo::kNoPosition); + Block* ignore_completion_block = + factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(if_flag_break, zone()); + inner_block->statements()->Add(ignore_completion_block, zone()); + } - inner_scope->set_end_position(scanner()->location().end_pos); - inner_block->set_scope(inner_scope); - scope_ = for_scope; + inner_scope->set_end_position(scanner()->location().end_pos); + inner_block->set_scope(inner_scope); + } outer_loop->Initialize(NULL, NULL, NULL, inner_block); return outer_block; @@ -3536,18 +3598,14 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok) { - // ForStatement :: - // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement - int stmt_pos = peek_position(); - bool is_const = false; Statement* init = NULL; ZoneList<const AstRawString*> lexical_bindings(1, zone()); // Create an in-between scope for let-bound iteration variables. - Scope* saved_scope = scope_; Scope* for_scope = NewScope(scope_, BLOCK_SCOPE); - scope_ = for_scope; + + BlockState block_state(&scope_, for_scope); Expect(Token::FOR, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); for_scope->set_start_position(scanner()->location().beg_pos); @@ -3556,23 +3614,20 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, if (peek() != Token::SEMICOLON) { if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) || (peek() == Token::LET && IsNextLetKeyword())) { - ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK); - is_const = parsing_result.descriptor.mode == CONST; + ParseVariableDeclarations(kForStatement, &parsing_result, nullptr, + CHECK_OK); - int num_decl = parsing_result.declarations.length(); - bool accept_IN = num_decl >= 1; ForEachStatement::VisitMode mode; int each_beg_pos = scanner()->location().beg_pos; int each_end_pos = scanner()->location().end_pos; - if (accept_IN && CheckInOrOf(&mode, ok)) { + if (CheckInOrOf(&mode, ok)) { if (!*ok) return nullptr; - if (num_decl != 1) { - const char* loop_type = - mode == ForEachStatement::ITERATE ? "for-of" : "for-in"; + if (parsing_result.declarations.length() != 1) { ParserTraits::ReportMessageAt( parsing_result.bindings_loc, - MessageTemplate::kForInOfLoopMultiBindings, loop_type); + MessageTemplate::kForInOfLoopMultiBindings, + ForEachStatement::VisitModeString(mode)); *ok = false; return nullptr; } @@ -3582,14 +3637,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, (is_strict(language_mode()) || mode == ForEachStatement::ITERATE || IsLexicalVariableMode(parsing_result.descriptor.mode) || !decl.pattern->IsVariableProxy())) { - if (mode == ForEachStatement::ITERATE) { - ReportMessageAt(parsing_result.first_initializer_loc, - MessageTemplate::kForOfLoopInitializer); - } else { - // TODO(caitp): This should be an error in sloppy mode too. - ReportMessageAt(parsing_result.first_initializer_loc, - MessageTemplate::kForInLoopInitializer); - } + ParserTraits::ReportMessageAt( + parsing_result.first_initializer_loc, + MessageTemplate::kForInOfLoopInitializer, + ForEachStatement::VisitModeString(mode)); *ok = false; return nullptr; } @@ -3599,6 +3650,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, // special case for legacy for (var/const x =.... in) if (!IsLexicalVariableMode(parsing_result.descriptor.mode) && decl.pattern->IsVariableProxy() && decl.initializer != nullptr) { + ++use_counts_[v8::Isolate::kForInInitializer]; const AstRawString* name = decl.pattern->AsVariableProxy()->raw_name(); VariableProxy* single_var = scope_->NewUnresolved( @@ -3630,52 +3682,59 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, // let x; // for TDZ // } - Variable* temp = scope_->NewTemporary( - ast_value_factory()->dot_for_string()); + Variable* temp = + scope_->NewTemporary(ast_value_factory()->dot_for_string()); ForEachStatement* loop = factory()->NewForEachStatement(mode, labels, stmt_pos); Target target(&this->target_stack_, loop); - Expression* enumerable = ParseExpression(true, CHECK_OK); + Expression* enumerable; + if (mode == ForEachStatement::ITERATE) { + ExpressionClassifier classifier(this); + enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); + } else { + enumerable = ParseExpression(true, CHECK_OK); + } Expect(Token::RPAREN, CHECK_OK); Scope* body_scope = NewScope(scope_, BLOCK_SCOPE); body_scope->set_start_position(scanner()->location().beg_pos); - scope_ = body_scope; - - Statement* body = ParseSubStatement(NULL, CHECK_OK); Block* body_block = factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition); - auto each_initialization_block = - factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition); { - auto descriptor = parsing_result.descriptor; - descriptor.declaration_pos = RelocInfo::kNoPosition; - descriptor.initialization_pos = RelocInfo::kNoPosition; - decl.initializer = factory()->NewVariableProxy(temp); + BlockState block_state(&scope_, body_scope); + + Statement* body = ParseSubStatement(NULL, CHECK_OK); + + auto each_initialization_block = + factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition); + { + auto descriptor = parsing_result.descriptor; + descriptor.declaration_pos = RelocInfo::kNoPosition; + descriptor.initialization_pos = RelocInfo::kNoPosition; + decl.initializer = factory()->NewVariableProxy(temp); + + PatternRewriter::DeclareAndInitializeVariables( + each_initialization_block, &descriptor, &decl, + IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings + : nullptr, + CHECK_OK); + } - PatternRewriter::DeclareAndInitializeVariables( - each_initialization_block, &descriptor, &decl, - IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings - : nullptr, - CHECK_OK); + body_block->statements()->Add(each_initialization_block, zone()); + body_block->statements()->Add(body, zone()); + VariableProxy* temp_proxy = + factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos); + InitializeForEachStatement(loop, temp_proxy, enumerable, body_block, + false); } - - body_block->statements()->Add(each_initialization_block, zone()); - body_block->statements()->Add(body, zone()); - VariableProxy* temp_proxy = - factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos); - InitializeForEachStatement(loop, temp_proxy, enumerable, body_block, - false); - scope_ = for_scope; body_scope->set_end_position(scanner()->location().end_pos); body_scope = body_scope->FinalizeBlockScope(); - if (body_scope != nullptr) { - body_block->set_scope(body_scope); - } + body_block->set_scope(body_scope); // Create a TDZ for any lexically-bound names. if (IsLexicalVariableMode(parsing_result.descriptor.mode)) { @@ -3688,28 +3747,31 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, // TODO(adamk): This needs to be some sort of special // INTERNAL variable that's invisible to the debugger // but visible to everything else. - VariableProxy* tdz_proxy = NewUnresolved(lexical_bindings[i], LET); + VariableProxy* tdz_proxy = + NewUnresolved(lexical_bindings[i], LET); Declaration* tdz_decl = factory()->NewVariableDeclaration( tdz_proxy, LET, scope_, RelocInfo::kNoPosition); - Variable* tdz_var = Declare(tdz_decl, DeclarationDescriptor::NORMAL, - true, CHECK_OK); + Variable* tdz_var = Declare( + tdz_decl, DeclarationDescriptor::NORMAL, true, CHECK_OK); tdz_var->set_initializer_position(position()); } } - scope_ = saved_scope; + Statement* final_loop = loop->IsForOfStatement() + ? FinalizeForOfStatement( + loop->AsForOfStatement(), RelocInfo::kNoPosition) + : loop; + for_scope->set_end_position(scanner()->location().end_pos); for_scope = for_scope->FinalizeBlockScope(); // Parsed for-in loop w/ variable declarations. if (init_block != nullptr) { - init_block->statements()->Add(loop, zone()); - if (for_scope != nullptr) { - init_block->set_scope(for_scope); - } + init_block->statements()->Add(final_loop, zone()); + init_block->set_scope(for_scope); return init_block; } else { DCHECK_NULL(for_scope); - return loop; + return final_loop; } } else { init = parsing_result.BuildInitializationBlock( @@ -3720,7 +3782,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, } } else { int lhs_beg_pos = peek_position(); - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); Expression* expression = ParseExpression(false, &classifier, CHECK_OK); int lhs_end_pos = scanner()->location().end_pos; ForEachStatement::VisitMode mode; @@ -3738,8 +3800,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, if (is_destructuring) { ValidateAssignmentPattern(&classifier, CHECK_OK); } else { - expression = - ParserTraits::RewriteNonPattern(expression, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); } if (is_for_each) { @@ -3753,7 +3814,15 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, factory()->NewForEachStatement(mode, labels, stmt_pos); Target target(&this->target_stack_, loop); - Expression* enumerable = ParseExpression(true, CHECK_OK); + Expression* enumerable; + if (mode == ForEachStatement::ITERATE) { + ExpressionClassifier classifier(this); + enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); + } else { + enumerable = ParseExpression(true, CHECK_OK); + } + Expect(Token::RPAREN, CHECK_OK); // Make a block around the statement in case a lexical binding @@ -3763,24 +3832,28 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, // expressions in head of the loop should actually have variables // resolved in the outer scope. Scope* body_scope = NewScope(for_scope, BLOCK_SCOPE); - scope_ = body_scope; - Block* block = - factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); - Statement* body = ParseSubStatement(NULL, CHECK_OK); - block->statements()->Add(body, zone()); - InitializeForEachStatement(loop, expression, enumerable, block, - is_destructuring); - scope_ = saved_scope; - body_scope->set_end_position(scanner()->location().end_pos); - body_scope = body_scope->FinalizeBlockScope(); - if (body_scope != nullptr) { + { + BlockState block_state(&scope_, body_scope); + Block* block = + factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); + Statement* body = ParseSubStatement(NULL, CHECK_OK); + block->statements()->Add(body, zone()); + InitializeForEachStatement(loop, expression, enumerable, block, + is_destructuring); + body_scope->set_end_position(scanner()->location().end_pos); + body_scope = body_scope->FinalizeBlockScope(); block->set_scope(body_scope); } + + Statement* final_loop = loop->IsForOfStatement() + ? FinalizeForOfStatement( + loop->AsForOfStatement(), RelocInfo::kNoPosition) + : loop; + for_scope->set_end_position(scanner()->location().end_pos); for_scope = for_scope->FinalizeBlockScope(); DCHECK(for_scope == nullptr); - // Parsed for-in loop. - return loop; + return final_loop; } else { init = factory()->NewExpressionStatement(expression, lhs_beg_pos); @@ -3802,40 +3875,42 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, } Expect(Token::SEMICOLON, CHECK_OK); + Expression* cond = NULL; + Statement* next = NULL; + Statement* body = NULL; + // If there are let bindings, then condition and the next statement of the // for loop must be parsed in a new scope. - Scope* inner_scope = NULL; + Scope* inner_scope = scope_; if (lexical_bindings.length() > 0) { inner_scope = NewScope(for_scope, BLOCK_SCOPE); inner_scope->set_start_position(scanner()->location().beg_pos); - scope_ = inner_scope; } + { + BlockState block_state(&scope_, inner_scope); - Expression* cond = NULL; - if (peek() != Token::SEMICOLON) { - cond = ParseExpression(true, CHECK_OK); - } - Expect(Token::SEMICOLON, CHECK_OK); + if (peek() != Token::SEMICOLON) { + cond = ParseExpression(true, CHECK_OK); + } + Expect(Token::SEMICOLON, CHECK_OK); - Statement* next = NULL; - if (peek() != Token::RPAREN) { - Expression* exp = ParseExpression(true, CHECK_OK); - next = factory()->NewExpressionStatement(exp, exp->position()); - } - Expect(Token::RPAREN, CHECK_OK); + if (peek() != Token::RPAREN) { + Expression* exp = ParseExpression(true, CHECK_OK); + next = factory()->NewExpressionStatement(exp, exp->position()); + } + Expect(Token::RPAREN, CHECK_OK); - Statement* body = ParseSubStatement(NULL, CHECK_OK); + body = ParseSubStatement(NULL, CHECK_OK); + } Statement* result = NULL; if (lexical_bindings.length() > 0) { - scope_ = for_scope; + BlockState block_state(&scope_, for_scope); result = DesugarLexicalBindingsInForStatement( - inner_scope, is_const, &lexical_bindings, loop, init, cond, - next, body, CHECK_OK); - scope_ = saved_scope; + inner_scope, parsing_result.descriptor.mode, &lexical_bindings, loop, + init, cond, next, body, CHECK_OK); for_scope->set_end_position(scanner()->location().end_pos); } else { - scope_ = saved_scope; for_scope->set_end_position(scanner()->location().end_pos); for_scope = for_scope->FinalizeBlockScope(); if (for_scope) { @@ -4035,7 +4110,7 @@ void ParserTraits::ParseArrowFunctionFormalParameterList( ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ok); if (!*ok) return; - ExpressionClassifier classifier; + Type::ExpressionClassifier classifier(parser_); if (!parameters->is_simple) { classifier.RecordNonSimpleParameter(); } @@ -4069,7 +4144,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral( const AstRawString* 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, LanguageMode language_mode, bool* ok) { // Function :: // '(' FormalParameterList? ')' '{' FunctionBody '}' @@ -4137,17 +4211,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral( int materialized_literal_count = -1; int expected_property_count = -1; DuplicateFinder duplicate_finder(scanner()->unicode_cache()); - ExpressionClassifier formals_classifier(&duplicate_finder); FunctionLiteral::EagerCompileHint eager_compile_hint = parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile : FunctionLiteral::kShouldLazyCompile; bool should_be_used_once_hint = false; + bool has_duplicate_parameters; // Parse function. { AstNodeFactory function_factory(ast_value_factory()); FunctionState function_state(&function_state_, &scope_, scope, kind, &function_factory); scope_->SetScopeName(function_name); + ExpressionClassifier formals_classifier(this, &duplicate_finder); if (is_generator) { // For generators, allocating variables in contexts is currently a win @@ -4172,11 +4247,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral( Expect(Token::RPAREN, CHECK_OK); int formals_end_position = scanner()->location().end_pos; - CheckArityRestrictions(arity, arity_restriction, - formals.has_rest, start_position, + CheckArityRestrictions(arity, kind, formals.has_rest, start_position, formals_end_position, CHECK_OK); Expect(Token::LBRACE, CHECK_OK); + // Don't include the rest parameter into the function's formal parameter + // count (esp. the SharedFunctionInfo::internal_formal_parameter_count, + // which says whether we need to create an arguments adaptor frame). + if (formals.has_rest) arity--; + // Determine if the function can be parsed lazily. Lazy parsing is different // from lazy compilation; we need to parse more eagerly than we compile. @@ -4321,10 +4400,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // If body can be inspected, rewrite queued destructuring assignments ParserTraits::RewriteDestructuringAssignments(); } + has_duplicate_parameters = + !formals_classifier.is_valid_formal_parameter_list_without_duplicates(); } - bool has_duplicate_parameters = - !formals_classifier.is_valid_formal_parameter_list_without_duplicates(); FunctionLiteral::ParameterFlag duplicate_parameters = has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters : FunctionLiteral::kNoDuplicateParameters; @@ -4337,10 +4416,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral( if (should_be_used_once_hint) function_literal->set_should_be_used_once_hint(); - if (scope->has_rest_parameter()) { - function_literal->set_dont_optimize_reason(kRestParameter); - } - if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal); return function_literal; } @@ -4462,8 +4537,7 @@ class InitializerRewriter : public AstExpressionVisitor { private: void VisitExpression(Expression* expr) { - RewritableAssignmentExpression* to_rewrite = - expr->AsRewritableAssignmentExpression(); + RewritableExpression* to_rewrite = expr->AsRewritableExpression(); if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return; Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite, @@ -4497,7 +4571,6 @@ Block* Parser::BuildParameterInitializationBlock( descriptor.scope = scope_; descriptor.hoist_scope = nullptr; descriptor.mode = LET; - descriptor.needs_init = true; descriptor.declaration_pos = parameter.pattern->position(); // The position that will be used by the AssignmentExpression // which copies from the temp parameter to the pattern. @@ -4597,35 +4670,72 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( { BlockState block_state(&scope_, inner_scope); - // For generators, allocate and yield an iterator on function entry. if (IsGeneratorFunction(kind)) { - ZoneList<Expression*>* arguments = - new(zone()) ZoneList<Expression*>(0, zone()); - CallRuntime* allocation = factory()->NewCallRuntime( - Runtime::kCreateJSGeneratorObject, arguments, pos); - VariableProxy* init_proxy = factory()->NewVariableProxy( - function_state_->generator_object_variable()); - Assignment* assignment = factory()->NewAssignment( - Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition); - VariableProxy* get_proxy = factory()->NewVariableProxy( - function_state_->generator_object_variable()); - Yield* yield = factory()->NewYield( - get_proxy, assignment, Yield::kInitial, RelocInfo::kNoPosition); - body->Add(factory()->NewExpressionStatement( - yield, RelocInfo::kNoPosition), zone()); - } + // We produce: + // + // try { InitialYield; ...body...; FinalYield } + // finally { %GeneratorClose(generator) } + // + // - InitialYield yields the actual generator object. + // - FinalYield yields {value: foo, done: true} where foo is the + // completion value of body. (This is needed here in case the body + // falls through without an explicit return.) + // - Any return statement inside the body will be converted into a similar + // FinalYield. + // - If the generator terminates for whatever reason, we must close it. + // Hence the finally clause. + + Block* try_block = + factory()->NewBlock(nullptr, 3, false, RelocInfo::kNoPosition); - ParseStatementList(body, Token::RBRACE, CHECK_OK); + { + ZoneList<Expression*>* arguments = + new (zone()) ZoneList<Expression*>(0, zone()); + CallRuntime* allocation = factory()->NewCallRuntime( + Runtime::kCreateJSGeneratorObject, arguments, pos); + VariableProxy* init_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Assignment* assignment = factory()->NewAssignment( + Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition); + VariableProxy* get_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Yield* yield = factory()->NewYield( + get_proxy, assignment, Yield::kInitial, RelocInfo::kNoPosition); + try_block->statements()->Add( + factory()->NewExpressionStatement(yield, RelocInfo::kNoPosition), + zone()); + } + + ParseStatementList(try_block->statements(), Token::RBRACE, CHECK_OK); - if (IsGeneratorFunction(kind)) { VariableProxy* get_proxy = factory()->NewVariableProxy( function_state_->generator_object_variable()); Expression* undefined = factory()->NewUndefinedLiteral(RelocInfo::kNoPosition); Yield* yield = factory()->NewYield(get_proxy, undefined, Yield::kFinal, RelocInfo::kNoPosition); - body->Add(factory()->NewExpressionStatement( - yield, RelocInfo::kNoPosition), zone()); + try_block->statements()->Add( + factory()->NewExpressionStatement(yield, RelocInfo::kNoPosition), + zone()); + + Block* finally_block = + factory()->NewBlock(nullptr, 1, false, RelocInfo::kNoPosition); + ZoneList<Expression*>* args = + new (zone()) ZoneList<Expression*>(1, zone()); + VariableProxy* call_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + args->Add(call_proxy, zone()); + Expression* call = factory()->NewCallRuntime( + Runtime::kGeneratorClose, args, RelocInfo::kNoPosition); + finally_block->statements()->Add( + factory()->NewExpressionStatement(call, RelocInfo::kNoPosition), + zone()); + + body->Add(factory()->NewTryFinallyStatement(try_block, finally_block, + RelocInfo::kNoPosition), + zone()); + } else { + ParseStatementList(body, Token::RBRACE, CHECK_OK); } if (IsSubclassConstructor(kind)) { @@ -4682,6 +4792,13 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( RelocInfo::kNoPosition)); } + // ES6 14.6.1 Static Semantics: IsInTailPosition + // Mark collected return expressions that are in tail call position. + const List<Expression*>& expressions_in_tail_position = + function_state_->expressions_in_tail_position(); + for (int i = 0; i < expressions_in_tail_position.length(); ++i) { + expressions_in_tail_position[i]->MarkTail(); + } return result; } @@ -4693,6 +4810,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( if (pre_parse_timer_ != NULL) { pre_parse_timer_->Start(); } + TRACE_EVENT0("v8", "V8.PreParse"); + DCHECK_EQ(Token::LBRACE, scanner()->current_token()); if (reusable_preparser_ == NULL) { @@ -4709,6 +4828,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( SET_ALLOW(strong_mode); SET_ALLOW(harmony_do_expressions); SET_ALLOW(harmony_function_name); + SET_ALLOW(harmony_function_sent); #undef SET_ALLOW } PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( @@ -4751,19 +4871,17 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, VariableProxy* proxy = NULL; if (name != NULL) { proxy = NewUnresolved(name, CONST); - const bool is_class_declaration = true; - Declaration* declaration = factory()->NewVariableDeclaration( - proxy, CONST, block_scope, pos, is_class_declaration, - scope_->class_declaration_group_start()); + Declaration* declaration = + factory()->NewVariableDeclaration(proxy, CONST, block_scope, pos); Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); } Expression* extends = NULL; if (Check(Token::EXTENDS)) { block_scope->set_start_position(scanner()->location().end_pos); - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); extends = ParseLeftHandSideExpression(&classifier, CHECK_OK); - extends = ParserTraits::RewriteNonPattern(extends, &classifier, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); } else { block_scope->set_start_position(scanner()->location().end_pos); } @@ -4784,25 +4902,27 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, const bool is_static = false; bool is_computed_name = false; // Classes do not care about computed // property names here. - ExpressionClassifier classifier; - const AstRawString* name = nullptr; + ExpressionClassifier classifier(this); + const AstRawString* property_name = nullptr; ObjectLiteral::Property* property = ParsePropertyDefinition( &checker, in_class, has_extends, is_static, &is_computed_name, - &has_seen_constructor, &classifier, &name, CHECK_OK); - property = ParserTraits::RewriteNonPatternObjectLiteralProperty( - property, &classifier, CHECK_OK); + &has_seen_constructor, &classifier, &property_name, CHECK_OK); + RewriteNonPattern(&classifier, CHECK_OK); if (has_seen_constructor && constructor == NULL) { constructor = GetPropertyValue(property)->AsFunctionLiteral(); DCHECK_NOT_NULL(constructor); + constructor->set_raw_name( + name != nullptr ? name : ast_value_factory()->empty_string()); } else { properties->Add(property, zone()); } if (fni_ != NULL) fni_->Infer(); - if (allow_harmony_function_name()) { - SetFunctionNameFromPropertyName(property, name); + if (allow_harmony_function_name() && + property_name != ast_value_factory()->constructor_string()) { + SetFunctionNameFromPropertyName(property, property_name); } } @@ -4810,8 +4930,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, int end_pos = scanner()->location().end_pos; if (constructor == NULL) { - constructor = DefaultConstructor(extends != NULL, block_scope, pos, end_pos, - block_scope->language_mode()); + constructor = DefaultConstructor(name, extends != NULL, block_scope, pos, + end_pos, block_scope->language_mode()); } // Note that we do not finalize this block scope because strong @@ -4823,8 +4943,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, proxy->var()->set_initializer_position(end_pos); } - return factory()->NewClassLiteral(name, block_scope, proxy, extends, - constructor, properties, pos, end_pos); + return factory()->NewClassLiteral(block_scope, proxy, extends, constructor, + properties, pos, end_pos); } @@ -4838,10 +4958,9 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) { const AstRawString* name = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); Scanner::Location spread_pos; - ExpressionClassifier classifier; + ExpressionClassifier classifier(this); ZoneList<Expression*>* args = ParseArguments(&spread_pos, &classifier, CHECK_OK); - args = RewriteNonPatternArguments(args, &classifier, CHECK_OK); DCHECK(!spread_pos.IsValid()); @@ -5051,6 +5170,12 @@ void Parser::Internalize(Isolate* isolate, Handle<Script> script, bool error) { isolate->CountUsage(v8::Isolate::UseCounterFeature(feature)); } } + if (scanner_.FoundHtmlComment()) { + isolate->CountUsage(v8::Isolate::kHtmlComment); + if (script->line_offset() == 0 && script->column_offset() == 0) { + isolate->CountUsage(v8::Isolate::kHtmlCommentInExternalScript); + } + } isolate->counters()->total_preparse_skipped()->Increment( total_preparse_skipped_); } @@ -5404,143 +5529,1379 @@ void ParserTraits::RewriteDestructuringAssignments() { } -Expression* ParserTraits::RewriteNonPattern( - Expression* expr, const ExpressionClassifier* classifier, bool* ok) { - return parser_->RewriteNonPattern(expr, classifier, ok); +void ParserTraits::RewriteNonPattern(Type::ExpressionClassifier* classifier, + bool* ok) { + parser_->RewriteNonPattern(classifier, ok); } -ZoneList<Expression*>* ParserTraits::RewriteNonPatternArguments( - ZoneList<Expression*>* args, const ExpressionClassifier* classifier, - bool* ok) { - return parser_->RewriteNonPatternArguments(args, classifier, ok); +Zone* ParserTraits::zone() const { + return parser_->function_state_->scope()->zone(); } -ObjectLiteralProperty* ParserTraits::RewriteNonPatternObjectLiteralProperty( - ObjectLiteralProperty* property, const ExpressionClassifier* classifier, - bool* ok) { - return parser_->RewriteNonPatternObjectLiteralProperty(property, classifier, - ok); +ZoneList<Expression*>* ParserTraits::GetNonPatternList() const { + return parser_->function_state_->non_patterns_to_rewrite(); } -Expression* Parser::RewriteNonPattern(Expression* expr, - const ExpressionClassifier* classifier, - bool* ok) { - // For the time being, this does no rewriting at all. - ValidateExpression(classifier, ok); - return expr; -} +class NonPatternRewriter : public AstExpressionRewriter { + public: + NonPatternRewriter(uintptr_t stack_limit, Parser* parser) + : AstExpressionRewriter(stack_limit), parser_(parser) {} + ~NonPatternRewriter() override {} + private: + bool RewriteExpression(Expression* expr) override { + if (expr->IsRewritableExpression()) return true; + // Rewrite only what could have been a pattern but is not. + if (expr->IsArrayLiteral()) { + // Spread rewriting in array literals. + ArrayLiteral* lit = expr->AsArrayLiteral(); + VisitExpressions(lit->values()); + replacement_ = parser_->RewriteSpreads(lit); + return false; + } + if (expr->IsObjectLiteral()) { + return true; + } + if (expr->IsBinaryOperation() && + expr->AsBinaryOperation()->op() == Token::COMMA) { + return true; + } + // Everything else does not need rewriting. + return false; + } -ZoneList<Expression*>* Parser::RewriteNonPatternArguments( - ZoneList<Expression*>* args, const ExpressionClassifier* classifier, - bool* ok) { - // For the time being, this does no rewriting at all. - ValidateExpression(classifier, ok); - return args; -} + void VisitObjectLiteralProperty(ObjectLiteralProperty* property) override { + if (property == nullptr) return; + // Do not rewrite (computed) key expressions + AST_REWRITE_PROPERTY(Expression, property, value); + } + Parser* parser_; +}; -ObjectLiteralProperty* Parser::RewriteNonPatternObjectLiteralProperty( - ObjectLiteralProperty* property, const ExpressionClassifier* classifier, - bool* ok) { - if (property != nullptr) { - Expression* key = RewriteNonPattern(property->key(), classifier, ok); - property->set_key(key); - Expression* value = RewriteNonPattern(property->value(), classifier, ok); - property->set_value(value); + +void Parser::RewriteNonPattern(ExpressionClassifier* classifier, bool* ok) { + ValidateExpression(classifier, ok); + if (!*ok) return; + auto non_patterns_to_rewrite = function_state_->non_patterns_to_rewrite(); + int begin = classifier->GetNonPatternBegin(); + int end = non_patterns_to_rewrite->length(); + if (begin < end) { + NonPatternRewriter rewriter(stack_limit_, this); + for (int i = begin; i < end; i++) { + DCHECK(non_patterns_to_rewrite->at(i)->IsRewritableExpression()); + rewriter.Rewrite(non_patterns_to_rewrite->at(i)); + } + non_patterns_to_rewrite->Rewind(begin); } - return property; } void Parser::RewriteDestructuringAssignments() { - FunctionState* func = function_state_; if (!allow_harmony_destructuring_assignment()) return; - const List<DestructuringAssignment>& assignments = - func->destructuring_assignments_to_rewrite(); + const auto& assignments = + function_state_->destructuring_assignments_to_rewrite(); for (int i = assignments.length() - 1; i >= 0; --i) { // Rewrite list in reverse, so that nested assignment patterns are rewritten // correctly. - DestructuringAssignment pair = assignments.at(i); - RewritableAssignmentExpression* to_rewrite = - pair.assignment->AsRewritableAssignmentExpression(); - Scope* scope = pair.scope; + const DestructuringAssignment& pair = assignments.at(i); + RewritableExpression* to_rewrite = + pair.assignment->AsRewritableExpression(); DCHECK_NOT_NULL(to_rewrite); if (!to_rewrite->is_rewritten()) { - PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope); + PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, + pair.scope); } } } +Expression* Parser::RewriteSpreads(ArrayLiteral* lit) { + // Array literals containing spreads are rewritten using do expressions, e.g. + // [1, 2, 3, ...x, 4, ...y, 5] + // is roughly rewritten as: + // do { + // $R = [1, 2, 3]; + // for ($i of x) %AppendElement($R, $i); + // %AppendElement($R, 4); + // for ($j of y) %AppendElement($R, $j); + // %AppendElement($R, 5); + // $R + // } + // where $R, $i and $j are fresh temporary variables. + ZoneList<Expression*>::iterator s = lit->FirstSpread(); + if (s == lit->EndValue()) return nullptr; // no spread, no rewriting... + Variable* result = + scope_->NewTemporary(ast_value_factory()->dot_result_string()); + // NOTE: The value assigned to R is the whole original array literal, + // spreads included. This will be fixed before the rewritten AST is returned. + // $R = lit + Expression* init_result = + factory()->NewAssignment(Token::INIT, factory()->NewVariableProxy(result), + lit, RelocInfo::kNoPosition); + Block* do_block = + factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition); + do_block->statements()->Add( + factory()->NewExpressionStatement(init_result, RelocInfo::kNoPosition), + zone()); + // Traverse the array literal starting from the first spread. + while (s != lit->EndValue()) { + Expression* value = *s++; + Spread* spread = value->AsSpread(); + if (spread == nullptr) { + // If the element is not a spread, we're adding a single: + // %AppendElement($R, value) + ZoneList<Expression*>* append_element_args = NewExpressionList(2, zone()); + append_element_args->Add(factory()->NewVariableProxy(result), zone()); + append_element_args->Add(value, zone()); + do_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewCallRuntime(Runtime::kAppendElement, + append_element_args, + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + } else { + // If it's a spread, we're adding a for/of loop iterating through it. + Variable* each = + scope_->NewTemporary(ast_value_factory()->dot_for_string()); + Expression* subject = spread->expression(); + Variable* iterator = + scope_->NewTemporary(ast_value_factory()->dot_iterator_string()); + Variable* element = + scope_->NewTemporary(ast_value_factory()->dot_result_string()); + // iterator = subject[Symbol.iterator]() + Expression* assign_iterator = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(iterator), + GetIterator(subject, factory(), spread->expression_position()), + subject->position()); + // !%_IsJSReceiver(element = iterator.next()) && + // %ThrowIteratorResultNotAnObject(element) + Expression* next_element; + { + // element = iterator.next() + Expression* iterator_proxy = factory()->NewVariableProxy(iterator); + next_element = BuildIteratorNextResult(iterator_proxy, element, + spread->expression_position()); + } + // element.done + Expression* element_done; + { + Expression* done_literal = factory()->NewStringLiteral( + ast_value_factory()->done_string(), RelocInfo::kNoPosition); + Expression* element_proxy = factory()->NewVariableProxy(element); + element_done = factory()->NewProperty(element_proxy, done_literal, + RelocInfo::kNoPosition); + } + // each = element.value + Expression* assign_each; + { + Expression* value_literal = factory()->NewStringLiteral( + ast_value_factory()->value_string(), RelocInfo::kNoPosition); + Expression* element_proxy = factory()->NewVariableProxy(element); + Expression* element_value = factory()->NewProperty( + element_proxy, value_literal, RelocInfo::kNoPosition); + assign_each = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(each), element_value, + RelocInfo::kNoPosition); + } + // %AppendElement($R, each) + Statement* append_body; + { + ZoneList<Expression*>* append_element_args = + NewExpressionList(2, zone()); + append_element_args->Add(factory()->NewVariableProxy(result), zone()); + append_element_args->Add(factory()->NewVariableProxy(each), zone()); + append_body = factory()->NewExpressionStatement( + factory()->NewCallRuntime(Runtime::kAppendElement, + append_element_args, + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + } + // for (each of spread) %AppendElement($R, each) + ForEachStatement* loop = factory()->NewForEachStatement( + ForEachStatement::ITERATE, nullptr, RelocInfo::kNoPosition); + ForOfStatement* for_of = loop->AsForOfStatement(); + for_of->Initialize(factory()->NewVariableProxy(each), subject, + append_body, iterator, assign_iterator, next_element, + element_done, assign_each); + do_block->statements()->Add(for_of, zone()); + } + } + // Now, rewind the original array literal to truncate everything from the + // first spread (included) until the end. This fixes $R's initialization. + lit->RewindSpreads(); + return factory()->NewDoExpression(do_block, result, lit->position()); +} + + void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) { - DCHECK(expr->IsRewritableAssignmentExpression()); + DCHECK(expr->IsRewritableExpression()); parser_->function_state_->AddDestructuringAssignment( Parser::DestructuringAssignment(expr, parser_->scope_)); } +void ParserTraits::QueueNonPatternForRewriting(Expression* expr) { + DCHECK(expr->IsRewritableExpression()); + parser_->function_state_->AddNonPatternForRewriting(expr); +} + + void ParserTraits::SetFunctionNameFromPropertyName( ObjectLiteralProperty* property, const AstRawString* name) { Expression* value = property->value(); - if (!value->IsFunctionLiteral() && !value->IsClassLiteral()) return; - // TODO(adamk): Support computed names. + // Computed name setting must happen at runtime. if (property->is_computed_name()) return; + + // Getter and setter names are handled here because their names + // change in ES2015, even though they are not anonymous. + auto function = value->AsFunctionLiteral(); + if (function != nullptr) { + bool is_getter = property->kind() == ObjectLiteralProperty::GETTER; + bool is_setter = property->kind() == ObjectLiteralProperty::SETTER; + if (is_getter || is_setter) { + DCHECK_NOT_NULL(name); + const AstRawString* prefix = + is_getter ? parser_->ast_value_factory()->get_space_string() + : parser_->ast_value_factory()->set_space_string(); + function->set_raw_name( + parser_->ast_value_factory()->NewConsString(prefix, name)); + return; + } + } + + if (!value->IsAnonymousFunctionDefinition()) return; DCHECK_NOT_NULL(name); // Ignore "__proto__" as a name when it's being used to set the [[Prototype]] // of an object literal. if (property->kind() == ObjectLiteralProperty::PROTOTYPE) return; - if (value->IsFunctionLiteral()) { - auto function = value->AsFunctionLiteral(); - if (function->is_anonymous()) { - if (property->kind() == ObjectLiteralProperty::GETTER) { - function->set_raw_name(parser_->ast_value_factory()->NewConsString( - parser_->ast_value_factory()->get_space_string(), name)); - } else if (property->kind() == ObjectLiteralProperty::SETTER) { - function->set_raw_name(parser_->ast_value_factory()->NewConsString( - parser_->ast_value_factory()->set_space_string(), name)); - } else { - function->set_raw_name(name); - DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind()); - } - } + if (function != nullptr) { + function->set_raw_name(name); + DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind()); } else { DCHECK(value->IsClassLiteral()); DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind()); - auto class_literal = value->AsClassLiteral(); - if (class_literal->raw_name() == nullptr) { - class_literal->set_raw_name(name); - } + value->AsClassLiteral()->constructor()->set_raw_name(name); } } void ParserTraits::SetFunctionNameFromIdentifierRef(Expression* value, Expression* identifier) { - if (!value->IsFunctionLiteral() && !value->IsClassLiteral()) return; + if (!value->IsAnonymousFunctionDefinition()) return; if (!identifier->IsVariableProxy()) return; auto name = identifier->AsVariableProxy()->raw_name(); DCHECK_NOT_NULL(name); - if (value->IsFunctionLiteral()) { - auto function = value->AsFunctionLiteral(); - if (function->is_anonymous()) { - function->set_raw_name(name); - } + auto function = value->AsFunctionLiteral(); + if (function != nullptr) { + function->set_raw_name(name); } else { DCHECK(value->IsClassLiteral()); - auto class_literal = value->AsClassLiteral(); - if (class_literal->raw_name() == nullptr) { - class_literal->set_raw_name(name); + value->AsClassLiteral()->constructor()->set_raw_name(name); + } +} + + +// Desugaring of yield* +// ==================== +// +// With the help of do-expressions and function.sent, we desugar yield* into a +// loop containing a "raw" yield (a yield that doesn't wrap an iterator result +// object around its argument). Concretely, "yield* iterable" turns into +// roughly the following code: +// +// do { +// const kNext = 0; +// const kReturn = 1; +// const kThrow = 2; +// +// let input = function.sent; +// let mode = kNext; +// let output = undefined; +// +// let iterator = iterable[Symbol.iterator](); +// if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid); +// +// while (true) { +// // From the generator to the iterator: +// // Forward input according to resume mode and obtain output. +// switch (mode) { +// case kNext: +// output = iterator.next(input); +// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); +// break; +// case kReturn: +// IteratorClose(iterator, input, output); // See below. +// break; +// case kThrow: +// let iteratorThrow = iterator.throw; +// if (IS_NULL_OR_UNDEFINED(iteratorThrow)) { +// IteratorClose(iterator); // See below. +// throw MakeTypeError(kThrowMethodMissing); +// } +// output = %_Call(iteratorThrow, iterator, input); +// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); +// break; +// } +// if (output.done) break; +// +// // From the generator to its user: +// // Forward output, receive new input, and determine resume mode. +// mode = kReturn; +// try { +// try { +// RawYield(output); // See explanation above. +// mode = kNext; +// } catch (error) { +// mode = kThrow; +// } +// } finally { +// input = function.sent; +// continue; +// } +// } +// +// output.value; +// } +// +// IteratorClose(iterator) expands to the following: +// +// let iteratorReturn = iterator.return; +// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return; +// let output = %_Call(iteratorReturn, iterator); +// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); +// +// IteratorClose(iterator, input, output) expands to the following: +// +// let iteratorReturn = iterator.return; +// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return input; +// output = %_Call(iteratorReturn, iterator, input); +// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); + + +Expression* ParserTraits::RewriteYieldStar( + Expression* generator, Expression* iterable, int pos) { + + const int nopos = RelocInfo::kNoPosition; + + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + auto scope = parser_->scope_; + auto zone = parser_->zone(); + + + // Forward definition for break/continue statements. + WhileStatement* loop = factory->NewWhileStatement(nullptr, nopos); + + + // let input = undefined; + Variable* var_input = scope->NewTemporary(avfactory->empty_string()); + Statement* initialize_input; + { + Expression* input_proxy = factory->NewVariableProxy(var_input); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, input_proxy, factory->NewUndefinedLiteral(nopos), nopos); + initialize_input = factory->NewExpressionStatement(assignment, nopos); + } + + + // let mode = kNext; + Variable* var_mode = scope->NewTemporary(avfactory->empty_string()); + Statement* initialize_mode; + { + Expression* mode_proxy = factory->NewVariableProxy(var_mode); + Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos); + initialize_mode = factory->NewExpressionStatement(assignment, nopos); + } + + + // let output = undefined; + Variable* var_output = scope->NewTemporary(avfactory->empty_string()); + Statement* initialize_output; + { + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, output_proxy, factory->NewUndefinedLiteral(nopos), + nopos); + initialize_output = factory->NewExpressionStatement(assignment, nopos); + } + + + // let iterator = iterable[Symbol.iterator]; + Variable* var_iterator = scope->NewTemporary(avfactory->empty_string()); + Statement* get_iterator; + { + Expression* iterator = GetIterator(iterable, factory, nopos); + Expression* iterator_proxy = factory->NewVariableProxy(var_iterator); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, iterator_proxy, iterator, nopos); + get_iterator = factory->NewExpressionStatement(assignment, nopos); + } + + + // if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid); + Statement* validate_iterator; + { + Expression* is_receiver_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_iterator), zone); + is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + } + + Statement* throw_call; + { + Expression* call = NewThrowTypeError( + MessageTemplate::kSymbolIteratorInvalid, avfactory->empty_string(), + nopos); + throw_call = factory->NewExpressionStatement(call, nopos); + } + + validate_iterator = factory->NewIfStatement( + is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + + + // output = iterator.next(input); + Statement* call_next; + { + Expression* iterator_proxy = factory->NewVariableProxy(var_iterator); + Expression* literal = + factory->NewStringLiteral(avfactory->next_string(), nopos); + Expression* next_property = + factory->NewProperty(iterator_proxy, literal, nopos); + Expression* input_proxy = factory->NewVariableProxy(var_input); + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(input_proxy, zone); + Expression* call = factory->NewCall(next_property, args, nopos); + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, output_proxy, call, nopos); + call_next = factory->NewExpressionStatement(assignment, nopos); + } + + + // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); + Statement* validate_next_output; + { + Expression* is_receiver_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + } + + Statement* throw_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + Expression* call = factory->NewCallRuntime( + Runtime::kThrowIteratorResultNotAnObject, args, nopos); + throw_call = factory->NewExpressionStatement(call, nopos); + } + + validate_next_output = factory->NewIfStatement( + is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + + + // let iteratorThrow = iterator.throw; + Variable* var_throw = scope->NewTemporary(avfactory->empty_string()); + Statement* get_throw; + { + Expression* iterator_proxy = factory->NewVariableProxy(var_iterator); + Expression* literal = + factory->NewStringLiteral(avfactory->throw_string(), nopos); + Expression* property = + factory->NewProperty(iterator_proxy, literal, nopos); + Expression* throw_proxy = factory->NewVariableProxy(var_throw); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, throw_proxy, property, nopos); + get_throw = factory->NewExpressionStatement(assignment, nopos); + } + + + // if (IS_NULL_OR_UNDEFINED(iteratorThrow) { + // IteratorClose(iterator); + // throw MakeTypeError(kThrowMethodMissing); + // } + Statement* check_throw; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ, factory->NewVariableProxy(var_throw), + factory->NewNullLiteral(nopos), nopos); + + Expression* call = NewThrowTypeError( + MessageTemplate::kThrowMethodMissing, + avfactory->empty_string(), nopos); + Statement* throw_call = factory->NewExpressionStatement(call, nopos); + + Block* then = factory->NewBlock(nullptr, 4+1, false, nopos); + Variable* var_tmp = scope->NewTemporary(avfactory->empty_string()); + BuildIteratorClose( + then->statements(), var_iterator, factory->NewUndefinedLiteral(nopos), + var_tmp); + then->statements()->Add(throw_call, zone); + check_throw = factory->NewIfStatement( + condition, then, factory->NewEmptyStatement(nopos), nopos); + } + + + // output = %_Call(iteratorThrow, iterator, input); + Statement* call_throw; + { + auto args = new (zone) ZoneList<Expression*>(3, zone); + args->Add(factory->NewVariableProxy(var_throw), zone); + args->Add(factory->NewVariableProxy(var_iterator), zone); + args->Add(factory->NewVariableProxy(var_input), zone); + Expression* call = + factory->NewCallRuntime(Runtime::kInlineCall, args, nopos); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, factory->NewVariableProxy(var_output), call, nopos); + call_throw = factory->NewExpressionStatement(assignment, nopos); + } + + + // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); + Statement* validate_throw_output; + { + Expression* is_receiver_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + } + + Statement* throw_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + Expression* call = factory->NewCallRuntime( + Runtime::kThrowIteratorResultNotAnObject, args, nopos); + throw_call = factory->NewExpressionStatement(call, nopos); } + + validate_throw_output = factory->NewIfStatement( + is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + + + // if (output.done) break; + Statement* if_done; + { + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* literal = + factory->NewStringLiteral(avfactory->done_string(), nopos); + Expression* property = factory->NewProperty(output_proxy, literal, nopos); + BreakStatement* break_loop = factory->NewBreakStatement(loop, nopos); + if_done = factory->NewIfStatement( + property, break_loop, factory->NewEmptyStatement(nopos), nopos); + } + + + // mode = kReturn; + Statement* set_mode_return; + { + Expression* mode_proxy = factory->NewVariableProxy(var_mode); + Expression* kreturn = + factory->NewSmiLiteral(JSGeneratorObject::RETURN, nopos); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, mode_proxy, kreturn, nopos); + set_mode_return = factory->NewExpressionStatement(assignment, nopos); + } + + + // RawYield(output); + Statement* yield_output; + { + Expression* output_proxy = factory->NewVariableProxy(var_output); + Yield* yield = factory->NewYield( + generator, output_proxy, Yield::kInitial, nopos); + yield_output = factory->NewExpressionStatement(yield, nopos); + } + + + // mode = kNext; + Statement* set_mode_next; + { + Expression* mode_proxy = factory->NewVariableProxy(var_mode); + Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos); + set_mode_next = factory->NewExpressionStatement(assignment, nopos); + } + + + // mode = kThrow; + Statement* set_mode_throw; + { + Expression* mode_proxy = factory->NewVariableProxy(var_mode); + Expression* kthrow = + factory->NewSmiLiteral(JSGeneratorObject::THROW, nopos); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, mode_proxy, kthrow, nopos); + set_mode_throw = factory->NewExpressionStatement(assignment, nopos); + } + + + // input = function.sent; + Statement* get_input; + { + Expression* function_sent = FunctionSentExpression(scope, factory, nopos); + Expression* input_proxy = factory->NewVariableProxy(var_input); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, input_proxy, function_sent, nopos); + get_input = factory->NewExpressionStatement(assignment, nopos); + } + + + // output.value; + Statement* get_value; + { + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* literal = + factory->NewStringLiteral(avfactory->value_string(), nopos); + Expression* property = factory->NewProperty(output_proxy, literal, nopos); + get_value = factory->NewExpressionStatement(property, nopos); + } + + + // Now put things together. + + + // try { ... } catch(e) { ... } + Statement* try_catch; + { + Block* try_block = factory->NewBlock(nullptr, 2, false, nopos); + try_block->statements()->Add(yield_output, zone); + try_block->statements()->Add(set_mode_next, zone); + + Block* catch_block = factory->NewBlock(nullptr, 1, false, nopos); + catch_block->statements()->Add(set_mode_throw, zone); + + Scope* catch_scope = NewScope(scope, CATCH_SCOPE); + const AstRawString* name = avfactory->dot_catch_string(); + Variable* catch_variable = + catch_scope->DeclareLocal(name, VAR, kCreatedInitialized, + Variable::NORMAL); + + try_catch = factory->NewTryCatchStatement( + try_block, catch_scope, catch_variable, catch_block, nopos); } + + + // try { ... } finally { ... } + Statement* try_finally; + { + Block* try_block = factory->NewBlock(nullptr, 1, false, nopos); + try_block->statements()->Add(try_catch, zone); + + Block* finally = factory->NewBlock(nullptr, 2, false, nopos); + finally->statements()->Add(get_input, zone); + finally->statements()->Add( + factory->NewContinueStatement(loop, nopos), zone); + + try_finally = factory->NewTryFinallyStatement(try_block, finally, nopos); + } + + + // switch (mode) { ... } + SwitchStatement* switch_mode = factory->NewSwitchStatement(nullptr, nopos); + { + auto case_next = new (zone) ZoneList<Statement*>(3, zone); + case_next->Add(call_next, zone); + case_next->Add(validate_next_output, zone); + case_next->Add(factory->NewBreakStatement(switch_mode, nopos), zone); + + auto case_return = new (zone) ZoneList<Statement*>(5, zone); + BuildIteratorClose(case_return, var_iterator, + factory->NewVariableProxy(var_input, nopos), var_output); + case_return->Add(factory->NewBreakStatement(switch_mode, nopos), zone); + + auto case_throw = new (zone) ZoneList<Statement*>(5, zone); + case_throw->Add(get_throw, zone); + case_throw->Add(check_throw, zone); + case_throw->Add(call_throw, zone); + case_throw->Add(validate_throw_output, zone); + case_throw->Add(factory->NewBreakStatement(switch_mode, nopos), zone); + + auto cases = new (zone) ZoneList<CaseClause*>(3, zone); + Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos); + Expression* kreturn = + factory->NewSmiLiteral(JSGeneratorObject::RETURN, nopos); + Expression* kthrow = + factory->NewSmiLiteral(JSGeneratorObject::THROW, nopos); + cases->Add(factory->NewCaseClause(knext, case_next, nopos), zone); + cases->Add(factory->NewCaseClause(kreturn, case_return, nopos), zone); + cases->Add(factory->NewCaseClause(kthrow, case_throw, nopos), zone); + + switch_mode->Initialize(factory->NewVariableProxy(var_mode), cases); + } + + + // while (true) { ... } + // Already defined earlier: WhileStatement* loop = ... + { + Block* loop_body = factory->NewBlock(nullptr, 4, false, nopos); + loop_body->statements()->Add(switch_mode, zone); + loop_body->statements()->Add(if_done, zone); + loop_body->statements()->Add(set_mode_return, zone); + loop_body->statements()->Add(try_finally, zone); + + loop->Initialize(factory->NewBooleanLiteral(true, nopos), loop_body); + } + + + // do { ... } + DoExpression* yield_star; + { + // The rewriter needs to process the get_value statement only, hence we + // put the preceding statements into an init block. + + Block* do_block_ = factory->NewBlock(nullptr, 6, true, nopos); + do_block_->statements()->Add(initialize_input, zone); + do_block_->statements()->Add(initialize_mode, zone); + do_block_->statements()->Add(initialize_output, zone); + do_block_->statements()->Add(get_iterator, zone); + do_block_->statements()->Add(validate_iterator, zone); + do_block_->statements()->Add(loop, zone); + + Block* do_block = factory->NewBlock(nullptr, 2, false, nopos); + do_block->statements()->Add(do_block_, zone); + do_block->statements()->Add(get_value, zone); + + Variable* dot_result = scope->NewTemporary(avfactory->dot_result_string()); + yield_star = factory->NewDoExpression(do_block, dot_result, nopos); + Rewriter::Rewrite(parser_, yield_star, avfactory); + } + + return yield_star; +} + +// Desugaring of (lhs) instanceof (rhs) +// ==================================== +// +// We desugar instanceof into a load of property @@hasInstance on the rhs. +// We end up with roughly the following code (O, C): +// +// do { +// let O = lhs; +// let C = rhs; +// if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck); +// let handler_result = C[Symbol.hasInstance]; +// if (handler_result === undefined) { +// if (!IS_CALLABLE(C)) { +// throw MakeTypeError(kCalledNonCallableInstanceOf); +// } +// handler_result = %ordinary_has_instance(C, O); +// } else { +// handler_result = !!(%_Call(handler_result, C, O)); +// } +// handler_result; +// } +// +Expression* ParserTraits::RewriteInstanceof(Expression* lhs, Expression* rhs, + int pos) { + const int nopos = RelocInfo::kNoPosition; + + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + auto scope = parser_->scope_; + auto zone = parser_->zone(); + + // let O = lhs; + Variable* var_O = scope->NewTemporary(avfactory->empty_string()); + Statement* get_O; + { + Expression* O_proxy = factory->NewVariableProxy(var_O); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, O_proxy, lhs, nopos); + get_O = factory->NewExpressionStatement(assignment, nopos); + } + + // let C = lhs; + Variable* var_C = scope->NewTemporary(avfactory->empty_string()); + Statement* get_C; + { + Expression* C_proxy = factory->NewVariableProxy(var_C); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, C_proxy, rhs, nopos); + get_C = factory->NewExpressionStatement(assignment, nopos); + } + + // if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck); + Statement* validate_C; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_C), zone); + Expression* is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + Expression* call = + NewThrowTypeError(MessageTemplate::kNonObjectInInstanceOfCheck, + avfactory->empty_string(), nopos); + Statement* throw_call = factory->NewExpressionStatement(call, nopos); + + validate_C = + factory->NewIfStatement(is_receiver_call, + factory->NewEmptyStatement(nopos), + throw_call, + nopos); + } + + // let handler_result = C[Symbol.hasInstance]; + Variable* var_handler_result = scope->NewTemporary(avfactory->empty_string()); + Statement* initialize_handler; + { + Expression* hasInstance_symbol_literal = + factory->NewSymbolLiteral("hasInstance_symbol", RelocInfo::kNoPosition); + Expression* prop = factory->NewProperty(factory->NewVariableProxy(var_C), + hasInstance_symbol_literal, pos); + Expression* handler_proxy = factory->NewVariableProxy(var_handler_result); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, handler_proxy, prop, nopos); + initialize_handler = factory->NewExpressionStatement(assignment, nopos); + } + + // if (handler_result === undefined) { + // if (!IS_CALLABLE(C)) { + // throw MakeTypeError(kCalledNonCallableInstanceOf); + // } + // result = %ordinary_has_instance(C, O); + // } else { + // handler_result = !!%_Call(handler_result, C, O); + // } + Statement* call_handler; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ_STRICT, factory->NewVariableProxy(var_handler_result), + factory->NewUndefinedLiteral(nopos), nopos); + + Block* then_side = factory->NewBlock(nullptr, 2, false, nopos); + { + Expression* throw_expr = + NewThrowTypeError(MessageTemplate::kCalledNonCallableInstanceOf, + avfactory->empty_string(), nopos); + Statement* validate_C = CheckCallable(var_C, throw_expr); + ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(2, zone); + args->Add(factory->NewVariableProxy(var_C), zone); + args->Add(factory->NewVariableProxy(var_O), zone); + CallRuntime* call = factory->NewCallRuntime( + Context::ORDINARY_HAS_INSTANCE_INDEX, args, pos); + Expression* result_proxy = factory->NewVariableProxy(var_handler_result); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, result_proxy, call, nopos); + Statement* assignment_return = + factory->NewExpressionStatement(assignment, nopos); + + then_side->statements()->Add(validate_C, zone); + then_side->statements()->Add(assignment_return, zone); + } + + Statement* else_side; + { + auto args = new (zone) ZoneList<Expression*>(3, zone); + args->Add(factory->NewVariableProxy(var_handler_result), zone); + args->Add(factory->NewVariableProxy(var_C), zone); + args->Add(factory->NewVariableProxy(var_O), zone); + Expression* call = + factory->NewCallRuntime(Runtime::kInlineCall, args, nopos); + Expression* inner_not = + factory->NewUnaryOperation(Token::NOT, call, nopos); + Expression* outer_not = + factory->NewUnaryOperation(Token::NOT, inner_not, nopos); + Expression* result_proxy = factory->NewVariableProxy(var_handler_result); + Expression* assignment = + factory->NewAssignment(Token::ASSIGN, result_proxy, outer_not, nopos); + + else_side = factory->NewExpressionStatement(assignment, nopos); + } + call_handler = + factory->NewIfStatement(condition, then_side, else_side, nopos); + } + + // do { ... } + DoExpression* instanceof; + { + Block* block = factory->NewBlock(nullptr, 5, true, nopos); + block->statements()->Add(get_O, zone); + block->statements()->Add(get_C, zone); + block->statements()->Add(validate_C, zone); + block->statements()->Add(initialize_handler, zone); + block->statements()->Add(call_handler, zone); + + // Here is the desugared instanceof. + instanceof = factory->NewDoExpression(block, var_handler_result, nopos); + Rewriter::Rewrite(parser_, instanceof, avfactory); + } + + return instanceof; +} + +Statement* ParserTraits::CheckCallable(Variable* var, Expression* error) { + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + const int nopos = RelocInfo::kNoPosition; + Statement* validate_var; + { + Expression* type_of = factory->NewUnaryOperation( + Token::TYPEOF, factory->NewVariableProxy(var), nopos); + Expression* function_literal = + factory->NewStringLiteral(avfactory->function_string(), nopos); + Expression* condition = factory->NewCompareOperation( + Token::EQ_STRICT, type_of, function_literal, nopos); + + Statement* throw_call = factory->NewExpressionStatement(error, nopos); + + validate_var = factory->NewIfStatement( + condition, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + return validate_var; +} + +void ParserTraits::BuildIteratorClose(ZoneList<Statement*>* statements, + Variable* iterator, + Expression* input, + Variable* var_output) { + // + // This function adds four statements to [statements], corresponding to the + // following code: + // + // let iteratorReturn = iterator.return; + // if (IS_NULL_OR_UNDEFINED(iteratorReturn) return input; + // output = %_Call(iteratorReturn, iterator); + // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); + // + + const int nopos = RelocInfo::kNoPosition; + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + auto zone = parser_->zone(); + + // let iteratorReturn = iterator.return; + Variable* var_return = var_output; // Reusing the output variable. + Statement* get_return; + { + Expression* iterator_proxy = factory->NewVariableProxy(iterator); + Expression* literal = + factory->NewStringLiteral(avfactory->return_string(), nopos); + Expression* property = + factory->NewProperty(iterator_proxy, literal, nopos); + Expression* return_proxy = factory->NewVariableProxy(var_return); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, return_proxy, property, nopos); + get_return = factory->NewExpressionStatement(assignment, nopos); + } + + // if (IS_NULL_OR_UNDEFINED(iteratorReturn) return input; + Statement* check_return; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ, factory->NewVariableProxy(var_return), + factory->NewNullLiteral(nopos), nopos); + + Statement* return_input = factory->NewReturnStatement(input, nopos); + + check_return = factory->NewIfStatement( + condition, return_input, factory->NewEmptyStatement(nopos), nopos); + } + + // output = %_Call(iteratorReturn, iterator); + Statement* call_return; + { + auto args = new (zone) ZoneList<Expression*>(3, zone); + args->Add(factory->NewVariableProxy(var_return), zone); + args->Add(factory->NewVariableProxy(iterator), zone); + + Expression* call = + factory->NewCallRuntime(Runtime::kInlineCall, args, nopos); + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, output_proxy, call, nopos); + call_return = factory->NewExpressionStatement(assignment, nopos); + } + + // if (!IS_RECEIVER(output)) %ThrowIteratorResultNotAnObject(output); + Statement* validate_output; + { + Expression* is_receiver_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + } + + Statement* throw_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + Expression* call = factory->NewCallRuntime( + Runtime::kThrowIteratorResultNotAnObject, args, nopos); + throw_call = factory->NewExpressionStatement(call, nopos); + } + + validate_output = factory->NewIfStatement( + is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + + statements->Add(get_return, zone); + statements->Add(check_return, zone); + statements->Add(call_return, zone); + statements->Add(validate_output, zone); +} + + +// Runtime encoding of different completion modes. +enum ForOfLoopBodyCompletion { BODY_COMPLETED, BODY_ABORTED, BODY_THREW }; + +void ParserTraits::BuildIteratorCloseForCompletion( + ZoneList<Statement*>* statements, Variable* iterator, + Variable* completion) { + // + // This function adds two statements to [statements], corresponding to the + // following code: + // + // let iteratorReturn = iterator.return; + // if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) { + // let output; + // if (completion === BODY_THREW) { + // if (!IS_CALLABLE(iteratorReturn)) { + // throw MakeTypeError(kReturnMethodNotCallable); + // } + // try { output = %_Call(iteratorReturn, iterator) } catch (_) { } + // } else { + // output = %_Call(iteratorReturn, iterator); + // } + // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); + // } + // + + const int nopos = RelocInfo::kNoPosition; + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + auto scope = parser_->scope_; + auto zone = parser_->zone(); + + // let output; + Variable* var_output = scope->NewTemporary(avfactory->empty_string()); + + // let iteratorReturn = iterator.return; + Variable* var_return = var_output; // Reusing the output variable. + Statement* get_return; + { + Expression* iterator_proxy = factory->NewVariableProxy(iterator); + Expression* literal = + factory->NewStringLiteral(avfactory->return_string(), nopos); + Expression* property = + factory->NewProperty(iterator_proxy, literal, nopos); + Expression* return_proxy = factory->NewVariableProxy(var_return); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, return_proxy, property, nopos); + get_return = factory->NewExpressionStatement(assignment, nopos); + } + + // if (!IS_CALLABLE(iteratorReturn)) { + // throw MakeTypeError(kReturnMethodNotCallable); + // } + Statement* check_return_callable; + { + Expression* throw_expr = NewThrowTypeError( + MessageTemplate::kReturnMethodNotCallable, + avfactory->empty_string(), nopos); + check_return_callable = CheckCallable(var_return, throw_expr); + } + + // output = %_Call(iteratorReturn, iterator); + Statement* call_return; + { + auto args = new (zone) ZoneList<Expression*>(2, zone); + args->Add(factory->NewVariableProxy(var_return), zone); + args->Add(factory->NewVariableProxy(iterator), zone); + Expression* call = + factory->NewCallRuntime(Runtime::kInlineCall, args, nopos); + + Expression* output_proxy = factory->NewVariableProxy(var_output); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, output_proxy, call, nopos); + call_return = factory->NewExpressionStatement(assignment, nopos); + } + + // try { output = %_Call(iteratorReturn, iterator) } catch (_) { } + Statement* try_call_return; + { + auto args = new (zone) ZoneList<Expression*>(2, zone); + args->Add(factory->NewVariableProxy(var_return), zone); + args->Add(factory->NewVariableProxy(iterator), zone); + + Expression* call = + factory->NewCallRuntime(Runtime::kInlineCall, args, nopos); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, factory->NewVariableProxy(var_output), call, nopos); + + Block* try_block = factory->NewBlock(nullptr, 1, false, nopos); + try_block->statements()->Add( + factory->NewExpressionStatement(assignment, nopos), zone); + + Block* catch_block = factory->NewBlock(nullptr, 0, false, nopos); + + Scope* catch_scope = NewScope(scope, CATCH_SCOPE); + Variable* catch_variable = catch_scope->DeclareLocal( + avfactory->dot_catch_string(), VAR, kCreatedInitialized, + Variable::NORMAL); + + try_call_return = factory->NewTryCatchStatement( + try_block, catch_scope, catch_variable, catch_block, nopos); + } + + // if (completion === ABRUPT_THROW) { + // #check_return_callable; + // #try_call_return; + // } else { + // #call_return; + // } + Statement* call_return_carefully; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ_STRICT, factory->NewVariableProxy(completion), + factory->NewSmiLiteral(BODY_THREW, nopos), nopos); + + Block* then_block = factory->NewBlock(nullptr, 2, false, nopos); + then_block->statements()->Add(check_return_callable, zone); + then_block->statements()->Add(try_call_return, zone); + + call_return_carefully = + factory->NewIfStatement(condition, then_block, call_return, nopos); + } + + // if (!IS_RECEIVER(output)) %ThrowIteratorResultNotAnObject(output); + Statement* validate_output; + { + Expression* is_receiver_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + is_receiver_call = + factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); + } + + Statement* throw_call; + { + auto args = new (zone) ZoneList<Expression*>(1, zone); + args->Add(factory->NewVariableProxy(var_output), zone); + Expression* call = factory->NewCallRuntime( + Runtime::kThrowIteratorResultNotAnObject, args, nopos); + throw_call = factory->NewExpressionStatement(call, nopos); + } + + validate_output = factory->NewIfStatement( + is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos); + } + + // if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) { ... } + Statement* maybe_call_return; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ, factory->NewVariableProxy(var_return), + factory->NewNullLiteral(nopos), nopos); + + Block* block = factory->NewBlock(nullptr, 2, false, nopos); + block->statements()->Add(call_return_carefully, zone); + block->statements()->Add(validate_output, zone); + + maybe_call_return = factory->NewIfStatement( + condition, factory->NewEmptyStatement(nopos), block, nopos); + } + + + statements->Add(get_return, zone); + statements->Add(maybe_call_return, zone); +} + + +Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) { + if (!FLAG_harmony_iterator_close) return loop; + + // + // This function replaces the loop with the following wrapping: + // + // let completion = BODY_COMPLETED; + // try { + // #loop; + // } catch(e) { + // if (completion === BODY_ABORTED) completion = BODY_THREW; + // throw e; + // } finally { + // if (!(completion === BODY_COMPLETED || IS_UNDEFINED(#iterator))) { + // #BuildIteratorClose(#iterator, completion) // See above. + // } + // } + // + // where the loop's body is wrapped as follows: + // + // { + // {{completion = BODY_ABORTED;}} + // #loop-body + // {{completion = BODY_COMPLETED;}} + // } + + const int nopos = RelocInfo::kNoPosition; + auto factory = parser_->factory(); + auto avfactory = parser_->ast_value_factory(); + auto scope = parser_->scope_; + auto zone = parser_->zone(); + + // let completion = BODY_COMPLETED; + Variable* var_completion = scope->NewTemporary(avfactory->empty_string()); + Statement* initialize_completion; + { + Expression* proxy = factory->NewVariableProxy(var_completion); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, proxy, + factory->NewSmiLiteral(BODY_COMPLETED, nopos), nopos); + initialize_completion = + factory->NewExpressionStatement(assignment, nopos); + } + + // if (completion === BODY_ABORTED) completion = BODY_THREW; + Statement* set_completion_throw; + { + Expression* condition = factory->NewCompareOperation( + Token::EQ_STRICT, factory->NewVariableProxy(var_completion), + factory->NewSmiLiteral(BODY_ABORTED, nopos), nopos); + + Expression* proxy = factory->NewVariableProxy(var_completion); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, proxy, factory->NewSmiLiteral(BODY_THREW, nopos), + nopos); + Statement* statement = factory->NewExpressionStatement(assignment, nopos); + set_completion_throw = factory->NewIfStatement( + condition, statement, factory->NewEmptyStatement(nopos), nopos); + } + + // if (!(completion === BODY_COMPLETED || IS_UNDEFINED(#iterator))) { + // #BuildIteratorClose(#iterator, completion) + // } + Block* maybe_close; + { + Expression* condition1 = factory->NewCompareOperation( + Token::EQ_STRICT, factory->NewVariableProxy(var_completion), + factory->NewSmiLiteral(BODY_COMPLETED, nopos), nopos); + Expression* condition2 = factory->NewCompareOperation( + Token::EQ_STRICT, factory->NewVariableProxy(loop->iterator()), + factory->NewUndefinedLiteral(nopos), nopos); + Expression* condition = factory->NewBinaryOperation( + Token::OR, condition1, condition2, nopos); + + Block* block = factory->NewBlock(nullptr, 2, false, nopos); + BuildIteratorCloseForCompletion( + block->statements(), loop->iterator(), var_completion); + DCHECK(block->statements()->length() == 2); + + maybe_close = factory->NewBlock(nullptr, 1, false, nopos); + maybe_close->statements()->Add(factory->NewIfStatement( + condition, factory->NewEmptyStatement(nopos), block, nopos), zone); + } + + // try { #try_block } + // catch(e) { + // #set_completion_throw; + // throw e; + // } + Statement* try_catch; + { + Scope* catch_scope = NewScope(scope, CATCH_SCOPE); + Variable* catch_variable = catch_scope->DeclareLocal( + avfactory->dot_catch_string(), VAR, kCreatedInitialized, + Variable::NORMAL); + + Statement* rethrow; + { + Expression* proxy = factory->NewVariableProxy(catch_variable); + rethrow = factory->NewExpressionStatement( + factory->NewThrow(proxy, nopos), nopos); + } + + Block* try_block = factory->NewBlock(nullptr, 1, false, nopos); + try_block->statements()->Add(loop, zone); + + Block* catch_block = factory->NewBlock(nullptr, 2, false, nopos); + catch_block->statements()->Add(set_completion_throw, zone); + catch_block->statements()->Add(rethrow, zone); + + try_catch = factory->NewTryCatchStatement( + try_block, catch_scope, catch_variable, catch_block, nopos); + } + + // try { #try_catch } finally { #maybe_close } + Statement* try_finally; + { + Block* try_block = factory->NewBlock(nullptr, 1, false, nopos); + try_block->statements()->Add(try_catch, zone); + + try_finally = + factory->NewTryFinallyStatement(try_block, maybe_close, nopos); + } + + // #initialize_completion; + // #try_finally; + Statement* final_loop; + { + Block* block = factory->NewBlock(nullptr, 2, false, nopos); + block->statements()->Add(initialize_completion, zone); + block->statements()->Add(try_finally, zone); + final_loop = block; + } + + // {{completion = BODY_ABORTED;}} + Statement* set_completion_break; + { + Expression* proxy = factory->NewVariableProxy(var_completion); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, proxy, + factory->NewSmiLiteral(BODY_ABORTED, nopos), nopos); + + Block* block = factory->NewBlock(nullptr, 1, true, nopos); + block->statements()->Add( + factory->NewExpressionStatement(assignment, nopos), zone); + set_completion_break = block; + } + + // {{completion = BODY_COMPLETED;}} + Statement* set_completion_normal; + { + Expression* proxy = factory->NewVariableProxy(var_completion); + Expression* assignment = factory->NewAssignment( + Token::ASSIGN, proxy, factory->NewSmiLiteral(BODY_COMPLETED, nopos), + nopos); + + Block* block = factory->NewBlock(nullptr, 1, true, nopos); + block->statements()->Add( + factory->NewExpressionStatement(assignment, nopos), zone); + set_completion_normal = block; + } + + // { #set_completion_break; #loop-body; #set_completion_normal } + Block* new_body = factory->NewBlock(nullptr, 2, false, nopos); + new_body->statements()->Add(set_completion_break, zone); + new_body->statements()->Add(loop->body(), zone); + new_body->statements()->Add(set_completion_normal, zone); + + loop->set_body(new_body); + return final_loop; } |