Classes: Implement correct name binding
authorarv <arv@chromium.org>
Fri, 14 Nov 2014 15:05:05 +0000 (07:05 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 14 Nov 2014 15:05:20 +0000 (15:05 +0000)
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}

src/ast-numbering.cc
src/ast.h
src/full-codegen.cc
src/full-codegen.h
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
test/mjsunit/harmony/classes.js

index 5ebbde8a5c3a76460858caae1301dd564bb11ca1..058921bc4a62eed68582e02eb6da4269c57e45b5 100644 (file)
@@ -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));
   }
index 65c4d4f546928d5989da640708ebedbc8712662e..3e12d54ff81a4dadf7faffb4833a9129ad485eb0 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -2641,6 +2641,8 @@ class ClassLiteral FINAL : public Expression {
 
   Handle<String> 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<Property*>* 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<Property*>* 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<Property*>* 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<ObjectLiteral::Property*>* 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,
index a993d4856b1e154a5924e71e80588e7993ef06e5..e32c59fc0746278c468de226c038445fc7aa2897 100644 (file)
@@ -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 __
 
 
index fd53ceaf613aaeed26e39e39a7bd1b6812a4906f..73a6c412ee9021cafc56e48af6fe6e0db28654b9 100644 (file)
@@ -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_;
index 99c93b88faffa57bcf087fdac07ad9010688cafa..b2cf3b1a6d385ee7d79b9b46bcda1dc2dd209346 100644 (file)
@@ -685,13 +685,6 @@ Expression* ParserTraits::SuperReference(Scope* scope, AstNodeFactory* factory,
       pos);
 }
 
-Expression* ParserTraits::ClassExpression(
-    const AstRawString* name, Expression* extends, Expression* constructor,
-    ZoneList<ObjectLiteral::Property*>* 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<ParserTraits>(&scanner_, parse_info->stack_limit,
                                info->extension(), NULL, info->zone(), this),
@@ -1993,8 +1994,8 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* 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<ObjectLiteral::Property*>* 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
index 44eb74fd20e8f06a8da641c72914ce0a8dd57654..e2afe56c667fd916f10f8a512c6a7bb968227a97 100644 (file)
@@ -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<ObjectLiteral::Property*>* 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<Statement*>* 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<ParserTraits> {
       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);
 
index 7ab9814bb41722de1be126ffd96313e523346379..1a2ddb61c560fa942bddf53381013be754d2a4bf 100644 (file)
@@ -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
index c44731279e734bf9387f9fe2111c481919e7af53..dd053615bfb70d3b7784a865c46d3902f10fede8 100644 (file)
@@ -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<PreParserTraits> {
       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<Traits>::ExpressionT ParserBase<
 }
 
 
-template <class Traits>
-typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::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 Traits>
 typename ParserBase<Traits>::ExpressionT
 ParserBase<Traits>::CheckAndRewriteReferenceExpression(
index 1c08e924c5e1cf29699c096959a388965c38f273..4efdaa179758d234a047597f2e42a4963a297c16 100644 (file)
@@ -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);
 })();
-*/