Class syntax parsing
authorarv@chromium.org <arv@chromium.org>
Tue, 16 Sep 2014 22:15:39 +0000 (22:15 +0000)
committerarv@chromium.org <arv@chromium.org>
Tue, 16 Sep 2014 22:15:39 +0000 (22:15 +0000)
This implements parsing for ClassExpression and ClassDeclaration.
The runtime is not yet implemented and the value is currently
hard coded to undefined.

BUG=v8:3330
LOG=Y
R=dslomov@chromium.org, marja@chromium.org, rossberg@chromium.org

Review URL: https://codereview.chromium.org/561913002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23988 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

20 files changed:
src/ast-value-factory.h
src/ast.cc
src/ast.h
src/compiler/ast-graph-builder.cc
src/flag-definitions.h
src/full-codegen.cc
src/hydrogen.cc
src/messages.js
src/objects.h
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/prettyprinter.cc
src/prettyprinter.h
src/scanner.cc
src/scanner.h
src/token.h
src/typing.cc
test/cctest/test-parsing.cc

index 4beff01..2f84163 100644 (file)
@@ -235,32 +235,33 @@ class AstValue : public ZoneObject {
 
 
 // For generating string constants.
-#define STRING_CONSTANTS(F) \
-  F(anonymous_function, "(anonymous function)") \
-  F(arguments, "arguments") \
-  F(done, "done") \
-  F(dot, ".") \
-  F(dot_for, ".for") \
-  F(dot_generator, ".generator") \
-  F(dot_generator_object, ".generator_object") \
-  F(dot_iterator, ".iterator") \
-  F(dot_module, ".module") \
-  F(dot_result, ".result") \
-  F(empty, "") \
-  F(eval, "eval") \
+#define STRING_CONSTANTS(F)                           \
+  F(anonymous_function, "(anonymous function)")       \
+  F(arguments, "arguments")                           \
+  F(constructor, "constructor")                       \
+  F(done, "done")                                     \
+  F(dot, ".")                                         \
+  F(dot_for, ".for")                                  \
+  F(dot_generator, ".generator")                      \
+  F(dot_generator_object, ".generator_object")        \
+  F(dot_iterator, ".iterator")                        \
+  F(dot_module, ".module")                            \
+  F(dot_result, ".result")                            \
+  F(empty, "")                                        \
+  F(eval, "eval")                                     \
   F(initialize_const_global, "initializeConstGlobal") \
-  F(initialize_var_global, "initializeVarGlobal") \
-  F(make_reference_error, "MakeReferenceError") \
-  F(make_syntax_error, "MakeSyntaxError") \
-  F(make_type_error, "MakeTypeError") \
-  F(module, "module") \
-  F(native, "native") \
-  F(next, "next") \
-  F(proto, "__proto__") \
-  F(prototype, "prototype") \
-  F(this, "this") \
-  F(use_asm, "use asm") \
-  F(use_strict, "use strict") \
+  F(initialize_var_global, "initializeVarGlobal")     \
+  F(make_reference_error, "MakeReferenceError")       \
+  F(make_syntax_error, "MakeSyntaxError")             \
+  F(make_type_error, "MakeTypeError")                 \
+  F(module, "module")                                 \
+  F(native, "native")                                 \
+  F(next, "next")                                     \
+  F(proto, "__proto__")                               \
+  F(prototype, "prototype")                           \
+  F(this, "this")                                     \
+  F(use_asm, "use asm")                               \
+  F(use_strict, "use strict")                         \
   F(value, "value")
 
 
index 66d02e5..6816992 100644 (file)
@@ -173,10 +173,12 @@ void FunctionLiteral::InitializeSharedInfo(
 
 ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
                                              AstValueFactory* ast_value_factory,
-                                             Literal* key, Expression* value) {
+                                             Literal* key, Expression* value,
+                                             bool is_static) {
   emit_store_ = true;
   key_ = key;
   value_ = value;
+  is_static_ = is_static;
   if (key->raw_value()->EqualsString(ast_value_factory->proto_string())) {
     kind_ = PROTOTYPE;
   } else if (value_->AsMaterializedLiteral() != NULL) {
@@ -189,11 +191,13 @@ ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
 }
 
 
-ObjectLiteralProperty::ObjectLiteralProperty(
-    Zone* zone, bool is_getter, FunctionLiteral* value) {
+ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone, bool is_getter,
+                                             FunctionLiteral* value,
+                                             bool is_static) {
   emit_store_ = true;
   value_ = value;
   kind_ = is_getter ? GETTER : SETTER;
+  is_static_ = is_static;
 }
 
 
@@ -1090,6 +1094,7 @@ DONT_OPTIMIZE_NODE(ModuleUrl)
 DONT_OPTIMIZE_NODE(ModuleStatement)
 DONT_OPTIMIZE_NODE(WithStatement)
 DONT_OPTIMIZE_NODE(DebuggerStatement)
+DONT_OPTIMIZE_NODE(ClassLiteral)
 DONT_OPTIMIZE_NODE(NativeFunctionLiteral)
 DONT_OPTIMIZE_NODE(SuperReference)
 
index 2e3b360..394e7f9 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -40,12 +40,12 @@ namespace internal {
 // Nodes of the abstract syntax tree. Only concrete classes are
 // enumerated here.
 
-#define DECLARATION_NODE_LIST(V)                \
-  V(VariableDeclaration)                        \
-  V(FunctionDeclaration)                        \
-  V(ModuleDeclaration)                          \
-  V(ImportDeclaration)                          \
-  V(ExportDeclaration)                          \
+#define DECLARATION_NODE_LIST(V) \
+  V(VariableDeclaration)         \
+  V(FunctionDeclaration)         \
+  V(ModuleDeclaration)           \
+  V(ImportDeclaration)           \
+  V(ExportDeclaration)
 
 #define MODULE_NODE_LIST(V)                     \
   V(ModuleLiteral)                              \
@@ -73,28 +73,29 @@ namespace internal {
   V(TryFinallyStatement)                        \
   V(DebuggerStatement)
 
-#define EXPRESSION_NODE_LIST(V)                 \
-  V(FunctionLiteral)                            \
-  V(NativeFunctionLiteral)                      \
-  V(Conditional)                                \
-  V(VariableProxy)                              \
-  V(Literal)                                    \
-  V(RegExpLiteral)                              \
-  V(ObjectLiteral)                              \
-  V(ArrayLiteral)                               \
-  V(Assignment)                                 \
-  V(Yield)                                      \
-  V(Throw)                                      \
-  V(Property)                                   \
-  V(Call)                                       \
-  V(CallNew)                                    \
-  V(CallRuntime)                                \
-  V(UnaryOperation)                             \
-  V(CountOperation)                             \
-  V(BinaryOperation)                            \
-  V(CompareOperation)                           \
-  V(ThisFunction)                               \
-  V(SuperReference)                             \
+#define EXPRESSION_NODE_LIST(V) \
+  V(FunctionLiteral)            \
+  V(ClassLiteral)               \
+  V(NativeFunctionLiteral)      \
+  V(Conditional)                \
+  V(VariableProxy)              \
+  V(Literal)                    \
+  V(RegExpLiteral)              \
+  V(ObjectLiteral)              \
+  V(ArrayLiteral)               \
+  V(Assignment)                 \
+  V(Yield)                      \
+  V(Throw)                      \
+  V(Property)                   \
+  V(Call)                       \
+  V(CallNew)                    \
+  V(CallRuntime)                \
+  V(UnaryOperation)             \
+  V(CountOperation)             \
+  V(BinaryOperation)            \
+  V(CompareOperation)           \
+  V(ThisFunction)               \
+  V(SuperReference)             \
   V(CaseClause)
 
 #define AST_NODE_LIST(V)                        \
@@ -1459,7 +1460,7 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
   };
 
   ObjectLiteralProperty(Zone* zone, AstValueFactory* ast_value_factory,
-                        Literal* key, Expression* value);
+                        Literal* key, Expression* value, bool is_static);
 
   Literal* key() { return key_; }
   Expression* value() { return value_; }
@@ -1478,7 +1479,8 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
  protected:
   template<class> friend class AstNodeFactory;
 
-  ObjectLiteralProperty(Zone* zone, bool is_getter, FunctionLiteral* value);
+  ObjectLiteralProperty(Zone* zone, bool is_getter, FunctionLiteral* value,
+                        bool is_static);
   void set_key(Literal* key) { key_ = key; }
 
  private:
@@ -1486,6 +1488,7 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
   Expression* value_;
   Kind kind_;
   bool emit_store_;
+  bool is_static_;
   Handle<Map> receiver_type_;
 };
 
@@ -2498,6 +2501,40 @@ class FunctionLiteral FINAL : public Expression {
 };
 
 
+class ClassLiteral FINAL : public Expression {
+ public:
+  typedef ObjectLiteralProperty Property;
+
+  DECLARE_NODE_TYPE(ClassLiteral)
+
+  Handle<String> name() const { return raw_name_->string(); }
+  const AstRawString* raw_name() const { return raw_name_; }
+  Expression* extends() const { return extends_; }
+  FunctionLiteral* constructor() const { return constructor_; }
+  ZoneList<Property*>* properties() const { return properties_; }
+
+ protected:
+  ClassLiteral(Zone* zone, const AstRawString* name, Expression* extends,
+               FunctionLiteral* constructor, ZoneList<Property*>* properties,
+               AstValueFactory* ast_value_factory, int position, IdGen* id_gen)
+      : Expression(zone, position, id_gen),
+        raw_name_(name),
+        raw_inferred_name_(ast_value_factory->empty_string()),
+        extends_(extends),
+        constructor_(constructor),
+        properties_(properties) {}
+
+ private:
+  const AstRawString* raw_name_;
+  Handle<String> name_;
+  const AstString* raw_inferred_name_;
+  Handle<String> inferred_name_;
+  Expression* extends_;
+  FunctionLiteral* constructor_;
+  ZoneList<Property*>* properties_;
+};
+
+
 class NativeFunctionLiteral FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(NativeFunctionLiteral)
@@ -3300,16 +3337,17 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
   }
 
   ObjectLiteral::Property* NewObjectLiteralProperty(Literal* key,
-                                                    Expression* value) {
-    return new (zone_)
-        ObjectLiteral::Property(zone_, ast_value_factory_, key, value);
+                                                    Expression* value,
+                                                    bool is_static) {
+    return new (zone_) ObjectLiteral::Property(zone_, ast_value_factory_, key,
+                                               value, is_static);
   }
 
   ObjectLiteral::Property* NewObjectLiteralProperty(bool is_getter,
                                                     FunctionLiteral* value,
-                                                    int pos) {
+                                                    int pos, bool is_static) {
     ObjectLiteral::Property* prop =
-        new(zone_) ObjectLiteral::Property(zone_, is_getter, value);
+        new (zone_) ObjectLiteral::Property(zone_, is_getter, value, is_static);
     prop->set_key(NewStringLiteral(value->raw_name(), pos));
     return prop;  // Not an AST node, will not be visited.
   }
