ES6: Add support for super in object literals
authorarv@chromium.org <arv@chromium.org>
Tue, 11 Nov 2014 19:54:21 +0000 (19:54 +0000)
committerarv@chromium.org <arv@chromium.org>
Tue, 11 Nov 2014 19:54:56 +0000 (19:54 +0000)
This only available under --harmony-classes

BUG=v8:3571
LOG=Y
R=dslomov@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#25271}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25271 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

15 files changed:
src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast.cc
src/ast.h
src/full-codegen.h
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/preparser.h
src/runtime/runtime-classes.cc
src/scopes.cc
src/scopes.h
src/x64/full-codegen-x64.cc
test/cctest/test-parsing.cc
test/mjsunit/harmony/object-literals-super.js [new file with mode: 0644]
test/mjsunit/mjsunit.status

index 1710b60..f4938a7 100644 (file)
@@ -1383,6 +1383,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
 }
 
 
+void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
+                                                  int offset) {
+  if (NeedsHomeObject(initializer)) {
+    __ ldr(StoreDescriptor::ReceiverRegister(), MemOperand(sp));
+    __ mov(StoreDescriptor::NameRegister(),
+           Operand(isolate()->factory()->home_object_symbol()));
+    __ ldr(StoreDescriptor::ValueRegister(),
+           MemOperand(sp, offset * kPointerSize));
+    CallStoreIC();
+  }
+}
+
+
 void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
                                                       TypeofState typeof_state,
                                                       Label* slow) {
@@ -1739,6 +1752,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
             __ ldr(StoreDescriptor::ReceiverRegister(), MemOperand(sp));
             CallStoreIC(key->LiteralFeedbackId());
             PrepareForBailoutForId(key->id(), NO_REGISTERS);
+
+            if (NeedsHomeObject(value)) {
+              __ Move(StoreDescriptor::ReceiverRegister(), r0);
+              __ mov(StoreDescriptor::NameRegister(),
+                     Operand(isolate()->factory()->home_object_symbol()));
+              __ ldr(StoreDescriptor::ValueRegister(), MemOperand(sp));
+              CallStoreIC();
+            }
           } else {
             VisitForEffect(value);
           }
@@ -1750,6 +1771,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
         VisitForStackValue(key);
         VisitForStackValue(value);
         if (property->emit_store()) {
+          EmitSetHomeObjectIfNeeded(value, 2);
           __ mov(r0, Operand(Smi::FromInt(SLOPPY)));  // PropertyAttributes
           __ push(r0);
           __ CallRuntime(Runtime::kSetProperty, 4);
@@ -1787,7 +1809,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ push(r0);
     VisitForStackValue(it->first);
     EmitAccessor(it->second->getter);
+    EmitSetHomeObjectIfNeeded(it->second->getter, 2);
     EmitAccessor(it->second->setter);
+    EmitSetHomeObjectIfNeeded(it->second->setter, 3);
     __ mov(r0, Operand(Smi::FromInt(NONE)));
     __ push(r0);
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
@@ -2534,6 +2558,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
     __ push(scratch);
     VisitForStackValue(key);
     VisitForStackValue(value);
+    EmitSetHomeObjectIfNeeded(value, 2);
 
     switch (property->kind()) {
       case ObjectLiteral::Property::CONSTANT:
index 4262875..b693d59 100644 (file)
@@ -1372,6 +1372,18 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
 }
 
 
+void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
+                                                  int offset) {
+  if (NeedsHomeObject(initializer)) {
+    __ Peek(StoreDescriptor::ReceiverRegister(), 0);
+    __ Mov(StoreDescriptor::NameRegister(),
+           Operand(isolate()->factory()->home_object_symbol()));
+    __ Peek(StoreDescriptor::ValueRegister(), offset * kPointerSize);
+    CallStoreIC();
+  }
+}
+
+
 void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
                                                       TypeofState typeof_state,
                                                       Label* slow) {
@@ -1721,6 +1733,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
             __ Peek(StoreDescriptor::ReceiverRegister(), 0);
             CallStoreIC(key->LiteralFeedbackId());
             PrepareForBailoutForId(key->id(), NO_REGISTERS);
+
+            if (NeedsHomeObject(value)) {
+              __ Mov(StoreDescriptor::ReceiverRegister(), x0);
+              __ Mov(StoreDescriptor::NameRegister(),
+                     Operand(isolate()->factory()->home_object_symbol()));
+              __ Peek(StoreDescriptor::ValueRegister(), 0);
+              CallStoreIC();
+            }
           } else {
             VisitForEffect(value);
           }
