Revision: 14210
Author: [email protected]
Date: Wed Apr 10 05:16:29 2013
Log: ES6 symbols: extend V8 API to support symbols
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.
[email protected]
BUG=v8:2158
Review URL: https://codereview.chromium.org/13626002
http://code.google.com/p/v8/source/detail?r=14210
Modified:
/branches/bleeding_edge/include/v8.h
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/api.h
/branches/bleeding_edge/src/handles.cc
/branches/bleeding_edge/src/handles.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/test/cctest/test-api.cc
=======================================
--- /branches/bleeding_edge/include/v8.h Wed Apr 10 02:34:37 2013
+++ /branches/bleeding_edge/include/v8.h Wed Apr 10 05:16:29 2013
@@ -127,6 +127,8 @@
class StackTrace;
class String;
class StringObject;
+class Symbol;
+class SymbolObject;
class Uint32;
class Utils;
class Value;
@@ -972,6 +974,12 @@
*/
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 @@
*/
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 @@
/** 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 @@
};
+/**
+ * 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 @@
*/
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.
@@ -1976,6 +2015,27 @@
V8_INLINE(static StringObject* Cast(v8::Value* obj));
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * 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);
};
@@ -4857,6 +4917,14 @@
if (!I::HasHeapObjectTag(obj)) return false;
return (I::GetInstanceType(obj) < I::kFirstNonstringType);
}
+
+
+Symbol* Symbol::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Symbol*>(value);
+}
Number* Number::Cast(v8::Value* value) {
@@ -4889,6 +4957,14 @@
#endif
return static_cast<StringObject*>(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) {
=======================================
--- /branches/bleeding_edge/src/api.cc Wed Apr 10 02:34:37 2013
+++ /branches/bleeding_edge/src/api.cc Wed Apr 10 05:16:29 2013
@@ -2365,6 +2365,12 @@
ASSERT_EQ(result, QuickIsString());
return result;
}
+
+
+bool Value::IsSymbol() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsSymbol()")) return
false;
+ return Utils::OpenHandle(this)->IsSymbol();
+}
bool Value::IsArray() const {
@@ -2449,6 +2455,16 @@
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(isolate->heap()->String_string());
}
+
+
+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 {
@@ -2662,6 +2678,15 @@
"v8::String::Cast()",
"Could not convert to string");
}
+
+
+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) {
@@ -2709,6 +2734,16 @@
"v8::StringObject::Cast()",
"Could not convert to StringObject");
}
+
+
+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) {
@@ -3079,13 +3114,13 @@
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 @@
}
-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();
}
@@ -4591,6 +4634,15 @@
return NULL;
}
}
+
+
+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 {
@@ -5455,6 +5507,29 @@
return Utils::ToLocal(
i::Handle<i::String>(i::String::cast(jsvalue->value())));
}
+
+
+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) {
@@ -5636,6 +5711,30 @@
i::Vector<const char>(data, length));
return Utils::ToLocal(result);
}
+
+
+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) {
=======================================
--- /branches/bleeding_edge/src/api.h Fri Mar 22 06:40:13 2013
+++ /branches/bleeding_edge/src/api.h Wed Apr 10 05:16:29 2013
@@ -171,6 +171,7 @@
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 @@
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, 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)
=======================================
--- /branches/bleeding_edge/src/handles.cc Fri Mar 22 07:33:27 2013
+++ /branches/bleeding_edge/src/handles.cc Wed Apr 10 05:16:29 2013
@@ -250,15 +250,32 @@
isolate, object, key, value, attributes),
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,
=======================================
--- /branches/bleeding_edge/src/handles.h Fri Mar 22 07:33:27 2013
+++ /branches/bleeding_edge/src/handles.h Wed Apr 10 05:16:29 2013
@@ -223,11 +223,13 @@
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,
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Apr 10 02:24:31 2013
+++ /branches/bleeding_edge/src/runtime.cc Wed Apr 10 05:16:29 2013
@@ -3945,6 +3945,33 @@
return object->GetElement(index);
}
+
+
+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,
@@ -4364,9 +4391,10 @@
}
-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 @@
return isolate->heap()->true_value();
}
- return receiver->DeleteElement(index, JSReceiver::FORCE_DELETION);
+ return receiver->DeleteElement(index, mode);
}
Handle<Name> name;
@@ -4397,7 +4425,7 @@
}
if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
- return receiver->DeleteProperty(*name, JSReceiver::FORCE_DELETION);
+ return receiver->DeleteProperty(*name, mode);
}
=======================================
--- /branches/bleeding_edge/src/runtime.h Fri Apr 5 04:57:02 2013
+++ /branches/bleeding_edge/src/runtime.h Wed Apr 10 05:16:29 2013
@@ -699,7 +699,13 @@
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);
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Tue Apr 9 00:34:32 2013
+++ /branches/bleeding_edge/test/cctest/test-api.cc Wed Apr 10 05:16:29 2013
@@ -2176,6 +2176,88 @@
CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
}
}
+
+
+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) {
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.