[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:
Erik Luo 2018-04-04 12:04:12 -07:00 committed by Commit Bot
parent 96e83b78d4
commit 6da12d420a
5 changed files with 281 additions and 72 deletions

View File

@ -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 {

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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();