@@ -1732,6 +1752,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
           __ Push(x0);
           VisitForStackValue(key);
           VisitForStackValue(value);
+          EmitSetHomeObjectIfNeeded(value, 2);
           __ Mov(x0, Smi::FromInt(SLOPPY));  // Strict mode
           __ Push(x0);
           __ CallRuntime(Runtime::kSetProperty, 4);
@@ -1769,7 +1790,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
       __ Push(x10);
       VisitForStackValue(it->first);
       EmitAccessor(it->second->getter);
+      EmitSetHomeObjectIfNeeded(it->second->getter, 2);
       EmitAccessor(it->second->setter);
+      EmitSetHomeObjectIfNeeded(it->second->setter, 3);
       __ Mov(x10, Smi::FromInt(NONE));
       __ Push(x10);
       __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
@@ -2203,6 +2226,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
     __ Push(scratch);
     VisitForStackValue(key);
     VisitForStackValue(value);
+    EmitSetHomeObjectIfNeeded(value, 2);
 
     switch (property->kind()) {
       case ObjectLiteral::Property::CONSTANT:
index 26bc491..2edd51e 100644 (file)
@@ -151,6 +151,12 @@ StrictMode FunctionLiteral::strict_mode() const {
 }
 
 
+bool FunctionLiteral::needs_super_binding() const {
+  DCHECK_NOT_NULL(scope());
+  return scope()->uses_super() || scope()->inner_uses_super();
+}
+
+
 void FunctionLiteral::InitializeSharedInfo(
     Handle<Code> unoptimized_code) {
   for (RelocIterator it(*unoptimized_code); !it.done(); it.next()) {
index 1700916..d19028d 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -2497,6 +2497,12 @@ class FunctionLiteral FINAL : public Expression {
   bool is_expression() const { return IsExpression::decode(bitfield_); }
   bool is_anonymous() const { return IsAnonymous::decode(bitfield_); }
   StrictMode strict_mode() const;
+  bool needs_super_binding() const;
+
+  static bool NeedsHomeObject(Expression* literal) {
+    return literal != NULL && literal->IsFunctionLiteral() &&
+           literal->AsFunctionLiteral()->needs_super_binding();
+  }
 
   int materialized_literal_count() { return materialized_literal_count_; }
   int expected_property_count() { return expected_property_count_; }
index f032854..fd53cea 100644 (file)
@@ -614,6 +614,15 @@ class FullCodeGenerator: public AstVisitor {
 
   void EmitLoadHomeObject(SuperReference* expr);
 
+  static bool NeedsHomeObject(Expression* expr) {
+    return FunctionLiteral::NeedsHomeObject(expr);
+  }
+
+  // Adds the [[HomeObject]] to |initializer| if it is a FunctionLiteral.
+  // The value of the initializer is expected to be at the top of the stack.
+  // |offset| is the offset in the stack where the home object can be found.
+  void EmitSetHomeObjectIfNeeded(Expression* initializer, int offset);
+
   void EmitLoadSuperConstructor(SuperReference* expr);
 
   void CallIC(Handle<Code> code,
index b9b3eb0..e91609d 100644 (file)
@@ -5653,6 +5653,17 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
           if (property->emit_store()) {
             CHECK_ALIVE(VisitForValue(value));
             HValue* value = Pop();
+
+            // Add [[HomeObject]] to function literals.
+            if (FunctionLiteral::NeedsHomeObject(property->value())) {
+              Handle<Symbol> sym = isolate()->factory()->home_object_symbol();
+              HInstruction* store_home = BuildKeyedGeneric(
+                  STORE, NULL, value, Add<HConstant>(sym), literal);
+              AddInstruction(store_home);
+              DCHECK(store_home->HasObservableSideEffects());
+              Add<HSimulate>(property->value()->id(), REMOVABLE_SIMULATE);
+            }
+
             Handle<Map> map = property->GetReceiverType();
             Handle<String> name = property->key()->AsPropertyName();
             HInstruction* store;
@@ -5674,9 +5685,8 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
               }
             }
             AddInstruction(store);
-            if (store->HasObservableSideEffects()) {
-              Add<HSimulate>(key->id(), REMOVABLE_SIMULATE);
-            }
+            DCHECK(store->HasObservableSideEffects());
+            Add<HSimulate>(key->id(), REMOVABLE_SIMULATE);
           } else {
             CHECK_ALIVE(VisitForEffect(value));
           }
index acf59b4..ab9438a 100644 (file)
@@ -1306,6 +1306,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
 }
 
 
+void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
+                                                  int offset) {
+  if (NeedsHomeObject(initializer)) {
+    __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0));
+    __ mov(StoreDescriptor::NameRegister(),
+           Immediate(isolate()->factory()->home_object_symbol()));
+    __ mov(StoreDescriptor::ValueRegister(),
+           Operand(esp, offset * kPointerSize));
+    CallStoreIC();
+  }
+}
+
+
 void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
                                                       TypeofState typeof_state,
                                                       Label* slow) {
@@ -1670,6 +1683,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
             __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0));
             CallStoreIC(key->LiteralFeedbackId());
             PrepareForBailoutForId(key->id(), NO_REGISTERS);
