// Copyright 2015 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_OBJECTS_BODY_DESCRIPTORS_INL_H_
#define V8_OBJECTS_BODY_DESCRIPTORS_INL_H_

#include "src/feedback-vector.h"
#include "src/objects-body-descriptors.h"
#include "src/objects/cell.h"
#include "src/objects/foreign-inl.h"
#include "src/objects/hash-table.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-weak-refs.h"
#include "src/objects/oddball.h"
#include "src/objects/slots.h"
#include "src/reloc-info.h"
#include "src/transitions.h"
#include "src/wasm/wasm-objects-inl.h"

namespace v8 {
namespace internal {

template <int start_offset>
int FlexibleBodyDescriptor<start_offset>::SizeOf(Map map, HeapObject object) {
  return object->SizeFromMap(map);
}

template <int start_offset>
int FlexibleWeakBodyDescriptor<start_offset>::SizeOf(Map map,
                                                     HeapObject object) {
  return object->SizeFromMap(map);
}

bool BodyDescriptorBase::IsValidJSObjectSlotImpl(Map map, HeapObject obj,
                                                 int offset) {
#ifdef V8_COMPRESS_POINTERS
  STATIC_ASSERT(kEmbedderDataSlotSize == 2 * kTaggedSize);
  int embedder_fields_offset = JSObject::GetEmbedderFieldsStartOffset(map);
  int inobject_fields_offset = map->GetInObjectPropertyOffset(0);
  // |embedder_fields_offset| may be greater than |inobject_fields_offset| if
  // the object does not have embedder fields but the check handles this
  // case properly.
  if (embedder_fields_offset <= offset && offset < inobject_fields_offset) {
    // offset points to embedder fields area:
    // [embedder_fields_offset, inobject_fields_offset).
    STATIC_ASSERT(base::bits::IsPowerOfTwo(kEmbedderDataSlotSize));
    return ((offset - embedder_fields_offset) & (kEmbedderDataSlotSize - 1)) ==
           EmbedderDataSlot::kTaggedPayloadOffset;
  }
#else
  // We store raw aligned pointers as Smis, so it's safe to treat the whole
  // embedder field area as tagged slots.
  STATIC_ASSERT(kEmbedderDataSlotSize == kTaggedSize);
#endif
  if (!FLAG_unbox_double_fields || map->HasFastPointerLayout()) {
    return true;
  } else {
    DCHECK(FLAG_unbox_double_fields);
    DCHECK(IsAligned(offset, kSystemPointerSize));

    LayoutDescriptorHelper helper(map);
    DCHECK(!helper.all_fields_tagged());
    return helper.IsTagged(offset);
  }
}

template <typename ObjectVisitor>
void BodyDescriptorBase::IterateJSObjectBodyImpl(Map map, HeapObject obj,
                                                 int start_offset,
                                                 int end_offset,
                                                 ObjectVisitor* v) {
#ifdef V8_COMPRESS_POINTERS
  STATIC_ASSERT(kEmbedderDataSlotSize == 2 * kTaggedSize);
  int header_size = JSObject::GetHeaderSize(map);
  int inobject_fields_offset = map->GetInObjectPropertyOffset(0);
  // We are always requested to process header and embedder fields.
  DCHECK_LE(inobject_fields_offset, end_offset);
  // Embedder fields are located between header rouned up to the system pointer
  // size and inobject properties.
  if (header_size < inobject_fields_offset) {
    // There are embedder fields.
    IteratePointers(obj, start_offset, header_size, v);
    // Iterate only tagged payload of the embedder slots and skip raw payload.
    int embedder_fields_offset = RoundUp(header_size, kSystemPointerSize);
    DCHECK_EQ(embedder_fields_offset,
              JSObject::GetEmbedderFieldsStartOffset(map));
    for (int offset =
             embedder_fields_offset + EmbedderDataSlot::kTaggedPayloadOffset;
         offset < inobject_fields_offset; offset += kEmbedderDataSlotSize) {
      IteratePointer(obj, offset, v);
    }
    // Proceed processing inobject properties.
    start_offset = inobject_fields_offset;
  }
#else
  // We store raw aligned pointers as Smis, so it's safe to iterate the whole
  // embedder field area as tagged slots.
  STATIC_ASSERT(kEmbedderDataSlotSize == kTaggedSize);
#endif
  if (!FLAG_unbox_double_fields || map->HasFastPointerLayout()) {
    IteratePointers(obj, start_offset, end_offset, v);
  } else {
    DCHECK(FLAG_unbox_double_fields);
    DCHECK(IsAligned(start_offset, kSystemPointerSize) &&
           IsAligned(end_offset, kSystemPointerSize));

    LayoutDescriptorHelper helper(map);
    DCHECK(!helper.all_fields_tagged());
    for (int offset = start_offset; offset < end_offset;) {
      int end_of_region_offset;
      if (helper.IsTagged(offset, end_offset, &end_of_region_offset)) {
        IteratePointers(obj, offset, end_of_region_offset, v);
      }
      offset = end_of_region_offset;
    }
  }
}

template <typename ObjectVisitor>
DISABLE_CFI_PERF void BodyDescriptorBase::IteratePointers(HeapObject obj,
                                                          int start_offset,
                                                          int end_offset,
                                                          ObjectVisitor* v) {
  v->VisitPointers(obj, HeapObject::RawField(obj, start_offset),
                   HeapObject::RawField(obj, end_offset));
}

template <typename ObjectVisitor>
void BodyDescriptorBase::IteratePointer(HeapObject obj, int offset,
                                        ObjectVisitor* v) {
  v->VisitPointer(obj, HeapObject::RawField(obj, offset));
}

template <typename ObjectVisitor>
DISABLE_CFI_PERF void BodyDescriptorBase::IterateMaybeWeakPointers(
    HeapObject obj, int start_offset, int end_offset, ObjectVisitor* v) {
  v->VisitPointers(obj, HeapObject::RawMaybeWeakField(obj, start_offset),
                   HeapObject::RawMaybeWeakField(obj, end_offset));
}

template <typename ObjectVisitor>
void BodyDescriptorBase::IterateMaybeWeakPointer(HeapObject obj, int offset,
                                                 ObjectVisitor* v) {
  v->VisitPointer(obj, HeapObject::RawMaybeWeakField(obj, offset));
}

template <typename ObjectVisitor>
DISABLE_CFI_PERF void BodyDescriptorBase::IterateCustomWeakPointers(
    HeapObject obj, int start_offset, int end_offset, ObjectVisitor* v) {
  v->VisitCustomWeakPointers(obj, HeapObject::RawField(obj, start_offset),
                             HeapObject::RawField(obj, end_offset));
}

template <typename ObjectVisitor>
void BodyDescriptorBase::IterateCustomWeakPointer(HeapObject obj, int offset,
                                                  ObjectVisitor* v) {
  v->VisitCustomWeakPointer(obj, HeapObject::RawField(obj, offset));
}

class JSObject::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static const int kStartOffset = JSReceiver::kPropertiesOrHashOffset;

  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    if (offset < kStartOffset) return false;
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IterateJSObjectBodyImpl(map, obj, kStartOffset, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class JSObject::FastBodyDescriptor final : public BodyDescriptorBase {
 public:
  static const int kStartOffset = JSReceiver::kPropertiesOrHashOffset;

  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= kStartOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, kStartOffset, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class JSWeakCell::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, JSReceiver::kPropertiesOrHashOffset, kTargetOffset, v);
    IterateCustomWeakPointer(obj, kTargetOffset, v);
    IterateJSObjectBodyImpl(map, obj, kTargetOffset + kTaggedSize, object_size,
                            v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class JSWeakRef::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return JSObject::BodyDescriptor::IsValidSlot(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, JSReceiver::kPropertiesOrHashOffset, kTargetOffset, v);
    IterateCustomWeakPointer(obj, kTargetOffset, v);
    IteratePointers(obj, kTargetOffset + kPointerSize, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class SharedFunctionInfo::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return FixedBodyDescriptor<kStartOfPointerFieldsOffset,
                               kEndOfTaggedFieldsOffset,
                               kAlignedSize>::IsValidSlot(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IterateCustomWeakPointer(obj, kFunctionDataOffset, v);
    IteratePointers(obj,
                    SharedFunctionInfo::kStartOfAlwaysStrongPointerFieldsOffset,
                    SharedFunctionInfo::kEndOfTaggedFieldsOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class AllocationSite::BodyDescriptor final : public BodyDescriptorBase {
 public:
  STATIC_ASSERT(AllocationSite::kCommonPointerFieldEndOffset ==
                AllocationSite::kPretenureDataOffset);
  STATIC_ASSERT(AllocationSite::kPretenureDataOffset + kInt32Size ==
                AllocationSite::kPretenureCreateCountOffset);
  STATIC_ASSERT(AllocationSite::kPretenureCreateCountOffset + kInt32Size ==
                AllocationSite::kWeakNextOffset);

  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    if (offset >= AllocationSite::kStartOffset &&
        offset < AllocationSite::kCommonPointerFieldEndOffset) {
      return true;
    }
    // check for weak_next offset
    if (map->instance_size() == AllocationSite::kSizeWithWeakNext &&
        offset == AllocationSite::kWeakNextOffset) {
      return true;
    }
    return false;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    // Iterate over all the common pointer fields
    IteratePointers(obj, AllocationSite::kStartOffset,
                    AllocationSite::kCommonPointerFieldEndOffset, v);
    // Skip PretenureDataOffset and PretenureCreateCount which are Int32 fields.
    // Visit weak_next only if it has weak_next field.
    if (object_size == AllocationSite::kSizeWithWeakNext) {
      IterateCustomWeakPointers(obj, AllocationSite::kWeakNextOffset,
                                AllocationSite::kSizeWithWeakNext, v);
    }
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class JSArrayBuffer::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    if (offset < kEndOfTaggedFieldsOffset) return true;
    if (offset < kHeaderSize) return false;
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    // JSArrayBuffer instances contain raw data that the GC does not know about.
    IteratePointers(obj, kPropertiesOrHashOffset, kEndOfTaggedFieldsOffset, v);
    IterateJSObjectBodyImpl(map, obj, kHeaderSize, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class JSArrayBufferView::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    if (offset < kEndOfTaggedFieldsOffset) return true;
    if (offset < kHeaderSize) return false;
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    // JSArrayBufferView contains raw data that the GC does not know about.
    IteratePointers(obj, kPropertiesOrHashOffset, kEndOfTaggedFieldsOffset, v);
    IterateJSObjectBodyImpl(map, obj, kHeaderSize, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

template <typename Derived>
class SmallOrderedHashTable<Derived>::BodyDescriptor final
    : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    Derived table = Derived::cast(obj);
    // Only data table part contains tagged values.
    return (offset >= DataTableStartOffset()) &&
           (offset < table->GetBucketsStartOffset());
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    Derived table = Derived::cast(obj);
    int start_offset = DataTableStartOffset();
    int end_offset = table->GetBucketsStartOffset();
    IteratePointers(obj, start_offset, end_offset, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) {
    Derived table = Derived::cast(obj);
    return table->SizeFor(table->Capacity());
  }
};

class ByteArray::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    return ByteArray::SizeFor(ByteArray::cast(obj)->synchronized_length());
  }
};

class BytecodeArray::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= kConstantPoolOffset &&
           offset <= kSourcePositionTableOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointer(obj, kConstantPoolOffset, v);
    IteratePointer(obj, kHandlerTableOffset, v);
    IteratePointer(obj, kSourcePositionTableOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) {
    return BytecodeArray::SizeFor(
        BytecodeArray::cast(obj)->synchronized_length());
  }
};

class BigInt::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    return BigInt::SizeFor(BigInt::cast(obj)->synchronized_length());
  }
};

class FixedDoubleArray::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    return FixedDoubleArray::SizeFor(
        FixedDoubleArray::cast(obj)->synchronized_length());
  }
};

class FixedTypedArrayBase::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset == kBasePointerOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointer(obj, kBasePointerOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return FixedTypedArrayBase::cast(object)->size();
  }
};

class FeedbackMetadata::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    return FeedbackMetadata::SizeFor(
        FeedbackMetadata::cast(obj)->synchronized_slot_count());
  }
};

