// 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_IC_ACCESSOR_ASSEMBLER_H_
#define V8_IC_ACCESSOR_ASSEMBLER_H_

#include "src/base/optional.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/compiler/code-assembler.h"

namespace v8 {
namespace internal {

namespace compiler {
class CodeAssemblerState;
}  // namespace compiler

class ExitPoint;

class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
 public:
  explicit AccessorAssembler(compiler::CodeAssemblerState* state)
      : CodeStubAssembler(state) {}

  void GenerateLoadIC();
  void GenerateLoadIC_Megamorphic();
  void GenerateLoadIC_Noninlined();
  void GenerateLoadIC_NoFeedback();
  void GenerateLoadGlobalIC_NoFeedback();
  void GenerateLoadICTrampoline();
  void GenerateLoadICBaseline();
  void GenerateLoadICTrampoline_Megamorphic();
  void GenerateLoadSuperIC();
  void GenerateLoadSuperICBaseline();
  void GenerateKeyedLoadIC();
  void GenerateKeyedLoadIC_Megamorphic();
  void GenerateKeyedLoadIC_PolymorphicName();
  void GenerateKeyedLoadICTrampoline();
  void GenerateKeyedLoadICBaseline();
  void GenerateKeyedLoadICTrampoline_Megamorphic();
  void GenerateStoreIC();
  void GenerateStoreICTrampoline();
  void GenerateStoreICBaseline();
  void GenerateStoreOwnIC();
  void GenerateStoreOwnICTrampoline();
  void GenerateStoreOwnICBaseline();
  void GenerateStoreGlobalIC();
  void GenerateStoreGlobalICTrampoline();
  void GenerateStoreGlobalICBaseline();
  void GenerateCloneObjectIC();
  void GenerateCloneObjectICBaseline();
  void GenerateCloneObjectIC_Slow();
  void GenerateKeyedHasIC();
  void GenerateKeyedHasICBaseline();
  void GenerateKeyedHasIC_Megamorphic();
  void GenerateKeyedHasIC_PolymorphicName();

  void GenerateLoadGlobalIC(TypeofMode typeof_mode);
  void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode);
  void GenerateLoadGlobalICBaseline(TypeofMode typeof_mode);
  void GenerateLookupGlobalICBaseline(TypeofMode typeof_mode);
  void GenerateLookupContextBaseline(TypeofMode typeof_mode);

  void GenerateKeyedStoreIC();
  void GenerateKeyedStoreICTrampoline();
  void GenerateKeyedStoreICBaseline();

  void GenerateKeyedDefineOwnIC();
  void GenerateKeyedDefineOwnICTrampoline();
  void GenerateKeyedDefineOwnICBaseline();

  void GenerateStoreInArrayLiteralIC();
  void GenerateStoreInArrayLiteralICBaseline();

  void TryProbeStubCache(StubCache* stub_cache,
                         TNode<Object> lookup_start_object, TNode<Name> name,
                         Label* if_handler, TVariable<MaybeObject>* var_handler,
                         Label* if_miss);

  TNode<IntPtrT> StubCachePrimaryOffsetForTesting(TNode<Name> name,
                                                  TNode<Map> map) {
    return StubCachePrimaryOffset(name, map);
  }
  TNode<IntPtrT> StubCacheSecondaryOffsetForTesting(TNode<Name> name,
                                                    TNode<Map> map) {
    return StubCacheSecondaryOffset(name, map);
  }

  struct LoadICParameters {
    LoadICParameters(
        TNode<Context> context, TNode<Object> receiver, TNode<Object> name,
        TNode<TaggedIndex> slot, TNode<HeapObject> vector,
        base::Optional<TNode<Object>> lookup_start_object = base::nullopt)
        : context_(context),
          receiver_(receiver),
          name_(name),
          slot_(slot),
          vector_(vector),
          lookup_start_object_(lookup_start_object ? lookup_start_object.value()
                                                   : receiver) {}

    LoadICParameters(const LoadICParameters* p, TNode<Object> unique_name)
        : context_(p->context_),
          receiver_(p->receiver_),
          name_(unique_name),
          slot_(p->slot_),
          vector_(p->vector_),
          lookup_start_object_(p->lookup_start_object_) {}

