Revision: 24442
Author:   [email protected]
Date:     Tue Oct  7 16:24:59 2014 UTC
Log:      This uses a runtime function to set up the the constructor and its
prototype.

This does not add the methods/accessors to the prototype or the
constructor.

BUG=v8:3330
LOG=Y
[email protected]

Review URL: https://codereview.chromium.org/631433002
https://code.google.com/p/v8/source/detail?r=24442

Added:
 /branches/bleeding_edge/test/mjsunit/harmony/classes.js
Modified:
 /branches/bleeding_edge/src/compiler/ast-graph-builder.cc
 /branches/bleeding_edge/src/compiler/pipeline.cc
 /branches/bleeding_edge/src/full-codegen.cc
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/src/parser.cc
 /branches/bleeding_edge/src/parser.h
 /branches/bleeding_edge/src/preparser.h
 /branches/bleeding_edge/src/runtime/runtime-classes.cc
 /branches/bleeding_edge/src/runtime/runtime.h

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/classes.js Tue Oct 7 16:24:59 2014 UTC
@@ -0,0 +1,167 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony
+
+(function TestBasics() {
+  var C = class C {}
+  assertEquals(typeof C, 'function');
+  assertEquals(C.__proto__, Function.prototype);
+  assertEquals(Object.prototype, Object.getPrototypeOf(C.prototype));
+  assertEquals(Function.prototype, Object.getPrototypeOf(C));
+  assertEquals('C', C.name);
+
+  class D {}
+  assertEquals(typeof D, 'function');
+  assertEquals(D.__proto__, Function.prototype);
+  assertEquals(Object.prototype, Object.getPrototypeOf(D.prototype));
+  assertEquals(Function.prototype, Object.getPrototypeOf(D));
+  assertEquals('D', D.name);
+
+  var E = class {}
+  assertEquals('', E.name);
+})();
+
+
+(function TestBasicsExtends() {
+  class C extends null {}
+  assertEquals(typeof C, 'function');
+  assertEquals(C.__proto__, Function.prototype);
+  assertEquals(null, Object.getPrototypeOf(C.prototype));
+
+  class D extends C {}
+  assertEquals(typeof D, 'function');
+  assertEquals(D.__proto__, C);
+  assertEquals(C.prototype, Object.getPrototypeOf(D.prototype));
+})();
+
+
+(function TestSideEffectInExtends() {
+  var calls = 0;
+  class C {}
+  class D extends (calls++, C) {}
+  assertEquals(1, calls);
+  assertEquals(typeof D, 'function');
+  assertEquals(D.__proto__, C);
+  assertEquals(C.prototype, Object.getPrototypeOf(D.prototype));
+})();
+
+
+(function TestInvalidExtends() {
+  assertThrows(function() {
+    class C extends 42 {}
+  }, TypeError);
+
+  assertThrows(function() {
+    // Function but its .prototype is not null or a function.
+    class C extends Math.abs {}
+  }, TypeError);
+
+  assertThrows(function() {
+    Math.abs.prototype = 42;
+    class C extends Math.abs {}
+  }, TypeError);
+  delete Math.abs.prototype;
+})();
+
+
+(function TestConstructorProperty() {
+  class C {}
+  assertEquals(C, C.prototype.constructor);
+  var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor');
+  assertTrue(descr.configurable);
+  assertFalse(descr.enumerable);
+  assertTrue(descr.writable);
+})();
+
+
+(function TestPrototypeProperty() {
+  class C {}
+  var descr = Object.getOwnPropertyDescriptor(C, 'prototype');
+  assertFalse(descr.configurable);
+  assertFalse(descr.enumerable);
+  assertFalse(descr.writable);
+})();
+
+
+(function TestConstructor() {
+  var count = 0;
+  class C {
+    constructor() {
+      assertEquals(Object.getPrototypeOf(this), C.prototype);
+      count++;
+    }
+  }
+  assertEquals(C, C.prototype.constructor);
+  var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor');
+  assertTrue(descr.configurable);
+  assertFalse(descr.enumerable);
+  assertTrue(descr.writable);
+
+  var c = new C();
+  assertEquals(1, count);
+  assertEquals(Object.getPrototypeOf(c), C.prototype);
+})();
+
+
+(function TestImplicitConstructor() {
+  class C {}
+  var c = new C();
+  assertEquals(Object.getPrototypeOf(c), C.prototype);
+})();
+
+
+(function TestConstructorStrict() {
+  class C {
+    constructor() {
+      assertThrows(function() {
+        nonExistingBinding = 42;
+      }, ReferenceError);
+    }
+  }
+  new C();
+})();
+
+
+(function TestSuperInConstructor() {
+  var calls = 0;
+  class B {}
+  B.prototype.x = 42;
+
+  class C extends B {
+    constructor() {
+      calls++;
+      assertEquals(42, super.x);
+    }
+  }
+
+  new C;
+  assertEquals(1, calls);
+})();
+
+
+(function TestStrictMode() {
+  class C {}
+
+  with ({a: 1}) {
+    assertEquals(1, a);
+  }
+
+ assertThrows('class C extends function B() { with ({}); return B; }() {}',
+               SyntaxError);
+
+})();
+
+/* TODO(arv): Implement
+(function TestNameBindingInConstructor() {
+  class C {
+    constructor() {
+      assertThrows(function() {
+        C = 42;
+      }, ReferenceError);
+    }
+  }
+  new C();
+})();
+*/
=======================================
--- /branches/bleeding_edge/src/compiler/ast-graph-builder.cc Wed Oct 1 14:03:02 2014 UTC +++ /branches/bleeding_edge/src/compiler/ast-graph-builder.cc Tue Oct 7 16:24:59 2014 UTC
@@ -813,7 +813,6 @@


 void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