class FeedbackVector::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset == kSharedFunctionInfoOffset ||
           offset == kOptimizedCodeOffset || offset >= kFeedbackSlotsOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointer(obj, kSharedFunctionInfoOffset, v);
    IterateMaybeWeakPointer(obj, kOptimizedCodeOffset, v);
    IterateMaybeWeakPointers(obj, kFeedbackSlotsOffset, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) {
    return FeedbackVector::SizeFor(FeedbackVector::cast(obj)->length());
  }
};

class PreparseData::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= PreparseData::cast(obj)->inner_start_offset();
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    PreparseData data = PreparseData::cast(obj);
    int start_offset = data->inner_start_offset();
    int end_offset = start_offset + data->children_length() * kTaggedSize;
    IteratePointers(obj, start_offset, end_offset, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) {
    PreparseData data = PreparseData::cast(obj);
    return PreparseData::SizeFor(data->data_length(), data->children_length());
  }
};

class PrototypeInfo::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= HeapObject::kHeaderSize;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, HeapObject::kHeaderSize, kObjectCreateMapOffset, v);
    IterateMaybeWeakPointer(obj, kObjectCreateMapOffset, v);
    IteratePointers(obj, kObjectCreateMapOffset + kTaggedSize, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) {
    return obj->SizeFromMap(map);
  }
};