    TNode<Context> context() const { return context_; }
    TNode<Object> receiver() const { return receiver_; }
    TNode<Object> name() const { return name_; }
    TNode<TaggedIndex> slot() const { return slot_; }
    TNode<HeapObject> vector() const { return vector_; }
    TNode<Object> lookup_start_object() const {
      return lookup_start_object_.value();
    }

    // Usable in cases where the receiver and the lookup start object are
    // expected to be the same, i.e., when "receiver != lookup_start_object"
    // case is not supported or not expected by the surrounding code.
    TNode<Object> receiver_and_lookup_start_object() const {
      DCHECK_EQ(receiver_, lookup_start_object_);
      return receiver_;
    }

   private:
    TNode<Context> context_;
    TNode<Object> receiver_;
    TNode<Object> name_;
    TNode<TaggedIndex> slot_;
    TNode<HeapObject> vector_;
    base::Optional<TNode<Object>> lookup_start_object_;
  };

  struct LazyLoadICParameters {
    LazyLoadICParameters(
        LazyNode<Context> context, TNode<Object> receiver,
        LazyNode<Object> name, LazyNode<TaggedIndex> slot,
        TNode<HeapObject> vector,
        base::Optional<TNode<Object>> lookup_start_object = base::nullopt)
        : context_(context),
          receiver_(receiver),
          name_(name),
          slot_(slot),
          vector_(vector),
          lookup_start_object_(lookup_start_object ? lookup_start_object.value()
                                                   : receiver) {}

    explicit LazyLoadICParameters(const LoadICParameters* p)
        : receiver_(p->receiver()),
          vector_(p->vector()),
          lookup_start_object_(p->lookup_start_object()) {
      slot_ = [=] { return p->slot(); };
      context_ = [=] { return p->context(); };
      name_ = [=] { return p->name(); };
    }

    TNode<Context> context() const { return context_(); }
    TNode<Object> receiver() const { return receiver_; }
    TNode<Object> name() const { return name_(); }
    TNode<TaggedIndex> slot() const { return slot_(); }
    TNode<HeapObject> vector() const { return vector_; }
    TNode<Object> lookup_start_object() const { return lookup_start_object_; }

    // Usable in cases where the receiver and the lookup start object are
    // expected to be the same, i.e., when "receiver != lookup_start_object"
    // case is not supported or not expected by the surrounding code.
    TNode<Object> receiver_and_lookup_start_object() const {
      DCHECK_EQ(receiver_, lookup_start_object_);
      return receiver_;
    }

   private:
    LazyNode<Context> context_;
    TNode<Object> receiver_;
    LazyNode<Object> name_;
    LazyNode<TaggedIndex> slot_;
    TNode<HeapObject> vector_;
    TNode<Object> lookup_start_object_;
  };