-  // TODO(arv): Implement.
   UNREACHABLE();
 }

=======================================
--- /branches/bleeding_edge/src/compiler/pipeline.cc Tue Oct 7 12:35:06 2014 UTC +++ /branches/bleeding_edge/src/compiler/pipeline.cc Tue Oct 7 16:24:59 2014 UTC
@@ -145,7 +145,7 @@
   }

 #define DEF_VISIT(type)                                               \
-  virtual void Visit##type(type* node) OVERRIDE {                  \
+  virtual void Visit##type(type* node) OVERRIDE {                     \
     SourcePositionTable::Scope pos(source_positions_,                 \
                                    SourcePosition(node->position())); \
     AstGraphBuilder::Visit##type(node);                               \
@@ -173,7 +173,7 @@
       info()->function()->dont_optimize_reason() == kForOfStatement ||
       // TODO(turbofan): Make super work and remove this bailout.
       info()->function()->dont_optimize_reason() == kSuperReference ||
-      // TODO(turbofan): Make classliterals work and remove this bailout.
+      // TODO(turbofan): Make class literals work and remove this bailout.
       info()->function()->dont_optimize_reason() == kClassLiteral ||
       // TODO(turbofan): Make OSR work and remove this bailout.
       info()->is_osr()) {
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Thu Oct  2 11:58:21 2014 UTC
+++ /branches/bleeding_edge/src/full-codegen.cc Tue Oct  7 16:24:59 2014 UTC
@@ -1542,12 +1542,30 @@


 void FullCodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
-  // TODO(arv): Implement
   Comment cmnt(masm_, "[ ClassLiteral");
+
+  if (expr->raw_name() != NULL) {
+    __ Push(expr->name());
+  } else {
+    __ Push(isolate()->factory()->undefined_value());
+  }
+
   if (expr->extends() != NULL) {
-    VisitForEffect(expr->extends());
+    VisitForStackValue(expr->extends());
+  } else {
+    __ Push(isolate()->factory()->the_hole_value());
   }
-  context()->Plug(isolate()->factory()->undefined_value());
+
+  if (expr->constructor() != NULL) {
+    VisitForStackValue(expr->constructor());
+  } else {
+    __ Push(isolate()->factory()->undefined_value());
+  }
+
+  // TODO(arv): Process methods
+
+  __ CallRuntime(Runtime::kDefineClass, 3);
+  context()->Plug(result_register());
 }


