Commit b4942eb3 authored by Philip Pfaffe's avatar Philip Pfaffe Committed by V8 LUCI CQ

Reland "[ic] Fix handling of API properties with side effects"

This is a reland of 0ce36e7d

The reland includes two fixes:
- Move the EvaluateGlobalForTesting into libv8 to avoid linkage issues
  and to avoid having to export ThreadLocalTop symbols.
- Give the ExecutionMode enum a uint8_t backing type to avoid endianess
  issues.

Original change's description:
> [ic] Fix handling of API properties with side effects
>
> DebugEvaluate can evaluate expressions in side-effect-free mode, where
> any operation that would cause observable side effects throws an
> exception. Currently, when accessors are backed by callbacks, it's
> possible that ICs call those accessors directly, bypassing the
> side-effect checks. This CL introduces a bailouts to runtime in those
> cases.
>
> Fixed: chromium:1201781
> Also-By: ishell@chromium.org, pfaffe@chromium.org
> Change-Id: Ie53bfb2bff7b3420f2b27091e8df6723382cf53c
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2857634
> Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#74507}

Fixes: v8:11761
Change-Id: I58cde8bd11ba0fc9d83adc19fa87733628ab6c13
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2891829Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74602}
parent 6a5cb689
...@@ -8938,9 +8938,9 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8938,9 +8938,9 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
{ {
Label slow_load(this, Label::kDeferred); Label slow_load(this, Label::kDeferred);
var_value = CallGetterIfAccessor(var_value.value(), object, var_value = CallGetterIfAccessor(
var_details.value(), context, var_value.value(), object, var_details.value(), context, object,
object, &slow_load, kCallJSGetter); next_key, &slow_load, kCallJSGetter);
Goto(&callback); Goto(&callback);
BIND(&slow_load); BIND(&slow_load);
...@@ -9392,8 +9392,8 @@ template void CodeStubAssembler::LoadPropertyFromDictionary( ...@@ -9392,8 +9392,8 @@ template void CodeStubAssembler::LoadPropertyFromDictionary(
// result of the getter call. // result of the getter call.
TNode<Object> CodeStubAssembler::CallGetterIfAccessor( TNode<Object> CodeStubAssembler::CallGetterIfAccessor(
TNode<Object> value, TNode<HeapObject> holder, TNode<Uint32T> details, TNode<Object> value, TNode<HeapObject> holder, TNode<Uint32T> details,
TNode<Context> context, TNode<Object> receiver, Label* if_bailout, TNode<Context> context, TNode<Object> receiver, TNode<Object> name,
GetOwnPropertyMode mode) { Label* if_bailout, GetOwnPropertyMode mode) {
TVARIABLE(Object, var_value, value); TVARIABLE(Object, var_value, value);
Label done(this), if_accessor_info(this, Label::kDeferred); Label done(this), if_accessor_info(this, Label::kDeferred);
...@@ -9421,13 +9421,16 @@ TNode<Object> CodeStubAssembler::CallGetterIfAccessor( ...@@ -9421,13 +9421,16 @@ TNode<Object> CodeStubAssembler::CallGetterIfAccessor(
BIND(&if_callable); BIND(&if_callable);
{ {
// Call the accessor. // Call the accessor. No need to check side-effect mode here, since it
// will be checked later in DebugOnFunctionCall.
var_value = Call(context, getter, receiver); var_value = Call(context, getter, receiver);
Goto(&done); Goto(&done);
} }
BIND(&if_function_template_info); BIND(&if_function_template_info);
{ {
Label runtime(this, Label::kDeferred);
GotoIf(IsSideEffectFreeDebuggingActive(), &runtime);
TNode<HeapObject> cached_property_name = LoadObjectField<HeapObject>( TNode<HeapObject> cached_property_name = LoadObjectField<HeapObject>(
getter, FunctionTemplateInfo::kCachedPropertyNameOffset); getter, FunctionTemplateInfo::kCachedPropertyNameOffset);
GotoIfNot(IsTheHole(cached_property_name), if_bailout); GotoIfNot(IsTheHole(cached_property_name), if_bailout);
...@@ -9438,6 +9441,13 @@ TNode<Object> CodeStubAssembler::CallGetterIfAccessor( ...@@ -9438,6 +9441,13 @@ TNode<Object> CodeStubAssembler::CallGetterIfAccessor(
Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver, Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver,
creation_context, getter, IntPtrConstant(0), receiver); creation_context, getter, IntPtrConstant(0), receiver);
Goto(&done); Goto(&done);
BIND(&runtime);
{
var_value = CallRuntime(Runtime::kGetProperty, context, holder, name,
receiver);
Goto(&done);
}
} }
} else { } else {
Goto(&done); Goto(&done);
...@@ -9572,7 +9582,7 @@ void CodeStubAssembler::TryGetOwnProperty( ...@@ -9572,7 +9582,7 @@ void CodeStubAssembler::TryGetOwnProperty(
} }
TNode<Object> value = TNode<Object> value =
CallGetterIfAccessor(var_value->value(), object, var_details->value(), CallGetterIfAccessor(var_value->value(), object, var_details->value(),
context, receiver, if_bailout, mode); context, receiver, unique_name, if_bailout, mode);
*var_value = value; *var_value = value;
Goto(if_found_value); Goto(if_found_value);
} }
...@@ -14027,6 +14037,20 @@ TNode<BoolT> CodeStubAssembler::IsDebugActive() { ...@@ -14027,6 +14037,20 @@ TNode<BoolT> CodeStubAssembler::IsDebugActive() {
return Word32NotEqual(is_debug_active, Int32Constant(0)); return Word32NotEqual(is_debug_active, Int32Constant(0));
} }
TNode<BoolT> CodeStubAssembler::IsSideEffectFreeDebuggingActive() {
TNode<Uint8T> debug_execution_mode = Load<Uint8T>(ExternalConstant(
ExternalReference::debug_execution_mode_address(isolate())));
TNode<BoolT> is_active =
Word32Equal(debug_execution_mode,
Int32Constant(DebugInfo::ExecutionMode::kSideEffects));
#ifdef DEBUG
CSA_ASSERT(this, Word32Or(IsDebugActive(), Word32BinaryNot(is_active)));
#endif
return is_active;
}
TNode<BoolT> CodeStubAssembler::HasAsyncEventDelegate() { TNode<BoolT> CodeStubAssembler::HasAsyncEventDelegate() {
const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant( const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant(
ExternalReference::async_event_delegate_address(isolate()))); ExternalReference::async_event_delegate_address(isolate())));
......
...@@ -3481,6 +3481,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -3481,6 +3481,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Debug helpers // Debug helpers
TNode<BoolT> IsDebugActive(); TNode<BoolT> IsDebugActive();
TNode<BoolT> IsSideEffectFreeDebuggingActive();
// JSArrayBuffer helpers // JSArrayBuffer helpers
TNode<RawPtrT> LoadJSArrayBufferBackingStorePtr( TNode<RawPtrT> LoadJSArrayBufferBackingStorePtr(
...@@ -3748,12 +3749,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -3748,12 +3749,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
const ForEachKeyValueFunction& body, const ForEachKeyValueFunction& body,
Label* bailout); Label* bailout);
TNode<Object> CallGetterIfAccessor(TNode<Object> value, TNode<Object> CallGetterIfAccessor(
TNode<HeapObject> holder, TNode<Object> value, TNode<HeapObject> holder, TNode<Uint32T> details,
TNode<Uint32T> details, TNode<Context> context, TNode<Object> receiver, TNode<Object> name,
TNode<Context> context, Label* if_bailout, GetOwnPropertyMode mode = kCallJSGetter);
TNode<Object> receiver, Label* if_bailout,
GetOwnPropertyMode mode = kCallJSGetter);
TNode<IntPtrT> TryToIntptr(TNode<Object> key, Label* if_not_intptr, TNode<IntPtrT> TryToIntptr(TNode<Object> key, Label* if_not_intptr,
TVariable<Int32T>* var_instance_type = nullptr); TVariable<Int32T>* var_instance_type = nullptr);
......
...@@ -43,6 +43,22 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, ...@@ -43,6 +43,22 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<String> source, Handle<String> source,
debug::EvaluateGlobalMode mode, debug::EvaluateGlobalMode mode,
REPLMode repl_mode) { REPLMode repl_mode) {
Handle<SharedFunctionInfo> shared_info;
if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) {
return MaybeHandle<Object>();
}
Handle<NativeContext> context = isolate->native_context();
Handle<JSFunction> fun =
Factory::JSFunctionBuilder{isolate, shared_info, context}.Build();
return Global(isolate, fun, mode, repl_mode);
}
MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<JSFunction> function,
debug::EvaluateGlobalMode mode,
REPLMode repl_mode) {
// Disable breaks in side-effect free mode. // Disable breaks in side-effect free mode.
DisableBreak disable_break_scope( DisableBreak disable_break_scope(
isolate->debug(), isolate->debug(),
...@@ -50,19 +66,14 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, ...@@ -50,19 +66,14 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
mode == mode ==
debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect); debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect);
Handle<SharedFunctionInfo> shared_info; Handle<NativeContext> context = isolate->native_context();
if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) { CHECK_EQ(function->native_context(), *context);
return MaybeHandle<Object>();
}
Handle<Context> context = isolate->native_context();
Handle<JSFunction> fun =
Factory::JSFunctionBuilder{isolate, shared_info, context}.Build();
if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
isolate->debug()->StartSideEffectCheckMode(); isolate->debug()->StartSideEffectCheckMode();
} }
MaybeHandle<Object> result = Execution::Call( MaybeHandle<Object> result = Execution::Call(
isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0, isolate, function, Handle<JSObject>(context->global_proxy(), isolate), 0,
nullptr); nullptr);
if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
isolate->debug()->StopSideEffectCheckMode(); isolate->debug()->StopSideEffectCheckMode();
......
...@@ -27,6 +27,10 @@ class DebugEvaluate : public AllStatic { ...@@ -27,6 +27,10 @@ class DebugEvaluate : public AllStatic {
debug::EvaluateGlobalMode mode, debug::EvaluateGlobalMode mode,
REPLMode repl_mode = REPLMode::kNo); REPLMode repl_mode = REPLMode::kNo);
static V8_EXPORT_PRIVATE MaybeHandle<Object> Global(
Isolate* isolate, Handle<JSFunction> function,
debug::EvaluateGlobalMode mode, REPLMode repl_mode = REPLMode::kNo);
// Evaluate a piece of JavaScript in the context of a stack frame for // Evaluate a piece of JavaScript in the context of a stack frame for
// debugging. Things that need special attention are: // debugging. Things that need special attention are:
// - Parameters and stack-allocated locals need to be materialized. Altered // - Parameters and stack-allocated locals need to be materialized. Altered
......
...@@ -951,6 +951,21 @@ MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate, ...@@ -951,6 +951,21 @@ MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate,
RETURN_ESCAPED(result); RETURN_ESCAPED(result);
} }
v8::MaybeLocal<v8::Value> EvaluateGlobalForTesting(
v8::Isolate* isolate, v8::Local<v8::Script> function,
v8::debug::EvaluateGlobalMode mode, bool repl) {
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(internal_isolate, Value);
i::REPLMode repl_mode = repl ? i::REPLMode::kYes : i::REPLMode::kNo;
Local<Value> result;
has_pending_exception = !ToLocal<Value>(
i::DebugEvaluate::Global(internal_isolate, Utils::OpenHandle(*function),
mode, repl_mode),
&result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
void QueryObjects(v8::Local<v8::Context> v8_context, void QueryObjects(v8::Local<v8::Context> v8_context,
QueryObjectPredicate* predicate, QueryObjectPredicate* predicate,
PersistentValueVector<v8::Object>* objects) { PersistentValueVector<v8::Object>* objects) {
......
...@@ -527,6 +527,10 @@ V8_EXPORT_PRIVATE v8::MaybeLocal<v8::Value> EvaluateGlobal( ...@@ -527,6 +527,10 @@ V8_EXPORT_PRIVATE v8::MaybeLocal<v8::Value> EvaluateGlobal(
v8::Isolate* isolate, v8::Local<v8::String> source, EvaluateGlobalMode mode, v8::Isolate* isolate, v8::Local<v8::String> source, EvaluateGlobalMode mode,
bool repl_mode = false); bool repl_mode = false);
V8_EXPORT_PRIVATE v8::MaybeLocal<v8::Value> EvaluateGlobalForTesting(
v8::Isolate* isolate, v8::Local<v8::Script> function,
v8::debug::EvaluateGlobalMode mode, bool repl);
int GetDebuggingId(v8::Local<v8::Function> function); int GetDebuggingId(v8::Local<v8::Function> function);
bool SetFunctionBreakpoint(v8::Local<v8::Function> function, bool SetFunctionBreakpoint(v8::Local<v8::Function> function,
......
...@@ -777,6 +777,34 @@ void FixedArray::FixedArrayPrint(std::ostream& os) { ...@@ -777,6 +777,34 @@ void FixedArray::FixedArrayPrint(std::ostream& os) {
PrintFixedArrayWithHeader(os, *this, "FixedArray"); PrintFixedArrayWithHeader(os, *this, "FixedArray");
} }
namespace {
const char* SideEffectType2String(SideEffectType type) {
switch (type) {
case SideEffectType::kHasSideEffect:
return "kHasSideEffect";
case SideEffectType::kHasNoSideEffect:
return "kHasNoSideEffect";
case SideEffectType::kHasSideEffectToReceiver:
return "kHasSideEffectToReceiver";
}
}
} // namespace
void AccessorInfo::AccessorInfoPrint(std::ostream& os) {
TorqueGeneratedAccessorInfo<AccessorInfo, Struct>::AccessorInfoPrint(os);
os << " - all_can_read: " << all_can_read();
os << "\n - all_can_write: " << all_can_write();
os << "\n - is_special_data_property: " << is_special_data_property();
os << "\n - is_sloppy: " << is_sloppy();
os << "\n - replace_on_access: " << replace_on_access();
os << "\n - getter_side_effect_type: "
<< SideEffectType2String(getter_side_effect_type());
os << "\n - setter_side_effect_type: "
<< SideEffectType2String(setter_side_effect_type());
os << "\n - initial_attributes: " << initial_property_attributes();
os << '\n';
}
namespace { namespace {
void PrintContextWithHeader(std::ostream& os, Context context, void PrintContextWithHeader(std::ostream& os, Context context,
const char* type) { const char* type) {
...@@ -2777,6 +2805,22 @@ V8_EXPORT_PRIVATE extern void _v8_internal_Print_Object(void* object) { ...@@ -2777,6 +2805,22 @@ V8_EXPORT_PRIVATE extern void _v8_internal_Print_Object(void* object) {
GetObjectFromRaw(object).Print(); GetObjectFromRaw(object).Print();
} }
V8_EXPORT_PRIVATE extern void _v8_internal_Print_LoadHandler(void* object) {
#ifdef OBJECT_PRINT
i::StdoutStream os;
i::LoadHandler::PrintHandler(GetObjectFromRaw(object), os);
os << std::flush;
#endif
}
V8_EXPORT_PRIVATE extern void _v8_internal_Print_StoreHandler(void* object) {
#ifdef OBJECT_PRINT
i::StdoutStream os;
i::StoreHandler::PrintHandler(GetObjectFromRaw(object), os);
os << std::flush;
#endif
}
V8_EXPORT_PRIVATE extern void _v8_internal_Print_Code(void* object) { V8_EXPORT_PRIVATE extern void _v8_internal_Print_Code(void* object) {
i::Address address = reinterpret_cast<i::Address>(object); i::Address address = reinterpret_cast<i::Address>(object);
i::Isolate* isolate = i::Isolate::Current(); i::Isolate* isolate = i::Isolate::Current();
......
...@@ -594,7 +594,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( ...@@ -594,7 +594,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
properties, var_name_index.value(), &var_details, &var_value); properties, var_name_index.value(), &var_details, &var_value);
TNode<Object> value = CallGetterIfAccessor( TNode<Object> value = CallGetterIfAccessor(
var_value.value(), CAST(holder), var_details.value(), p->context(), var_value.value(), CAST(holder), var_details.value(), p->context(),
p->receiver(), miss); p->receiver(), p->name(), miss);
exit_point->Return(value); exit_point->Return(value);
} }
} }
...@@ -614,11 +614,17 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( ...@@ -614,11 +614,17 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
} }
BIND(&native_data_property); BIND(&native_data_property);
{
GotoIf(IsSideEffectFreeDebuggingActive(), &slow);
HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point); HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point);
}
BIND(&api_getter); BIND(&api_getter);
HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler), handler_kind, {
exit_point); GotoIf(IsSideEffectFreeDebuggingActive(), &slow);
HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler),
handler_kind, exit_point);
}
BIND(&proxy); BIND(&proxy);
{ {
...@@ -678,7 +684,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( ...@@ -678,7 +684,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
GotoIf(IsTheHole(value), miss); GotoIf(IsTheHole(value), miss);
exit_point->Return(CallGetterIfAccessor(value, CAST(holder), details, exit_point->Return(CallGetterIfAccessor(value, CAST(holder), details,
p->context(), p->receiver(), miss)); p->context(), p->receiver(),
p->name(), miss));
} }
BIND(&interceptor); BIND(&interceptor);
...@@ -964,7 +971,7 @@ void AccessorAssembler::HandleLoadICProtoHandler( ...@@ -964,7 +971,7 @@ void AccessorAssembler::HandleLoadICProtoHandler(
properties, name_index, &var_details, &var_value); properties, name_index, &var_details, &var_value);
TNode<Object> value = CallGetterIfAccessor( TNode<Object> value = CallGetterIfAccessor(
var_value.value(), CAST(var_holder->value()), var_details.value(), var_value.value(), CAST(var_holder->value()), var_details.value(),
p->context(), p->receiver(), miss); p->context(), p->receiver(), p->name(), miss);
exit_point->Return(value); exit_point->Return(value);
} }
}, },
...@@ -1737,9 +1744,12 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -1737,9 +1744,12 @@ void AccessorAssembler::HandleStoreICProtoHandler(
Goto(&store); Goto(&store);
BIND(&store); BIND(&store);
{
GotoIf(IsSideEffectFreeDebuggingActive(), &if_slow);
TNode<IntPtrT> argc = IntPtrConstant(1); TNode<IntPtrT> argc = IntPtrConstant(1);
Return(CallApiCallback(context, callback, argc, data, api_holder.value(), Return(CallApiCallback(context, callback, argc, data,
p->receiver(), p->value())); api_holder.value(), p->receiver(), p->value()));
}
} }
BIND(&if_store_global_proxy); BIND(&if_store_global_proxy);
...@@ -2561,7 +2571,7 @@ void AccessorAssembler::GenericPropertyLoad( ...@@ -2561,7 +2571,7 @@ void AccessorAssembler::GenericPropertyLoad(
{ {
TNode<Object> value = CallGetterIfAccessor( TNode<Object> value = CallGetterIfAccessor(
var_value.value(), lookup_start_object, var_details.value(), var_value.value(), lookup_start_object, var_details.value(),
p->context(), p->receiver(), slow); p->context(), p->receiver(), p->name(), slow);
IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1); IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1);
Return(value); Return(value);
} }
......
...@@ -64,6 +64,8 @@ class AccessorInfo : public TorqueGeneratedAccessorInfo<AccessorInfo, Struct> { ...@@ -64,6 +64,8 @@ class AccessorInfo : public TorqueGeneratedAccessorInfo<AccessorInfo, Struct> {
static int AppendUnique(Isolate* isolate, Handle<Object> descriptors, static int AppendUnique(Isolate* isolate, Handle<Object> descriptors,
Handle<FixedArray> array, int valid_descriptors); Handle<FixedArray> array, int valid_descriptors);
DECL_PRINTER(AccessorInfo)
private: private:
inline bool HasExpectedReceiverType(); inline bool HasExpectedReceiverType();
......
...@@ -37,7 +37,10 @@ class DebugInfo : public TorqueGeneratedDebugInfo<DebugInfo, Struct> { ...@@ -37,7 +37,10 @@ class DebugInfo : public TorqueGeneratedDebugInfo<DebugInfo, Struct> {
// --- Debug execution --- // --- Debug execution ---
// ----------------------- // -----------------------
enum ExecutionMode { kBreakpoints = 0, kSideEffects = kDebugExecutionMode }; enum ExecutionMode : uint8_t {
kBreakpoints = 0,
kSideEffects = kDebugExecutionMode
};
// Returns current debug execution mode. Debug execution mode defines by // Returns current debug execution mode. Debug execution mode defines by
// applied to bytecode patching. False for breakpoints, true for side effect // applied to bytecode patching. False for breakpoints, true for side effect
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#include "include/v8.h" #include "include/v8.h"
#include "src/api/api.h" #include "src/api/api-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
namespace i = v8::internal; namespace i = v8::internal;
...@@ -250,6 +250,8 @@ static void Setter(v8::Local<v8::Name> name, v8::Local<v8::Value> value, ...@@ -250,6 +250,8 @@ static void Setter(v8::Local<v8::Name> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) { const v8::PropertyCallbackInfo<void>& info) {
set_accessor_call_count++; set_accessor_call_count++;
} }
static void EmptyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {}
} // namespace } // namespace
// Re-declaration of non-configurable accessors should throw. // Re-declaration of non-configurable accessors should throw.
...@@ -276,6 +278,175 @@ TEST(RedeclareAccessor) { ...@@ -276,6 +278,175 @@ TEST(RedeclareAccessor) {
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
class NoopDelegate : public v8::debug::DebugDelegate {};
static void CheckSideEffectFreeAccesses(v8::Isolate* isolate,
v8::Local<v8::String> call_getter,
v8::Local<v8::String> call_setter) {
const int kIterationsCountForICProgression = 20;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::Script> func = v8_compile(call_getter);
// Check getter. Run enough number of times to ensure IC creates data handler.
for (int i = 0; i < kIterationsCountForICProgression; i++) {
v8::TryCatch try_catch(isolate);
CHECK(EvaluateGlobalForTesting(
isolate, func,
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect,
true)
.IsEmpty());
CHECK(try_catch.HasCaught());
// Ensure that IC state progresses.
CHECK(!func->Run(context).IsEmpty());
}
func = v8_compile(call_setter);
// Check setter. Run enough number of times to ensure IC creates data handler.
for (int i = 0; i < kIterationsCountForICProgression; i++) {
v8::TryCatch try_catch(isolate);
CHECK(EvaluateGlobalForTesting(
isolate, func,
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect,
true)
.IsEmpty());
CHECK(try_catch.HasCaught());
// Ensure that IC state progresses.
CHECK(!func->Run(context).IsEmpty());
}
}
TEST(AccessorsWithSideEffects) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
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());
v8::Local<v8::FunctionTemplate> templ_with_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasSideEffect);
v8::Local<v8::FunctionTemplate> templ_no_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasNoSideEffect);
// Install non-native properties with side effects
obj->SetAccessorProperty(
v8_str("get"),
templ_with_sideffect->GetFunction(context).ToLocalChecked(), {},
v8::PropertyAttribute::None, v8::AccessControl::DEFAULT);
obj->SetAccessorProperty(
v8_str("set"), templ_no_sideffect->GetFunction(context).ToLocalChecked(),
templ_with_sideffect->GetFunction(context).ToLocalChecked(),
v8::PropertyAttribute::None, v8::AccessControl::DEFAULT);
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"),
v8_str("obj.set = 123;"));
}
TEST(TemplateAccessorsWithSideEffects) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::FunctionTemplate> templ_with_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasSideEffect);
v8::Local<v8::FunctionTemplate> templ_no_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessorProperty(v8_str("get"), templ_with_sideffect);
templ->SetAccessorProperty(v8_str("set"), templ_no_sideffect,
templ_with_sideffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"),
v8_str("obj.set = 123;"));
}
TEST(NativeTemplateAccessorWithSideEffects) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("get"), Getter, nullptr, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::Local<v8::AccessorSignature>(),
v8::SideEffectType::kHasSideEffect);
templ->SetAccessor(v8_str("set"), Getter, Setter, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::Local<v8::AccessorSignature>(),
v8::SideEffectType::kHasNoSideEffect,
v8::SideEffectType::kHasSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"),
v8_str("obj.set = 123;"));
}
TEST(NativeAccessorsWithSideEffects) {
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());
// Install native data property with side effects.
obj->SetAccessor(context, v8_str("get"), Getter, nullptr,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None,
v8::SideEffectType::kHasSideEffect)
.ToChecked();
obj->SetAccessor(context, v8_str("set"), Getter, Setter,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect,
v8::SideEffectType::kHasSideEffect)
.ToChecked();
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"),
v8_str("obj.set = 123;"));
}
// Accessors can be allowlisted as side-effect-free via SetAccessor. // Accessors can be allowlisted as side-effect-free via SetAccessor.
TEST(AccessorSetHasNoSideEffect) { TEST(AccessorSetHasNoSideEffect) {
LocalContext env; LocalContext env;
...@@ -283,6 +454,10 @@ TEST(AccessorSetHasNoSideEffect) { ...@@ -283,6 +454,10 @@ TEST(AccessorSetHasNoSideEffect) {
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
...@@ -323,6 +498,10 @@ TEST(SetAccessorSetSideEffectReceiverCheck1) { ...@@ -323,6 +498,10 @@ TEST(SetAccessorSetSideEffectReceiverCheck1) {
v8::Isolate* isolate = env->GetIsolate(); v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
...@@ -356,6 +535,10 @@ TEST(SetAccessorSetSideEffectReceiverCheck2) { ...@@ -356,6 +535,10 @@ TEST(SetAccessorSetSideEffectReceiverCheck2) {
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
i::FLAG_enable_one_shot_optimization = false; i::FLAG_enable_one_shot_optimization = false;
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New( v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(
isolate, ConstructCallback, v8::Local<v8::Value>(), isolate, ConstructCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
...@@ -390,6 +573,10 @@ TEST(AccessorSetNativeDataPropertyHasNoSideEffect) { ...@@ -390,6 +573,10 @@ TEST(AccessorSetNativeDataPropertyHasNoSideEffect) {
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
...@@ -429,6 +616,10 @@ TEST(AccessorSetLazyDataPropertyHasNoSideEffect) { ...@@ -429,6 +616,10 @@ TEST(AccessorSetLazyDataPropertyHasNoSideEffect) {
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
...@@ -466,6 +657,10 @@ TEST(ObjectTemplateSetAccessorHasNoSideEffect) { ...@@ -466,6 +657,10 @@ TEST(ObjectTemplateSetAccessorHasNoSideEffect) {
v8::Isolate* isolate = env->GetIsolate(); v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessor(v8_str("foo"), StringGetter); templ->SetAccessor(v8_str("foo"), StringGetter);
templ->SetAccessor( templ->SetAccessor(
...@@ -503,6 +698,10 @@ TEST(ObjectTemplateSetNativePropertyHasNoSideEffect) { ...@@ -503,6 +698,10 @@ TEST(ObjectTemplateSetNativePropertyHasNoSideEffect) {
v8::Isolate* isolate = env->GetIsolate(); v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("foo"), Getter); templ->SetNativeDataProperty(v8_str("foo"), Getter);
templ->SetNativeDataProperty( templ->SetNativeDataProperty(
...@@ -540,6 +739,10 @@ TEST(ObjectTemplateSetLazyPropertyHasNoSideEffect) { ...@@ -540,6 +739,10 @@ TEST(ObjectTemplateSetLazyPropertyHasNoSideEffect) {
v8::Isolate* isolate = env->GetIsolate(); v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetLazyDataProperty(v8_str("foo"), Getter); templ->SetLazyDataProperty(v8_str("foo"), Getter);
templ->SetLazyDataProperty(v8_str("foo2"), Getter, v8::Local<v8::Value>(), templ->SetLazyDataProperty(v8_str("foo2"), Getter, v8::Local<v8::Value>(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment