Index: test/cctest/test-api.cc |
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
index 484d2f32264643d36cb29130e0e3259061185f06..09875b9de3403ee0906ad3f23236f8bd43536d3f 100644 |
--- a/test/cctest/test-api.cc |
+++ b/test/cctest/test-api.cc |
@@ -16004,6 +16004,242 @@ TEST(DefineOwnProperty) { |
} |
} |
+TEST(DefineProperty) { |
+ LocalContext env; |
+ v8::Isolate* isolate = env->GetIsolate(); |
+ v8::HandleScope handle_scope(isolate); |
+ |
+ CompileRun( |
+ "var a = {};" |
+ "var b = [];" |
+ "Object.defineProperty(a, 'foo', {value: 23});" |
+ "Object.defineProperty(a, 'bar', {value: 23, configurable: true});"); |
+ |
+ v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast( |
+ env->Global()->Get(env.local(), v8_str("a")).ToLocalChecked()); |
+ v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast( |
+ env->Global()->Get(env.local(), v8_str("b")).ToLocalChecked()); |
+ |
+ const v8::PropertyDescriptor desc = v8::PropertyDescriptor(v8_num(42), true); |
+ { |
+ // Use a data descriptor. |
+ |
+ // Can't change a non-configurable property. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(!obj->DefineProperty(env.local(), v8_str("foo"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("foo")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); |
+ |
+ // Change configurable property |
+ CHECK(obj->DefineProperty(env.local(), v8_str("bar"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ val = obj->Get(env.local(), v8_str("bar")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ } |
+ |
+ { |
+ // Set a regular property. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("blub"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("blub")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ } |
+ |
+ { |
+ // Set an indexed property. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("1"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ } |
+ |
+ { |
+ // No special case when changing array length. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(arr->DefineProperty(env.local(), v8_str("length"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Special cases for arrays: index exceeds the array's length. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(arr->DefineProperty(env.local(), v8_str("100"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ CHECK_EQ(101U, arr->Length()); |
+ v8::Local<v8::Value> val = arr->Get(env.local(), 100).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ |
+ // Set an existing entry. |
+ CHECK(arr->DefineProperty(env.local(), v8_str("0"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ val = arr->Get(env.local(), 0).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ } |
+ |
+ { |
+ // Use a generic descriptor. |
+ const v8::PropertyDescriptor desc = v8::PropertyDescriptor(); |
+ |
+ v8::TryCatch try_catch(isolate); |
+ CHECK( |
+ obj->DefineProperty(env.local(), v8_str("generic"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("generic")).ToLocalChecked(); |
+ CHECK(val->IsUndefined()); |
+ |
+ obj->Set(env.local(), v8_str("generic"), v8_num(1)).FromJust(); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ val = obj->Get(env.local(), v8_str("generic")).ToLocalChecked(); |
+ CHECK(val->IsUndefined()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Use a data descriptor with empty handle. |
+ const v8::PropertyDescriptor desc = |
+ v8::PropertyDescriptor(v8::Local<v8::Value>(), true); |
+ |
+ v8::TryCatch try_catch(isolate); |
+ CHECK( |
+ obj->DefineProperty(env.local(), v8_str("generic"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("generic")).ToLocalChecked(); |
+ CHECK(val->IsUndefined()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Use a descriptor with attribute == v8::ReadOnly. |
+ const v8::PropertyDescriptor desc = v8::PropertyDescriptor( |
+ v8_num(42), true, true, true, true, true, false, true); |
+ |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("lala"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("lala")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
+ CHECK_EQ( |
+ v8::ReadOnly, |
+ obj->GetPropertyAttributes(env.local(), v8_str("lala")).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Use an accessor descriptor with empty handles. |
+ const v8::PropertyDescriptor desc = v8::PropertyDescriptor( |
+ v8::Local<v8::Function>(), true, v8::Local<v8::Function>(), true); |
+ |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("undef_accessor"), &desc) |
+ .FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("undef_accessor")).ToLocalChecked(); |
+ CHECK(val->IsUndefined()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Use an accessor descriptor. |
+ CompileRun( |
+ "var set = function(x) {this.val = 2*x;};" |
+ "var get = function() {return this.val || 0;};"); |
+ |
+ v8::Local<v8::Function> get = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("get")).ToLocalChecked()); |
+ v8::Local<v8::Function> set = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("set")).ToLocalChecked()); |
+ const v8::PropertyDescriptor desc = |
+ v8::PropertyDescriptor(get, true, set, true); |
+ |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("twice"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ v8::Local<v8::Value> val = |
+ obj->Get(env.local(), v8_str("twice")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(0.0, val->NumberValue(env.local()).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ obj->Set(env.local(), v8_str("twice"), v8_num(7)).FromJust(); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ val = obj->Get(env.local(), v8_str("twice")).ToLocalChecked(); |
+ CHECK(val->IsNumber()); |
+ CHECK_EQ(14.0, val->NumberValue(env.local()).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ { |
+ // Re-define a property |
+ // desc = {value: 42, enumerable: true} |
+ const v8::PropertyDescriptor desc = |
+ v8::PropertyDescriptor(v8_num(42), false, true, true); |
+ v8::TryCatch try_catch(isolate); |
+ CHECK( |
+ obj->DefineProperty(env.local(), v8_str("has_enum"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ // desc = {enumerable: true} |
+ // successful because has_enumerable with same value as existing descriptor |
+ const v8::PropertyDescriptor desc_true = |
+ v8::PropertyDescriptor(v8::Local<v8::Value>(), false, true, true); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("has_enum"), &desc_true) |
+ .FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ // desc = {} |
+ // successful because !has_enumerable |
+ const v8::PropertyDescriptor desc_empty = v8::PropertyDescriptor(); |
+ CHECK(obj->DefineProperty(env.local(), v8_str("has_enum"), &desc_empty) |
+ .FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ |
+ // desc = {enumerable: false} |
+ const v8::PropertyDescriptor desc_false = |
+ v8::PropertyDescriptor(v8::Local<v8::Value>(), false, false, true); |
+ // not be successful, because we cannot overwrite enumerable |
+ CHECK(!obj->DefineProperty(env.local(), v8_str("has_enum"), &desc_false) |
+ .FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ CompileRun("Object.freeze(a);"); |
+ { |
+ // Can't change non-extensible objects. |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(!obj->DefineProperty(env.local(), v8_str("baz"), &desc).FromJust()); |
+ CHECK(!try_catch.HasCaught()); |
+ } |
+ |
+ v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
+ templ->SetAccessCheckCallback(AccessAlwaysBlocked); |
+ v8::Local<v8::Object> access_checked = |
+ templ->NewInstance(env.local()).ToLocalChecked(); |
+ { |
+ v8::TryCatch try_catch(isolate); |
+ CHECK(access_checked->DefineProperty(env.local(), v8_str("foo"), &desc) |
+ .IsNothing()); |
+ CHECK(try_catch.HasCaught()); |
+ } |
+} |
THREADED_TEST(GetCurrentContextWhenNotInContext) { |
i::Isolate* isolate = CcTest::i_isolate(); |
@@ -23339,6 +23575,191 @@ TEST(EventLogging) { |
CHECK_EQ(1, last_event_status); |
} |
+TEST(PropertyDescriptor) { |
+ LocalContext context; |
+ v8::Isolate* isolate = context->GetIsolate(); |
+ v8::HandleScope scope(isolate); |
+ |
+ // empty descriptor |
+ v8::PropertyDescriptor desc; |
+ CHECK(!desc.has_value()); |
+ CHECK(desc.value() == v8::Local<v8::Value>()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, false, true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, false, false, true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, false, false, false, true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, false, false, false, false, |
+ true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8_num(42), false, false, false, false, false, |
+ false, true); |
+ CHECK(desc.value() == v8_num(42)); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(desc.has_writable()); |
+ |
+ // data descriptor |
+ desc = v8::PropertyDescriptor(v8::Local<v8::Value>(), false, false, false, |
+ false, false, false, true); |
+ CHECK(!desc.has_value()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.has_get()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(desc.has_writable()); |
+ CHECK(!desc.writable()); |
+ |
+ // accessor descriptor |
+ LocalContext env; |
+ |
+ CompileRun( |
+ "var set = function() {return 43;};" |
+ "var get = function() {return 42;};"); |
+ |
+ v8::Local<v8::Function> get = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("get")).ToLocalChecked()); |
+ v8::Local<v8::Function> set = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("set")).ToLocalChecked()); |
+ desc = |
+ v8::PropertyDescriptor(get, false, set, true, false, false, false, true); |
+ CHECK(!desc.has_value()); |
+ CHECK(desc.get() == get); |
+ CHECK(!desc.has_get()); |
+ CHECK(desc.set() == set); |
+ CHECK(desc.has_set()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // accessor descriptor with Proxy |
+ CompileRun( |
+ "var set = new Proxy(function() {}, {});" |
+ "var get = function() {return 42;};"); |
+ |
+ get = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("get")).ToLocalChecked()); |
+ set = v8::Local<v8::Function>::Cast( |
+ env->Global()->Get(env.local(), v8_str("set")).ToLocalChecked()); |
+ desc = |
+ v8::PropertyDescriptor(get, false, set, true, false, false, false, true); |
+ CHECK(!desc.has_value()); |
+ CHECK(desc.get() == get); |
+ CHECK(!desc.has_get()); |
+ CHECK(desc.set() == set); |
+ CHECK(desc.has_set()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+ |
+ // accessor descriptor with empty function handle |
+ get = v8::Local<v8::Function>(); |
+ desc = v8::PropertyDescriptor(get, false); |
+ CHECK(!desc.has_value()); |
+ CHECK(desc.get() == get); |
+ CHECK(!desc.has_get()); |
+ CHECK(desc.set() == v8::Local<v8::Function>()); |
+ CHECK(!desc.has_set()); |
+ CHECK(!desc.enumerable()); |
+ CHECK(!desc.has_enumerable()); |
+ CHECK(!desc.configurable()); |
+ CHECK(!desc.has_configurable()); |
+ CHECK(!desc.writable()); |
+ CHECK(!desc.has_writable()); |
+} |
TEST(Promises) { |
LocalContext context; |