=======================================
--- /branches/bleeding_edge/src/messages.js     Wed Sep 24 08:39:04 2014 UTC
+++ /branches/bleeding_edge/src/messages.js     Tue Oct  7 16:24:59 2014 UTC
@@ -174,7 +174,9 @@
invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"],
   module_type_error:             ["Module '", "%0", "' used improperly"],
module_export_undefined: ["Export '", "%0", "' is not defined in module"],
-  unexpected_super:              ["'super' keyword unexpected here"]
+  unexpected_super:              ["'super' keyword unexpected here"],
+ extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"], + prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"]
 };


=======================================
--- /branches/bleeding_edge/src/parser.cc       Tue Oct  7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/parser.cc       Tue Oct  7 16:24:59 2014 UTC
@@ -646,7 +646,7 @@
       pos);
 }

-Expression* ParserTraits::ClassLiteral(
+Expression* ParserTraits::ClassExpression(
     const AstRawString* name, Expression* extends, Expression* constructor,
     ZoneList<ObjectLiteral::Property*>* properties, int pos,
     AstNodeFactory<AstConstructionVisitor>* factory) {
@@ -1956,21 +1956,18 @@
   Expression* 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());
+  VariableProxy* proxy = NewUnresolved(name, LET, Interface::NewValue());
   Declaration* declaration =
-      factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
+      factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
   Declare(declaration, true, CHECK_OK);
+  proxy->var()->set_initializer_position(pos);

   Token::Value init_op = Token::INIT_LET;
Assignment* assignment = factory()->NewAssignment(init_op, proxy, value, pos);
-  block->AddStatement(
- factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
-      zone());
-
+  Statement* assignment_statement =
+ factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
   if (names) names->Add(name, zone());
-  return block;
+  return assignment_statement;
 }


=======================================
--- /branches/bleeding_edge/src/parser.h        Tue Oct  7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/parser.h        Tue Oct  7 16:24:59 2014 UTC
@@ -416,6 +416,15 @@
   static bool IsArrayIndex(const AstRawString* string, uint32_t* index) {
     return string->AsArrayIndex(index);
   }
+
+  bool IsConstructorProperty(ObjectLiteral::Property* property) {
+    return property->key()->raw_value()->EqualsString(
+        ast_value_factory()->constructor_string());
+  }
+
+  static Expression* GetPropertyValue(ObjectLiteral::Property* property) {
+    return property->value();
+  }

// Functions for encapsulating the differences between parsing and preparsing;
   // operations interleaved with the recursive descent.
@@ -546,11 +555,11 @@
   Expression* SuperReference(Scope* scope,
AstNodeFactory<AstConstructionVisitor>* factory,
                              int pos = RelocInfo::kNoPosition);
-  Expression* ClassLiteral(const AstRawString* name, Expression* extends,
-                           Expression* constructor,
-                           ZoneList<ObjectLiteral::Property*>* properties,
-                           int pos,
- AstNodeFactory<AstConstructionVisitor>* factory); + Expression* ClassExpression(const AstRawString* name, Expression* extends,
+                              Expression* constructor,
+ ZoneList<ObjectLiteral::Property*>* properties,
+                              int pos,
+ AstNodeFactory<AstConstructionVisitor>* factory);

   Literal* ExpressionFromLiteral(
       Token::Value token, int pos, Scanner* scanner,
=======================================
--- /branches/bleeding_edge/src/preparser.h     Tue Oct  7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/preparser.h     Tue Oct  7 16:24:59 2014 UTC
@@ -150,6 +150,13 @@
           scope_(scope) {
       *scope_stack_ = scope_;
     }
+    BlockState(typename Traits::Type::Scope** scope_stack,
+               typename Traits::Type::Scope** scope)
+        : scope_stack_(scope_stack),
+          outer_scope_(*scope_stack),
+          scope_(*scope) {
+      *scope_stack_ = scope_;
+    }
     ~BlockState() { *scope_stack_ = outer_scope_; }

    private:
@@ -1194,6 +1201,13 @@
   static bool IsArrayIndex(PreParserIdentifier string, uint32_t* index) {
     return false;
   }
+
+ bool IsConstructorProperty(PreParserExpression property) { return false; }
+
+ static PreParserExpression GetPropertyValue(PreParserExpression property) {
+    UNREACHABLE();
+    return PreParserExpression::Default();
+  }

// Functions for encapsulating the differences between parsing and preparsing;
   // operations interleaved with the recursive descent.
@@ -1320,12 +1334,12 @@
     return PreParserExpression::Super();
   }