  void LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector,
                    const LazyNode<TaggedIndex>& lazy_slot,
                    const LazyNode<Context>& lazy_context,
                    const LazyNode<Name>& lazy_name, TypeofMode typeof_mode,
                    ExitPoint* exit_point);

  // Specialized LoadIC for inlined bytecode handler, hand-tuned to omit frame
  // construction on common paths.
  void LoadIC_BytecodeHandler(const LazyLoadICParameters* p,
                              ExitPoint* exit_point);

  // Loads dataX field from the DataHandler object.
  TNode<MaybeObject> LoadHandlerDataField(TNode<DataHandler> handler,
                                          int data_index);

 protected:
  enum class StoreICMode {
    kDefault,
    kStoreOwn,
    kDefineOwn,
  };
  struct StoreICParameters {
    StoreICParameters(TNode<Context> context,
                      base::Optional<TNode<Object>> receiver,
                      TNode<Object> name, TNode<Object> value,
                      TNode<TaggedIndex> slot, TNode<HeapObject> vector,
                      StoreICMode mode)
        : context_(context),
          receiver_(receiver),
          name_(name),
          value_(value),
          slot_(slot),
          vector_(vector),
          mode_(mode) {}

    TNode<Context> context() const { return context_; }
    TNode<Object> receiver() const { return receiver_.value(); }
    TNode<Object> name() const { return name_; }
    TNode<Object> value() const { return value_; }
    TNode<TaggedIndex> slot() const { return slot_; }
    TNode<HeapObject> vector() const { return vector_; }

    TNode<Object> lookup_start_object() const { return receiver(); }

    bool receiver_is_null() const { return !receiver_.has_value(); }

    bool IsStoreOwn() const { return mode_ == StoreICMode::kStoreOwn; }
    bool IsDefineOwn() const { return mode_ == StoreICMode::kDefineOwn; }
    bool IsAnyStoreOwn() const { return IsStoreOwn() || IsDefineOwn(); }

   private:
    TNode<Context> context_;
    base::Optional<TNode<Object>> receiver_;
    TNode<Object> name_;
    TNode<Object> value_;
    TNode<TaggedIndex> slot_;
    TNode<HeapObject> vector_;
    StoreICMode mode_;
  };

  enum class LoadAccessMode { kLoad, kHas };
  enum class ICMode { kNonGlobalIC, kGlobalIC };
  enum ElementSupport { kOnlyProperties, kSupportElements };
  void HandleStoreICHandlerCase(
      const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss,
      ICMode ic_mode, ElementSupport support_elements = kOnlyProperties);
  enum StoreTransitionMapFlags {
    kDontCheckPrototypeValidity = 0,
    kCheckPrototypeValidity = 1 << 0,
    kValidateTransitionHandler = 1 << 1,
    kStoreTransitionMapFlagsMask =
        kCheckPrototypeValidity | kValidateTransitionHandler,
  };
  void HandleStoreICTransitionMapHandlerCase(const StoreICParameters* p,
                                             TNode<Map> transition_map,
                                             Label* miss,
                                             StoreTransitionMapFlags flags);

  void JumpIfDataProperty(TNode<Uint32T> details, Label* writable,
                          Label* readonly);

  void InvalidateValidityCellIfPrototype(
      TNode<Map> map, base::Optional<TNode<Uint32T>> bitfield3 = base::nullopt);

  void OverwriteExistingFastDataProperty(TNode<HeapObject> object,
                                         TNode<Map> object_map,
                                         TNode<DescriptorArray> descriptors,
                                         TNode<IntPtrT> descriptor_name_index,
                                         TNode<Uint32T> details,
                                         TNode<Object> value, Label* slow,
                                         bool do_transitioning_store);

  TNode<BoolT> IsPropertyDetailsConst(TNode<Uint32T> details);

  void CheckFieldType(TNode<DescriptorArray> descriptors,
                      TNode<IntPtrT> name_index, TNode<Word32T> representation,
                      TNode<Object> value, Label* bailout);

 private:
  // Stub generation entry points.

  // LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains
  // logic not inlined into Ignition bytecode handlers.
  void LoadIC(const LoadICParameters* p);

  // Can be used in the receiver != lookup_start_object case.
  void LoadIC_Noninlined(const LoadICParameters* p,
                         TNode<Map> lookup_start_object_map,
                         TNode<HeapObject> feedback,
                         TVariable<MaybeObject>* var_handler, Label* if_handler,
                         Label* miss, ExitPoint* exit_point);

  void LoadSuperIC(const LoadICParameters* p);

  TNode<Object> LoadDescriptorValue(TNode<Map> map,
                                    TNode<IntPtrT> descriptor_entry);
  TNode<MaybeObject> LoadDescriptorValueOrFieldType(
      TNode<Map> map, TNode<IntPtrT> descriptor_entry);

  void LoadIC_NoFeedback(const LoadICParameters* p, TNode<Smi> smi_typeof_mode);
  void LoadSuperIC_NoFeedback(const LoadICParameters* p);
  void LoadGlobalIC_NoFeedback(TNode<Context> context, TNode<Object> name,
                               TNode<Smi> smi_typeof_mode);

  void KeyedLoadIC(const LoadICParameters* p, LoadAccessMode access_mode);
  void KeyedLoadICGeneric(const LoadICParameters* p);
  void KeyedLoadICPolymorphicName(const LoadICParameters* p,
                                  LoadAccessMode access_mode);

  void StoreIC(const StoreICParameters* p);
  void StoreGlobalIC(const StoreICParameters* p);
  void StoreGlobalIC_PropertyCellCase(TNode<PropertyCell> property_cell,
                                      TNode<Object> value,
                                      ExitPoint* exit_point, Label* miss);
  void KeyedStoreIC(const StoreICParameters* p);
  void KeyedDefineOwnIC(const StoreICParameters* p);
  void StoreInArrayLiteralIC(const StoreICParameters* p);

  // IC dispatcher behavior.

  // Checks monomorphic case. Returns {feedback} entry of the vector.
  TNode<MaybeObject> TryMonomorphicCase(TNode<TaggedIndex> slot,
                                        TNode<FeedbackVector> vector,
                                        TNode<Map> lookup_start_object_map,
                                        Label* if_handler,
                                        TVariable<MaybeObject>* var_handler,
                                        Label* if_miss);
  void HandlePolymorphicCase(TNode<Map> lookup_start_object_map,
                             TNode<WeakFixedArray> feedback, Label* if_handler,
                             TVariable<MaybeObject>* var_handler,
                             Label* if_miss);

  void TryMegaDOMCase(TNode<Object> lookup_start_object,
                      TNode<Map> lookup_start_object_map,
                      TVariable<MaybeObject>* var_handler, TNode<Object> vector,
                      TNode<TaggedIndex> slot, Label* miss,
                      ExitPoint* exit_point);

  // LoadIC implementation.
  void HandleLoadICHandlerCase(
      const LazyLoadICParameters* p, TNode<Object> handler, Label* miss,
      ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC,
      OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined,
      ElementSupport support_elements = kOnlyProperties,
      LoadAccessMode access_mode = LoadAccessMode::kLoad);

  void HandleLoadICSmiHandlerCase(const LazyLoadICParameters* p,
                                  TNode<Object> holder, TNode<Smi> smi_handler,
                                  TNode<Object> handler, Label* miss,
                                  ExitPoint* exit_point, ICMode ic_mode,
                                  OnNonExistent on_nonexistent,
                                  ElementSupport support_elements,
                                  LoadAccessMode access_mode);

  void HandleLoadICProtoHandler(const LazyLoadICParameters* p,
                                TNode<DataHandler> handler,
                                TVariable<Object>* var_holder,
                                TVariable<Object>* var_smi_handler,
                                Label* if_smi_handler, Label* miss,
                                ExitPoint* exit_point, ICMode ic_mode,
                                LoadAccessMode access_mode);

  void HandleLoadCallbackProperty(const LazyLoadICParameters* p,
                                  TNode<JSObject> holder,
                                  TNode<WordT> handler_word,
                                  ExitPoint* exit_point);

  void HandleLoadAccessor(const LazyLoadICParameters* p,
                          TNode<CallHandlerInfo> call_handler_info,
                          TNode<WordT> handler_word, TNode<DataHandler> handler,
                          TNode<IntPtrT> handler_kind, ExitPoint* exit_point);

  void HandleLoadField(TNode<JSObject> holder, TNode<WordT> handler_word,
                       TVariable<Float64T>* var_double_value,
                       Label* rebox_double, Label* miss, ExitPoint* exit_point);

