Add a native data property that replaces itself with a real data property

This is useful for things that don't ever change, but we don't want to
eagerly compute the result.

Doing this from the embedder is difficult, using DefineOwnProperty would
read the property to get the property descriptor, creating an endless
recursion.

R=verwaest@chromium.org,haraken@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2449783006
Cr-Commit-Position: refs/heads/master@{#40648}
This commit is contained in:
jochen 2016-10-28 07:39:33 -07:00 committed by Commit bot
parent e12f9a113a
commit f074215082
6 changed files with 55 additions and 14 deletions

View File

@ -4685,6 +4685,14 @@ class V8_EXPORT Template : public Data {
Local<AccessorSignature> signature = Local<AccessorSignature>(),
AccessControl settings = DEFAULT);
/**
* Like SetNativeDataProperty, but V8 will replace the native data property
* with a real data property on first access.
*/
void SetLazyDataProperty(Local<Name> name, AccessorNameGetterCallback getter,
Local<Value> data = Local<Value>(),
PropertyAttribute attribute = None);
/**
* During template instantiation, sets the value with the intrinsic property
* from the correct context.

View File

@ -29,6 +29,7 @@ Handle<AccessorInfo> Accessors::MakeAccessor(
info->set_all_can_write(false);
info->set_is_special_data_property(true);
info->set_is_sloppy(false);
info->set_replace_on_access(false);
name = factory->InternalizeName(name);
info->set_name(*name);
Handle<Object> get = v8::FromCData(isolate, getter);

View File

@ -1297,10 +1297,13 @@ template <typename Getter, typename Setter>
i::Handle<i::AccessorInfo> MakeAccessorInfo(
v8::Local<Name> name, Getter getter, Setter setter, v8::Local<Value> data,
v8::AccessControl settings, v8::PropertyAttribute attributes,
v8::Local<AccessorSignature> signature, bool is_special_data_property) {
v8::Local<AccessorSignature> signature, bool is_special_data_property,
bool replace_on_access) {
i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate();
i::Handle<i::AccessorInfo> obj = isolate->factory()->NewAccessorInfo();
SET_FIELD_WRAPPED(obj, set_getter, getter);
DCHECK_IMPLIES(replace_on_access,
is_special_data_property && setter == nullptr);
if (is_special_data_property && setter == nullptr) {
setter = reinterpret_cast<Setter>(&i::Accessors::ReconfigureToDataProperty);
}
@ -1312,6 +1315,7 @@ i::Handle<i::AccessorInfo> MakeAccessorInfo(
}
obj->set_data(*Utils::OpenHandle(*data));
obj->set_is_special_data_property(is_special_data_property);
obj->set_replace_on_access(replace_on_access);
return SetAccessorInfoProperties(obj, name, settings, attributes, signature);
}
@ -1463,20 +1467,21 @@ static i::Handle<i::FunctionTemplateInfo> EnsureConstructor(
return constructor;
}
template <typename Getter, typename Setter, typename Data, typename Template>
static bool TemplateSetAccessor(Template* template_obj, v8::Local<Name> name,
Getter getter, Setter setter, Data data,
AccessControl settings,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature,
bool is_special_data_property) {
bool is_special_data_property,
bool replace_on_access) {
auto info = Utils::OpenHandle(template_obj);
auto isolate = info->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
auto obj = MakeAccessorInfo(name, getter, setter, data, settings, attribute,
signature, is_special_data_property);
auto obj =
MakeAccessorInfo(name, getter, setter, data, settings, attribute,
signature, is_special_data_property, replace_on_access);
if (obj.is_null()) return false;
i::ApiNatives::AddNativeDataProperty(isolate, info, obj);
return true;
@ -1491,7 +1496,7 @@ void Template::SetNativeDataProperty(v8::Local<String> name,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, true);
signature, true, false);
}
@ -1503,9 +1508,17 @@ void Template::SetNativeDataProperty(v8::Local<Name> name,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, true);
signature, true, false);
}
void Template::SetLazyDataProperty(v8::Local<Name> name,
AccessorNameGetterCallback getter,
v8::Local<Value> data,
PropertyAttribute attribute) {
TemplateSetAccessor(
this, name, getter, static_cast<AccessorNameSetterCallback>(nullptr),
data, DEFAULT, attribute, Local<AccessorSignature>(), true, true);
}
void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
PropertyAttribute attribute) {
@ -1526,7 +1539,7 @@ void ObjectTemplate::SetAccessor(v8::Local<String> name,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, i::FLAG_disable_old_api_accessors);
signature, i::FLAG_disable_old_api_accessors, false);
}
@ -1537,7 +1550,7 @@ void ObjectTemplate::SetAccessor(v8::Local<Name> name,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, i::FLAG_disable_old_api_accessors);
signature, i::FLAG_disable_old_api_accessors, false);
}
template <typename Getter, typename Setter, typename Query, typename Descriptor,
@ -4488,8 +4501,9 @@ static Maybe<bool> ObjectSetAccessor(Local<Context> context, Object* self,
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(self));
v8::Local<AccessorSignature> signature;
auto info = MakeAccessorInfo(name, getter, setter, data, settings, attributes,
signature, i::FLAG_disable_old_api_accessors);
auto info =
MakeAccessorInfo(name, getter, setter, data, settings, attributes,
signature, i::FLAG_disable_old_api_accessors, false);
if (info.is_null()) return Nothing<bool>();
bool fast = obj->HasFastProperties();
i::Handle<i::Object> result;

View File

@ -7724,6 +7724,14 @@ void AccessorInfo::set_is_special_data_property(bool value) {
set_flag(BooleanBit::set(flag(), kSpecialDataProperty, value));
}
bool AccessorInfo::replace_on_access() {
return BooleanBit::get(flag(), kReplaceOnAccess);
}
void AccessorInfo::set_replace_on_access(bool value) {
set_flag(BooleanBit::set(flag(), kReplaceOnAccess, value));
}
bool AccessorInfo::is_sloppy() { return BooleanBit::get(flag(), kIsSloppy); }
void AccessorInfo::set_is_sloppy(bool value) {

View File

@ -1352,8 +1352,14 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) {
Handle<Object> result = args.Call(call_fun, name);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (result.is_null()) return isolate->factory()->undefined_value();
// Rebox handle before return.
return handle(*result, isolate);
Handle<Object> reboxed_result = handle(*result, isolate);
if (info->replace_on_access() && receiver->IsJSReceiver()) {
args.Call(reinterpret_cast<GenericNamedPropertySetterCallback>(
&Accessors::ReconfigureToDataProperty),
name, result);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
}
return reboxed_result;
}
// Regular accessor.

View File

@ -11217,6 +11217,9 @@ class AccessorInfo: public Struct {
inline bool is_special_data_property();
inline void set_is_special_data_property(bool value);
inline bool replace_on_access();
inline void set_replace_on_access(bool value);
inline bool is_sloppy();
inline void set_is_sloppy(bool value);
@ -11258,7 +11261,8 @@ class AccessorInfo: public Struct {
static const int kAllCanWriteBit = 1;
static const int kSpecialDataProperty = 2;
static const int kIsSloppy = 3;
class AttributesField : public BitField<PropertyAttributes, 4, 3> {};
static const int kReplaceOnAccess = 4;
class AttributesField : public BitField<PropertyAttributes, 5, 3> {};
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessorInfo);
};