-  static PreParserExpression ClassLiteral(PreParserIdentifier name,
-                                          PreParserExpression extends,
-                                          PreParserExpression constructor,
- PreParserExpressionList properties,
-                                          int position,
-                                          PreParserFactory* factory) {
+  static PreParserExpression ClassExpression(PreParserIdentifier name,
+                                             PreParserExpression extends,
+ PreParserExpression constructor, + PreParserExpressionList properties,
+                                             int position,
+                                             PreParserFactory* factory) {
     return PreParserExpression::Default();
   }

@@ -1978,16 +1992,22 @@
       *ok = false;
       return this->EmptyObjectLiteralProperty();
     }
- if (is_generator && in_class && !is_static && this->IsConstructor(name)) {
-      ReportMessageAt(scanner()->location(), "constructor_special_method");
-      *ok = false;
-      return this->EmptyObjectLiteralProperty();
+
+ FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
+                                     : FunctionKind::kConciseMethod;
+
+    if (in_class && !is_static && this->IsConstructor(name)) {
+      if (is_generator) {
+ ReportMessageAt(scanner()->location(), "constructor_special_method");
+        *ok = false;
+        return this->EmptyObjectLiteralProperty();
+      }
+
+      kind = FunctionKind::kNormalFunction;
     }

     checker->CheckProperty(name_token, kValueProperty,
                            CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
-                                     : FunctionKind::kConciseMethod;

     value = this->ParseFunctionLiteral(
         name, scanner()->location(),
@@ -2744,26 +2764,25 @@
     *ok = false;
     return this->EmptyExpression();
   }
-
-  // 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)) {
+ typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE);
+    BlockState block_state(&scope_, &scope);
+    scope_->SetStrictMode(STRICT);
     extends = this->ParseLeftHandSideExpression(CHECK_OK);
   }
+
+  // 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_, &scope);
+  scope_->SetStrictMode(STRICT);
+  scope_->SetScopeName(name);

   ObjectLiteralChecker checker(this, STRICT);
   typename Traits::Type::PropertyList properties =
       this->NewPropertyList(4, zone_);
-  FunctionLiteralT constructor = this->EmptyFunctionLiteral();
+  ExpressionT constructor = this->EmptyExpression();

   Expect(Token::LBRACE, CHECK_OK);
   while (peek() != Token::RBRACE) {
@@ -2775,7 +2794,11 @@
     ObjectLiteralPropertyT property =
this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK);

-    properties->Add(property, zone());
+    if (this->IsConstructorProperty(property)) {
+      constructor = this->GetPropertyValue(property);
+    } else {
+      properties->Add(property, zone());
+    }

     if (fni_ != NULL) {
       fni_->Infer();
@@ -2784,8 +2807,8 @@
   }
   Expect(Token::RBRACE, CHECK_OK);

-  return this->ClassLiteral(name, extends, constructor, properties, pos,
-                            factory());
+  return this->ClassExpression(name, extends, constructor, properties, pos,
+                               factory());
 }


