| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/ast/ast.h" |
| #include "src/messages.h" |
| #include "src/parsing/parameter-initializer-rewriter.h" |
| #include "src/parsing/parser.h" |
| |
| namespace v8 { |
| |
| namespace internal { |
| |
| void Parser::PatternRewriter::DeclareAndInitializeVariables( |
| Block* block, const DeclarationDescriptor* declaration_descriptor, |
| const DeclarationParsingResult::Declaration* declaration, |
| ZoneList<const AstRawString*>* names, bool* ok) { |
| PatternRewriter rewriter; |
| |
| DCHECK(block->ignore_completion_value()); |
| |
| rewriter.scope_ = declaration_descriptor->scope; |
| rewriter.parser_ = declaration_descriptor->parser; |
| rewriter.context_ = BINDING; |
| rewriter.pattern_ = declaration->pattern; |
| rewriter.initializer_position_ = declaration->initializer_position; |
| rewriter.block_ = block; |
| rewriter.descriptor_ = declaration_descriptor; |
| rewriter.names_ = names; |
| rewriter.ok_ = ok; |
| rewriter.recursion_level_ = 0; |
| |
| rewriter.RecurseIntoSubpattern(rewriter.pattern_, declaration->initializer); |
| } |
| |
| |
| void Parser::PatternRewriter::RewriteDestructuringAssignment( |
| Parser* parser, RewritableExpression* to_rewrite, Scope* scope) { |
| PatternRewriter rewriter; |
| |
| DCHECK(!to_rewrite->is_rewritten()); |
| |
| bool ok = true; |
| rewriter.scope_ = scope; |
| rewriter.parser_ = parser; |
| rewriter.context_ = ASSIGNMENT; |
| rewriter.pattern_ = to_rewrite; |
| rewriter.block_ = nullptr; |
| rewriter.descriptor_ = nullptr; |
| rewriter.names_ = nullptr; |
| rewriter.ok_ = &ok; |
| rewriter.recursion_level_ = 0; |
| |
| rewriter.RecurseIntoSubpattern(rewriter.pattern_, nullptr); |
| DCHECK(ok); |
| } |
| |
| |
| Expression* Parser::PatternRewriter::RewriteDestructuringAssignment( |
| Parser* parser, Assignment* assignment, Scope* scope) { |
| DCHECK_NOT_NULL(assignment); |
| DCHECK_EQ(Token::ASSIGN, assignment->op()); |
| auto to_rewrite = parser->factory()->NewRewritableExpression(assignment); |
| RewriteDestructuringAssignment(parser, to_rewrite, scope); |
| return to_rewrite->expression(); |
| } |
| |
| |
| bool Parser::PatternRewriter::IsAssignmentContext(PatternContext c) const { |
| return c == ASSIGNMENT || c == ASSIGNMENT_INITIALIZER; |
| } |
| |
| |
| bool Parser::PatternRewriter::IsBindingContext(PatternContext c) const { |
| return c == BINDING || c == INITIALIZER; |
| } |
| |
| |
| Parser::PatternRewriter::PatternContext |
| Parser::PatternRewriter::SetAssignmentContextIfNeeded(Expression* node) { |
| PatternContext old_context = context(); |
| // AssignmentExpressions may occur in the Initializer position of a |
| // SingleNameBinding. Such expressions should not prompt a change in the |
| // pattern's context. |
| if (node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN && |
| !IsInitializerContext()) { |
| set_context(ASSIGNMENT); |
| } |
| return old_context; |
| } |
| |
| |
| Parser::PatternRewriter::PatternContext |
| Parser::PatternRewriter::SetInitializerContextIfNeeded(Expression* node) { |
| // Set appropriate initializer context for BindingElement and |
| // AssignmentElement nodes |
| PatternContext old_context = context(); |
| bool is_destructuring_assignment = |
| node->IsRewritableExpression() && |
| !node->AsRewritableExpression()->is_rewritten(); |
| bool is_assignment = |
| node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN; |
| if (is_destructuring_assignment || is_assignment) { |
| switch (old_context) { |
| case BINDING: |
| set_context(INITIALIZER); |
| break; |
| case ASSIGNMENT: |
| set_context(ASSIGNMENT_INITIALIZER); |
| break; |
| default: |
| break; |
| } |
| } |
| return old_context; |
| } |
| |
| |
| void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { |
| Expression* value = current_value_; |
| |
| if (IsAssignmentContext()) { |
| // In an assignment context, simply perform the assignment |
| Assignment* assignment = factory()->NewAssignment( |
| Token::ASSIGN, pattern, value, pattern->position()); |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, pattern->position()), |
| zone()); |
| return; |
| } |
| |
| descriptor_->scope->RemoveUnresolved(pattern); |
| |
| // Declare variable. |
| // Note that we *always* must treat the initial value via a separate init |
| // assignment for variables and constants because the value must be assigned |
| // when the variable is encountered in the source. But the variable/constant |
| // is declared (and set to 'undefined') upon entering the function within |
| // which the variable or constant is declared. Only function variables have |
| // an initial value in the declaration (because they are initialized upon |
| // entering the function). |
| // |
| // If we have a legacy const declaration, in an inner scope, the proxy |
| // is always bound to the declared variable (independent of possibly |
| // surrounding 'with' statements). |
| // For let/const declarations in harmony mode, we can also immediately |
| // pre-resolve the proxy because it resides in the same scope as the |
| // declaration. |
| const AstRawString* name = pattern->raw_name(); |
| VariableProxy* proxy = parser_->NewUnresolved(name, descriptor_->mode); |
| Declaration* declaration = factory()->NewVariableDeclaration( |
| proxy, descriptor_->mode, descriptor_->scope, |
| descriptor_->declaration_pos); |
| Variable* var = |
| parser_->Declare(declaration, descriptor_->declaration_kind, |
| descriptor_->mode != VAR, ok_, descriptor_->hoist_scope); |
| if (!*ok_) return; |
| DCHECK_NOT_NULL(var); |
| DCHECK(!proxy->is_resolved() || proxy->var() == var); |
| var->set_initializer_position(initializer_position_); |
| |
| DCHECK(initializer_position_ != RelocInfo::kNoPosition); |
| |
| Scope* declaration_scope = IsLexicalVariableMode(descriptor_->mode) |
| ? descriptor_->scope |
| : descriptor_->scope->DeclarationScope(); |
| if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { |
| parser_->ReportMessage(MessageTemplate::kTooManyVariables); |
| *ok_ = false; |
| return; |
| } |
| if (names_) { |
| names_->Add(name, zone()); |
| } |
| |
| // Initialize variables if needed. A |
| // declaration of the form: |
| // |
| // var v = x; |
| // |
| // is syntactic sugar for: |
| // |
| // var v; v = x; |
| // |
| // In particular, we need to re-lookup 'v' (in scope_, not |
| // declaration_scope) as it may be a different 'v' than the 'v' in the |
| // declaration (e.g., if we are inside a 'with' statement or 'catch' |
| // block). |
| // |
| // However, note that const declarations are different! A const |
| // declaration of the form: |
| // |
| // const c = x; |
| // |
| // is *not* syntactic sugar for: |
| // |
| // const c; c = x; |
| // |
| // The "variable" c initialized to x is the same as the declared |
| // one - there is no re-lookup (see the last parameter of the |
| // Declare() call above). |
| Scope* initialization_scope = IsImmutableVariableMode(descriptor_->mode) |
| ? declaration_scope |
| : descriptor_->scope; |
| |
| |
| // Global variable declarations must be compiled in a specific |
| // way. When the script containing the global variable declaration |
| // is entered, the global variable must be declared, so that if it |
| // doesn't exist (on the global object itself, see ES5 errata) it |
| // gets created with an initial undefined value. This is handled |
| // by the declarations part of the function representing the |
| // top-level global code; see Runtime::DeclareGlobalVariable. If |
| // it already exists (in the object or in a prototype), it is |
| // *not* touched until the variable declaration statement is |
| // executed. |
| // |
| // Executing the variable declaration statement will always |
| // guarantee to give the global object an own property. |
| // This way, global variable declarations can shadow |
| // properties in the prototype chain, but only after the variable |
| // declaration statement has been executed. This is important in |
| // browsers where the global object (window) has lots of |
| // properties defined in prototype objects. |
| if (initialization_scope->is_script_scope() && |
| !IsLexicalVariableMode(descriptor_->mode)) { |
| // Compute the arguments for the runtime |
| // call.test-parsing/InitializedDeclarationsInStrictForOfError |
| ZoneList<Expression*>* arguments = |
| new (zone()) ZoneList<Expression*>(3, zone()); |
| // We have at least 1 parameter. |
| arguments->Add( |
| factory()->NewStringLiteral(name, descriptor_->declaration_pos), |
| zone()); |
| CallRuntime* initialize; |
| |
| if (IsImmutableVariableMode(descriptor_->mode)) { |
| arguments->Add(value, zone()); |
| // Construct the call to Runtime_InitializeConstGlobal |
| // and add it to the initialization statement block. |
| // Note that the function does different things depending on |
| // the number of arguments (1 or 2). |
| initialize = factory()->NewCallRuntime(Runtime::kInitializeConstGlobal, |
| arguments, value->position()); |
| value = NULL; // zap the value to avoid the unnecessary assignment |
| } else { |
| // Add language mode. |
| // We may want to pass singleton to avoid Literal allocations. |
| LanguageMode language_mode = initialization_scope->language_mode(); |
| arguments->Add( |
| factory()->NewNumberLiteral(language_mode, RelocInfo::kNoPosition), |
| zone()); |
| |
| // Be careful not to assign a value to the global variable if |
| // we're in a with. The initialization value should not |
| // necessarily be stored in the global object in that case, |
| // which is why we need to generate a separate assignment node. |
| if (value != NULL && !descriptor_->scope->inside_with()) { |
| arguments->Add(value, zone()); |
| // Construct the call to Runtime_InitializeVarGlobal |
| // and add it to the initialization statement block. |
| initialize = factory()->NewCallRuntime(Runtime::kInitializeVarGlobal, |
| arguments, value->position()); |
| value = NULL; // zap the value to avoid the unnecessary assignment |
| } else { |
| initialize = NULL; |
| } |
| } |
| |
| if (initialize != NULL) { |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(initialize, initialize->position()), |
| zone()); |
| } |
| } else if (value != nullptr && IsLexicalVariableMode(descriptor_->mode)) { |
| // For 'let' and 'const' declared variables the initialization always |
| // assigns to the declared variable. |
| DCHECK_NOT_NULL(proxy); |
| DCHECK_NOT_NULL(proxy->var()); |
| DCHECK_NOT_NULL(value); |
| // Add break location for destructured sub-pattern. |
| int pos = IsSubPattern() ? pattern->position() : value->position(); |
| Assignment* assignment = |
| factory()->NewAssignment(Token::INIT, proxy, value, pos); |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, pos), zone()); |
| value = NULL; |
| } |
| |
| // Add an assignment node to the initialization statement block if we still |
| // have a pending initialization value. |
| if (value != NULL) { |
| DCHECK(descriptor_->mode == VAR); |
| // 'var' initializations are simply assignments (with all the consequences |
| // if they are inside a 'with' statement - they may change a 'with' object |
| // property). |
| VariableProxy* proxy = initialization_scope->NewUnresolved(factory(), name); |
| // Add break location for destructured sub-pattern. |
| int pos = IsSubPattern() ? pattern->position() : value->position(); |
| Assignment* assignment = |
| factory()->NewAssignment(Token::INIT, proxy, value, pos); |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, pos), zone()); |
| } |
| } |
| |
| |
| Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) { |
| auto temp = scope()->NewTemporary(ast_value_factory()->empty_string()); |
| if (value != nullptr) { |
| auto assignment = factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(temp), value, |
| RelocInfo::kNoPosition); |
| |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), |
| zone()); |
| } |
| return temp; |
| } |
| |
| |
| void Parser::PatternRewriter::VisitRewritableExpression( |
| RewritableExpression* node) { |
| // If this is not a destructuring assignment... |
| if (!IsAssignmentContext() || !node->expression()->IsAssignment()) { |
| // Mark the node as rewritten to prevent redundant rewriting, and |
| // perform BindingPattern rewriting |
| DCHECK(!node->is_rewritten()); |
| node->Rewrite(node->expression()); |
| return node->expression()->Accept(this); |
| } |
| |
| if (node->is_rewritten()) return; |
| DCHECK(IsAssignmentContext()); |
| Assignment* assign = node->expression()->AsAssignment(); |
| DCHECK_NOT_NULL(assign); |
| DCHECK_EQ(Token::ASSIGN, assign->op()); |
| |
| auto initializer = assign->value(); |
| auto value = initializer; |
| |
| if (IsInitializerContext()) { |
| // let {<pattern> = <init>} = <value> |
| // becomes |
| // temp = <value>; |
| // <pattern> = temp === undefined ? <init> : temp; |
| auto temp_var = CreateTempVar(current_value_); |
| Expression* is_undefined = factory()->NewCompareOperation( |
| Token::EQ_STRICT, factory()->NewVariableProxy(temp_var), |
| factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| value = factory()->NewConditional(is_undefined, initializer, |
| factory()->NewVariableProxy(temp_var), |
| RelocInfo::kNoPosition); |
| } |
| |
| PatternContext old_context = SetAssignmentContextIfNeeded(initializer); |
| int pos = assign->position(); |
| Block* old_block = block_; |
| block_ = factory()->NewBlock(nullptr, 8, true, pos); |
| Variable* temp = nullptr; |
| Expression* pattern = assign->target(); |
| Expression* old_value = current_value_; |
| current_value_ = value; |
| if (pattern->IsObjectLiteral()) { |
| VisitObjectLiteral(pattern->AsObjectLiteral(), &temp); |
| } else { |
| DCHECK(pattern->IsArrayLiteral()); |
| VisitArrayLiteral(pattern->AsArrayLiteral(), &temp); |
| } |
| DCHECK_NOT_NULL(temp); |
| current_value_ = old_value; |
| Expression* expr = factory()->NewDoExpression(block_, temp, pos); |
| node->Rewrite(expr); |
| block_ = old_block; |
| if (block_) { |
| block_->statements()->Add(factory()->NewExpressionStatement(expr, pos), |
| zone()); |
| } |
| return set_context(old_context); |
| } |
| |
| // Two cases for scope rewriting the scope of default parameters: |
| // - Eagerly parsed arrow functions are initially parsed as having |
| // expressions in the enclosing scope, but when the arrow is encountered, |
| // need to be in the scope of the function. |
| // - When an extra declaration scope needs to be inserted to account for |
| // a sloppy eval in a default parameter or function body, the expressions |
| // needs to be in that new inner scope which was added after initial |
| // parsing. |
| // Each of these cases can be handled by rewriting the contents of the |
| // expression to the current scope. The source scope is typically the outer |
| // scope when one case occurs; when both cases occur, both scopes need to |
| // be included as the outer scope. (Both rewritings still need to be done |
| // to account for lazily parsed arrow functions which hit the second case.) |
| // TODO(littledan): Remove the outer_scope parameter of |
| // RewriteParameterInitializerScope |
| void Parser::PatternRewriter::RewriteParameterScopes(Expression* expr) { |
| if (!IsBindingContext()) return; |
| if (descriptor_->declaration_kind != DeclarationDescriptor::PARAMETER) return; |
| if (!scope()->is_arrow_scope() && !scope()->is_block_scope()) return; |
| |
| // Either this scope is an arrow scope or a declaration block scope. |
| DCHECK(scope()->is_declaration_scope()); |
| |
| if (scope()->outer_scope()->is_arrow_scope() && scope()->is_block_scope()) { |
| RewriteParameterInitializerScope(parser_->stack_limit(), expr, |
| scope()->outer_scope()->outer_scope(), |
| scope()); |
| } |
| RewriteParameterInitializerScope(parser_->stack_limit(), expr, |
| scope()->outer_scope(), scope()); |
| } |
| |
| void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern, |
| Variable** temp_var) { |
| auto temp = *temp_var = CreateTempVar(current_value_); |
| |
| block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone()); |
| |
| for (ObjectLiteralProperty* property : *pattern->properties()) { |
| PatternContext context = SetInitializerContextIfNeeded(property->value()); |
| |
| // Computed property names contain expressions which might require |
| // scope rewriting. |
| if (!property->key()->IsLiteral()) RewriteParameterScopes(property->key()); |
| |
| RecurseIntoSubpattern( |
| property->value(), |
| factory()->NewProperty(factory()->NewVariableProxy(temp), |
| property->key(), RelocInfo::kNoPosition)); |
| set_context(context); |
| } |
| } |
| |
| |
| void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) { |
| Variable* temp_var = nullptr; |
| VisitObjectLiteral(node, &temp_var); |
| } |
| |
| |
| void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, |
| Variable** temp_var) { |
| DCHECK(block_->ignore_completion_value()); |
| |
| auto temp = *temp_var = CreateTempVar(current_value_); |
| auto iterator = CreateTempVar(parser_->GetIterator( |
| factory()->NewVariableProxy(temp), factory(), RelocInfo::kNoPosition)); |
| auto done = CreateTempVar( |
| factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition)); |
| auto result = CreateTempVar(); |
| auto v = CreateTempVar(); |
| auto completion = CreateTempVar(); |
| auto nopos = RelocInfo::kNoPosition; |
| |
| // For the purpose of iterator finalization, we temporarily set block_ to a |
| // new block. In the main body of this function, we write to block_ (both |
| // explicitly and implicitly via recursion). At the end of the function, we |
| // wrap this new block in a try-finally statement, restore block_ to its |
| // original value, and add the try-finally statement to block_. |
| auto target = block_; |
| if (FLAG_harmony_iterator_close) { |
| block_ = factory()->NewBlock(nullptr, 8, true, nopos); |
| } |
| |
| Spread* spread = nullptr; |
| for (Expression* value : *node->values()) { |
| if (value->IsSpread()) { |
| spread = value->AsSpread(); |
| break; |
| } |
| |
| PatternContext context = SetInitializerContextIfNeeded(value); |
| |
| // if (!done) { |
| // done = true; // If .next, .done or .value throws, don't close. |
| // result = IteratorNext(iterator); |
| // if (result.done) { |
| // v = undefined; |
| // } else { |
| // v = result.value; |
| // done = false; |
| // } |
| // } |
| Statement* if_not_done; |
| { |
| auto result_done = factory()->NewProperty( |
| factory()->NewVariableProxy(result), |
| factory()->NewStringLiteral(ast_value_factory()->done_string(), |
| RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| |
| auto assign_undefined = factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(v), |
| factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| |
| auto assign_value = factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(v), |
| factory()->NewProperty( |
| factory()->NewVariableProxy(result), |
| factory()->NewStringLiteral(ast_value_factory()->value_string(), |
| RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| |
| auto unset_done = factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(done), |
| factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| |
| auto inner_else = |
| factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition); |
| inner_else->statements()->Add( |
| factory()->NewExpressionStatement(assign_value, nopos), zone()); |
| inner_else->statements()->Add( |
| factory()->NewExpressionStatement(unset_done, nopos), zone()); |
| |
| auto inner_if = factory()->NewIfStatement( |
| result_done, |
| factory()->NewExpressionStatement(assign_undefined, nopos), |
| inner_else, nopos); |
| |
| auto next_block = |
| factory()->NewBlock(nullptr, 3, true, RelocInfo::kNoPosition); |
| next_block->statements()->Add( |
| factory()->NewExpressionStatement( |
| factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(done), |
| factory()->NewBooleanLiteral(true, nopos), nopos), |
| nopos), |
| zone()); |
| next_block->statements()->Add( |
| factory()->NewExpressionStatement( |
| parser_->BuildIteratorNextResult( |
| factory()->NewVariableProxy(iterator), result, |
| RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition), |
| zone()); |
| next_block->statements()->Add(inner_if, zone()); |
| |
| if_not_done = factory()->NewIfStatement( |
| factory()->NewUnaryOperation(Token::NOT, |
| factory()->NewVariableProxy(done), |
| RelocInfo::kNoPosition), |
| next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| } |
| block_->statements()->Add(if_not_done, zone()); |
| |
| if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) { |
| if (FLAG_harmony_iterator_close) { |
| // completion = kAbruptCompletion; |
| Expression* proxy = factory()->NewVariableProxy(completion); |
| Expression* assignment = factory()->NewAssignment( |
| Token::ASSIGN, proxy, |
| factory()->NewSmiLiteral(kAbruptCompletion, nopos), nopos); |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, nopos), zone()); |
| } |
| |
| RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); |
| |
| if (FLAG_harmony_iterator_close) { |
| // completion = kNormalCompletion; |
| Expression* proxy = factory()->NewVariableProxy(completion); |
| Expression* assignment = factory()->NewAssignment( |
| Token::ASSIGN, proxy, |
| factory()->NewSmiLiteral(kNormalCompletion, nopos), nopos); |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, nopos), zone()); |
| } |
| } |
| set_context(context); |
| } |
| |
| if (spread != nullptr) { |
| // A spread can only occur as the last component. It is not handled by |
| // RecurseIntoSubpattern above. |
| |
| // let array = []; |
| // while (!done) { |
| // done = true; // If .next, .done or .value throws, don't close. |
| // result = IteratorNext(iterator); |
| // if (!result.done) { |
| // %AppendElement(array, result.value); |
| // done = false; |
| // } |
| // } |
| |
| // let array = []; |
| Variable* array; |
| { |
| auto empty_exprs = new (zone()) ZoneList<Expression*>(0, zone()); |
| array = CreateTempVar(factory()->NewArrayLiteral( |
| empty_exprs, |
| // Reuse pattern's literal index - it is unused since there is no |
| // actual literal allocated. |
| node->literal_index(), RelocInfo::kNoPosition)); |
| } |
| |
| // done = true; |
| Statement* set_done = factory()->NewExpressionStatement( |
| factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(done), |
| factory()->NewBooleanLiteral(true, nopos), nopos), |
| nopos); |
| |
| // result = IteratorNext(iterator); |
| Statement* get_next = factory()->NewExpressionStatement( |
| parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator), |
| result, nopos), |
| nopos); |
| |
| // %AppendElement(array, result.value); |
| Statement* append_element; |
| { |
| auto args = new (zone()) ZoneList<Expression*>(2, zone()); |
| args->Add(factory()->NewVariableProxy(array), zone()); |
| args->Add(factory()->NewProperty( |
| factory()->NewVariableProxy(result), |
| factory()->NewStringLiteral( |
| ast_value_factory()->value_string(), nopos), |
| nopos), |
| zone()); |
| append_element = factory()->NewExpressionStatement( |
| factory()->NewCallRuntime(Runtime::kAppendElement, args, nopos), |
| nopos); |
| } |
| |
| // done = false; |
| Statement* unset_done = factory()->NewExpressionStatement( |
| factory()->NewAssignment( |
| Token::ASSIGN, factory()->NewVariableProxy(done), |
| factory()->NewBooleanLiteral(false, nopos), nopos), |
| nopos); |
| |
| // if (!result.done) { #append_element; #unset_done } |
| Statement* maybe_append_and_unset_done; |
| { |
| Expression* result_done = |
| factory()->NewProperty(factory()->NewVariableProxy(result), |
| factory()->NewStringLiteral( |
| ast_value_factory()->done_string(), nopos), |
| nopos); |
| |
| Block* then = factory()->NewBlock(nullptr, 2, true, nopos); |
| then->statements()->Add(append_element, zone()); |
| then->statements()->Add(unset_done, zone()); |
| |
| maybe_append_and_unset_done = factory()->NewIfStatement( |
| factory()->NewUnaryOperation(Token::NOT, result_done, nopos), then, |
| factory()->NewEmptyStatement(nopos), nopos); |
| } |
| |
| // while (!done) { |
| // #set_done; |
| // #get_next; |
| // #maybe_append_and_unset_done; |
| // } |
| WhileStatement* loop = factory()->NewWhileStatement(nullptr, nopos); |
| { |
| Expression* condition = factory()->NewUnaryOperation( |
| Token::NOT, factory()->NewVariableProxy(done), nopos); |
| Block* body = factory()->NewBlock(nullptr, 3, true, nopos); |
| body->statements()->Add(set_done, zone()); |
| body->statements()->Add(get_next, zone()); |
| body->statements()->Add(maybe_append_and_unset_done, zone()); |
| loop->Initialize(condition, body); |
| } |
| |
| block_->statements()->Add(loop, zone()); |
| RecurseIntoSubpattern(spread->expression(), |
| factory()->NewVariableProxy(array)); |
| } |
| |
| if (FLAG_harmony_iterator_close) { |
| Expression* closing_condition = factory()->NewUnaryOperation( |
| Token::NOT, factory()->NewVariableProxy(done), nopos); |
| parser_->FinalizeIteratorUse(completion, closing_condition, iterator, |
| block_, target); |
| block_ = target; |
| } |
| } |
| |
| |
| void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { |
| Variable* temp_var = nullptr; |
| VisitArrayLiteral(node, &temp_var); |
| } |
| |
| |
| void Parser::PatternRewriter::VisitAssignment(Assignment* node) { |
| // let {<pattern> = <init>} = <value> |
| // becomes |
| // temp = <value>; |
| // <pattern> = temp === undefined ? <init> : temp; |
| DCHECK_EQ(Token::ASSIGN, node->op()); |
| |
| auto initializer = node->value(); |
| auto value = initializer; |
| auto temp = CreateTempVar(current_value_); |
| |
| if (IsInitializerContext()) { |
| Expression* is_undefined = factory()->NewCompareOperation( |
| Token::EQ_STRICT, factory()->NewVariableProxy(temp), |
| factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), |
| RelocInfo::kNoPosition); |
| value = factory()->NewConditional(is_undefined, initializer, |
| factory()->NewVariableProxy(temp), |
| RelocInfo::kNoPosition); |
| } |
| |
| // Initializer may have been parsed in the wrong scope. |
| RewriteParameterScopes(initializer); |
| |
| PatternContext old_context = SetAssignmentContextIfNeeded(initializer); |
| RecurseIntoSubpattern(node->target(), value); |
| set_context(old_context); |
| } |
| |
| |
| // =============== AssignmentPattern only ================== |
| |
| void Parser::PatternRewriter::VisitProperty(v8::internal::Property* node) { |
| DCHECK(IsAssignmentContext()); |
| auto value = current_value_; |
| |
| Assignment* assignment = |
| factory()->NewAssignment(Token::ASSIGN, node, value, node->position()); |
| |
| block_->statements()->Add( |
| factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), |
| zone()); |
| } |
| |
| |
| // =============== UNREACHABLE ============================= |
| |
| void Parser::PatternRewriter::Visit(AstNode* node) { UNREACHABLE(); } |
| |
| #define NOT_A_PATTERN(Node) \ |
| void Parser::PatternRewriter::Visit##Node(v8::internal::Node*) { \ |
| UNREACHABLE(); \ |
| } |
| |
| NOT_A_PATTERN(BinaryOperation) |
| NOT_A_PATTERN(Block) |
| NOT_A_PATTERN(BreakStatement) |
| NOT_A_PATTERN(Call) |
| NOT_A_PATTERN(CallNew) |
| NOT_A_PATTERN(CallRuntime) |
| NOT_A_PATTERN(CaseClause) |
| NOT_A_PATTERN(ClassLiteral) |
| NOT_A_PATTERN(CompareOperation) |
| NOT_A_PATTERN(Conditional) |
| NOT_A_PATTERN(ContinueStatement) |
| NOT_A_PATTERN(CountOperation) |
| NOT_A_PATTERN(DebuggerStatement) |
| NOT_A_PATTERN(DoExpression) |
| NOT_A_PATTERN(DoWhileStatement) |
| NOT_A_PATTERN(EmptyStatement) |
| NOT_A_PATTERN(EmptyParentheses) |
| NOT_A_PATTERN(ExportDeclaration) |
| NOT_A_PATTERN(ExpressionStatement) |
| NOT_A_PATTERN(ForInStatement) |
| NOT_A_PATTERN(ForOfStatement) |
| NOT_A_PATTERN(ForStatement) |
| NOT_A_PATTERN(FunctionDeclaration) |
| NOT_A_PATTERN(FunctionLiteral) |
| NOT_A_PATTERN(IfStatement) |
| NOT_A_PATTERN(ImportDeclaration) |
| NOT_A_PATTERN(Literal) |
| NOT_A_PATTERN(NativeFunctionLiteral) |
| NOT_A_PATTERN(RegExpLiteral) |
| NOT_A_PATTERN(ReturnStatement) |
| NOT_A_PATTERN(SloppyBlockFunctionStatement) |
| NOT_A_PATTERN(Spread) |
| NOT_A_PATTERN(SuperPropertyReference) |
| NOT_A_PATTERN(SuperCallReference) |
| NOT_A_PATTERN(SwitchStatement) |
| NOT_A_PATTERN(ThisFunction) |
| NOT_A_PATTERN(Throw) |
| NOT_A_PATTERN(TryCatchStatement) |
| NOT_A_PATTERN(TryFinallyStatement) |
| NOT_A_PATTERN(UnaryOperation) |
| NOT_A_PATTERN(VariableDeclaration) |
| NOT_A_PATTERN(WhileStatement) |
| NOT_A_PATTERN(WithStatement) |
| NOT_A_PATTERN(Yield) |
| |
| #undef NOT_A_PATTERN |
| } // namespace internal |
| } // namespace v8 |