 Chromium Code Reviews
 Chromium Code Reviews Issue 2244123005:
  [api] Add PropertyDescriptor and DefineProperty().  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master
    
  
    Issue 2244123005:
  [api] Add PropertyDescriptor and DefineProperty().  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master| Index: test/cctest/test-api.cc | 
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc | 
| index 484d2f32264643d36cb29130e0e3259061185f06..b036946180dbb692b6f48def68bae81181b1ee55 100644 | 
| --- a/test/cctest/test-api.cc | 
| +++ b/test/cctest/test-api.cc | 
| @@ -16004,6 +16004,284 @@ TEST(DefineOwnProperty) { | 
| } | 
| } | 
| +TEST(DefineProperty) { | 
| + LocalContext env; | 
| + v8::Isolate* isolate = env->GetIsolate(); | 
| + v8::HandleScope handle_scope(isolate); | 
| + | 
| + v8::Local<v8::Name> p; | 
| + | 
| + CompileRun( | 
| + "var a = {};" | 
| + "var b = [];" | 
| + "Object.defineProperty(a, 'v1', {value: 23});" | 
| + "Object.defineProperty(a, 'v2', {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_num(42), true, false, false); | 
| + { | 
| + // Use a data descriptor. | 
| + | 
| + // Can't change a non-configurable property. | 
| + p = v8_str("v1"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(!obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsNumber()); | 
| + CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); | 
| + | 
| + // Change configurable property | 
| + p = v8_str("v2"); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsNumber()); | 
| + CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); | 
| + } | 
| + | 
| + { | 
| + // Set a regular property. | 
| + p = v8_str("v3"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).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_generic; | 
| + | 
| + p = v8_str("v4"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_generic).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsUndefined()); | 
| + | 
| + obj->Set(env.local(), p, v8_num(1)).FromJust(); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsUndefined()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + } | 
| + | 
| + { | 
| + // Use a data descriptor with empty handle should return Nothing. | 
| + const v8::PropertyDescriptor desc_empty(v8::Local<v8::Value>(), true, false, | 
| + false); | 
| + | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_empty).IsNothing()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + } | 
| + | 
| + { | 
| + // Use a data descriptor with undefined value. | 
| + const v8::PropertyDescriptor desc_empty(v8::Undefined(isolate), true, false, | 
| + false); | 
| + | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsUndefined()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + } | 
| + | 
| + { | 
| + // Use a descriptor with attribute == v8::ReadOnly. | 
| + v8::PropertyDescriptor desc_read_only(v8_num(42), true, false, true); | 
| + desc_read_only.set_enumerable(true); | 
| + desc_read_only.set_configurable(true); | 
| + | 
| + p = v8_str("v5"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_read_only).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsNumber()); | 
| + CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); | 
| + CHECK_EQ(v8::ReadOnly, | 
| + obj->GetPropertyAttributes(env.local(), p).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + } | 
| + | 
| + { | 
| + // Use an accessor descriptor with empty handles. | 
| + const v8::PropertyDescriptor desc_empty(v8::Local<v8::Function>(), true, | 
| + v8::Local<v8::Function>(), true); | 
| + | 
| + p = v8_str("v6"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).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(get, true, set, true); | 
| + | 
| + p = v8_str("v7"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); | 
| + CHECK(val->IsNumber()); | 
| + CHECK_EQ(0.0, val->NumberValue(env.local()).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + obj->Set(env.local(), p, v8_num(7)).FromJust(); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + val = obj->Get(env.local(), p).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} | 
| + v8::PropertyDescriptor desc(v8_num(42), true, false, false); | 
| + desc.set_enumerable(true); | 
| + | 
| + p = v8_str("v8"); | 
| + v8::TryCatch try_catch(isolate); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {enumerable: true} | 
| + // successful because has_enumerable with same value as existing descriptor | 
| + v8::PropertyDescriptor desc_true(v8::Local<v8::Value>(), false, false, | 
| + false); | 
| + desc_true.set_enumerable(true); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_true).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {} | 
| + // successful because !has_enumerable | 
| + const v8::PropertyDescriptor desc_empty; | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {enumerable: false} | 
| + v8::PropertyDescriptor desc_false(v8::Local<v8::Value>(), false, false, | 
| + false); | 
| + desc_false.set_enumerable(false); | 
| + // not successful, because we cannot overwrite enumerable | 
| + CHECK(!obj->DefineProperty(env.local(), p, desc_false).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + } | 
| + | 
| + { | 
| + // Re-define a property that has a getter | 
| + CompileRun("var get = function() {};"); | 
| + v8::Local<v8::Function> get = v8::Local<v8::Function>::Cast( | 
| + env->Global()->Get(env.local(), v8_str("get")).ToLocalChecked()); | 
| + | 
| + // desc = {get: function() {}} | 
| + const v8::PropertyDescriptor desc(get, true, v8::Local<v8::Function>(), | 
| + false); | 
| + v8::TryCatch try_catch(isolate); | 
| + | 
| + p = v8_str("v9"); | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {} | 
| + // successful because get not redefined | 
| + const v8::PropertyDescriptor desc_empty; | 
| + CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {get: function() {}} | 
| + // successful because re-define with same value | 
| 
Jakob Kummerow
2016/08/26 13:28:30
nit: here and other comments: capitalization, gram
 | 
| + CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| + CHECK(!try_catch.HasCaught()); | 
| + | 
| + // desc = {get: undefined} | 
| + const v8::PropertyDescriptor desc_undefined( | 
| + v8::Local<v8::Function>(), true, v8::Local<v8::Function>(), false); | 
| + // not successful, because we cannot overwrite with undefined | 
| + CHECK(!obj->DefineProperty(env.local(), p, desc_undefined).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("v10"), 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("v11"), desc) | 
| + .IsNothing()); | 
| + CHECK(try_catch.HasCaught()); | 
| + } | 
| +} | 
| THREADED_TEST(GetCurrentContextWhenNotInContext) { | 
| i::Isolate* isolate = CcTest::i_isolate(); | 
| @@ -23339,6 +23617,211 @@ 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()); | 
| 
Jakob Kummerow
2016/08/26 13:28:30
This seems rather pointless. Given that !desc.has_
 | 
