| Index: test/cctest/test-api.cc | 
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc | 
| index 484d2f32264643d36cb29130e0e3259061185f06..6275fbf7eb27c9512a245bf9be57468997578381 100644 | 
| --- a/test/cctest/test-api.cc | 
| +++ b/test/cctest/test-api.cc | 
| @@ -16004,6 +16004,292 @@ 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()); | 
| + | 
| +  v8::PropertyDescriptor desc(v8_num(42)); | 
| +  { | 
| +    // Use a data descriptor. | 
| + | 
| +    // Cannot 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 a configurable property. | 
| +    p = v8_str("v2"); | 
| +    obj->DefineProperty(env.local(), p, desc).FromJust(); | 
| +    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()); | 
| + | 
| +    // Check that missing writable has default value false. | 
| +    p = v8_str("v12"); | 
| +    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()); | 
| +    v8::PropertyDescriptor desc2(v8_num(43)); | 
| +    CHECK(!obj->DefineProperty(env.local(), p, desc2).FromJust()); | 
| +    val = obj->Get(env.local(), p).ToLocalChecked(); | 
| +    CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| +  } | 
| + | 
| +  { | 
| +    // 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); | 
| +    // Use a writable descriptor, otherwise the next test, that changes | 
| +    // the array length will fail. | 
| +    v8::PropertyDescriptor desc(v8_num(42), true); | 
| +    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. | 
| +    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 undefined value. | 
| +    v8::PropertyDescriptor desc_empty(v8::Undefined(isolate)); | 
| + | 
| +    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), false); | 
| +    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. | 
| +    v8::PropertyDescriptor desc_empty(v8::Undefined(isolate), | 
| +                                      v8::Undefined(isolate)); | 
| + | 
| +    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()); | 
| +    v8::PropertyDescriptor desc(get, set); | 
| + | 
| +    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()); | 
| +  } | 
| + | 
| +  { | 
| +    // Redefine an existing property. | 
| + | 
| +    // desc = {value: 42, enumerable: true} | 
| +    v8::PropertyDescriptor desc(v8_num(42)); | 
| +    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} | 
| +    v8::PropertyDescriptor desc_true((v8::Local<v8::Value>())); | 
| +    desc_true.set_enumerable(true); | 
| + | 
| +    // Successful redefinition because all present attributes have the same | 
| +    // value as the current descriptor. | 
| +    CHECK(obj->DefineProperty(env.local(), p, desc_true).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| + | 
| +    // desc = {} | 
| +    v8::PropertyDescriptor desc_empty; | 
| +    // Successful redefinition because no attributes are overwritten in the | 
| +    // current descriptor. | 
| +    CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| + | 
| +    // desc = {enumerable: false} | 
| +    v8::PropertyDescriptor desc_false((v8::Local<v8::Value>())); | 
| +    desc_false.set_enumerable(false); | 
| +    // Not successful because we cannot define a different value for enumerable. | 
| +    CHECK(!obj->DefineProperty(env.local(), p, desc_false).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| +  } | 
| + | 
| +  { | 
| +    // Redefine 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() {}} | 
| +    v8::PropertyDescriptor desc(get, v8::Local<v8::Function>()); | 
| +    v8::TryCatch try_catch(isolate); | 
| + | 
| +    p = v8_str("v9"); | 
| +    CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| + | 
| +    // desc_empty = {} | 
| +    // Successful because we are not redefining the current getter. | 
| +    v8::PropertyDescriptor desc_empty; | 
| +    CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| + | 
| +    // desc = {get: function() {}} | 
| +    // Successful because we redefine the getter with its current value. | 
| +    CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| + | 
| +    // desc = {get: undefined} | 
| +    v8::PropertyDescriptor desc_undefined(v8::Undefined(isolate), | 
| +                                          v8::Local<v8::Function>()); | 
| +    // Not successful because we cannot redefine with the current value of get | 
| +    // with undefined. | 
| +    CHECK(!obj->DefineProperty(env.local(), p, desc_undefined).FromJust()); | 
| +    CHECK(!try_catch.HasCaught()); | 
| +  } | 
| + | 
| +  CompileRun("Object.freeze(a);"); | 
| +  { | 
| +    // We cannot 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 +23625,140 @@ 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.has_set()); | 
| +    CHECK(!desc.has_get()); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(!desc.has_configurable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // data descriptor | 
| +    v8::PropertyDescriptor desc(v8_num(42)); | 
| +    desc.set_enumerable(false); | 
| +    CHECK(desc.value() == v8_num(42)); | 
| +    CHECK(desc.has_value()); | 
| +    CHECK(!desc.has_set()); | 
| +    CHECK(!desc.has_get()); | 
| +    CHECK(desc.has_enumerable()); | 
| +    CHECK(!desc.enumerable()); | 
| +    CHECK(!desc.has_configurable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // data descriptor | 
| +    v8::PropertyDescriptor desc(v8_num(42)); | 
| +    desc.set_configurable(true); | 
| +    CHECK(desc.value() == v8_num(42)); | 
| +    CHECK(desc.has_value()); | 
| +    CHECK(!desc.has_set()); | 
| +    CHECK(!desc.has_get()); | 
| +    CHECK(desc.has_configurable()); | 
| +    CHECK(desc.configurable()); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // data descriptor | 
| +    v8::PropertyDescriptor desc(v8_num(42)); | 
| +    desc.set_configurable(false); | 
| +    CHECK(desc.value() == v8_num(42)); | 
| +    CHECK(desc.has_value()); | 
| +    CHECK(!desc.has_set()); | 
| +    CHECK(!desc.has_get()); | 
| +    CHECK(desc.has_configurable()); | 
| +    CHECK(!desc.configurable()); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // data descriptor | 
| +    v8::PropertyDescriptor desc(v8_num(42), false); | 
| +    CHECK(desc.value() == v8_num(42)); | 
| +    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()); | 
| +  } | 
| +  { | 
| +    // data descriptor | 
| +    v8::PropertyDescriptor desc(v8::Local<v8::Value>(), 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 | 
| +    CompileRun("var set = function() {return 43;};"); | 
| + | 
| +    v8::Local<v8::Function> set = | 
| +        v8::Local<v8::Function>::Cast(context->Global() | 
| +                                          ->Get(context.local(), v8_str("set")) | 
| +                                          .ToLocalChecked()); | 
| +    v8::PropertyDescriptor desc(v8::Undefined(isolate), set); | 
| +    desc.set_configurable(false); | 
| +    CHECK(!desc.has_value()); | 
| +    CHECK(desc.has_get()); | 
| +    CHECK(desc.get() == v8::Undefined(isolate)); | 
| +    CHECK(desc.has_set()); | 
| +    CHECK(desc.set() == set); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(desc.has_configurable()); | 
| +    CHECK(!desc.configurable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // accessor descriptor with Proxy | 
| +    CompileRun( | 
| +        "var set = new Proxy(function() {}, {});" | 
| +        "var get = undefined;"); | 
| + | 
| +    v8::Local<v8::Value> get = | 
| +        v8::Local<v8::Value>::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, set); | 
| +    desc.set_configurable(false); | 
| +    CHECK(!desc.has_value()); | 
| +    CHECK(desc.get() == v8::Undefined(isolate)); | 
| +    CHECK(desc.has_get()); | 
| +    CHECK(desc.set() == set); | 
| +    CHECK(desc.has_set()); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(desc.has_configurable()); | 
| +    CHECK(!desc.configurable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +  { | 
| +    // accessor descriptor with empty function handle | 
| +    v8::Local<v8::Function> get = v8::Local<v8::Function>(); | 
| +    v8::PropertyDescriptor desc(get, get); | 
| +    CHECK(!desc.has_value()); | 
| +    CHECK(!desc.has_get()); | 
| +    CHECK(!desc.has_set()); | 
| +    CHECK(!desc.has_enumerable()); | 
| +    CHECK(!desc.has_configurable()); | 
| +    CHECK(!desc.has_writable()); | 
| +  } | 
| +} | 
|  | 
| TEST(Promises) { | 
| LocalContext context; | 
|  |