class StackTrace;
class String;
class StringObject;
+class Symbol;
+class SymbolObject;
class Uint32;
class Utils;
class Value;
*/
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.
*/
*/
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.
*/
/** 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);
/**
};
+/**
+ * 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)
*/
*/
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.
};
+/**
+ * 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).
*/
}
+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);
}
+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);
}
+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();
}
+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;
}
+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);
}
+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;
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);
}
}
-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();
}
}
+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);
}
+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()");
}
+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()");
V(Object, JSObject) \
V(Array, JSArray) \
V(String, String) \
+ V(Symbol, Symbol) \
V(Script, Object) \
V(Function, JSFunction) \
V(Message, JSObject) \
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(
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)
}
+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();
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,
}
+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) {
}
-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.
return isolate->heap()->true_value();
}
- return receiver->DeleteElement(index, JSReceiver::FORCE_DELETION);
+ return receiver->DeleteElement(index, mode);
}
Handle<Name> name;
}
if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
- return receiver->DeleteProperty(*name, JSReceiver::FORCE_DELETION);
+ return receiver->DeleteProperty(*name, mode);
}
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);
}
+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());