| + CHECK(!desc.has_enumerable()); | 
| + CHECK(!desc.configurable()); | 
| + CHECK(!desc.has_configurable()); | 
| + CHECK(!desc.writable()); | 
| + CHECK(!desc.has_writable()); | 
| + } | 
| + | 
| + { // data descriptor | 
| + v8::PropertyDescriptor desc(v8_num(42), true, false, false); | 
| + 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 | 
| + v8::PropertyDescriptor desc(v8_num(42), false, false, false); | 
| + desc.set_enumerable(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 | 
| + v8::PropertyDescriptor desc(v8_num(42), false, false, false); | 
| + desc.set_enumerable(false); | 
| + 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 | 
| + v8::PropertyDescriptor desc(v8_num(42), false, false, false); | 
| + desc.set_configurable(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 | 
| + v8::PropertyDescriptor desc(v8_num(42), false, false, false); | 
| + desc.set_configurable(false); | 
| + 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 | 
| + v8::PropertyDescriptor desc(v8_num(42), false, true, false); | 
| + 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 | 
| + v8::PropertyDescriptor desc(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 | 
| + v8::PropertyDescriptor desc(v8::Local<v8::Value>(), false, false, true); | 
| + CHECK(desc.value() == v8::Local<v8::Value>()); | 
| + CHECK(!desc.has_value()); | 
| + CHECK(!desc.has_set()); | 
| + CHECK(!desc.has_get()); | 
| + CHECK(!desc.has_enumerable()); | 
| + CHECK(!desc.has_configurable()); | 
| + CHECK(!desc.writable()); | 
| + CHECK(desc.has_writable()); | 
| + } | 
| + { | 
| + // accessor descriptor | 
| + CompileRun( | 
| + "var set = function() {return 43;};" | 
| + "var get = function() {return 42;};"); | 
| + | 
| + v8::Local<v8::Function> get = | 
| + v8::Local<v8::Function>::Cast(context->Global() | 
| + ->Get(context.local(), v8_str("get")) | 
| + .ToLocalChecked()); | 
| + v8::Local<v8::Function> set = | 
| + v8::Local<v8::Function>::Cast(context->Global() | 
| + ->Get(context.local(), v8_str("set")) | 
| + .ToLocalChecked()); | 
| + v8::PropertyDescriptor desc(get, false, set, true); | 
| + desc.set_configurable(false); | 
| + 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;};"); | 
| + | 
| + v8::Local<v8::Function> get = | 
| + v8::Local<v8::Function>::Cast(context->Global() | 
| + ->Get(context.local(), v8_str("get")) | 
| + .ToLocalChecked()); | 
| + v8::Local<v8::Function> set = | 
| + v8::Local<v8::Function>::Cast(context->Global() | 
| + ->Get(context.local(), v8_str("set")) | 
| + .ToLocalChecked()); | 
| + v8::PropertyDescriptor desc(get, false, set, true); | 
| + desc.set_configurable(false); | 
| + 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 | 
| + v8::Local<v8::Function> get = v8::Local<v8::Function>(); | 
| + v8::PropertyDescriptor desc(get, false, v8::Local<v8::Function>(), 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; |