class JSWeakCollection::BodyDescriptorImpl final : public BodyDescriptorBase {
 public:
  STATIC_ASSERT(kTableOffset + kTaggedSize == kSize);

  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IterateJSObjectBodyImpl(map, obj, kPropertiesOrHashOffset, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class Foreign::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    v->VisitExternalReference(
        Foreign::cast(obj),
        reinterpret_cast<Address*>(
            HeapObject::RawField(obj, kForeignAddressOffset).address()));
  }

  static inline int SizeOf(Map map, HeapObject object) { return kSize; }
};

class ExternalOneByteString::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject object) { return kSize; }
};

class ExternalTwoByteString::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject object) { return kSize; }
};

class Code::BodyDescriptor final : public BodyDescriptorBase {
 public:
  STATIC_ASSERT(kRelocationInfoOffset + kTaggedSize ==
                kDeoptimizationDataOffset);
  STATIC_ASSERT(kDeoptimizationDataOffset + kTaggedSize ==
                kSourcePositionTableOffset);
  STATIC_ASSERT(kSourcePositionTableOffset + kTaggedSize ==
                kCodeDataContainerOffset);
  STATIC_ASSERT(kCodeDataContainerOffset + kTaggedSize == kDataStart);

  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    // Slots in code can't be invalid because we never trim code objects.
    return true;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, ObjectVisitor* v) {
    static constexpr int kModeMask =
        RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
        RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET) |
        RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
        RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
        RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
        RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
        RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
        RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);

    // GC does not visit data/code in the header and in the body directly.
    IteratePointers(obj, kRelocationInfoOffset, kDataStart, v);

    RelocIterator it(Code::cast(obj), kModeMask);
    v->VisitRelocInfo(&it);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IterateBody(map, obj, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return Code::unchecked_cast(object)->CodeSize();
  }
};

