diff options
Diffstat (limited to 'deps/v8/src/parsing/pattern-rewriter.cc')
-rw-r--r-- | deps/v8/src/parsing/pattern-rewriter.cc | 628 |
1 files changed, 628 insertions, 0 deletions
diff --git a/deps/v8/src/parsing/pattern-rewriter.cc b/deps/v8/src/parsing/pattern-rewriter.cc new file mode 100644 index 0000000000..6e20282785 --- /dev/null +++ b/deps/v8/src/parsing/pattern-rewriter.cc @@ -0,0 +1,628 @@ +// 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; + + 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, RewritableAssignmentExpression* 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()->NewRewritableAssignmentExpression(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(); + if (node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN) { + 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->IsRewritableAssignmentExpression() && + !node->AsRewritableAssignmentExpression()->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()); + value = NULL; // zap the value to avoid the unnecessary assignment + + // 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, + descriptor_->initialization_pos); + } 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, + descriptor_->declaration_pos), + 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()); + value = NULL; // zap the value to avoid the unnecessary assignment + // Construct the call to Runtime_InitializeVarGlobal + // and add it to the initialization statement block. + initialize = + factory()->NewCallRuntime(Runtime::kInitializeVarGlobal, arguments, + descriptor_->declaration_pos); + } else { + initialize = NULL; + } + } + + if (initialize != NULL) { + block_->statements()->Add( + factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition), + zone()); + } + } else if (value != nullptr && (descriptor_->mode == CONST_LEGACY || + IsLexicalVariableMode(descriptor_->mode))) { + // Constant initializations always assign to the declared constant which + // is always at the function scope level. This is only relevant for + // dynamically looked-up variables and constants (the + // start context for constant lookups is always the function context, + // while it is the top context for var declared variables). Sigh... + // For 'let' and 'const' declared variables in harmony mode the + // initialization also 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() : RelocInfo::kNoPosition; + 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() : RelocInfo::kNoPosition; + 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::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + if (!IsAssignmentContext()) { + // Mark the assignment 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, false, 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); +} + + +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()); + 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) { + auto temp = *temp_var = CreateTempVar(current_value_); + + block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone()); + + 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(); + + Spread* spread = nullptr; + for (Expression* value : *node->values()) { + if (value->IsSpread()) { + spread = value->AsSpread(); + break; + } + + PatternContext context = SetInitializerContextIfNeeded(value); + // if (!done) { + // result = IteratorNext(iterator); + // v = (done = result.done) ? undefined : result.value; + // } + auto next_block = + factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition); + next_block->statements()->Add(factory()->NewExpressionStatement( + parser_->BuildIteratorNextResult( + factory()->NewVariableProxy(iterator), + result, RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + auto assign_to_done = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(done), + factory()->NewProperty( + factory()->NewVariableProxy(result), + factory()->NewStringLiteral(ast_value_factory()->done_string(), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + auto next_value = factory()->NewConditional( + assign_to_done, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + factory()->NewProperty( + factory()->NewVariableProxy(result), + factory()->NewStringLiteral(ast_value_factory()->value_string(), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + next_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewAssignment(Token::ASSIGN, + factory()->NewVariableProxy(v), next_value, + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + auto if_statement = factory()->NewIfStatement( + factory()->NewUnaryOperation(Token::NOT, + factory()->NewVariableProxy(done), + RelocInfo::kNoPosition), + next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + block_->statements()->Add(if_statement, zone()); + + if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) { + RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); + } + set_context(context); + } + + if (spread != nullptr) { + // array = []; + // if (!done) %concat_iterable_to_array(array, iterator); + auto empty_exprs = new (zone()) ZoneList<Expression*>(0, zone()); + auto array = CreateTempVar(factory()->NewArrayLiteral( + empty_exprs, + // Reuse pattern's literal index - it is unused since there is no + // actual literal allocated. + node->literal_index(), is_strong(scope()->language_mode()), + RelocInfo::kNoPosition)); + + auto arguments = new (zone()) ZoneList<Expression*>(2, zone()); + arguments->Add(factory()->NewVariableProxy(array), zone()); + arguments->Add(factory()->NewVariableProxy(iterator), zone()); + auto spread_into_array_call = + factory()->NewCallRuntime(Context::CONCAT_ITERABLE_TO_ARRAY_INDEX, + arguments, RelocInfo::kNoPosition); + + auto if_statement = factory()->NewIfStatement( + factory()->NewUnaryOperation(Token::NOT, + factory()->NewVariableProxy(done), + RelocInfo::kNoPosition), + factory()->NewExpressionStatement(spread_into_array_call, + RelocInfo::kNoPosition), + factory()->NewEmptyStatement(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + block_->statements()->Add(if_statement, zone()); + + RecurseIntoSubpattern(spread->expression(), + factory()->NewVariableProxy(array)); + } +} + + +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); + } + + if (IsBindingContext() && + descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER && + scope()->is_arrow_scope()) { + RewriteParameterInitializerScope(parser_->stack_limit(), initializer, + scope()->outer_scope(), scope()); + } + + 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 |