// 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/objects-inl.h" #include "src/parsing/expression-scope-reparenter.h" #include "src/parsing/parser.h" namespace v8 { namespace internal { class PatternRewriter final : public AstVisitor<PatternRewriter> { public: // Limit the allowed number of local variables in a function. The hard limit // is that offsets computed by FullCodeGenerator::StackOperand and similar // functions are ints, and they should not overflow. In addition, accessing // local variables creates user-controlled constants in the generated code, // and we don't want too much user-controlled memory inside the code (this was // the reason why this limit was introduced in the first place; see // https://codereview.chromium.org/7003030/ ). static const int kMaxNumFunctionLocals = 4194303; // 2^22-1 typedef Parser::DeclarationDescriptor DeclarationDescriptor; static void DeclareAndInitializeVariables( Parser* parser, Block* block, const DeclarationDescriptor* declaration_descriptor, const Parser::DeclarationParsingResult::Declaration* declaration, ZonePtrList<const AstRawString>* names, bool* ok); static void RewriteDestructuringAssignment(Parser* parser, RewritableExpression* to_rewrite, Scope* scope); private: enum PatternContext { BINDING, ASSIGNMENT, ASSIGNMENT_ELEMENT }; class AssignmentElementScope { public: explicit AssignmentElementScope(PatternRewriter* rewriter) : rewriter_(rewriter), context_(rewriter->context()) { if (context_ == ASSIGNMENT) rewriter->context_ = ASSIGNMENT_ELEMENT; } ~AssignmentElementScope() { rewriter_->context_ = context_; } private: PatternRewriter* const rewriter_; const PatternContext context_; }; PatternRewriter(Scope* scope, Parser* parser, PatternContext context) : scope_(scope), parser_(parser), context_(context), initializer_position_(kNoSourcePosition), value_beg_position_(kNoSourcePosition), block_(nullptr), descriptor_(nullptr), names_(nullptr), current_value_(nullptr), recursion_level_(0), ok_(nullptr) {} #define DECLARE_VISIT(type) void Visit##type(v8::internal::type* node); // Visiting functions for AST nodes make this an AstVisitor. AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT PatternContext context() const { return context_; } void RecurseIntoSubpattern(AstNode* pattern, Expression* value) { Expression* old_value = current_value_; current_value_ = value; recursion_level_++; Visit(pattern); recursion_level_--; current_value_ = old_value; } void VisitObjectLiteral(ObjectLiteral* node, Variable** temp_var); void VisitArrayLiteral(ArrayLiteral* node, Variable** temp_var); bool IsBindingContext() const { return context_ == BINDING; } bool IsAssignmentContext() const { return context_ == ASSIGNMENT || context_ == ASSIGNMENT_ELEMENT; } bool IsSubPattern() const { return recursion_level_ > 1; } bool DeclaresParameterContainingSloppyEval() const; void RewriteParameterScopes(Expression* expr); Variable* CreateTempVar(Expression* value = nullptr); AstNodeFactory* factory() const { return parser_->factory(); } AstValueFactory* ast_value_factory() const { return parser_->ast_value_factory(); } Zone* zone() const { return parser_->zone(); } Scope* scope() const { return scope_; } Scope* const scope_; Parser* const parser_; PatternContext context_; int initializer_position_; int value_beg_position_; Block* block_; const DeclarationDescriptor* descriptor_; ZonePtrList<const AstRawString>* names_; Expression* current_value_; int recursion_level_; bool* ok_; DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW() }; void Parser::DeclareAndInitializeVariables( Block* block, const DeclarationDescriptor* declaration_descriptor, const DeclarationParsingResult::Declaration* declaration, ZonePtrList<const AstRawString>* names, bool* ok) { PatternRewriter::DeclareAndInitializeVariables( this, block, declaration_descriptor, declaration, names, ok); } void Parser::RewriteDestructuringAssignment(RewritableExpression* to_rewrite) { PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope()); } Expression* Parser::RewriteDestructuringAssignment(Assignment* assignment) { DCHECK_NOT_NULL(assignment); DCHECK_EQ(Token::ASSIGN, assignment->op()); auto to_rewrite = factory()->NewRewritableExpression(assignment, scope()); RewriteDestructuringAssignment(to_rewrite); return to_rewrite->expression(); } void PatternRewriter::DeclareAndInitializeVariables( Parser* parser, Block* block, const DeclarationDescriptor* declaration_descriptor, const Parser::DeclarationParsingResult::Declaration* declaration, ZonePtrList<const AstRawString>* names, bool* ok) { DCHECK(block->ignore_completion_value()); PatternRewriter rewriter(declaration_descriptor->scope, parser, BINDING); rewriter.initializer_position_ = declaration->initializer_position; rewriter.value_beg_position_ = declaration->value_beg_position; rewriter.block_ = block; rewriter.descriptor_ = declaration_descriptor; rewriter.names_ = names; rewriter.ok_ = ok; rewriter.RecurseIntoSubpattern(declaration->pattern, declaration->initializer); } void PatternRewriter::RewriteDestructuringAssignment( Parser* parser, RewritableExpression* to_rewrite, Scope* scope) { DCHECK(!scope->HasBeenRemoved()); DCHECK(!to_rewrite->is_rewritten()); PatternRewriter rewriter(scope, parser, ASSIGNMENT); rewriter.RecurseIntoSubpattern(to_rewrite, nullptr); } void 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; } DCHECK_NOT_NULL(block_); DCHECK_NOT_NULL(descriptor_); DCHECK_NOT_NULL(ok_); 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). const AstRawString* name = pattern->raw_name(); VariableProxy* proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE, pattern->position()); Declaration* declaration; if (descriptor_->mode == VariableMode::kVar && !descriptor_->scope->is_declaration_scope()) { DCHECK(descriptor_->scope->is_block_scope() || descriptor_->scope->is_with_scope()); declaration = factory()->NewNestedVariableDeclaration( proxy, descriptor_->scope, descriptor_->declaration_pos); } else { declaration = factory()->NewVariableDeclaration(proxy, descriptor_->declaration_pos); } // When an extra declaration scope needs to be inserted to account for // a sloppy eval in a default parameter or function body, the parameter // needs to be declared in the function's scope, not in the varblock // scope which will be used for the initializer expression. Scope* outer_function_scope = nullptr; if (DeclaresParameterContainingSloppyEval()) { outer_function_scope = descriptor_->scope->outer_scope(); } Variable* var = parser_->Declare( declaration, descriptor_->declaration_kind, descriptor_->mode, Variable::DefaultInitializationFlag(descriptor_->mode), ok_, outer_function_scope); if (!*ok_) return; DCHECK_NOT_NULL(var); DCHECK(proxy->is_resolved()); DCHECK_NE(initializer_position_, kNoSourcePosition); var->set_initializer_position(initializer_position_); Scope* declaration_scope = outer_function_scope != nullptr ? outer_function_scope : (IsLexicalVariableMode(descriptor_->mode) ? descriptor_->scope : descriptor_->scope->GetDeclarationScope()); if (declaration_scope->num_var() > kMaxNumFunctionLocals) { parser_->ReportMessage(MessageTemplate::kTooManyVariables); *ok_ = false; return; } if (names_) { names_->Add(name, zone()); } // If there's no initializer, we're done. if (value == nullptr) return; Scope* var_init_scope = descriptor_->scope; Parser::MarkLoopVariableAsAssigned(var_init_scope, proxy->var(), descriptor_->declaration_kind); // A declaration of the form: // // var v = x; // // is syntactic sugar for: // // var v; v = x; // // In particular, we need to re-lookup 'v' as it may be a different // 'v' than the 'v' in the declaration (e.g., if we are inside a // 'with' statement or 'catch' block). Global var declarations // also need special treatment. // For 'let' and 'const' declared variables the initialization always // assigns to the declared variable. // But for var declarations we need to do a new lookup. if (descriptor_->mode == VariableMode::kVar) { proxy = var_init_scope->NewUnresolved(factory(), name); } else { DCHECK_NOT_NULL(proxy); DCHECK_NOT_NULL(proxy->var()); } // Add break location for destructured sub-pattern. int pos = value_beg_position_; if (pos == kNoSourcePosition) { pos = IsSubPattern() ? pattern->position() : value->position(); } Assignment* assignment = factory()->NewAssignment(Token::INIT, proxy, value, pos); block_->statements()->Add(factory()->NewExpressionStatement(assignment, pos), zone()); } Variable* 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, kNoSourcePosition); block_->statements()->Add( factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone()); } return temp; } void PatternRewriter::VisitRewritableExpression(RewritableExpression* node) { if (!node->expression()->IsAssignment()) { // RewritableExpressions are also used for desugaring Spread, which is // orthogonal to PatternRewriter; just visit the underlying expression. DCHECK_EQ(AstNode::kArrayLiteral, node->expression()->node_type()); return Visit(node->expression()); } else if (context() != ASSIGNMENT) { // This is not a destructuring assignment. Mark the node as rewritten to // prevent redundant rewriting and visit the underlying expression. DCHECK(!node->is_rewritten()); node->set_rewritten(); return Visit(node->expression()); } DCHECK(!node->is_rewritten()); DCHECK_EQ(ASSIGNMENT, context()); Assignment* assign = node->expression()->AsAssignment(); DCHECK_NOT_NULL(assign); DCHECK_EQ(Token::ASSIGN, assign->op()); int pos = assign->position(); Block* old_block = block_; block_ = factory()->NewBlock(8, true); Variable* temp = nullptr; Expression* pattern = assign->target(); Expression* old_value = current_value_; current_value_ = assign->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()); } } bool PatternRewriter::DeclaresParameterContainingSloppyEval() const { // Need to check for a binding context to make sure we have a descriptor. if (IsBindingContext() && // Only relevant for parameters. descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER && // And only when scope is a block scope; // without eval, it is a function scope. scope()->is_block_scope()) { DCHECK(scope()->is_declaration_scope()); DCHECK(scope()->AsDeclarationScope()->calls_sloppy_eval()); DCHECK(scope()->outer_scope()->is_function_scope()); return true; } return false; } // 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. void PatternRewriter::RewriteParameterScopes(Expression* expr) { if (DeclaresParameterContainingSloppyEval()) { ReparentExpressionScope(parser_->stack_limit(), expr, scope()); } } void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern, Variable** temp_var) { auto temp = *temp_var = CreateTempVar(current_value_); ZonePtrList<Expression>* rest_runtime_callargs = nullptr; if (pattern->has_rest_property()) { // non_rest_properties_count = pattern->properties()->length - 1; // args_length = 1 + non_rest_properties_count because we need to // pass temp as well to the runtime function. int args_length = pattern->properties()->length(); rest_runtime_callargs = new (zone()) ZonePtrList<Expression>(args_length, zone()); rest_runtime_callargs->Add(factory()->NewVariableProxy(temp), zone()); } block_->statements()->Add(parser_->BuildAssertIsCoercible(temp, pattern), zone()); for (ObjectLiteralProperty* property : *pattern->properties()) { Expression* value; if (property->kind() == ObjectLiteralProperty::Kind::SPREAD) { // var { y, [x++]: a, ...c } = temp // becomes // var y = temp.y; // var temp1 = %ToName(x++); // var a = temp[temp1]; // var c; // c = %CopyDataPropertiesWithExcludedProperties(temp, "y", temp1); value = factory()->NewCallRuntime( Runtime::kCopyDataPropertiesWithExcludedProperties, rest_runtime_callargs, kNoSourcePosition); } else { Expression* key = property->key(); if (!key->IsLiteral()) { // Computed property names contain expressions which might require // scope rewriting. RewriteParameterScopes(key); } if (pattern->has_rest_property()) { Expression* excluded_property = key; if (property->is_computed_name()) { DCHECK(!key->IsPropertyName() || !key->IsNumberLiteral()); auto args = new (zone()) ZonePtrList<Expression>(1, zone()); args->Add(key, zone()); auto to_name_key = CreateTempVar(factory()->NewCallRuntime( Runtime::kToName, args, kNoSourcePosition)); key = factory()->NewVariableProxy(to_name_key); excluded_property = factory()->NewVariableProxy(to_name_key); } else { DCHECK(key->IsPropertyName() || key->IsNumberLiteral()); } DCHECK_NOT_NULL(rest_runtime_callargs); rest_runtime_callargs->Add(excluded_property, zone()); } value = factory()->NewProperty(factory()->NewVariableProxy(temp), key, kNoSourcePosition); } AssignmentElementScope element_scope(this); RecurseIntoSubpattern(property->value(), value); } } void PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) { Variable* temp_var = nullptr; VisitObjectLiteral(node, &temp_var); } void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, Variable** temp_var) { DCHECK(block_->ignore_completion_value()); auto temp = *temp_var = CreateTempVar(current_value_); auto iterator = CreateTempVar(factory()->NewGetIterator( factory()->NewVariableProxy(temp), current_value_, IteratorType::kNormal, current_value_->position())); auto next = CreateTempVar(factory()->NewProperty( factory()->NewVariableProxy(iterator), factory()->NewStringLiteral(ast_value_factory()->next_string(), kNoSourcePosition), kNoSourcePosition)); auto done = CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition)); auto result = CreateTempVar(); auto v = CreateTempVar(); auto completion = CreateTempVar(); auto nopos = kNoSourcePosition; // 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_; block_ = factory()->NewBlock(8, true); Spread* spread = nullptr; for (Expression* value : *node->values()) { if (value->IsSpread()) { spread = value->AsSpread(); break; } // 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(), kNoSourcePosition), kNoSourcePosition); auto assign_undefined = factory()->NewAssignment( Token::ASSIGN, factory()->NewVariableProxy(v), factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); auto assign_value = factory()->NewAssignment( Token::ASSIGN, factory()->NewVariableProxy(v), factory()->NewProperty( factory()->NewVariableProxy(result), factory()->NewStringLiteral(ast_value_factory()->value_string(), kNoSourcePosition), kNoSourcePosition), kNoSourcePosition); auto unset_done = factory()->NewAssignment( Token::ASSIGN, factory()->NewVariableProxy(done), factory()->NewBooleanLiteral(false, kNoSourcePosition), kNoSourcePosition); auto inner_else = factory()->NewBlock(2, true); 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(3, true); 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), factory()->NewVariableProxy(next), result, IteratorType::kNormal, kNoSourcePosition), kNoSourcePosition), zone()); next_block->statements()->Add(inner_if, zone()); if_not_done = factory()->NewIfStatement( factory()->NewUnaryOperation( Token::NOT, factory()->NewVariableProxy(done), kNoSourcePosition), next_block, factory()->NewEmptyStatement(kNoSourcePosition), kNoSourcePosition); } block_->statements()->Add(if_not_done, zone()); if (!value->IsTheHoleLiteral()) { { // completion = kAbruptCompletion; Expression* proxy = factory()->NewVariableProxy(completion); Expression* assignment = factory()->NewAssignment( Token::ASSIGN, proxy, factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos); block_->statements()->Add( factory()->NewExpressionStatement(assignment, nopos), zone()); } { AssignmentElementScope element_scope(this); RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); } { // completion = kNormalCompletion; Expression* proxy = factory()->NewVariableProxy(completion); Expression* assignment = factory()->NewAssignment( Token::ASSIGN, proxy, factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos); block_->statements()->Add( factory()->NewExpressionStatement(assignment, nopos), zone()); } } } if (spread != nullptr) { // A spread can only occur as the last component. It is not handled by // RecurseIntoSubpattern above. // let array = []; // let index = 0; // while (!done) { // done = true; // If .next, .done or .value throws, don't close. // result = IteratorNext(iterator); // if (!result.done) { // StoreInArrayLiteral(array, index, result.value); // done = false; // } // index++; // } // let array = []; Variable* array; { auto empty_exprs = new (zone()) ZonePtrList<Expression>(0, zone()); array = CreateTempVar( factory()->NewArrayLiteral(empty_exprs, kNoSourcePosition)); } // let index = 0; Variable* index = CreateTempVar(factory()->NewSmiLiteral(0, kNoSourcePosition)); // 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), factory()->NewVariableProxy(next), result, IteratorType::kNormal, nopos), nopos); // StoreInArrayLiteral(array, index, result.value); Statement* store; { auto value = factory()->NewProperty( factory()->NewVariableProxy(result), factory()->NewStringLiteral(ast_value_factory()->value_string(), nopos), nopos); store = factory()->NewExpressionStatement( factory()->NewStoreInArrayLiteral(factory()->NewVariableProxy(array), factory()->NewVariableProxy(index), value, 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) { #store; #unset_done } Statement* maybe_store_and_unset_done; { Expression* result_done = factory()->NewProperty(factory()->NewVariableProxy(result), factory()->NewStringLiteral( ast_value_factory()->done_string(), nopos), nopos); Block* then = factory()->NewBlock(2, true); then->statements()->Add(store, zone()); then->statements()->Add(unset_done, zone()); maybe_store_and_unset_done = factory()->NewIfStatement( factory()->NewUnaryOperation(Token::NOT, result_done, nopos), then, factory()->NewEmptyStatement(nopos), nopos); } // index++; Statement* increment_index; { increment_index = factory()->NewExpressionStatement( factory()->NewCountOperation( Token::INC, false, factory()->NewVariableProxy(index), nopos), nopos); } // while (!done) { // #set_done; // #get_next; // #maybe_store_and_unset_done; // #increment_index; // } WhileStatement* loop = factory()->NewWhileStatement(nullptr, nullptr, nopos); { Expression* condition = factory()->NewUnaryOperation( Token::NOT, factory()->NewVariableProxy(done), nopos); Block* body = factory()->NewBlock(4, true); body->statements()->Add(set_done, zone()); body->statements()->Add(get_next, zone()); body->statements()->Add(maybe_store_and_unset_done, zone()); body->statements()->Add(increment_index, zone()); loop->Initialize(condition, body); } block_->statements()->Add(loop, zone()); RecurseIntoSubpattern(spread->expression(), factory()->NewVariableProxy(array)); } Expression* closing_condition = factory()->NewUnaryOperation( Token::NOT, factory()->NewVariableProxy(done), nopos); parser_->FinalizeIteratorUse(completion, closing_condition, iterator, block_, target, IteratorType::kNormal); block_ = target; } void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { Variable* temp_var = nullptr; VisitArrayLiteral(node, &temp_var); } void PatternRewriter::VisitAssignment(Assignment* node) { // let {<pattern> = <init>} = <value> // becomes // temp = <value>; // <pattern> = temp === undefined ? <init> : temp; DCHECK_EQ(Token::ASSIGN, node->op()); // Rewriting of Assignment nodes for destructuring assignment // is handled in VisitRewritableExpression(). DCHECK_NE(ASSIGNMENT, context()); auto initializer = node->value(); auto value = initializer; auto temp = CreateTempVar(current_value_); Expression* is_undefined = factory()->NewCompareOperation( Token::EQ_STRICT, factory()->NewVariableProxy(temp), factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); value = factory()->NewConditional(is_undefined, initializer, factory()->NewVariableProxy(temp), kNoSourcePosition); // Initializer may have been parsed in the wrong scope. RewriteParameterScopes(initializer); RecurseIntoSubpattern(node->target(), value); } // =============== AssignmentPattern only ================== void 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, kNoSourcePosition), zone()); } // =============== UNREACHABLE ============================= #define NOT_A_PATTERN(Node) \ void PatternRewriter::Visit##Node(v8::internal::Node*) { UNREACHABLE(); } NOT_A_PATTERN(BinaryOperation) NOT_A_PATTERN(NaryOperation) NOT_A_PATTERN(Block) NOT_A_PATTERN(BreakStatement) NOT_A_PATTERN(Call) NOT_A_PATTERN(CallNew) NOT_A_PATTERN(CallRuntime) NOT_A_PATTERN(ClassLiteral) NOT_A_PATTERN(CompareOperation) NOT_A_PATTERN(CompoundAssignment) 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(ExpressionStatement) NOT_A_PATTERN(ForInStatement) NOT_A_PATTERN(ForOfStatement) NOT_A_PATTERN(ForStatement) NOT_A_PATTERN(FunctionDeclaration) NOT_A_PATTERN(FunctionLiteral) NOT_A_PATTERN(GetIterator) NOT_A_PATTERN(GetTemplateObject) NOT_A_PATTERN(IfStatement) NOT_A_PATTERN(ImportCallExpression) NOT_A_PATTERN(Literal) NOT_A_PATTERN(NativeFunctionLiteral) NOT_A_PATTERN(RegExpLiteral) NOT_A_PATTERN(ResolvedProperty) NOT_A_PATTERN(ReturnStatement) NOT_A_PATTERN(SloppyBlockFunctionStatement) NOT_A_PATTERN(Spread) NOT_A_PATTERN(StoreInArrayLiteral) NOT_A_PATTERN(SuperPropertyReference) NOT_A_PATTERN(SuperCallReference) NOT_A_PATTERN(SwitchStatement) NOT_A_PATTERN(TemplateLiteral) 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) NOT_A_PATTERN(YieldStar) NOT_A_PATTERN(Await) NOT_A_PATTERN(InitializeClassFieldsStatement) #undef NOT_A_PATTERN } // namespace internal } // namespace v8