class SeqOneByteString::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    SeqOneByteString string = SeqOneByteString::cast(obj);
    return string->SizeFor(string->synchronized_length());
  }
};

class SeqTwoByteString::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) { return false; }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {}

  static inline int SizeOf(Map map, HeapObject obj) {
    SeqTwoByteString string = SeqTwoByteString::cast(obj);
    return string->SizeFor(string->synchronized_length());
  }
};

class WasmInstanceObject::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    if (offset < kMemoryStartOffset) return true;
    if (offset < kModuleObjectOffset) return false;
    return IsValidJSObjectSlotImpl(map, obj, offset);
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, kPropertiesOrHashOffset, kEndOfTaggedFieldsOffset, v);
    IterateJSObjectBodyImpl(map, obj, kSize, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return map->instance_size();
  }
};

class Map::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= Map::kPointerFieldsBeginOffset &&
           offset < Map::kPointerFieldsEndOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, Map::kPointerFieldsBeginOffset,
                    Map::kTransitionsOrPrototypeInfoOffset, v);
    IterateMaybeWeakPointer(obj, kTransitionsOrPrototypeInfoOffset, v);
    IteratePointers(obj, Map::kTransitionsOrPrototypeInfoOffset + kTaggedSize,
                    Map::kPointerFieldsEndOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject obj) { return Map::kSize; }
};

