From 47104429415c4c580cea8da4a3e3e7bf32338775 Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Tue, 16 Jan 2018 14:00:27 -0500 Subject: [PATCH] Implement v8::Object::SetLazyDataProperty. It is analogous to Template::SetLazyDataProperty, but for a single existing object. Similar to how SetNativeDataProperty exists on both. Bug: v8:7303 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I634358ee455e28150198bd87a2bd79dc59e3e449 Reviewed-on: https://chromium-review.googlesource.com/867474 Reviewed-by: Toon Verwaest Commit-Queue: Jeremy Roman Cr-Commit-Position: refs/heads/master@{#50841} --- include/v8.h | 13 +++++++++++++ src/api.cc | 20 +++++++++++++++----- test/cctest/test-accessors.cc | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/include/v8.h b/include/v8.h index c0f9679c8b..75d15ce1ce 100644 --- a/include/v8.h +++ b/include/v8.h @@ -3198,6 +3198,19 @@ class V8_EXPORT Object : public Value { AccessorNameSetterCallback setter = nullptr, Local data = Local(), PropertyAttribute attributes = None); + /** + * Attempts to create a property with the given name which behaves like a data + * property, except that the provided getter is invoked (and provided with the + * data value) to supply its value the first time it is read. After the + * property is accessed once, it is replaced with an ordinary data property. + * + * Analogous to Template::SetLazyDataProperty. + */ + V8_WARN_UNUSED_RESULT Maybe SetLazyDataProperty( + Local context, Local name, + AccessorNameGetterCallback getter, Local data = Local(), + PropertyAttribute attributes = None); + /** * Functionality for private properties. * This is an experimental feature, use at your own risk. diff --git a/src/api.cc b/src/api.cc index 66f38f0e98..4cc96d6df8 100644 --- a/src/api.cc +++ b/src/api.cc @@ -4765,14 +4765,14 @@ Maybe v8::Object::Has(Local context, uint32_t index) { return maybe; } - template static Maybe ObjectSetAccessor(Local context, Object* self, Local name, Getter getter, Setter setter, Data data, AccessControl settings, PropertyAttribute attributes, - bool is_special_data_property) { + bool is_special_data_property, + bool replace_on_access) { auto isolate = reinterpret_cast(context->GetIsolate()); ENTER_V8_NO_SCRIPT(isolate, context, Object, SetAccessor, Nothing(), i::HandleScope); @@ -4782,7 +4782,7 @@ static Maybe ObjectSetAccessor(Local context, Object* self, v8::Local signature; i::Handle info = MakeAccessorInfo(isolate, name, getter, setter, data, settings, signature, - is_special_data_property, false); + is_special_data_property, replace_on_access); if (info.is_null()) return Nothing(); bool fast = obj->HasFastProperties(); i::Handle result; @@ -4808,7 +4808,7 @@ Maybe Object::SetAccessor(Local context, Local name, PropertyAttribute attribute) { return ObjectSetAccessor(context, this, name, getter, setter, data.FromMaybe(Local()), settings, attribute, - i::FLAG_disable_old_api_accessors); + i::FLAG_disable_old_api_accessors, false); } @@ -4838,7 +4838,17 @@ Maybe Object::SetNativeDataProperty(v8::Local context, v8::Local data, PropertyAttribute attributes) { return ObjectSetAccessor(context, this, name, getter, setter, data, DEFAULT, - attributes, true); + attributes, true, false); +} + +Maybe Object::SetLazyDataProperty(v8::Local context, + v8::Local name, + AccessorNameGetterCallback getter, + v8::Local data, + PropertyAttribute attributes) { + return ObjectSetAccessor(context, this, name, getter, + static_cast(nullptr), + data, DEFAULT, attributes, true, true); } Maybe v8::Object::HasOwnProperty(Local context, diff --git a/test/cctest/test-accessors.cc b/test/cctest/test-accessors.cc index 6a68ef1e38..b21c611a25 100644 --- a/test/cctest/test-accessors.cc +++ b/test/cctest/test-accessors.cc @@ -821,3 +821,38 @@ TEST(Regress609134) { "var a = 42;" "for (var i = 0; i<3; i++) { a.foo; }"); } + +TEST(ObjectSetLazyDataProperty) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope scope(isolate); + v8::Local obj = v8::Object::New(isolate); + CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); + + // Despite getting the property multiple times, the getter should only be + // called once and data property reads should continue to produce the same + // value. + static int getter_call_count; + getter_call_count = 0; + auto result = obj->SetLazyDataProperty( + env.local(), v8_str("foo"), + [](Local name, const v8::PropertyCallbackInfo& info) { + getter_call_count++; + info.GetReturnValue().Set(getter_call_count); + }); + CHECK(result.FromJust()); + CHECK_EQ(0, getter_call_count); + for (int i = 0; i < 2; i++) { + ExpectInt32("obj.foo", 1); + CHECK_EQ(1, getter_call_count); + } + + // Setting should overwrite the data property. + result = obj->SetLazyDataProperty( + env.local(), v8_str("bar"), + [](Local name, const v8::PropertyCallbackInfo& info) { + CHECK(false); + }); + CHECK(result.FromJust()); + ExpectInt32("obj.bar = -1; obj.bar;", -1); +}