#if V8_ENABLE_WEBASSEMBLY
  void HandleLoadWasmField(TNode<WasmObject> holder,
                           TNode<Int32T> wasm_value_type,
                           TNode<IntPtrT> field_offset,
                           TVariable<Float64T>* var_double_value,
                           Label* rebox_double, ExitPoint* exit_point);

  void HandleLoadWasmField(TNode<WasmObject> holder, TNode<WordT> handler_word,
                           TVariable<Float64T>* var_double_value,
                           Label* rebox_double, ExitPoint* exit_point);
#endif  // V8_ENABLE_WEBASSEMBLY

  void EmitAccessCheck(TNode<Context> expected_native_context,
                       TNode<Context> context, TNode<Object> receiver,
                       Label* can_access, Label* miss);

  void HandleLoadICSmiHandlerLoadNamedCase(
      const LazyLoadICParameters* p, TNode<Object> holder,
      TNode<IntPtrT> handler_kind, TNode<WordT> handler_word,
      Label* rebox_double, TVariable<Float64T>* var_double_value,
      TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode,
      OnNonExistent on_nonexistent, ElementSupport support_elements);

  void HandleLoadICSmiHandlerHasNamedCase(const LazyLoadICParameters* p,
                                          TNode<Object> holder,
                                          TNode<IntPtrT> handler_kind,
                                          Label* miss, ExitPoint* exit_point,
                                          ICMode ic_mode);

  // LoadGlobalIC implementation.

  void LoadGlobalIC_TryPropertyCellCase(TNode<FeedbackVector> vector,
                                        TNode<TaggedIndex> slot,
                                        const LazyNode<Context>& lazy_context,
                                        ExitPoint* exit_point,
                                        Label* try_handler, Label* miss);

  void LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector,
                                   TNode<TaggedIndex> slot,
                                   const LazyNode<Context>& lazy_context,
                                   const LazyNode<Name>& lazy_name,
                                   TypeofMode typeof_mode,
                                   ExitPoint* exit_point, Label* miss);

  // This is a copy of ScriptContextTable::Lookup. They should be kept in sync.
  void ScriptContextTableLookup(TNode<Name> name,
                                TNode<NativeContext> native_context,
                                Label* found_hole, Label* not_found);

  // StoreIC implementation.

  void HandleStoreICProtoHandler(const StoreICParameters* p,
                                 TNode<StoreHandler> handler, Label* miss,
                                 ICMode ic_mode,
                                 ElementSupport support_elements);
  void HandleStoreICSmiHandlerCase(TNode<Word32T> handler_word,
                                   TNode<JSObject> holder, TNode<Object> value,
                                   Label* miss);
  void HandleStoreFieldAndReturn(TNode<Word32T> handler_word,
                                 TNode<JSObject> holder, TNode<Object> value,
                                 base::Optional<TNode<Float64T>> double_value,
                                 Representation representation, Label* miss);

  void CheckPrototypeValidityCell(TNode<Object> maybe_validity_cell,
                                  Label* miss);
  void HandleStoreICNativeDataProperty(const StoreICParameters* p,
                                       TNode<HeapObject> holder,
                                       TNode<Word32T> handler_word);

  void HandleStoreToProxy(const StoreICParameters* p, TNode<JSProxy> proxy,
                          Label* miss, ElementSupport support_elements);

  void HandleStoreAccessor(const StoreICParameters* p, TNode<HeapObject> holder,
                           TNode<Word32T> handler_word);

  // KeyedLoadIC_Generic implementation.

  void GenericElementLoad(TNode<HeapObject> lookup_start_object,
                          TNode<Map> lookup_start_object_map,
                          TNode<Int32T> lookup_start_object_instance_type,
                          TNode<IntPtrT> index, Label* slow);

  enum UseStubCache { kUseStubCache, kDontUseStubCache };
  void GenericPropertyLoad(TNode<HeapObject> lookup_start_object,
                           TNode<Map> lookup_start_object_map,
                           TNode<Int32T> lookup_start_object_instance_type,
                           const LoadICParameters* p, Label* slow,
                           UseStubCache use_stub_cache = kUseStubCache);

  // Low-level helpers.

  using OnCodeHandler = std::function<void(TNode<CodeT> code_handler)>;
  using OnFoundOnLookupStartObject = std::function<void(
      TNode<PropertyDictionary> properties, TNode<IntPtrT> name_index)>;

  template <typename ICHandler, typename ICParameters>
  TNode<Object> HandleProtoHandler(
      const ICParameters* p, TNode<DataHandler> handler,
      const OnCodeHandler& on_code_handler,
      const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
      Label* miss, ICMode ic_mode);

  void CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word,
                                            TNode<JSObject> holder,
                                            TNode<Object> value,
                                            Label* bailout);
  // Double fields store double values in a mutable box, where stores are
  // writes into this box rather than HeapNumber assignment.
  void CheckDescriptorConsidersNumbersMutable(TNode<Word32T> handler_word,
                                              TNode<JSObject> holder,
                                              Label* bailout);

  // Extends properties backing store by JSObject::kFieldsAdded elements,
  // returns updated properties backing store.
  TNode<PropertyArray> ExtendPropertiesBackingStore(TNode<HeapObject> object,
                                                    TNode<IntPtrT> index);

  void EmitFastElementsBoundsCheck(TNode<JSObject> object,
                                   TNode<FixedArrayBase> elements,
                                   TNode<IntPtrT> intptr_index,
                                   TNode<BoolT> is_jsarray_condition,
                                   Label* miss);
  void EmitElementLoad(TNode<HeapObject> object, TNode<Word32T> elements_kind,
                       TNode<IntPtrT> key, TNode<BoolT> is_jsarray_condition,
                       Label* if_hole, Label* rebox_double,
                       TVariable<Float64T>* var_double_value,
                       Label* unimplemented_elements_kind, Label* out_of_bounds,
                       Label* miss, ExitPoint* exit_point,
                       LoadAccessMode access_mode = LoadAccessMode::kLoad);

  // Stub cache access helpers.

  // This enum is used here as a replacement for StubCache::Table to avoid
  // including stub cache header.
  enum StubCacheTable : int;

  TNode<IntPtrT> StubCachePrimaryOffset(TNode<Name> name, TNode<Map> map);
  TNode<IntPtrT> StubCacheSecondaryOffset(TNode<Name> name, TNode<Map> map);

  void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id,
                              TNode<IntPtrT> entry_offset, TNode<Object> name,
                              TNode<Map> map, Label* if_handler,
                              TVariable<MaybeObject>* var_handler,
                              Label* if_miss);

  void BranchIfPrototypesHaveNoElements(TNode<Map> receiver_map,
                                        Label* definitely_no_elements,
                                        Label* possibly_elements);
};

