[debug] expose SideEffectType for whitelisting embedder callbacks
This exposes new flags to allow embedders to whitelist callbacks as side-effect-free during evaluation with throwOnSideEffect. Accessors and Functions/FunctionTemplates can take a new param on: - v8::Object::SetNativeDataProperty - v8::Object::SetLazyDataProperty - v8::Object::SetAccessor - v8::FunctionTemplate::New - v8::FunctionTemplate::NewWithCache - v8::Function::New While Interceptors can be created with an additional flag: PropertyHandlerFlag::kHasNoSideEffect Bug: v8:7515 Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng Change-Id: I14823316bdd6de6d362a1104b65f13504d0db056 Reviewed-on: https://chromium-review.googlesource.com/994550 Commit-Queue: Erik Luo <luoe@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#52367}
This commit is contained in:
parent
96e83b78d4
commit
6da12d420a
50
include/v8.h
50
include/v8.h
@ -3109,6 +3109,15 @@ enum PropertyFilter {
|
||||
SKIP_SYMBOLS = 16
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for marking whether callbacks may trigger JS-observable side effects.
|
||||
* Side-effect-free callbacks are whitelisted during debug evaluation with
|
||||
* throwOnSideEffect. It applies when calling a Function, FunctionTemplate,
|
||||
* or an Accessor's getter callback. For Interceptors, please see
|
||||
* PropertyHandlerFlags's kHasNoSideEffect.
|
||||
*/
|
||||
enum class SideEffectType { kHasSideEffect, kHasNoSideEffect };
|
||||
|
||||
/**
|
||||
* Keys/Properties filter enums:
|
||||
*
|
||||
@ -3241,13 +3250,15 @@ class V8_EXPORT Object : public Value {
|
||||
V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,
|
||||
uint32_t index);
|
||||
|
||||
V8_WARN_UNUSED_RESULT Maybe<bool> SetAccessor(Local<Context> context,
|
||||
Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter = 0,
|
||||
MaybeLocal<Value> data = MaybeLocal<Value>(),
|
||||
AccessControl settings = DEFAULT,
|
||||
PropertyAttribute attribute = None);
|
||||
/**
|
||||
* Note: SideEffectType affects the getter only, not the setter.
|
||||
*/
|
||||
V8_WARN_UNUSED_RESULT Maybe<bool> SetAccessor(
|
||||
Local<Context> context, Local<Name> name,
|
||||
AccessorNameGetterCallback getter, AccessorNameSetterCallback setter = 0,
|
||||
MaybeLocal<Value> data = MaybeLocal<Value>(),
|
||||
AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
void SetAccessorProperty(Local<Name> name, Local<Function> getter,
|
||||
Local<Function> setter = Local<Function>(),
|
||||
@ -3262,7 +3273,8 @@ class V8_EXPORT Object : public Value {
|
||||
Local<Context> context, Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter = nullptr,
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attributes = None);
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attributes = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Attempts to create a property with the given name which behaves like a data
|
||||
@ -3275,7 +3287,8 @@ class V8_EXPORT Object : public Value {
|
||||
V8_WARN_UNUSED_RESULT Maybe<bool> SetLazyDataProperty(
|
||||
Local<Context> context, Local<Name> name,
|
||||
AccessorNameGetterCallback getter, Local<Value> data = Local<Value>(),
|
||||
PropertyAttribute attributes = None);
|
||||
PropertyAttribute attributes = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Functionality for private properties.
|
||||
@ -3867,7 +3880,8 @@ class V8_EXPORT Function : public Object {
|
||||
static MaybeLocal<Function> New(
|
||||
Local<Context> context, FunctionCallback callback,
|
||||
Local<Value> data = Local<Value>(), int length = 0,
|
||||
ConstructorBehavior behavior = ConstructorBehavior::kAllow);
|
||||
ConstructorBehavior behavior = ConstructorBehavior::kAllow,
|
||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
||||
static V8_DEPRECATE_SOON(
|
||||
"Use maybe version",
|
||||
Local<Function> New(Isolate* isolate, FunctionCallback callback,
|
||||
@ -5521,7 +5535,8 @@ class V8_EXPORT FunctionTemplate : public Template {
|
||||
Isolate* isolate, FunctionCallback callback = 0,
|
||||
Local<Value> data = Local<Value>(),
|
||||
Local<Signature> signature = Local<Signature>(), int length = 0,
|
||||
ConstructorBehavior behavior = ConstructorBehavior::kAllow);
|
||||
ConstructorBehavior behavior = ConstructorBehavior::kAllow,
|
||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/** Get a template included in the snapshot by index. */
|
||||
static MaybeLocal<FunctionTemplate> FromSnapshot(Isolate* isolate,
|
||||
@ -5533,7 +5548,8 @@ class V8_EXPORT FunctionTemplate : public Template {
|
||||
static Local<FunctionTemplate> NewWithCache(
|
||||
Isolate* isolate, FunctionCallback callback,
|
||||
Local<Private> cache_property, Local<Value> data = Local<Value>(),
|
||||
Local<Signature> signature = Local<Signature>(), int length = 0);
|
||||
Local<Signature> signature = Local<Signature>(), int length = 0,
|
||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/** Returns the unique function instance in the current execution context.*/
|
||||
V8_DEPRECATE_SOON("Use maybe version", Local<Function> GetFunction());
|
||||
@ -5554,8 +5570,9 @@ class V8_EXPORT FunctionTemplate : public Template {
|
||||
* callback is called whenever the function created from this
|
||||
* FunctionTemplate is called.
|
||||
*/
|
||||
void SetCallHandler(FunctionCallback callback,
|
||||
Local<Value> data = Local<Value>());
|
||||
void SetCallHandler(
|
||||
FunctionCallback callback, Local<Value> data = Local<Value>(),
|
||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/** Set the predefined length property for the FunctionTemplate. */
|
||||
void SetLength(int length);
|
||||
@ -5666,6 +5683,11 @@ enum class PropertyHandlerFlags {
|
||||
* named interceptors.
|
||||
*/
|
||||
kOnlyInterceptStrings = 1 << 2,
|
||||
|
||||
/**
|
||||
* The getter, query, enumerator callbacks do not produce side effects.
|
||||
*/
|
||||
kHasNoSideEffect = 1 << 3,
|
||||
};
|
||||
|
||||
struct NamedPropertyHandlerConfiguration {
|
||||
|
82
src/api.cc
82
src/api.cc
@ -1402,7 +1402,8 @@ void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) {
|
||||
static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
i::Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||
v8::Local<Signature> signature, int length, bool do_not_cache,
|
||||
v8::Local<Private> cached_property_name = v8::Local<Private>()) {
|
||||
v8::Local<Private> cached_property_name = v8::Local<Private>(),
|
||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
|
||||
i::Handle<i::Struct> struct_obj =
|
||||
isolate->factory()->NewStruct(i::FUNCTION_TEMPLATE_INFO_TYPE, i::TENURED);
|
||||
i::Handle<i::FunctionTemplateInfo> obj =
|
||||
@ -1415,7 +1416,7 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
}
|
||||
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
|
||||
if (callback != 0) {
|
||||
Utils::ToLocal(obj)->SetCallHandler(callback, data);
|
||||
Utils::ToLocal(obj)->SetCallHandler(callback, data, side_effect_type);
|
||||
}
|
||||
obj->set_length(length);
|
||||
obj->set_undetectable(false);
|
||||
@ -1433,14 +1434,15 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
|
||||
Local<FunctionTemplate> FunctionTemplate::New(
|
||||
Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||
v8::Local<Signature> signature, int length, ConstructorBehavior behavior) {
|
||||
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
|
||||
SideEffectType side_effect_type) {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
// Changes to the environment cannot be captured in the snapshot. Expect no
|
||||
// function templates when the isolate is created for serialization.
|
||||
LOG_API(i_isolate, FunctionTemplate, New);
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
|
||||
auto templ =
|
||||
FunctionTemplateNew(i_isolate, callback, data, signature, length, false);
|
||||
auto templ = FunctionTemplateNew(i_isolate, callback, data, signature, length,
|
||||
false, Local<Private>(), side_effect_type);
|
||||
if (behavior == ConstructorBehavior::kThrow) templ->RemovePrototype();
|
||||
return templ;
|
||||
}
|
||||
@ -1462,12 +1464,13 @@ MaybeLocal<FunctionTemplate> FunctionTemplate::FromSnapshot(Isolate* isolate,
|
||||
|
||||
Local<FunctionTemplate> FunctionTemplate::NewWithCache(
|
||||
Isolate* isolate, FunctionCallback callback, Local<Private> cache_property,
|
||||
Local<Value> data, Local<Signature> signature, int length) {
|
||||
Local<Value> data, Local<Signature> signature, int length,
|
||||
SideEffectType side_effect_type) {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
LOG_API(i_isolate, FunctionTemplate, NewWithCache);
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
|
||||
return FunctionTemplateNew(i_isolate, callback, data, signature, length,
|
||||
false, cache_property);
|
||||
false, cache_property, side_effect_type);
|
||||
}
|
||||
|
||||
Local<Signature> Signature::New(Isolate* isolate,
|
||||
@ -1488,13 +1491,15 @@ Local<AccessorSignature> AccessorSignature::New(
|
||||
} while (false)
|
||||
|
||||
void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
||||
v8::Local<Value> data) {
|
||||
v8::Local<Value> data,
|
||||
SideEffectType side_effect_type) {
|
||||
auto info = Utils::OpenHandle(this);
|
||||
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetCallHandler");
|
||||
i::Isolate* isolate = info->GetIsolate();
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
i::HandleScope scope(isolate);
|
||||
i::Handle<i::CallHandlerInfo> obj = isolate->factory()->NewCallHandlerInfo();
|
||||
i::Handle<i::CallHandlerInfo> obj = isolate->factory()->NewCallHandlerInfo(
|
||||
side_effect_type == SideEffectType::kHasNoSideEffect);
|
||||
SET_FIELD_WRAPPED(obj, set_callback, callback);
|
||||
SET_FIELD_WRAPPED(obj, set_js_callback, obj->redirected_callback());
|
||||
if (data.IsEmpty()) {
|
||||
@ -1802,6 +1807,9 @@ static i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
|
||||
static_cast<int>(PropertyHandlerFlags::kAllCanRead));
|
||||
obj->set_non_masking(static_cast<int>(flags) &
|
||||
static_cast<int>(PropertyHandlerFlags::kNonMasking));
|
||||
obj->set_has_no_side_effect(
|
||||
static_cast<int>(flags) &
|
||||
static_cast<int>(PropertyHandlerFlags::kHasNoSideEffect));
|
||||
|
||||
if (data.IsEmpty()) {
|
||||
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
@ -4691,13 +4699,12 @@ Maybe<bool> v8::Object::Has(Local<Context> context, uint32_t index) {
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename Data>
|
||||
static Maybe<bool> ObjectSetAccessor(Local<Context> context, Object* self,
|
||||
Local<Name> name, Getter getter,
|
||||
Setter setter, Data data,
|
||||
AccessControl settings,
|
||||
PropertyAttribute attributes,
|
||||
bool is_special_data_property,
|
||||
bool replace_on_access) {
|
||||
static Maybe<bool> ObjectSetAccessor(
|
||||
Local<Context> context, Object* self, Local<Name> name, Getter getter,
|
||||
Setter setter, Data data, AccessControl settings,
|
||||
PropertyAttribute attributes, bool is_special_data_property,
|
||||
bool replace_on_access,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect) {
|
||||
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
|
||||
ENTER_V8_NO_SCRIPT(isolate, context, Object, SetAccessor, Nothing<bool>(),
|
||||
i::HandleScope);
|
||||
@ -4708,6 +4715,8 @@ static Maybe<bool> ObjectSetAccessor(Local<Context> context, Object* self,
|
||||
i::Handle<i::AccessorInfo> info =
|
||||
MakeAccessorInfo(isolate, name, getter, setter, data, settings, signature,
|
||||
is_special_data_property, replace_on_access);
|
||||
info->set_has_no_side_effect(getter_side_effect_type ==
|
||||
SideEffectType::kHasNoSideEffect);
|
||||
if (info.is_null()) return Nothing<bool>();
|
||||
bool fast = obj->HasFastProperties();
|
||||
i::Handle<i::Object> result;
|
||||
@ -4725,15 +4734,16 @@ static Maybe<bool> ObjectSetAccessor(Local<Context> context, Object* self,
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
|
||||
Maybe<bool> Object::SetAccessor(Local<Context> context, Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter,
|
||||
MaybeLocal<Value> data, AccessControl settings,
|
||||
PropertyAttribute attribute) {
|
||||
PropertyAttribute attribute,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter, setter,
|
||||
data.FromMaybe(Local<Value>()), settings, attribute,
|
||||
i::FLAG_disable_old_api_accessors, false);
|
||||
i::FLAG_disable_old_api_accessors, false,
|
||||
getter_side_effect_type);
|
||||
}
|
||||
|
||||
|
||||
@ -4756,24 +4766,23 @@ void Object::SetAccessorProperty(Local<Name> name, Local<Function> getter,
|
||||
static_cast<i::PropertyAttributes>(attribute));
|
||||
}
|
||||
|
||||
Maybe<bool> Object::SetNativeDataProperty(v8::Local<v8::Context> context,
|
||||
v8::Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter,
|
||||
v8::Local<Value> data,
|
||||
PropertyAttribute attributes) {
|
||||
Maybe<bool> Object::SetNativeDataProperty(
|
||||
v8::Local<v8::Context> context, v8::Local<Name> name,
|
||||
AccessorNameGetterCallback getter, AccessorNameSetterCallback setter,
|
||||
v8::Local<Value> data, PropertyAttribute attributes,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter, setter, data, DEFAULT,
|
||||
attributes, true, false);
|
||||
attributes, true, false, getter_side_effect_type);
|
||||
}
|
||||
|
||||
Maybe<bool> Object::SetLazyDataProperty(v8::Local<v8::Context> context,
|
||||
v8::Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
v8::Local<Value> data,
|
||||
PropertyAttribute attributes) {
|
||||
Maybe<bool> Object::SetLazyDataProperty(
|
||||
v8::Local<v8::Context> context, v8::Local<Name> name,
|
||||
AccessorNameGetterCallback getter, v8::Local<Value> data,
|
||||
PropertyAttribute attributes, SideEffectType getter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter,
|
||||
static_cast<AccessorNameSetterCallback>(nullptr),
|
||||
data, DEFAULT, attributes, true, true);
|
||||
data, DEFAULT, attributes, true, true,
|
||||
getter_side_effect_type);
|
||||
}
|
||||
|
||||
Maybe<bool> v8::Object::HasOwnProperty(Local<Context> context,
|
||||
@ -5036,15 +5045,16 @@ MaybeLocal<Value> Object::CallAsConstructor(Local<Context> context, int argc,
|
||||
RETURN_ESCAPED(result);
|
||||
}
|
||||
|
||||
|
||||
MaybeLocal<Function> Function::New(Local<Context> context,
|
||||
FunctionCallback callback, Local<Value> data,
|
||||
int length, ConstructorBehavior behavior) {
|
||||
int length, ConstructorBehavior behavior,
|
||||
SideEffectType side_effect_type) {
|
||||
i::Isolate* isolate = Utils::OpenHandle(*context)->GetIsolate();
|
||||
LOG_API(isolate, Function, New);
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
auto templ = FunctionTemplateNew(isolate, callback, data, Local<Signature>(),
|
||||
length, true);
|
||||
auto templ =
|
||||
FunctionTemplateNew(isolate, callback, data, Local<Signature>(), length,
|
||||
true, Local<Private>(), side_effect_type);
|
||||
if (behavior == ConstructorBehavior::kThrow) templ->RemovePrototype();
|
||||
return templ->GetFunction(context);
|
||||
}
|
||||
|
@ -231,6 +231,9 @@ TEST(CachedAccessorOnGlobalObject) {
|
||||
|
||||
namespace {
|
||||
|
||||
static void Getter(v8::Local<v8::Name> 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) {}
|
||||
}
|
||||
@ -258,3 +261,94 @@ TEST(RedeclareAccessor) {
|
||||
v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty();
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Accessors can be whitelisted as side-effect-free via SetAccessor.
|
||||
TEST(AccessorSetHasNoSideEffect) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
|
||||
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
||||
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
|
||||
obj->SetAccessor(context, v8_str("foo"), Getter).ToChecked();
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
|
||||
|
||||
obj->SetAccessor(context, v8_str("foo"), Getter, 0,
|
||||
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
|
||||
v8::PropertyAttribute::None,
|
||||
v8::SideEffectType::kHasNoSideEffect)
|
||||
.ToChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).ToLocalChecked();
|
||||
|
||||
// Check that setter is not whitelisted.
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo = 1"), true)
|
||||
.IsEmpty());
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), false)
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Accessors can be whitelisted as side-effect-free via SetNativeDataProperty.
|
||||
TEST(AccessorSetNativeDataPropertyHasNoSideEffect) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
|
||||
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
||||
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
|
||||
obj->SetNativeDataProperty(context, v8_str("foo"), Getter).ToChecked();
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
|
||||
|
||||
obj->SetNativeDataProperty(
|
||||
context, v8_str("foo"), Getter, nullptr, v8::Local<v8::Value>(),
|
||||
v8::PropertyAttribute::None, v8::SideEffectType::kHasNoSideEffect)
|
||||
.ToChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).ToLocalChecked();
|
||||
|
||||
// Check that setter is not whitelisted.
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo = 1"), true)
|
||||
.IsEmpty());
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), false)
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Accessors can be whitelisted as side-effect-free via SetLazyDataProperty.
|
||||
TEST(AccessorSetLazyDataPropertyHasNoSideEffect) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
|
||||
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
||||
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
|
||||
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
|
||||
obj->SetLazyDataProperty(context, v8_str("foo"), Getter).ToChecked();
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).IsEmpty());
|
||||
|
||||
obj->SetLazyDataProperty(context, v8_str("foo"), Getter,
|
||||
v8::Local<v8::Value>(), v8::PropertyAttribute::None,
|
||||
v8::SideEffectType::kHasNoSideEffect)
|
||||
.ToChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true).ToLocalChecked();
|
||||
|
||||
// Check that setter is not whitelisted.
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo = 1"), true)
|
||||
.IsEmpty());
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), false)
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
@ -2598,12 +2598,12 @@ static void InterceptorForHiddenProperties(
|
||||
THREADED_TEST(NoSideEffectPropertyHandler) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext context;
|
||||
|
||||
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
||||
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery,
|
||||
EmptyInterceptorDeleter, EmptyInterceptorEnumerator));
|
||||
|
||||
LocalContext context;
|
||||
v8::Local<v8::Object> object =
|
||||
templ->NewInstance(context.local()).ToLocalChecked();
|
||||
context->Global()->Set(context.local(), v8_str("obj"), object).FromJust();
|
||||
@ -2620,20 +2620,25 @@ THREADED_TEST(NoSideEffectPropertyHandler) {
|
||||
isolate, v8_str("(function() { for (var p in obj) ; })()"), true)
|
||||
.IsEmpty());
|
||||
|
||||
// TODO(luoe): turn on has_no_side_effect flag from API once it is exposed.
|
||||
i::Handle<i::JSObject> internal_object =
|
||||
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*object));
|
||||
internal_object->GetNamedInterceptor()->set_has_no_side_effect(true);
|
||||
// Side-effect-free version.
|
||||
Local<ObjectTemplate> templ2 = ObjectTemplate::New(isolate);
|
||||
templ2->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery,
|
||||
EmptyInterceptorDeleter, EmptyInterceptorEnumerator,
|
||||
v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kHasNoSideEffect));
|
||||
v8::Local<v8::Object> object2 =
|
||||
templ2->NewInstance(context.local()).ToLocalChecked();
|
||||
context->Global()->Set(context.local(), v8_str("obj2"), object2).FromJust();
|
||||
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj.x"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj2.x"), true).ToLocalChecked();
|
||||
CHECK(
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj.x = 1"), true).IsEmpty());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("'x' in obj"), true)
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("obj2.x = 1"), true).IsEmpty());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("'x' in obj2"), true)
|
||||
.ToLocalChecked();
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("delete obj.x"), true)
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("delete obj2.x"), true)
|
||||
.IsEmpty());
|
||||
v8::debug::EvaluateGlobal(
|
||||
isolate, v8_str("(function() { for (var p in obj) ; })()"), true)
|
||||
isolate, v8_str("(function() { for (var p in obj2) ; })()"), true)
|
||||
.ToLocalChecked();
|
||||
}
|
||||
|
||||
|
@ -12981,16 +12981,95 @@ TEST(CallHandlerHasNoSideEffect) {
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()"), true).IsEmpty());
|
||||
|
||||
// Side-effect-free version.
|
||||
// TODO(luoe): turn on has_no_side_effect flag from API once it is exposed.
|
||||
i::Handle<i::FunctionTemplateInfo> info =
|
||||
i::Handle<i::FunctionTemplateInfo>::cast(v8::Utils::OpenHandle(*templ));
|
||||
i::Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap();
|
||||
i::CallHandlerInfo* handler_info =
|
||||
i::CallHandlerInfo::cast(info->call_code());
|
||||
CHECK(!handler_info->IsSideEffectFreeCallHandlerInfo());
|
||||
handler_info->set_map(heap->side_effect_free_call_handler_info_map());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("f()"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f()"), true).ToLocalChecked();
|
||||
Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(isolate);
|
||||
templ2->SetCallHandler(EmptyHandler, v8::Local<Value>(),
|
||||
v8::SideEffectType::kHasNoSideEffect);
|
||||
CHECK(context->Global()
|
||||
->Set(context.local(), v8_str("f2"),
|
||||
templ2->GetFunction(context.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("f2()"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f2()"), true).ToLocalChecked();
|
||||
}
|
||||
|
||||
TEST(FunctionTemplateNewHasNoSideEffect) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext context;
|
||||
|
||||
// Function template with call handler.
|
||||
Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(isolate, EmptyHandler);
|
||||
CHECK(context->Global()
|
||||
->Set(context.local(), v8_str("f"),
|
||||
templ->GetFunction(context.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()"), true).IsEmpty());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()"), true).IsEmpty());
|
||||
|
||||
// Side-effect-free version.
|
||||
Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(
|
||||
isolate, EmptyHandler, v8::Local<Value>(), v8::Local<v8::Signature>(), 0,
|
||||
v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect);
|
||||
CHECK(context->Global()
|
||||
->Set(context.local(), v8_str("f2"),
|
||||
templ2->GetFunction(context.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("f2()"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f2()"), true).ToLocalChecked();
|
||||
}
|
||||
|
||||
TEST(FunctionTemplateNewWithCacheHasNoSideEffect) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext context;
|
||||
v8::Local<v8::Private> priv =
|
||||
v8::Private::ForApi(isolate, v8_str("Foo#draft"));
|
||||
|
||||
// Function template with call handler.
|
||||
Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::NewWithCache(isolate, EmptyHandler, priv);
|
||||
CHECK(context->Global()
|
||||
->Set(context.local(), v8_str("f"),
|
||||
templ->GetFunction(context.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()"), true).IsEmpty());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()"), true).IsEmpty());
|
||||
|
||||
// Side-effect-free version.
|
||||
Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::NewWithCache(
|
||||
isolate, EmptyHandler, priv, v8::Local<Value>(),
|
||||
v8::Local<v8::Signature>(), 0, v8::SideEffectType::kHasNoSideEffect);
|
||||
CHECK(context->Global()
|
||||
->Set(context.local(), v8_str("f2"),
|
||||
templ2->GetFunction(context.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("f2()"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f2()"), true).ToLocalChecked();
|
||||
}
|
||||
|
||||
TEST(FunctionNewHasNoSideEffect) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext context;
|
||||
|
||||
// Function with side-effect.
|
||||
Local<Function> func =
|
||||
Function::New(context.local(), EmptyHandler).ToLocalChecked();
|
||||
CHECK(context->Global()->Set(context.local(), v8_str("f"), func).FromJust());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()"), true).IsEmpty());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()"), true).IsEmpty());
|
||||
|
||||
// Side-effect-free version.
|
||||
Local<Function> func2 =
|
||||
Function::New(context.local(), EmptyHandler, Local<Value>(), 0,
|
||||
v8::ConstructorBehavior::kAllow,
|
||||
v8::SideEffectType::kHasNoSideEffect)
|
||||
.ToLocalChecked();
|
||||
CHECK(
|
||||
context->Global()->Set(context.local(), v8_str("f2"), func2).FromJust());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("f2()"), true).ToLocalChecked();
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f2()"), true).ToLocalChecked();
|
||||
}
|
||||
|
||||
TEST(CallHandlerAsFunctionHasNoSideEffectNotSupported) {
|
||||
@ -13006,7 +13085,6 @@ TEST(CallHandlerAsFunctionHasNoSideEffectNotSupported) {
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj()"), true).IsEmpty());
|
||||
|
||||
// Side-effect-free version is not supported.
|
||||
// TODO(luoe): turn on has_no_side_effect flag from API once it is exposed.
|
||||
i::FunctionTemplateInfo* cons = i::FunctionTemplateInfo::cast(
|
||||
v8::Utils::OpenHandle(*templ)->constructor());
|
||||
i::Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap();
|
||||
|
Loading…
Reference in New Issue
Block a user