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 {
// TODO(dcarney): gcc can't handle Local below
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
Local<AccessorSignature> signature = Local<AccessorSignature>(),
AccessControl settings = DEFAULT);
AccessControl settings = DEFAULT,
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
void SetNativeDataProperty(
Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0,
// TODO(dcarney): gcc can't handle Local below
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
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
* with a real data property on first access.
*/
void SetLazyDataProperty(Local<Name> name, AccessorNameGetterCallback getter,
Local<Value> data = Local<Value>(),
PropertyAttribute attribute = None);
void SetLazyDataProperty(
Local<Name> name, AccessorNameGetterCallback getter,
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
......@@ -5856,12 +5859,14 @@ class V8_EXPORT ObjectTemplate : public Template {
Local<String> name, AccessorGetterCallback getter,
AccessorSetterCallback setter = 0, Local<Value> data = Local<Value>(),
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(
Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0, Local<Value> data = Local<Value>(),
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.
......
......@@ -76,12 +76,12 @@ bool Accessors::IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name,
}
}
namespace {
V8_WARN_UNUSED_RESULT MaybeHandle<Object> ReplaceAccessorWithDataProperty(
Isolate* isolate, Handle<Object> receiver, Handle<JSObject> holder,
Handle<Name> name, Handle<Object> value) {
V8_WARN_UNUSED_RESULT MaybeHandle<Object>
Accessors::ReplaceAccessorWithDataProperty(Isolate* isolate,
Handle<Object> receiver,
Handle<JSObject> holder,
Handle<Name> name,
Handle<Object> value) {
LookupIterator it(receiver, name, holder,
LookupIterator::OWN_SKIP_INTERCEPTOR);
// 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(
return value;
}
} // namespace
//
// Accessors::ReconfigureToDataProperty
......@@ -113,8 +112,8 @@ void Accessors::ReconfigureToDataProperty(
Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
Handle<Name> name = Utils::OpenHandle(*key);
Handle<Object> value = Utils::OpenHandle(*val);
MaybeHandle<Object> result =
ReplaceAccessorWithDataProperty(isolate, receiver, holder, name, value);
MaybeHandle<Object> result = Accessors::ReplaceAccessorWithDataProperty(
isolate, receiver, holder, name, value);
if (result.is_null()) {
isolate->OptionalRescheduleException(false);
} else {
......@@ -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
......@@ -1194,8 +1182,8 @@ void Accessors::ErrorStackGetter(
Utils::OpenHandle(*v8::Local<v8::Value>(info.This()));
Handle<Name> name = Utils::OpenHandle(*key);
if (IsAccessor(receiver, name, holder)) {
result = ReplaceAccessorWithDataProperty(isolate, receiver, holder, name,
formatted_stack_trace);
result = Accessors::ReplaceAccessorWithDataProperty(
isolate, receiver, holder, name, formatted_stack_trace);
if (result.is_null()) {
isolate->OptionalRescheduleException(false);
return;
......
......@@ -33,7 +33,6 @@ class JavaScriptFrame;
V(function_name, FunctionName) \
V(function_length, FunctionLength) \
V(function_prototype, FunctionPrototype) \
V(reconfigure_to_data_property, ReconfigureToDataProperty) \
V(script_column_offset, ScriptColumnOffset) \
V(script_compilation_type, ScriptCompilationType) \
V(script_context_data, ScriptContextData) \
......@@ -110,6 +109,10 @@ class Accessors : public AllStatic {
static bool IsJSObjectFieldAccessor(Handle<Map> map, Handle<Name> name,
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).
//
// Note that the type of setter is AccessorNameBooleanSetterCallback instead
......
......@@ -1706,13 +1706,11 @@ static i::Handle<i::FunctionTemplateInfo> EnsureConstructor(
}
template <typename Getter, typename Setter, typename Data, typename Template>
static void 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 replace_on_access) {
static void 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 replace_on_access, SideEffectType getter_side_effect_type) {
auto info = Utils::OpenHandle(template_obj);
auto isolate = info->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
......@@ -1722,40 +1720,38 @@ static void TemplateSetAccessor(Template* template_obj, v8::Local<Name> name,
is_special_data_property, replace_on_access);
accessor_info->set_initial_property_attributes(
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);
}
void Template::SetNativeDataProperty(v8::Local<String> name,
AccessorGetterCallback getter,
AccessorSetterCallback setter,
v8::Local<Value> data,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
void Template::SetNativeDataProperty(
v8::Local<String> name, AccessorGetterCallback getter,
AccessorSetterCallback setter, v8::Local<Value> data,
PropertyAttribute attribute, v8::Local<AccessorSignature> signature,
AccessControl settings, SideEffectType getter_side_effect_type) {
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
signature, true, false);
signature, true, false, getter_side_effect_type);
}
void Template::SetNativeDataProperty(v8::Local<Name> name,
AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter,
v8::Local<Value> data,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature,
AccessControl settings) {
void Template::SetNativeDataProperty(
v8::Local<Name> name, AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter, v8::Local<Value> data,
PropertyAttribute attribute, v8::Local<AccessorSignature> signature,
AccessControl settings, SideEffectType getter_side_effect_type) {
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,
AccessorNameGetterCallback getter,
v8::Local<Value> data,
PropertyAttribute attribute) {
TemplateSetAccessor(
this, name, getter, static_cast<AccessorNameSetterCallback>(nullptr),
data, DEFAULT, attribute, Local<AccessorSignature>(), true, true);
PropertyAttribute attribute,
SideEffectType getter_side_effect_type) {
TemplateSetAccessor(this, name, getter,
static_cast<AccessorNameSetterCallback>(nullptr), data,
DEFAULT, attribute, Local<AccessorSignature>(), true,
true, getter_side_effect_type);
}
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));
}
void ObjectTemplate::SetAccessor(v8::Local<String> name,
AccessorGetterCallback getter,
AccessorSetterCallback setter,
v8::Local<Value> data, AccessControl settings,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) {
v8::Local<AccessorSignature> signature,
SideEffectType getter_side_effect_type) {
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,
AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter,
v8::Local<Value> data, AccessControl settings,
PropertyAttribute attribute,
v8::Local<AccessorSignature> signature) {
v8::Local<AccessorSignature> signature,
SideEffectType getter_side_effect_type) {
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,
......
......@@ -1509,10 +1509,10 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) {
if (result.is_null()) return isolate->factory()->undefined_value();
Handle<Object> reboxed_result = handle(*result, isolate);
if (info->replace_on_access() && receiver->IsJSReceiver()) {
args.CallAccessorSetter(
isolate->factory()->reconfigure_to_data_property_accessor(), name,
result);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
RETURN_ON_EXCEPTION(isolate,
Accessors::ReplaceAccessorWithDataProperty(
isolate, receiver, holder, name, result),
Object);
}
return reboxed_result;
}
......
......@@ -231,12 +231,18 @@ TEST(CachedAccessorOnGlobalObject) {
namespace {
// Getter return value should be non-null to trigger lazy property paths.
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) {}
static void Setter(v8::Local<v8::String> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {}
}
} // namespace
// Re-declaration of non-configurable accessors should throw.
TEST(RedeclareAccessor) {
......@@ -352,3 +358,86 @@ TEST(AccessorSetLazyDataPropertyHasNoSideEffect) {
->Int32Value(env.local())
.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