From: arv Date: Fri, 14 Nov 2014 15:05:05 +0000 (-0800) Subject: Classes: Implement correct name binding X-Git-Tag: upstream/4.7.83~5680 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f3d5b13e04c9cedd1f020ce0beec64275fcc40d5;p=platform%2Fupstream%2Fv8.git Classes: Implement correct name binding Named class declarations and class expression have a const binding for the name that is in TDZ for the extends expression. BUG=v8:3330 LOG=Y R=dslomov@chromium.org, adamk Review URL: https://codereview.chromium.org/722793005 Cr-Commit-Position: refs/heads/master@{#25360} --- diff --git a/src/ast-numbering.cc b/src/ast-numbering.cc index 5ebbde8..058921b 100644 --- a/src/ast-numbering.cc +++ b/src/ast-numbering.cc @@ -448,6 +448,9 @@ void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) { node->set_base_id(ReserveIdRange(ClassLiteral::num_ids())); if (node->extends()) Visit(node->extends()); if (node->constructor()) Visit(node->constructor()); + if (node->class_variable_proxy()) { + VisitVariableProxy(node->class_variable_proxy()); + } for (int i = 0; i < node->properties()->length(); i++) { VisitObjectLiteralProperty(node->properties()->at(i)); } diff --git a/src/ast.h b/src/ast.h index 65c4d4f..3e12d54 100644 --- a/src/ast.h +++ b/src/ast.h @@ -2641,6 +2641,8 @@ class ClassLiteral FINAL : public Expression { Handle name() const { return raw_name_->string(); } const AstRawString* raw_name() const { return raw_name_; } + Scope* scope() const { return scope_; } + VariableProxy* class_variable_proxy() const { return class_variable_proxy_; } Expression* extends() const { return extends_; } Expression* constructor() const { return constructor_; } ZoneList* properties() const { return properties_; } @@ -2648,11 +2650,14 @@ class ClassLiteral FINAL : public Expression { int end_position() const { return end_position_; } protected: - ClassLiteral(Zone* zone, const AstRawString* name, Expression* extends, + ClassLiteral(Zone* zone, const AstRawString* name, Scope* scope, + VariableProxy* class_variable_proxy, Expression* extends, Expression* constructor, ZoneList* properties, int start_position, int end_position) : Expression(zone, start_position), raw_name_(name), + scope_(scope), + class_variable_proxy_(class_variable_proxy), extends_(extends), constructor_(constructor), properties_(properties), @@ -2660,6 +2665,8 @@ class ClassLiteral FINAL : public Expression { private: const AstRawString* raw_name_; + Scope* scope_; + VariableProxy* class_variable_proxy_; Expression* extends_; Expression* constructor_; ZoneList* properties_; @@ -3500,12 +3507,14 @@ class AstNodeFactory FINAL BASE_EMBEDDED { position); } - ClassLiteral* NewClassLiteral(const AstRawString* name, Expression* extends, + ClassLiteral* NewClassLiteral(const AstRawString* name, Scope* scope, + VariableProxy* proxy, Expression* extends, Expression* constructor, ZoneList* properties, int start_position, int end_position) { - return new (zone_) ClassLiteral(zone_, name, extends, constructor, - properties, start_position, end_position); + return new (zone_) + ClassLiteral(zone_, name, scope, proxy, extends, constructor, + properties, start_position, end_position); } NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name, diff --git a/src/full-codegen.cc b/src/full-codegen.cc index a993d48..e32c59f 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -1073,41 +1073,12 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { NestedBlock nested_block(this, stmt); SetStatementPosition(stmt); - Scope* saved_scope = scope(); - // Push a block context when entering a block with block scoped variables. - if (stmt->scope() == NULL) { - PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); - } else { - scope_ = stmt->scope(); - DCHECK(!scope_->is_module_scope()); - { Comment cmnt(masm_, "[ Extend block context"); - __ Push(scope_->GetScopeInfo()); - PushFunctionArgumentForContextAllocation(); - __ CallRuntime(Runtime::kPushBlockContext, 2); - - // Replace the context stored in the frame. - StoreToFrameField(StandardFrameConstants::kContextOffset, - context_register()); - PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); - } - { Comment cmnt(masm_, "[ Declarations"); - VisitDeclarations(scope_->declarations()); - PrepareForBailoutForId(stmt->DeclsId(), NO_REGISTERS); - } - } - - VisitStatements(stmt->statements()); - scope_ = saved_scope; - __ bind(nested_block.break_label()); - - // Pop block context if necessary. - if (stmt->scope() != NULL) { - LoadContextField(context_register(), Context::PREVIOUS_INDEX); - // Update local stack frame context field. - StoreToFrameField(StandardFrameConstants::kContextOffset, - context_register()); + { + EnterBlockScopeIfNeeded block_scope_state( + this, stmt->scope(), stmt->EntryId(), stmt->DeclsId(), stmt->ExitId()); + VisitStatements(stmt->statements()); + __ bind(nested_block.break_label()); } - PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); } @@ -1608,26 +1579,38 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { void FullCodeGenerator::VisitClassLiteral(ClassLiteral* lit) { Comment cmnt(masm_, "[ ClassLiteral"); - if (lit->raw_name() != NULL) { - __ Push(lit->name()); - } else { - __ Push(isolate()->factory()->undefined_value()); - } + { + EnterBlockScopeIfNeeded block_scope_state( + this, lit->scope(), BailoutId::None(), BailoutId::None(), + BailoutId::None()); - if (lit->extends() != NULL) { - VisitForStackValue(lit->extends()); - } else { - __ Push(isolate()->factory()->the_hole_value()); - } + if (lit->raw_name() != NULL) { + __ Push(lit->name()); + } else { + __ Push(isolate()->factory()->undefined_value()); + } - VisitForStackValue(lit->constructor()); + if (lit->extends() != NULL) { + VisitForStackValue(lit->extends()); + } else { + __ Push(isolate()->factory()->the_hole_value()); + } + + VisitForStackValue(lit->constructor()); - __ Push(script()); - __ Push(Smi::FromInt(lit->start_position())); - __ Push(Smi::FromInt(lit->end_position())); + __ Push(script()); + __ Push(Smi::FromInt(lit->start_position())); + __ Push(Smi::FromInt(lit->end_position())); - __ CallRuntime(Runtime::kDefineClass, 6); - EmitClassDefineProperties(lit); + __ CallRuntime(Runtime::kDefineClass, 6); + EmitClassDefineProperties(lit); + + if (lit->scope() != NULL) { + DCHECK_NOT_NULL(lit->class_variable_proxy()); + EmitVariableAssignment(lit->class_variable_proxy()->var(), + Token::INIT_CONST); + } + } context()->Plug(result_register()); } @@ -1795,6 +1778,49 @@ bool BackEdgeTable::Verify(Isolate* isolate, Code* unoptimized) { #endif // DEBUG +FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded( + FullCodeGenerator* codegen, Scope* scope, BailoutId entry_id, + BailoutId declarations_id, BailoutId exit_id) + : codegen_(codegen), scope_(scope), exit_id_(exit_id) { + saved_scope_ = codegen_->scope(); + + if (scope == NULL) { + codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS); + } else { + codegen_->scope_ = scope; + { + Comment cmnt(masm(), "[ Extend block context"); + __ Push(scope->GetScopeInfo()); + codegen_->PushFunctionArgumentForContextAllocation(); + __ CallRuntime(Runtime::kPushBlockContext, 2); + + // Replace the context stored in the frame. + codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, + codegen_->context_register()); + codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS); + } + { + Comment cmnt(masm(), "[ Declarations"); + codegen_->VisitDeclarations(scope->declarations()); + codegen_->PrepareForBailoutForId(declarations_id, NO_REGISTERS); + } + } +} + + +FullCodeGenerator::EnterBlockScopeIfNeeded::~EnterBlockScopeIfNeeded() { + if (scope_ != NULL) { + codegen_->LoadContextField(codegen_->context_register(), + Context::PREVIOUS_INDEX); + // Update local stack frame context field. + codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, + codegen_->context_register()); + } + codegen_->PrepareForBailoutForId(exit_id_, NO_REGISTERS); + codegen_->scope_ = saved_scope_; +} + + #undef __ diff --git a/src/full-codegen.h b/src/full-codegen.h index fd53cea..73a6c41 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -882,6 +882,22 @@ class FullCodeGenerator: public AstVisitor { virtual bool IsEffect() const { return true; } }; + class EnterBlockScopeIfNeeded { + public: + EnterBlockScopeIfNeeded(FullCodeGenerator* codegen, Scope* scope, + BailoutId entry_id, BailoutId declarations_id, + BailoutId exit_id); + ~EnterBlockScopeIfNeeded(); + + private: + MacroAssembler* masm() const { return codegen_->masm(); } + + FullCodeGenerator* codegen_; + Scope* scope_; + Scope* saved_scope_; + BailoutId exit_id_; + }; + MacroAssembler* masm_; CompilationInfo* info_; Scope* scope_; diff --git a/src/parser.cc b/src/parser.cc index 99c93b8..b2cf3b1 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -685,13 +685,6 @@ Expression* ParserTraits::SuperReference(Scope* scope, AstNodeFactory* factory, pos); } -Expression* ParserTraits::ClassExpression( - const AstRawString* name, Expression* extends, Expression* constructor, - ZoneList* properties, int start_position, - int end_position, AstNodeFactory* factory) { - return factory->NewClassLiteral(name, extends, constructor, properties, - start_position, end_position); -} Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope, int pos, int end_pos) { @@ -777,6 +770,14 @@ FunctionLiteral* ParserTraits::ParseFunctionLiteral( } +ClassLiteral* ParserTraits::ParseClassLiteral( + const AstRawString* name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + return parser_->ParseClassLiteral(name, class_name_location, + name_is_strict_reserved, pos, ok); +} + + Parser::Parser(CompilationInfo* info, ParseInfo* parse_info) : ParserBase(&scanner_, parse_info->stack_limit, info->extension(), NULL, info->zone(), this), @@ -1993,8 +1994,8 @@ Statement* Parser::ParseClassDeclaration(ZoneList* names, bool is_strict_reserved = false; const AstRawString* name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK); - Expression* value = ParseClassLiteral(name, scanner()->location(), - is_strict_reserved, pos, CHECK_OK); + ClassLiteral* value = ParseClassLiteral(name, scanner()->location(), + is_strict_reserved, pos, CHECK_OK); VariableProxy* proxy = NewUnresolved(name, LET, Interface::NewValue()); Declaration* declaration = @@ -3893,6 +3894,90 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( } +ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok) { + // All parts of a ClassDeclaration and ClassExpression are strict code. + if (name_is_strict_reserved) { + ReportMessageAt(class_name_location, "unexpected_strict_reserved"); + *ok = false; + return NULL; + } + if (IsEvalOrArguments(name)) { + ReportMessageAt(class_name_location, "strict_eval_arguments"); + *ok = false; + return NULL; + } + + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, block_scope); + scope_->SetStrictMode(STRICT); + scope_->SetScopeName(name); + + VariableProxy* proxy = NULL; + if (name != NULL) { + proxy = NewUnresolved(name, CONST, Interface::NewConst()); + Declaration* declaration = + factory()->NewVariableDeclaration(proxy, CONST, block_scope, pos); + Declare(declaration, true, CHECK_OK); + } + + Expression* extends = NULL; + if (Check(Token::EXTENDS)) { + block_scope->set_start_position(scanner()->location().end_pos); + extends = ParseLeftHandSideExpression(CHECK_OK); + } else { + block_scope->set_start_position(scanner()->location().end_pos); + } + + ZoneList* properties = NewPropertyList(4, zone()); + Expression* constructor = NULL; + bool has_seen_constructor = false; + + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + if (Check(Token::SEMICOLON)) continue; + if (fni_ != NULL) fni_->Enter(); + const bool in_class = true; + const bool is_static = false; + ObjectLiteral::Property* property = ParsePropertyDefinition( + NULL, in_class, is_static, &has_seen_constructor, CHECK_OK); + + if (has_seen_constructor && constructor == NULL) { + constructor = GetPropertyValue(property); + } else { + properties->Add(property, zone()); + } + + if (fni_ != NULL) { + fni_->Infer(); + fni_->Leave(); + } + } + + Expect(Token::RBRACE, CHECK_OK); + int end_pos = scanner()->location().end_pos; + + if (constructor == NULL) { + constructor = + DefaultConstructor(extends != NULL, block_scope, pos, end_pos); + } + + block_scope->set_end_position(end_pos); + block_scope = block_scope->FinalizeBlockScope(); + + if (name != NULL) { + DCHECK_NOT_NULL(proxy); + DCHECK_NOT_NULL(block_scope); + proxy->var()->set_initializer_position(end_pos); + } + + return factory()->NewClassLiteral(name, block_scope, proxy, extends, + constructor, properties, pos, end_pos); +} + + Expression* Parser::ParseV8Intrinsic(bool* ok) { // CallRuntime :: // '%' Identifier Arguments diff --git a/src/parser.h b/src/parser.h index 44eb74f..e2afe56 100644 --- a/src/parser.h +++ b/src/parser.h @@ -538,12 +538,6 @@ class ParserTraits { int pos = RelocInfo::kNoPosition); Expression* SuperReference(Scope* scope, AstNodeFactory* factory, int pos = RelocInfo::kNoPosition); - Expression* ClassExpression(const AstRawString* name, Expression* extends, - Expression* constructor, - ZoneList* properties, - int start_position, int end_position, - AstNodeFactory* factory); - Expression* DefaultConstructor(bool call_super, Scope* scope, int pos, int end_pos); Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner, @@ -583,6 +577,12 @@ class ParserTraits { V8_INLINE ZoneList* ParseEagerFunctionBody( const AstRawString* name, int pos, Variable* fvar, Token::Value fvar_init_op, bool is_generator, bool* ok); + + ClassLiteral* ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope, bool* ok); @@ -763,6 +763,12 @@ class Parser : public ParserBase { int function_token_position, FunctionLiteral::FunctionType type, FunctionLiteral::ArityRestriction arity_restriction, bool* ok); + + ClassLiteral* ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + // Magical syntax support. Expression* ParseV8Intrinsic(bool* ok); diff --git a/src/preparser.cc b/src/preparser.cc index 7ab9814..1a2ddb6 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -132,6 +132,14 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction( } +PreParserExpression PreParserTraits::ParseClassLiteral( + PreParserIdentifier name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + return pre_parser_->ParseClassLiteral(name, class_name_location, + name_is_strict_reserved, pos, ok); +} + + // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. // See preparser-data.h for the data. @@ -928,6 +936,47 @@ void PreParser::ParseLazyFunctionLiteralBody(bool* ok) { } +PreParserExpression PreParser::ParseClassLiteral( + PreParserIdentifier name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + // All parts of a ClassDeclaration and ClassExpression are strict code. + if (name_is_strict_reserved) { + ReportMessageAt(class_name_location, "unexpected_strict_reserved"); + *ok = false; + return EmptyExpression(); + } + if (IsEvalOrArguments(name)) { + ReportMessageAt(class_name_location, "strict_eval_arguments"); + *ok = false; + return EmptyExpression(); + } + + PreParserScope scope = NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, &scope); + scope_->SetStrictMode(STRICT); + scope_->SetScopeName(name); + + if (Check(Token::EXTENDS)) { + ParseLeftHandSideExpression(CHECK_OK); + } + + bool has_seen_constructor = false; + + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + if (Check(Token::SEMICOLON)) continue; + const bool in_class = true; + const bool is_static = false; + ParsePropertyDefinition(NULL, in_class, is_static, &has_seen_constructor, + CHECK_OK); + } + + Expect(Token::RBRACE, CHECK_OK); + + return Expression::Default(); +} + + PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) { // CallRuntime :: // '%' Identifier Arguments diff --git a/src/preparser.h b/src/preparser.h index c447312..dd05361 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -490,10 +490,6 @@ class ParserBase : public Traits { bool* ok); ExpressionT ParseArrowFunctionLiteral(int start_pos, ExpressionT params_ast, bool* ok); - ExpressionT ParseClassLiteral(IdentifierT name, - Scanner::Location function_name_location, - bool name_is_strict_reserved, int pos, - bool* ok); // Checks if the expression is a valid reference expression (e.g., on the // left-hand side of assignments). Although ruled out by ECMA as early errors, @@ -1090,12 +1086,13 @@ class PreParserFactory { int position) { return PreParserExpression::Default(); } - PreParserExpression NewClassLiteral(PreParserIdentifier name, - PreParserExpression extends, - PreParserExpression constructor, - PreParserExpressionList properties, - int start_position, int end_position) { - return PreParserExpression::Default(); + + // Return the object itself as AstVisitor and implement the needed + // dummy method right in this class. + PreParserFactory* visitor() { return this; } + int* ast_properties() { + static int dummy = 42; + return &dummy; } }; @@ -1308,13 +1305,6 @@ class PreParserTraits { return PreParserExpression::Super(); } - static PreParserExpression ClassExpression( - PreParserIdentifier name, PreParserExpression extends, - PreParserExpression constructor, PreParserExpressionList properties, - int start_position, int end_position, PreParserFactory* factory) { - return PreParserExpression::Default(); - } - static PreParserExpression DefaultConstructor(bool call_super, PreParserScope* scope, int pos, int end_pos) { @@ -1387,6 +1377,11 @@ class PreParserTraits { int function_token_position, FunctionLiteral::FunctionType type, FunctionLiteral::ArityRestriction arity_restriction, bool* ok); + PreParserExpression ParseClassLiteral(PreParserIdentifier name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + private: PreParser* pre_parser_; }; @@ -1524,6 +1519,11 @@ class PreParser : public ParserBase { FunctionLiteral::ArityRestriction arity_restriction, bool* ok); void ParseLazyFunctionLiteralBody(bool* ok); + PreParserExpression ParseClassLiteral(PreParserIdentifier name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + bool CheckInOrOf(bool accept_OF); }; @@ -2722,78 +2722,6 @@ typename ParserBase::ExpressionT ParserBase< } -template -typename ParserBase::ExpressionT ParserBase::ParseClassLiteral( - IdentifierT name, Scanner::Location class_name_location, - bool name_is_strict_reserved, int pos, bool* ok) { - // All parts of a ClassDeclaration or a ClassExpression are strict code. - if (name_is_strict_reserved) { - ReportMessageAt(class_name_location, "unexpected_strict_reserved"); - *ok = false; - return this->EmptyExpression(); - } - if (this->IsEvalOrArguments(name)) { - ReportMessageAt(class_name_location, "strict_eval_arguments"); - *ok = false; - return this->EmptyExpression(); - } - - bool has_extends = false; - ExpressionT extends = this->EmptyExpression(); - if (Check(Token::EXTENDS)) { - typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE); - BlockState block_state(&scope_, Traits::Type::ptr_to_scope(scope)); - scope_->SetStrictMode(STRICT); - extends = this->ParseLeftHandSideExpression(CHECK_OK); - has_extends = true; - } - - // TODO(arv): Implement scopes and name binding in class body only. - typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE); - BlockState block_state(&scope_, Traits::Type::ptr_to_scope(scope)); - scope_->SetStrictMode(STRICT); - scope_->SetScopeName(name); - - typename Traits::Type::PropertyList properties = - this->NewPropertyList(4, zone_); - ExpressionT constructor = this->EmptyExpression(); - bool has_seen_constructor = false; - - Expect(Token::LBRACE, CHECK_OK); - while (peek() != Token::RBRACE) { - if (Check(Token::SEMICOLON)) continue; - if (fni_ != NULL) fni_->Enter(); - const bool in_class = true; - const bool is_static = false; - bool old_has_seen_constructor = has_seen_constructor; - ObjectLiteralPropertyT property = this->ParsePropertyDefinition( - NULL, in_class, is_static, &has_seen_constructor, CHECK_OK); - - if (has_seen_constructor != old_has_seen_constructor) { - constructor = this->GetPropertyValue(property); - } else { - properties->Add(property, zone()); - } - - if (fni_ != NULL) { - fni_->Infer(); - fni_->Leave(); - } - } - - int end_pos = peek_position(); - Expect(Token::RBRACE, CHECK_OK); - - if (!has_seen_constructor) { - constructor = - this->DefaultConstructor(has_extends, scope_, pos, end_pos + 1); - } - - return this->ClassExpression(name, extends, constructor, properties, pos, - end_pos + 1, factory()); -} - - template typename ParserBase::ExpressionT ParserBase::CheckAndRewriteReferenceExpression( diff --git a/test/mjsunit/harmony/classes.js b/test/mjsunit/harmony/classes.js index 1c08e92..4efdaa1 100644 --- a/test/mjsunit/harmony/classes.js +++ b/test/mjsunit/harmony/classes.js @@ -673,15 +673,98 @@ function assertAccessorDescriptor(object, name) { })(); -/* TODO(arv): Implement -(function TestNameBindingInConstructor() { +(function TestNameBindingConst() { + assertThrows('class C { constructor() { C = 42; } }', SyntaxError); + assertThrows('(class C { constructor() { C = 42; } })', SyntaxError); + assertThrows('class C { m() { C = 42; } }', SyntaxError); + assertThrows('(class C { m() { C = 42; } })', SyntaxError); + assertThrows('class C { get x() { C = 42; } }', SyntaxError); + assertThrows('(class C { get x() { C = 42; } })', SyntaxError); + assertThrows('class C { set x(_) { C = 42; } }', SyntaxError); + assertThrows('(class C { set x(_) { C = 42; } })', SyntaxError); +})(); + + +(function TestNameBinding() { + var C2; class C { constructor() { - assertThrows(function() { - C = 42; - }, ReferenceError); + C2 = C; + } + m() { + C2 = C; + } + get x() { + C2 = C; + } + set x(_) { + C2 = C; + } + } + new C(); + assertEquals(C, C2); + + C2 = undefined; + new C().m(); + assertEquals(C, C2); + + C2 = undefined; + new C().x; + assertEquals(C, C2); + + C2 = undefined; + new C().x = 1; + assertEquals(C, C2); +})(); + + +(function TestNameBindingExpression() { + var C3; + var C = class C2 { + constructor() { + assertEquals(C2, C); + C3 = C2; + } + m() { + assertEquals(C2, C); + C3 = C2; + } + get x() { + assertEquals(C2, C); + C3 = C2; + } + set x(_) { + assertEquals(C2, C); + C3 = C2; } } new C(); + assertEquals(C, C3); + + C3 = undefined; + new C().m(); + assertEquals(C, C3); + + C3 = undefined; + new C().x; + assertEquals(C, C3); + + C3 = undefined; + new C().x = 1; + assertEquals(C, C3); +})(); + + +(function TestNameBindingInExtendsExpression() { + assertThrows(function() { + class x extends x {} + }, ReferenceError); + + assertThrows(function() { + (class x extends x {}); + }, ReferenceError); + + assertThrows(function() { + var x = (class x extends x {}); + }, ReferenceError); })(); -*/