// Abstraction over direct and indirect exit points. Direct exits correspond to
// tailcalls and Return, while indirect exits store the result in a variable
// and then jump to an exit label.
class ExitPoint {
 private:
  using CodeAssemblerLabel = compiler::CodeAssemblerLabel;

 public:
  using IndirectReturnHandler = std::function<void(TNode<Object> result)>;

  explicit ExitPoint(CodeStubAssembler* assembler)
      : ExitPoint(assembler, nullptr) {}

  ExitPoint(CodeStubAssembler* assembler,
            const IndirectReturnHandler& indirect_return_handler)
      : asm_(assembler), indirect_return_handler_(indirect_return_handler) {}

  ExitPoint(CodeStubAssembler* assembler, CodeAssemblerLabel* out,
            compiler::CodeAssembler::TVariable<Object>* var_result)
      : ExitPoint(assembler, [=](TNode<Object> result) {
          *var_result = result;
          assembler->Goto(out);
        }) {
    DCHECK_EQ(out != nullptr, var_result != nullptr);
  }

  template <class... TArgs>
  void ReturnCallRuntime(Runtime::FunctionId function, TNode<Context> context,
                         TArgs... args) {
    if (IsDirect()) {
      asm_->TailCallRuntime(function, context, args...);
    } else {
      indirect_return_handler_(asm_->CallRuntime(function, context, args...));
    }
  }

  template <class... TArgs>
  void ReturnCallStub(Callable const& callable, TNode<Context> context,
                      TArgs... args) {
    if (IsDirect()) {
      asm_->TailCallStub(callable, context, args...);
    } else {
      indirect_return_handler_(asm_->CallStub(callable, context, args...));
    }
  }

  template <class... TArgs>
  void ReturnCallStub(const CallInterfaceDescriptor& descriptor,
                      TNode<Code> target, TNode<Context> context,
                      TArgs... args) {
    if (IsDirect()) {
      asm_->TailCallStub(descriptor, target, context, args...);
    } else {
      indirect_return_handler_(
          asm_->CallStub(descriptor, target, context, args...));
    }
  }

  void Return(const TNode<Object> result) {
    if (IsDirect()) {
      asm_->Return(result);
    } else {
      indirect_return_handler_(result);
    }
  }

  bool IsDirect() const { return !indirect_return_handler_; }

 private:
  CodeStubAssembler* const asm_;
  IndirectReturnHandler indirect_return_handler_;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_IC_ACCESSOR_ASSEMBLER_H_