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

CustomArgumentsBase::CustomArgumentsBase(Isolate* isolate)
    : Relocatable(isolate) {}

template <typename T>
CustomArguments<T>::~CustomArguments() {
  slot_at(kReturnValueIndex).store(Tagged<Object>(kHandleZapValue));
}

template <typename T>
template <typename V>
Handle<V> CustomArguments<T>::GetReturnValue(Isolate* isolate) const {
  // Check the ReturnValue.
  FullObjectSlot slot = slot_at(kReturnValueIndex);
  DCHECK(Is<JSAny>(*slot));
  return Cast<V>(Handle<Object>(slot.location()));
}

inline DirectHandle<JSObject> PropertyCallbackArguments::holder() const {
  return DirectHandle<JSObject>::FromSlot(slot_at(T::kHolderIndex).location());
}

inline DirectHandle<Object> PropertyCallbackArguments::receiver() const {
  return DirectHandle<Object>::FromSlot(slot_at(T::kThisIndex).location());
}

#define DCHECK_NAME_COMPATIBLE(interceptor, name) \
  DCHECK(interceptor->is_named());                \
  DCHECK(!name->IsPrivate());                     \
  DCHECK_IMPLIES(IsSymbol(*name), interceptor->can_intercept_symbols());

#define PREPARE_CALLBACK_INFO_ACCESSOR(ISOLATE, F, API_RETURN_TYPE,            \
                                       ACCESSOR_INFO, RECEIVER, ACCESSOR_KIND, \
                                       EXCEPTION_CONTEXT)                      \
  if (ISOLATE->should_check_side_effects() &&                                  \
      !ISOLATE->debug()->PerformSideEffectCheckForAccessor(                    \
          ACCESSOR_INFO, RECEIVER, 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);

FunctionCallbackArguments::FunctionCallbackArguments(
    Isolate* isolate, Tagged<FunctionTemplateInfo> target,
    Tagged<HeapObject> new_target, Address* argv, int argc)
    : Super(isolate), argv_(argv), argc_(argc) {
  slot_at(T::kTargetIndex).store(target);
  slot_at(T::kUnusedIndex).store(ReadOnlyRoots(isolate).undefined_value());
  slot_at(T::kNewTargetIndex).store(new_target);
  slot_at(T::kIsolateIndex)
      .store(Tagged<Object>(reinterpret_cast<Address>(isolate)));
  slot_at(T::kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
  slot_at(T::kContextIndex).store(isolate->context());
  DCHECK(IsSmi(*slot_at(T::kIsolateIndex)));
}

DirectHandle<Object> FunctionCallbackArguments::CallOrConstruct(
    Tagged<FunctionTemplateInfo> function, bool is_construct) {
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionCallback);
  HITRACE_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 {};
  }
  FunctionCallbackInfo<v8::Value> info(values_, argv_, argc_);
  ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f),
                                   is_construct ? ExceptionContext::kConstructor
                                                : ExceptionContext::kOperation,
                                   &info);
  f(info);
  return GetReturnValue<Object>(isolate);
}

PropertyCallbackArguments::PropertyCallbackArguments(
    Isolate* isolate, Tagged<Object> self, Tagged<JSObject> holder,
    Maybe<ShouldThrow> should_throw)
    : Super(isolate)
#ifdef DEBUG
      ,
      javascript_execution_counter_(isolate->javascript_execution_counter())