class DataHandler::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= HeapObject::kHeaderSize;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    static_assert(kSmiHandlerOffset < kData1Offset,
                  "Field order must be in sync with this iteration code");
    static_assert(kData1Offset < kSizeWithData1,
                  "Field order must be in sync with this iteration code");
    IteratePointers(obj, kSmiHandlerOffset, kData1Offset, v);
    IterateMaybeWeakPointers(obj, kData1Offset, object_size, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return object->SizeFromMap(map);
  }
};

class NativeContext::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset < NativeContext::kEndOfTaggedFieldsOffset;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, NativeContext::kStartOfStrongFieldsOffset,
                    NativeContext::kEndOfStrongFieldsOffset, v);
    IterateCustomWeakPointers(obj, NativeContext::kStartOfWeakFieldsOffset,
                              NativeContext::kEndOfWeakFieldsOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return NativeContext::kSize;
  }
};

class CodeDataContainer::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
    return offset >= CodeDataContainer::kHeaderSize &&
           offset < CodeDataContainer::kSize;
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
    IteratePointers(obj, CodeDataContainer::kHeaderSize,
                    CodeDataContainer::kPointerFieldsStrongEndOffset, v);
    IterateCustomWeakPointers(
        obj, CodeDataContainer::kPointerFieldsStrongEndOffset,
        CodeDataContainer::kPointerFieldsWeakEndOffset, v);
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return CodeDataContainer::kSize;
  }
};

class EmbedderDataArray::BodyDescriptor final : public BodyDescriptorBase {
 public:
  static bool IsValidSlot(Map map, HeapObject obj, int offset) {
#ifdef V8_COMPRESS_POINTERS
    STATIC_ASSERT(kEmbedderDataSlotSize == 2 * kSystemPointerSize);
    STATIC_ASSERT(base::bits::IsPowerOfTwo(kEmbedderDataSlotSize));
    return (offset < EmbedderDataArray::kHeaderSize) ||
           (((offset - EmbedderDataArray::kHeaderSize) &
             (kEmbedderDataSlotSize - 1)) ==
            EmbedderDataSlot::kTaggedPayloadOffset);
#else
    STATIC_ASSERT(kEmbedderDataSlotSize == kTaggedSize);
    // We store raw aligned pointers as Smis, so it's safe to iterate the whole
    // array.
    return true;
#endif
  }

  template <typename ObjectVisitor>
  static inline void IterateBody(Map map, HeapObject obj, int object_size,
                                 ObjectVisitor* v) {
#ifdef V8_COMPRESS_POINTERS
    STATIC_ASSERT(kEmbedderDataSlotSize == 2 * kSystemPointerSize);
    // Iterate only tagged payload of the embedder slots and skip raw payload.
    for (int offset = EmbedderDataArray::OffsetOfElementAt(0) +
                      EmbedderDataSlot::kTaggedPayloadOffset;
         offset < object_size; offset += kEmbedderDataSlotSize) {
      IteratePointer(obj, offset, v);
    }
#else
    // We store raw aligned pointers as Smis, so it's safe to iterate the whole
    // array.
    STATIC_ASSERT(kEmbedderDataSlotSize == kTaggedSize);
    IteratePointers(obj, EmbedderDataArray::kHeaderSize, object_size, v);
#endif
  }

  static inline int SizeOf(Map map, HeapObject object) {
    return object->SizeFromMap(map);
  }
};

