current commit
261007.21 · deps: update V8 to 15.5.35.20
// Copyright 2017 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_DESCRIPTOR_ARRAY_H_
#define V8_OBJECTS_DESCRIPTOR_ARRAY_H_

#include <atomic>

#include "src/base/bit-field.h"
#include "src/common/globals.h"
#include "src/objects/fixed-array.h"
#include "src/objects/internal-index.h"
#include "src/objects/objects.h"
#include "src/objects/struct.h"
#include "src/utils/utils.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

namespace compiler {
class AccessBuilder;
}  // namespace compiler

namespace maglev {
class MaglevGraphBuilder;
}  // namespace maglev

class AccessorAssembler;
class CodeStubAssembler;
class ObjectBuiltinsAssembler;
class ObjectEntriesValuesBuiltinsAssembler;
class StructBodyDescriptor;

// An EnumCache is a pair used to hold keys and indices caches.
V8_OBJECT class EnumCache : public Struct {
 public:
  inline Tagged<FixedArray> keys() const;
  inline void set_keys(Tagged<FixedArray> value,
                       WriteBarrierMode mode = UPDATE_WRITE_BARRIER);

  inline Tagged<FixedArray> indices() const;
  inline void set_indices(Tagged<FixedArray> value,
                          WriteBarrierMode mode = UPDATE_WRITE_BARRIER);

  DECL_PRINTER(EnumCache)
  DECL_VERIFIER(EnumCache)

  using BodyDescriptor = StructBodyDescriptor;

 private:
  friend class TorqueGeneratedEnumCacheAsserts;
  friend class compiler::AccessBuilder;
  friend class maglev::MaglevGraphBuilder;
  friend class CodeStubAssembler;
  friend class AccessorAssembler;
  friend class ObjectBuiltinsAssembler;
  friend class ObjectEntriesValuesBuiltinsAssembler;
  friend class ObjectKeysAssembler;
  friend class ObjectGetOwnPropertyNamesAssembler;

  TaggedMember<FixedArray> keys_;
  TaggedMember<FixedArray> indices_;
} V8_OBJECT_END;