#endif  // DEBUG
{
  if (DEBUG_BOOL) {
    // Zap these fields to ensure that they are initialized by a subsequent
    // CallXXX(..).
    Tagged<Object> zap_value(kZapValue);
    slot_at(T::kPropertyKeyIndex).store(zap_value);
    slot_at(T::kReturnValueIndex).store(zap_value);
    slot_at(T::kCallbackInfoIndex).store(zap_value);
  }
  slot_at(T::kThisIndex).store(self);
  slot_at(T::kHolderIndex).store(holder);
  DCHECK(!IsJSGlobalObject(*holder));
  slot_at(T::kIsolateIndex)
      .store(Tagged<Object>(reinterpret_cast<Address>(isolate)));
  int value = Internals::kInferShouldThrowMode;
  if (should_throw.IsJust()) {
    value = should_throw.FromJust();
  }
  slot_at(T::kShouldThrowOnErrorIndex).store(Smi::FromInt(value));
  slot_at(T::kUnusedIndex).store(Smi::zero());
  DCHECK(IsSmi(*slot_at(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");
// }
#endif  // DEBUG
}

Maybe<InterceptorResult> PropertyCallbackArguments::GetBooleanReturnValue(
    v8::Intercepted intercepted, const char* callback_kind_for_error_message,
    bool ignore_return_value) {
  Isolate* isolate = this->isolate();
  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>(isolate), isolate);
  return Just(result ? InterceptorResult::kTrue : InterceptorResult::kFalse);
}

// -------------------------------------------------------------------------
// Named Interceptor callbacks.

DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallNamedEnumerator(
    DirectHandle<InterceptorInfo> interceptor) {
  DCHECK(interceptor->is_named());
  RCS_SCOPE(isolate(), RuntimeCallCounterId::kNamedEnumeratorCallback);
  HITRACE_RCS_SCOPE(isolate(), RuntimeCallCounterId::kNamedEnumeratorCallback);
  return CallPropertyEnumerator(interceptor);
}

// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallNamedQuery(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedQueryCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedQueryCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  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>(isolate);
}

DirectHandle<JSAny> PropertyCallbackArguments::CallNamedGetter(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  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>(isolate);
}

Handle<JSAny> PropertyCallbackArguments::CallNamedDescriptor(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDescriptorCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDescriptorCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  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>(isolate);
}

v8::Intercepted PropertyCallbackArguments::CallNamedSetter(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
    DirectHandle<Object> value) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedSetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedSetterCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
  NamedPropertySetterCallback f = reinterpret_cast<NamedPropertySetterCallback>(
      interceptor->named_setter(isolate));
  DirectHandle<InterceptorInfo> has_side_effects;
  PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, 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(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
    const v8::PropertyDescriptor& desc) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDefinerCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDefinerCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
  NamedPropertyDefinerCallback f =
      reinterpret_cast<NamedPropertyDefinerCallback>(
          interceptor->named_definer(isolate));
  DirectHandle<InterceptorInfo> has_side_effects;
  PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
                                    ExceptionContext::kNamedDefiner);
  v8::Intercepted intercepted =
      f(v8::Utils::ToLocal(name), desc, callback_info);
  return intercepted;
}

v8::Intercepted PropertyCallbackArguments::CallNamedDeleter(
    DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
  DCHECK_NAME_COMPATIBLE(interceptor, name);
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDeleterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDeleterCallback);
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(*name);
  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(
    DirectHandle<InterceptorInfo> interceptor) {
  DCHECK(!interceptor->is_named());
  RCS_SCOPE(isolate(), RuntimeCallCounterId::kIndexedEnumeratorCallback);
  HITRACE_RCS_SCOPE(isolate(), RuntimeCallCounterId::kIndexedEnumeratorCallback);
  return CallPropertyEnumerator(interceptor);
}

// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallIndexedQuery(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedQueryCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedQueryCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(Smi::FromInt(v8::None));
  IndexedPropertyQueryCallbackV2 f =
      reinterpret_cast<IndexedPropertyQueryCallbackV2>(
          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>(isolate);
}

DirectHandle<JSAny> PropertyCallbackArguments::CallIndexedGetter(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
  IndexedPropertyGetterCallbackV2 f =
      reinterpret_cast<IndexedPropertyGetterCallbackV2>(
          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>(isolate);
}

Handle<JSAny> PropertyCallbackArguments::CallIndexedDescriptor(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDescriptorCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDescriptorCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
  IndexedPropertyDescriptorCallbackV2 f =
      reinterpret_cast<IndexedPropertyDescriptorCallbackV2>(
          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>(isolate);
}

v8::Intercepted PropertyCallbackArguments::CallIndexedSetter(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index,
    DirectHandle<Object> value) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedSetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedSetterCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
  IndexedPropertySetterCallbackV2 f =
      reinterpret_cast<IndexedPropertySetterCallbackV2>(
          interceptor->indexed_setter(isolate));
  DirectHandle<InterceptorInfo> has_side_effects;
  PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
                                    ExceptionContext::kIndexedSetter);
  v8::Intercepted intercepted =
      f(index, v8::Utils::ToLocal(value), callback_info);
  return intercepted;
}