@@ -3465,6 +3503,17 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
     return lit;
   }
 
+  ClassLiteral* NewClassLiteral(const AstRawString* name, Expression* extends,
+                                FunctionLiteral* constructor,
+                                ZoneList<ObjectLiteral::Property*>* properties,
+                                AstValueFactory* ast_value_factory,
+                                int position) {
+    ClassLiteral* lit =
+        new (zone_) ClassLiteral(zone_, name, extends, constructor, properties,
+                                 ast_value_factory, position, id_gen_);
+    VISIT_AND_RETURN(ClassLiteral, lit)
+  }
+
   NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
                                                   v8::Extension* extension,
                                                   int pos) {
index 8d2a9bf..5c61471 100644 (file)
@@ -812,6 +812,12 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
 }
 
 
+void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
+  // TODO(arv): Implement.
+  UNREACHABLE();
+}
+
+
 void AstGraphBuilder::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
   UNREACHABLE();
 }
index 772d73b..4208e62 100644 (file)
@@ -175,6 +175,8 @@ DEFINE_IMPLICATION(harmony, harmony_arrow_functions)
 DEFINE_IMPLICATION(harmony, harmony_classes)
 DEFINE_IMPLICATION(harmony, harmony_object_literals)
 DEFINE_IMPLICATION(harmony_modules, harmony_scoping)
+DEFINE_IMPLICATION(harmony_classes, harmony_scoping)
+DEFINE_IMPLICATION(harmony_classes, harmony_object_literals)
 
 DEFINE_IMPLICATION(harmony, es_staging)
 
index 40e8c1d..c1642b1 100644 (file)
@@ -33,18 +33,22 @@ void BreakableStatementChecker::VisitVariableDeclaration(
     VariableDeclaration* decl) {
 }
 
+
 void BreakableStatementChecker::VisitFunctionDeclaration(
     FunctionDeclaration* decl) {
 }
 
+
 void BreakableStatementChecker::VisitModuleDeclaration(
     ModuleDeclaration* decl) {
 }
 
+
 void BreakableStatementChecker::VisitImportDeclaration(
     ImportDeclaration* decl) {
 }
 
+
 void BreakableStatementChecker::VisitExportDeclaration(
     ExportDeclaration* decl) {
 }
@@ -178,6 +182,13 @@ void BreakableStatementChecker::VisitFunctionLiteral(FunctionLiteral* expr) {
 }
 
 
+void BreakableStatementChecker::VisitClassLiteral(ClassLiteral* expr) {
+  if (expr->extends() != NULL) {
+    Visit(expr->extends());
+  }
+}
+
+
 void BreakableStatementChecker::VisitNativeFunctionLiteral(
     NativeFunctionLiteral* expr) {
 }
@@ -1531,6 +1542,16 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
 }
 
 
+void FullCodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
+  // TODO(arv): Implement
+  Comment cmnt(masm_, "[ ClassLiteral");
+  if (expr->extends() != NULL) {
+    VisitForEffect(expr->extends());
+  }
+  context()->Plug(isolate()->factory()->undefined_value());
+}
+
+
 void FullCodeGenerator::VisitNativeFunctionLiteral(
     NativeFunctionLiteral* expr) {
   Comment cmnt(masm_, "[ NativeFunctionLiteral");
index c5f8dc1..3c0705c 100644 (file)
@@ -5248,6 +5248,14 @@ void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
 }
 
 