// A DescriptorArray is a custom array that holds instance descriptors.
// It has the following layout:
//   Header:
//     [16:0  bits]: number_of_all_descriptors (including slack)
//     [32:16 bits]: number_of_descriptors
//     [64:32 bits]: raw_gc_state (used by GC)
//     [kEnumCacheOffset]: enum cache
//   Elements:
//     [kHeaderSize + 0]: first key (and internalized String)
//     [kHeaderSize + 1]: first descriptor details (see PropertyDetails)
//     [kHeaderSize + 2]: first value for constants / Tagged<Smi>(1) when not
//     used
//   Slack:
//     [kHeaderSize + number of descriptors * 3]: start of slack
// The "value" fields store either values or field types. A field type is either
// FieldType::None(), FieldType::Any() or a weak reference to a Map. All other
// references are strong.
V8_OBJECT class DescriptorArray : public HeapObject {
  V8_IT_OWN_TYPE;

 public:
  // Do linear search for small arrays, and for searches in the background
  // thread.
  static constexpr int kMaxElementsForLinearSearch = 32;

  inline int16_t number_of_all_descriptors() const;
  inline void set_number_of_all_descriptors(int16_t value, ReleaseStoreTag);
  inline int16_t number_of_descriptors() const;
  inline void set_number_of_descriptors(int16_t value);
  inline uint32_t flags(RelaxedLoadTag) const;
  inline void set_flags(uint32_t value, RelaxedStoreTag);
  inline int16_t number_of_slack_descriptors() const;
  inline int number_of_entries() const;

  enum class FastIterableState : uint8_t {
    // Descriptors are JSON fast iterable, iff all of the following conditions
    // are met:
    // - No key is a symbol.
    // - All keys are enumberable.
    // - All keys are one-byte and don't contain any character that requires
    //   escaping.
    // - All properties are located in field.
    kJsonFast = 0b00,
    kJsonSlow = 0b01,
    kUnknown = 0b11
  };

  using FastIterableBits =
      base::BitField<DescriptorArray::FastIterableState, 0, 2, uint32_t>;

  inline FastIterableState fast_iterable() const;
  inline void set_fast_iterable(FastIterableState value);
  inline void set_fast_iterable_if(FastIterableState new_value,
                                   FastIterableState if_value);

  void ClearEnumCache();
  inline void CopyEnumCacheFrom(Tagged<DescriptorArray> array);
  static void InitializeOrChangeEnumCache(
      DirectHandle<DescriptorArray> descriptors, Isolate* isolate,
      DirectHandle<FixedArray> keys, DirectHandle<FixedArray> indices,
      AllocationType allocation_if_initialize);

  inline Tagged<EnumCache> enum_cache() const;
  inline void set_enum_cache(Tagged<EnumCache> value,
                             WriteBarrierMode mode = UPDATE_WRITE_BARRIER);

  // Accessors for fetching instance descriptor at descriptor number.
  inline Tagged<Name> GetKey(InternalIndex descriptor_number) const;
  inline Tagged<Object> GetStrongValue(InternalIndex descriptor_number);
  inline Tagged<MaybeObject> GetValue(InternalIndex descriptor_number);
  inline PropertyDetails GetDetails(InternalIndex descriptor_number);
  inline int GetOffsetInWords(InternalIndex descriptor_number);
  inline Tagged<FieldType> GetFieldType(InternalIndex descriptor_number);

  // Returns true if given entry is already initialized. Useful in cases
  // when a heap stats collector might see a half-initialized descriptor.
  inline bool IsInitializedDescriptor(InternalIndex descriptor_number) const;

  inline Tagged<Name> GetSortedKey(int descriptor_number);
  inline int GetSortedKeyIndex(int descriptor_number);

  // Accessor for complete descriptor.
  inline void Set(InternalIndex descriptor_number, Descriptor* desc);
  inline void Set(InternalIndex descriptor_number, Tagged<Name> key,
                  Tagged<MaybeObject> value, PropertyDetails details);
  void Replace(InternalIndex descriptor_number, Descriptor* descriptor);

  // Generalizes constness, representation and field type of all field
  // descriptors.
  void GeneralizeAllFields();

  // Append automatically sets the enumeration index. This should only be used
  // to add descriptors in bulk at the end, followed by sorting the descriptor
  // array.
  inline void Append(Descriptor* desc);

  static DirectHandle<DescriptorArray> CopyUpTo(
      Isolate* isolate, DirectHandle<DescriptorArray> desc,
      int enumeration_index, int slack = 0);

  static DirectHandle<DescriptorArray> CopyUpToAddAttributes(
      Isolate* isolate, DirectHandle<DescriptorArray> desc,
      int enumeration_index, PropertyAttributes attributes, int slack = 0);

  // Sort the instance descriptors by the hash codes of their keys.
  inline void Sort();

  // Iterate through Name hash collisions in the descriptor array starting from
  // insertion index checking for Name collisions. Note: If we ever add binary
  // insertion for large DescriptorArrays it would need to be hardened in a
  // similar way. This function only expects to be called on Sorted
  // DescriptorArrays.
  V8_EXPORT_PRIVATE void CheckNameCollisionDuringInsertion(
      Descriptor* desc, uint32_t descriptor_hash, int insertion_index);

  // Search the instance descriptors for given name. {concurrent_search} signals
  // if we are doing the search on a background thread. If so, we will sacrifice
  // speed for thread-safety.
  V8_INLINE InternalIndex Search(Tagged<Name> name,
                                 int number_of_own_descriptors,
                                 bool concurrent_search = false);
  V8_INLINE InternalIndex Search(Tagged<Name> name, Tagged<Map> map,
                                 bool concurrent_search = false);

  // As the above, but uses DescriptorLookupCache and updates it when
  // necessary.
  V8_INLINE InternalIndex SearchWithCache(Isolate* isolate, Tagged<Name> name,
                                          Tagged<Map> map);

  bool IsEqualUpTo(Tagged<DescriptorArray> desc, int nof_descriptors);

  // Allocates a DescriptorArray, but returns the singleton
  // empty descriptor array object if number_of_descriptors is 0.
  template <typename IsolateT>
  V8_EXPORT_PRIVATE static Handle<DescriptorArray> Allocate(
      IsolateT* isolate, int nof_descriptors, int slack,
      AllocationType allocation = AllocationType::kYoung);

  void Initialize(Tagged<EnumCache> enum_cache,
                  Tagged<HeapObject> undefined_value, int nof_descriptors,
                  int slack);

  // Constant for denoting key was not found.
  static const int kNotFound = -1;

  static constexpr int SizeFor(int number_of_all_descriptors);
  static constexpr int OffsetOfDescriptorAt(int descriptor);
  inline ObjectSlot GetFirstPointerSlot();
  inline ObjectSlot GetDescriptorSlot(int descriptor);

  class BodyDescriptor;

  // Layout of descriptor.
  // Naming is consistent with Dictionary classes for easy templating.
  static const int kEntryKeyIndex = 0;
  static const int kEntryDetailsIndex = 1;
  static const int kEntryValueIndex = 2;
  static const int kEntrySize = 3;

  static const int kEntryKeyOffset = kEntryKeyIndex * kTaggedSize;
  static const int kEntryDetailsOffset = kEntryDetailsIndex * kTaggedSize;
  static const int kEntryValueOffset = kEntryValueIndex * kTaggedSize;

  static const int kHeaderSize;
  static const int kDescriptorsOffset;

  // Print all the descriptors.
  void PrintDescriptors(std::ostream& os);
  void PrintDescriptorDetails(std::ostream& os, InternalIndex descriptor,
                              PropertyDetails::PrintMode mode);

  DECL_PRINTER(DescriptorArray)
  DECL_VERIFIER(DescriptorArray)

#ifdef VERIFY_HEAP
  // Per-entry type check only (key is Name|Undefined, details is Smi|Undefined,
  // value is JSAny|Weak<Map>|AccessorInfo|AccessorPair|ClassPositions|
  // NumberDictionary). This is the hand-rolled equivalent of the old Torque-
  // generated DescriptorArrayVerify and is what test/cctest/test-verifiers.cc
  // exercises; the full DescriptorArrayVerify additionally enforces semantic
  // invariants (field values are FieldType-shaped, private keys are
  // non-enumerable, etc.) that assume a well-formed descriptor array.
  V8_EXPORT_PRIVATE void DescriptorArrayEntryTypesVerify(Isolate* isolate);
#endif

#ifdef DEBUG
  // Is the descriptor array sorted and without duplicates?
  V8_EXPORT_PRIVATE bool IsSortedNoDuplicates();

  // Are two DescriptorArrays equal?
  bool IsEqualTo(Tagged<DescriptorArray> other);
#endif

  static constexpr int ToDetailsIndex(int descriptor_number) {
    return (descriptor_number * kEntrySize) + kEntryDetailsIndex;
  }

  // Conversion from descriptor number to array indices.
  static constexpr int ToKeyIndex(int descriptor_number) {
    return (descriptor_number * kEntrySize) + kEntryKeyIndex;
  }

  static constexpr int ToValueIndex(int descriptor_number) {
    return (descriptor_number * kEntrySize) + kEntryValueIndex;
  }

 private:
  V8_EXPORT_PRIVATE void SortImpl(const int len);

  inline void SetKey(InternalIndex descriptor_number, Tagged<Name> key);
  inline void SetValue(InternalIndex descriptor_number,
                       Tagged<MaybeObject> value);
  inline void SetDetails(InternalIndex descriptor_number,
                         PropertyDetails details);

  V8_INLINE InternalIndex BinarySearch(Tagged<Name> name,
                                       int number_of_own_descriptors);
  V8_INLINE InternalIndex LinearSearch(Tagged<Name> name,
                                       int number_of_own_descriptors);

  // Transfer a complete descriptor from the src descriptor array to this
  // descriptor array.
  void CopyFrom(InternalIndex index, Tagged<DescriptorArray> src);

  inline void SetSortedKey(int pointer, int descriptor_number);

  // Swap first and second descriptor.
  inline void SwapSortedKeys(int first, int second);

 public:
  // A single descriptor tuple (key, details, value). The three fields occupy
  // three adjacent tagged slots, matching the kEntry{Key,Details,Value}Offset
  // constants above. `key` and `details` are always strong after
  // initialization but may be `Undefined` in unused / slack entries;
  // `value` may hold a weak reference to a Map. See the custom
  // BodyDescriptor for GC iteration.
  struct Entry {
    TaggedMember<UnionOf<Name, Undefined>> key;
    TaggedMember<UnionOf<Smi, Undefined>> details;
    TaggedMember<UnionOf<JSAny, Weak<Map>, AccessorInfo, AccessorPair,
                         ClassPositions, NumberDictionary>>
        value;
  };

  // Declared atomic so that concurrent readers (e.g. from the marker) see a
  // consistent value during trimming in mark-compact.
  std::atomic<uint16_t> number_of_all_descriptors_;
  std::atomic<uint16_t> number_of_descriptors_;
  std::atomic<uint32_t> flags_;
  TaggedMember<EnumCache> enum_cache_;
  FLEXIBLE_ARRAY_MEMBER(Entry, entries);
} V8_OBJECT_END;

