Commit 9b3dafb9 authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

[debug] expose SideEffectType when setting template accessors

This expands the SideEffectType flag to cover whitelisting embedder
callbacks that are setup with Template accessors.

- v8::ObjectTemplate::SetNativeDataProperty
- v8::ObjectTemplate::SetLazyDataProperty
- v8::ObjectTemplate::SetAccessor

Bug: v8:7515
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: Ieda6c793141ab249c4f41d00e6572fe2a29ac629
Reviewed-on: https://chromium-review.googlesource.com/1015896Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Erik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52770}
parent 7bfed2ad
...@@ -5117,22 +5117,25 @@ class V8_EXPORT Template : public Data { ...@@ -5117,22 +5117,25 @@ class V8_EXPORT Template : public Data {
// TODO(dcarney): gcc can't handle Local below // TODO(dcarney): gcc can't handle Local below
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None, Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
Local<AccessorSignature> signature = Local<AccessorSignature>(), Local<AccessorSignature> signature = Local<AccessorSignature>(),
AccessControl settings = DEFAULT); AccessControl settings = DEFAULT,
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
void SetNativeDataProperty( void SetNativeDataProperty(
Local<Name> name, AccessorNameGetterCallback getter, Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0, AccessorNameSetterCallback setter = 0,
// TODO(dcarney): gcc can't handle Local below // TODO(dcarney): gcc can't handle Local below
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None, Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
Local<AccessorSignature> signature = Local<AccessorSignature>(), Local<AccessorSignature> signature = Local<AccessorSignature>(),
AccessControl settings = DEFAULT); AccessControl settings = DEFAULT,
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
/** /**
* Like SetNativeDataProperty, but V8 will replace the native data property * Like SetNativeDataProperty, but V8 will replace the native data property
* with a real data property on first access. * with a real data property on first access.
*/ */
void SetLazyDataProperty(Local<Name> name, AccessorNameGetterCallback getter, void SetLazyDataProperty(
Local<Value> data = Local<Value>(), Local<Name> name, AccessorNameGetterCallback getter,
PropertyAttribute attribute = None); Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
/** /**
* During template instantiation, sets the value with the intrinsic property * During template instantiation, sets the value with the intrinsic property
...@@ -5856,12 +5859,14 @@ class V8_EXPORT ObjectTemplate : public Template { ...@@ -5856,12 +5859,14 @@ class V8_EXPORT ObjectTemplate : public Template {
Local<String> name, AccessorGetterCallback getter, Local<String> name, AccessorGetterCallback getter,
AccessorSetterCallback setter = 0, Local<Value> data = Local<Value>(), AccessorSetterCallback setter = 0, Local<Value> data = Local<Value>(),
AccessControl settings = DEFAULT, PropertyAttribute attribute = None, AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
Local<AccessorSignature> signature = Local<AccessorSignature>()); Local<AccessorSignature> signature = Local<AccessorSignature>(),
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
void SetAccessor( void SetAccessor(
Local<Name> name, AccessorNameGetterCallback getter, Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0, Local<Value> data = Local<Value>(), AccessorNameSetterCallback setter = 0, Local<Value> data = Local<Value>(),
AccessControl settings = DEFAULT, PropertyAttribute attribute = None, AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
Local<AccessorSignature> signature = Local<AccessorSignature>()); Local<AccessorSignature> signature = Local<AccessorSignature>(),
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
/** /**
* Sets a named property handler on the object template. * Sets a named property handler on the object template.
......
...@@ -76,12 +76,12 @@ bool Accessors::IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name, ...@@ -76,12 +76,12 @@ bool Accessors::IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name,
} }
} }
V8_WARN_UNUSED_RESULT MaybeHandle<Object>
namespace { Accessors::ReplaceAccessorWithDataProperty(Isolate* isolate,
Handle<Object> receiver,
V8_WARN_UNUSED_RESULT MaybeHandle<Object> ReplaceAccessorWithDataProperty( Handle<JSObject> holder,
Isolate* isolate, Handle<Object> receiver, Handle<JSObject> holder, Handle<Name> name,
Handle<Name> name, Handle<Object> value) { Handle<Object> value) {
LookupIterator it(receiver, name, holder, LookupIterator it(receiver, name, holder,
LookupIterator::OWN_SKIP_INTERCEPTOR); LookupIterator::OWN_SKIP_INTERCEPTOR);
// Skip any access checks we might hit. This accessor should never hit in a // Skip any access checks we might hit. This accessor should never hit in a
...@@ -96,7 +96,6 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> ReplaceAccessorWithDataProperty( ...@@ -96,7 +96,6 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> ReplaceAccessorWithDataProperty(
return value; return value;
} }
} // namespace
// //
// Accessors::ReconfigureToDataProperty // Accessors::ReconfigureToDataProperty
...@@ -113,8 +112,8 @@ void Accessors::ReconfigureToDataProperty( ...@@ -113,8 +112,8 @@ void Accessors::ReconfigureToDataProperty(
Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder())); Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
Handle<Name> name = Utils::OpenHandle(*key); Handle<Name> name = Utils::OpenHandle(*key);
Handle<Object> value = Utils::OpenHandle(*val); Handle<Object> value = Utils::OpenHandle(*val);
MaybeHandle<Object> result = MaybeHandle<Object> result = Accessors::ReplaceAccessorWithDataProperty(
ReplaceAccessorWithDataProperty(isolate, receiver, holder, name, value); isolate, receiver, holder, name, value);
if (result.is_null()) { if (result.is_null()) {
isolate->OptionalRescheduleException(false); isolate->OptionalRescheduleException(false);
} else { } else {
...@@ -122,17 +121,6 @@ void Accessors::ReconfigureToDataProperty( ...@@ -122,17 +121,6 @@ void Accessors::ReconfigureToDataProperty(
} }
} }
void Accessors::ReconfigureToDataPropertyGetter(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
UNREACHABLE();
}
Handle<AccessorInfo> Accessors::MakeReconfigureToDataPropertyInfo(
Isolate* isolate) {
Handle<Name> name = isolate->factory()->ReconfigureToDataProperty_string();
return MakeAccessor(isolate, name, &ReconfigureToDataPropertyGetter,
&ReconfigureToDataProperty);
}
// //
// Accessors::ArgumentsIterator // Accessors::ArgumentsIterator
...@@ -1194,8 +1182,8 @@ void Accessors::ErrorStackGetter( ...@@ -1194,8 +1182,8 @@ void Accessors::ErrorStackGetter(
Utils::OpenHandle(*v8::Local<v8::Value>(info.This())); Utils::OpenHandle(*v8::Local<v8::Value>(info.This()));
Handle<Name> name = Utils::OpenHandle(*key); Handle<Name> name = Utils::OpenHandle(*key);
if (IsAccessor(receiver, name, holder)) { if (IsAccessor(receiver, name, holder)) {
result = ReplaceAccessorWithDataProperty(isolate, receiver, holder, name, result = Accessors::ReplaceAccessorWithDataProperty(
formatted_stack_trace); isolate, receiver, holder, name, formatted_stack_trace);
if (result.is_null()) { if (result.is_null()) {
isolate->OptionalRescheduleException(false); isolate->OptionalRescheduleException(false);
return; return;
......
...@@ -33,7 +33,6 @@ class JavaScriptFrame; ...@@ -33,7 +33,6 @@ class JavaScriptFrame;
V(function_name, FunctionName) \ V(function_name, FunctionName) \
V(function_length, FunctionLength) \ V(function_length, FunctionLength) \
V(function_prototype, FunctionPrototype) \ V(function_prototype, FunctionPrototype) \
V(reconfigure_to_data_property, ReconfigureToDataProperty) \
V(script_column_offset, ScriptColumnOffset) \ V(script_column_offset, ScriptColumnOffset) \
V(script_compilation_type, ScriptCompilationType) \ V(script_compilation_type, ScriptCompilationType) \
V(script_context_data, ScriptContextData) \ V(script_context_data, ScriptContextData) \
...@@ -110,6 +109,10 @@ class Accessors : public AllStatic { ...@@ -110,6 +109,10 @@ class Accessors : public AllStatic {
static bool IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name, static bool IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name,
FieldIndex* field_index); FieldIndex* field_index);
static MaybeHandle<Object> ReplaceAccessorWithDataProperty(
Isolate* isolate, Handle<Object> receiver, Handle<JSObject> holder,
Handle<Name> name, Handle<Object> value);
// Create an AccessorInfo. The setter is optional (can be nullptr). // Create an AccessorInfo. The setter is optional (can be nullptr).
// //
// Note that the type of setter is AccessorNameBooleanSetterCallback instead // Note that the type of setter is AccessorNameBooleanSetterCallback instead
......
...@@ -1706,13 +1706,11 @@ static i::Handle<i::FunctionTemplateInfo> EnsureConstructor( ...@@ -1706,13 +1706,11 @@ static i::Handle<i::FunctionTemplateInfo> EnsureConstructor(
} }
template <typename Getter, typename Setter, typename Data, typename Template> template <typename Getter, typename Setter, typename Data, typename Template>
static void TemplateSetAccessor(Template* template_obj, v8::Local<Name> name, static void TemplateSetAccessor(
Getter getter, Setter setter, Data data, Template* template_obj, v8::Local<Name> name, Getter getter, Setter setter,
AccessControl settings, Data data, AccessControl settings, PropertyAttribute attribute,
PropertyAttribute attribute, v8::Local<AccessorSignature> signature, bool is_special_data_property,
v8::Local<AccessorSignature> signature, bool replace_on_access, SideEffectType getter_side_effect_type) {
bool is_special_data_property,
bool replace_on_access) {
auto info = Utils::OpenHandle(template_obj); auto info = Utils::OpenHandle(template_obj);
auto isolate = info->GetIsolate(); auto isolate = info->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
...@@ -1722,40 +1720,38 @@ static void TemplateSetAccessor(Template* template_obj, v8::Local<Name> name, ...@@ -1722,40 +1720,38 @@ static void TemplateSetAccessor(Template* template_obj, v8::Local<Name> name,
is_special_data_property, replace_on_access); is_special_data_property, replace_on_access);
accessor_info->set_initial_property_attributes( accessor_info->set_initial_property_attributes(
static_cast<i::PropertyAttributes>(attribute)); static_cast<i::PropertyAttributes>(attribute));
accessor_info->set_has_no_side_effect(getter_side_effect_type ==
SideEffectType::kHasNoSideEffect);
i::ApiNatives::AddNativeDataProperty(isolate, info, accessor_info); i::ApiNatives::AddNativeDataProperty(isolate, info, accessor_info);
} }
void Template::SetNativeDataProperty(
void Template::SetNativeDataProperty(v8::Local<String> name, v8::Local<String> name, AccessorGetterCallback getter,
AccessorGetterCallback getter, AccessorSetterCallback setter, v8::Local<Value> data,
AccessorSetterCallback setter, PropertyAttribute attribute, v8::Local<AccessorSignature> signature,
v8::Local<Value> data, AccessControl settings, SideEffectType getter_side_effect_type) {
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, true, false); signature, true, false, getter_side_effect_type);
} }
void Template::SetNativeDataProperty(
void Template::SetNativeDataProperty(v8::Local<Name> name, v8::Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameGetterCallback getter, AccessorNameSetterCallback setter, v8::Local<Value> data,
AccessorNameSetterCallback setter, PropertyAttribute attribute, v8::Local<AccessorSignature> signature,
v8::Local<Value> data, AccessControl settings, SideEffectType getter_side_effect_type) {
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, true, false); signature, true, false, getter_side_effect_type);
} }
void Template::SetLazyDataProperty(v8::Local<Name> name, void Template::SetLazyDataProperty(v8::Local<Name> name,
AccessorNameGetterCallback getter, AccessorNameGetterCallback getter,
v8::Local<Value> data, v8::Local<Value> data,
PropertyAttribute attribute) { PropertyAttribute attribute,
TemplateSetAccessor( SideEffectType getter_side_effect_type) {
this, name, getter, static_cast<AccessorNameSetterCallback>(nullptr), TemplateSetAccessor(this, name, getter,
data, DEFAULT, attribute, Local<AccessorSignature>(), true, true); static_cast<AccessorNameSetterCallback>(nullptr), data,
DEFAULT, attribute, Local<AccessorSignature>(), true,
true, getter_side_effect_type);
} }
void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic, void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
...@@ -1769,26 +1765,28 @@ void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic, ...@@ -1769,26 +1765,28 @@ void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
static_cast<i::PropertyAttributes>(attribute)); static_cast<i::PropertyAttributes>(attribute));
} }
void ObjectTemplate::SetAccessor(v8::Local<String> name, void ObjectTemplate::SetAccessor(v8::Local<String> name,
AccessorGetterCallback getter, AccessorGetterCallback getter,
AccessorSetterCallback setter, AccessorSetterCallback setter,
v8::Local<Value> data, AccessControl settings, v8::Local<Value> data, AccessControl settings,
PropertyAttribute attribute, PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) { v8::Local<AccessorSignature> signature,
SideEffectType getter_side_effect_type) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, i::FLAG_disable_old_api_accessors, false); signature, i::FLAG_disable_old_api_accessors, false,
getter_side_effect_type);
} }
void ObjectTemplate::SetAccessor(v8::Local<Name> name, void ObjectTemplate::SetAccessor(v8::Local<Name> name,
AccessorNameGetterCallback getter, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter, AccessorNameSetterCallback setter,
v8::Local<Value> data, AccessControl settings, v8::Local<Value> data, AccessControl settings,
PropertyAttribute attribute, PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) { v8::Local<AccessorSignature> signature,
SideEffectType getter_side_effect_type) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, i::FLAG_disable_old_api_accessors, false); signature, i::FLAG_disable_old_api_accessors, false,
getter_side_effect_type);
} }
template <typename Getter, typename Setter, typename Query, typename Descriptor, template <typename Getter, typename Setter, typename Query, typename Descriptor,
......
...@@ -1509,10 +1509,10 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { ...@@ -1509,10 +1509,10 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) {
if (result.is_null()) return isolate->factory()->undefined_value(); if (result.is_null()) return isolate->factory()->undefined_value();
Handle<Object> reboxed_result = handle(*result, isolate); Handle<Object> reboxed_result = handle(*result, isolate);
if (info->replace_on_access() && receiver->IsJSReceiver()) { if (info->replace_on_access() && receiver->IsJSReceiver()) {
args.CallAccessorSetter( RETURN_ON_EXCEPTION(isolate,
isolate->factory()->reconfigure_to_data_property_accessor(), name, Accessors::ReplaceAccessorWithDataProperty(
result); isolate, receiver, holder, name, result),
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); Object);
} }
return reboxed_result; return reboxed_result;
} }
......
...@@ -231,12 +231,18 @@ TEST(CachedAccessorOnGlobalObject) { ...@@ -231,12 +231,18 @@ TEST(CachedAccessorOnGlobalObject) {
namespace { namespace {
// Getter return value should be non-null to trigger lazy property paths.
static void Getter(v8::Local<v8::Name> name, static void Getter(v8::Local<v8::Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_str("return value"));
}
static void StringGetter(v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {} const v8::PropertyCallbackInfo<v8::Value>& info) {}
static void Setter(v8::Local<v8::String> name, v8::Local<v8::Value> value, static void Setter(v8::Local<v8::String> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {} const v8::PropertyCallbackInfo<void>& info) {}
} } // namespace
// Re-declaration of non-configurable accessors should throw. // Re-declaration of non-configurable accessors should throw.
TEST(RedeclareAccessor) { TEST(RedeclareAccessor) {
...@@ -352,3 +358,86 @@ TEST(AccessorSetLazyDataPropertyHasNoSideEffect) { ...@@ -352,3 +358,86 @@ TEST(AccessorSetLazyDataPropertyHasNoSideEffect) {
->Int32Value(env.local()) ->Int32Value(env.local())
.FromJust()); .FromJust());
} }
TEST(ObjectTemplateSetAccessorHasNoSideEffect) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("foo"), StringGetter);
templ->SetAccessor(v8_str("foo2"), StringGetter, 0, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::Local<v8::AccessorSignature>(),
v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), true).ToLocalChecked();
// Check that setter is not whitelisted.
v8::TryCatch try_catch(isolate);
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2 = 1"), true)
.IsEmpty());
CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), false)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
}
TEST(ObjectTemplateSetNativePropertyHasNoSideEffect) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("foo"), Getter);
templ->SetNativeDataProperty(
v8_str("foo2"), Getter, 0, v8::Local<v8::Value>(),
v8::PropertyAttribute::None, v8::Local<v8::AccessorSignature>(),
v8::AccessControl::DEFAULT, v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), true).ToLocalChecked();
// Check that setter is not whitelisted.
v8::TryCatch try_catch(isolate);
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2 = 1"), true)
.IsEmpty());
CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), false)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
}
TEST(ObjectTemplateSetLazyPropertyHasNoSideEffect) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetLazyDataProperty(v8_str("foo"), Getter);
templ->SetLazyDataProperty(v8_str("foo2"), Getter, v8::Local<v8::Value>(),
v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), true).ToLocalChecked();
// Check that setter is not whitelisted.
v8::TryCatch try_catch(isolate);
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2 = 1"), true)
.IsEmpty());
CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), false)
.ToLocalChecked()
->Int32Value(env.local())
.FromJust());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment