ES6 symbols: extend V8 API to support symbols
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Apr 2013 12:16:29 +0000 (12:16 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Apr 2013 12:16:29 +0000 (12:16 +0000)
Specifically:
- Introduce Symbol and SymbolObject classes.
- Generalise Object::Has and Object::Delete to arbitrary Value-typed keys.
- Generalise some places in the API implementation from String to Name.

It is not possible to intercept symbol-named properties. That is consistent with the idea that symbols are private and should not leak.

R=svenpanne@chromium.org
BUG=v8:2158

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

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

include/v8.h
src/api.cc
src/api.h
src/handles.cc
src/handles.h
src/runtime.cc
src/runtime.h
test/cctest/test-api.cc

index a43446639749441a888509ee2f81ac97f5efded0..00337efc6706efc586fcfdf86b94faf81f03841f 100644 (file)
@@ -127,6 +127,8 @@ class StackFrame;
 class StackTrace;
 class String;
 class StringObject;
+class Symbol;
+class SymbolObject;
 class Uint32;
 class Utils;
 class Value;
@@ -972,6 +974,12 @@ class V8EXPORT Value : public Data {
    */
   V8_INLINE(bool IsString() const);
 
+  /**
+   * Returns true if this value is a symbol.
+   * This is an experimental feature.
+   */
+  bool IsSymbol() const;
+
   /**
    * Returns true if this value is a function.
    */
@@ -1032,6 +1040,12 @@ class V8EXPORT Value : public Data {
    */
   bool IsStringObject() const;
 
+  /**
+   * Returns true if this value is a Symbol object.
+   * This is an experimental feature.
+   */
+  bool IsSymbolObject() const;
+
   /**
    * Returns true if this value is a NativeError.
    */
@@ -1311,7 +1325,11 @@ class V8EXPORT String : public Primitive {
   /** Allocates a new string from 16-bit character codes.*/
   static Local<String> New(const uint16_t* data, int length = -1);
 
-  /** Creates a symbol. Returns one if it exists already.*/
+  /**
+   * Creates an internalized string (historically called a "symbol",
+   * not to be confused with ES6 symbols). Returns one if it exists already.
+   * TODO(rossberg): Deprecate me when the new string API is here.
+   */
   static Local<String> NewSymbol(const char* data, int length = -1);
 
   /**
@@ -1449,6 +1467,29 @@ class V8EXPORT String : public Primitive {
 };
 
 
+/**
+ * A JavaScript symbol (ECMA-262 edition 6)
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8EXPORT Symbol : public Primitive {
+ public:
+  // Returns the print name string of the symbol, or undefined if none.
+  Local<Value> Name() const;
+
+  // Create a symbol without a print name.
+  static Local<Symbol> New(Isolate* isolate);
+
+  // Create a symbol with a print name.
+  static Local<Symbol> New(Isolate *isolate, const char* data, int length = -1);
+
+  V8_INLINE(static Symbol* Cast(v8::Value* obj));
+ private:
+  Symbol();
+  static void CheckCast(v8::Value* obj);
+};
+
+
 /**
  * A JavaScript number value (ECMA-262, 4.3.20)
  */
@@ -1590,11 +1631,9 @@ class V8EXPORT Object : public Value {
    */
   PropertyAttribute GetPropertyAttributes(Handle<Value> key);
 
-  // TODO(1245389): Replace the type-specific versions of these
-  // functions with generic ones that accept a Handle<Value> key.
-  bool Has(Handle<String> key);
+  bool Has(Handle<Value> key);
 
-  bool Delete(Handle<String> key);
+  bool Delete(Handle<Value> key);
 
   // Delete a property on this object bypassing interceptors and
   // ignoring dont-delete attributes.
@@ -1981,6 +2020,27 @@ class V8EXPORT StringObject : public Object {
 };
 
 
+/**
+ * A Symbol object (ECMA-262 edition 6).
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8EXPORT SymbolObject : public Object {
+ public:
+  static Local<Value> New(Isolate* isolate, Handle<Symbol> value);
+
+  /**
+   * Returns the Symbol held by the object.
+   */
+  Local<Symbol> SymbolValue() const;
+
+  V8_INLINE(static SymbolObject* Cast(v8::Value* obj));
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
 /**
  * An instance of the built-in RegExp constructor (ECMA-262, 15.10).
  */
@@ -4859,6 +4919,14 @@ bool Value::QuickIsString() const {
 }
 
 
+Symbol* Symbol::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Symbol*>(value);
+}
+
+
 Number* Number::Cast(v8::Value* value) {
 #ifdef V8_ENABLE_CHECKS
   CheckCast(value);
@@ -4891,6 +4959,14 @@ StringObject* StringObject::Cast(v8::Value* value) {
 }
 
 
+SymbolObject* SymbolObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<SymbolObject*>(value);
+}
+
+
 NumberObject* NumberObject::Cast(v8::Value* value) {
 #ifdef V8_ENABLE_CHECKS
   CheckCast(value);
index a7b45c4307e7a51b2642d94e6c77a56c18cac0a8..76a036b28d940f62ec7d8d332ee7cdba984ffae3 100644 (file)
@@ -2367,6 +2367,12 @@ bool Value::FullIsString() const {
 }
 
 
+bool Value::IsSymbol() const {
+  if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsSymbol()")) return false;
+  return Utils::OpenHandle(this)->IsSymbol();
+}
+
+
 bool Value::IsArray() const {
   if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsArray()")) return false;
   return Utils::OpenHandle(this)->IsJSArray();
@@ -2451,6 +2457,16 @@ bool Value::IsStringObject() const {
 }
 
 
+bool Value::IsSymbolObject() const {
+  // TODO(svenpanne): these and other test functions should be written such
+  // that they do not use Isolate::Current().
+  i::Isolate* isolate = i::Isolate::Current();
+  if (IsDeadCheck(isolate, "v8::Value::IsSymbolObject()")) return false;
+  i::Handle<i::Object> obj = Utils::OpenHandle(this);
+  return obj->HasSpecificClassOf(isolate->heap()->Symbol_string());
+}
+
+
 bool Value::IsNumberObject() const {
   i::Isolate* isolate = i::Isolate::Current();
   if (IsDeadCheck(isolate, "v8::Value::IsNumberObject()")) return false;
@@ -2664,6 +2680,15 @@ void v8::String::CheckCast(v8::Value* that) {
 }
 
 
+void v8::Symbol::CheckCast(v8::Value* that) {
+  if (IsDeadCheck(i::Isolate::Current(), "v8::Symbol::Cast()")) return;
+  i::Handle<i::Object> obj = Utils::OpenHandle(that);
+  ApiCheck(obj->IsSymbol(),
+           "v8::Symbol::Cast()",
+           "Could not convert to symbol");
+}
+
+
 void v8::Number::CheckCast(v8::Value* that) {
   if (IsDeadCheck(i::Isolate::Current(), "v8::Number::Cast()")) return;
   i::Handle<i::Object> obj = Utils::OpenHandle(that);
@@ -2711,6 +2736,16 @@ void v8::StringObject::CheckCast(v8::Value* that) {
 }
 
 
+void v8::SymbolObject::CheckCast(v8::Value* that) {
+  i::Isolate* isolate = i::Isolate::Current();
+  if (IsDeadCheck(isolate, "v8::SymbolObject::Cast()")) return;
+  i::Handle<i::Object> obj = Utils::OpenHandle(that);
+  ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Symbol_string()),
+           "v8::SymbolObject::Cast()",
+           "Could not convert to SymbolObject");
+}
+
+
 void v8::NumberObject::CheckCast(v8::Value* that) {
   i::Isolate* isolate = i::Isolate::Current();
   if (IsDeadCheck(isolate, "v8::NumberObject::Cast()")) return;
@@ -3079,13 +3114,13 @@ PropertyAttribute v8::Object::GetPropertyAttributes(v8::Handle<Value> key) {
   i::HandleScope scope(isolate);
   i::Handle<i::JSObject> self = Utils::OpenHandle(this);
   i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
-  if (!key_obj->IsString()) {
+  if (!key_obj->IsName()) {
     EXCEPTION_PREAMBLE(isolate);
     key_obj = i::Execution::ToString(key_obj, &has_pending_exception);
     EXCEPTION_BAILOUT_CHECK(isolate, static_cast<PropertyAttribute>(NONE));
   }
-  i::Handle<i::String> key_string = i::Handle<i::String>::cast(key_obj);
-  PropertyAttributes result = self->GetPropertyAttribute(*key_string);
+  i::Handle<i::Name> key_name = i::Handle<i::Name>::cast(key_obj);
+  PropertyAttributes result = self->GetPropertyAttribute(*key_name);
   if (result == ABSENT) return static_cast<PropertyAttribute>(NONE);
   return static_cast<PropertyAttribute>(result);
 }
@@ -3255,24 +3290,32 @@ Local<String> v8::Object::GetConstructorName() {
 }
 
 
-bool v8::Object::Delete(v8::Handle<String> key) {
+bool v8::Object::Delete(v8::Handle<Value> key) {
   i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
   ON_BAILOUT(isolate, "v8::Object::Delete()", return false);
   ENTER_V8(isolate);
   i::HandleScope scope(isolate);
   i::Handle<i::JSObject> self = Utils::OpenHandle(this);
-  i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
-  return i::JSObject::DeleteProperty(self, key_obj)->IsTrue();
+  i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+  EXCEPTION_PREAMBLE(isolate);
+  i::Handle<i::Object> obj = i::DeleteProperty(self, key_obj);
+  has_pending_exception = obj.is_null();
+  EXCEPTION_BAILOUT_CHECK(isolate, false);
+  return obj->IsTrue();
 }
 
 
-bool v8::Object::Has(v8::Handle<String> key) {
+bool v8::Object::Has(v8::Handle<Value> key) {
   i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
   ON_BAILOUT(isolate, "v8::Object::Has()", return false);
   ENTER_V8(isolate);
-  i::Handle<i::JSObject> self = Utils::OpenHandle(this);
-  i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
-  return self->HasProperty(*key_obj);
+  i::Handle<i::JSReceiver> self = Utils::OpenHandle(this);
+  i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+  EXCEPTION_PREAMBLE(isolate);
+  i::Handle<i::Object> obj = i::HasProperty(self, key_obj);
+  has_pending_exception = obj.is_null();
+  EXCEPTION_BAILOUT_CHECK(isolate, false);
+  return obj->IsTrue();
 }
 
 
@@ -4593,6 +4636,15 @@ const v8::String::ExternalAsciiStringResource*
 }
 
 
+Local<Value> Symbol::Name() const {
+  if (IsDeadCheck(i::Isolate::Current(), "v8::Symbol::Name()"))
+    return Local<Value>();
+  i::Handle<i::Symbol> sym = Utils::OpenHandle(this);
+  i::Handle<i::Object> name(sym->name(), sym->GetIsolate());
+  return Utils::ToLocal(name);
+}
+
+
 double Number::Value() const {
   if (IsDeadCheck(i::Isolate::Current(), "v8::Number::Value()")) return 0;
   i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -5457,6 +5509,29 @@ Local<v8::String> v8::StringObject::StringValue() const {
 }
 
 
+Local<v8::Value> v8::SymbolObject::New(Isolate* isolate, Handle<Symbol> value) {
+  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+  EnsureInitializedForIsolate(i_isolate, "v8::SymbolObject::New()");
+  LOG_API(i_isolate, "SymbolObject::New");
+  ENTER_V8(i_isolate);
+  i::Handle<i::Object> obj =
+      i_isolate->factory()->ToObject(Utils::OpenHandle(*value));
+  return Utils::ToLocal(obj);
+}
+
+
+Local<v8::Symbol> v8::SymbolObject::SymbolValue() const {
+  i::Isolate* isolate = i::Isolate::Current();
+  if (IsDeadCheck(isolate, "v8::SymbolObject::SymbolValue()"))
+    return Local<v8::Symbol>();
+  LOG_API(isolate, "SymbolObject::SymbolValue");
+  i::Handle<i::Object> obj = Utils::OpenHandle(this);
+  i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+  return Utils::ToLocal(
+      i::Handle<i::Symbol>(i::Symbol::cast(jsvalue->value())));
+}
+
+
 Local<v8::Value> v8::Date::New(double time) {
   i::Isolate* isolate = i::Isolate::Current();
   EnsureInitializedForIsolate(isolate, "v8::Date::New()");
@@ -5638,6 +5713,30 @@ Local<String> v8::String::NewSymbol(const char* data, int length) {
 }
 
 
+Local<Symbol> v8::Symbol::New(Isolate* isolate) {
+  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+  EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
+  LOG_API(i_isolate, "Symbol::New()");
+  ENTER_V8(i_isolate);
+  i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+  return Utils::ToLocal(result);
+}
+
+
+Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
+  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+  EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
+  LOG_API(i_isolate, "Symbol::New(char)");
+  ENTER_V8(i_isolate);
+  if (length == -1) length = i::StrLength(data);
+  i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+      i::Vector<const char>(data, length));
+  i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+  result->set_name(*name);
+  return Utils::ToLocal(result);
+}
+
+
 Local<Number> v8::Number::New(double value) {
   i::Isolate* isolate = i::Isolate::Current();
   EnsureInitializedForIsolate(isolate, "v8::Number::New()");
index d73646d99d668ec2a7ca6ea08e7a5a367b26867e..0cd16f1f01f4fcc60d9c3deb5c6ad34fe725a326 100644 (file)
--- a/src/api.h
+++ b/src/api.h
@@ -171,6 +171,7 @@ class RegisteredExtension {
   V(Object, JSObject)                          \
   V(Array, JSArray)                            \
   V(String, String)                            \
+  V(Symbol, Symbol)                            \
   V(Script, Object)                            \
   V(Function, JSFunction)                      \
   V(Message, JSObject)                         \
@@ -196,6 +197,8 @@ class Utils {
       v8::internal::Handle<v8::internal::JSFunction> obj);
   static inline Local<String> ToLocal(
       v8::internal::Handle<v8::internal::String> obj);
+  static inline Local<Symbol> ToLocal(
+      v8::internal::Handle<v8::internal::Symbol> obj);
   static inline Local<RegExp> ToLocal(
       v8::internal::Handle<v8::internal::JSRegExp> obj);
   static inline Local<Object> ToLocal(
@@ -268,6 +271,7 @@ MAKE_TO_LOCAL(ToLocal, Context, Context)
 MAKE_TO_LOCAL(ToLocal, Object, Value)
 MAKE_TO_LOCAL(ToLocal, JSFunction, Function)
 MAKE_TO_LOCAL(ToLocal, String, String)
+MAKE_TO_LOCAL(ToLocal, Symbol, Symbol)
 MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
 MAKE_TO_LOCAL(ToLocal, JSObject, Object)
 MAKE_TO_LOCAL(ToLocal, JSArray, Array)
index b24a4cd1d8b541bb95561e2f684122ccbc9be456..3de4f5d9f92f6037a16384df316caf1c1a03558d 100644 (file)
@@ -252,15 +252,32 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object,
 }
 
 
+Handle<Object> DeleteProperty(Handle<JSObject> object, Handle<Object> key) {
+  Isolate* isolate = object->GetIsolate();
+  CALL_HEAP_FUNCTION(isolate,
+                     Runtime::DeleteObjectProperty(
+                         isolate, object, key, JSReceiver::NORMAL_DELETION),
+                     Object);
+}
+
+
 Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
                                    Handle<Object> key) {
   Isolate* isolate = object->GetIsolate();
   CALL_HEAP_FUNCTION(isolate,
-                     Runtime::ForceDeleteObjectProperty(isolate, object, key),
+                     Runtime::DeleteObjectProperty(
+                         isolate, object, key, JSReceiver::FORCE_DELETION),
                      Object);
 }
 
 
+Handle<Object> HasProperty(Handle<JSReceiver> obj, Handle<Object> key) {
+  Isolate* isolate = obj->GetIsolate();
+  CALL_HEAP_FUNCTION(isolate,
+                     Runtime::HasObjectProperty(isolate, obj, key), Object);
+}
+
+
 Handle<Object> GetProperty(Handle<JSReceiver> obj,
                            const char* name) {
   Isolate* isolate = obj->GetIsolate();
index c69713e44e8e4422bcfc19f41fe3f58c6f8d208d..245b1c3f1479dab793ff341b1859e4f10224ebd2 100644 (file)
@@ -223,11 +223,13 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object,
                                 Handle<Object> value,
                                 PropertyAttributes attributes);
 
-Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
-                                   Handle<Object> key);
+Handle<Object> DeleteProperty(Handle<JSObject> object, Handle<Object> key);
 
-Handle<Object> GetProperty(Handle<JSReceiver> obj,
-                           const char* name);
+Handle<Object> ForceDeleteProperty(Handle<JSObject> object, Handle<Object> key);
+
+Handle<Object> HasProperty(Handle<JSReceiver> obj, Handle<Object> key);
+
+Handle<Object> GetProperty(Handle<JSReceiver> obj, const char* name);
 
 Handle<Object> GetProperty(Isolate* isolate,
                            Handle<Object> obj,
index 61ac7ab2750aabce2b7b8fc143794a1e0ff6ddd2..7288f38171eb75f6c4e15106817249b4b0b950fe 100644 (file)
@@ -3947,6 +3947,33 @@ MaybeObject* Runtime::GetElementOrCharAt(Isolate* isolate,
 }
 
 
+MaybeObject* Runtime::HasObjectProperty(Isolate* isolate,
+                                        Handle<JSReceiver> object,
+                                        Handle<Object> key) {
+  HandleScope scope(isolate);
+
+  // Check if the given key is an array index.
+  uint32_t index;
+  if (key->ToArrayIndex(&index)) {
+    return isolate->heap()->ToBoolean(object->HasElement(index));
+  }
+
+  // Convert the key to a name - possibly by calling back into JavaScript.
+  Handle<Name> name;
+  if (key->IsName()) {
+    name = Handle<Name>::cast(key);
+  } else {
+    bool has_pending_exception = false;
+    Handle<Object> converted =
+        Execution::ToString(key, &has_pending_exception);
+    if (has_pending_exception) return Failure::Exception();
+    name = Handle<Name>::cast(converted);
+  }
+
+  return isolate->heap()->ToBoolean(object->HasProperty(*name));
+}
+
+
 MaybeObject* Runtime::GetObjectProperty(Isolate* isolate,
                                         Handle<Object> object,
                                         Handle<Object> key) {
@@ -4364,9 +4391,10 @@ MaybeObject* Runtime::ForceSetObjectProperty(Isolate* isolate,
 }
 
 
-MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate,
-                                                Handle<JSReceiver> receiver,
-                                                Handle<Object> key) {
+MaybeObject* Runtime::DeleteObjectProperty(Isolate* isolate,
+                                           Handle<JSReceiver> receiver,
+                                           Handle<Object> key,
+                                           JSReceiver::DeleteMode mode) {
   HandleScope scope(isolate);
 
   // Check if the given key is an array index.
@@ -4382,7 +4410,7 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate,
       return isolate->heap()->true_value();
     }
 
-    return receiver->DeleteElement(index, JSReceiver::FORCE_DELETION);
+    return receiver->DeleteElement(index, mode);
   }
 
   Handle<Name> name;
@@ -4397,7 +4425,7 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate,
   }
 
   if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
-  return receiver->DeleteProperty(*name, JSReceiver::FORCE_DELETION);
+  return receiver->DeleteProperty(*name, mode);
 }
 
 
index 487bd8042c010ee3abfd68ed5d73476480136360..f82963edac552ab38b78c41de18e492c6f51420d 100644 (file)
@@ -699,7 +699,13 @@ class Runtime : public AllStatic {
       Handle<Object> value,
       PropertyAttributes attr);
 
-  MUST_USE_RESULT static MaybeObject* ForceDeleteObjectProperty(
+  MUST_USE_RESULT static MaybeObject* DeleteObjectProperty(
+      Isolate* isolate,
+      Handle<JSReceiver> object,
+      Handle<Object> key,
+      JSReceiver::DeleteMode mode);
+
+  MUST_USE_RESULT static MaybeObject* HasObjectProperty(
       Isolate* isolate,
       Handle<JSReceiver> object,
       Handle<Object> key);
index 1e789f30da847221395b9489802aacc8375187be..fc026a485771e9219475c3f723b3d1cf23d7866c 100644 (file)
@@ -2178,6 +2178,88 @@ THREADED_TEST(IdentityHash) {
 }
 
 
+THREADED_TEST(SymbolProperties) {
+  i::FLAG_harmony_symbols = true;
+
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope scope(isolate);
+
+  v8::Local<v8::Object> obj = v8::Object::New();
+  v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate);
+  v8::Local<v8::Symbol> sym2 = v8::Symbol::New(isolate, "my-symbol");
+
+  HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  // Check basic symbol functionality.
+  CHECK(sym1->IsSymbol());
+  CHECK(sym2->IsSymbol());
+  CHECK(!obj->IsSymbol());
+
+  CHECK(sym1->Equals(sym1));
+  CHECK(sym2->Equals(sym2));
+  CHECK(!sym1->Equals(sym2));
+  CHECK(!sym2->Equals(sym1));
+  CHECK(sym1->StrictEquals(sym1));
+  CHECK(sym2->StrictEquals(sym2));
+  CHECK(!sym1->StrictEquals(sym2));
+  CHECK(!sym2->StrictEquals(sym1));
+
+  CHECK(sym2->Name()->Equals(v8::String::New("my-symbol")));
+
+  v8::Local<v8::Value> sym_val = sym2;
+  CHECK(sym_val->IsSymbol());
+  CHECK(sym_val->Equals(sym2));
+  CHECK(sym_val->StrictEquals(sym2));
+  CHECK(v8::Symbol::Cast(*sym_val)->Equals(sym2));
+
+  v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2);
+  CHECK(sym_obj->IsSymbolObject());
+  CHECK(!sym2->IsSymbolObject());
+  CHECK(!obj->IsSymbolObject());
+  CHECK(sym_obj->Equals(sym2));
+  CHECK(!sym_obj->StrictEquals(sym2));
+  CHECK(v8::SymbolObject::Cast(*sym_obj)->Equals(sym_obj));
+  CHECK(v8::SymbolObject::Cast(*sym_obj)->SymbolValue()->Equals(sym2));
+
+  // Make sure delete of a non-existent symbol property works.
+  CHECK(obj->Delete(sym1));
+  CHECK(!obj->Has(sym1));
+
+  CHECK(obj->Set(sym1, v8::Integer::New(1503)));
+  CHECK(obj->Has(sym1));
+  CHECK_EQ(1503, obj->Get(sym1)->Int32Value());
+  CHECK(obj->Set(sym1, v8::Integer::New(2002)));
+  CHECK(obj->Has(sym1));
+  CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
+  CHECK_EQ(v8::None, obj->GetPropertyAttributes(sym1));
+
+  CHECK_EQ(0, obj->GetOwnPropertyNames()->Length());
+  int num_props = obj->GetPropertyNames()->Length();
+  CHECK(obj->Set(v8::String::New("bla"), v8::Integer::New(20)));
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+  CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length());
+
+  HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  // Add another property and delete it afterwards to force the object in
+  // slow case.
+  CHECK(obj->Set(sym2, v8::Integer::New(2008)));
+  CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
+  CHECK_EQ(2008, obj->Get(sym2)->Int32Value());
+  CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+  CHECK(obj->Has(sym1));
+  CHECK(obj->Has(sym2));
+  CHECK(obj->Delete(sym2));
+  CHECK(obj->Has(sym1));
+  CHECK(!obj->Has(sym2));
+  CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+}
+
+
 THREADED_TEST(HiddenProperties) {
   LocalContext env;
   v8::HandleScope scope(env->GetIsolate());