v8::Intercepted PropertyCallbackArguments::CallIndexedDefiner(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index,
    const v8::PropertyDescriptor& desc) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDefinerCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDefinerCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
  IndexedPropertyDefinerCallbackV2 f =
      reinterpret_cast<IndexedPropertyDefinerCallbackV2>(
          interceptor->indexed_definer(isolate));
  DirectHandle<InterceptorInfo> has_side_effects;
  PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
                                    ExceptionContext::kIndexedDefiner);
  v8::Intercepted intercepted = f(index, desc, callback_info);
  return intercepted;
}

v8::Intercepted PropertyCallbackArguments::CallIndexedDeleter(
    DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
  DCHECK(!interceptor->is_named());
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDeleterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDeleterCallback);
  index_ = index;
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // indexed callback marker
  slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
  IndexedPropertyDeleterCallbackV2 f =
      reinterpret_cast<IndexedPropertyDeleterCallbackV2>(
          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(
    DirectHandle<InterceptorInfo> interceptor) {
  // Named and indexed enumerator callbacks have same signatures.
  static_assert(std::is_same_v<NamedPropertyEnumeratorCallback,
                               IndexedPropertyEnumeratorCallback>);
  Isolate* isolate = this->isolate();
  slot_at(kCallbackInfoIndex).store(*interceptor);
  slot_at(kPropertyKeyIndex).store(Smi::zero());  // not relevant
  // 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>(isolate);
  DCHECK(IsUndefined(*result) || IsJSObject(*result));
  return Cast<JSObjectOrUndefined>(result);
}

// -------------------------------------------------------------------------
// Accessors

DirectHandle<JSAny> PropertyCallbackArguments::CallAccessorGetter(
    DirectHandle<AccessorInfo> accessor_info, DirectHandle<Name> name) {
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback);
  // Unlike interceptor callbacks we know that the property exists, so
  // the callback is allowed to have side effects.
  AcceptSideEffects();

  slot_at(kCallbackInfoIndex).store(*accessor_info);
  slot_at(kPropertyKeyIndex).store(*name);
  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,
                                 receiver(), ACCESSOR_GETTER,
                                 ExceptionContext::kAttributeGet);
  f(v8::Utils::ToLocal(name), callback_info);
  return GetReturnValue<JSAny>(isolate);
}

bool PropertyCallbackArguments::CallAccessorSetter(
    DirectHandle<AccessorInfo> accessor_info, DirectHandle<Name> name,
    DirectHandle<Object> value) {
  Isolate* isolate = this->isolate();
  RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback);
  HITRACE_RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback);
  // Unlike interceptor callbacks we know that the property exists, so
  // the callback is allowed to have side effects.
  AcceptSideEffects();

  slot_at(kCallbackInfoIndex).store(*accessor_info);
  slot_at(kPropertyKeyIndex).store(*name);
  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).
  // TODO(ishell, 348660658): update V8 Api to allow setter callbacks provide
  // the result of [[Set]] operation according to JavaScript semantics.
  AccessorNameSetterCallback f = reinterpret_cast<AccessorNameSetterCallback>(
      accessor_info->setter(isolate));
  PREPARE_CALLBACK_INFO_ACCESSOR(isolate, f, void, accessor_info, receiver(),
                                 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>(isolate);
  return IsTrue(*result, isolate);
}

#undef PREPARE_CALLBACK_INFO_ACCESSOR
#undef PREPARE_CALLBACK_INFO_INTERCEPTOR

}  // namespace internal
}  // namespace v8

#endif  // V8_API_API_ARGUMENTS_INL_H_