=======================================
--- /branches/bleeding_edge/src/runtime/runtime-classes.cc Mon Oct 6 08:25:27 2014 UTC +++ /branches/bleeding_edge/src/runtime/runtime-classes.cc Tue Oct 7 16:24:59 2014 UTC
@@ -151,5 +151,84 @@

   return StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY);
 }
+
+
+RUNTIME_FUNCTION(Runtime_DefineClass) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 1);
+  CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 2);
+
+  Handle<Object> prototype_parent;
+  Handle<Object> constructor_parent;
+
+  if (super_class->IsTheHole()) {
+    prototype_parent = isolate->initial_object_prototype();
+  } else {
+    if (super_class->IsNull()) {
+      prototype_parent = isolate->factory()->null_value();
+    } else if (super_class->IsSpecFunction()) {
+      ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+          isolate, prototype_parent,
+          Runtime::GetObjectProperty(isolate, super_class,
+ isolate->factory()->prototype_string())); + if (!prototype_parent->IsNull() && !prototype_parent->IsSpecObject()) {
+        Handle<Object> args[1] = {prototype_parent};
+        THROW_NEW_ERROR_RETURN_FAILURE(
+            isolate, NewTypeError("prototype_parent_not_an_object",
+                                  HandleVector(args, 1)));
+      }
+      constructor_parent = super_class;
+    } else {
+      // TODO(arv): Should be IsConstructor.
+      Handle<Object> args[1] = {super_class};
+      THROW_NEW_ERROR_RETURN_FAILURE(
+          isolate,
+ NewTypeError("extends_value_not_a_function", HandleVector(args, 1)));
+    }
+  }
+
+  Handle<Map> map =
+      isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+  map->set_prototype(*prototype_parent);
+  Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map);
+
+  Handle<String> name_string = name->IsString()
+                                   ? Handle<String>::cast(name)
+                                   : isolate->factory()->empty_string();
+
+  Handle<JSFunction> ctor;
+  if (constructor->IsSpecFunction()) {
+    ctor = Handle<JSFunction>::cast(constructor);
+    JSFunction::SetPrototype(ctor, prototype);
+    PropertyAttributes attribs =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+    RETURN_FAILURE_ON_EXCEPTION(
+        isolate,
+        JSObject::SetOwnPropertyIgnoreAttributes(
+ ctor, isolate->factory()->prototype_string(), prototype, attribs));
+  } else {
+    // TODO(arv): This should not use an empty function but a function that
+    // calls super.
+ Handle<Code> code(isolate->builtins()->builtin(Builtins::kEmptyFunction)); + ctor = isolate->factory()->NewFunction(name_string, code, prototype, true);
+  }
+
+  Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol());
+  RETURN_FAILURE_ON_EXCEPTION(
+      isolate, JSObject::SetOwnPropertyIgnoreAttributes(
+                   ctor, home_object_symbol, prototype, DONT_ENUM));
+
+  if (!constructor_parent.is_null()) {
+    RETURN_FAILURE_ON_EXCEPTION(
+        isolate, JSObject::SetPrototype(ctor, constructor_parent, false));
+  }
+
+ JSObject::AddProperty(prototype, isolate->factory()->constructor_string(),
+                        ctor, DONT_ENUM);
+
+  return *ctor;
+}
 }
 }  // namespace v8::internal
=======================================
--- /branches/bleeding_edge/src/runtime/runtime.h Tue Oct 7 13:30:28 2014 UTC +++ /branches/bleeding_edge/src/runtime/runtime.h Tue Oct 7 16:24:59 2014 UTC
@@ -192,7 +192,8 @@
   F(LoadFromSuper, 3, 1)                                   \
   F(LoadKeyedFromSuper, 3, 1)                              \
   F(StoreToSuper_Strict, 4, 1)                             \
-  F(StoreToSuper_Sloppy, 4, 1)
+  F(StoreToSuper_Sloppy, 4, 1)                             \
+  F(DefineClass, 3, 1)


 #define RUNTIME_FUNCTION_LIST_ALWAYS_2(F)              \

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to