+
+            if (NeedsHomeObject(value)) {
+              __ mov(StoreDescriptor::ReceiverRegister(), eax);
+              __ mov(StoreDescriptor::NameRegister(),
+                     Immediate(isolate()->factory()->home_object_symbol()));
+              __ mov(StoreDescriptor::ValueRegister(), Operand(esp, 0));
+              CallStoreIC();
+            }
           } else {
             VisitForEffect(value);
           }
@@ -1679,6 +1700,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
         VisitForStackValue(key);
         VisitForStackValue(value);
         if (property->emit_store()) {
+          EmitSetHomeObjectIfNeeded(value, 2);
           __ push(Immediate(Smi::FromInt(SLOPPY)));  // Strict mode
           __ CallRuntime(Runtime::kSetProperty, 4);
         } else {
@@ -1711,7 +1733,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ push(Operand(esp, 0));  // Duplicate receiver.
     VisitForStackValue(it->first);
     EmitAccessor(it->second->getter);
+    EmitSetHomeObjectIfNeeded(it->second->getter, 2);
     EmitAccessor(it->second->setter);
+    EmitSetHomeObjectIfNeeded(it->second->setter, 3);
     __ push(Immediate(Smi::FromInt(NONE)));
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
@@ -2448,6 +2472,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
     }
     VisitForStackValue(key);
     VisitForStackValue(value);
