First simple implementation of class literals in TurboFan.
authormstarzinger <mstarzinger@chromium.org>
Fri, 16 Jan 2015 12:38:13 +0000 (04:38 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 16 Jan 2015 12:38:26 +0000 (12:38 +0000)
R=rossberg@chromium.org,jarin@chromium.org
TEST=cctest/test-run-jsops/ClassLiteral

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

Cr-Commit-Position: refs/heads/master@{#26101}

src/ast-numbering.cc
src/ast.h
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/pipeline.cc
src/full-codegen.cc
src/types.cc
test/cctest/compiler/test-run-jsops.cc
test/mjsunit/mjsunit.status

index f9144bc..6998187 100644 (file)
@@ -452,7 +452,7 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) {
 
 void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
   IncrementNodeCount();
-  DisableTurbofan(kClassLiteral);
+  DisableCrankshaft(kClassLiteral);
   node->set_base_id(ReserveIdRange(ClassLiteral::num_ids()));
   if (node->extends()) Visit(node->extends());
   if (node->constructor()) Visit(node->constructor());
index 6cf55c2..716fded 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -2628,6 +2628,11 @@ class ClassLiteral FINAL : public Expression {
   int start_position() const { return position(); }
   int end_position() const { return end_position_; }
 
+  static int num_ids() { return parent_num_ids() + 3; }
+  BailoutId EntryId() const { return BailoutId(local_id(0)); }
+  BailoutId DeclsId() const { return BailoutId(local_id(1)); }
+  BailoutId ExitId() { return BailoutId(local_id(2)); }
+
  protected:
   ClassLiteral(Zone* zone, const AstRawString* name, Scope* scope,
                VariableProxy* class_variable_proxy, Expression* extends,
@@ -2641,8 +2646,11 @@ class ClassLiteral FINAL : public Expression {
         constructor_(constructor),
         properties_(properties),
         end_position_(end_position) {}
+  static int parent_num_ids() { return Expression::num_ids(); }
 
  private:
+  int local_id(int n) const { return base_id() + parent_num_ids() + n; }
+
   const AstRawString* raw_name_;
   Scope* scope_;
   VariableProxy* class_variable_proxy_;
index 4a22abf..c4b9b31 100644 (file)
@@ -320,6 +320,14 @@ void AstGraphBuilder::VisitForValueOrNull(Expression* expr) {
 }
 
 
+void AstGraphBuilder::VisitForValueOrTheHole(Expression* expr) {
+  if (expr == NULL) {
+    return environment()->Push(jsgraph()->TheHoleConstant());
+  }
+  VisitForValue(expr);
+}
+
+
 void AstGraphBuilder::VisitForValues(ZoneList<Expression*>* exprs) {
   for (int i = 0; i < exprs->length(); ++i) {
     VisitForValue(exprs->at(i));
@@ -469,12 +477,9 @@ void AstGraphBuilder::VisitBlock(Block* stmt) {
     // Visit statements in the same scope, no declarations.
     VisitStatements(stmt->statements());
   } else {
-    const Operator* op = javascript()->CreateBlockContext();
-    Node* scope_info = jsgraph()->Constant(stmt->scope()->GetScopeInfo());
-    Node* context = NewNode(op, scope_info, GetFunctionClosure());
-    ContextScope scope(this, stmt->scope(), context);
-
     // Visit declarations and statements in a block scope.
+    Node* context = BuildLocalBlockContext(stmt->scope());
+    ContextScope scope(this, stmt->scope(), context);
     VisitDeclarations(stmt->scope()->declarations());
     VisitStatements(stmt->statements());
   }
@@ -842,7 +847,105 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
 
 
 void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
-  UNREACHABLE();
+  if (expr->scope() == NULL) {
+    // Visit class literal in the same scope, no declarations.
+    VisitClassLiteralContents(expr);
+  } else {
+    // Visit declarations and class literal in a block scope.
+    Node* context = BuildLocalBlockContext(expr->scope());
+    ContextScope scope(this, expr->scope(), context);
+    VisitDeclarations(expr->scope()->declarations());
+    VisitClassLiteralContents(expr);
+  }
+}
+
+
+void AstGraphBuilder::VisitClassLiteralContents(ClassLiteral* expr) {
+  Node* class_name = expr->raw_name() ? jsgraph()->Constant(expr->name())
+                                      : jsgraph()->UndefinedConstant();
+
+  // The class name is expected on the operand stack.
+  environment()->Push(class_name);
+  VisitForValueOrTheHole(expr->extends());
+  VisitForValue(expr->constructor());
+
+  // Create node to instantiate a new class.
+  Node* constructor = environment()->Pop();
+  Node* extends = environment()->Pop();
+  Node* name = environment()->Pop();
+  Node* script = jsgraph()->Constant(info()->script());
+  Node* start = jsgraph()->Constant(expr->start_position());
+  Node* end = jsgraph()->Constant(expr->end_position());
+  const Operator* opc = javascript()->CallRuntime(Runtime::kDefineClass, 6);
+  Node* literal = NewNode(opc, name, extends, constructor, script, start, end);
+
+  // The prototype is ensured to exist by Runtime_DefineClass. No access check
+  // is needed here since the constructor is created by the class literal.
+  Node* proto =
+      BuildLoadObjectField(literal, JSFunction::kPrototypeOrInitialMapOffset);
+
+  // The class literal and the prototype are both expected on the operand stack
+  // during evaluation of the method values.
+  environment()->Push(literal);
+  environment()->Push(proto);
+
+  // Create nodes to store method values into the literal.
+  for (int i = 0; i < expr->properties()->length(); i++) {
+    ObjectLiteral::Property* property = expr->properties()->at(i);
+    environment()->Push(property->is_static() ? literal : proto);
+
+    VisitForValue(property->key());
+    VisitForValue(property->value());
+    Node* value = environment()->Pop();
+    Node* key = environment()->Pop();
+    Node* receiver = environment()->Pop();
+    switch (property->kind()) {
+      case ObjectLiteral::Property::CONSTANT:
+      case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        UNREACHABLE();
+      case ObjectLiteral::Property::COMPUTED:
+      case ObjectLiteral::Property::PROTOTYPE: {
+        const Operator* op =
+            javascript()->CallRuntime(Runtime::kDefineClassMethod, 3);
+        NewNode(op, receiver, key, value);
+        break;
+      }
+      case ObjectLiteral::Property::GETTER: {
+        const Operator* op = javascript()->CallRuntime(
+            Runtime::kDefineGetterPropertyUnchecked, 3);
+        NewNode(op, receiver, key, value);
+        break;
+      }
+      case ObjectLiteral::Property::SETTER: {
+        const Operator* op = javascript()->CallRuntime(
+            Runtime::kDefineSetterPropertyUnchecked, 3);
+        NewNode(op, receiver, key, value);
+        break;
+      }
+    }
+
+    // TODO(mstarzinger): This is temporary to make "super" work and replicates
+    // the existing FullCodeGenerator::NeedsHomeObject predicate.
+    if (FunctionLiteral::NeedsHomeObject(property->value())) {
+      Unique<Name> name =
+          MakeUnique(isolate()->factory()->home_object_symbol());
+      NewNode(javascript()->StoreNamed(strict_mode(), name), value, receiver);
+    }
+  }
+
+  // Transform both the class literal and the prototype to fast properties.
+  const Operator* op = javascript()->CallRuntime(Runtime::kToFastProperties, 1);
+  NewNode(op, environment()->Pop());  // prototype
+  NewNode(op, environment()->Pop());  // literal
+
+  // Assign to class variable.
+  if (expr->scope() != NULL) {
+    DCHECK_NOT_NULL(expr->class_variable_proxy());
+    Variable* var = expr->class_variable_proxy()->var();
+    BuildVariableAssignment(var, literal, Token::INIT_CONST, BailoutId::None());
+  }
+
+  ast_context()->ProduceValue(literal);
 }
 
 
@@ -1829,6 +1932,18 @@ Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context, Node* closure) {
 }
 
 
+Node* AstGraphBuilder::BuildLocalBlockContext(Scope* scope) {
+  Node* closure = GetFunctionClosure();
+
+  // Allocate a new local context.
+  const Operator* op = javascript()->CreateBlockContext();
+  Node* scope_info = jsgraph()->Constant(scope->GetScopeInfo());
+  Node* local_context = NewNode(op, scope_info, closure);
+
+  return local_context;
+}
+
+
 Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
   if (arguments == NULL) return NULL;
 
index 92c8712..8493d42 100644 (file)
@@ -72,8 +72,9 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   // Builder to create a receiver check for sloppy mode.
   Node* BuildPatchReceiverToGlobalProxy(Node* receiver);
 
-  // Builder to create a local function context.
+  // Builders to create local function and block contexts.
   Node* BuildLocalFunctionContext(Node* context, Node* closure);
+  Node* BuildLocalBlockContext(Scope* scope);
 
   // Builder to create an arguments object if it is used.
   Node* BuildArgumentsObject(Variable* arguments);
@@ -174,6 +175,7 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   void VisitForEffect(Expression* expr);
   void VisitForValue(Expression* expr);
   void VisitForValueOrNull(Expression* expr);
+  void VisitForValueOrTheHole(Expression* expr);
   void VisitForValues(ZoneList<Expression*>* exprs);
 
   // Common for all IterationStatement bodies.
@@ -196,6 +198,9 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   // Dispatched from VisitForInStatement.
   void VisitForInAssignment(Expression* expr, Node* value);
 
+  // Dispatched from VisitClassLiteral.
+  void VisitClassLiteralContents(ClassLiteral* expr);
+
   // Builds deoptimization for a given node.
   void PrepareFrameState(
       Node* node, BailoutId ast_id,
index 082f11d..c43ed1b 100644 (file)
@@ -756,8 +756,6 @@ Handle<Code> Pipeline::GenerateCode() {
       info()->function()->dont_optimize_reason() == kTryFinallyStatement ||
       // TODO(turbofan): Make super work and remove this bailout.
       info()->function()->dont_optimize_reason() == kSuperReference ||
-      // TODO(turbofan): Make class literals work and remove this bailout.
-      info()->function()->dont_optimize_reason() == kClassLiteral ||
       // TODO(turbofan): Make OSR work with inner loops and remove this bailout.
       (info()->is_osr() && !FLAG_turbo_osr)) {
     return Handle<Code>::null();
index ec674ff..0d1ff4f 100644 (file)
@@ -1589,8 +1589,7 @@ void FullCodeGenerator::VisitClassLiteral(ClassLiteral* lit) {
 
   {
     EnterBlockScopeIfNeeded block_scope_state(
-        this, lit->scope(), BailoutId::None(), BailoutId::None(),
-        BailoutId::None());
+        this, lit->scope(), lit->EntryId(), lit->DeclsId(), lit->ExitId());
 
     if (lit->raw_name() != NULL) {
       __ Push(lit->name());
index c4f1bae..c7bd5cb 100644 (file)
@@ -251,6 +251,7 @@ TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
     case FIXED_ARRAY_TYPE:
     case BYTE_ARRAY_TYPE:
     case FOREIGN_TYPE:
+    case SCRIPT_TYPE:
     case CODE_TYPE:
       return kInternal & kTaggedPointer;
     default:
index eb39760..df65afa 100644 (file)
@@ -522,3 +522,24 @@ TEST(RegExpLiteral) {
   T.CheckTrue(T.Val("abc"));
   T.CheckFalse(T.Val("xyz"));
 }
+
+
+TEST(ClassLiteral) {
+  FLAG_harmony_classes = true;
+  FLAG_harmony_sloppy = true;
+  FLAG_harmony_object_literals = true;
+  const char* src =
+      "(function(a,b) {"
+      "  class C {"
+      "    x() { return a; }"
+      "    static y() { return b; }"
+      "    get z() { return 0; }"
+      "    constructor() {}"
+      "  }"
+      "  return new C().x() + C.y();"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
index 2e76927..26ec10b 100644 (file)
   # nosse2. Also for arm novfp3.
   'regress/regress-2989': [FAIL, NO_VARIANTS, ['system == linux and arch == x87 or arch == arm and simulator == True', PASS]],
 
+  # Skip endain dependent test for mips due to different typed views of the same
+  # array buffer.
+  'nans': [PASS, ],
+
   # This test variant makes only sense on arm.
   'math-floor-of-div-nosudiv': [PASS, SLOW, ['arch not in [arm, arm64, android_arm, android_arm64]', SKIP]],