[debug-evaluate] extend accessors by runtime receiver checks
Also extend the API to reflect this new feature. R=jgruber@chromium.org, szuend@google.com, ulan@chromium.org Bug: v8:8125 Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng Change-Id: Ic7a7604a8c663ba04b324eb8902ff325a25654e7 Reviewed-on: https://chromium-review.googlesource.com/1202087 Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Commit-Queue: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#55604}
This commit is contained in:
parent
cbec08d5ad
commit
1c2aa60520
35
include/v8.h
35
include/v8.h
@ -3279,10 +3279,17 @@ enum PropertyFilter {
|
||||
* 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
|
||||
* or an Accessor callback. For Interceptors, please see
|
||||
* PropertyHandlerFlags's kHasNoSideEffect.
|
||||
* Callbacks that only cause side effects to the receiver are whitelisted if
|
||||
* invoked on receiver objects that are created within the same debug-evaluate
|
||||
* call, as these objects are temporary and the side effect does not escape.
|
||||
*/
|
||||
enum class SideEffectType { kHasSideEffect, kHasNoSideEffect };
|
||||
enum class SideEffectType {
|
||||
kHasSideEffect,
|
||||
kHasNoSideEffect,
|
||||
kHasSideEffectToReceiver
|
||||
};
|
||||
|
||||
/**
|
||||
* Keys/Properties filter enums:
|
||||
@ -3423,7 +3430,8 @@ class V8_EXPORT Object : public Value {
|
||||
AccessorNameGetterCallback getter, AccessorNameSetterCallback setter = 0,
|
||||
MaybeLocal<Value> data = MaybeLocal<Value>(),
|
||||
AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
void SetAccessorProperty(Local<Name> name, Local<Function> getter,
|
||||
Local<Function> setter = Local<Function>(),
|
||||
@ -3439,7 +3447,8 @@ class V8_EXPORT Object : public Value {
|
||||
AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter = nullptr,
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attributes = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Attempts to create a property with the given name which behaves like a data
|
||||
@ -3453,7 +3462,8 @@ class V8_EXPORT Object : public Value {
|
||||
Local<Context> context, Local<Name> name,
|
||||
AccessorNameGetterCallback getter, Local<Value> data = Local<Value>(),
|
||||
PropertyAttribute attributes = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Functionality for private properties.
|
||||
@ -5388,7 +5398,8 @@ class V8_EXPORT Template : public Data {
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
|
||||
Local<AccessorSignature> signature = Local<AccessorSignature>(),
|
||||
AccessControl settings = DEFAULT,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
void SetNativeDataProperty(
|
||||
Local<Name> name, AccessorNameGetterCallback getter,
|
||||
AccessorNameSetterCallback setter = 0,
|
||||
@ -5396,7 +5407,8 @@ class V8_EXPORT Template : public Data {
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
|
||||
Local<AccessorSignature> signature = Local<AccessorSignature>(),
|
||||
AccessControl settings = DEFAULT,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Like SetNativeDataProperty, but V8 will replace the native data property
|
||||
@ -5405,7 +5417,8 @@ class V8_EXPORT Template : public Data {
|
||||
void SetLazyDataProperty(
|
||||
Local<Name> name, AccessorNameGetterCallback getter,
|
||||
Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* During template instantiation, sets the value with the intrinsic property
|
||||
@ -6120,13 +6133,15 @@ class V8_EXPORT ObjectTemplate : public Template {
|
||||
AccessorSetterCallback setter = 0, Local<Value> data = Local<Value>(),
|
||||
AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
|
||||
Local<AccessorSignature> signature = Local<AccessorSignature>(),
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_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>(),
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect,
|
||||
SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect);
|
||||
|
||||
/**
|
||||
* Sets a named property handler on the object template.
|
||||
|
@ -31,7 +31,8 @@ Handle<AccessorInfo> Accessors::MakeAccessor(
|
||||
info->set_is_special_data_property(true);
|
||||
info->set_is_sloppy(false);
|
||||
info->set_replace_on_access(false);
|
||||
info->set_has_no_side_effect(false);
|
||||
info->set_getter_side_effect_type(SideEffectType::kHasSideEffect);
|
||||
info->set_setter_side_effect_type(SideEffectType::kHasSideEffect);
|
||||
name = factory->InternalizeName(name);
|
||||
info->set_name(*name);
|
||||
Handle<Object> get = v8::FromCData(isolate, getter);
|
||||
|
@ -22,27 +22,27 @@ class JavaScriptFrame;
|
||||
|
||||
// The list of accessor descriptors. This is a second-order macro
|
||||
// taking a macro to be applied to all accessor descriptor names.
|
||||
// V(accessor_name, AccessorName, GetterSideEffectType, SetterSideEffectType)
|
||||
#define ACCESSOR_INFO_LIST(V) \
|
||||
V(arguments_iterator, ArgumentsIterator) \
|
||||
V(array_length, ArrayLength) \
|
||||
V(bound_function_length, BoundFunctionLength) \
|
||||
V(bound_function_name, BoundFunctionName) \
|
||||
V(error_stack, ErrorStack) \
|
||||
V(function_arguments, FunctionArguments) \
|
||||
V(function_caller, FunctionCaller) \
|
||||
V(function_name, FunctionName) \
|
||||
V(function_length, FunctionLength) \
|
||||
V(function_prototype, FunctionPrototype) \
|
||||
V(string_length, StringLength)
|
||||
|
||||
#define SIDE_EFFECT_FREE_ACCESSOR_INFO_LIST(V) \
|
||||
V(ArrayLength) \
|
||||
V(BoundFunctionLength) \
|
||||
V(BoundFunctionName) \
|
||||
V(FunctionName) \
|
||||
V(FunctionLength) \
|
||||
V(FunctionPrototype) \
|
||||
V(StringLength)
|
||||
V(arguments_iterator, ArgumentsIterator, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(array_length, ArrayLength, kHasNoSideEffect, kHasSideEffectToReceiver) \
|
||||
V(bound_function_length, BoundFunctionLength, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(bound_function_name, BoundFunctionName, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(error_stack, ErrorStack, kHasSideEffectToReceiver, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(function_arguments, FunctionArguments, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(function_caller, FunctionCaller, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(function_name, FunctionName, kHasNoSideEffect, kHasSideEffectToReceiver) \
|
||||
V(function_length, FunctionLength, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(function_prototype, FunctionPrototype, kHasNoSideEffect, \
|
||||
kHasSideEffectToReceiver) \
|
||||
V(string_length, StringLength, kHasNoSideEffect, kHasSideEffectToReceiver)
|
||||
|
||||
#define ACCESSOR_SETTER_LIST(V) \
|
||||
V(ArrayLengthSetter) \
|
||||
@ -55,7 +55,7 @@ class JavaScriptFrame;
|
||||
|
||||
class Accessors : public AllStatic {
|
||||
public:
|
||||
#define ACCESSOR_GETTER_DECLARATION(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_GETTER_DECLARATION(accessor_name, AccessorName, ...) \
|
||||
static void AccessorName##Getter( \
|
||||
v8::Local<v8::Name> name, \
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
@ -118,7 +118,7 @@ class Accessors : public AllStatic {
|
||||
AccessorNameBooleanSetterCallback setter);
|
||||
|
||||
private:
|
||||
#define ACCESSOR_INFO_DECLARATION(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_DECLARATION(accessor_name, AccessorName, ...) \
|
||||
static Handle<AccessorInfo> Make##AccessorName##Info(Isolate* isolate);
|
||||
ACCESSOR_INFO_LIST(ACCESSOR_INFO_DECLARATION)
|
||||
#undef ACCESSOR_INFO_DECLARATION
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/api-arguments.h"
|
||||
|
||||
#include "src/api-inl.h"
|
||||
#include "src/debug/debug.h"
|
||||
#include "src/objects/api-callbacks.h"
|
||||
#include "src/tracing/trace-event.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
@ -34,6 +35,10 @@ inline JSObject* PropertyCallbackArguments::holder() {
|
||||
return JSObject::cast(this->begin()[T::kHolderIndex]);
|
||||
}
|
||||
|
||||
inline Object* PropertyCallbackArguments::receiver() {
|
||||
return Object::cast(this->begin()[T::kThisIndex]);
|
||||
}
|
||||
|
||||
inline JSObject* FunctionCallbackArguments::holder() {
|
||||
return JSObject::cast(this->begin()[T::kHolderIndex]);
|
||||
}
|
||||
@ -48,9 +53,19 @@ inline JSObject* FunctionCallbackArguments::holder() {
|
||||
DCHECK_IMPLIES(name->IsSymbol(), interceptor->can_intercept_symbols());
|
||||
|
||||
#define PREPARE_CALLBACK_INFO(ISOLATE, F, RETURN_VALUE, API_RETURN_TYPE, \
|
||||
CALLBACK_INFO) \
|
||||
CALLBACK_INFO, RECEIVER, ACCESSOR_KIND) \
|
||||
if (ISOLATE->debug_execution_mode() == DebugInfo::kSideEffects && \
|
||||
!ISOLATE->debug()->PerformSideEffectCheckForCallback(CALLBACK_INFO)) { \
|
||||
!ISOLATE->debug()->PerformSideEffectCheckForCallback( \
|
||||
CALLBACK_INFO, RECEIVER, Debug::k##ACCESSOR_KIND)) { \
|
||||
return RETURN_VALUE(); \
|
||||
} \
|
||||
VMState<EXTERNAL> state(ISOLATE); \
|
||||
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F)); \
|
||||
PropertyCallbackInfo<API_RETURN_TYPE> callback_info(begin());
|
||||
|
||||
#define PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK(ISOLATE, F, RETURN_VALUE, \
|
||||
API_RETURN_TYPE) \
|
||||
if (ISOLATE->debug_execution_mode() == DebugInfo::kSideEffects) { \
|
||||
return RETURN_VALUE(); \
|
||||
} \
|
||||
VMState<EXTERNAL> state(ISOLATE); \
|
||||
@ -65,11 +80,13 @@ inline JSObject* FunctionCallbackArguments::holder() {
|
||||
Isolate* isolate = this->isolate(); \
|
||||
RuntimeCallTimerScope timer( \
|
||||
isolate, RuntimeCallCounterId::kNamed##FUNCTION##Callback); \
|
||||
Handle<Object> receiver_check_unsupported; \
|
||||
GenericNamedProperty##FUNCTION##Callback f = \
|
||||
ToCData<GenericNamedProperty##FUNCTION##Callback>( \
|
||||
interceptor->TYPE()); \
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<RETURN_TYPE>, API_RETURN_TYPE, \
|
||||
INFO_FOR_SIDE_EFFECT); \
|
||||
INFO_FOR_SIDE_EFFECT, receiver_check_unsupported, \
|
||||
NotAccessor); \
|
||||
LOG(isolate, \
|
||||
ApiNamedPropertyAccess("interceptor-named-" #TYPE, holder(), *name)); \
|
||||
f(v8::Utils::ToLocal(name), callback_info); \
|
||||
@ -87,10 +104,12 @@ FOR_EACH_CALLBACK(CREATE_NAMED_CALLBACK)
|
||||
Isolate* isolate = this->isolate(); \
|
||||
RuntimeCallTimerScope timer( \
|
||||
isolate, RuntimeCallCounterId::kIndexed##FUNCTION##Callback); \
|
||||
Handle<Object> receiver_check_unsupported; \
|
||||
IndexedProperty##FUNCTION##Callback f = \
|
||||
ToCData<IndexedProperty##FUNCTION##Callback>(interceptor->TYPE()); \
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<RETURN_TYPE>, API_RETURN_TYPE, \
|
||||
INFO_FOR_SIDE_EFFECT); \
|
||||
INFO_FOR_SIDE_EFFECT, receiver_check_unsupported, \
|
||||
NotAccessor); \
|
||||
LOG(isolate, ApiIndexedPropertyAccess("interceptor-indexed-" #TYPE, \
|
||||
holder(), index)); \
|
||||
f(index, callback_info); \
|
||||
@ -108,9 +127,11 @@ Handle<Object> FunctionCallbackArguments::Call(CallHandlerInfo* handler) {
|
||||
RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kFunctionCallback);
|
||||
v8::FunctionCallback f =
|
||||
v8::ToCData<v8::FunctionCallback>(handler->callback());
|
||||
Handle<Object> receiver_check_unsupported;
|
||||
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects &&
|
||||
!isolate->debug()->PerformSideEffectCheckForCallback(
|
||||
handle(handler, isolate))) {
|
||||
handle(handler, isolate), receiver_check_unsupported,
|
||||
Debug::kNotAccessor)) {
|
||||
return Handle<Object>();
|
||||
}
|
||||
VMState<EXTERNAL> state(isolate);
|
||||
@ -167,10 +188,11 @@ Handle<Object> PropertyCallbackArguments::CallNamedDescriptor(
|
||||
|
||||
Handle<Object> PropertyCallbackArguments::BasicCallNamedGetterCallback(
|
||||
GenericNamedPropertyGetterCallback f, Handle<Name> name,
|
||||
Handle<Object> info) {
|
||||
Handle<Object> info, Handle<Object> receiver) {
|
||||
DCHECK(!name->IsPrivate());
|
||||
Isolate* isolate = this->isolate();
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value, info);
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value, info, receiver,
|
||||
Getter);
|
||||
f(v8::Utils::ToLocal(name), callback_info);
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
@ -184,9 +206,8 @@ Handle<Object> PropertyCallbackArguments::CallNamedSetter(
|
||||
Isolate* isolate = this->isolate();
|
||||
RuntimeCallTimerScope timer(isolate,
|
||||
RuntimeCallCounterId::kNamedSetterCallback);
|
||||
Handle<Object> side_effect_check_not_supported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
|
||||
side_effect_check_not_supported);
|
||||
PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK(isolate, f, Handle<Object>,
|
||||
v8::Value);
|
||||
LOG(isolate,
|
||||
ApiNamedPropertyAccess("interceptor-named-set", holder(), *name));
|
||||
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
|
||||
@ -202,9 +223,8 @@ Handle<Object> PropertyCallbackArguments::CallNamedDefiner(
|
||||
RuntimeCallCounterId::kNamedDefinerCallback);
|
||||
GenericNamedPropertyDefinerCallback f =
|
||||
ToCData<GenericNamedPropertyDefinerCallback>(interceptor->definer());
|
||||
Handle<Object> side_effect_check_not_supported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
|
||||
side_effect_check_not_supported);
|
||||
PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK(isolate, f, Handle<Object>,
|
||||
v8::Value);
|
||||
LOG(isolate,
|
||||
ApiNamedPropertyAccess("interceptor-named-define", holder(), *name));
|
||||
f(v8::Utils::ToLocal(name), desc, callback_info);
|
||||
@ -219,9 +239,8 @@ Handle<Object> PropertyCallbackArguments::CallIndexedSetter(
|
||||
RuntimeCallCounterId::kIndexedSetterCallback);
|
||||
IndexedPropertySetterCallback f =
|
||||
ToCData<IndexedPropertySetterCallback>(interceptor->setter());
|
||||
Handle<Object> side_effect_check_not_supported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
|
||||
side_effect_check_not_supported);
|
||||
PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK(isolate, f, Handle<Object>,
|
||||
v8::Value);
|
||||
LOG(isolate,
|
||||
ApiIndexedPropertyAccess("interceptor-indexed-set", holder(), index));
|
||||
f(index, v8::Utils::ToLocal(value), callback_info);
|
||||
@ -237,9 +256,8 @@ Handle<Object> PropertyCallbackArguments::CallIndexedDefiner(
|
||||
RuntimeCallCounterId::kIndexedDefinerCallback);
|
||||
IndexedPropertyDefinerCallback f =
|
||||
ToCData<IndexedPropertyDefinerCallback>(interceptor->definer());
|
||||
Handle<Object> side_effect_check_not_supported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
|
||||
side_effect_check_not_supported);
|
||||
PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK(isolate, f, Handle<Object>,
|
||||
v8::Value);
|
||||
LOG(isolate,
|
||||
ApiIndexedPropertyAccess("interceptor-indexed-define", holder(), index));
|
||||
f(index, desc, callback_info);
|
||||
@ -275,7 +293,9 @@ Handle<Object> PropertyCallbackArguments::CallIndexedDescriptor(
|
||||
Handle<Object> PropertyCallbackArguments::BasicCallIndexedGetterCallback(
|
||||
IndexedPropertyGetterCallback f, uint32_t index, Handle<Object> info) {
|
||||
Isolate* isolate = this->isolate();
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value, info);
|
||||
Handle<Object> receiver_check_unsupported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value, info,
|
||||
receiver_check_unsupported, Getter);
|
||||
f(index, callback_info);
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
@ -287,7 +307,9 @@ Handle<JSObject> PropertyCallbackArguments::CallPropertyEnumerator(
|
||||
v8::ToCData<IndexedPropertyEnumeratorCallback>(interceptor->enumerator());
|
||||
// TODO(cbruni): assert same type for indexed and named callback.
|
||||
Isolate* isolate = this->isolate();
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<JSObject>, v8::Array, interceptor);
|
||||
Handle<Object> receiver_check_unsupported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<JSObject>, v8::Array, interceptor,
|
||||
receiver_check_unsupported, NotAccessor);
|
||||
f(callback_info);
|
||||
return GetReturnValue<JSObject>(isolate);
|
||||
}
|
||||
@ -303,7 +325,8 @@ Handle<Object> PropertyCallbackArguments::CallAccessorGetter(
|
||||
LOG(isolate, ApiNamedPropertyAccess("accessor-getter", holder(), *name));
|
||||
AccessorNameGetterCallback f =
|
||||
ToCData<AccessorNameGetterCallback>(info->getter());
|
||||
return BasicCallNamedGetterCallback(f, name, info);
|
||||
return BasicCallNamedGetterCallback(f, name, info,
|
||||
handle(receiver(), isolate));
|
||||
}
|
||||
|
||||
Handle<Object> PropertyCallbackArguments::CallAccessorSetter(
|
||||
@ -314,15 +337,15 @@ Handle<Object> PropertyCallbackArguments::CallAccessorSetter(
|
||||
RuntimeCallCounterId::kAccessorSetterCallback);
|
||||
AccessorNameSetterCallback f =
|
||||
ToCData<AccessorNameSetterCallback>(accessor_info->setter());
|
||||
Handle<Object> side_effect_check_not_supported;
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, void,
|
||||
side_effect_check_not_supported);
|
||||
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, void, accessor_info,
|
||||
handle(receiver(), isolate), Setter);
|
||||
LOG(isolate, ApiNamedPropertyAccess("accessor-setter", holder(), *name));
|
||||
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
|
||||
#undef PREPARE_CALLBACK_INFO
|
||||
#undef PREPARE_CALLBACK_INFO_FAIL_SIDE_EFFECT_CHECK
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -133,9 +133,10 @@ class PropertyCallbackArguments
|
||||
IndexedPropertyGetterCallback f, uint32_t index, Handle<Object> info);
|
||||
inline Handle<Object> BasicCallNamedGetterCallback(
|
||||
GenericNamedPropertyGetterCallback f, Handle<Name> name,
|
||||
Handle<Object> info);
|
||||
Handle<Object> info, Handle<Object> receiver = Handle<Object>());
|
||||
|
||||
inline JSObject* holder();
|
||||
inline Object* receiver();
|
||||
|
||||
// Don't copy PropertyCallbackArguments, because they would both have the
|
||||
// same prev_ pointer.
|
||||
|
60
src/api.cc
60
src/api.cc
@ -1676,7 +1676,8 @@ 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) {
|
||||
bool replace_on_access, SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
auto info = Utils::OpenHandle(template_obj);
|
||||
auto isolate = info->GetIsolate();
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
@ -1686,8 +1687,8 @@ static void TemplateSetAccessor(
|
||||
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);
|
||||
accessor_info->set_getter_side_effect_type(getter_side_effect_type);
|
||||
accessor_info->set_setter_side_effect_type(setter_side_effect_type);
|
||||
i::ApiNatives::AddNativeDataProperty(isolate, info, accessor_info);
|
||||
}
|
||||
|
||||
@ -1695,29 +1696,34 @@ 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) {
|
||||
AccessControl settings, SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
|
||||
signature, true, false, getter_side_effect_type);
|
||||
signature, true, false, getter_side_effect_type,
|
||||
setter_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, SideEffectType getter_side_effect_type) {
|
||||
AccessControl settings, SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
|
||||
signature, true, false, getter_side_effect_type);
|
||||
signature, true, false, getter_side_effect_type,
|
||||
setter_side_effect_type);
|
||||
}
|
||||
|
||||
void Template::SetLazyDataProperty(v8::Local<Name> name,
|
||||
AccessorNameGetterCallback getter,
|
||||
v8::Local<Value> data,
|
||||
PropertyAttribute attribute,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
TemplateSetAccessor(this, name, getter,
|
||||
static_cast<AccessorNameSetterCallback>(nullptr), data,
|
||||
DEFAULT, attribute, Local<AccessorSignature>(), true,
|
||||
true, getter_side_effect_type);
|
||||
true, getter_side_effect_type, setter_side_effect_type);
|
||||
}
|
||||
|
||||
void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
|
||||
@ -1737,10 +1743,11 @@ void ObjectTemplate::SetAccessor(v8::Local<String> name,
|
||||
v8::Local<Value> data, AccessControl settings,
|
||||
PropertyAttribute attribute,
|
||||
v8::Local<AccessorSignature> signature,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
|
||||
signature, i::FLAG_disable_old_api_accessors, false,
|
||||
getter_side_effect_type);
|
||||
getter_side_effect_type, setter_side_effect_type);
|
||||
}
|
||||
|
||||
void ObjectTemplate::SetAccessor(v8::Local<Name> name,
|
||||
@ -1749,10 +1756,11 @@ void ObjectTemplate::SetAccessor(v8::Local<Name> name,
|
||||
v8::Local<Value> data, AccessControl settings,
|
||||
PropertyAttribute attribute,
|
||||
v8::Local<AccessorSignature> signature,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
TemplateSetAccessor(this, name, getter, setter, data, settings, attribute,
|
||||
signature, i::FLAG_disable_old_api_accessors, false,
|
||||
getter_side_effect_type);
|
||||
getter_side_effect_type, setter_side_effect_type);
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename Query, typename Descriptor,
|
||||
@ -4569,8 +4577,8 @@ 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) {
|
||||
bool replace_on_access, SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
|
||||
ENTER_V8_NO_SCRIPT(isolate, context, Object, SetAccessor, Nothing<bool>(),
|
||||
i::HandleScope);
|
||||
@ -4581,8 +4589,8 @@ static Maybe<bool> ObjectSetAccessor(
|
||||
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);
|
||||
info->set_getter_side_effect_type(getter_side_effect_type);
|
||||
info->set_setter_side_effect_type(setter_side_effect_type);
|
||||
if (info.is_null()) return Nothing<bool>();
|
||||
bool fast = obj->HasFastProperties();
|
||||
i::Handle<i::Object> result;
|
||||
@ -4605,11 +4613,12 @@ Maybe<bool> Object::SetAccessor(Local<Context> context, Local<Name> name,
|
||||
AccessorNameSetterCallback setter,
|
||||
MaybeLocal<Value> data, AccessControl settings,
|
||||
PropertyAttribute attribute,
|
||||
SideEffectType getter_side_effect_type) {
|
||||
SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter, setter,
|
||||
data.FromMaybe(Local<Value>()), settings, attribute,
|
||||
i::FLAG_disable_old_api_accessors, false,
|
||||
getter_side_effect_type);
|
||||
getter_side_effect_type, setter_side_effect_type);
|
||||
}
|
||||
|
||||
|
||||
@ -4636,19 +4645,22 @@ 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) {
|
||||
SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter, setter, data, DEFAULT,
|
||||
attributes, true, false, getter_side_effect_type);
|
||||
attributes, true, false, getter_side_effect_type,
|
||||
setter_side_effect_type);
|
||||
}
|
||||
|
||||
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) {
|
||||
PropertyAttribute attributes, SideEffectType getter_side_effect_type,
|
||||
SideEffectType setter_side_effect_type) {
|
||||
return ObjectSetAccessor(context, this, name, getter,
|
||||
static_cast<AccessorNameSetterCallback>(nullptr),
|
||||
data, DEFAULT, attributes, true, true,
|
||||
getter_side_effect_type);
|
||||
getter_side_effect_type, setter_side_effect_type);
|
||||
}
|
||||
|
||||
Maybe<bool> v8::Object::HasOwnProperty(Local<Context> context,
|
||||
@ -9684,7 +9696,7 @@ int debug::GetNativeAccessorDescriptor(v8::Local<v8::Context> context,
|
||||
}
|
||||
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
|
||||
int result = 0;
|
||||
#define IS_BUILTIN_ACESSOR(name, _) \
|
||||
#define IS_BUILTIN_ACESSOR(name, ...) \
|
||||
if (*structure == *isolate->factory()->name##_accessor()) \
|
||||
result |= static_cast<int>(debug::NativeAccessorType::IsBuiltin);
|
||||
ACCESSOR_INFO_LIST(IS_BUILTIN_ACESSOR)
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "src/interpreter/bytecode-array-iterator.h"
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/objects/api-callbacks.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -796,7 +795,10 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
|
||||
case Builtins::kMakeURIError:
|
||||
// RegExp builtins.
|
||||
case Builtins::kRegExpConstructor:
|
||||
// Internal.
|
||||
case Builtins::kStrictPoisonPillThrower:
|
||||
return DebugInfo::kHasNoSideEffect;
|
||||
|
||||
// Set builtins.
|
||||
case Builtins::kSetIteratorPrototypeNext:
|
||||
case Builtins::kSetPrototypeAdd:
|
||||
@ -950,34 +952,6 @@ DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
|
||||
return DebugInfo::kHasSideEffects;
|
||||
}
|
||||
|
||||
// static
|
||||
bool DebugEvaluate::CallbackHasNoSideEffect(Object* callback_info) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (callback_info->IsAccessorInfo()) {
|
||||
// List of whitelisted internal accessors can be found in accessors.h.
|
||||
AccessorInfo* info = AccessorInfo::cast(callback_info);
|
||||
if (info->has_no_side_effect()) return true;
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API Callback '");
|
||||
info->name()->ShortPrint();
|
||||
PrintF("' may cause side effect.\n");
|
||||
}
|
||||
} else if (callback_info->IsInterceptorInfo()) {
|
||||
InterceptorInfo* info = InterceptorInfo::cast(callback_info);
|
||||
if (info->has_no_side_effect()) return true;
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
|
||||
}
|
||||
} else if (callback_info->IsCallHandlerInfo()) {
|
||||
CallHandlerInfo* info = CallHandlerInfo::cast(callback_info);
|
||||
if (info->IsSideEffectFreeCallHandlerInfo()) return true;
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void DebugEvaluate::ApplySideEffectChecks(
|
||||
Handle<BytecodeArray> bytecode_array) {
|
||||
|
@ -41,7 +41,6 @@ class DebugEvaluate : public AllStatic {
|
||||
|
||||
static DebugInfo::SideEffectState FunctionGetSideEffectState(
|
||||
Isolate* isolate, Handle<SharedFunctionInfo> info);
|
||||
static bool CallbackHasNoSideEffect(Object* callback_info);
|
||||
static void ApplySideEffectChecks(Handle<BytecodeArray> bytecode_array);
|
||||
|
||||
private:
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/log.h"
|
||||
#include "src/messages.h"
|
||||
#include "src/objects/api-callbacks-inl.h"
|
||||
#include "src/objects/debug-objects-inl.h"
|
||||
#include "src/objects/js-generator-inl.h"
|
||||
#include "src/objects/js-promise-inl.h"
|
||||
@ -2180,16 +2181,55 @@ Handle<Object> Debug::return_value_handle() {
|
||||
return handle(thread_local_.return_value_, isolate_);
|
||||
}
|
||||
|
||||
bool Debug::PerformSideEffectCheckForCallback(Handle<Object> callback_info) {
|
||||
bool Debug::PerformSideEffectCheckForCallback(
|
||||
Handle<Object> callback_info, Handle<Object> receiver,
|
||||
Debug::AccessorKind accessor_kind) {
|
||||
DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
|
||||
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
|
||||
if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
|
||||
i::CallHandlerInfo::cast(*callback_info)->NextCallHasNoSideEffect()) {
|
||||
return true;
|
||||
}
|
||||
// TODO(7515): always pass a valid callback info object.
|
||||
if (!callback_info.is_null() &&
|
||||
DebugEvaluate::CallbackHasNoSideEffect(*callback_info)) {
|
||||
if (!callback_info.is_null()) {
|
||||
if (callback_info->IsAccessorInfo()) {
|
||||
// List of whitelisted internal accessors can be found in accessors.h.
|
||||
AccessorInfo* info = AccessorInfo::cast(*callback_info);
|
||||
DCHECK_NE(kNotAccessor, accessor_kind);
|
||||
switch (accessor_kind == kSetter ? info->setter_side_effect_type()
|
||||
: info->getter_side_effect_type()) {
|
||||
case SideEffectType::kHasNoSideEffect:
|
||||
// We do not support setter accessors with no side effects, since
|
||||
// calling set accessors go through a store bytecode. Store bytecodes
|
||||
// are considered to cause side effects (to non-temporary objects).
|
||||
DCHECK_NE(kSetter, accessor_kind);
|
||||
return true;
|
||||
case SideEffectType::kHasSideEffectToReceiver:
|
||||
DCHECK(!receiver.is_null());
|
||||
if (PerformSideEffectCheckForObject(receiver)) return true;
|
||||
isolate_->OptionalRescheduleException(false);
|
||||
return false;
|
||||
case SideEffectType::kHasSideEffect:
|
||||
break;
|
||||
}
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API Callback '");
|
||||
info->name()->ShortPrint();
|
||||
PrintF("' may cause side effect.\n");
|
||||
}
|
||||
} else if (callback_info->IsInterceptorInfo()) {
|
||||
InterceptorInfo* info = InterceptorInfo::cast(*callback_info);
|
||||
if (info->has_no_side_effect()) return true;
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
|
||||
}
|
||||
} else if (callback_info->IsCallHandlerInfo()) {
|
||||
CallHandlerInfo* info = CallHandlerInfo::cast(*callback_info);
|
||||
if (info->IsSideEffectFreeCallHandlerInfo()) return true;
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
side_effect_check_failed_ = true;
|
||||
// Throw an uncatchable termination exception.
|
||||
@ -2226,11 +2266,14 @@ bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
|
||||
bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
|
||||
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
|
||||
|
||||
if (object->IsHeapObject()) {
|
||||
// We expect no side-effects for primitives.
|
||||
if (object->IsNumber()) return true;
|
||||
if (object->IsName()) return true;
|
||||
|
||||
if (temporary_objects_->HasObject(Handle<HeapObject>::cast(object))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG_trace_side_effect_free_debug_evaluate) {
|
||||
PrintF("[debug-evaluate] failed runtime side effect check.\n");
|
||||
}
|
||||
|
@ -326,7 +326,11 @@ class Debug {
|
||||
|
||||
bool PerformSideEffectCheck(Handle<JSFunction> function,
|
||||
Handle<Object> receiver);
|
||||
bool PerformSideEffectCheckForCallback(Handle<Object> callback_info);
|
||||
|
||||
enum AccessorKind { kNotAccessor, kGetter, kSetter };
|
||||
bool PerformSideEffectCheckForCallback(Handle<Object> callback_info,
|
||||
Handle<Object> receiver,
|
||||
AccessorKind accessor_kind);
|
||||
bool PerformSideEffectCheckAtBytecode(InterpretedFrame* frame);
|
||||
bool PerformSideEffectCheckForObject(Handle<Object> object);
|
||||
|
||||
|
@ -159,7 +159,7 @@ void ExternalReferenceTable::AddAccessors(int* index) {
|
||||
};
|
||||
|
||||
static const AccessorRefTable getters[] = {
|
||||
#define ACCESSOR_INFO_DECLARATION(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_DECLARATION(accessor_name, AccessorName, ...) \
|
||||
{FUNCTION_ADDR(&Accessors::AccessorName##Getter), \
|
||||
"Accessors::" #AccessorName "Getter"}, /* NOLINT(whitespace/indent) */
|
||||
ACCESSOR_INFO_LIST(ACCESSOR_INFO_DECLARATION)
|
||||
|
@ -73,7 +73,7 @@ PUBLIC_SYMBOL_LIST(SYMBOL_ACCESSOR)
|
||||
WELL_KNOWN_SYMBOL_LIST(SYMBOL_ACCESSOR)
|
||||
#undef SYMBOL_ACCESSOR
|
||||
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName, ...) \
|
||||
Handle<AccessorInfo> Factory::accessor_name##_accessor() { \
|
||||
return Handle<AccessorInfo>(bit_cast<AccessorInfo**>( \
|
||||
&isolate() \
|
||||
|
@ -861,7 +861,7 @@ class V8_EXPORT_PRIVATE Factory {
|
||||
WELL_KNOWN_SYMBOL_LIST(SYMBOL_ACCESSOR)
|
||||
#undef SYMBOL_ACCESSOR
|
||||
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, ...) \
|
||||
inline Handle<AccessorInfo> accessor_name##_accessor();
|
||||
ACCESSOR_INFO_LIST(ACCESSOR_INFO_ACCESSOR)
|
||||
#undef ACCESSOR_INFO_ACCESSOR
|
||||
|
@ -64,7 +64,7 @@ MUTABLE_ROOT_LIST(ROOT_ACCESSOR)
|
||||
DATA_HANDLER_LIST(DATA_HANDLER_MAP_ACCESSOR)
|
||||
#undef DATA_HANDLER_MAP_ACCESSOR
|
||||
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName, ...) \
|
||||
AccessorInfo* Heap::accessor_name##_accessor() { \
|
||||
return AccessorInfo::cast(roots_[k##AccessorName##AccessorRootIndex]); \
|
||||
}
|
||||
|
@ -325,7 +325,8 @@ class Heap {
|
||||
WELL_KNOWN_SYMBOL_LIST(DECL)
|
||||
#undef DECL
|
||||
|
||||
#define DECL(accessor_name, AccessorName) k##AccessorName##AccessorRootIndex,
|
||||
#define DECL(accessor_name, AccessorName, ...) \
|
||||
k##AccessorName##AccessorRootIndex,
|
||||
ACCESSOR_INFO_LIST(DECL)
|
||||
#undef DECL
|
||||
|
||||
@ -821,7 +822,7 @@ class Heap {
|
||||
DATA_HANDLER_LIST(DATA_HANDLER_MAP_ACCESSOR)
|
||||
#undef DATA_HANDLER_MAP_ACCESSOR
|
||||
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, AccessorName) \
|
||||
#define ACCESSOR_INFO_ACCESSOR(accessor_name, ...) \
|
||||
inline AccessorInfo* accessor_name##_accessor();
|
||||
ACCESSOR_INFO_LIST(ACCESSOR_INFO_ACCESSOR)
|
||||
#undef ACCESSOR_INFO_ACCESSOR
|
||||
|
@ -901,16 +901,19 @@ void Heap::CreateInternalAccessorInfoObjects() {
|
||||
HandleScope scope(isolate);
|
||||
Handle<AccessorInfo> acessor_info;
|
||||
|
||||
#define INIT_ACCESSOR_INFO(accessor_name, AccessorName) \
|
||||
#define INIT_ACCESSOR_INFO(accessor_name, AccessorName, ...) \
|
||||
acessor_info = Accessors::Make##AccessorName##Info(isolate); \
|
||||
roots_[k##AccessorName##AccessorRootIndex] = *acessor_info;
|
||||
ACCESSOR_INFO_LIST(INIT_ACCESSOR_INFO)
|
||||
#undef INIT_ACCESSOR_INFO
|
||||
|
||||
#define INIT_SIDE_EFFECT_FLAG(AccessorName) \
|
||||
#define INIT_SIDE_EFFECT_FLAG(accessor_name, AccessorName, GetterType, \
|
||||
SetterType) \
|
||||
AccessorInfo::cast(roots_[k##AccessorName##AccessorRootIndex]) \
|
||||
->set_has_no_side_effect(true);
|
||||
SIDE_EFFECT_FREE_ACCESSOR_INFO_LIST(INIT_SIDE_EFFECT_FLAG)
|
||||
->set_getter_side_effect_type(SideEffectType::GetterType); \
|
||||
AccessorInfo::cast(roots_[k##AccessorName##AccessorRootIndex]) \
|
||||
->set_setter_side_effect_type(SideEffectType::SetterType);
|
||||
ACCESSOR_INFO_LIST(INIT_SIDE_EFFECT_FLAG)
|
||||
#undef INIT_SIDE_EFFECT_FLAG
|
||||
}
|
||||
|
||||
|
@ -59,8 +59,22 @@ BIT_FIELD_ACCESSORS(AccessorInfo, flags, is_special_data_property,
|
||||
BIT_FIELD_ACCESSORS(AccessorInfo, flags, replace_on_access,
|
||||
AccessorInfo::ReplaceOnAccessBit)
|
||||
BIT_FIELD_ACCESSORS(AccessorInfo, flags, is_sloppy, AccessorInfo::IsSloppyBit)
|
||||
BIT_FIELD_ACCESSORS(AccessorInfo, flags, has_no_side_effect,
|
||||
AccessorInfo::HasNoSideEffectBit)
|
||||
BIT_FIELD_ACCESSORS(AccessorInfo, flags, getter_side_effect_type,
|
||||
AccessorInfo::GetterSideEffectTypeBits)
|
||||
|
||||
SideEffectType AccessorInfo::setter_side_effect_type() const {
|
||||
return SetterSideEffectTypeBits::decode(flags());
|
||||
}
|
||||
|
||||
void AccessorInfo::set_setter_side_effect_type(SideEffectType value) {
|
||||
// We do not support describing setters as having no side effect, since
|
||||
// calling set accessors must go through a store bytecode. Store bytecodes
|
||||
// support checking receivers for temporary objects, but still expect
|
||||
// the receiver to be written to.
|
||||
CHECK_NE(value, SideEffectType::kHasNoSideEffect);
|
||||
set_flags(SetterSideEffectTypeBits::update(flags(), value));
|
||||
}
|
||||
|
||||
BIT_FIELD_ACCESSORS(AccessorInfo, flags, initial_property_attributes,
|
||||
AccessorInfo::InitialAttributesBits)
|
||||
|
||||
|
@ -48,7 +48,12 @@ class AccessorInfo : public Struct {
|
||||
DECL_BOOLEAN_ACCESSORS(is_special_data_property)
|
||||
DECL_BOOLEAN_ACCESSORS(replace_on_access)
|
||||
DECL_BOOLEAN_ACCESSORS(is_sloppy)
|
||||
DECL_BOOLEAN_ACCESSORS(has_no_side_effect)
|
||||
|
||||
inline SideEffectType getter_side_effect_type() const;
|
||||
inline void set_getter_side_effect_type(SideEffectType type);
|
||||
|
||||
inline SideEffectType setter_side_effect_type() const;
|
||||
inline void set_setter_side_effect_type(SideEffectType type);
|
||||
|
||||
// The property attributes used when an API object template is instantiated
|
||||
// for the first time. Changing of this value afterwards does not affect
|
||||
@ -95,7 +100,9 @@ class AccessorInfo : public Struct {
|
||||
V(IsSpecialDataPropertyBit, bool, 1, _) \
|
||||
V(IsSloppyBit, bool, 1, _) \
|
||||
V(ReplaceOnAccessBit, bool, 1, _) \
|
||||
V(HasNoSideEffectBit, bool, 1, _) \
|
||||
V(GetterSideEffectTypeBits, SideEffectType, 2, _) \
|
||||
/* We could save a bit from setter side-effect type, if necessary */ \
|
||||
V(SetterSideEffectTypeBits, SideEffectType, 2, _) \
|
||||
V(InitialAttributesBits, PropertyAttributes, 3, _)
|
||||
|
||||
DEFINE_BIT_FIELDS(ACCESSOR_INFO_FLAGS_BIT_FIELDS)
|
||||
|
@ -1935,8 +1935,7 @@ const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
|
||||
PUBLIC_SYMBOL_LIST(SYMBOL_NAME)
|
||||
WELL_KNOWN_SYMBOL_LIST(SYMBOL_NAME)
|
||||
#undef SYMBOL_NAME
|
||||
#define ACCESSOR_NAME(accessor_name, AccessorName) \
|
||||
NAME_ENTRY(accessor_name##_accessor)
|
||||
#define ACCESSOR_NAME(accessor_name, ...) NAME_ENTRY(accessor_name##_accessor)
|
||||
ACCESSOR_INFO_LIST(ACCESSOR_NAME)
|
||||
#undef ACCESSOR_NAME
|
||||
#undef NAME_ENTRY
|
||||
|
@ -240,8 +240,12 @@ static void Getter(v8::Local<v8::Name> name,
|
||||
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) {}
|
||||
static int set_accessor_call_count = 0;
|
||||
|
||||
static void Setter(v8::Local<v8::Name> name, v8::Local<v8::Value> value,
|
||||
const v8::PropertyCallbackInfo<void>& info) {
|
||||
set_accessor_call_count++;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Re-declaration of non-configurable accessors should throw.
|
||||
@ -297,6 +301,64 @@ TEST(AccessorSetHasNoSideEffect) {
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
CHECK_EQ(0, set_accessor_call_count);
|
||||
}
|
||||
|
||||
// Set accessors can be whitelisted as side-effect-free via SetAccessor.
|
||||
TEST(SetAccessorSetSideEffectReceiverCheck1) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
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(env.local(), v8_str("foo"), Getter, Setter,
|
||||
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
|
||||
v8::PropertyAttribute::None,
|
||||
v8::SideEffectType::kHasNoSideEffect,
|
||||
v8::SideEffectType::kHasSideEffectToReceiver)
|
||||
.ToChecked();
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), true)
|
||||
.ToLocalChecked()
|
||||
->Equals(env.local(), v8_str("return value"))
|
||||
.FromJust());
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo = 1"), true)
|
||||
.IsEmpty());
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_EQ(0, set_accessor_call_count);
|
||||
}
|
||||
|
||||
static void ConstructCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
}
|
||||
|
||||
TEST(SetAccessorSetSideEffectReceiverCheck2) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(
|
||||
isolate, ConstructCallback, v8::Local<v8::Value>(),
|
||||
v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
|
||||
v8::SideEffectType::kHasNoSideEffect);
|
||||
templ->InstanceTemplate()->SetAccessor(
|
||||
v8_str("bar"), Getter, Setter, v8::Local<v8::Value>(),
|
||||
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
|
||||
v8::Local<v8::AccessorSignature>(),
|
||||
v8::SideEffectType::kHasSideEffectToReceiver,
|
||||
v8::SideEffectType::kHasSideEffectToReceiver);
|
||||
CHECK(env->Global()
|
||||
->Set(env.local(), v8_str("f"),
|
||||
templ->GetFunction(env.local()).ToLocalChecked())
|
||||
.FromJust());
|
||||
CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f().bar"), true)
|
||||
.ToLocalChecked()
|
||||
->Equals(env.local(), v8_str("return value"))
|
||||
.FromJust());
|
||||
v8::debug::EvaluateGlobal(isolate, v8_str("new f().bar = 1"), true)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(1, set_accessor_call_count);
|
||||
}
|
||||
|
||||
// Accessors can be whitelisted as side-effect-free via SetNativeDataProperty.
|
||||
|
@ -124,7 +124,7 @@ TEST(TestAccessorInfosNotReadOnly) {
|
||||
Factory* factory = CcTest::i_isolate()->factory();
|
||||
Heap* heap = CcTest::i_isolate()->heap();
|
||||
|
||||
#define TEST_ROOT(name, AccessorName) CHECK_NOT_IN_RO_SPACE(name##_accessor)
|
||||
#define TEST_ROOT(name, ...) CHECK_NOT_IN_RO_SPACE(name##_accessor)
|
||||
ACCESSOR_INFO_LIST(TEST_ROOT)
|
||||
#undef TEST_ROOT
|
||||
}
|
||||
|
@ -50,8 +50,7 @@ success([1], `return_array_use_spread([1])`);
|
||||
// CallAccessorSetter
|
||||
var array = [1,2,3];
|
||||
fail(`array.length = 2`);
|
||||
// TODO(7515): this one should be side effect free
|
||||
fail(`[1,2,3].length = 2`);
|
||||
success(2, `[1,2,3].length = 2`);
|
||||
|
||||
// StaDataPropertyInLiteral
|
||||
function return_literal_with_data_property(a) {
|
||||
|
@ -9,6 +9,7 @@ let a = 1;
|
||||
var object = { property : 2,
|
||||
get getter() { return 3; }
|
||||
};
|
||||
var string0 = new String("string");
|
||||
var string1 = { toString() { return "x"; } };
|
||||
var string2 = { toString() { print("x"); return "x"; } };
|
||||
var array = [4, 5];
|
||||
@ -19,6 +20,9 @@ function set_a() { a = 2; }
|
||||
function get_a() { return a; }
|
||||
var bound = get_a.bind(0);
|
||||
|
||||
function return_arg0() { return return_arg0.arguments[0]; }
|
||||
function return_caller_name() { return return_caller_name.caller.name; }
|
||||
|
||||
var global_eval = eval;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
@ -32,6 +36,7 @@ function listener(event, exec_state, event_data, data) {
|
||||
assertThrows(() => exec_state.frame(0).evaluate(source, true),
|
||||
EvalError);
|
||||
}
|
||||
|
||||
// Simple test.
|
||||
success(3, "1 + 2");
|
||||
// Dymanic load.
|
||||
@ -62,8 +67,9 @@ function listener(event, exec_state, event_data, data) {
|
||||
success("set_a", "set_a.name");
|
||||
success(0, "bound.length");
|
||||
success("bound get_a", "bound.name");
|
||||
success(1, "return_arg0(1)");
|
||||
success("f", "(function f() { return return_caller_name() })()");
|
||||
// Non-evaluated call.
|
||||
success("abc", "['abc'].join('foo')");
|
||||
// Constructed literals.
|
||||
success([1], "[1]");
|
||||
success({x: 1}, "({x: 1})");
|
||||
@ -82,13 +88,25 @@ function listener(event, exec_state, event_data, data) {
|
||||
fail("try { set_a() } catch (e) {}");
|
||||
// Test that call to set accessor fails.
|
||||
fail("array.length = 4");
|
||||
fail("'x'.length = 1");
|
||||
fail("set_a.name = 'set_b'");
|
||||
fail("set_a.length = 1");
|
||||
fail("bound.name = 'bound'");
|
||||
fail("bound.length = 1");
|
||||
fail("set_a.prototype = null");
|
||||
// Test that call to non-whitelisted get accessor fails.
|
||||
fail("error.stack");
|
||||
// Call to set accessors with receiver check.
|
||||
success(1, "[].length = 1");
|
||||
success(1, "'x'.length = 1");
|
||||
fail("string0.length = 1");
|
||||
success(1, "(new String('abc')).length = 1");
|
||||
success("g", "(function(){}).name = 'g'");
|
||||
success(1, "(function(){}).length = 1");
|
||||
success("g", "get_a.bind(0).name = 'g'");
|
||||
success(1, "get_a.bind(0).length = 1");
|
||||
success(null, "(function(){}).prototype = null");
|
||||
success(true, "(new Error()).stack.length > 1");
|
||||
success("a", "(new Error()).stack = 'a'");
|
||||
// Eval is not allowed.
|
||||
fail("eval('Math.sin(1)')");
|
||||
fail("eval('exception = 1')");
|
||||
|
Loading…
Reference in New Issue
Block a user