ES6 computed property names
authorarv <arv@chromium.org>
Wed, 17 Dec 2014 18:38:38 +0000 (10:38 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 17 Dec 2014 18:38:55 +0000 (18:38 +0000)
This adds support for computed property names, under the flag
--harmony-computed-property-names, for both object literals and
classes.

This is a revert of the revert, a76419f0f497db156eb1010e9b4be398951280cb.

This changes to do an early bailout in
HOptimizedGraphBuilder::VisitObjectLiteral instead of doing that in the later
loop.

BUG=v8:3754
LOG=Y
TBR=dslomov@chromium.org

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

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

28 files changed:
src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast-numbering.cc
src/ast.cc
src/ast.h
src/bailout-reason.h
src/bootstrapper.cc
src/builtins.h
src/compiler/ast-graph-builder.cc
src/flag-definitions.h
src/full-codegen.cc
src/full-codegen.h
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/runtime.js
src/runtime/runtime-classes.cc
src/runtime/runtime-object.cc
src/runtime/runtime.h
src/typing.cc
src/x64/full-codegen-x64.cc
test/cctest/test-parsing.cc
test/mjsunit/harmony/computed-property-names-classes.js [new file with mode: 0644]
test/mjsunit/harmony/computed-property-names-object-literals-methods.js [new file with mode: 0644]
test/mjsunit/harmony/computed-property-names.js [new file with mode: 0644]

index 3dc5420..24ccf09 100644 (file)
@@ -1686,11 +1686,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
   expr->CalculateEmitStore(zone());
 
   AccessorTable accessor_table(zone());
-  for (int i = 0; i < expr->properties()->length(); i++) {
-    ObjectLiteral::Property* property = expr->properties()->at(i);
+  int property_index = 0;
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+    if (property->is_computed_name()) break;
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
     if (!result_saved) {
       __ push(r0);  // Save result on stack
@@ -1778,6 +1780,67 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
 
+  // Object literals have two parts. The "static" part on the left contains no
+  // computed property names, and so we can compute its map ahead of time; see
+  // runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
+  // starts with the first computed property name, and continues with all
+  // properties to its right.  All the code from above initializes the static
+  // component of the object literal, and arranges for the map of the result to
+  // reflect the static order in which the keys appear. For the dynamic
+  // properties, we compile them into a series of "SetOwnProperty" runtime
+  // calls. This will preserve insertion order.
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+
+    Expression* value = property->value();
+    if (!result_saved) {
+      __ push(r0);  // Save result on the stack
+      result_saved = true;
+    }
+
+    __ ldr(r0, MemOperand(sp));  // Duplicate receiver.
+    __ push(r0);
+
+    if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
+      DCHECK(!property->is_computed_name());
+      VisitForStackValue(value);
+      if (property->emit_store()) {
+        __ CallRuntime(Runtime::kInternalSetPrototype, 2);
+      } else {
+        __ Drop(2);
+      }
+    } else {
+      EmitPropertyKey(property);
+      VisitForStackValue(value);
+
+      switch (property->kind()) {
+        case ObjectLiteral::Property::CONSTANT:
+        case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        case ObjectLiteral::Property::COMPUTED:
+          if (property->emit_store()) {
+            __ mov(r0, Operand(Smi::FromInt(NONE)));
+            __ push(r0);
+            __ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
+          } else {
+            __ Drop(3);
+          }
+          break;
+
+        case ObjectLiteral::Property::PROTOTYPE:
+          UNREACHABLE();
+          break;
+
+        case ObjectLiteral::Property::GETTER:
+          __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
+          break;
+
+        case ObjectLiteral::Property::SETTER:
+          __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
+          break;
+      }
+    }
+  }
+
   if (expr->has_function()) {
     DCHECK(result_saved);
     __ ldr(r0, MemOperand(sp));
@@ -2478,9 +2541,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
 
   for (int i = 0; i < lit->properties()->length(); i++) {
     ObjectLiteral::Property* property = lit->properties()->at(i);
-    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
-    DCHECK(key != NULL);
 
     if (property->is_static()) {
       __ ldr(scratch, MemOperand(sp, kPointerSize));  // constructor
@@ -2488,7 +2549,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
       __ ldr(scratch, MemOperand(sp, 0));  // prototype
     }
     __ push(scratch);
-    VisitForStackValue(key);
+    EmitPropertyKey(property);
     VisitForStackValue(value);
     EmitSetHomeObjectIfNeeded(value, 2);
 
@@ -2501,11 +2562,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
         break;
 
       case ObjectLiteral::Property::GETTER:
-        __ CallRuntime(Runtime::kDefineClassGetter, 3);
+        __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
         break;
 
       case ObjectLiteral::Property::SETTER:
-        __ CallRuntime(Runtime::kDefineClassSetter, 3);
+        __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
         break;
 
       default:
index 0d3d34b..67267a6 100644 (file)
@@ -1667,11 +1667,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
   expr->CalculateEmitStore(zone());
 
   AccessorTable accessor_table(zone());
-  for (int i = 0; i < expr->properties()->length(); i++) {
-    ObjectLiteral::Property* property = expr->properties()->at(i);
+  int property_index = 0;
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+    if (property->is_computed_name()) break;
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
     if (!result_saved) {
       __ Push(x0);  // Save result on stack
@@ -1759,6 +1761,67 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
       __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
 
+  // Object literals have two parts. The "static" part on the left contains no
+  // computed property names, and so we can compute its map ahead of time; see
+  // runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
+  // starts with the first computed property name, and continues with all
+  // properties to its right.  All the code from above initializes the static
+  // component of the object literal, and arranges for the map of the result to
+  // reflect the static order in which the keys appear. For the dynamic
+  // properties, we compile them into a series of "SetOwnProperty" runtime
+  // calls. This will preserve insertion order.
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+
+    Expression* value = property->value();
+    if (!result_saved) {
+      __ Push(x0);  // Save result on stack
+      result_saved = true;
+    }
+
+    __ Peek(x10, 0);  // Duplicate receiver.
+    __ Push(x10);
+
+    if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
+      DCHECK(!property->is_computed_name());
+      VisitForStackValue(value);
+      if (property->emit_store()) {
+        __ CallRuntime(Runtime::kInternalSetPrototype, 2);
+      } else {
+        __ Drop(2);
+      }
+    } else {
+      EmitPropertyKey(property);
+      VisitForStackValue(value);
+
+      switch (property->kind()) {
+        case ObjectLiteral::Property::CONSTANT:
+        case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        case ObjectLiteral::Property::COMPUTED:
+          if (property->emit_store()) {
+            __ Mov(x0, Smi::FromInt(NONE));
+            __ Push(x0);
+            __ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
+          } else {
+            __ Drop(3);
+          }
+          break;
+
+        case ObjectLiteral::Property::PROTOTYPE:
+          UNREACHABLE();
+          break;
+
+        case ObjectLiteral::Property::GETTER:
+          __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
+          break;
+
+        case ObjectLiteral::Property::SETTER:
+          __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
+          break;
+      }
+    }
+  }
+
   if (expr->has_function()) {
     DCHECK(result_saved);
     __ Peek(x0, 0);
@@ -2175,9 +2238,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
 
   for (int i = 0; i < lit->properties()->length(); i++) {
     ObjectLiteral::Property* property = lit->properties()->at(i);
-    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
-    DCHECK(key != NULL);
 
     if (property->is_static()) {
       __ Peek(scratch, kPointerSize);  // constructor
@@ -2185,7 +2246,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
       __ Peek(scratch, 0);  // prototype
     }
     __ Push(scratch);
-    VisitForStackValue(key);
+    EmitPropertyKey(property);
     VisitForStackValue(value);
     EmitSetHomeObjectIfNeeded(value, 2);
 
@@ -2198,11 +2259,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
         break;
 
       case ObjectLiteral::Property::GETTER:
-        __ CallRuntime(Runtime::kDefineClassGetter, 3);
+        __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
         break;
 
       case ObjectLiteral::Property::SETTER:
-        __ CallRuntime(Runtime::kDefineClassSetter, 3);
+        __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
         break;
 
       default:
index c2dd70e..8b1c2ec 100644 (file)
@@ -476,6 +476,7 @@ void AstNumberingVisitor::VisitObjectLiteral(ObjectLiteral* node) {
 
 void AstNumberingVisitor::VisitObjectLiteralProperty(
     ObjectLiteralProperty* node) {
+  if (node->is_computed_name()) DisableTurbofan(kComputedPropertyName);
   Visit(node->key());
   Visit(node->value());
 }
index 6329371..298cd23 100644 (file)
@@ -185,13 +185,17 @@ void FunctionLiteral::InitializeSharedInfo(
 
 ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
                                              AstValueFactory* ast_value_factory,
-                                             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())) {
+                                             Expression* key, Expression* value,
+                                             bool is_static,
+                                             bool is_computed_name)
+    : key_(key),
+      value_(value),
+      emit_store_(true),
+      is_static_(is_static),
+      is_computed_name_(is_computed_name) {
+  if (!is_computed_name &&
+      key->AsLiteral()->raw_value()->EqualsString(
+          ast_value_factory->proto_string())) {
     kind_ = PROTOTYPE;
   } else if (value_->AsMaterializedLiteral() != NULL) {
     kind_ = MATERIALIZED_LITERAL;
@@ -204,13 +208,16 @@ ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
 
 
 ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone, bool is_getter,
+                                             Expression* key,
                                              FunctionLiteral* value,
-                                             bool is_static) {
-  emit_store_ = true;
-  value_ = value;
-  kind_ = is_getter ? GETTER : SETTER;
-  is_static_ = is_static;
-}
+                                             bool is_static,
+                                             bool is_computed_name)
+    : key_(key),
+      value_(value),
+      kind_(is_getter ? GETTER : SETTER),
+      emit_store_(true),
+      is_static_(is_static),
+      is_computed_name_(is_computed_name) {}
 
 
 bool ObjectLiteral::Property::IsCompileTimeValue() {
@@ -237,10 +244,11 @@ void ObjectLiteral::CalculateEmitStore(Zone* zone) {
                     allocator);
   for (int i = properties()->length() - 1; i >= 0; i--) {
     ObjectLiteral::Property* property = properties()->at(i);
-    Literal* literal = property->key();
+    if (property->is_computed_name()) continue;
+    Literal* literal = property->key()->AsLiteral();
     if (literal->value()->IsNull()) continue;
     uint32_t hash = literal->Hash();
-    // If the key of a computed property is in the table, do not emit
+    // If the key of a computed property value is in the table, do not emit
     // a store for the property later.
     if ((property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL ||
          property->kind() == ObjectLiteral::Property::COMPUTED) &&
@@ -279,6 +287,13 @@ void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
       is_simple = false;
       continue;
     }
+
+    if (position == boilerplate_properties_ * 2) {
+      DCHECK(property->is_computed_name());
+      break;
+    }
+    DCHECK(!property->is_computed_name());
+
     MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
     if (m_literal != NULL) {
       m_literal->BuildConstants(isolate);
@@ -288,7 +303,7 @@ void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
     // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
     // value for COMPUTED properties, the real value is filled in at
     // runtime. The enumeration order is maintained.
-    Handle<Object> key = property->key()->value();
+    Handle<Object> key = property->key()->AsLiteral()->value();
     Handle<Object> value = GetBoilerplateValue(property->value(), isolate);
 
     // Ensure objects that may, at any point in time, contain fields with double
@@ -640,7 +655,8 @@ void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
 
 
 void ObjectLiteral::Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
-  TypeFeedbackId id = key()->LiteralFeedbackId();
+  DCHECK(!is_computed_name());
+  TypeFeedbackId id = key()->AsLiteral()->LiteralFeedbackId();
   SmallMapList maps;
   oracle->CollectReceiverTypes(id, &maps);
   receiver_type_ = maps.length() == 1 ? maps.at(0)
index 43bde6a..8579ff4 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1467,10 +1467,7 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
     PROTOTYPE              // Property is __proto__.
   };
 
-  ObjectLiteralProperty(Zone* zone, AstValueFactory* ast_value_factory,
-                        Literal* key, Expression* value, bool is_static);
-
-  Literal* key() { return key_; }
+  Expression* key() { return key_; }
   Expression* value() { return value_; }
   Kind kind() { return kind_; }
 
@@ -1485,20 +1482,26 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
   bool emit_store();
 
   bool is_static() const { return is_static_; }
+  bool is_computed_name() const { return is_computed_name_; }
 
  protected:
   friend class AstNodeFactory;
 
-  ObjectLiteralProperty(Zone* zone, bool is_getter, FunctionLiteral* value,
-                        bool is_static);
-  void set_key(Literal* key) { key_ = key; }
+  ObjectLiteralProperty(Zone* zone, AstValueFactory* ast_value_factory,
+                        Expression* key, Expression* value, bool is_static,
+                        bool is_computed_name);
+
+  ObjectLiteralProperty(Zone* zone, bool is_getter, Expression* key,
+                        FunctionLiteral* value, bool is_static,
+                        bool is_computed_name);
 
  private:
-  Literal* key_;
+  Expression* key_;
   Expression* value_;
   Kind kind_;
   bool emit_store_;
   bool is_static_;
+  bool is_computed_name_;
   Handle<Map> receiver_type_;
 };
 
@@ -3377,20 +3380,21 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
                                      boilerplate_properties, has_function, pos);
   }
 
-  ObjectLiteral::Property* NewObjectLiteralProperty(Literal* key,
+  ObjectLiteral::Property* NewObjectLiteralProperty(Expression* key,
                                                     Expression* value,
-                                                    bool is_static) {
-    return new (zone_) ObjectLiteral::Property(zone_, ast_value_factory_, key,
-                                               value, is_static);
+                                                    bool is_static,
+                                                    bool is_computed_name) {
+    return new (zone_) ObjectLiteral::Property(
+        zone_, ast_value_factory_, key, value, is_static, is_computed_name);
   }
 
   ObjectLiteral::Property* NewObjectLiteralProperty(bool is_getter,
+                                                    Expression* key,
                                                     FunctionLiteral* value,
-                                                    int pos, bool is_static) {
-    ObjectLiteral::Property* prop =
-        new (zone_) ObjectLiteral::Property(zone_, is_getter, value, is_static);
-    prop->set_key(NewStringLiteral(value->raw_name(), pos));
-    return prop;
+                                                    int pos, bool is_static,
+                                                    bool is_computed_name) {
+    return new (zone_) ObjectLiteral::Property(zone_, is_getter, key, value,
+                                               is_static, is_computed_name);
   }
 
   RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern,
index 7287d62..1c65fb9 100644 (file)
@@ -47,6 +47,7 @@ namespace internal {
   V(kCodeGenerationFailed, "Code generation failed")                           \
   V(kCodeObjectNotProperlyPatched, "Code object not properly patched")         \
   V(kCompoundAssignmentToLookupSlot, "Compound assignment to lookup slot")     \
+  V(kComputedPropertyName, "Computed property name")                           \
   V(kContextAllocatedArguments, "Context-allocated arguments")                 \
   V(kCopyBuffersOverlap, "Copy buffers overlap")                               \
   V(kCouldNotGenerateZero, "Could not generate +0.0")                          \
index 7105eb2..d4fc6cd 100644 (file)
@@ -1596,6 +1596,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_tostring)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_templates)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_sloppy)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode)
+EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_computed_property_names)
 
 
 void Genesis::InstallNativeFunctions_harmony_proxies() {
@@ -1626,6 +1627,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_proxies)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_templates)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_sloppy)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode)
+EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_computed_property_names)
 
 void Genesis::InitializeGlobal_harmony_regexps() {
   Handle<JSObject> builtins(native_context()->builtins());
@@ -2180,6 +2182,7 @@ bool Genesis::InstallExperimentalNatives() {
       "native harmony-templates.js", NULL};
   static const char* harmony_sloppy_natives[] = {NULL};
   static const char* harmony_unicode_natives[] = {NULL};
+  static const char* harmony_computed_property_names_natives[] = {NULL};
 
   for (int i = ExperimentalNatives::GetDebuggerCount();
        i < ExperimentalNatives::GetBuiltinsCount(); i++) {
index 13a4b80..b776f73 100644 (file)
@@ -165,35 +165,36 @@ enum BuiltinExtraArguments {
                                                DEBUG_BREAK)
 
 // Define list of builtins implemented in JavaScript.
-#define BUILTINS_LIST_JS(V)              \
-  V(EQUALS, 1)                           \
-  V(STRICT_EQUALS, 1)                    \
-  V(COMPARE, 2)                          \
-  V(ADD, 1)                              \
-  V(SUB, 1)                              \
-  V(MUL, 1)                              \
-  V(DIV, 1)                              \
-  V(MOD, 1)                              \
-  V(BIT_OR, 1)                           \
-  V(BIT_AND, 1)                          \
-  V(BIT_XOR, 1)                          \
-  V(SHL, 1)                              \
-  V(SAR, 1)                              \
-  V(SHR, 1)                              \
-  V(DELETE, 2)                           \
-  V(IN, 1)                               \
-  V(INSTANCE_OF, 1)                      \
-  V(FILTER_KEY, 1)                       \
-  V(CALL_NON_FUNCTION, 0)                \
-  V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
+#define BUILTINS_LIST_JS(V)                \
+  V(EQUALS, 1)                             \
+  V(STRICT_EQUALS, 1)                      \
+  V(COMPARE, 2)                            \
+  V(ADD, 1)                                \
+  V(SUB, 1)                                \
+  V(MUL, 1)                                \
+  V(DIV, 1)                                \
+  V(MOD, 1)                                \
+  V(BIT_OR, 1)                             \
+  V(BIT_AND, 1)                            \
+  V(BIT_XOR, 1)                            \
+  V(SHL, 1)                                \
+  V(SAR, 1)                                \
+  V(SHR, 1)                                \
+  V(DELETE, 2)                             \
+  V(IN, 1)                                 \
+  V(INSTANCE_OF, 1)                        \
+  V(FILTER_KEY, 1)                         \
+  V(CALL_NON_FUNCTION, 0)                  \
+  V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0)   \
   V(CALL_FUNCTION_PROXY, 1)                \
   V(CALL_FUNCTION_PROXY_AS_CONSTRUCTOR, 1) \
-  V(TO_OBJECT, 0)                        \
-  V(TO_NUMBER, 0)                        \
-  V(TO_STRING, 0)                        \
-  V(STRING_ADD_LEFT, 1)                  \
-  V(STRING_ADD_RIGHT, 1)                 \
-  V(APPLY_PREPARE, 1)                    \
+  V(TO_OBJECT, 0)                          \
+  V(TO_NUMBER, 0)                          \
+  V(TO_STRING, 0)                          \
+  V(TO_NAME, 0)                            \
+  V(STRING_ADD_LEFT, 1)                    \
+  V(STRING_ADD_RIGHT, 1)                   \
+  V(APPLY_PREPARE, 1)                      \
   V(STACK_OVERFLOW, 1)
 
 class BuiltinFunctionTable;
index cde5e71..1c53873 100644 (file)
@@ -910,7 +910,7 @@ void AstGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
     ObjectLiteral::Property* property = expr->properties()->at(i);
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     switch (property->kind()) {
       case ObjectLiteral::Property::CONSTANT:
         UNREACHABLE();
index cb95aaf..9c50931 100644 (file)
@@ -172,15 +172,16 @@ DEFINE_IMPLICATION(harmony, es_staging)
 DEFINE_IMPLICATION(es_staging, harmony)
 
 // Features that are still work in progress (behind individual flags).
-#define HARMONY_INPROGRESS(V)                                             \
-  V(harmony_modules, "harmony modules (implies block scoping)")           \
-  V(harmony_arrays, "harmony array methods")                              \
-  V(harmony_array_includes, "harmony Array.prototype.includes")           \
-  V(harmony_regexps, "harmony regular expression extensions")             \
-  V(harmony_arrow_functions, "harmony arrow functions")                   \
-  V(harmony_proxies, "harmony proxies")                                   \
-  V(harmony_sloppy, "harmony features in sloppy mode")                    \
-  V(harmony_unicode, "harmony unicode escapes")
+#define HARMONY_INPROGRESS(V)                                   \
+  V(harmony_modules, "harmony modules (implies block scoping)") \
+  V(harmony_arrays, "harmony array methods")                    \
+  V(harmony_array_includes, "harmony Array.prototype.includes") \
+  V(harmony_regexps, "harmony regular expression extensions")   \
+  V(harmony_arrow_functions, "harmony arrow functions")         \
+  V(harmony_proxies, "harmony proxies")                         \
+  V(harmony_sloppy, "harmony features in sloppy mode")          \
+  V(harmony_unicode, "harmony unicode escapes")                 \
+  V(harmony_computed_property_names, "harmony computed property names")
 
 // Features that are complete (but still behind --harmony/es-staging flag).
 #define HARMONY_STAGED(V)                                                 \
index cb8f4aa..ec674ff 100644 (file)
@@ -1211,6 +1211,13 @@ void FullCodeGenerator::EmitUnwindBeforeReturn() {
 }
 
 
+void FullCodeGenerator::EmitPropertyKey(ObjectLiteralProperty* property) {
+  VisitForStackValue(property->key());
+  __ InvokeBuiltin(Builtins::TO_NAME, CALL_FUNCTION);
+  __ Push(result_register());
+}
+
+
 void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
   Comment cmnt(masm_, "[ ReturnStatement");
   SetStatementPosition(stmt);
index 1439942..f870cae 100644 (file)
@@ -568,6 +568,9 @@ class FullCodeGenerator: public AstVisitor {
   // in the accumulator after installing all the properties.
   void EmitClassDefineProperties(ClassLiteral* lit);
 
+  // Pushes the property key as a Name on the stack.
+  void EmitPropertyKey(ObjectLiteralProperty* property);
+
   // Apply the compound assignment operator. Expects the left operand on top
   // of the stack and the right one in the accumulator.
   void EmitBinaryOp(BinaryOperation* expr,
index 987d60b..9f4075e 100644 (file)
@@ -5552,6 +5552,13 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
   DCHECK(!HasStackOverflow());
   DCHECK(current_block() != NULL);
   DCHECK(current_block()->HasPredecessor());
+
+  for (int i = 0; i < expr->properties()->length(); i++) {
+    if (expr->properties()->at(i)->is_computed_name()) {
+      return Bailout(kComputedPropertyName);
+    }
+  }
+
   expr->BuildConstantProperties(isolate());
   Handle<JSFunction> closure = function_state()->compilation_info()->closure();
   HInstruction* literal;
@@ -5607,9 +5614,10 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
 
   for (int i = 0; i < expr->properties()->length(); i++) {
     ObjectLiteral::Property* property = expr->properties()->at(i);
+    DCHECK(!property->is_computed_name());
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
 
     switch (property->kind()) {
@@ -5635,7 +5643,7 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
             }
 
             Handle<Map> map = property->GetReceiverType();
-            Handle<String> name = property->key()->AsPropertyName();
+            Handle<String> name = key->AsPropertyName();
             HInstruction* store;
             if (map.is_null()) {
               // If we don't know the monomorphic type, do a generic store.
index 1ba4095..45627ea 100644 (file)
@@ -1617,11 +1617,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
   expr->CalculateEmitStore(zone());
 
   AccessorTable accessor_table(zone());
-  for (int i = 0; i < expr->properties()->length(); i++) {
-    ObjectLiteral::Property* property = expr->properties()->at(i);
+  int property_index = 0;
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+    if (property->is_computed_name()) break;
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
     if (!result_saved) {
       __ push(eax);  // Save result on the stack
@@ -1701,6 +1703,65 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
 
+  // Object literals have two parts. The "static" part on the left contains no
+  // computed property names, and so we can compute its map ahead of time; see
+  // runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
+  // starts with the first computed property name, and continues with all
+  // properties to its right.  All the code from above initializes the static
+  // component of the object literal, and arranges for the map of the result to
+  // reflect the static order in which the keys appear. For the dynamic
+  // properties, we compile them into a series of "SetOwnProperty" runtime
+  // calls. This will preserve insertion order.
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+
+    Expression* value = property->value();
+    if (!result_saved) {
+      __ push(eax);  // Save result on the stack
+      result_saved = true;
+    }
+
+    __ push(Operand(esp, 0));  // Duplicate receiver.
+
+    if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
+      DCHECK(!property->is_computed_name());
+      VisitForStackValue(value);
+      if (property->emit_store()) {
+        __ CallRuntime(Runtime::kInternalSetPrototype, 2);
+      } else {
+        __ Drop(2);
+      }
+    } else {
+      EmitPropertyKey(property);
+      VisitForStackValue(value);
+
+      switch (property->kind()) {
+        case ObjectLiteral::Property::CONSTANT:
+        case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        case ObjectLiteral::Property::COMPUTED:
+          if (property->emit_store()) {
+            __ push(Immediate(Smi::FromInt(NONE)));
+            __ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
+          } else {
+            __ Drop(3);
+          }
+          break;
+
+        case ObjectLiteral::Property::PROTOTYPE:
+          UNREACHABLE();
+          break;
+
+        case ObjectLiteral::Property::GETTER:
+          __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
+          break;
+
+        case ObjectLiteral::Property::SETTER:
+          __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
+          break;
+      }
+    }
+  }
+
   if (expr->has_function()) {
     DCHECK(result_saved);
     __ push(Operand(esp, 0));
@@ -2394,16 +2455,14 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
 
   for (int i = 0; i < lit->properties()->length(); i++) {
     ObjectLiteral::Property* property = lit->properties()->at(i);
-    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
-    DCHECK(key != NULL);
 
     if (property->is_static()) {
       __ push(Operand(esp, kPointerSize));  // constructor
     } else {
       __ push(Operand(esp, 0));  // prototype
     }
-    VisitForStackValue(key);
+    EmitPropertyKey(property);
     VisitForStackValue(value);
     EmitSetHomeObjectIfNeeded(value, 2);
 
@@ -2416,11 +2475,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
         break;
 
       case ObjectLiteral::Property::GETTER:
-        __ CallRuntime(Runtime::kDefineClassGetter, 3);
+        __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
         break;
 
       case ObjectLiteral::Property::SETTER:
-        __ CallRuntime(Runtime::kDefineClassSetter, 3);
+        __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
         break;
 
       default:
index 0713b26..e5b366b 100644 (file)
@@ -407,9 +407,8 @@ bool ParserTraits::IsConstructor(const AstRawString* identifier) const {
 bool ParserTraits::IsThisProperty(Expression* expression) {
   DCHECK(expression != NULL);
   Property* property = expression->AsProperty();
-  return property != NULL &&
-      property->obj()->AsVariableProxy() != NULL &&
-      property->obj()->AsVariableProxy()->is_this();
+  return property != NULL && property->obj()->IsVariableProxy() &&
+         property->obj()->AsVariableProxy()->is_this();
 }
 
 
@@ -433,8 +432,7 @@ void ParserTraits::PushPropertyName(FuncNameInferrer* fni,
 void ParserTraits::CheckAssigningFunctionLiteralToProperty(Expression* left,
                                                            Expression* right) {
   DCHECK(left != NULL);
-  if (left->AsProperty() != NULL &&
-      right->AsFunctionLiteral() != NULL) {
+  if (left->IsProperty() && right->IsFunctionLiteral()) {
     right->AsFunctionLiteral()->set_pretenure();
   }
 }
@@ -806,6 +804,8 @@ Parser::Parser(CompilationInfo* info, ParseInfo* parse_info)
   set_allow_harmony_templates(FLAG_harmony_templates);
   set_allow_harmony_sloppy(FLAG_harmony_sloppy);
   set_allow_harmony_unicode(FLAG_harmony_unicode);
+  set_allow_harmony_computed_property_names(
+      FLAG_harmony_computed_property_names);
   for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
        ++feature) {
     use_counts_[feature] = 0;
@@ -3976,6 +3976,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
     reusable_preparser_->set_allow_harmony_templates(allow_harmony_templates());
     reusable_preparser_->set_allow_harmony_sloppy(allow_harmony_sloppy());
     reusable_preparser_->set_allow_harmony_unicode(allow_harmony_unicode());
+    reusable_preparser_->set_allow_harmony_computed_property_names(
+        allow_harmony_computed_property_names());
   }
   PreParser::PreParseResult result =
       reusable_preparser_->PreParseLazyFunction(strict_mode(),
@@ -4035,8 +4037,11 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
     if (fni_ != NULL) fni_->Enter();
     const bool in_class = true;
     const bool is_static = false;
-    ObjectLiteral::Property* property = ParsePropertyDefinition(
-        NULL, in_class, is_static, &has_seen_constructor, CHECK_OK);
+    bool is_computed_name = false;  // Classes do not care about computed
+                                    // property names here.
+    ObjectLiteral::Property* property =
+        ParsePropertyDefinition(NULL, in_class, is_static, &is_computed_name,
+                                &has_seen_constructor, CHECK_OK);
 
     if (has_seen_constructor && constructor == NULL) {
       constructor = GetPropertyValue(property);
index 02dfe15..0c58869 100644 (file)
@@ -412,11 +412,6 @@ class ParserTraits {
     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();
   }
@@ -426,7 +421,9 @@ class ParserTraits {
   static void PushLiteralName(FuncNameInferrer* fni, const AstRawString* id) {
     fni->PushLiteralName(id);
   }
+
   void PushPropertyName(FuncNameInferrer* fni, Expression* expression);
+
   static void InferFunctionName(FuncNameInferrer* fni,
                                 FunctionLiteral* func_to_infer) {
     fni->AddFunction(func_to_infer);
index 1bd3340..51cac17 100644 (file)
@@ -990,8 +990,10 @@ PreParserExpression PreParser::ParseClassLiteral(
     if (Check(Token::SEMICOLON)) continue;
     const bool in_class = true;
     const bool is_static = false;
-    ParsePropertyDefinition(NULL, in_class, is_static, &has_seen_constructor,
-                            CHECK_OK);
+    bool is_computed_name = false;  // Classes do not care about computed
+                                    // property names here.
+    ParsePropertyDefinition(NULL, in_class, is_static, &is_computed_name,
+                            &has_seen_constructor, CHECK_OK);
   }
 
   Expect(Token::RBRACE, CHECK_OK);
index 3b221ac..c79f6a6 100644 (file)
@@ -87,6 +87,7 @@ class ParserBase : public Traits {
         allow_harmony_arrow_functions_(false),
         allow_harmony_object_literals_(false),
         allow_harmony_sloppy_(false),
+        allow_harmony_computed_property_names_(false),
         zone_(zone) {}
 
   // Getters that indicate whether certain syntactical constructs are
@@ -108,6 +109,9 @@ class ParserBase : public Traits {
   bool allow_harmony_templates() const { return scanner()->HarmonyTemplates(); }
   bool allow_harmony_sloppy() const { return allow_harmony_sloppy_; }
   bool allow_harmony_unicode() const { return scanner()->HarmonyUnicode(); }
+  bool allow_harmony_computed_property_names() const {
+    return allow_harmony_computed_property_names_;
+  }
 
   // Setters that determine whether certain syntactical constructs are
   // allowed to be parsed by this instance of the parser.
@@ -140,6 +144,9 @@ class ParserBase : public Traits {
   void set_allow_harmony_unicode(bool allow) {
     scanner()->SetHarmonyUnicode(allow);
   }
+  void set_allow_harmony_computed_property_names(bool allow) {
+    allow_harmony_computed_property_names_ = allow;
+  }
 
  protected:
   enum AllowEvalOrArgumentsAsIdentifier {
@@ -490,11 +497,13 @@ 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,
+  ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
+                                bool* is_static, bool* is_computed_name,
                                 bool* ok);
   ExpressionT ParseObjectLiteral(bool* ok);
   ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker,
                                                  bool in_class, bool is_static,
+                                                 bool* is_computed_name,
                                                  bool* has_seen_constructor,
                                                  bool* ok);
   typename Traits::Type::ExpressionList ParseArguments(bool* ok);
@@ -598,6 +607,7 @@ class ParserBase : public Traits {
   bool allow_harmony_arrow_functions_;
   bool allow_harmony_object_literals_;
   bool allow_harmony_sloppy_;
+  bool allow_harmony_computed_property_names_;
 
   typename Traits::Type::Zone* zone_;  // Only used by Parser.
 };
@@ -1031,13 +1041,16 @@ class PreParserFactory {
     return PreParserExpression::Default();
   }
   PreParserExpression NewObjectLiteralProperty(bool is_getter,
+                                               PreParserExpression key,
                                                PreParserExpression value,
-                                               int pos, bool is_static) {
+                                               int pos, bool is_static,
+                                               bool is_computed_name) {
     return PreParserExpression::Default();
   }
   PreParserExpression NewObjectLiteralProperty(PreParserExpression key,
                                                PreParserExpression value,
-                                               bool is_static) {
+                                               bool is_static,
+                                               bool is_computed_name) {
     return PreParserExpression::Default();
   }
   PreParserExpression NewObjectLiteral(PreParserExpressionList properties,
@@ -1223,11 +1236,13 @@ class PreParserTraits {
     // PreParser should not use FuncNameInferrer.
     UNREACHABLE();
   }
+
   static void PushPropertyName(FuncNameInferrer* fni,
                                PreParserExpression expression) {
     // PreParser should not use FuncNameInferrer.
     UNREACHABLE();
   }
+
   static void InferFunctionName(FuncNameInferrer* fni,
                                 PreParserExpression expression) {
     // PreParser should not use FuncNameInferrer.
@@ -1987,24 +2002,55 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
 
 
 template <class Traits>
-typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName(
-    bool* is_get, bool* is_set, bool* is_static, bool* ok) {
-  Token::Value next = peek();
-  switch (next) {
+typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
+    IdentifierT* name, bool* is_get, bool* is_set, bool* is_static,
+    bool* is_computed_name, bool* ok) {
+  Token::Value token = peek();
+  int pos = peek_position();
+
+  // For non computed property names we normalize the name a bit:
+  //
+  //   "12" -> 12
+  //   12.3 -> "12.3"
+  //   12.30 -> "12.3"
+  //   identifier -> "identifier"
+  //
+  // This is important because we use the property name as a key in a hash
+  // table when we compute constant properties.
+  switch (token) {
     case Token::STRING:
       Consume(Token::STRING);
-      return this->GetSymbol(scanner_);
+      *name = this->GetSymbol(scanner());
+      break;
+
     case Token::NUMBER:
       Consume(Token::NUMBER);
-      return this->GetNumberAsSymbol(scanner_);
+      *name = this->GetNumberAsSymbol(scanner());
+      break;
+
+    case Token::LBRACK:
+      if (allow_harmony_computed_property_names_) {
+        *is_computed_name = true;
+        Consume(Token::LBRACK);
+        ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK);
+        Expect(Token::RBRACK, CHECK_OK);
+        return expression;
+      }
+
+    // Fall through.
     case Token::STATIC:
       *is_static = true;
-      // Fall through.
+
+    // Fall through.
     default:
-      return ParseIdentifierNameOrGetOrSet(is_get, is_set, ok);
+      *name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK);
+      break;
   }
-  UNREACHABLE();
-  return this->EmptyIdentifier();
+
+  uint32_t index;
+  return this->IsArrayIndex(*name, &index)
+             ? factory()->NewNumberLiteral(index, pos)
+             : factory()->NewStringLiteral(*name, pos);
 }
 
 
@@ -2012,9 +2058,11 @@ template <class Traits>
 typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
     Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker,
                                      bool in_class, bool is_static,
+                                     bool* is_computed_name,
                                      bool* has_seen_constructor, bool* ok) {
   DCHECK(!in_class || is_static || has_seen_constructor != NULL);
   ExpressionT value = this->EmptyExpression();
+  IdentifierT name = this->EmptyIdentifier();
   bool is_get = false;
   bool is_set = false;
   bool name_is_static = false;
@@ -2022,15 +2070,17 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
 
   Token::Value name_token = peek();
   int next_pos = peek_position();
-  IdentifierT name =
-      ParsePropertyName(&is_get, &is_set, &name_is_static,
-                        CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+  ExpressionT name_expression = ParsePropertyName(
+      &name, &is_get, &is_set, &name_is_static, is_computed_name,
+      CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
 
-  if (fni_ != NULL) this->PushLiteralName(fni_, name);
+  if (fni_ != NULL && !*is_computed_name) {
+    this->PushLiteralName(fni_, name);
+  }
 
   if (!in_class && !is_generator && peek() == Token::COLON) {
     // PropertyDefinition : PropertyName ':' AssignmentExpression
-    if (checker != NULL) {
+    if (!*is_computed_name && checker != NULL) {
       checker->CheckProperty(name_token, kValueProperty,
                              CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
     }
@@ -2068,7 +2118,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
       kind = FunctionKind::kNormalFunction;
     }
 
-    if (checker != NULL) {
+    if (!*is_computed_name && checker != NULL) {
       checker->CheckProperty(name_token, kValueProperty,
                              CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
     }
@@ -2082,14 +2132,18 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
 
   } else if (in_class && name_is_static && !is_static) {
     // static MethodDefinition
-    return ParsePropertyDefinition(checker, true, true, NULL, ok);
+    return ParsePropertyDefinition(checker, true, true, is_computed_name, NULL,
+                                   ok);
 
   } else if (is_get || is_set) {
     // Accessor
+    name = this->EmptyIdentifier();
     bool dont_care = false;
     name_token = peek();
-    name = ParsePropertyName(&dont_care, &dont_care, &dont_care,
-                             CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+
+    name_expression = ParsePropertyName(
+        &name, &dont_care, &dont_care, &dont_care, is_computed_name,
+        CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
 
     // Validate the property.
     if (is_static && this->IsPrototype(name)) {
@@ -2101,7 +2155,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
       *ok = false;
       return this->EmptyObjectLiteralProperty();
     }
-    if (checker != NULL) {
+    if (!*is_computed_name && checker != NULL) {
       checker->CheckProperty(name_token,
                              is_get ? kGetterProperty : kSetterProperty,
                              CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
@@ -2114,8 +2168,17 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
         FunctionLiteral::ANONYMOUS_EXPRESSION,
         is_get ? FunctionLiteral::GETTER_ARITY : FunctionLiteral::SETTER_ARITY,
         CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
-    return factory()->NewObjectLiteralProperty(is_get, value, next_pos,
-                                               is_static);
+
+    // Make sure the name expression is a string since we need a Name for
+    // Runtime_DefineAccessorPropertyUnchecked and since we can determine this
+    // statically we can skip the extra runtime check.
+    if (!*is_computed_name) {
+      name_expression =
+          factory()->NewStringLiteral(name, name_expression->position());
+    }
+
+    return factory()->NewObjectLiteralProperty(
+        is_get, name_expression, value, next_pos, is_static, *is_computed_name);
 
   } else if (!in_class && allow_harmony_object_literals_ &&
              Token::IsIdentifier(name_token, strict_mode(),
@@ -2129,12 +2192,8 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
     return this->EmptyObjectLiteralProperty();
   }
 
-  uint32_t index;
-  LiteralT key = this->IsArrayIndex(name, &index)
-                     ? factory()->NewNumberLiteral(index, next_pos)
-                     : factory()->NewStringLiteral(name, next_pos);
-
-  return factory()->NewObjectLiteralProperty(key, value, is_static);
+  return factory()->NewObjectLiteralProperty(name_expression, value, is_static,
+                                             *is_computed_name);
 }
 
 
@@ -2149,6 +2208,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
       this->NewPropertyList(4, zone_);
   int number_of_boilerplate_properties = 0;
   bool has_function = false;
+  bool has_computed_names = false;
 
   ObjectLiteralChecker checker(this, strict_mode());
 
@@ -2159,8 +2219,13 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
 
     const bool in_class = false;
     const bool is_static = false;
+    bool is_computed_name = false;
     ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
-        &checker, in_class, is_static, NULL, CHECK_OK);
+        &checker, in_class, is_static, &is_computed_name, NULL, CHECK_OK);
+
+    if (is_computed_name) {
+      has_computed_names = true;
+    }
 
     // Mark top-level object literals that contain function literals and
     // pretenure the literal so it can be added as a constant function
@@ -2169,7 +2234,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
                                                           &has_function);
 
     // Count CONSTANT or COMPUTED properties to maintain the enumeration order.
-    if (this->IsBoilerplateProperty(property)) {
+    if (!has_computed_names && this->IsBoilerplateProperty(property)) {
       number_of_boilerplate_properties++;
     }
     properties->Add(property, zone());
index 79824e4..145823b 100644 (file)
@@ -468,6 +468,12 @@ function TO_STRING() {
 }
 
 
+// Convert the receiver to a string or symbol - forward to ToName.
+function TO_NAME() {
+  return %ToName(this);
+}
+
+
 /* -------------------------------------
    - - -   C o n v e r s i o n s   - - -
    -------------------------------------
index 7c827f0..00567d4 100644 (file)
@@ -153,18 +153,10 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 3);
   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
-  CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+  CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 2);
 
   uint32_t index;
-  if (key->ToArrayIndex(&index)) {
-    RETURN_FAILURE_ON_EXCEPTION(
-        isolate, JSObject::SetOwnElement(object, index, function, STRICT));
-  }
-
-  Handle<Name> name;
-  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
-                                     Runtime::ToName(isolate, key));
   if (name->AsArrayIndex(&index)) {
     RETURN_FAILURE_ON_EXCEPTION(
         isolate, JSObject::SetOwnElement(object, index, function, STRICT));
@@ -177,42 +169,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
 }
 
 
-RUNTIME_FUNCTION(Runtime_DefineClassGetter) {
-  HandleScope scope(isolate);
-  DCHECK(args.length() == 3);
-  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
-  CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
-  CONVERT_ARG_HANDLE_CHECKED(JSFunction, getter, 2);
-
-  Handle<Name> name;
-  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
-                                     Runtime::ToName(isolate, key));
-  RETURN_FAILURE_ON_EXCEPTION(
-      isolate,
-      JSObject::DefineAccessor(object, name, getter,
-                               isolate->factory()->null_value(), NONE));
-  return isolate->heap()->undefined_value();
-}
-
-
-RUNTIME_FUNCTION(Runtime_DefineClassSetter) {
-  HandleScope scope(isolate);
-  DCHECK(args.length() == 3);
-  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
-  CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
-  CONVERT_ARG_HANDLE_CHECKED(JSFunction, setter, 2);
-
-  Handle<Name> name;
-  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
-                                     Runtime::ToName(isolate, key));
-  RETURN_FAILURE_ON_EXCEPTION(
-      isolate,
-      JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
-                               setter, NONE));
-  return isolate->heap()->undefined_value();
-}
-
-
 RUNTIME_FUNCTION(Runtime_ClassGetSourceCode) {
   HandleScope shs(isolate);
   DCHECK(args.length() == 1);
index 407f237..43620bb 100644 (file)
@@ -1606,5 +1606,35 @@ RUNTIME_FUNCTION(RuntimeReference_ClassOf) {
   if (!obj->IsJSReceiver()) return isolate->heap()->null_value();
   return JSReceiver::cast(obj)->class_name();
 }
+
+
+RUNTIME_FUNCTION(Runtime_DefineGetterPropertyUnchecked) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, getter, 2);
+
+  RETURN_FAILURE_ON_EXCEPTION(
+      isolate,
+      JSObject::DefineAccessor(object, name, getter,
+                               isolate->factory()->null_value(), NONE));
+  return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DefineSetterPropertyUnchecked) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, setter, 2);
+
+  RETURN_FAILURE_ON_EXCEPTION(
+      isolate,
+      JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
+                               setter, NONE));
+  return isolate->heap()->undefined_value();
+}
 }
 }  // namespace v8::internal
index 9e6c495..a39e320 100644 (file)
@@ -187,8 +187,6 @@ namespace internal {
   F(HomeObjectSymbol, 0, 1)                                \
   F(DefineClass, 6, 1)                                     \
   F(DefineClassMethod, 3, 1)                               \
-  F(DefineClassGetter, 3, 1)                               \
-  F(DefineClassSetter, 3, 1)                               \
   F(ClassGetSourceCode, 1, 1)                              \
   F(ThrowNonMethodError, 0, 1)                             \
   F(ThrowUnsupportedSuperError, 0, 1)                      \
@@ -262,6 +260,8 @@ namespace internal {
   F(DefineDataPropertyUnchecked, 4, 1)                 \
   F(DefineAccessorPropertyUnchecked, 5, 1)             \
   F(GetDataProperty, 2, 1)                             \
+  F(DefineGetterPropertyUnchecked, 3, 1)               \
+  F(DefineSetterPropertyUnchecked, 3, 1)               \
                                                        \
   /* Arrays */                                         \
   F(RemoveArrayHoles, 2, 1)                            \
index 88c530e..3fb6030 100644 (file)
@@ -408,7 +408,9 @@ void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) {
     if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL &&
         !CompileTimeValue::IsCompileTimeValue(prop->value())) ||
         prop->kind() == ObjectLiteral::Property::COMPUTED) {
-      if (prop->key()->value()->IsInternalizedString() && prop->emit_store()) {
+      if (!prop->is_computed_name() &&
+          prop->key()->AsLiteral()->value()->IsInternalizedString() &&
+          prop->emit_store()) {
         prop->RecordTypeFeedback(oracle());
       }
     }
index 24747ee..571cc33 100644 (file)
@@ -1651,11 +1651,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
   expr->CalculateEmitStore(zone());
 
   AccessorTable accessor_table(zone());
-  for (int i = 0; i < expr->properties()->length(); i++) {
-    ObjectLiteral::Property* property = expr->properties()->at(i);
+  int property_index = 0;
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+    if (property->is_computed_name()) break;
     if (property->IsCompileTimeValue()) continue;
 
-    Literal* key = property->key();
+    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
     if (!result_saved) {
       __ Push(rax);  // Save result on the stack
@@ -1735,6 +1737,65 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     __ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
   }
 
+  // Object literals have two parts. The "static" part on the left contains no
+  // computed property names, and so we can compute its map ahead of time; see
+  // runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
+  // starts with the first computed property name, and continues with all
+  // properties to its right.  All the code from above initializes the static
+  // component of the object literal, and arranges for the map of the result to
+  // reflect the static order in which the keys appear. For the dynamic
+  // properties, we compile them into a series of "SetOwnProperty" runtime
+  // calls. This will preserve insertion order.
+  for (; property_index < expr->properties()->length(); property_index++) {
+    ObjectLiteral::Property* property = expr->properties()->at(property_index);
+
+    Expression* value = property->value();
+    if (!result_saved) {
+      __ Push(rax);  // Save result on the stack
+      result_saved = true;
+    }
+
+    __ Push(Operand(rsp, 0));  // Duplicate receiver.
+
+    if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
+      DCHECK(!property->is_computed_name());
+      VisitForStackValue(value);
+      if (property->emit_store()) {
+        __ CallRuntime(Runtime::kInternalSetPrototype, 2);
+      } else {
+        __ Drop(2);
+      }
+    } else {
+      EmitPropertyKey(property);
+      VisitForStackValue(value);
+
+      switch (property->kind()) {
+        case ObjectLiteral::Property::CONSTANT:
+        case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+        case ObjectLiteral::Property::COMPUTED:
+          if (property->emit_store()) {
+            __ Push(Smi::FromInt(NONE));
+            __ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
+          } else {
+            __ Drop(3);
+          }
+          break;
+
+        case ObjectLiteral::Property::PROTOTYPE:
+          UNREACHABLE();
+          break;
+
+        case ObjectLiteral::Property::GETTER:
+          __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
+          break;
+
+        case ObjectLiteral::Property::SETTER:
+          __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
+          break;
+      }
+    }
+  }
+
   if (expr->has_function()) {
     DCHECK(result_saved);
     __ Push(Operand(rsp, 0));
@@ -2393,16 +2454,14 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
 
   for (int i = 0; i < lit->properties()->length(); i++) {
     ObjectLiteral::Property* property = lit->properties()->at(i);
-    Literal* key = property->key()->AsLiteral();
     Expression* value = property->value();
-    DCHECK(key != NULL);
 
     if (property->is_static()) {
       __ Push(Operand(rsp, kPointerSize));  // constructor
     } else {
       __ Push(Operand(rsp, 0));  // prototype
     }
-    VisitForStackValue(key);
+    EmitPropertyKey(property);
     VisitForStackValue(value);
     EmitSetHomeObjectIfNeeded(value, 2);
 
@@ -2415,11 +2474,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
         break;
 
       case ObjectLiteral::Property::GETTER:
-        __ CallRuntime(Runtime::kDefineClassGetter, 3);
+        __ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
         break;
 
       case ObjectLiteral::Property::SETTER:
-        __ CallRuntime(Runtime::kDefineClassSetter, 3);
+        __ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
         break;
 
       default:
index ef6b5d3..f08f9b3 100644 (file)
@@ -1358,7 +1358,8 @@ enum ParserFlag {
   kAllowHarmonyObjectLiterals,
   kAllowHarmonyTemplates,
   kAllowHarmonySloppy,
-  kAllowHarmonyUnicode
+  kAllowHarmonyUnicode,
+  kAllowHarmonyComputedPropertyNames
 };
 
 
@@ -1385,6 +1386,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
   parser->set_allow_harmony_templates(flags.Contains(kAllowHarmonyTemplates));
   parser->set_allow_harmony_sloppy(flags.Contains(kAllowHarmonySloppy));
   parser->set_allow_harmony_unicode(flags.Contains(kAllowHarmonyUnicode));
+  parser->set_allow_harmony_computed_property_names(
+      flags.Contains(kAllowHarmonyComputedPropertyNames));
 }
 
 
@@ -4582,3 +4585,61 @@ TEST(LexicalScopingSloppyMode) {
                     always_true_flags, arraysize(always_true_flags),
                     always_false_flags, arraysize(always_false_flags));
 }
+
+
+TEST(ComputedPropertyName) {
+  const char* context_data[][2] = {{"({[", "]: 1});"},
+                                   {"({get [", "]() {}});"},
+                                   {"({set [", "](_) {}});"},
+                                   {"({[", "]() {}});"},
+                                   {"({*[", "]() {}});"},
+                                   {"(class {get [", "]() {}});"},
+                                   {"(class {set [", "](_) {}});"},
+                                   {"(class {[", "]() {}});"},
+                                   {"(class {*[", "]() {}});"},
+                                   {NULL, NULL}};
+  const char* error_data[] = {
+    "1, 2",
+    "var name",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowHarmonyClasses,
+    kAllowHarmonyComputedPropertyNames,
+    kAllowHarmonyObjectLiterals,
+    kAllowHarmonySloppy,
+  };
+  RunParserSyncTest(context_data, error_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+
+  const char* name_data[] = {
+    "1",
+    "1 + 2",
+    "'name'",
+    "\"name\"",
+    "[]",
+    "{}",
+    NULL};
+
+  RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
+TEST(ComputedPropertyNameShorthandError) {
+  const char* context_data[][2] = {{"({", "});"},
+                                   {NULL, NULL}};
+  const char* error_data[] = {
+    "a: 1, [2]",
+    "[1], a: 1",
+    NULL};
+
+  static const ParserFlag always_flags[] = {
+    kAllowHarmonyClasses,
+    kAllowHarmonyComputedPropertyNames,
+    kAllowHarmonyObjectLiterals,
+    kAllowHarmonySloppy,
+  };
+  RunParserSyncTest(context_data, error_data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
diff --git a/test/mjsunit/harmony/computed-property-names-classes.js b/test/mjsunit/harmony/computed-property-names-classes.js
new file mode 100644 (file)
index 0000000..bcd0318
--- /dev/null
@@ -0,0 +1,367 @@
+// 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.
+
+'use strict';
+
+// Flags: --harmony-computed-property-names --harmony-classes
+
+
+function ID(x) {
+  return x;
+}
+
+
+(function TestClassMethodString() {
+  class C {
+    a() { return 'A'}
+    ['b']() { return 'B'; }
+    c() { return 'C'; }
+    [ID('d')]() { return 'D'; }
+  }
+  assertEquals('A', new C().a());
+  assertEquals('B', new C().b());
+  assertEquals('C', new C().c());
+  assertEquals('D', new C().d());
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C.prototype));
+})();
+
+
+(function TestClassMethodNumber() {
+  class C {
+    a() { return 'A'; }
+    [1]() { return 'B'; }
+    c() { return 'C'; }
+    [ID(2)]() { return 'D'; }
+  }
+  assertEquals('A', new C().a());
+  assertEquals('B', new C()[1]());
+  assertEquals('C', new C().c());
+  assertEquals('D', new C()[2]());
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C.prototype));
+})();
+
+
+(function TestClassMethodSymbol() {
+  var sym1 = Symbol();
+  var sym2 = Symbol();
+  class C {
+    a() { return 'A'; }
+    [sym1]() { return 'B'; }
+    c() { return 'C'; }
+    [ID(sym2)]() { return 'D'; }
+  }
+  assertEquals('A', new C().a());
+  assertEquals('B', new C()[sym1]());
+  assertEquals('C', new C().c());
+  assertEquals('D', new C()[sym2]());
+  assertArrayEquals(['a', 'c'], Object.keys(C.prototype));
+  assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C.prototype));
+})();
+
+
+
+(function TestStaticClassMethodString() {
+  class C {
+    static a() { return 'A'}
+    static ['b']() { return 'B'; }
+    static c() { return 'C'; }
+    static ['d']() { return 'D'; }
+  }
+  assertEquals('A', C.a());
+  assertEquals('B', C.b());
+  assertEquals('C', C.c());
+  assertEquals('D', C.d());
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C));
+})();
+
+
+(function TestStaticClassMethodNumber() {
+  class C {
+    static a() { return 'A'; }
+    static [1]() { return 'B'; }
+    static c() { return 'C'; }
+    static [2]() { return 'D'; }
+  }
+  assertEquals('A', C.a());
+  assertEquals('B', C[1]());
+  assertEquals('C', C.c());
+  assertEquals('D', C[2]());
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C));
+})();
+
+
+(function TestStaticClassMethodSymbol() {
+  var sym1 = Symbol();
+  var sym2 = Symbol();
+  class C {
+    static a() { return 'A'; }
+    static [sym1]() { return 'B'; }
+    static c() { return 'C'; }
+    static [sym2]() { return 'D'; }
+  }
+  assertEquals('A', C.a());
+  assertEquals('B', C[sym1]());
+  assertEquals('C', C.c());
+  assertEquals('D', C[sym2]());
+  assertArrayEquals(['a', 'c'], Object.keys(C));
+  assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C));
+})();
+
+
+
+function assertIteratorResult(value, done, result) {
+  assertEquals({ value: value, done: done}, result);
+}
+
+
+(function TestGeneratorComputedName() {
+  class C {
+    *['a']() {
+      yield 1;
+      yield 2;
+    }
+  }
+  var iter = new C().a();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  assertIteratorResult(undefined, true, iter.next());
+  assertArrayEquals(['a'], Object.keys(C.prototype));
+})();
+
+
+(function TestToNameSideEffects() {
+  var counter = 0;
+  var key1 = {
+    toString: function() {
+      assertEquals(0, counter++);
+      return 'b';
+    }
+  };
+  var key2 = {
+    toString: function() {
+      assertEquals(1, counter++);
+      return 'd';
+    }
+  };
+  class C {
+    a() { return 'A'; }
+    [key1]() { return 'B'; }
+    c() { return 'C'; }
+    [key2]() { return 'D'; }
+  }
+  assertEquals(2, counter);
+  assertEquals('A', new C().a());
+  assertEquals('B', new C().b());
+  assertEquals('C', new C().c());
+  assertEquals('D', new C().d());
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C.prototype));
+})();
+
+
+(function TestToNameSideEffectsNumbers() {
+  var counter = 0;
+  var key1 = {
+    valueOf: function() {
+      assertEquals(0, counter++);
+      return 1;
+    },
+    toString: null
+  };
+  var key2 = {
+    valueOf: function() {
+      assertEquals(1, counter++);
+      return 2;
+    },
+    toString: null
+  };
+
+  class C {
+    a() { return 'A'; }
+    [key1]() { return 'B'; }
+    c() { return 'C'; }
+    [key2]() { return 'D'; }
+  }
+  assertEquals(2, counter);
+  assertEquals('A', new C().a());
+  assertEquals('B', new C()[1]());
+  assertEquals('C', new C().c());
+  assertEquals('D', new C()[2]());
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C.prototype));
+})();
+
+
+(function TestGetter() {
+  class C {
+    get ['a']() {
+      return 'A';
+    }
+  }
+  assertEquals('A', new C().a);
+
+  class C2 {
+    get b() {
+      assertUnreachable();
+    }
+    get ['b']() {
+      return 'B';
+    }
+  }
+  assertEquals('B', new C2().b);
+
+  class C3 {
+    get c() {
+      assertUnreachable();
+    }
+    get ['c']() {
+      assertUnreachable();
+    }
+    get ['c']() {
+      return 'C';
+    }
+  }
+  assertEquals('C', new C3().c);
+
+  class C4 {
+    get ['d']() {
+      assertUnreachable();
+    }
+    get d() {
+      return 'D';
+    }
+  }
+  assertEquals('D', new C4().d);
+})();
+
+
+(function TestSetter() {
+  var calls = 0;
+  class C {
+    set ['a'](_) {
+      calls++;
+    }
+  }
+  new C().a = 'A';
+  assertEquals(1, calls);
+
+  calls = 0;
+  class C2 {
+    set b(_) {
+      assertUnreachable();
+    }
+    set ['b'](_) {
+      calls++;
+    }
+  }
+  new C2().b = 'B';
+  assertEquals(1, calls);
+
+  calls = 0;
+  class C3 {
+    set c(_) {
+      assertUnreachable()
+    }
+    set ['c'](_) {
+      assertUnreachable()
+    }
+    set ['c'](_) {
+      calls++
+    }
+  }
+  new C3().c = 'C';
+  assertEquals(1, calls);
+
+  calls = 0;
+  class C4 {
+    set ['d'](_) {
+      assertUnreachable()
+    }
+    set d(_) {
+      calls++
+    }
+  }
+  new C4().d = 'D';
+  assertEquals(1, calls);
+})();
+
+
+(function TestPrototype() {
+  // Normally a static prototype property is not allowed.
+  class C {
+    static ['prototype']() {
+      return 1;
+    }
+  }
+  assertEquals(1, C.prototype());
+
+  class C2 {
+    static get ['prototype']() {
+      return 2;
+    }
+  }
+  assertEquals(2, C2.prototype);
+
+  var calls = 0;
+  class C3 {
+    static set ['prototype'](x) {
+      assertEquals(3, x);
+      calls++;
+    }
+  }
+  C3.prototype = 3;
+  assertEquals(1, calls);
+
+  class C4 {
+    static *['prototype']() {
+      yield 1;
+      yield 2;
+    }
+  }
+  var iter = C4.prototype();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  assertIteratorResult(undefined, true, iter.next());
+})();
+
+
+(function TestConstructor() {
+  // Normally a constructor property is not allowed.
+  class C {
+    ['constructor']() {
+      return 1;
+    }
+  }
+  assertTrue(C !== C.prototype.constructor);
+  assertEquals(1, new C().constructor());
+
+  class C2 {
+    get ['constructor']() {
+      return 2;
+    }
+  }
+  assertEquals(2, new C2().constructor);
+
+  var calls = 0;
+  class C3 {
+    set ['constructor'](x) {
+      assertEquals(3, x);
+      calls++;
+    }
+  }
+  new C3().constructor = 3;
+  assertEquals(1, calls);
+
+  class C4 {
+    *['constructor']() {
+      yield 1;
+      yield 2;
+    }
+  }
+  var iter = new C4().constructor();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  assertIteratorResult(undefined, true, iter.next());
+})();
diff --git a/test/mjsunit/harmony/computed-property-names-object-literals-methods.js b/test/mjsunit/harmony/computed-property-names-object-literals-methods.js
new file mode 100644 (file)
index 0000000..135d098
--- /dev/null
@@ -0,0 +1,121 @@
+// 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-computed-property-names --harmony-object-literals
+
+
+function ID(x) {
+  return x;
+}
+
+
+(function TestMethodComputedNameString() {
+  var object = {
+    a() { return 'A'},
+    ['b']() { return 'B'; },
+    c() { return 'C'; },
+    [ID('d')]() { return 'D'; },
+  };
+  assertEquals('A', object.a());
+  assertEquals('B', object.b());
+  assertEquals('C', object.c());
+  assertEquals('D', object.d());
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
+})();
+
+
+(function TestMethodComputedNameNumber() {
+  var object = {
+    a() { return 'A'; },
+    [1]() { return 'B'; },
+    c() { return 'C'; },
+    [ID(2)]() { return 'D'; },
+  };
+  assertEquals('A', object.a());
+  assertEquals('B', object[1]());
+  assertEquals('C', object.c());
+  assertEquals('D', object[2]());
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
+})();
+
+
+(function TestMethodComputedNameSymbol() {
+  var sym1 = Symbol();
+  var sym2 = Symbol();
+  var object = {
+    a() { return 'A'; },
+    [sym1]() { return 'B'; },
+    c() { return 'C'; },
+    [ID(sym2)]() { return 'D'; },
+  };
+  assertEquals('A', object.a());
+  assertEquals('B', object[sym1]());
+  assertEquals('C', object.c());
+  assertEquals('D', object[sym2]());
+  assertArrayEquals(['a', 'c'], Object.keys(object));
+  assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(object));
+})();
+
+
+function assertIteratorResult(value, done, result) {
+  assertEquals({ value: value, done: done}, result);
+}
+
+
+(function TestGeneratorComputedName() {
+  var object = {
+    *['a']() {
+      yield 1;
+      yield 2;
+    }
+  };
+  var iter = object.a();
+  assertIteratorResult(1, false, iter.next());
+  assertIteratorResult(2, false, iter.next());
+  assertIteratorResult(undefined, true, iter.next());
+  assertArrayEquals(['a'], Object.keys(object));
+})();
+
+
+(function TestToNameSideEffects() {
+  var counter = 0;
+  var key1 = {
+    toString: function() {
+      assertEquals(0, counter++);
+      return 'b';
+    }
+  };
+  var key2 = {
+    toString: function() {
+      assertEquals(1, counter++);
+      return 'd';
+    }
+  };
+  var object = {
+    a() { return 'A'; },
+    [key1]() { return 'B'; },
+    c() { return 'C'; },
+    [key2]() { return 'D'; },
+  };
+  assertEquals(2, counter);
+  assertEquals('A', object.a());
+  assertEquals('B', object.b());
+  assertEquals('C', object.c());
+  assertEquals('D', object.d());
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
+})();
+
+
+(function TestDuplicateKeys() {
+  'use strict';
+  // ES5 does not allow duplicate keys.
+  // ES6 does but we haven't changed our code yet.
+
+  var object = {
+    a() { return 1; },
+    ['a']() { return 2; },
+  };
+  assertEquals(2, object.a());
+})();
diff --git a/test/mjsunit/harmony/computed-property-names.js b/test/mjsunit/harmony/computed-property-names.js
new file mode 100644 (file)
index 0000000..36ce675
--- /dev/null
@@ -0,0 +1,270 @@
+// 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-computed-property-names
+
+
+function ID(x) {
+  return x;
+}
+
+
+
+(function TestBasicsString() {
+  var object = {
+    a: 'A',
+    ['b']: 'B',
+    c: 'C',
+    [ID('d')]: 'D',
+  };
+  assertEquals('A', object.a);
+  assertEquals('B', object.b);
+  assertEquals('C', object.c);
+  assertEquals('D', object.d);
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
+})();
+
+
+(function TestBasicsNumber() {
+  var object = {
+    a: 'A',
+    [1]: 'B',
+    c: 'C',
+    [ID(2)]: 'D',
+  };
+  assertEquals('A', object.a);
+  assertEquals('B', object[1]);
+  assertEquals('C', object.c);
+  assertEquals('D', object[2]);
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
+})();
+
+
+(function TestBasicsSymbol() {
+  var sym1 = Symbol();
+  var sym2 = Symbol();
+  var object = {
+    a: 'A',
+    [sym1]: 'B',
+    c: 'C',
+    [ID(sym2)]: 'D',
+  };
+  assertEquals('A', object.a);
+  assertEquals('B', object[sym1]);
+  assertEquals('C', object.c);
+  assertEquals('D', object[sym2]);
+  assertArrayEquals(['a', 'c'], Object.keys(object));
+  assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(object));
+})();
+
+
+(function TestToNameSideEffects() {
+  var counter = 0;
+  var key1 = {
+    toString: function() {
+      assertEquals(0, counter++);
+      return 'b';
+    }
+  };
+  var key2 = {
+    toString: function() {
+      assertEquals(1, counter++);
+      return 'd';
+    }
+  };
+  var object = {
+    a: 'A',
+    [key1]: 'B',
+    c: 'C',
+    [key2]: 'D',
+  };
+  assertEquals(2, counter);
+  assertEquals('A', object.a);
+  assertEquals('B', object.b);
+  assertEquals('C', object.c);
+  assertEquals('D', object.d);
+  assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
+})();
+
+
+(function TestToNameSideEffectsNumbers() {
+  var counter = 0;
+  var key1 = {
+    valueOf: function() {
+      assertEquals(0, counter++);
+      return 1;
+    },
+    toString: null
+  };
+  var key2 = {
+    valueOf: function() {
+      assertEquals(1, counter++);
+      return 2;
+    },
+    toString: null
+  };
+
+  var object = {
+    a: 'A',
+    [key1]: 'B',
+    c: 'C',
+    [key2]: 'D',
+  };
+  assertEquals(2, counter);
+  assertEquals('A', object.a);
+  assertEquals('B', object[1]);
+  assertEquals('C', object.c);
+  assertEquals('D', object[2]);
+  // Array indexes first.
+  assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
+})();
+
+
+(function TestDoubleName() {
+  var object = {
+    [1.2]: 'A',
+    [1e55]: 'B',
+    [0.000001]: 'C',
+    [-0]: 'D',
+    [Infinity]: 'E',
+    [-Infinity]: 'F',
+    [NaN]: 'G',
+  };
+  assertEquals('A', object['1.2']);
+  assertEquals('B', object['1e+55']);
+  assertEquals('C', object['0.000001']);
+  assertEquals('D', object[0]);
+  assertEquals('E', object[Infinity]);
+  assertEquals('F', object[-Infinity]);
+  assertEquals('G', object[NaN]);
+})();
+
+
+(function TestGetter() {
+  var object = {
+    get ['a']() {
+      return 'A';
+    }
+  };
+  assertEquals('A', object.a);
+
+  object = {
+    get b() {
+      assertUnreachable();
+    },
+    get ['b']() {
+      return 'B';
+    }
+  };
+  assertEquals('B', object.b);
+
+  object = {
+    get c() {
+      assertUnreachable();
+    },
+    get ['c']() {
+      assertUnreachable();
+    },
+    get ['c']() {
+      return 'C';
+    }
+  };
+  assertEquals('C', object.c);
+
+  object = {
+    get ['d']() {
+      assertUnreachable();
+    },
+    get d() {
+      return 'D';
+    }
+  };
+  assertEquals('D', object.d);
+})();
+
+
+(function TestSetter() {
+  var calls = 0;
+  var object = {
+    set ['a'](_) {
+      calls++;
+    }
+  };
+  object.a = 'A';
+  assertEquals(1, calls);
+
+  calls = 0;
+  object = {
+    set b(_) {
+      assertUnreachable();
+    },
+    set ['b'](_) {
+      calls++;
+    }
+  };
+  object.b = 'B';
+  assertEquals(1, calls);
+
+  calls = 0;
+  object = {
+    set c(_) {
+      assertUnreachable()
+    },
+    set ['c'](_) {
+      assertUnreachable()
+    },
+    set ['c'](_) {
+      calls++
+    }
+  };
+  object.c = 'C';
+  assertEquals(1, calls);
+
+  calls = 0;
+  object = {
+    set ['d'](_) {
+      assertUnreachable()
+    },
+    set d(_) {
+      calls++
+    }
+  };
+  object.d = 'D';
+  assertEquals(1, calls);
+})();
+
+
+(function TestDuplicateKeys() {
+  'use strict';
+  // ES5 does not allow duplicate keys.
+  // ES6 does but we haven't changed our code yet.
+
+  var object = {
+    a: 1,
+    ['a']: 2,
+  };
+  assertEquals(2, object.a);
+})();
+
+
+(function TestProto() {
+  var proto = {};
+  var object = {
+    __proto__: proto
+  };
+  assertEquals(proto, Object.getPrototypeOf(object));
+
+  object = {
+    '__proto__': proto
+  };
+  assertEquals(proto, Object.getPrototypeOf(object));
+
+  var object = {
+    ['__proto__']: proto
+  };
+  assertEquals(Object.prototype, Object.getPrototypeOf(object));
+  assertEquals(proto, object.__proto__);
+  assertTrue(object.hasOwnProperty('__proto__'));
+})();