template <typename Op, typename ReturnType, typename T1, typename T2,
          typename T3, typename T4>
ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
  if (type < FIRST_NONSTRING_TYPE) {
    switch (type & kStringRepresentationMask) {
      case kSeqStringTag:
        return ReturnType();
      case kConsStringTag:
        return Op::template apply<ConsString::BodyDescriptor>(p1, p2, p3, p4);
      case kThinStringTag:
        return Op::template apply<ThinString::BodyDescriptor>(p1, p2, p3, p4);
      case kSlicedStringTag:
        return Op::template apply<SlicedString::BodyDescriptor>(p1, p2, p3, p4);
      case kExternalStringTag:
        if ((type & kStringEncodingMask) == kOneByteStringTag) {
          return Op::template apply<ExternalOneByteString::BodyDescriptor>(
              p1, p2, p3, p4);
        } else {
          return Op::template apply<ExternalTwoByteString::BodyDescriptor>(
              p1, p2, p3, p4);
        }
    }
    UNREACHABLE();
  }

  switch (type) {
    case EMBEDDER_DATA_ARRAY_TYPE:
      return Op::template apply<EmbedderDataArray::BodyDescriptor>(p1, p2, p3,
                                                                   p4);
    case FIXED_ARRAY_TYPE:
    case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
    case HASH_TABLE_TYPE:
    case ORDERED_HASH_MAP_TYPE:
    case ORDERED_HASH_SET_TYPE:
    case ORDERED_NAME_DICTIONARY_TYPE:
    case NAME_DICTIONARY_TYPE:
    case GLOBAL_DICTIONARY_TYPE:
    case NUMBER_DICTIONARY_TYPE:
    case SIMPLE_NUMBER_DICTIONARY_TYPE:
    case STRING_TABLE_TYPE:
    case EPHEMERON_HASH_TABLE_TYPE:
    case SCOPE_INFO_TYPE:
    case SCRIPT_CONTEXT_TABLE_TYPE:
      return Op::template apply<FixedArray::BodyDescriptor>(p1, p2, p3, p4);
    case AWAIT_CONTEXT_TYPE:
    case BLOCK_CONTEXT_TYPE:
    case CATCH_CONTEXT_TYPE:
    case DEBUG_EVALUATE_CONTEXT_TYPE:
    case EVAL_CONTEXT_TYPE:
    case FUNCTION_CONTEXT_TYPE:
    case MODULE_CONTEXT_TYPE:
    case SCRIPT_CONTEXT_TYPE:
    case WITH_CONTEXT_TYPE:
      return Op::template apply<Context::BodyDescriptor>(p1, p2, p3, p4);
    case NATIVE_CONTEXT_TYPE:
      return Op::template apply<NativeContext::BodyDescriptor>(p1, p2, p3, p4);
    case WEAK_FIXED_ARRAY_TYPE:
      return Op::template apply<WeakFixedArray::BodyDescriptor>(p1, p2, p3, p4);
    case WEAK_ARRAY_LIST_TYPE:
      return Op::template apply<WeakArrayList::BodyDescriptor>(p1, p2, p3, p4);
    case FIXED_DOUBLE_ARRAY_TYPE:
      return ReturnType();
    case FEEDBACK_METADATA_TYPE:
      return Op::template apply<FeedbackMetadata::BodyDescriptor>(p1, p2, p3,
                                                                  p4);
    case PROPERTY_ARRAY_TYPE:
      return Op::template apply<PropertyArray::BodyDescriptor>(p1, p2, p3, p4);
    case DESCRIPTOR_ARRAY_TYPE:
      return Op::template apply<DescriptorArray::BodyDescriptor>(p1, p2, p3,
                                                                 p4);
    case TRANSITION_ARRAY_TYPE:
      return Op::template apply<TransitionArray::BodyDescriptor>(p1, p2, p3,
                                                                 p4);
    case FEEDBACK_CELL_TYPE:
      return Op::template apply<FeedbackCell::BodyDescriptor>(p1, p2, p3, p4);
    case FEEDBACK_VECTOR_TYPE:
      return Op::template apply<FeedbackVector::BodyDescriptor>(p1, p2, p3, p4);
    case JS_OBJECT_TYPE:
    case JS_ERROR_TYPE:
    case JS_ARGUMENTS_TYPE:
    case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
    case JS_PROMISE_TYPE:
    case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
    case JS_GENERATOR_OBJECT_TYPE:
    case JS_ASYNC_FUNCTION_OBJECT_TYPE:
    case JS_ASYNC_GENERATOR_OBJECT_TYPE:
    case JS_VALUE_TYPE:
    case JS_DATE_TYPE:
    case JS_ARRAY_TYPE:
    case JS_ARRAY_ITERATOR_TYPE:
    case JS_MODULE_NAMESPACE_TYPE:
    case JS_SET_TYPE:
    case JS_MAP_TYPE:
    case JS_SET_KEY_VALUE_ITERATOR_TYPE:
    case JS_SET_VALUE_ITERATOR_TYPE:
    case JS_MAP_KEY_ITERATOR_TYPE:
    case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
    case JS_MAP_VALUE_ITERATOR_TYPE:
    case JS_STRING_ITERATOR_TYPE:
    case JS_REGEXP_STRING_ITERATOR_TYPE:
    case JS_REGEXP_TYPE:
    case JS_GLOBAL_PROXY_TYPE:
    case JS_GLOBAL_OBJECT_TYPE:
    case JS_API_OBJECT_TYPE:
    case JS_SPECIAL_API_OBJECT_TYPE:
    case JS_MESSAGE_OBJECT_TYPE:
    case JS_BOUND_FUNCTION_TYPE:
    case JS_WEAK_FACTORY_CLEANUP_ITERATOR_TYPE:
    case JS_WEAK_FACTORY_TYPE:
#ifdef V8_INTL_SUPPORT
    case JS_INTL_V8_BREAK_ITERATOR_TYPE:
    case JS_INTL_COLLATOR_TYPE:
    case JS_INTL_DATE_TIME_FORMAT_TYPE:
    case JS_INTL_LIST_FORMAT_TYPE:
    case JS_INTL_LOCALE_TYPE:
    case JS_INTL_NUMBER_FORMAT_TYPE:
    case JS_INTL_PLURAL_RULES_TYPE:
    case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
    case JS_INTL_SEGMENT_ITERATOR_TYPE:
    case JS_INTL_SEGMENTER_TYPE:
#endif  // V8_INTL_SUPPORT
    case WASM_EXCEPTION_TYPE:
    case WASM_GLOBAL_TYPE:
    case WASM_MEMORY_TYPE:
    case WASM_MODULE_TYPE:
    case WASM_TABLE_TYPE:
      return Op::template apply<JSObject::BodyDescriptor>(p1, p2, p3, p4);
    case WASM_INSTANCE_TYPE:
      return Op::template apply<WasmInstanceObject::BodyDescriptor>(p1, p2, p3,
                                                                    p4);
    case JS_WEAK_MAP_TYPE:
    case JS_WEAK_SET_TYPE:
      return Op::template apply<JSWeakCollection::BodyDescriptor>(p1, p2, p3,
                                                                  p4);
    case JS_ARRAY_BUFFER_TYPE:
      return Op::template apply<JSArrayBuffer::BodyDescriptor>(p1, p2, p3, p4);
    case JS_DATA_VIEW_TYPE:
      return Op::template apply<JSDataView::BodyDescriptor>(p1, p2, p3, p4);
    case JS_TYPED_ARRAY_TYPE:
      return Op::template apply<JSTypedArray::BodyDescriptor>(p1, p2, p3, p4);
    case JS_FUNCTION_TYPE:
      return Op::template apply<JSFunction::BodyDescriptor>(p1, p2, p3, p4);
    case JS_WEAK_CELL_TYPE:
      return Op::template apply<JSWeakCell::BodyDescriptor>(p1, p2, p3, p4);
    case JS_WEAK_REF_TYPE:
      return Op::template apply<JSWeakRef::BodyDescriptor>(p1, p2, p3, p4);
    case ODDBALL_TYPE:
      return Op::template apply<Oddball::BodyDescriptor>(p1, p2, p3, p4);
    case JS_PROXY_TYPE:
      return Op::template apply<JSProxy::BodyDescriptor>(p1, p2, p3, p4);
    case FOREIGN_TYPE:
      return Op::template apply<Foreign::BodyDescriptor>(p1, p2, p3, p4);
    case MAP_TYPE:
      return Op::template apply<Map::BodyDescriptor>(p1, p2, p3, p4);
    case CODE_TYPE:
      return Op::template apply<Code::BodyDescriptor>(p1, p2, p3, p4);
    case CELL_TYPE:
      return Op::template apply<Cell::BodyDescriptor>(p1, p2, p3, p4);
    case PROPERTY_CELL_TYPE:
      return Op::template apply<PropertyCell::BodyDescriptor>(p1, p2, p3, p4);
    case SYMBOL_TYPE:
      return Op::template apply<Symbol::BodyDescriptor>(p1, p2, p3, p4);
    case BYTECODE_ARRAY_TYPE:
      return Op::template apply<BytecodeArray::BodyDescriptor>(p1, p2, p3, p4);
    case SMALL_ORDERED_HASH_SET_TYPE:
      return Op::template apply<
          SmallOrderedHashTable<SmallOrderedHashSet>::BodyDescriptor>(p1, p2,
                                                                      p3, p4);
    case SMALL_ORDERED_HASH_MAP_TYPE:
      return Op::template apply<
          SmallOrderedHashTable<SmallOrderedHashMap>::BodyDescriptor>(p1, p2,
                                                                      p3, p4);
    case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
      return Op::template apply<
          SmallOrderedHashTable<SmallOrderedNameDictionary>::BodyDescriptor>(
          p1, p2, p3, p4);
    case CODE_DATA_CONTAINER_TYPE:
      return Op::template apply<CodeDataContainer::BodyDescriptor>(p1, p2, p3,
                                                                   p4);
    case PREPARSE_DATA_TYPE:
      return Op::template apply<PreparseData::BodyDescriptor>(p1, p2, p3, p4);
    case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE:
      return Op::template apply<
          UncompiledDataWithoutPreparseData::BodyDescriptor>(p1, p2, p3, p4);
    case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE:
      return Op::template apply<UncompiledDataWithPreparseData::BodyDescriptor>(
          p1, p2, p3, p4);
    case HEAP_NUMBER_TYPE:
    case MUTABLE_HEAP_NUMBER_TYPE:
    case FILLER_TYPE:
    case BYTE_ARRAY_TYPE:
    case FREE_SPACE_TYPE:
    case BIGINT_TYPE:
      return ReturnType();

#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype)                              \
  case FIXED_##TYPE##_ARRAY_TYPE:                                              \
    return Op::template apply<FixedTypedArrayBase::BodyDescriptor>(p1, p2, p3, \
                                                                   p4);
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE

    case SHARED_FUNCTION_INFO_TYPE: {
      return Op::template apply<SharedFunctionInfo::BodyDescriptor>(p1, p2, p3,
                                                                    p4);
    }
    case ALLOCATION_SITE_TYPE:
      return Op::template apply<AllocationSite::BodyDescriptor>(p1, p2, p3, p4);

#define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE:
      STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
      if (type == PROTOTYPE_INFO_TYPE) {
        return Op::template apply<PrototypeInfo::BodyDescriptor>(p1, p2, p3,
                                                                 p4);
      } else {
        return Op::template apply<StructBodyDescriptor>(p1, p2, p3, p4);
      }
    case CALL_HANDLER_INFO_TYPE:
      return Op::template apply<StructBodyDescriptor>(p1, p2, p3, p4);
    case LOAD_HANDLER_TYPE:
    case STORE_HANDLER_TYPE:
      return Op::template apply<DataHandler::BodyDescriptor>(p1, p2, p3, p4);
    default:
      PrintF("Unknown type: %d\n", type);
      UNREACHABLE();
  }
}


template <typename ObjectVisitor>
void HeapObject::IterateFast(ObjectVisitor* v) {
  BodyDescriptorBase::IteratePointer(*this, kMapOffset, v);
  IterateBodyFast(v);
}


template <typename ObjectVisitor>
void HeapObject::IterateBodyFast(ObjectVisitor* v) {
  Map m = map();
  IterateBodyFast(m, SizeFromMap(m), v);
}


struct CallIterateBody {
  template <typename BodyDescriptor, typename ObjectVisitor>
  static void apply(Map map, HeapObject obj, int object_size,
                    ObjectVisitor* v) {
    BodyDescriptor::IterateBody(map, obj, object_size, v);
  }
};

template <typename ObjectVisitor>
void HeapObject::IterateBodyFast(Map map, int object_size, ObjectVisitor* v) {
  BodyDescriptorApply<CallIterateBody, void>(map->instance_type(), map, *this,
                                             object_size, v);
}
}  // namespace internal
}  // namespace v8

#endif  // V8_OBJECTS_BODY_DESCRIPTORS_INL_H_
