Commit 8437ed16 authored by Igor Sheludko's avatar Igor Sheludko Committed by V8 LUCI CQ

[runtime] Add interceptors side effects detector

This CL introduces SideEffectDetectorScope which requires explicit
allowlisting of cases when side effects are allowed after calling
interceptor callbacks.
Side effects are not allowed when the callback does not intercept
the request.
The side effects detector is not enabled yet, it will be enabled in
a follow-up CL.

Bug: chromium:1310062
Change-Id: I805764920ed016cb37390aef7bb02cbdf5f72846
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3641172Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80484}
parent ef77fe0f
...@@ -148,6 +148,17 @@ Handle<Object> FunctionCallbackArguments::Call(CallHandlerInfo handler) { ...@@ -148,6 +148,17 @@ Handle<Object> FunctionCallbackArguments::Call(CallHandlerInfo handler) {
return GetReturnValue<Object>(isolate); return GetReturnValue<Object>(isolate);
} }
PropertyCallbackArguments::~PropertyCallbackArguments(){
#ifdef DEBUG
// TODO(chromium:1310062): enable this check.
// if (javascript_execution_counter_) {
// CHECK_WITH_MSG(javascript_execution_counter_ ==
// isolate()->javascript_execution_counter(),
// "Unexpected side effect detected");
// }
#endif // DEBUG
}
Handle<JSObject> PropertyCallbackArguments::CallNamedEnumerator( Handle<JSObject> PropertyCallbackArguments::CallNamedEnumerator(
Handle<InterceptorInfo> interceptor) { Handle<InterceptorInfo> interceptor) {
DCHECK(interceptor->is_named()); DCHECK(interceptor->is_named());
...@@ -296,6 +307,10 @@ Handle<Object> PropertyCallbackArguments::CallAccessorGetter( ...@@ -296,6 +307,10 @@ Handle<Object> PropertyCallbackArguments::CallAccessorGetter(
Handle<AccessorInfo> info, Handle<Name> name) { Handle<AccessorInfo> info, Handle<Name> name) {
Isolate* isolate = this->isolate(); Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback); RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
AccessorNameGetterCallback f = AccessorNameGetterCallback f =
ToCData<AccessorNameGetterCallback>(info->getter()); ToCData<AccessorNameGetterCallback>(info->getter());
return BasicCallNamedGetterCallback(f, name, info, return BasicCallNamedGetterCallback(f, name, info,
...@@ -307,6 +322,10 @@ Handle<Object> PropertyCallbackArguments::CallAccessorSetter( ...@@ -307,6 +322,10 @@ Handle<Object> PropertyCallbackArguments::CallAccessorSetter(
Handle<Object> value) { Handle<Object> value) {
Isolate* isolate = this->isolate(); Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback); RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
AccessorNameSetterCallback f = AccessorNameSetterCallback f =
ToCData<AccessorNameSetterCallback>(accessor_info->setter()); ToCData<AccessorNameSetterCallback>(accessor_info->setter());
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, void, accessor_info, PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, void, accessor_info,
......
...@@ -12,7 +12,12 @@ namespace internal { ...@@ -12,7 +12,12 @@ namespace internal {
PropertyCallbackArguments::PropertyCallbackArguments( PropertyCallbackArguments::PropertyCallbackArguments(
Isolate* isolate, Object data, Object self, JSObject holder, Isolate* isolate, Object data, Object self, JSObject holder,
Maybe<ShouldThrow> should_throw) Maybe<ShouldThrow> should_throw)
: Super(isolate) { : Super(isolate)
#ifdef DEBUG
,
javascript_execution_counter_(isolate->javascript_execution_counter())
#endif // DEBUG
{
slot_at(T::kThisIndex).store(self); slot_at(T::kThisIndex).store(self);
slot_at(T::kHolderIndex).store(holder); slot_at(T::kHolderIndex).store(holder);
slot_at(T::kDataIndex).store(data); slot_at(T::kDataIndex).store(data);
......
...@@ -58,7 +58,15 @@ class CustomArguments : public CustomArgumentsBase { ...@@ -58,7 +58,15 @@ class CustomArguments : public CustomArgumentsBase {
// Note: Calling args.Call() sets the return value on args. For multiple // Note: Calling args.Call() sets the return value on args. For multiple
// Call()'s, a new args should be used every time. // Call()'s, a new args should be used every time.
class PropertyCallbackArguments // This class also serves as a side effects detection scope (JavaScript code
// execution). It is used for ensuring correctness of the interceptor callback
// implementations. The idea is that the interceptor callback that does not
// intercept an operation must not produce side effects. If the callback
// signals that it has handled the operation (by either returning a respective
// result or by throwing an exception) then the AcceptSideEffects() method
// must be called to "accept" the side effects that have happened during the
// lifetime of the PropertyCallbackArguments object.
class PropertyCallbackArguments final
: public CustomArguments<PropertyCallbackInfo<Value> > { : public CustomArguments<PropertyCallbackInfo<Value> > {
public: public:
using T = PropertyCallbackInfo<Value>; using T = PropertyCallbackInfo<Value>;
...@@ -74,6 +82,7 @@ class PropertyCallbackArguments ...@@ -74,6 +82,7 @@ class PropertyCallbackArguments
PropertyCallbackArguments(Isolate* isolate, Object data, Object self, PropertyCallbackArguments(Isolate* isolate, Object data, Object self,
JSObject holder, Maybe<ShouldThrow> should_throw); JSObject holder, Maybe<ShouldThrow> should_throw);
inline ~PropertyCallbackArguments();
// Don't copy PropertyCallbackArguments, because they would both have the // Don't copy PropertyCallbackArguments, because they would both have the
// same prev_ pointer. // same prev_ pointer.
...@@ -128,6 +137,14 @@ class PropertyCallbackArguments ...@@ -128,6 +137,14 @@ class PropertyCallbackArguments
inline Handle<JSObject> CallIndexedEnumerator( inline Handle<JSObject> CallIndexedEnumerator(
Handle<InterceptorInfo> interceptor); Handle<InterceptorInfo> interceptor);
// Accept potential JavaScript side effects that might occurr during life
// time of this object.
inline void AcceptSideEffects() {
#ifdef DEBUG
javascript_execution_counter_ = 0;
#endif // DEBUG
}
private: private:
/* /*
* The following Call functions wrap the calling of all callbacks to handle * The following Call functions wrap the calling of all callbacks to handle
...@@ -148,6 +165,13 @@ class PropertyCallbackArguments ...@@ -148,6 +165,13 @@ class PropertyCallbackArguments
inline JSObject holder(); inline JSObject holder();
inline Object receiver(); inline Object receiver();
#ifdef DEBUG
// This stores current value of Isolate::javascript_execution_counter().
// It's used for detecting whether JavaScript code was executed between
// PropertyCallbackArguments's constructior and destructor.
uint32_t javascript_execution_counter_;
#endif // DEBUG
}; };
class FunctionCallbackArguments class FunctionCallbackArguments
......
...@@ -377,6 +377,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> Invoke(Isolate* isolate, ...@@ -377,6 +377,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> Invoke(Isolate* isolate,
V8::GetCurrentPlatform()->DumpWithoutCrashing(); V8::GetCurrentPlatform()->DumpWithoutCrashing();
return isolate->factory()->undefined_value(); return isolate->factory()->undefined_value();
} }
isolate->IncrementJavascriptExecutionCounter();
if (params.execution_target == Execution::Target::kCallable) { if (params.execution_target == Execution::Target::kCallable) {
Handle<Context> context = isolate->native_context(); Handle<Context> context = isolate->native_context();
......
...@@ -180,6 +180,16 @@ class StackMemory; ...@@ -180,6 +180,16 @@ class StackMemory;
} \ } \
} while (false) } while (false)
#define RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, detector) \
do { \
Isolate* __isolate__ = (isolate); \
DCHECK(!__isolate__->has_pending_exception()); \
if (__isolate__->has_scheduled_exception()) { \
detector.AcceptSideEffects(); \
return __isolate__->PromoteScheduledException(); \
} \
} while (false)
// Macros for MaybeHandle. // Macros for MaybeHandle.
#define RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, value) \ #define RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, value) \
...@@ -192,6 +202,10 @@ class StackMemory; ...@@ -192,6 +202,10 @@ class StackMemory;
} \ } \
} while (false) } while (false)
#define RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, detector, value) \
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, \
(detector.AcceptSideEffects(), value))
#define RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, T) \ #define RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, T) \
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, MaybeHandle<T>()) RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, MaybeHandle<T>())
...@@ -518,6 +532,7 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>; ...@@ -518,6 +532,7 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>;
V(bool, javascript_execution_assert, true) \ V(bool, javascript_execution_assert, true) \
V(bool, javascript_execution_throws, true) \ V(bool, javascript_execution_throws, true) \
V(bool, javascript_execution_dump, true) \ V(bool, javascript_execution_dump, true) \
V(uint32_t, javascript_execution_counter, 0) \
V(bool, deoptimization_assert, true) \ V(bool, deoptimization_assert, true) \
V(bool, compilation_assert, true) \ V(bool, compilation_assert, true) \
V(bool, no_exception_assert, true) V(bool, no_exception_assert, true)
...@@ -1655,6 +1670,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -1655,6 +1670,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
return reinterpret_cast<Address>(&javascript_execution_assert_); return reinterpret_cast<Address>(&javascript_execution_assert_);
} }
void IncrementJavascriptExecutionCounter() {
javascript_execution_counter_++;
}
Address handle_scope_implementer_address() { Address handle_scope_implementer_address() {
return reinterpret_cast<Address>(&handle_scope_implementer_); return reinterpret_cast<Address>(&handle_scope_implementer_);
} }
......
...@@ -3332,14 +3332,22 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { ...@@ -3332,14 +3332,22 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
isolate, receiver, Object::ConvertReceiver(isolate, receiver)); isolate, receiver, Object::ConvertReceiver(isolate, receiver));
} }
Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate); {
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate);
*holder, Just(kDontThrow)); PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
Handle<Object> result = arguments.CallNamedGetter(interceptor, name); *holder, Just(kDontThrow));
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); Handle<Object> result = arguments.CallNamedGetter(interceptor, name);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments);
if (!result.is_null()) return *result; if (!result.is_null()) {
arguments.AcceptSideEffects();
return *result;
}
// If the interceptor didn't handle the request, then there must be no
// side effects.
}
LookupIterator it(isolate, receiver, name, holder); LookupIterator it(isolate, receiver, name, holder);
// Skip any lookup work until we hit the (possibly non-masking) interceptor. // Skip any lookup work until we hit the (possibly non-masking) interceptor.
...@@ -3350,6 +3358,7 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { ...@@ -3350,6 +3358,7 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
} }
// Skip past the interceptor. // Skip past the interceptor.
it.Next(); it.Next();
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Object::GetProperty(&it)); ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Object::GetProperty(&it));
if (it.IsFound()) return *result; if (it.IsFound()) return *result;
...@@ -3387,16 +3396,20 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { ...@@ -3387,16 +3396,20 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) {
handle(JSObject::cast(receiver->map().prototype()), isolate); handle(JSObject::cast(receiver->map().prototype()), isolate);
} }
DCHECK(interceptor_holder->HasNamedInterceptor()); DCHECK(interceptor_holder->HasNamedInterceptor());
Handle<InterceptorInfo> interceptor(interceptor_holder->GetNamedInterceptor(), {
isolate); Handle<InterceptorInfo> interceptor(
interceptor_holder->GetNamedInterceptor(), isolate);
DCHECK(!interceptor->non_masking()); DCHECK(!interceptor->non_masking());
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*receiver, Just(kDontThrow)); *receiver, Just(kDontThrow));
Handle<Object> result = arguments.CallNamedSetter(interceptor, name, value); Handle<Object> result = arguments.CallNamedSetter(interceptor, name, value);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments);
if (!result.is_null()) return *value; if (!result.is_null()) return *value;
// If the interceptor didn't handle the request, then there must be no
// side effects.
}
LookupIterator it(isolate, receiver, name, receiver); LookupIterator it(isolate, receiver, name, receiver);
// Skip past any access check on the receiver. // Skip past any access check on the receiver.
...@@ -3426,7 +3439,7 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { ...@@ -3426,7 +3439,7 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) {
*receiver, Just(kDontThrow)); *receiver, Just(kDontThrow));
Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); Handle<Object> result = arguments.CallIndexedGetter(interceptor, index);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments);
if (result.is_null()) { if (result.is_null()) {
LookupIterator it(isolate, receiver, index, receiver); LookupIterator it(isolate, receiver, index, receiver);
...@@ -3465,24 +3478,30 @@ RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) { ...@@ -3465,24 +3478,30 @@ RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) {
DCHECK_GE(args.smi_value_at(1), 0); DCHECK_GE(args.smi_value_at(1), 0);
uint32_t index = args.smi_value_at(1); uint32_t index = args.smi_value_at(1);
Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(), {
isolate); Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(),
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, isolate);
*receiver, Just(kDontThrow)); PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*receiver, Just(kDontThrow));
if (!interceptor->query().IsUndefined(isolate)) {
Handle<Object> result = arguments.CallIndexedQuery(interceptor, index); if (!interceptor->query().IsUndefined(isolate)) {
if (!result.is_null()) { Handle<Object> result = arguments.CallIndexedQuery(interceptor, index);
int32_t value; if (!result.is_null()) {
CHECK(result->ToInt32(&value)); int32_t value;
return value == ABSENT ? ReadOnlyRoots(isolate).false_value() CHECK(result->ToInt32(&value));
: ReadOnlyRoots(isolate).true_value(); if (value == ABSENT) return ReadOnlyRoots(isolate).false_value();
} arguments.AcceptSideEffects();
} else if (!interceptor->getter().IsUndefined(isolate)) { return ReadOnlyRoots(isolate).true_value();
Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); }
if (!result.is_null()) { } else if (!interceptor->getter().IsUndefined(isolate)) {
return ReadOnlyRoots(isolate).true_value(); Handle<Object> result = arguments.CallIndexedGetter(interceptor, index);
if (!result.is_null()) {
arguments.AcceptSideEffects();
return ReadOnlyRoots(isolate).true_value();
}
} }
// If the interceptor didn't handle the request, then there must be no
// side effects.
} }
LookupIterator it(isolate, receiver, index, receiver); LookupIterator it(isolate, receiver, index, receiver);
......
...@@ -1167,9 +1167,11 @@ MaybeHandle<Object> GetPropertyWithInterceptorInternal( ...@@ -1167,9 +1167,11 @@ MaybeHandle<Object> GetPropertyWithInterceptorInternal(
result = args.CallNamedGetter(interceptor, it->name()); result = args.CallNamedGetter(interceptor, it->name());
} }
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args,
MaybeHandle<Object>());
if (result.is_null()) return isolate->factory()->undefined_value(); if (result.is_null()) return isolate->factory()->undefined_value();
*done = true; *done = true;
args.AcceptSideEffects();
// Rebox handle before return // Rebox handle before return
return handle(*result, isolate); return handle(*result, isolate);
} }
...@@ -1205,6 +1207,10 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( ...@@ -1205,6 +1207,10 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
CHECK(result->ToInt32(&value)); CHECK(result->ToInt32(&value));
DCHECK_IMPLIES((value & ~PropertyAttributes::ALL_ATTRIBUTES_MASK) != 0, DCHECK_IMPLIES((value & ~PropertyAttributes::ALL_ATTRIBUTES_MASK) != 0,
value == PropertyAttributes::ABSENT); value == PropertyAttributes::ABSENT);
// In case of absent property side effects are not allowed.
if (value != PropertyAttributes::ABSENT) {
args.AcceptSideEffects();
}
return Just(static_cast<PropertyAttributes>(value)); return Just(static_cast<PropertyAttributes>(value));
} }
} else if (!interceptor->getter().IsUndefined(isolate)) { } else if (!interceptor->getter().IsUndefined(isolate)) {
...@@ -1215,10 +1221,14 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( ...@@ -1215,10 +1221,14 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
} else { } else {
result = args.CallNamedGetter(interceptor, it->name()); result = args.CallNamedGetter(interceptor, it->name());
} }
if (!result.is_null()) return Just(DONT_ENUM); if (!result.is_null()) {
args.AcceptSideEffects();
return Just(DONT_ENUM);
}
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args,
Nothing<PropertyAttributes>());
return Just(ABSENT); return Just(ABSENT);
} }
...@@ -1252,7 +1262,9 @@ Maybe<bool> SetPropertyWithInterceptorInternal( ...@@ -1252,7 +1262,9 @@ Maybe<bool> SetPropertyWithInterceptorInternal(
result = !args.CallNamedSetter(interceptor, it->name(), value).is_null(); result = !args.CallNamedSetter(interceptor, it->name(), value).is_null();
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(it->isolate(), args,
Nothing<bool>());
if (result) args.AcceptSideEffects();
return Just(result); return Just(result);
} }
...@@ -1274,8 +1286,6 @@ Maybe<bool> DefinePropertyWithInterceptorInternal( ...@@ -1274,8 +1286,6 @@ Maybe<bool> DefinePropertyWithInterceptorInternal(
Object::ConvertReceiver(isolate, receiver), Object::ConvertReceiver(isolate, receiver),
Nothing<bool>()); Nothing<bool>());
} }
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, should_throw);
std::unique_ptr<v8::PropertyDescriptor> descriptor( std::unique_ptr<v8::PropertyDescriptor> descriptor(
new v8::PropertyDescriptor()); new v8::PropertyDescriptor());
...@@ -1298,6 +1308,8 @@ Maybe<bool> DefinePropertyWithInterceptorInternal( ...@@ -1298,6 +1308,8 @@ Maybe<bool> DefinePropertyWithInterceptorInternal(
descriptor->set_configurable(desc->configurable()); descriptor->set_configurable(desc->configurable());
} }
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, should_throw);
if (it->IsElement(*holder)) { if (it->IsElement(*holder)) {
result = result =
!args.CallIndexedDefiner(interceptor, it->array_index(), *descriptor) !args.CallIndexedDefiner(interceptor, it->array_index(), *descriptor)
...@@ -1307,7 +1319,9 @@ Maybe<bool> DefinePropertyWithInterceptorInternal( ...@@ -1307,7 +1319,9 @@ Maybe<bool> DefinePropertyWithInterceptorInternal(
!args.CallNamedDefiner(interceptor, it->name(), *descriptor).is_null(); !args.CallNamedDefiner(interceptor, it->name(), *descriptor).is_null();
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(it->isolate(), args,
Nothing<bool>());
if (result) args.AcceptSideEffects();
return Just(result); return Just(result);
} }
...@@ -1754,10 +1768,11 @@ Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it, ...@@ -1754,10 +1768,11 @@ Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it,
result = args.CallNamedDescriptor(interceptor, it->name()); result = args.CallNamedDescriptor(interceptor, it->name());
} }
// An exception was thrown in the interceptor. Propagate. // An exception was thrown in the interceptor. Propagate.
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args, Nothing<bool>());
if (!result.is_null()) { if (!result.is_null()) {
// Request successfully intercepted, try to set the property // Request was successfully intercepted, try to set the property
// descriptor. // descriptor.
args.AcceptSideEffects();
Utils::ApiCheck( Utils::ApiCheck(
PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc), PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc),
it->IsElement(*holder) ? "v8::IndexedPropertyDescriptorCallback" it->IsElement(*holder) ? "v8::IndexedPropertyDescriptorCallback"
...@@ -3918,10 +3933,11 @@ Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it, ...@@ -3918,10 +3933,11 @@ Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it,
result = args.CallNamedDeleter(interceptor, it->name()); result = args.CallNamedDeleter(interceptor, it->name());
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args, Nothing<bool>());
if (result.is_null()) return Nothing<bool>(); if (result.is_null()) return Nothing<bool>();
DCHECK(result->IsBoolean()); DCHECK(result->IsBoolean());
args.AcceptSideEffects();
// Rebox CustomArguments::kReturnValueOffset before returning. // Rebox CustomArguments::kReturnValueOffset before returning.
return Just(result->IsTrue(isolate)); return Just(result->IsTrue(isolate));
} }
......
...@@ -660,6 +660,7 @@ KeyAccumulator::FilterForEnumerableProperties( ...@@ -660,6 +660,7 @@ KeyAccumulator::FilterForEnumerableProperties(
if (!accessor->HasEntry(*result, entry)) continue; if (!accessor->HasEntry(*result, entry)) continue;
// args are invalid after args.Call(), create a new one in every iteration. // args are invalid after args.Call(), create a new one in every iteration.
// Query callbacks are not expected to have side effects.
PropertyCallbackArguments args(isolate_, interceptor->data(), *receiver, PropertyCallbackArguments args(isolate_, interceptor->data(), *receiver,
*object, Just(kDontThrow)); *object, Just(kDontThrow));
...@@ -702,9 +703,14 @@ Maybe<bool> KeyAccumulator::CollectInterceptorKeysInternal( ...@@ -702,9 +703,14 @@ Maybe<bool> KeyAccumulator::CollectInterceptorKeysInternal(
result = enum_args.CallNamedEnumerator(interceptor); result = enum_args.CallNamedEnumerator(interceptor);
} }
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>()); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate_, enum_args,
Nothing<bool>());
if (result.is_null()) return Just(true); if (result.is_null()) return Just(true);
// Request was successfully intercepted, so accept potential side effects
// happened up to this point.
enum_args.AcceptSideEffects();
if ((filter_ & ONLY_ENUMERABLE) && if ((filter_ & ONLY_ENUMERABLE) &&
!interceptor->query().IsUndefined(isolate_)) { !interceptor->query().IsUndefined(isolate_)) {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(FilterForEnumerableProperties( RETURN_NOTHING_IF_NOT_SUCCESSFUL(FilterForEnumerableProperties(
......
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
# These tests are expected to hit a CHECK (i.e. a FAIL result actually means # These tests are expected to hit a CHECK (i.e. a FAIL result actually means
# the test passed). # the test passed).
'test-verifiers/Fail*': [FAIL, CRASH], 'test-verifiers/Fail*': [FAIL, CRASH],
# BUG(chromium:1310062): these tests must crash once the side effect check
# is enabled.
#'test-api-interceptors/Crash*': [FAIL, CRASH],
# This test always fails. It tests that LiveEdit causes abort when turned off. # This test always fails. It tests that LiveEdit causes abort when turned off.
'test-debug/LiveEditDisabled': [FAIL], 'test-debug/LiveEditDisabled': [FAIL],
......
...@@ -701,7 +701,7 @@ THREADED_TEST(GlobalObjectAccessor) { ...@@ -701,7 +701,7 @@ THREADED_TEST(GlobalObjectAccessor) {
static void EmptyGetter(Local<Name> name, static void EmptyGetter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
} }
......
...@@ -224,7 +224,7 @@ v8::Local<v8::Object> bottom; ...@@ -224,7 +224,7 @@ v8::Local<v8::Object> bottom;
void CheckThisIndexedPropertyHandler( void CheckThisIndexedPropertyHandler(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -233,7 +233,7 @@ void CheckThisIndexedPropertyHandler( ...@@ -233,7 +233,7 @@ void CheckThisIndexedPropertyHandler(
void CheckThisNamedPropertyHandler( void CheckThisNamedPropertyHandler(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -243,7 +243,7 @@ void CheckThisIndexedPropertyDefiner( ...@@ -243,7 +243,7 @@ void CheckThisIndexedPropertyDefiner(
uint32_t index, const v8::PropertyDescriptor& desc, uint32_t index, const v8::PropertyDescriptor& desc,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -253,7 +253,7 @@ void CheckThisNamedPropertyDefiner( ...@@ -253,7 +253,7 @@ void CheckThisNamedPropertyDefiner(
Local<Name> property, const v8::PropertyDescriptor& desc, Local<Name> property, const v8::PropertyDescriptor& desc,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -263,7 +263,7 @@ void CheckThisIndexedPropertySetter( ...@@ -263,7 +263,7 @@ void CheckThisIndexedPropertySetter(
uint32_t index, Local<Value> value, uint32_t index, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -272,7 +272,7 @@ void CheckThisIndexedPropertySetter( ...@@ -272,7 +272,7 @@ void CheckThisIndexedPropertySetter(
void CheckThisIndexedPropertyDescriptor( void CheckThisIndexedPropertyDescriptor(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDescriptor)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDescriptor));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -281,7 +281,7 @@ void CheckThisIndexedPropertyDescriptor( ...@@ -281,7 +281,7 @@ void CheckThisIndexedPropertyDescriptor(
void CheckThisNamedPropertyDescriptor( void CheckThisNamedPropertyDescriptor(
Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDescriptor)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDescriptor));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -291,7 +291,7 @@ void CheckThisNamedPropertySetter( ...@@ -291,7 +291,7 @@ void CheckThisNamedPropertySetter(
Local<Name> property, Local<Value> value, Local<Name> property, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -300,7 +300,7 @@ void CheckThisNamedPropertySetter( ...@@ -300,7 +300,7 @@ void CheckThisNamedPropertySetter(
void CheckThisIndexedPropertyQuery( void CheckThisIndexedPropertyQuery(
uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -310,7 +310,7 @@ void CheckThisIndexedPropertyQuery( ...@@ -310,7 +310,7 @@ void CheckThisIndexedPropertyQuery(
void CheckThisNamedPropertyQuery( void CheckThisNamedPropertyQuery(
Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -320,7 +320,7 @@ void CheckThisNamedPropertyQuery( ...@@ -320,7 +320,7 @@ void CheckThisNamedPropertyQuery(
void CheckThisIndexedPropertyDeleter( void CheckThisIndexedPropertyDeleter(
uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -330,7 +330,7 @@ void CheckThisIndexedPropertyDeleter( ...@@ -330,7 +330,7 @@ void CheckThisIndexedPropertyDeleter(
void CheckThisNamedPropertyDeleter( void CheckThisNamedPropertyDeleter(
Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) { Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -340,7 +340,7 @@ void CheckThisNamedPropertyDeleter( ...@@ -340,7 +340,7 @@ void CheckThisNamedPropertyDeleter(
void CheckThisIndexedPropertyEnumerator( void CheckThisIndexedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) { const v8::PropertyCallbackInfo<v8::Array>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -350,7 +350,7 @@ void CheckThisIndexedPropertyEnumerator( ...@@ -350,7 +350,7 @@ void CheckThisIndexedPropertyEnumerator(
void CheckThisNamedPropertyEnumerator( void CheckThisNamedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) { const v8::PropertyCallbackInfo<v8::Array>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator)); CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CHECK(info.This() CHECK(info.This()
->Equals(info.GetIsolate()->GetCurrentContext(), bottom) ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
.FromJust()); .FromJust());
...@@ -372,12 +372,12 @@ void EchoNamedProperty(Local<Name> name, ...@@ -372,12 +372,12 @@ void EchoNamedProperty(Local<Name> name,
void InterceptorHasOwnPropertyGetter( void InterceptorHasOwnPropertyGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
} }
void InterceptorHasOwnPropertyGetterGC( void InterceptorHasOwnPropertyGetterGC(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CcTest::CollectAllGarbage(); CcTest::CollectAllGarbage();
} }
...@@ -883,7 +883,7 @@ void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, ...@@ -883,7 +883,7 @@ void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
v8::GenericNamedPropertyQueryCallback query, v8::GenericNamedPropertyQueryCallback query,
v8::GenericNamedPropertyDefinerCallback definer, v8::GenericNamedPropertyDefinerCallback definer,
v8::PropertyHandlerFlags flags, const char* source, v8::PropertyHandlerFlags flags, const char* source,
int expected) { std::optional<int> expected) {
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
...@@ -896,12 +896,16 @@ void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, ...@@ -896,12 +896,16 @@ void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
templ->NewInstance(context.local()).ToLocalChecked()) templ->NewInstance(context.local()).ToLocalChecked())
.FromJust(); .FromJust();
v8::Local<Value> value = CompileRun(source); v8::Local<Value> value = CompileRun(source);
CHECK_EQ(expected, value->Int32Value(context.local()).FromJust()); if (expected) {
CHECK_EQ(*expected, value->Int32Value(context.local()).FromJust());
} else {
CHECK(value.IsEmpty());
}
} }
void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
v8::GenericNamedPropertyQueryCallback query, v8::GenericNamedPropertyQueryCallback query,
const char* source, int expected) { const char* source, std::optional<int> expected) {
CheckInterceptorIC(getter, nullptr, query, nullptr, CheckInterceptorIC(getter, nullptr, query, nullptr,
v8::PropertyHandlerFlags::kNone, source, expected); v8::PropertyHandlerFlags::kNone, source, expected);
} }
...@@ -944,13 +948,14 @@ namespace { ...@@ -944,13 +948,14 @@ namespace {
void InterceptorLoadXICGetter(Local<Name> name, void InterceptorLoadXICGetter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); if (v8_str("x")
info.GetReturnValue().Set( ->Equals(info.GetIsolate()->GetCurrentContext(), name)
v8_str("x") .FromJust()) {
->Equals(info.GetIsolate()->GetCurrentContext(), name) // Side effects are allowed only when the property is present or throws.
.FromJust() ApiTestFuzzer::Fuzz();
? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42)) info.GetReturnValue().Set(
: v8::Local<v8::Value>()); v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42)));
}
} }
void InterceptorLoadXICGetterWithSideEffects( void InterceptorLoadXICGetterWithSideEffects(
...@@ -1472,7 +1477,10 @@ namespace { ...@@ -1472,7 +1477,10 @@ namespace {
template <typename TKey, v8::internal::PropertyAttributes attribute> template <typename TKey, v8::internal::PropertyAttributes attribute>
void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) { void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz(); if (attribute != v8::internal::ABSENT) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
}
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New(isolate, attribute)); info.GetReturnValue().Set(v8::Integer::New(isolate, attribute));
...@@ -1481,19 +1489,25 @@ void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) { ...@@ -1481,19 +1489,25 @@ void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) {
template <typename TKey> template <typename TKey>
void HasICQueryToggle(TKey name, void HasICQueryToggle(TKey name,
const v8::PropertyCallbackInfo<v8::Integer>& info) { const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz(); static bool is_absent = false;
static bool toggle = false; is_absent = !is_absent;
toggle = !toggle; if (!is_absent) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
}
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New( info.GetReturnValue().Set(v8::Integer::New(
isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE)); isolate, is_absent ? v8::internal::ABSENT : v8::internal::NONE));
} }
template <typename TKey, v8::internal::PropertyAttributes attribute> template <typename TKey, v8::internal::PropertyAttributes attribute>
void HasICQuerySideEffect(TKey name, void HasICQuerySideEffect(TKey name,
const v8::PropertyCallbackInfo<v8::Integer>& info) { const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz(); if (attribute != v8::internal::ABSENT) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
}
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(isolate, info.GetIsolate());
CompileRun("interceptor_query_side_effect()"); CompileRun("interceptor_query_side_effect()");
...@@ -1567,7 +1581,26 @@ THREADED_TEST(InterceptorHasICQueryToggle) { ...@@ -1567,7 +1581,26 @@ THREADED_TEST(InterceptorHasICQueryToggle) {
500); 500);
} }
THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks) { THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks1) {
CheckInterceptorIC(EmptyInterceptorGetter,
HasICQuerySideEffect<Local<Name>, v8::internal::NONE>,
"let r;"
"let inside_side_effect = false;"
"let interceptor_query_side_effect = function() {"
" if (!inside_side_effect) {"
" inside_side_effect = true;"
" r.x = 153;"
" inside_side_effect = false;"
" }"
"};"
"for (var i = 0; i < 20; i++) {"
" r = { __proto__: o };"
" r.x = i;"
"}",
19);
}
TEST(Crash_InterceptorStoreICWithSideEffectfulCallbacks1) {
CheckInterceptorIC(EmptyInterceptorGetter, CheckInterceptorIC(EmptyInterceptorGetter,
HasICQuerySideEffect<Local<Name>, v8::internal::ABSENT>, HasICQuerySideEffect<Local<Name>, v8::internal::ABSENT>,
"let r;" "let r;"
...@@ -1584,7 +1617,9 @@ THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks) { ...@@ -1584,7 +1617,9 @@ THREADED_TEST(InterceptorStoreICWithSideEffectfulCallbacks) {
" r.x = i;" " r.x = i;"
"}", "}",
19); 19);
}
TEST(Crash_InterceptorStoreICWithSideEffectfulCallbacks2) {
CheckInterceptorIC(InterceptorLoadXICGetterWithSideEffects, CheckInterceptorIC(InterceptorLoadXICGetterWithSideEffects,
nullptr, // query callback is not provided nullptr, // query callback is not provided
"let r;" "let r;"
...@@ -1615,7 +1650,39 @@ THREADED_TEST(InterceptorDefineICWithSideEffectfulCallbacks) { ...@@ -1615,7 +1650,39 @@ THREADED_TEST(InterceptorDefineICWithSideEffectfulCallbacks) {
" o.y = 153;" " o.y = 153;"
" inside_side_effect = false;" " inside_side_effect = false;"
" }" " }"
" return null;" " return true;" // Accept the request.
"};"
"class Base {"
" constructor(arg) {"
" return arg;"
" }"
"}"
"class ClassWithField extends Base {"
" y = (() => {"
" return 42;"
" })();"
" constructor(arg) {"
" super(arg);"
" }"
"}"
"new ClassWithField(o);"
"o.y",
153);
}
TEST(Crash_InterceptorDefineICWithSideEffectfulCallbacks) {
CheckInterceptorIC(EmptyInterceptorGetter, EmptyInterceptorSetter,
EmptyInterceptorQuery,
EmptyInterceptorDefinerWithSideEffect,
v8::PropertyHandlerFlags::kNonMasking,
"let inside_side_effect = false;"
"let interceptor_definer_side_effect = function() {"
" if (!inside_side_effect) {"
" inside_side_effect = true;"
" o.y = 153;"
" inside_side_effect = false;"
" }"
" return null;" // Decline the request.
"};" "};"
"class Base {" "class Base {"
" constructor(arg) {" " constructor(arg) {"
...@@ -2741,16 +2808,16 @@ TEST(PropertyHandlerInPrototypeWithDefine) { ...@@ -2741,16 +2808,16 @@ TEST(PropertyHandlerInPrototypeWithDefine) {
bool is_bootstrapping = false; bool is_bootstrapping = false;
static void PrePropertyHandlerGet( static void PrePropertyHandlerGet(
Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (!is_bootstrapping && if (!is_bootstrapping &&
v8_str("pre") v8_str("pre")
->Equals(info.GetIsolate()->GetCurrentContext(), key) ->Equals(info.GetIsolate()->GetCurrentContext(), key)
.FromJust()) { .FromJust()) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre")); info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
} }
} }
static void PrePropertyHandlerQuery( static void PrePropertyHandlerQuery(
Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) { Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (!is_bootstrapping && if (!is_bootstrapping &&
...@@ -3318,26 +3385,25 @@ THREADED_TEST(NamedInterceptorMapTransitionRead) { ...@@ -3318,26 +3385,25 @@ THREADED_TEST(NamedInterceptorMapTransitionRead) {
CHECK_EQ(23, result->Int32Value(context.local()).FromJust()); CHECK_EQ(23, result->Int32Value(context.local()).FromJust());
} }
static void IndexedPropertyGetter( static void IndexedPropertyGetter(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (index == 37) { if (index == 37) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(625)); info.GetReturnValue().Set(v8_num(625));
} }
} }
static void IndexedPropertySetter( static void IndexedPropertySetter(
uint32_t index, Local<Value> value, uint32_t index, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (index == 39) { if (index == 39) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(value); info.GetReturnValue().Set(value);
} }
} }
THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
...@@ -3370,26 +3436,25 @@ THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { ...@@ -3370,26 +3436,25 @@ THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
CHECK(v8_num(625)->Equals(context.local(), result).FromJust()); CHECK(v8_num(625)->Equals(context.local(), result).FromJust());
} }
static void UnboxedDoubleIndexedPropertyGetter( static void UnboxedDoubleIndexedPropertyGetter(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (index < 25) { if (index < 25) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(index)); info.GetReturnValue().Set(v8_num(index));
} }
} }
static void UnboxedDoubleIndexedPropertySetter( static void UnboxedDoubleIndexedPropertySetter(
uint32_t index, Local<Value> value, uint32_t index, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (index < 25) { if (index < 25) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(index)); info.GetReturnValue().Set(v8_num(index));
} }
} }
void UnboxedDoubleIndexedPropertyEnumerator( void UnboxedDoubleIndexedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) { const v8::PropertyCallbackInfo<v8::Array>& info) {
// Force the list of returned keys to be stored in a FastDoubleArray. // Force the list of returned keys to be stored in a FastDoubleArray.
...@@ -3797,7 +3862,7 @@ void IndexedQueryCallback(uint32_t index, ...@@ -3797,7 +3862,7 @@ void IndexedQueryCallback(uint32_t index,
void IndexHasICQueryAbsent(uint32_t index, void IndexHasICQueryAbsent(uint32_t index,
const v8::PropertyCallbackInfo<v8::Integer>& info) { const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New(isolate, v8::internal::ABSENT)); info.GetReturnValue().Set(v8::Integer::New(isolate, v8::internal::ABSENT));
...@@ -3950,23 +4015,25 @@ THREADED_TEST(Deleter) { ...@@ -3950,23 +4015,25 @@ THREADED_TEST(Deleter) {
v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined()); v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined());
} }
static void GetK(Local<Name> name, static void GetK(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (name->Equals(context, v8_str("foo")).FromJust() || if (name->Equals(context, v8_str("foo")).FromJust() ||
name->Equals(context, v8_str("bar")).FromJust() || name->Equals(context, v8_str("bar")).FromJust() ||
name->Equals(context, v8_str("baz")).FromJust()) { name->Equals(context, v8_str("baz")).FromJust()) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().SetUndefined(); info.GetReturnValue().SetUndefined();
} }
} }
static void IndexedGetK(uint32_t index, static void IndexedGetK(uint32_t index,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); if (index == 0 || index == 1) {
if (index == 0 || index == 1) info.GetReturnValue().SetUndefined(); // Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().SetUndefined();
}
} }
...@@ -4691,17 +4758,17 @@ static int interceptor_call_count = 0; ...@@ -4691,17 +4758,17 @@ static int interceptor_call_count = 0;
static void InterceptorICRefErrorGetter( static void InterceptorICRefErrorGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (!is_bootstrapping && if (!is_bootstrapping &&
v8_str("x") v8_str("x")
->Equals(info.GetIsolate()->GetCurrentContext(), name) ->Equals(info.GetIsolate()->GetCurrentContext(), name)
.FromJust() && .FromJust() &&
interceptor_call_count++ < 20) { interceptor_call_count++ < 20) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(call_ic_function2); info.GetReturnValue().Set(call_ic_function2);
} }
} }
// This test should hit load and call ICs for the interceptor case. // This test should hit load and call ICs for the interceptor case.
// Once in a while, the interceptor will reply that a property was not // Once in a while, the interceptor will reply that a property was not
// found in which case we should get a reference error. // found in which case we should get a reference error.
...@@ -4743,21 +4810,23 @@ static int interceptor_ic_exception_get_count = 0; ...@@ -4743,21 +4810,23 @@ static int interceptor_ic_exception_get_count = 0;
static void InterceptorICExceptionGetter( static void InterceptorICExceptionGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (is_bootstrapping) return; if (is_bootstrapping) return;
if (v8_str("x") if (v8_str("x")
->Equals(info.GetIsolate()->GetCurrentContext(), name) ->Equals(info.GetIsolate()->GetCurrentContext(), name)
.FromJust() && .FromJust() &&
++interceptor_ic_exception_get_count < 20) { ++interceptor_ic_exception_get_count < 20) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(call_ic_function3); info.GetReturnValue().Set(call_ic_function3);
} }
if (interceptor_ic_exception_get_count == 20) { if (interceptor_ic_exception_get_count == 20) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetIsolate()->ThrowException(v8_num(42)); info.GetIsolate()->ThrowException(v8_num(42));
return; return;
} }
} }
// Test interceptor load/call IC where the interceptor throws an // Test interceptor load/call IC where the interceptor throws an
// exception once in a while. // exception once in a while.
THREADED_TEST(InterceptorICGetterExceptions) { THREADED_TEST(InterceptorICGetterExceptions) {
...@@ -4800,13 +4869,13 @@ static int interceptor_ic_exception_set_count = 0; ...@@ -4800,13 +4869,13 @@ static int interceptor_ic_exception_set_count = 0;
static void InterceptorICExceptionSetter( static void InterceptorICExceptionSetter(
Local<Name> key, Local<Value> value, Local<Name> key, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (++interceptor_ic_exception_set_count > 20) { if (++interceptor_ic_exception_set_count > 20) {
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetIsolate()->ThrowException(v8_num(42)); info.GetIsolate()->ThrowException(v8_num(42));
} }
} }
// Test interceptor store IC where the interceptor throws an exception // Test interceptor store IC where the interceptor throws an exception
// once in a while. // once in a while.
THREADED_TEST(InterceptorICSetterExceptions) { THREADED_TEST(InterceptorICSetterExceptions) {
...@@ -5287,23 +5356,23 @@ struct ShouldInterceptData { ...@@ -5287,23 +5356,23 @@ struct ShouldInterceptData {
bool should_intercept; bool should_intercept;
}; };
void ShouldNamedInterceptor(Local<Name> name, void ShouldNamedInterceptor(Local<Name> name,
const v8::PropertyCallbackInfo<Value>& info) { const v8::PropertyCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor)); CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor));
auto data = GetWrappedObject<ShouldInterceptData>(info.Data()); auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
if (!data->should_intercept) return; if (!data->should_intercept) return;
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(data->value)); info.GetReturnValue().Set(v8_num(data->value));
} }
void ShouldIndexedInterceptor(uint32_t, void ShouldIndexedInterceptor(uint32_t,
const v8::PropertyCallbackInfo<Value>& info) { const v8::PropertyCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor)); CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor));
auto data = GetWrappedObject<ShouldInterceptData>(info.Data()); auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
if (!data->should_intercept) return; if (!data->should_intercept) return;
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(v8_num(data->value)); info.GetReturnValue().Set(v8_num(data->value));
} }
...@@ -5885,22 +5954,25 @@ namespace { ...@@ -5885,22 +5954,25 @@ namespace {
void DatabaseGetter(Local<Name> name, void DatabaseGetter(Local<Name> name,
const v8::PropertyCallbackInfo<Value>& info) { const v8::PropertyCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
auto context = info.GetIsolate()->GetCurrentContext(); auto context = info.GetIsolate()->GetCurrentContext();
v8::MaybeLocal<Value> maybe_db = v8::MaybeLocal<Value> maybe_db =
info.Holder()->GetRealNamedProperty(context, v8_str("db")); info.Holder()->GetRealNamedProperty(context, v8_str("db"));
if (maybe_db.IsEmpty()) return; if (maybe_db.IsEmpty()) return;
Local<v8::Object> db = maybe_db.ToLocalChecked().As<v8::Object>(); Local<v8::Object> db = maybe_db.ToLocalChecked().As<v8::Object>();
if (!db->Has(context, name).FromJust()) return; if (!db->Has(context, name).FromJust()) return;
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked()); info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked());
} }
void DatabaseSetter(Local<Name> name, Local<Value> value, void DatabaseSetter(Local<Name> name, Local<Value> value,
const v8::PropertyCallbackInfo<Value>& info) { const v8::PropertyCallbackInfo<Value>& info) {
ApiTestFuzzer::Fuzz();
auto context = info.GetIsolate()->GetCurrentContext(); auto context = info.GetIsolate()->GetCurrentContext();
if (name->Equals(context, v8_str("db")).FromJust()) return; if (name->Equals(context, v8_str("db")).FromJust()) return;
// Side effects are allowed only when the property is present or throws.
ApiTestFuzzer::Fuzz();
Local<v8::Object> db = info.Holder() Local<v8::Object> db = info.Holder()
->GetRealNamedProperty(context, v8_str("db")) ->GetRealNamedProperty(context, v8_str("db"))
.ToLocalChecked() .ToLocalChecked()
......
...@@ -7998,7 +7998,8 @@ static void PGetter2(Local<Name> name, ...@@ -7998,7 +7998,8 @@ static void PGetter2(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); ApiTestFuzzer::Fuzz();
p_getter_count2++; p_getter_count2++;
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> global = context->Global(); v8::Local<v8::Object> global = context->Global();
CHECK( CHECK(
info.Holder() info.Holder()
...@@ -8025,6 +8026,8 @@ static void PGetter2(Local<Name> name, ...@@ -8025,6 +8026,8 @@ static void PGetter2(Local<Name> name,
global->Get(context, v8_str("o4")).ToLocalChecked()) global->Get(context, v8_str("o4")).ToLocalChecked())
.FromJust()); .FromJust());
} }
// Return something to indicate that the operation was intercepted.
info.GetReturnValue().Set(True(isolate));
} }
...@@ -10226,7 +10229,7 @@ THREADED_TEST(InstanceProperties) { ...@@ -10226,7 +10229,7 @@ THREADED_TEST(InstanceProperties) {
static void GlobalObjectInstancePropertiesGet( static void GlobalObjectInstancePropertiesGet(
Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) { Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
} }
static int script_execution_count = 0; static int script_execution_count = 0;
...@@ -11584,7 +11587,7 @@ THREADED_TEST(HandleIteration) { ...@@ -11584,7 +11587,7 @@ THREADED_TEST(HandleIteration) {
static void InterceptorCallICFastApi( static void InterceptorCallICFastApi(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz(); // The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi)); CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi));
int* call_count = int* call_count =
reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value()); reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value());
...@@ -12156,6 +12159,8 @@ static void ShouldThrowOnErrorSetter(Local<Name> name, Local<v8::Value> value, ...@@ -12156,6 +12159,8 @@ static void ShouldThrowOnErrorSetter(Local<Name> name, Local<v8::Value> value,
->Set(isolate->GetCurrentContext(), v8_str("should_throw_setter"), ->Set(isolate->GetCurrentContext(), v8_str("should_throw_setter"),
should_throw_on_error_value) should_throw_on_error_value)
.FromJust()); .FromJust());
// Return a boolean to indicate that the operation was intercepted.
info.GetReturnValue().Set(True(isolate));
} }
...@@ -12224,6 +12229,8 @@ static void ShouldThrowOnErrorDeleter( ...@@ -12224,6 +12229,8 @@ static void ShouldThrowOnErrorDeleter(
->Set(isolate->GetCurrentContext(), v8_str("should_throw_deleter"), ->Set(isolate->GetCurrentContext(), v8_str("should_throw_deleter"),
should_throw_on_error_value) should_throw_on_error_value)
.FromJust()); .FromJust());
// Return a boolean to indicate that the operation was intercepted.
info.GetReturnValue().Set(True(isolate));
} }
...@@ -12986,6 +12993,11 @@ int ApiTestFuzzer::current_; ...@@ -12986,6 +12993,11 @@ int ApiTestFuzzer::current_;
// We are in a callback and want to switch to another thread (if we // We are in a callback and want to switch to another thread (if we
// are currently running the thread fuzzing test). // are currently running the thread fuzzing test).
void ApiTestFuzzer::Fuzz() { void ApiTestFuzzer::Fuzz() {
// Emulate context switch which might cause side effects as well.
// This is mostly to ensure that the callbacks in the tests do not cause
// side effects when they don't intercept the operation.
CcTest::i_isolate()->IncrementJavascriptExecutionCounter();
if (!fuzzing_) return; if (!fuzzing_) return;
ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
test->ContextSwitch(); test->ContextSwitch();
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