// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_API_API_ARGUMENTS_INL_H_
#define V8_API_API_ARGUMENTS_INL_H_
#include "src/api/api-arguments.h"
// Include the non-inl header before the rest of the headers.
#include "src/api/api-inl.h"
#include "src/debug/debug.h"
#include "src/execution/vm-state-inl.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/instance-type.h"
#include "src/objects/slots-inl.h"
namespace v8 {
namespace internal {
template <typename T>
Handle<T> PropertyCallbackArguments::GetReturnValue() const {
// Check the ReturnValue.
FullObjectSlot slot = slot_at(kReturnValueIndex);
DCHECK(Is<JSAny>(*slot));
return Cast<T>(Handle<Object>(slot.location()));
}
bool PropertyCallbackArguments::is_named() const {
int frame_type = Smi::ToInt(Tagged<Object>(values_[T::kFrameTypeIndex]));
DCHECK(frame_type == StackFrame::API_NAMED_ACCESSOR_EXIT ||
frame_type == StackFrame::API_INDEXED_ACCESSOR_EXIT);
return frame_type == StackFrame::API_NAMED_ACCESSOR_EXIT;
}
void PropertyCallbackArguments::set_property_key(Tagged<Name> name) {
values_[T::kPropertyKeyIndex] = name->ptr();
values_[T::kFrameTypeIndex] =
Smi::FromInt(StackFrame::API_NAMED_ACCESSOR_EXIT).ptr();
}
void PropertyCallbackArguments::set_property_key(uint32_t index) {
values_[T::kPropertyKeyIndex] = index;
values_[T::kFrameTypeIndex] =
Smi::FromInt(StackFrame::API_INDEXED_ACCESSOR_EXIT).ptr();
}
DirectHandle<JSObject> PropertyCallbackArguments::holder() const {
return DirectHandle<JSObject>::FromSlot(slot_at(T::kHolderIndex).location());
}
#define DCHECK_NAME_COMPATIBLE(interceptor, name) \
DCHECK(interceptor->is_named()); \
DCHECK(!name->IsAnyPrivate()); \
DCHECK_IMPLIES(IsSymbol(*name), interceptor->can_intercept_symbols());
#define PREPARE_CALLBACK_INFO_ACCESSOR(ISOLATE, F, API_RETURN_TYPE, \
ACCESSOR_INFO, HOLDER, ACCESSOR_KIND, \
EXCEPTION_CONTEXT) \
if (ISOLATE->should_check_side_effects() && \
!ISOLATE->debug()->PerformSideEffectCheckForAccessor( \
ACCESSOR_INFO, HOLDER, ACCESSOR_KIND)) { \
return {}; \
} \
const PropertyCallbackInfo<API_RETURN_TYPE>& callback_info = \
GetPropertyCallbackInfo<API_RETURN_TYPE>(); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F), \
EXCEPTION_CONTEXT, &callback_info);
#define PREPARE_CALLBACK_INFO_INTERCEPTOR(ISOLATE, F, API_RETURN_TYPE, \
INTERCEPTOR_INFO, EXCEPTION_CONTEXT) \
if (ISOLATE->should_check_side_effects() && \
!ISOLATE->debug()->PerformSideEffectCheckForInterceptor( \
INTERCEPTOR_INFO)) { \
return {}; \
} \
const PropertyCallbackInfo<API_RETURN_TYPE>& callback_info = \
GetPropertyCallbackInfo<API_RETURN_TYPE>(); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F), \
EXCEPTION_CONTEXT, &callback_info);
template <typename ArgT>
FunctionCallbackArguments::FunctionCallbackArguments(
Isolate* isolate, Tagged<FunctionTemplateInfo> target,
Tagged<Object> receiver, const base::Vector<const ArgT> args)
: Relocatable(isolate) {
Initialize<false>(isolate, target, Smi::zero(), receiver, args);
}
template <typename ArgT>
FunctionCallbackArguments::FunctionCallbackArguments(
Isolate* isolate, Tagged<FunctionTemplateInfo> target,
Tagged<HeapObject> new_target, Tagged<Object> receiver,
const base::Vector<const ArgT> args)
: Relocatable(isolate) {
Initialize<true>(isolate, target, new_target, receiver, args);
}
template <bool is_construct, typename ArgT>
requires(std::is_same_v<ArgT, DirectHandle<Object>> ||
std::is_same_v<ArgT, Address>)
void FunctionCallbackArguments::Initialize(
Isolate* isolate, Tagged<FunctionTemplateInfo> target,
Tagged<Object> new_target, Tagged<Object> receiver,
const base::Vector<const ArgT> args) {
uint32_t argc = static_cast<uint32_t>(args.size());
values_.resize(argc + T::kArgsLength + T::kOptionalArgsLength);
Address* values = &values_.data()[T::kOptionalArgsLength];
// Initialize frame part.
values[T::kNewTargetIndex] = is_construct ? new_target.ptr() : 0;
values[T::kArgcIndex] = argc;
values[T::kFrameTypeIndex] =
Smi::FromInt(is_construct ? StackFrame::API_CONSTRUCT_EXIT
: StackFrame::API_CALLBACK_EXIT)
.ptr();
if (DEBUG_BOOL) {
// These values are not supposed to be looked at.
values[T::kFrameSPIndex] = kZapValue;
values[T::kFrameConstantPoolIndex] = kZapValue;
values[T::kFrameFPIndex] = kZapValue;
values[T::kFramePCIndex] = kZapValue;
}
// Initialize Api arguments part.
values[T::kTargetIndex] = target.ptr();
values[T::kIsolateIndex] = reinterpret_cast<Address>(isolate);
values[T::kReturnValueIndex] = ReadOnlyRoots(isolate).undefined_value().ptr();
values[T::kContextIndex] = isolate->context().ptr();
// Make sure the Isolate slot is safe to visit by GC (Isolate pointer
// is guaranteed to be page aligned).
DCHECK(HAS_SMI_TAG(values[T::kIsolateIndex]));
// Initialize JS arguments part.
values[T::kReceiverIndex] = receiver.ptr();
for (uint32_t i = 0; i < argc; ++i) {
if constexpr (std::is_same_v<ArgT, Address>) {
values[T::kFirstJSArgumentIndex + i] = args[i];
} else {
values[T::kFirstJSArgumentIndex + i] = (*args[i]).ptr();
}
}
}
FunctionCallbackArguments::~FunctionCallbackArguments() {
if (DEBUG_BOOL) {
// Make sure the result handle located inside this structure is not used
// after this object dies.
values_.data()[T::kReturnValueIndex] = kZapValue;
}
}
Tagged<JSAny> FunctionCallbackArguments::CallOrConstruct(
Isolate* isolate, Tagged<FunctionTemplateInfo> function,
bool is_construct) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionCallback);
v8::FunctionCallback f =
reinterpret_cast<v8::FunctionCallback>(function->callback(isolate));
if (isolate->should_check_side_effects() &&
!isolate->debug()->PerformSideEffectCheckForCallback(
handle(function, isolate))) {
return {};
}
// v8::FunctionCallbackInfo structure might start at different positions in
// values_ array depending on whether it's a construct call or not.
auto info =
reinterpret_cast<FunctionCallbackInfo<v8::Value>*>(slot_at(0).location());
ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f),
is_construct ? ExceptionContext::kConstructor
: ExceptionContext::kOperation,
info);
f(*info);
Tagged<Object> result = *slot_at(T::kReturnValueIndex);
DCHECK(Is<JSAny>(result));
return Cast<JSAny>(result);
}
PropertyCallbackArguments::PropertyCallbackArguments(Isolate* isolate,
Tagged<JSObject> holder)
: Relocatable(isolate)
#ifdef DEBUG
,
is_setter_definer_deleter_(false),
javascript_execution_counter_(isolate->javascript_execution_counter())
#endif // DEBUG
{
Initialize(isolate, holder);
}
PropertyCallbackArguments::PropertyCallbackArguments(
Isolate* isolate, Tagged<JSObject> holder, Maybe<ShouldThrow> should_throw)
: Relocatable(isolate)
#ifdef DEBUG
,
is_setter_definer_deleter_(true),
javascript_execution_counter_(isolate->javascript_execution_counter())
#endif // DEBUG
{
Initialize(isolate, holder);
int value = Internals::kInferShouldThrowMode;
if (should_throw.IsJust()) {
value = should_throw.FromJust();
}
slot_at(T::kShouldThrowOnErrorIndex).store(Smi::FromInt(value));
}
void PropertyCallbackArguments::Initialize(Isolate* isolate,
Tagged<JSObject> holder) {
if (DEBUG_BOOL) {
// Zap these fields to ensure that they are initialized by a subsequent
// CallXXX(..).
values_[T::kFrameSPIndex] = kZapValue;
values_[T::kFrameConstantPoolIndex] = kZapValue;
values_[T::kFrameTypeIndex] = kZapValue;
values_[T::kFrameFPIndex] = kZapValue;
values_[T::kFramePCIndex] = kZapValue;
values_[T::kPropertyKeyIndex] = kZapValue;
values_[T::kReturnValueIndex] = kZapValue;
values_[T::kCallbackInfoIndex] = kZapValue;
// This field is used only for setter/definer/deleter callbacks.
values_[T::kShouldThrowOnErrorIndex] = kZapValue;
}
values_[T::kIsolateIndex] = reinterpret_cast<Address>(isolate);
values_[T::kHolderIndex] = holder.ptr();
DCHECK(!IsJSGlobalObject(holder));
// Make sure the Isolate slot is safe to visit by GC (Isolate pointer
// is guaranteed to be page aligned).
DCHECK(HAS_SMI_TAG(values_[T::kIsolateIndex]));
}
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");
// }
values_[T::kReturnValueIndex] = kZapValue;
#endif // DEBUG
}
Maybe<InterceptorResult> PropertyCallbackArguments::GetBooleanReturnValue(
Isolate* isolate, v8::Intercepted intercepted,
const char* callback_kind_for_error_message, bool ignore_return_value) {
if (isolate->has_exception()) {
// TODO(ishell, 328490288): fix Node.js which has Setter/Definer
// interceptor callbacks not returning v8::Intercepted::kYes on exceptions.
if ((false) && DEBUG_BOOL && (intercepted == v8::Intercepted::kNo)) {
FATAL(
"Check failed: %s interceptor callback has thrown an "
"exception but hasn't returned v8::Intercepted::kYes.",
callback_kind_for_error_message);
}
return Nothing<InterceptorResult>();
}
if (intercepted == v8::Intercepted::kNo) {
// Not intercepted, there must be no side effects including exceptions.
DCHECK(!isolate->has_exception());
return Just(InterceptorResult::kNotIntercepted);
}
DCHECK_EQ(intercepted, v8::Intercepted::kYes);
AcceptSideEffects();
if (ignore_return_value) return Just(InterceptorResult::kTrue);
bool result = IsTrue(*GetReturnValue<Boolean>());
return Just(result ? InterceptorResult::kTrue : InterceptorResult::kFalse);
}
// -------------------------------------------------------------------------
// Named Interceptor callbacks.
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallNamedEnumerator(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor) {
DCHECK(!is_setter_definer_deleter_);
DCHECK(interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedEnumeratorCallback);
return CallPropertyEnumerator(isolate, interceptor);
}
// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallNamedQuery(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name) {
DCHECK(!is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedQueryCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(Smi::FromInt(v8::None));
NamedPropertyQueryCallback f = reinterpret_cast<NamedPropertyQueryCallback>(
interceptor->named_query(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor,
ExceptionContext::kNamedQuery);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<Object>();
}
DirectHandle<JSAny> PropertyCallbackArguments::CallNamedGetter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name) {
DCHECK(!is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
NamedPropertyGetterCallback f = reinterpret_cast<NamedPropertyGetterCallback>(
interceptor->named_getter(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kNamedGetter);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>();
}
Handle<JSAny> PropertyCallbackArguments::CallNamedDescriptor(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name) {
DCHECK(!is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDescriptorCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
NamedPropertyDescriptorCallback f =
reinterpret_cast<NamedPropertyDescriptorCallback>(
interceptor->named_descriptor(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kNamedDescriptor);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>();
}
v8::Intercepted PropertyCallbackArguments::CallNamedSetter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name, DirectHandle<Object> value) {
DCHECK(is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedSetterCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertySetterCallbackV2 f =
reinterpret_cast<NamedPropertySetterCallbackV2>(
interceptor->named_setter(isolate));
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kNamedSetter);
v8::Intercepted intercepted =
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallNamedDefiner(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name, const v8::PropertyDescriptor& desc) {
DCHECK(is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDefinerCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertyDefinerCallbackV2 f =
reinterpret_cast<NamedPropertyDefinerCallbackV2>(
interceptor->named_definer(isolate));
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kNamedDefiner);
v8::Intercepted intercepted =
f(v8::Utils::ToLocal(name), desc, callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallNamedDeleter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Name> name) {
DCHECK(is_setter_definer_deleter_);
DCHECK_NAME_COMPATIBLE(interceptor, name);
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDeleterCallback);
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertyDeleterCallback f =
reinterpret_cast<NamedPropertyDeleterCallback>(
interceptor->named_deleter(isolate));
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kNamedDeleter);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
return intercepted;
}
// -------------------------------------------------------------------------
// Indexed Interceptor callbacks.
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallIndexedEnumerator(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor) {
DCHECK(!is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedEnumeratorCallback);
return CallPropertyEnumerator(isolate, interceptor);
}
// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallIndexedQuery(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
uint32_t index) {
DCHECK(!is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedQueryCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(Smi::FromInt(v8::None));
IndexedPropertyQueryCallback f =
reinterpret_cast<IndexedPropertyQueryCallback>(
interceptor->indexed_query(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor,
ExceptionContext::kIndexedQuery);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<Object>();
}
DirectHandle<JSAny> PropertyCallbackArguments::CallIndexedGetter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
uint32_t index) {
DCHECK(!is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyGetterCallback f =
reinterpret_cast<IndexedPropertyGetterCallback>(
interceptor->indexed_getter(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kIndexedGetter);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>();
}
Handle<JSAny> PropertyCallbackArguments::CallIndexedDescriptor(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
uint32_t index) {
DCHECK(!is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDescriptorCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyDescriptorCallback f =
reinterpret_cast<IndexedPropertyDescriptorCallback>(
interceptor->indexed_descriptor(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kIndexedDescriptor);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>();
}
v8::Intercepted PropertyCallbackArguments::CallIndexedSetter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, uint32_t index,
DirectHandle<Object> value) {
DCHECK(is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedSetterCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertySetterCallback f =
reinterpret_cast<IndexedPropertySetterCallback>(
interceptor->indexed_setter(isolate));
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kIndexedSetter);
v8::Intercepted intercepted =
f(index, v8::Utils::ToLocal(value), callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallIndexedDefiner(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, uint32_t index,
const v8::PropertyDescriptor& desc) {
DCHECK(is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDefinerCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertyDefinerCallback f =
reinterpret_cast<IndexedPropertyDefinerCallback>(
interceptor->indexed_definer(isolate));
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kIndexedDefiner);
v8::Intercepted intercepted = f(index, desc, callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallIndexedDeleter(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
uint32_t index) {
DCHECK(is_setter_definer_deleter_);
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDeleterCallback);
set_property_key(index);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertyDeleterCallback f =
reinterpret_cast<IndexedPropertyDeleterCallback>(
interceptor->indexed_deleter(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, interceptor,
ExceptionContext::kIndexedDeleter);
v8::Intercepted intercepted = f(index, callback_info);
return intercepted;
}
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallPropertyEnumerator(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor) {
// Named and indexed enumerator callbacks have same signatures.
static_assert(std::is_same_v<NamedPropertyEnumeratorCallback,
IndexedPropertyEnumeratorCallback>);
DCHECK(!is_setter_definer_deleter_);
// The actual property key is not relevant for this callback.
set_property_key(0);
slot_at(kCallbackInfoIndex).store(*interceptor);
// Enumerator callback's return value is initialized with undefined even
// though it's supposed to return v8::Array.
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
// TODO(ishell): consider making it return v8::Intercepted to indicate
// whether the result was set or not.
IndexedPropertyEnumeratorCallback f;
if (interceptor->is_named()) {
f = reinterpret_cast<NamedPropertyEnumeratorCallback>(
interceptor->named_enumerator(isolate));
} else {
f = reinterpret_cast<IndexedPropertyEnumeratorCallback>(
interceptor->indexed_enumerator(isolate));
}
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Array, interceptor,
ExceptionContext::kNamedEnumerator);
f(callback_info);
DirectHandle<JSAny> result = GetReturnValue<JSAny>();
DCHECK(IsUndefined(*result) || IsJSObject(*result));
return Cast<JSObjectOrUndefined>(result);
}
uint32_t PropertyCallbackArguments::CallIndexedIndexOf(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor,
DirectHandle<Object> value, uint32_t start_index, uint32_t end_index,
uint32_t* in_out_length) {
DCHECK(!is_setter_definer_deleter_);
// The actual property key is not relevant for this callback.
set_property_key(0);
slot_at(kCallbackInfoIndex).store(*interceptor);
// IndexOf callback doesn't use return value.
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyIndexOfCallback f =
reinterpret_cast<IndexedPropertyIndexOfCallback>(
interceptor->indexed_index_of(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, interceptor,
ExceptionContext::kUnknown);
return f(v8::Utils::ToLocal(value), start_index, end_index, in_out_length,
callback_info);
}
DirectHandle<JSAny> PropertyCallbackArguments::CallIndexedIterableToList(
Isolate* isolate, DirectHandle<InterceptorInfo> interceptor) {
DCHECK(!is_setter_definer_deleter_);
set_property_key(0);
slot_at(kCallbackInfoIndex).store(*interceptor);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyIterableToListCallback f =
reinterpret_cast<IndexedPropertyIterableToListCallback>(
interceptor->indexed_iterable_to_list(isolate));
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kUnknown);
f(callback_info);
return GetReturnValue<JSAny>();
}
// -------------------------------------------------------------------------
// Accessors
DirectHandle<JSAny> PropertyCallbackArguments::CallAccessorGetter(
Isolate* isolate, DirectHandle<AccessorInfo> accessor_info,
DirectHandle<Name> name) {
DCHECK(!is_setter_definer_deleter_);
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*accessor_info);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
AccessorNameGetterCallback f = reinterpret_cast<AccessorNameGetterCallback>(
accessor_info->getter(isolate));
PREPARE_CALLBACK_INFO_ACCESSOR(isolate, f, v8::Value, accessor_info, holder(),
ACCESSOR_GETTER,
ExceptionContext::kAttributeGet);
f(v8::Utils::ToLocal(name), callback_info);
return GetReturnValue<JSAny>();
}
bool PropertyCallbackArguments::CallAccessorSetter(
Isolate* isolate, DirectHandle<AccessorInfo> accessor_info,
DirectHandle<Name> name, DirectHandle<Object> value) {
DCHECK(is_setter_definer_deleter_);
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
set_property_key(*name);
slot_at(kCallbackInfoIndex).store(*accessor_info);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
// The actual type of setter callback is either
// v8::AccessorNameSetterCallback or
// i::Accessors::AccessorNameBooleanSetterCallback, depending on whether the
// AccessorInfo was created by the API or internally (see accessors.cc).
// Here we handle both cases using the AccessorNameSetterCallback signature
// and checking whether the returned result is set to default value
// (the undefined value).
AccessorNameSetterCallbackV2 f =
reinterpret_cast<AccessorNameSetterCallbackV2>(
accessor_info->setter(isolate));
PREPARE_CALLBACK_INFO_ACCESSOR(isolate, f, v8::Boolean, accessor_info,
holder(), ACCESSOR_SETTER,
ExceptionContext::kAttributeSet);
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
// Historically, in case of v8::AccessorNameSetterCallback it wasn't allowed
// to set the result and not setting the result was treated as successful
// execution.
// During interceptors Api refactoring it was temporarily allowed to call
// v8::ReturnValue<void>::Set[NonEmpty](Local<S>) and the result was just
// converted to v8::Boolean which was then treated as a result of [[Set]].
// In case of AccessorNameBooleanSetterCallback, the result is always
// set to v8::Boolean or an exception is be thrown (in which case the
// result is ignored anyway). So, regardless of whether the signature was
// v8::AccessorNameSetterCallback or AccessorNameBooleanSetterCallback
// the result is guaranteed to be v8::Boolean value indicating success or
// failure.
DirectHandle<Boolean> result = GetReturnValue<Boolean>();
return IsTrue(*result);
}
#undef PREPARE_CALLBACK_INFO_ACCESSOR
#undef PREPARE_CALLBACK_INFO_INTERCEPTOR
} // namespace internal
} // namespace v8
#endif // V8_API_API_ARGUMENTS_INL_H_