+void HOptimizedGraphBuilder::VisitClassLiteral(ClassLiteral* lit) {
+  DCHECK(!HasStackOverflow());
+  DCHECK(current_block() != NULL);
+  DCHECK(current_block()->HasPredecessor());
+  return Bailout(kClassLiteral);
+}
+
+
 void HOptimizedGraphBuilder::VisitNativeFunctionLiteral(
     NativeFunctionLiteral* expr) {
   DCHECK(!HasStackOverflow());
index c86c12d..2fd5b38 100644 (file)
@@ -8,6 +8,7 @@ var kMessages = {
   // Error
   cyclic_proto:                  ["Cyclic __proto__ value"],
   code_gen_from_strings:         ["%0"],
+  constructor_special_method:    ["Class constructor may not be an accessor"],
   generator_running:             ["Generator is already running"],
   generator_finished:            ["Generator has already finished"],
   // TypeError
@@ -139,6 +140,7 @@ var kMessages = {
   array_indexof_not_defined:     ["Array.getIndexOf: Argument undefined"],
   object_not_extensible:         ["Can't add property ", "%0", ", object is not extensible"],
   illegal_access:                ["Illegal access"],
+  static_prototype:              ["Classes may not have static property named prototype"],
   strict_mode_with:              ["Strict mode code may not include a with statement"],
   strict_eval_arguments:         ["Unexpected eval or arguments in strict mode"],
   too_many_arguments:            ["Too many arguments in function call (only 65535 allowed)"],
index 8203b36..5e46502 100644 (file)
@@ -1016,6 +1016,7 @@ template <class C> inline bool Is(Object* obj);
     "Call to a JavaScript runtime function")                                   \
   V(kCannotTranslatePositionInChangedArea,                                     \
     "Cannot translate position in changed area")                               \
+  V(kClassLiteral, "Class literal")                                            \
   V(kCodeGenerationFailed, "Code generation failed")                           \
   V(kCodeObjectNotProperlyPatched, "Code object not properly patched")         \
   V(kCompoundAssignmentToLookupSlot, "Compound assignment to lookup slot")     \
index 8d69b6e..2e552e1 100644 (file)
@@ -366,6 +366,16 @@ bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const {
 }
 
 
+bool ParserTraits::IsPrototype(const AstRawString* identifier) const {
+  return identifier == parser_->ast_value_factory()->prototype_string();
+}
+
+
+bool ParserTraits::IsConstructor(const AstRawString* identifier) const {
+  return identifier == parser_->ast_value_factory()->constructor_string();
+}
+
+
 bool ParserTraits::IsThisProperty(Expression* expression) {
   DCHECK(expression != NULL);
   Property* property = expression->AsProperty();
@@ -1136,6 +1146,8 @@ Statement* Parser::ParseModuleElement(ZoneList<const AstRawString*>* labels,
   switch (peek()) {
     case Token::FUNCTION:
       return ParseFunctionDeclaration(NULL, ok);
+    case Token::CLASS:
+      return ParseClassDeclaration(NULL, ok);
     case Token::IMPORT:
       return ParseImportDeclaration(ok);
     case Token::EXPORT:
@@ -1475,6 +1487,10 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
       result = ParseFunctionDeclaration(&names, CHECK_OK);
       break;
 
+    case Token::CLASS:
+      result = ParseClassDeclaration(&names, CHECK_OK);
+      break;
+
     case Token::VAR:
     case Token::LET:
     case Token::CONST:
@@ -1537,10 +1553,13 @@ Statement* Parser::ParseBlockElement(ZoneList<const AstRawString*>* labels,
   //    LetDeclaration
   //    ConstDeclaration
   //    GeneratorDeclaration
+  //    ClassDeclaration
 
   switch (peek()) {
     case Token::FUNCTION:
       return ParseFunctionDeclaration(NULL, ok);
+    case Token::CLASS:
+      return ParseClassDeclaration(NULL, ok);
     case Token::CONST:
       return ParseVariableStatement(kModuleElement, NULL, ok);
     case Token::LET:
@@ -1652,6 +1671,9 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
       return ParseFunctionDeclaration(NULL, ok);
     }
 
+    case Token::CLASS:
+      return ParseClassDeclaration(NULL, ok);
+
     case Token::DEBUGGER:
       return ParseDebuggerStatement(ok);
 
@@ -1920,6 +1942,47 @@ Statement* Parser::ParseFunctionDeclaration(
 }
 
 
+Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
+                                         bool* ok) {
+  // ClassDeclaration ::
+  //   'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
+  //
+  // A ClassDeclaration
+  //
+  //   class C { ... }
+  //
+  // has the same semantics as:
+  //
+  //   let C = class C { ... };
+  //
+  // so rewrite it as such.
+
+  Expect(Token::CLASS, CHECK_OK);
+  int pos = position();
+  bool is_strict_reserved = false;
+  const AstRawString* name =
+      ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
+  ClassLiteral* value = ParseClassLiteral(name, scanner()->location(),
+                                          is_strict_reserved, pos, CHECK_OK);
+
+  Block* block = factory()->NewBlock(NULL, 1, true, pos);
+  VariableMode mode = LET;
+  VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue());
+  Declaration* declaration =
+      factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
+  Declare(declaration, true, CHECK_OK);
+
+  Token::Value init_op = Token::INIT_LET;
+  Assignment* assignment = factory()->NewAssignment(init_op, proxy, value, pos);
+  block->AddStatement(
+      factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
+      zone());
+
+  if (names) names->Add(name, zone());
+  return block;
+}
+
+
 Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok) {
   if (allow_harmony_scoping() && strict_mode() == STRICT) {
     return ParseScopedBlock(labels, ok);
index b5bf239..ab9c61a 100644 (file)
@@ -363,6 +363,7 @@ class ParserTraits {
     typedef v8::internal::Expression* Expression;
     typedef Yield* YieldExpression;
     typedef v8::internal::FunctionLiteral* FunctionLiteral;
+    typedef v8::internal::ClassLiteral* ClassLiteral;
     typedef v8::internal::Literal* Literal;
     typedef ObjectLiteral::Property* ObjectLiteralProperty;
     typedef ZoneList<v8::internal::Expression*>* ExpressionList;
@@ -401,6 +402,10 @@ class ParserTraits {
 
   static bool IsIdentifier(Expression* expression);
 
+  bool IsPrototype(const AstRawString* identifier) const;
+
+  bool IsConstructor(const AstRawString* identifier) const;
+
   static const AstRawString* AsIdentifier(Expression* expression) {
     DCHECK(IsIdentifier(expression));
     return expression->AsVariableProxy()->raw_name();
@@ -518,6 +523,8 @@ class ParserTraits {
     return NULL;
   }
   static ObjectLiteralProperty* EmptyObjectLiteralProperty() { return NULL; }
+  static FunctionLiteral* EmptyFunctionLiteral() { return NULL; }
+  static ClassLiteral* EmptyClassLiteral() { return NULL; }
 
   // Used in error return values.
   static ZoneList<Expression*>* NullExpressionList() {
@@ -705,6 +712,8 @@ class Parser : public ParserBase<ParserTraits> {
   Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok);
   Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
                                       bool* ok);
+  Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names,
+                                   bool* ok);
   Statement* ParseNativeDeclaration(bool* ok);
   Block* ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok);
   Block* ParseVariableStatement(VariableDeclarationContext var_context,
index a60742a..3173cc0 100644 (file)
@@ -78,6 +78,12 @@ PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) {
   if (scanner->UnescapedLiteralMatches("arguments", 9)) {
     return PreParserIdentifier::Arguments();
   }
+  if (scanner->UnescapedLiteralMatches("prototype", 9)) {
+    return PreParserIdentifier::Prototype();
+  }
+  if (scanner->UnescapedLiteralMatches("constructor", 11)) {
+    return PreParserIdentifier::Constructor();
+  }
   return PreParserIdentifier::Default();
 }
 
@@ -178,6 +184,8 @@ PreParser::Statement PreParser::ParseSourceElement(bool* ok) {
   switch (peek()) {
     case Token::FUNCTION:
       return ParseFunctionDeclaration(ok);
+    case Token::CLASS:
+      return ParseClassDeclaration(ok);
     case Token::CONST:
       return ParseVariableStatement(kSourceElement, ok);
     case Token::LET:
@@ -305,6 +313,9 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
       }
     }
 
+    case Token::CLASS:
+      return ParseClassDeclaration(CHECK_OK);
+
     case Token::DEBUGGER:
       return ParseDebuggerStatement(ok);
 
@@ -345,6 +356,18 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
 }
 
 
+PreParser::Statement PreParser::ParseClassDeclaration(bool* ok) {
+  Expect(Token::CLASS, CHECK_OK);
+  int pos = position();
+  bool is_strict_reserved = false;
+  Identifier name =
+      ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
+  ParseClassLiteral(name, scanner()->location(), is_strict_reserved, pos,
+                    CHECK_OK);
+  return Statement::Default();
+}
+
+
 PreParser::Statement PreParser::ParseBlock(bool* ok) {
   // Block ::
   //   '{' Statement* '}'
index b24c072..0147caf 100644 (file)
@@ -46,6 +46,7 @@ namespace internal {
 //     typedef Identifier;
 //     typedef Expression;
 //     typedef FunctionLiteral;
+//     typedef ClassLiteral;
 //     typedef ObjectLiteralProperty;
 //     typedef Literal;
 //     typedef ExpressionList;
@@ -63,6 +64,7 @@ class ParserBase : public Traits {
   typedef typename Traits::Type::Expression ExpressionT;
   typedef typename Traits::Type::Identifier IdentifierT;
   typedef typename Traits::Type::FunctionLiteral FunctionLiteralT;
+  typedef typename Traits::Type::ClassLiteral ClassLiteralT;
   typedef typename Traits::Type::Literal LiteralT;
   typedef typename Traits::Type::ObjectLiteralProperty ObjectLiteralPropertyT;
 
@@ -480,10 +482,12 @@ class ParserBase : public Traits {
   ExpressionT ParsePrimaryExpression(bool* ok);
   ExpressionT ParseExpression(bool accept_IN, bool* ok);
   ExpressionT ParseArrayLiteral(bool* ok);
+  IdentifierT ParsePropertyName(bool* is_get, bool* is_set, bool* is_static,
+                                bool* ok);
   ExpressionT ParseObjectLiteral(bool* ok);
   ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker,
+                                                 bool in_class, bool is_static,
                                                  bool* ok);
-  IdentifierT ParsePropertyName(bool* is_getter, bool* is_setter, bool* ok);
   typename Traits::Type::ExpressionList ParseArguments(bool* ok);
   ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
   ExpressionT ParseYieldExpression(bool* ok);
@@ -498,6 +502,10 @@ class ParserBase : public Traits {
                                                 bool* ok);
   ExpressionT ParseArrowFunctionLiteral(int start_pos, ExpressionT params_ast,
                                         bool* ok);
+  ClassLiteralT 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,
@@ -612,10 +620,20 @@ class PreParserIdentifier {
   static PreParserIdentifier Yield() {
     return PreParserIdentifier(kYieldIdentifier);
   }
+  static PreParserIdentifier Prototype() {
+    return PreParserIdentifier(kPrototypeIdentifier);
+  }
+  static PreParserIdentifier Constructor() {
+    return PreParserIdentifier(kConstructorIdentifier);
+  }
   bool IsEval() const { return type_ == kEvalIdentifier; }
   bool IsArguments() const { return type_ == kArgumentsIdentifier; }
-  bool IsEvalOrArguments() const { return type_ >= kEvalIdentifier; }
   bool IsYield() const { return type_ == kYieldIdentifier; }
+  bool IsPrototype() const { return type_ == kPrototypeIdentifier; }
+  bool IsConstructor() const { return type_ == kConstructorIdentifier; }
+  bool IsEvalOrArguments() const {
+    return type_ == kEvalIdentifier || type_ == kArgumentsIdentifier;
+  }
   bool IsFutureReserved() const { return type_ == kFutureReservedIdentifier; }
   bool IsFutureStrictReserved() const {
     return type_ == kFutureStrictReservedIdentifier;
@@ -638,7 +656,9 @@ class PreParserIdentifier {
     kLetIdentifier,
     kYieldIdentifier,
     kEvalIdentifier,
-    kArgumentsIdentifier
+    kArgumentsIdentifier,
+    kPrototypeIdentifier,
+    kConstructorIdentifier
   };
   explicit PreParserIdentifier(Type type) : type_(type) {}
   Type type_;
@@ -927,6 +947,7 @@ class PreParserScope {
   ScopeType type() { return scope_type_; }
   StrictMode strict_mode() const { return strict_mode_; }
   void SetStrictMode(StrictMode strict_mode) { strict_mode_ = strict_mode; }
+  void SetScopeName(PreParserIdentifier name) {}
 
   // When PreParser is in use, lazy compilation is already being done,
   // things cannot get lazier than that.
@@ -971,11 +992,12 @@ class PreParserFactory {
   }
   PreParserExpression NewObjectLiteralProperty(bool is_getter,
                                                PreParserExpression value,
-                                               int pos) {
+                                               int pos, bool is_static) {
     return PreParserExpression::Default();
   }
   PreParserExpression NewObjectLiteralProperty(PreParserExpression key,
-                                               PreParserExpression value) {
+                                               PreParserExpression value,
+                                               bool is_static) {
     return PreParserExpression::Default();
   }
   PreParserExpression NewObjectLiteral(PreParserExpressionList properties,
@@ -985,7 +1007,7 @@ class PreParserFactory {
                                        int pos) {
     return PreParserExpression::Default();
   }
-  PreParserExpression NewVariableProxy(void* generator_variable) {
+  PreParserExpression NewVariableProxy(void* variable) {
     return PreParserExpression::Default();
   }
   PreParserExpression NewProperty(PreParserExpression obj,
@@ -1061,6 +1083,14 @@ class PreParserFactory {
       int position) {
     return PreParserExpression::Default();
   }
+  PreParserExpression NewClassLiteral(PreParserIdentifier name,
+                                      PreParserExpression extends,
+                                      PreParserExpression constructor,
+                                      PreParserExpressionList properties,
+                                      AstValueFactory* ast_value_factory,
+                                      int position) {
+    return PreParserExpression::Default();
+  }
 
   // Return the object itself as AstVisitor and implement the needed
   // dummy method right in this class.
@@ -1099,6 +1129,7 @@ class PreParserTraits {
     typedef PreParserExpression Expression;
     typedef PreParserExpression YieldExpression;
     typedef PreParserExpression FunctionLiteral;
+    typedef PreParserExpression ClassLiteral;
     typedef PreParserExpression ObjectLiteralProperty;
     typedef PreParserExpression Literal;
     typedef PreParserExpressionList ExpressionList;
@@ -1125,6 +1156,14 @@ class PreParserTraits {
     return identifier.IsEvalOrArguments();
   }
 
+  static bool IsPrototype(PreParserIdentifier identifier) {
+    return identifier.IsPrototype();
+  }
+
+  static bool IsConstructor(PreParserIdentifier identifier) {
+    return identifier.IsConstructor();
+  }
+
   // Returns true if the expression is of type "this.foo".
   static bool IsThisProperty(PreParserExpression expression) {
     return expression.IsThisProperty();
@@ -1245,6 +1284,12 @@ class PreParserTraits {
   static PreParserExpression EmptyObjectLiteralProperty() {
     return PreParserExpression::Default();
   }
+  static PreParserExpression EmptyFunctionLiteral() {
+    return PreParserExpression::Default();
+  }
+  static PreParserExpression EmptyClassLiteral() {
+    return PreParserExpression::Default();
+  }
   static PreParserExpressionList NullExpressionList() {
     return PreParserExpressionList();
   }
@@ -1435,6 +1480,7 @@ class PreParser : public ParserBase<PreParserTraits> {
   SourceElements ParseSourceElements(int end_token, bool* ok);
   Statement ParseStatement(bool* ok);
   Statement ParseFunctionDeclaration(bool* ok);
+  Statement ParseClassDeclaration(bool* ok);
   Statement ParseBlock(bool* ok);
   Statement ParseVariableStatement(VariableDeclarationContext var_context,
                                    bool* ok);
@@ -1708,6 +1754,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
   //   ArrayLiteral
   //   ObjectLiteral
   //   RegExpLiteral
+  //   ClassLiteral
   //   '(' Expression ')'
 
   int pos = peek_position();
@@ -1778,6 +1825,23 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
       }
       break;
 
+    case Token::CLASS: {
+      Consume(Token::CLASS);
+      int class_token_position = position();
+      IdentifierT name = this->EmptyIdentifier();
+      bool is_strict_reserved_name = false;
+      Scanner::Location class_name_location = Scanner::Location::invalid();
+      if (peek_any_identifier()) {
+        name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name,
+                                                   CHECK_OK);
+        class_name_location = scanner()->location();
+      }
+      result = this->ParseClassLiteral(name, class_name_location,
+                                       is_strict_reserved_name,
+                                       class_token_position, CHECK_OK);
+      break;
+    }
+
     case Token::MOD:
       if (allow_natives_syntax() || extension_ != NULL) {
         result = this->ParseV8Intrinsic(CHECK_OK);
@@ -1848,7 +1912,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
 
 template <class Traits>
 typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName(
-    bool* is_getter, bool* is_setter, bool* ok) {
+    bool* is_get, bool* is_set, bool* is_static, bool* ok) {
   Token::Value next = peek();
   switch (next) {
     case Token::STRING:
@@ -1857,27 +1921,35 @@ typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName(
     case Token::NUMBER:
       Consume(Token::NUMBER);
       return this->GetNumberAsSymbol(scanner_);
+    case Token::STATIC:
+      *is_static = true;
+      // Fall through.
     default:
-      return ParseIdentifierNameOrGetOrSet(is_getter, is_setter,
-                                           CHECK_OK_CUSTOM(EmptyIdentifier));
+      return ParseIdentifierNameOrGetOrSet(is_get, is_set, ok);
   }
+  UNREACHABLE();
+  return this->EmptyIdentifier();
 }
 
 
 template <class Traits>
 typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
-    Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker, bool* ok) {
+    Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker,
+                                     bool in_class, bool is_static, bool* ok) {
   // TODO(arv): Add support for concise generator methods.
   ExpressionT value = this->EmptyExpression();
-  bool is_getter = false;
-  bool is_setter = false;
+  bool is_get = false;
+  bool is_set = false;
+  bool name_is_static = false;
   Token::Value name_token = peek();
   int next_pos = peek_position();
-  IdentifierT name = ParsePropertyName(
-      &is_getter, &is_setter, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+  IdentifierT name =
+      ParsePropertyName(&is_get, &is_set, &name_is_static,
+                        CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+
   if (fni_ != NULL) this->PushLiteralName(fni_, name);
 
-  if (peek() == Token::COLON) {
+  if (!in_class && peek() == Token::COLON) {
     // PropertyDefinition : PropertyName ':' AssignmentExpression
     checker->CheckProperty(name_token, kValueProperty,
                            CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
@@ -1887,6 +1959,13 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
 
   } else if (allow_harmony_object_literals_ && peek() == Token::LPAREN) {
     // Concise Method
+
+    if (is_static && this->IsPrototype(name)) {
+      ReportMessageAt(scanner()->location(), "static_prototype");
+      *ok = false;
+      return this->EmptyObjectLiteralProperty();
+    }
+
     checker->CheckProperty(name_token, kValueProperty,
                            CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
     value = this->ParseFunctionLiteral(
@@ -1896,25 +1975,43 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
         FunctionLiteral::ANONYMOUS_EXPRESSION, FunctionLiteral::NORMAL_ARITY,
         CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
 
-  } else if (is_getter || is_setter) {
+  } else if (in_class && name_is_static && !is_static) {
+    // static MethodDefinition
+    return ParsePropertyDefinition(checker, true, true, ok);
+
+  } else if (is_get || is_set) {
     // Accessor
     bool dont_care = false;
     name_token = peek();
-    name = ParsePropertyName(&dont_care, &dont_care,
+    name = ParsePropertyName(&dont_care, &dont_care, &dont_care,
                              CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+
     // Validate the property.
+    if (is_static && this->IsPrototype(name)) {
+      ReportMessageAt(scanner()->location(), "static_prototype");
+      *ok = false;
+      return this->EmptyObjectLiteralProperty();
+    } else if (in_class && !is_static && this->IsConstructor(name)) {
+      // ES6, spec draft rev 27, treats static get constructor as an error too.
+      // https://bugs.ecmascript.org/show_bug.cgi?id=3223
+      // TODO(arv): Update when bug is resolved.
+      ReportMessageAt(scanner()->location(), "constructor_special_method");
+      *ok = false;
+      return this->EmptyObjectLiteralProperty();
+    }
     checker->CheckProperty(name_token,
-                           is_getter ? kGetterProperty : kSetterProperty,
+                           is_get ? kGetterProperty : kSetterProperty,
                            CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+
     typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral(
         name, scanner()->location(),
         false,  // reserved words are allowed here
         FunctionKind::kNormalFunction, RelocInfo::kNoPosition,
         FunctionLiteral::ANONYMOUS_EXPRESSION,
-        is_getter ? FunctionLiteral::GETTER_ARITY
-                  : FunctionLiteral::SETTER_ARITY,
+        is_get ? FunctionLiteral::GETTER_ARITY : FunctionLiteral::SETTER_ARITY,
         CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
-    return factory()->NewObjectLiteralProperty(is_getter, value, next_pos);
+    return factory()->NewObjectLiteralProperty(is_get, value, next_pos,
+                                               is_static);
   } else {
     Token::Value next = Next();
     ReportUnexpectedToken(next);
@@ -1927,7 +2024,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
                      ? factory()->NewNumberLiteral(index, next_pos)
                      : factory()->NewStringLiteral(name, next_pos);
 
-  return factory()->NewObjectLiteralProperty(key, value);
+  return factory()->NewObjectLiteralProperty(key, value, is_static);
 }
 
 
@@ -1950,8 +2047,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
   while (peek() != Token::RBRACE) {
     if (fni_ != NULL) fni_->Enter();
 
+    const bool in_class = false;
+    const bool is_static = false;
     ObjectLiteralPropertyT property =
-        this->ParsePropertyDefinition(&checker, CHECK_OK);
+        this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK);
 
     // Mark top-level object literals that contain function literals and
     // pretenure the literal so it can be added as a constant function
@@ -2400,7 +2499,7 @@ template <class Traits>
 typename ParserBase<Traits>::ExpressionT
 ParserBase<Traits>::ParseMemberExpression(bool* ok) {
   // MemberExpression ::
-  //   (PrimaryExpression | FunctionLiteral)
+  //   (PrimaryExpression | FunctionLiteral | ClassLiteral)
   //     ('[' Expression ']' | '.' Identifier | Arguments)*
 
   // The '[' Expression ']' and '.' Identifier parts are parsed by
@@ -2603,6 +2702,69 @@ typename ParserBase<Traits>::ExpressionT ParserBase<
 }
 
 
+template <class Traits>
+typename ParserBase<Traits>::ClassLiteralT
+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->EmptyClassLiteral();
+  }
+  if (this->IsEvalOrArguments(name)) {
+    ReportMessageAt(class_name_location, "strict_eval_arguments");
+    *ok = false;
+    return this->EmptyClassLiteral();
+  }
+
+  // TODO(arv): Implement scopes and name binding in class body only.
+  // TODO(arv): Maybe add CLASS_SCOPE?
+  typename Traits::Type::ScopePtr extends_scope =
+      this->NewScope(scope_, BLOCK_SCOPE);
+  FunctionState extends_function_state(
+      &function_state_, &scope_, &extends_scope, zone(),
+      this->ast_value_factory(), ast_node_id_gen_);
+  scope_->SetStrictMode(STRICT);
+  scope_->SetScopeName(name);
+
+  ExpressionT extends = this->EmptyExpression();
+  if (Check(Token::EXTENDS)) {
+    extends =
+        this->ParseLeftHandSideExpression(CHECK_OK_CUSTOM(EmptyClassLiteral));
+  }
+
+  ObjectLiteralChecker checker(this, STRICT);
+  typename Traits::Type::PropertyList properties =
+      this->NewPropertyList(4, zone_);
+  FunctionLiteralT constructor = this->EmptyFunctionLiteral();
+
+  Expect(Token::LBRACE, CHECK_OK_CUSTOM(EmptyClassLiteral));
+  while (peek() != Token::RBRACE) {
+    if (Check(Token::SEMICOLON)) continue;
+    if (fni_ != NULL) fni_->Enter();
+
+    const bool in_class = true;
+    const bool is_static = false;
+    ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
+        &checker, in_class, is_static, CHECK_OK_CUSTOM(EmptyClassLiteral));
+
+    properties->Add(property, zone());
+
+    if (fni_ != NULL) {
+      fni_->Infer();
+      fni_->Leave();
+    }
+  }
+  Expect(Token::RBRACE, CHECK_OK_CUSTOM(EmptyClassLiteral));
+
+  return factory()->NewClassLiteral(name, extends, constructor, properties,
+                                    this->ast_value_factory(), pos);
+}
+
+
 template <typename Traits>
 typename ParserBase<Traits>::ExpressionT
 ParserBase<Traits>::CheckAndRewriteReferenceExpression(
index 0f5d225..1ff2edd 100644 (file)
@@ -289,6 +289,21 @@ void PrettyPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
 }
 
 
+void PrettyPrinter::VisitClassLiteral(ClassLiteral* node) {
+  Print("(class ");
+  PrintLiteral(node->name(), false);
+  if (node->extends()) {
+    Print(" extends ");
+    Visit(node->extends());
+  }
+  Print(" { ");
+  for (int i = 0; i < node->properties()->length(); i++) {
+    PrintObjectLiteralProperty(node->properties()->at(i));
+  }
+  Print(" })");
+}
+
+
 void PrettyPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
   Print("(");
   PrintLiteral(node->name(), false);
@@ -323,16 +338,22 @@ void PrettyPrinter::VisitObjectLiteral(ObjectLiteral* node) {
   Print("{ ");
   for (int i = 0; i < node->properties()->length(); i++) {
     if (i != 0) Print(",");
-    ObjectLiteral::Property* property = node->properties()->at(i);
-    Print(" ");
-    Visit(property->key());
-    Print(": ");
-    Visit(property->value());
+    PrintObjectLiteralProperty(node->properties()->at(i));
   }
   Print(" }");
 }
 
 
+void PrettyPrinter::PrintObjectLiteralProperty(
+    ObjectLiteralProperty* property) {
+  // TODO(arv): Better printing of methods etc.
+  Print(" ");
+  Visit(property->key());
+  Print(": ");
+  Visit(property->value());
+}
+
+
 void PrettyPrinter::VisitArrayLiteral(ArrayLiteral* node) {
   Print("[ ");
   for (int i = 0; i < node->values()->length(); i++) {
@@ -969,6 +990,12 @@ void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
 }
 
 
+void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
+  IndentedScope indent(this, "CLASS LITERAL");
+  PrintLiteralIndented("NAME", node->name(), false);
+}
+
+
 void AstPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
   IndentedScope indent(this, "NATIVE FUNC LITERAL");
   PrintLiteralIndented("NAME", node->name(), false);
index de40aae..d300d9a 100644 (file)
@@ -52,6 +52,7 @@ class PrettyPrinter: public AstVisitor {
   void PrintDeclarations(ZoneList<Declaration*>* declarations);
   void PrintFunctionLiteral(FunctionLiteral* function);
   void PrintCaseClause(CaseClause* clause);
+  void PrintObjectLiteralProperty(ObjectLiteralProperty* property);
 
   DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
 };
index 0e9de14..72874aa 100644 (file)
@@ -902,70 +902,73 @@ uc32 Scanner::ScanIdentifierUnicodeEscape() {
 // ----------------------------------------------------------------------------
 // Keyword Matcher
 
-#define KEYWORDS(KEYWORD_GROUP, KEYWORD)                                      \
-  KEYWORD_GROUP('b')                                                          \
-  KEYWORD("break", Token::BREAK)                                              \
-  KEYWORD_GROUP('c')                                                          \
-  KEYWORD("case", Token::CASE)                                                \
-  KEYWORD("catch", Token::CATCH)                                              \
-  KEYWORD("class", Token::FUTURE_RESERVED_WORD)                               \
-  KEYWORD("const", Token::CONST)                                              \
-  KEYWORD("continue", Token::CONTINUE)                                        \
-  KEYWORD_GROUP('d')                                                          \
-  KEYWORD("debugger", Token::DEBUGGER)                                        \
-  KEYWORD("default", Token::DEFAULT)                                          \
-  KEYWORD("delete", Token::DELETE)                                            \
-  KEYWORD("do", Token::DO)                                                    \
-  KEYWORD_GROUP('e')                                                          \
-  KEYWORD("else", Token::ELSE)                                                \
-  KEYWORD("enum", Token::FUTURE_RESERVED_WORD)                                \
-  KEYWORD("export",                                                           \
-          harmony_modules ? Token::EXPORT : Token::FUTURE_RESERVED_WORD)      \
-  KEYWORD("extends", Token::FUTURE_RESERVED_WORD)                             \
-  KEYWORD_GROUP('f')                                                          \
-  KEYWORD("false", Token::FALSE_LITERAL)                                      \
-  KEYWORD("finally", Token::FINALLY)                                          \
-  KEYWORD("for", Token::FOR)                                                  \
-  KEYWORD("function", Token::FUNCTION)                                        \
-  KEYWORD_GROUP('i')                                                          \
-  KEYWORD("if", Token::IF)                                                    \
-  KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD)                   \
-  KEYWORD("import",                                                           \
-          harmony_modules ? Token::IMPORT : Token::FUTURE_RESERVED_WORD)      \
-  KEYWORD("in", Token::IN)                                                    \
-  KEYWORD("instanceof", Token::INSTANCEOF)                                    \
-  KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD)                    \
-  KEYWORD_GROUP('l')                                                          \
-  KEYWORD("let",                                                              \
-          harmony_scoping ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD)  \
-  KEYWORD_GROUP('n')                                                          \
-  KEYWORD("new", Token::NEW)                                                  \
-  KEYWORD("null", Token::NULL_LITERAL)                                        \
-  KEYWORD_GROUP('p')                                                          \
-  KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD)                      \
-  KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD)                      \
-  KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD)                    \
-  KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD)                       \
-  KEYWORD_GROUP('r')                                                          \
-  KEYWORD("return", Token::RETURN)                                            \
-  KEYWORD_GROUP('s')                                                          \
-  KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD)                       \
-  KEYWORD("super",                                                            \
-          harmony_classes ? Token::SUPER : Token::FUTURE_RESERVED_WORD)       \
-  KEYWORD("switch", Token::SWITCH)                                            \
-  KEYWORD_GROUP('t')                                                          \
-  KEYWORD("this", Token::THIS)                                                \
-  KEYWORD("throw", Token::THROW)                                              \
-  KEYWORD("true", Token::TRUE_LITERAL)                                        \
-  KEYWORD("try", Token::TRY)                                                  \
-  KEYWORD("typeof", Token::TYPEOF)                                            \
-  KEYWORD_GROUP('v')                                                          \
-  KEYWORD("var", Token::VAR)                                                  \
-  KEYWORD("void", Token::VOID)                                                \
-  KEYWORD_GROUP('w')                                                          \
-  KEYWORD("while", Token::WHILE)                                              \
-  KEYWORD("with", Token::WITH)                                                \
-  KEYWORD_GROUP('y')                                                          \
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD)                                     \
+  KEYWORD_GROUP('b')                                                         \
+  KEYWORD("break", Token::BREAK)                                             \
+  KEYWORD_GROUP('c')                                                         \
+  KEYWORD("case", Token::CASE)                                               \
+  KEYWORD("catch", Token::CATCH)                                             \
+  KEYWORD("class",                                                           \
+          harmony_classes ? Token::CLASS : Token::FUTURE_RESERVED_WORD)      \
+  KEYWORD("const", Token::CONST)                                             \
+  KEYWORD("continue", Token::CONTINUE)                                       \
+  KEYWORD_GROUP('d')                                                         \
+  KEYWORD("debugger", Token::DEBUGGER)                                       \
+  KEYWORD("default", Token::DEFAULT)                                         \
+  KEYWORD("delete", Token::DELETE)                                           \
+  KEYWORD("do", Token::DO)                                                   \
+  KEYWORD_GROUP('e')                                                         \
+  KEYWORD("else", Token::ELSE)                                               \
+  KEYWORD("enum", Token::FUTURE_RESERVED_WORD)                               \
+  KEYWORD("export",                                                          \
+          harmony_modules ? Token::EXPORT : Token::FUTURE_RESERVED_WORD)     \
+  KEYWORD("extends",                                                         \
+          harmony_classes ? Token::EXTENDS : Token::FUTURE_RESERVED_WORD)    \
+  KEYWORD_GROUP('f')                                                         \
+  KEYWORD("false", Token::FALSE_LITERAL)                                     \
+  KEYWORD("finally", Token::FINALLY)                                         \
+  KEYWORD("for", Token::FOR)                                                 \
+  KEYWORD("function", Token::FUNCTION)                                       \
+  KEYWORD_GROUP('i')                                                         \
+  KEYWORD("if", Token::IF)                                                   \
+  KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD)                  \
+  KEYWORD("import",                                                          \
+          harmony_modules ? Token::IMPORT : Token::FUTURE_RESERVED_WORD)     \
+  KEYWORD("in", Token::IN)                                                   \
+  KEYWORD("instanceof", Token::INSTANCEOF)                                   \
+  KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD)                   \
+  KEYWORD_GROUP('l')                                                         \
+  KEYWORD("let",                                                             \
+          harmony_scoping ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \
+  KEYWORD_GROUP('n')                                                         \
+  KEYWORD("new", Token::NEW)                                                 \
+  KEYWORD("null", Token::NULL_LITERAL)                                       \
+  KEYWORD_GROUP('p')                                                         \
+  KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD)                     \
+  KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD)                     \
+  KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD)                   \
+  KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD)                      \
+  KEYWORD_GROUP('r')                                                         \
+  KEYWORD("return", Token::RETURN)                                           \
+  KEYWORD_GROUP('s')                                                         \
+  KEYWORD("static", harmony_classes ? Token::STATIC                          \
+                                    : Token::FUTURE_STRICT_RESERVED_WORD)    \
+  KEYWORD("super",                                                           \
+          harmony_classes ? Token::SUPER : Token::FUTURE_RESERVED_WORD)      \
+  KEYWORD("switch", Token::SWITCH)                                           \
+  KEYWORD_GROUP('t')                                                         \
+  KEYWORD("this", Token::THIS)                                               \
+  KEYWORD("throw", Token::THROW)                                             \
+  KEYWORD("true", Token::TRUE_LITERAL)                                       \
+  KEYWORD("try", Token::TRY)                                                 \
+  KEYWORD("typeof", Token::TYPEOF)                                           \
+  KEYWORD_GROUP('v')                                                         \
+  KEYWORD("var", Token::VAR)                                                 \
+  KEYWORD("void", Token::VOID)                                               \
+  KEYWORD_GROUP('w')                                                         \
+  KEYWORD("while", Token::WHILE)                                             \
+  KEYWORD("with", Token::WITH)                                               \
+  KEYWORD_GROUP('y')                                                         \
   KEYWORD("yield", Token::YIELD)
 
 
index b77c7f5..356c8e4 100644 (file)
@@ -653,7 +653,7 @@ class Scanner {
   bool harmony_modules_;
   // Whether we scan 0o777 and 0b111 as numbers.
   bool harmony_numeric_literals_;
-  // Whether we scan 'super' as keyword.
+  // Whether we scan 'class', 'extends', 'static' and 'super' as keywords.
   bool harmony_classes_;
 };
 
index fe95412..9c719b8 100644 (file)
@@ -148,10 +148,13 @@ namespace internal {
   /* Future reserved words (ECMA-262, section 7.6.1.2). */           \
   T(FUTURE_RESERVED_WORD, NULL, 0)                                   \
   T(FUTURE_STRICT_RESERVED_WORD, NULL, 0)                            \
+  K(CLASS, "class", 0)                                               \
   K(CONST, "const", 0)                                               \
   K(EXPORT, "export", 0)                                             \
+  K(EXTENDS, "extends", 0)                                           \
   K(IMPORT, "import", 0)                                             \
   K(LET, "let", 0)                                                   \
+  K(STATIC, "static", 0)                                             \
   K(YIELD, "yield", 0)                                               \
   K(SUPER, "super", 0)                                               \
                                                                      \
index ee473ed..02c9603 100644 (file)
@@ -352,6 +352,9 @@ void AstTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
 }
 
 
+void AstTyper::VisitClassLiteral(ClassLiteral* expr) {}
+
+
 void AstTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
 }
 
index 4f2603d..aa35009 100644 (file)
@@ -1448,9 +1448,15 @@ TEST(ParserSync) {
       i::GetCurrentStackPosition() - 128 * 1024);
 
   static const ParserFlag flags1[] = {
-      kAllowLazy,                   kAllowHarmonyScoping,
-      kAllowModules,                kAllowArrowFunctions,
-      kAllowHarmonyNumericLiterals, kAllowHarmonyObjectLiterals};
+    kAllowArrowFunctions,
+    kAllowClasses,
+    kAllowHarmonyNumericLiterals,
+    kAllowHarmonyObjectLiterals,
+    kAllowHarmonyScoping,
+    kAllowLazy,
+    kAllowModules,
+  };
+
   for (int i = 0; context_data[i][0] != NULL; ++i) {
     for (int j = 0; statement_data[j] != NULL; ++j) {
       for (int k = 0; termination_data[k] != NULL; ++k) {
@@ -1525,10 +1531,14 @@ void RunParserSyncTest(const char* context_data[][2],
       i::GetCurrentStackPosition() - 128 * 1024);
 
   static const ParserFlag default_flags[] = {
-      kAllowArrowFunctions,         kAllowClasses,
-      kAllowHarmonyNumericLiterals, kAllowHarmonyObjectLiterals,
-      kAllowHarmonyScoping,         kAllowLazy,
-      kAllowModules,                kAllowNativesSyntax,
+    kAllowArrowFunctions,
+    kAllowClasses,
+    kAllowHarmonyNumericLiterals,
+    kAllowHarmonyObjectLiterals,
+    kAllowHarmonyScoping,
+    kAllowLazy,
+    kAllowModules,
+    kAllowNativesSyntax,
   };
   ParserFlag* generated_flags = NULL;
   if (flags == NULL) {
@@ -3537,3 +3547,405 @@ TEST(MethodDefinitionDuplicateProperty) {
   RunParserSyncTest(context_data, params_data, kError, NULL, 0,
                     always_flags, arraysize(always_flags));
 }
+
+
+TEST(NoErrorsClassExpression) {
+  const char* context_data[][2] = {{"(", ");"},
+                                   {"var C = ", ";"},
+                                   {"bar, ", ";"},
+                                   {NULL, NULL}};
+  const char* class_data[] = {
+    "class {}",
+    "class name {}",
+    "class extends F {}",
+    "class name extends F {}",
+    "class extends (F, G) {}",
+    "class name extends (F, G) {}",
+    "class extends class {} {}",
+    "class name extends class {} {}",
+    "class extends class base {} {}",
+    "class name extends class base {} {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {kAllowClasses};
+  RunParserSyncTest(context_data, class_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsClassDeclaration) {
+  const char* context_data[][2] = {{"", ""},
+                                   {"{", "}"},
+                                   {"if (true) {", "}"},
+                                   {NULL, NULL}};
+  const char* statement_data[] = {
+    "class name {}",
+    "class name extends F {}",
+    "class name extends (F, G) {}",
+    "class name extends class {} {}",
+    "class name extends class base {} {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {kAllowClasses};
+  RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsClassBody) {
+  // Tests that parser and preparser accept valid class syntax.
+  const char* context_data[][2] = {{"(class {", "});"},
+                                   {"(class extends Base {", "});"},
+                                   {"class C {", "}"},
+                                   {"class C extends Base {", "}"},
+                                   {NULL, NULL}};
+  const char* class_body_data[] = {
+    ";",
+    ";;",
+    "m() {}",
+    "m() {};",
+    ";m() {}",
+    "m() {}; n(x) {}",
+    "get x() {}",
+    "set x(v) {}",
+    "get() {}",
+    "set() {}",
+    "static() {}",
+    "static m() {}",
+    "static get x() {}",
+    "static set x(v) {}",
+    "static get() {}",
+    "static set() {}",
+    "static static() {}",
+    "static get static() {}",
+    "static set static(v) {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(MethodDefinitionstrictFormalParamereters) {
+  const char* context_data[][2] = {{"({method(", "){}});"},
+                                   {NULL, NULL}};
+
+  const char* params_data[] = {
+    "x, x",
+    "x, y, x",
+    "eval",
+    "arguments",
+    "var",
+    "const",
+    NULL
+  };
+
+  static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+  RunParserSyncTest(context_data, params_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsClassPropertyName) {
+  const char* context_data[][2] = {{"(class {", "() {}});"},
+                                   {"(class { get ", "() {}});"},
+                                   {"(class { set ", "(v) {}});"},
+                                   {"(class { static ", "() {}});"},
+                                   {"(class { static get ", "() {}});"},
+                                   {"(class { static set ", "(v) {}});"},
+                                   {"class C {", "() {}}"},
+                                   {"class C { get ", "() {}}"},
+                                   {"class C { set ", "(v) {}}"},
+                                   {"class C { static ", "() {}}"},
+                                   {"class C { static get ", "() {}}"},
+                                   {"class C { static set ", "(v) {}}"},
+                                   {NULL, NULL}};
+  const char* name_data[] = {
+    "42",
+    "42.5",
+    "42e2",
+    "42e+2",
+    "42e-2",
+    "null",
+    "false",
+    "true",
+    "'str'",
+    "\"str\"",
+    "static",
+    "get",
+    "set",
+    "var",
+    "const",
+    "let",
+    "this",
+    "class",
+    "function",
+    "yield",
+    "if",
+    "else",
+    "for",
+    "while",
+    "do",
+    "try",
+    "catch",
+    "finally",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassExpression) {
+  const char* context_data[][2] = {{"(", ");"},
+                                   {"var C = ", ";"},
+                                   {"bar, ", ";"},
+                                   {NULL, NULL}};
+  const char* class_data[] = {
+    "class",
+    "class name",
+    "class name extends",
+    "class extends",
+    "class {",
+    "class { m }",
+    "class { m; n }",
+    "class { m: 1 }",
+    "class { m(); n() }",
+    "class { get m }",
+    "class { get m() }",
+    "class { get m() { }",
+    "class { set m() {} }",  // Missing required parameter.
+    "class { m() {}, n() {} }",  // No commas allowed.
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassDeclaration) {
+  const char* context_data[][2] = {{"", ""},
+                                   {"{", "}"},
+                                   {"if (true) {", "}"},
+                                   {NULL, NULL}};
+  const char* class_data[] = {
+    "class",
+    "class name",
+    "class name extends",
+    "class extends",
+    "class name {",
+    "class name { m }",
+    "class name { m; n }",
+    "class name { m: 1 }",
+    "class name { m(); n() }",
+    "class name { get m }",
+    "class name { get m() }",
+    "class name { set m() {) }",  // missing required param
+    "class {}",  // Name is required for declaration
+    "class extends base {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyNumericLiterals
+  };
+  RunParserSyncTest(context_data, class_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassName) {
+  const char* context_data[][2] = {{"class ", "{}"},
+                                   {"(class ", "{});"},
+                                   {"'use strict'; class ", "{}"},
+                                   {"'use strict'; (class ", "{});"},
+                                   {NULL, NULL}};
+  const char* class_name[] = {
+    "arguments",
+    "eval",
+    "implements",
+    "interface",
+    "let",
+    "package",
+    "private",
+    "protected",
+    "public",
+    "static",
+    "var",
+    "yield",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_name, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassGetterParamName) {
+  const char* context_data[][2] = {
+    {"class C { get name(", ") {} }"},
+    {"(class { get name(", ") {} });"},
+    {"'use strict'; class C { get name(", ") {} }"},
+    {"'use strict'; (class { get name(", ") {} })"},
+    {NULL, NULL}
+  };
+
+  const char* class_name[] = {
+    "arguments",
+    "eval",
+    "implements",
+    "interface",
+    "let",
+    "package",
+    "private",
+    "protected",
+    "public",
+    "static",
+    "var",
+    "yield",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_name, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassStaticPrototype) {
+  const char* context_data[][2] = {{"class C {", "}"},
+                                   {"(class {", "});"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "static prototype() {}",
+    "static get prototype() {}",
+    "static set prototype(_) {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassSpecialConstructor) {
+  const char* context_data[][2] = {{"class C {", "}"},
+                                   {"(class {", "});"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "get constructor() {}",
+    "get constructor(_) {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsClassConstructor) {
+  const char* context_data[][2] = {{"class C {", "}"},
+                                   {"(class {", "});"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "constructor() {}",
+    "static constructor() {}",
+    "static get constructor() {}",
+    "static set constructor(_) {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassMultipleConstructor) {
+  // We currently do not allow any duplicate properties in class bodies. This
+  // test ensures that when we change that we still throw on duplicate
+  // constructors.
+  const char* context_data[][2] = {{"class C {", "}"},
+                                   {"(class {", "});"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "constructor() {}; constructor() {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+// TODO(arv): We should allow duplicate property names.
+// https://code.google.com/p/v8/issues/detail?id=3570
+DISABLED_TEST(NoErrorsClassMultiplePropertyNames) {
+  const char* context_data[][2] = {{"class C {", "}"},
+                                   {"(class {", "});"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "constructor() {}; static constructor() {}",
+    "m() {}; static m() {}",
+    "m() {}; m() {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsClassesAreStrict) {
+  const char* context_data[][2] = {{"", ""},
+                                   {"(", ");"},
+                                   {NULL, NULL}};
+
+  const char* class_body_data[] = {
+    "class C { method() { with ({}) {} } }",
+    "class C extends function() { with ({}) {} } {}",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowClasses,
+    kAllowHarmonyObjectLiterals
+  };
+  RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}