static_assert(sizeof(DescriptorArray::Entry) ==
              DescriptorArray::kEntrySize * kTaggedSize);
static_assert(offsetof(DescriptorArray::Entry, key) ==
              DescriptorArray::kEntryKeyOffset);
static_assert(offsetof(DescriptorArray::Entry, details) ==
              DescriptorArray::kEntryDetailsOffset);
static_assert(offsetof(DescriptorArray::Entry, value) ==
              DescriptorArray::kEntryValueOffset);

inline constexpr int DescriptorArray::kHeaderSize =
    OFFSET_OF_DATA_START(DescriptorArray);
inline constexpr int DescriptorArray::kDescriptorsOffset =
    OFFSET_OF_DATA_START(DescriptorArray);

constexpr int DescriptorArray::SizeFor(int number_of_all_descriptors) {
  return OFFSET_OF_DATA_START(DescriptorArray) +
         number_of_all_descriptors * kEntrySize * kTaggedSize;
}

constexpr int DescriptorArray::OffsetOfDescriptorAt(int descriptor) {
  return OFFSET_OF_DATA_START(DescriptorArray) +
         descriptor * kEntrySize * kTaggedSize;
}

static_assert(IsAligned(DescriptorArray::kHeaderSize, kTaggedSize));
static_assert(sizeof(std::atomic<uint16_t>) == 2);
static_assert(alignof(std::atomic<uint16_t>) == 2);
static_assert(sizeof(std::atomic<uint32_t>) == 4);
static_assert(alignof(std::atomic<uint32_t>) == 4);
static_assert(offsetof(DescriptorArray, number_of_all_descriptors_) ==
              sizeof(HeapObject));
static_assert(offsetof(DescriptorArray, number_of_descriptors_) ==
              sizeof(HeapObject) + sizeof(uint16_t));
static_assert(offsetof(DescriptorArray, flags_) ==
              sizeof(HeapObject) + 2 * sizeof(uint16_t));

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_DESCRIPTOR_ARRAY_H_