+    EmitSetHomeObjectIfNeeded(value, 2);
 
     switch (property->kind()) {
       case ObjectLiteral::Property::CONSTANT:
index 7c9791f..a93c1e5 100644 (file)
@@ -965,6 +965,7 @@ class PreParserScope {
   bool IsDeclared(const PreParserIdentifier& identifier) const { return false; }
   void DeclareParameter(const PreParserIdentifier& identifier, VariableMode) {}
   void RecordArgumentsUsage() {}
+  void RecordSuperUsage() {}
   void RecordThisUsage() {}
 
   // Allow scope->Foo() to work.
@@ -2508,6 +2509,7 @@ ParserBase<Traits>::ParseMemberWithNewPrefixesExpression(bool* ok) {
     int new_pos = position();
     ExpressionT result = this->EmptyExpression();
     if (Check(Token::SUPER)) {
+      scope_->RecordSuperUsage();
       result = this->SuperReference(scope_, factory());
     } else {
       result = this->ParseMemberWithNewPrefixesExpression(CHECK_OK);
@@ -2567,6 +2569,7 @@ ParserBase<Traits>::ParseMemberExpression(bool* ok) {
   } else if (peek() == Token::SUPER) {
     int beg_pos = position();
     Consume(Token::SUPER);
+    scope_->RecordSuperUsage();
     Token::Value next = peek();
     if (next == Token::PERIOD || next == Token::LBRACK ||
         next == Token::LPAREN) {
index 30ff918..d1dbe21 100644 (file)
@@ -113,6 +113,8 @@ RUNTIME_FUNCTION(Runtime_DefineClass) {
       isolate, JSObject::SetOwnPropertyIgnoreAttributes(
                    constructor, isolate->factory()->prototype_string(),
                    prototype, attribs));
+
+  // TODO(arv): Only do this conditionally.
   Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol());
   RETURN_FAILURE_ON_EXCEPTION(
       isolate, JSObject::SetOwnPropertyIgnoreAttributes(
@@ -153,11 +155,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
   CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 2);
 
-  RETURN_FAILURE_ON_EXCEPTION(
-      isolate, JSObject::SetOwnPropertyIgnoreAttributes(
-                   function, isolate->factory()->home_object_symbol(), object,
-                   DONT_ENUM));
-
   uint32_t index;
   if (key->ToArrayIndex(&index)) {
     RETURN_FAILURE_ON_EXCEPTION(
@@ -191,11 +188,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassGetter) {
                                      Runtime::ToName(isolate, key));
   RETURN_FAILURE_ON_EXCEPTION(
       isolate,
-      JSObject::SetOwnPropertyIgnoreAttributes(
-          getter, isolate->factory()->home_object_symbol(), object, DONT_ENUM));
-
-  RETURN_FAILURE_ON_EXCEPTION(
-      isolate,
       JSObject::DefineAccessor(object, name, getter,
                                isolate->factory()->null_value(), NONE));
   return isolate->heap()->undefined_value();
@@ -214,10 +206,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassSetter) {
                                      Runtime::ToName(isolate, key));
   RETURN_FAILURE_ON_EXCEPTION(
       isolate,
-      JSObject::SetOwnPropertyIgnoreAttributes(
-          setter, isolate->factory()->home_object_symbol(), object, DONT_ENUM));
-  RETURN_FAILURE_ON_EXCEPTION(
-      isolate,
       JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
                                setter, NONE));
   return isolate->heap()->undefined_value();
index 51c0065..92a8dc7 100644 (file)
@@ -160,16 +160,18 @@ void Scope::SetDefaults(ScopeType scope_type,
   scope_inside_with_ = false;
   scope_contains_with_ = false;
   scope_calls_eval_ = false;
-  scope_uses_this_ = false;
   scope_uses_arguments_ = false;
+  scope_uses_super_ = false;
+  scope_uses_this_ = false;
   asm_module_ = false;
   asm_function_ = outer_scope != NULL && outer_scope->asm_module_;
   // Inherit the strict mode from the parent scope.
   strict_mode_ = outer_scope != NULL ? outer_scope->strict_mode_ : SLOPPY;
   outer_scope_calls_sloppy_eval_ = false;
   inner_scope_calls_eval_ = false;
-  inner_scope_uses_this_ = false;
   inner_scope_uses_arguments_ = false;
+  inner_scope_uses_this_ = false;
+  inner_scope_uses_super_ = false;
   force_eager_compilation_ = false;
   force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
       ? outer_scope->has_forced_context_allocation() : false;
@@ -889,12 +891,14 @@ void Scope::Print(int n) {
   if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n");
   if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n");
   if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
-  if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
   if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n");
-  if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
+  if (scope_uses_super_) Indent(n1, "// scope uses 'super'\n");
+  if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
   if (inner_scope_uses_arguments_) {
     Indent(n1, "// inner scope uses 'arguments'\n");
   }
+  if (inner_scope_uses_super_) Indent(n1, "// inner scope uses 'super'\n");
+  if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
   if (outer_scope_calls_sloppy_eval_) {
     Indent(n1, "// outer scope calls 'eval' in sloppy context\n");
   }
@@ -1177,15 +1181,18 @@ void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) {
       inner_scope_calls_eval_ = true;
     }
     // If the inner scope is an arrow function, propagate the flags tracking
-    // usage of this/arguments, but do not propagate them out from normal
+    // usage of arguments/super/this, but do not propagate them out from normal
     // functions.
     if (!inner->is_function_scope() || inner->is_arrow_scope()) {
-      if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
-        inner_scope_uses_this_ = true;
-      }
       if (inner->scope_uses_arguments_ || inner->inner_scope_uses_arguments_) {
         inner_scope_uses_arguments_ = true;
       }
+      if (inner->scope_uses_super_ || inner->inner_scope_uses_super_) {
+        inner_scope_uses_super_ = true;
+      }
+      if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
+        inner_scope_uses_this_ = true;
+      }
     }
     if (inner->force_eager_compilation_) {
       force_eager_compilation_ = true;
index 25305de..b506c58 100644 (file)
@@ -211,12 +211,15 @@ class Scope: public ZoneObject {
   // Inform the scope that the corresponding code contains an eval call.
   void RecordEvalCall() { if (!is_global_scope()) scope_calls_eval_ = true; }
 
-  // Inform the scope that the corresponding code uses "this".
-  void RecordThisUsage() { scope_uses_this_ = true; }
-
   // Inform the scope that the corresponding code uses "arguments".
   void RecordArgumentsUsage() { scope_uses_arguments_ = true; }
 
+  // Inform the scope that the corresponding code uses "super".
+  void RecordSuperUsage() { scope_uses_super_ = true; }
+
+  // Inform the scope that the corresponding code uses "this".
+  void RecordThisUsage() { scope_uses_this_ = true; }
+
   // Set the strict mode flag (unless disabled by a global flag).
   void SetStrictMode(StrictMode strict_mode) { strict_mode_ = strict_mode; }
 
@@ -301,14 +304,18 @@ class Scope: public ZoneObject {
   // Does this scope contain a with statement.
   bool contains_with() const { return scope_contains_with_; }
 
-  // Does this scope access "this".
-  bool uses_this() const { return scope_uses_this_; }
-  // Does any inner scope access "this".
-  bool inner_uses_this() const { return inner_scope_uses_this_; }
   // Does this scope access "arguments".
   bool uses_arguments() const { return scope_uses_arguments_; }
   // Does any inner scope access "arguments".
   bool inner_uses_arguments() const { return inner_scope_uses_arguments_; }
+  // Does this scope access "super".
+  bool uses_super() const { return scope_uses_super_; }
+  // Does any inner scope access "super".
+  bool inner_uses_super() const { return inner_scope_uses_super_; }
+  // Does this scope access "this".
+  bool uses_this() const { return scope_uses_this_; }
+  // Does any inner scope access "this".
+  bool inner_uses_this() const { return inner_scope_uses_this_; }
 
   // ---------------------------------------------------------------------------
   // Accessors.
@@ -486,10 +493,12 @@ class Scope: public ZoneObject {
   // This scope or a nested catch scope or with scope contain an 'eval' call. At
   // the 'eval' call site this scope is the declaration scope.
   bool scope_calls_eval_;
-  // This scope uses "this".
-  bool scope_uses_this_;
   // This scope uses "arguments".
   bool scope_uses_arguments_;
+  // This scope uses "super".
+  bool scope_uses_super_;
+  // This scope uses "this".
+  bool scope_uses_this_;
   // This scope contains an "use asm" annotation.
   bool asm_module_;
   // This scope's outer context is an asm module.
@@ -503,8 +512,9 @@ class Scope: public ZoneObject {
   // Computed via PropagateScopeInfo.
   bool outer_scope_calls_sloppy_eval_;
   bool inner_scope_calls_eval_;
-  bool inner_scope_uses_this_;
   bool inner_scope_uses_arguments_;
+  bool inner_scope_uses_super_;
+  bool inner_scope_uses_this_;
   bool force_eager_compilation_;
   bool force_context_allocation_;
 
index 25bfd34..cbee082 100644 (file)
@@ -1341,6 +1341,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
 }
 
 
+void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
+                                                  int offset) {
+  if (NeedsHomeObject(initializer)) {
+    __ movp(StoreDescriptor::ReceiverRegister(), Operand(rsp, 0));
+    __ Move(StoreDescriptor::NameRegister(),
+            isolate()->factory()->home_object_symbol());
+    __ movp(StoreDescriptor::ValueRegister(),
+            Operand(rsp, offset * kPointerSize));
+    CallStoreIC();
+  }
+}
+
+
 void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
                                                       TypeofState typeof_state,
                                                       Label* slow) {
@@ -1704,6 +1717,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
             __ movp(StoreDescriptor::ReceiverRegister(), Operand(rsp, 0));
             CallStoreIC(key->LiteralFeedbackId());
             PrepareForBailoutForId(key->id(), NO_REGISTERS);
+
+            if (NeedsHomeObject(value)) {
+              __ movp(StoreDescriptor::ReceiverRegister(), rax);
+              __ Move(StoreDescriptor::NameRegister(),
+                      isolate()->factory()->home_object_symbol());
+              __ movp(StoreDescriptor::ValueRegister(), Operand(rsp, 0));
+              CallStoreIC();
+            }
           } else {
             VisitForEffect(value);
           }
@@ -1713,6 +1734,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
         VisitForStackValue(key);
         VisitForStackValue(value);
         if (property->emit_store()) {
+          EmitSetHomeObjectIfNeeded(value, 2);
           __ Push(Smi::FromInt(SLOPPY));  // Strict mode
           __ CallRuntime(Runtime::kSetProperty, 4);
         } else {
@@ -1745,7 +1767,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ Push(Operand(rsp, 0));  // Duplicate receiver.
     VisitForStackValue(it->first);
     EmitAccessor(it->second->getter);
+    EmitSetHomeObjectIfNeeded(it->second->getter, 2);
     EmitAccessor(it->second->setter);
+    EmitSetHomeObjectIfNeeded(it->second->setter, 3);
     __ Push(Smi::FromInt(NONE));
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
@@ -2447,6 +2471,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
     }
     VisitForStackValue(key);
     VisitForStackValue(value);
+    EmitSetHomeObjectIfNeeded(value, 2);
 
     switch (property->kind()) {
       case ObjectLiteral::Property::CONSTANT:
index 79d7654..7a07eab 100644 (file)
@@ -919,7 +919,7 @@ static int Utf8LengthHelper(const char* s) {
 }
 
 
-TEST(ScopeUsesThisAndArguments) {
+TEST(ScopeUsesArgumentsSuperThis) {
   static const struct {
     const char* prefix;
     const char* suffix;
@@ -928,62 +928,66 @@ TEST(ScopeUsesThisAndArguments) {
     { "var f = () => {", "}" },
   };
 
+  enum Expected {
+    NONE = 0,
+    ARGUMENTS = 1,
+    SUPER = 2,
+    THIS = 4,
+    INNER_ARGUMENTS = 8,
+    INNER_SUPER = 16,
+    INNER_THIS = 32
+  };
+
   static const struct {
     const char* body;
-    bool uses_this;
-    bool uses_arguments;
-    bool inner_uses_this;
-    bool inner_uses_arguments;
+    int expected;
   } source_data[] = {
-    { "",
-      false, false, false, false },
-    { "return this",
-      true, false, false, false },
-    { "return arguments",
-      false, true, false, false },
-    { "return arguments[0]",
-      false, true, false, false },
-    { "return this + arguments[0]",
-      true, true, false, false },
-    { "return x => this + x",
-      false, false, true, false },
-    { "this.foo = 42;",
-      true, false, false, false },
-    { "this.foo();",
-      true, false, false, false },
-    { "if (foo()) { this.f() }",
-      true, false, false, false },
-    { "if (arguments.length) { this.f() }",
-      true, true, false, false },
-    { "while (true) { this.f() }",
-      true, false, false, false },
-    { "if (true) { while (true) this.foo(arguments) }",
-      true, true, false, false },
-    // Multiple nesting levels must work as well.
-    { "while (true) { while (true) { while (true) return this } }",
-      true, false, false, false },
-    { "if (1) { return () => { while (true) new this() } }",
-      false, false, true, false },
-    // Note that propagation of the inner_uses_this() value does not
-    // cross boundaries of normal functions onto parent scopes.
-    { "return function (x) { return this + x }",
-      false, false, false, false },
-    { "var x = function () { this.foo = 42 };",
-      false, false, false, false },
-    { "if (1) { return function () { while (true) new this() } }",
-      false, false, false, false },
-    { "return function (x) { return () => this }",
-      false, false, false, false },
-    // Flags must be correctly set when using block scoping.
-    { "\"use strict\"; while (true) { let x; this, arguments; }",
-      false, false, true, true },
-    { "\"use strict\"; if (foo()) { let x; this.f() }",
-      false, false, true, false },
-    { "\"use strict\"; if (1) {"
-      "  let x; return function () { return this + arguments }"
-      "}",
-      false, false, false, false },
-  };
+        {"", NONE},
+        {"return this", THIS},
+        {"return arguments", ARGUMENTS},
+        {"return super()", SUPER},
+        {"return super.x", SUPER},
+        {"return arguments[0]", ARGUMENTS},
+        {"return this + arguments[0]", ARGUMENTS | THIS},
+        {"return this + arguments[0] + super.x", ARGUMENTS | SUPER | THIS},
+        {"return x => this + x", INNER_THIS},
+        {"return x => super() + x", INNER_SUPER},
+        {"this.foo = 42;", THIS},
+        {"this.foo();", THIS},
+        {"if (foo()) { this.f() }", THIS},
+        {"if (foo()) { super.f() }", SUPER},
+        {"if (arguments.length) { this.f() }", ARGUMENTS | THIS},
+        {"while (true) { this.f() }", THIS},
+        {"while (true) { super.f() }", SUPER},
+        {"if (true) { while (true) this.foo(arguments) }", ARGUMENTS | THIS},
+        // Multiple nesting levels must work as well.
+        {"while (true) { while (true) { while (true) return this } }", THIS},
+        {"while (true) { while (true) { while (true) return super() } }",
+         SUPER},
+        {"if (1) { return () => { while (true) new this() } }", INNER_THIS},
+        {"if (1) { return () => { while (true) new super() } }", INNER_SUPER},
+        // Note that propagation of the inner_uses_this() value does not
+        // cross boundaries of normal functions onto parent scopes.
+        {"return function (x) { return this + x }", NONE},
+        {"return function (x) { return super() + x }", NONE},
+        {"var x = function () { this.foo = 42 };", NONE},
+        {"var x = function () { super.foo = 42 };", NONE},
+        {"if (1) { return function () { while (true) new this() } }", NONE},
+        {"if (1) { return function () { while (true) new super() } }", NONE},
+        {"return function (x) { return () => this }", NONE},
+        {"return function (x) { return () => super() }", NONE},
+        // Flags must be correctly set when using block scoping.
+        {"\"use strict\"; while (true) { let x; this, arguments; }",
+         INNER_ARGUMENTS | INNER_THIS},
+        {"\"use strict\"; while (true) { let x; this, super(), arguments; }",
+         INNER_ARGUMENTS | INNER_SUPER | INNER_THIS},
+        {"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS},
+        {"\"use strict\"; if (foo()) { let x; super.f() }", INNER_SUPER},
+        {"\"use strict\"; if (1) {"
+         "  let x; return function () { return this + super() + arguments }"
+         "}",
+         NONE},
+    };
 
   i::Isolate* isolate = CcTest::i_isolate();
   i::Factory* factory = isolate->factory();
@@ -1013,6 +1017,7 @@ TEST(ScopeUsesThisAndArguments) {
                                          isolate->unicode_cache()};
       i::Parser parser(&info, &parse_info);
       parser.set_allow_arrow_functions(true);
+      parser.set_allow_classes(true);
       parser.set_allow_harmony_scoping(true);
       info.MarkAsGlobal();
       parser.Parse();
@@ -1025,11 +1030,16 @@ TEST(ScopeUsesThisAndArguments) {
       CHECK_EQ(1, global_scope->inner_scopes()->length());
 
       i::Scope* scope = global_scope->inner_scopes()->at(0);
-      CHECK_EQ(source_data[i].uses_this, scope->uses_this());
-      CHECK_EQ(source_data[i].uses_arguments, scope->uses_arguments());
-      CHECK_EQ(source_data[i].inner_uses_this, scope->inner_uses_this());
-      CHECK_EQ(source_data[i].inner_uses_arguments,
+      CHECK_EQ((source_data[i].expected & ARGUMENTS) != 0,
+               scope->uses_arguments());
+      CHECK_EQ((source_data[i].expected & SUPER) != 0, scope->uses_super());
+      CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this());
+      CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0,
                scope->inner_uses_arguments());
+      CHECK_EQ((source_data[i].expected & INNER_SUPER) != 0,
+               scope->inner_uses_super());
+      CHECK_EQ((source_data[i].expected & INNER_THIS) != 0,
+               scope->inner_uses_this());
     }
   }
 }
diff --git a/test/mjsunit/harmony/object-literals-super.js b/test/mjsunit/harmony/object-literals-super.js
new file mode 100644 (file)
index 0000000..ec22b8a
--- /dev/null
@@ -0,0 +1,168 @@
+// 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-classes --allow-natives-syntax
+
+
+(function TestHomeObject() {
+  var object = {
+    method() {
+      return super.method();
+    },
+    get getter() {
+      return super.getter;
+    },
+    set setter(v) {
+      super.setter = v;
+    },
+    get accessor() {
+      return super.accessor;
+    },
+    set accessor(v) {
+      super.accessor = v;
+    },
+    property: function() {
+      super.property();
+    },
+    propertyWithParen: (function() {
+      super.property();
+    }),
+    propertyWithParens: ((function() {
+      super.property();
+    })),
+
+    methodNoSuper() {},
+    get getterNoSuper() {},
+    set setterNoSuper(v) {},
+    get accessorNoSuper() {},
+    set accessorNoSuper(v) {},
+    propertyNoSuper: function() {},
+    propertyWithParenNoSuper: (function() {}),
+    propertyWithParensNoSuper: ((function() {}))
+  };
+
+  assertEquals(object, object.method[%HomeObjectSymbol()]);
+  var desc = Object.getOwnPropertyDescriptor(object, 'getter');
+  assertEquals(object, desc.get[%HomeObjectSymbol()]);
+  desc = Object.getOwnPropertyDescriptor(object, 'setter');
+  assertEquals(object, desc.set[%HomeObjectSymbol()]);
+  desc = Object.getOwnPropertyDescriptor(object, 'accessor');
+  assertEquals(object, desc.get[%HomeObjectSymbol()]);
+  assertEquals(object, desc.set[%HomeObjectSymbol()]);
+  assertEquals(object, object.property[%HomeObjectSymbol()]);
+  assertEquals(object, object.propertyWithParen[%HomeObjectSymbol()]);
+  assertEquals(object, object.propertyWithParens[%HomeObjectSymbol()]);
+
+  assertEquals(undefined, object.methodNoSuper[%HomeObjectSymbol()]);
+  desc = Object.getOwnPropertyDescriptor(object, 'getterNoSuper');
+  assertEquals(undefined, desc.get[%HomeObjectSymbol()]);
+  desc = Object.getOwnPropertyDescriptor(object, 'setterNoSuper');
+  assertEquals(undefined, desc.set[%HomeObjectSymbol()]);
+  desc = Object.getOwnPropertyDescriptor(object, 'accessorNoSuper');
+  assertEquals(undefined, desc.get[%HomeObjectSymbol()]);
+  assertEquals(undefined, desc.set[%HomeObjectSymbol()]);
+  assertEquals(undefined, object.propertyNoSuper[%HomeObjectSymbol()]);
+  assertEquals(undefined, object.propertyWithParenNoSuper[%HomeObjectSymbol()]);
+  assertEquals(undefined,
+               object.propertyWithParensNoSuper[%HomeObjectSymbol()]);
+})();
+
+
+(function TestMethod() {
+  var object = {
+    __proto__: {
+      method(x) {
+        return 'proto' + x;
+      }
+    },
+    method(x) {
+      return super.method(x);
+    }
+  };
+  assertEquals('proto42', object.method(42));
+})();
+
+
+(function TestGetter() {
+  var object = {
+    __proto__: {
+      _x: 42,
+      get x() {
+        return 'proto' + this._x;
+      }
+    },
+    get x() {
+      return super.x;
+    }
+  };
+  assertEquals('proto42', object.x);
+})();
+
+
+(function TestSetter() {
+  var object = {
+    __proto__: {
+      _x: 0,
+      set x(v) {
+        return this._x = v;
+      }
+    },
+    set x(v) {
+      super.x = v;
+    }
+  };
+  assertEquals(1, object.x = 1);
+  assertEquals(1, object._x);
+  assertEquals(0, Object.getPrototypeOf(object)._x);
+})();
+
+
+(function TestMethodAsProperty() {
+  var object = {
+    __proto__: {
+      method: function(x) {
+        return 'proto' + x;
+      }
+    },
+    method: function(x) {
+      return super.method(x);
+    }
+  };
+  assertEquals('proto42', object.method(42));
+})();
+
+
+(function TestOptimized() {
+  // Object literals without any accessors get optimized.
+  var object = {
+    method() {
+      return super.toString;
+    }
+  };
+  assertEquals(Object.prototype.toString, object.method());
+})();
+
+
+(function TestConciseGenerator() {
+  var o = {
+    __proto__: {
+      m() {
+        return 42;
+      }
+    },
+    *g() {
+      yield super.m();
+    },
+    g2: function*() {
+      yield super.m() + 1;
+    },
+    g3: (function*() {
+      yield super.m() + 2;
+    })
+  };
+
+  assertEquals(42, o.g().next().value);
+  assertEquals(43, o.g2().next().value);
+  assertEquals(44, o.g3().next().value);
+})();
index 0ab7289..e655a5a 100644 (file)
   'regress/regress-crbug-259300': [PASS, NO_VARIANTS],
   'regress/regress-frame-details-null-receiver': [PASS, NO_VARIANTS],
 
+  # TODO(arv): TurboFan does not yet add [[HomeObject]] as needed.
+  'harmony/object-literals-super': [PASS, NO_VARIANTS],
+
   ##############################################################################
   # Too slow in debug mode with --stress-opt mode.
   'compiler/regress-stacktrace-methods': [PASS, ['mode == debug', SKIP]],