@ 261106.6
current
// 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_MAP_H_
#define V8_OBJECTS_MAP_H_

#include <atomic>
#include <optional>
#include <span>

#include "src/base/bit-field.h"
#include "src/common/globals.h"
#include "src/objects/fixed-array-base.h"
#include "src/objects/fixed-array.h"
#include "src/objects/heap-object.h"
#include "src/objects/instance-type-checker.h"
#include "src/objects/internal-index.h"
#include "src/objects/maybe-object.h"
#include "src/objects/objects.h"
#include "src/objects/prototype-info.h"
#include "src/roots/roots.h"

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

namespace v8::internal {

class WasmTypeInfo;

enum InstanceType : uint16_t;

#define DATA_ONLY_VISITOR_ID_LIST(V) \
  V(BigInt)                          \
  V(CoverageInfo)                    \
  V(FeedbackMetadata)                \
  V(Filler)                          \
  V(HeapNumber)                      \
  V(HashSeedWrapper)                 \
  V(Hole)                            \
  V(SeqOneByteString)                \
  V(SeqTwoByteString)                \
  V(TurbofanBitsetType)              \
  V(TurbofanOtherNumberConstantType) \
  V(TurbofanRangeType)               \
  V(TurboshaftFloat64RangeType)      \
  V(TurboshaftFloat64SetType)        \
  V(TurboshaftWord32RangeType)       \
  V(TurboshaftWord32SetType)         \
  V(TurboshaftWord64RangeType)       \
  V(TurboshaftWord64SetType)         \
  IF_WASM(V, WasmNull)

#define POINTER_VISITOR_ID_LIST(V)   \
  V(AccessorInfo)                    \
  V(AllocationSite)                  \
  V(BytecodeWrapper)                 \
  V(CallSiteInfo)                    \
  V(Cell)                            \
  V(CodeWrapper)                     \
  V(ConsString)                      \
  V(Context)                         \
  V(ContextCell)                     \
  V(CppHeapExternalObject)           \
  V(CppGCManagedBase)                \
  V(DataHandler)                     \
  V(DescriptorArray)                 \
  V(DoubleStringCache)               \
  V(EmbedderDataArray)               \
  V(EphemeronHashTable)              \
  V(ExternalString)                  \
  V(FeedbackCell)                    \
  V(FeedbackVector)                  \
  V(Foreign)                         \
  V(FreeSpace)                       \
  V(FunctionTemplateInfo)            \
  V(InterceptorInfo)                 \
  V(JSApiObject)                     \
  V(JSArrayBuffer)                   \
  V(JSDataViewOrRabGsabDataView)     \
  V(JSDate)                          \
  V(JSExternalObject)                \
  V(JSFinalizationRegistry)          \
  V(JSFunction)                      \
  V(JSGlobalProxy)                   \
  V(JSObject)                        \
  V(JSObjectFast)                    \
  V(JSProxy)                         \
  V(JSRegExp)                        \
  V(JSSynchronizationPrimitive)      \
  V(JSTypedArray)                    \
  V(JSWeakCollection)                \
  V(JSWeakRef)                       \
  V(Map)                             \
  V(MegaDomHandler)                  \
  V(NativeContext)                   \
  V(Oddball)                         \
  V(OnHeapBasicBlockProfilerData)    \
  V(PreparseData)                    \
  V(PropertyArray)                   \
  V(PropertyCell)                    \
  V(PrototypeInfo)                   \
  V(PrototypeSharedClosureInfo)      \
  V(RegExpBoilerplateDescription)    \
  V(RegExpDataWrapper)               \
  V(ScopeInfo)                       \
  V(SharedFunctionInfo)              \
  V(ShortcutCandidate)               \
  V(SlicedString)                    \
  V(SmallOrderedHashMap)             \
  V(SmallOrderedHashSet)             \
  V(SmallOrderedNameDictionary)      \
  V(SortState)                       \
  V(SourceTextModule)                \
  V(Struct)                          \
  V(SwissNameDictionary)             \
  V(Symbol)                          \
  V(TurbofanHeapConstantType)        \
  V(TurbofanUnionType)               \
  V(SyntheticModule)                 \
  V(ThinString)                      \
  V(TransitionArray)                 \
  IF_WASM(V, WasmArray)              \
  IF_WASM(V, WasmCustomMap)          \
  IF_WASM(V, WasmFuncRef)            \
  IF_WASM(V, WasmGlobalObject)       \
  IF_WASM(V, WasmInstanceObject)     \
  IF_WASM(V, WasmMemoryObject)       \
  IF_WASM(V, WasmResumeData)         \
  IF_WASM(V, WasmStruct)             \
  IF_WASM(V, WasmSuspendingObject)   \
  IF_WASM(V, WasmContinuationObject) \
  IF_WASM(V, WasmFastApiCallData)    \
  IF_WASM(V, WasmStackObject)        \
  IF_WASM(V, WasmStringViewIter)     \
  IF_WASM(V, WasmTableObject)        \
  IF_WASM(V, WasmTagObject)          \
  IF_WASM(V, WasmTypeInfo)           \
  V(WeakCell)                        \
  SIMPLE_HEAP_OBJECT_LIST1(V)

#define TRUSTED_VISITOR_ID_LIST(V) CONCRETE_TRUSTED_OBJECT_TYPE_LIST1(V)

// Objects with the same visitor id are processed in the same way by
// the heap visitors. The visitor ids for data only objects must precede
// other visitor ids. We rely on kDataOnlyVisitorIdCount for quick check
// of whether an object contains only data or may contain pointers.
enum VisitorId {
#define VISITOR_ID_ENUM_DECL(id) kVisit##id,
  // clang-format off
  DATA_ONLY_VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
  kDataOnlyVisitorIdCount,
  POINTER_VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
  TRUSTED_VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
  kVisitorIdCount
// clang-format on
#undef VISITOR_ID_ENUM_DECL
};

V8_EXPORT_PRIVATE const char* ToString(VisitorId visitor_id);

enum class ObjectFields {
  kDataOnly,
  kMaybePointers,
};

using MapHandles =
    DirectHandleSmallVector<Map, DEFAULT_MAX_POLYMORPHIC_MAP_COUNT>;
using MapHandlesSpan = std::span<DirectHandle<Map>>;

// All heap objects have a Map that describes their structure.
//  A Map contains information about:
//  - Size information about the object
//  - How to iterate over an object (for garbage collection)
//
// Map layout:
// +---------------+-------------------------------------------------+
// |   _ Type _    | _ Description _                                 |
// +---------------+-------------------------------------------------+
// | TaggedPointer | map - Always a pointer to the MetaMap root      |
// +---------------+-------------------------------------------------+
// | Int           | The first int field                             |
//  `---+----------+-------------------------------------------------+
//      | Byte     | [instance_size]                                 |
//      +----------+-------------------------------------------------+
//      | Byte     | If Map for a primitive type:                    |
//      |          |   native context index for constructor fn       |
//      |          | If Map for an Object type:                      |
//      |          |   inobject properties start offset in words     |
//      +----------+-------------------------------------------------+
//      | Byte     | [used_or_unused_instance_size_in_words]         |
//      |          | For JSObject in fast mode this byte encodes     |
//      |          | the size of the object that includes only       |
//      |          | the used property fields or the slack size      |
//      |          | in properties backing store.                    |
//      +----------+-------------------------------------------------+
//      | Byte     | [visitor_id]                                    |
// +----+----------+-------------------------------------------------+
// | Int           | The second int field                            |
//  `---+----------+-------------------------------------------------+
//      | Short    | [instance_type]                                 |
//      +----------+-------------------------------------------------+
//      | Byte     | [bit_field]                                     |
//      |          |   - is_callable (bit 0)                         |
//      |          |   - has_named_interceptor (bit 1)               |
//      |          |   - has_indexed_interceptor (bit 2)             |
//      |          |   - is_undetectable (bit 3)                     |
//      |          |   - is_access_check_needed (bit 4)              |
//      |          |   - is_constructor (bit 5)                      |
//      |          |   - is_extended_map (bit 6)                     |
//      +----------+-------------------------------------------------+
//      | Byte     | [bit_field2]                                    |
//      |          |   - new_target_is_base (bit 0)                  |
//      |          |   - is_immutable_proto (bit 1)                  |
//      |          |   - elements_kind (bits 2..7)                   |
// +----+----------+-------------------------------------------------+
// | Int           | [bit_field3]                                    |
// |               |   - enum_length (bit 0..9)                      |
// |               |   - number_of_own_descriptors (bit 10..19)      |
// |               |   - is_prototype_map (bit 20)                   |
// |               |   - is_dictionary_map (bit 21)                  |
// |               |   - owns_descriptors (bit 22)                   |
// |               |   - is_in_retained_map_list (bit 23)            |
// |               |   - is_deprecated (bit 24)                      |
// |               |   - is_unstable (bit 25)                        |
// |               |   - is_migration_target (bit 26)                |
// |               |   - is_extensible (bit 27)                      |
// |               |   - may_have_interesting_properties (bit 28)    |
// |               |   - construction_counter (bit 29..31)           |
// |               |                                                 |
// +*****************************************************************+
// | Int           | On systems with 64bit pointer types, there      |
// |               | is an unused 32bits after bit_field3            |
// +*****************************************************************+
// | TaggedPointer | [prototype]                                     |
// +---------------+-------------------------------------------------+
// | TaggedPointer | [constructor_or_back_pointer_or_native_context] |
// |               | [WasmTypeInfo] (if Wasm map)                    |
// +---------------+-------------------------------------------------+
// | TaggedPointer | [instance_descriptors] (if JS object)           |
// |               | [custom_descriptor]    (if WasmStruct)          |
// +---------------+-------------------------------------------------+
// | TaggedPointer | [immediate_supertype_map] (if WasmStruct with   |
// |               |                            custom descriptor)   |
// |               | [dependent_code] (all other maps)               |
// +---------------+-------------------------------------------------+
// | TaggedPointer | [prototype_validity_cell]                       |
// +---------------+-------------------------------------------------+
// | TaggedPointer | If Map is a prototype map:                      |
// |               |   [prototype_info]                              |
// |               | Else:                                           |
// |               |   [raw_transitions]                             |
// +---------------+-------------------------------------------------+

V8_OBJECT class Map : public HeapObject {
  V8_IT_OWN_TYPE;

 public:
  // Instance size.
  // Size in bytes or kVariableSizeSentinel if instances do not have
  // a fixed size.
  DECL_INT_ACCESSORS(instance_size)
  // Size in words or kVariableSizeSentinel if instances do not have
  // a fixed size.
  DECL_INT_ACCESSORS(instance_size_in_words)

  // [inobject_properties_start_or_constructor_function_index]:
  // Provides access to the inobject properties start offset in words in case of
  // JSObject maps, or the constructor function index in case of primitive maps.
  DECL_UINT8_ACCESSORS(inobject_properties_start_or_constructor_function_index)

  // Get/set the in-object property area start offset in words in the object.
  inline uint8_t GetInObjectPropertiesStartInWords() const;
  inline void SetInObjectPropertiesStartInWords(uint8_t value);
  inline void SetInObjectPropertiesStartInWords(int value);
  // Count of properties allocated in the object (JSObject only).
  inline int GetInObjectProperties() const;
  inline bool IsFieldInObject(int field_index) const;
  // Index of the constructor function in the native context (primitives only),
  // or the special sentinel value to indicate that there is no object wrapper
  // for the primitive (i.e. in case of null or undefined).
  static const int kNoConstructorFunctionIndex = 0;
  inline int GetConstructorFunctionIndex() const;
  inline void SetConstructorFunctionIndex(int value);
  static std::optional<Tagged<JSFunction>> GetConstructorFunction(
      Tagged<Map> map, Tagged<Context> native_context);

  // Retrieve interceptors.
  inline Tagged<InterceptorInfo> GetNamedInterceptor() const;
  inline Tagged<InterceptorInfo> GetIndexedInterceptor() const;

  // Instance type.
  // Inline definition here to avoid a circular dependency in map-inl.h
  // with instance-types-inl.h
  inline InstanceType instance_type() const {
    // TODO(solanes, v8:7790, v8:11353, v8:11945): Make this and the setter
    // non-atomic when TSAN sees the map's store synchronization.
    return static_cast<InstanceType>(
        instance_type_.load(std::memory_order_relaxed));
  }
  inline void set_instance_type(InstanceType value);

  // Size of this map object.
  inline int AllocatedSize() const;

  // Returns the size of the used in-object area including object header
  // (only used for JSObject in fast mode, for the other kinds of objects it
  // is equal to the instance size).
  inline int UsedInstanceSize() const;

  inline bool HasOutOfObjectProperties() const;

  // Tells how many unused property fields (in-object or out-of object) are
  // available in the instance (only used for JSObject in fast mode).
  inline int UnusedPropertyFields() const;
  // Tells how many unused in-object property words are present.
  inline int UnusedInObjectProperties() const;
  // Updates the counters tracking unused fields in the object.
  inline void SetInObjectUnusedPropertyFields(int unused_property_fields);
  // Updates the counters tracking unused fields in the property array.
  inline void SetOutOfObjectUnusedPropertyFields(int unused_property_fields);
  inline void CopyUnusedPropertyFields(Tagged<Map> map);
  inline void CopyUnusedPropertyFieldsAdjustedForInstanceSize(Tagged<Map> map);
  inline void AccountAddedPropertyField();
  inline void AccountAddedOutOfObjectPropertyField(
      int unused_in_property_array);

  //
  // Bit field.
  //
  // The setter in this pair calls the relaxed setter if concurrent marking is
  // on, or performs the write non-atomically if it's off. The read is always
  // non-atomically. This is done to have wider TSAN coverage on the cases where
  // it's possible.
  DECL_PRIMITIVE_ACCESSORS(bit_field, uint8_t)

  // Atomic accessors, used for allowlisting legitimate concurrent accesses.
  DECL_PRIMITIVE_ACCESSORS(relaxed_bit_field, uint8_t)

  // Bit positions for |bit_field|.
  struct Bits1 {
    using IsCallableBit = base::BitField<bool, 0, 1, uint8_t>;
    using HasNamedInterceptorBit = IsCallableBit::Next<bool, 1>;
    using HasIndexedInterceptorBit = HasNamedInterceptorBit::Next<bool, 1>;
    using IsUndetectableBit = HasIndexedInterceptorBit::Next<bool, 1>;
    using IsAccessCheckNeededBit = IsUndetectableBit::Next<bool, 1>;
    using IsConstructorBit = IsAccessCheckNeededBit::Next<bool, 1>;
    using IsExtendedMapBit = IsConstructorBit::Next<bool, 1>;
  };

  //
  // Bit field 2.
  //
  DECL_PRIMITIVE_ACCESSORS(bit_field2, uint8_t)

  // Bit positions for |bit_field2|.
  struct Bits2 {
    using NewTargetIsBaseBit = base::BitField<bool, 0, 1, uint8_t>;
    using IsImmutablePrototypeBit = NewTargetIsBaseBit::Next<bool, 1>;
    using ElementsKindBits = IsImmutablePrototypeBit::Next<ElementsKind, 6>;
  };

  //
  // Bit field 3.
  //
  // {bit_field3} calls the relaxed accessors if concurrent marking is on, or
  // performs the read/write non-atomically if it's off. This is done to have
  // wider TSAN coverage on the cases where it's possible.
  DECL_PRIMITIVE_ACCESSORS(bit_field3, uint32_t)

  DECL_PRIMITIVE_ACCESSORS(relaxed_bit_field3, uint32_t)
  DECL_PRIMITIVE_ACCESSORS(release_acquire_bit_field3, uint32_t)

  // Clear uninitialized padding space. This ensures that the snapshot content
  // is deterministic. Depending on the V8 build mode there could be no padding.
  V8_INLINE void clear_padding();

  // Bit positions for |bit_field3|.
  struct Bits3 {
    using EnumLengthBits = base::BitField<int32_t, 0, 10, uint32_t>;
    using NumberOfOwnDescriptorsBits = EnumLengthBits::Next<int32_t, 10>;
    using IsPrototypeMapBit = NumberOfOwnDescriptorsBits::Next<bool, 1>;
    using IsDictionaryMapBit = IsPrototypeMapBit::Next<bool, 1>;
    using OwnsDescriptorsBit = IsDictionaryMapBit::Next<bool, 1>;
    using IsInRetainedMapListBit = OwnsDescriptorsBit::Next<bool, 1>;
    using IsDeprecatedBit = IsInRetainedMapListBit::Next<bool, 1>;
    using IsUnstableBit = IsDeprecatedBit::Next<bool, 1>;
    using IsMigrationTargetBit = IsUnstableBit::Next<bool, 1>;
    using IsExtensibleBit = IsMigrationTargetBit::Next<bool, 1>;
    using MayHaveInterestingPropertiesBit = IsExtensibleBit::Next<bool, 1>;
    using ConstructionCounterBits =
        MayHaveInterestingPropertiesBit::Next<int32_t, 3>;
  };

  // Ensure that Torque-defined bit widths for |bit_field3| are as expected.
  static_assert(Bits3::EnumLengthBits::kSize == kDescriptorIndexBitCount);
  static_assert(Bits3::NumberOfOwnDescriptorsBits::kSize ==
                kDescriptorIndexBitCount);

  static_assert(Bits3::NumberOfOwnDescriptorsBits::kMax >=
                kMaxNumberOfDescriptors);

  static const int kSlackTrackingCounterStart = 7;
  static const int kSlackTrackingCounterEnd = 1;
  static const int kNoSlackTracking = 0;
  static_assert(kSlackTrackingCounterStart <=
                Bits3::ConstructionCounterBits::kMax);

  // Inobject slack tracking is the way to reclaim unused inobject space.
  //
  // The instance size is initially determined by adding some slack to
  // expected_nof_properties (to allow for a few extra properties added
  // after the constructor). There is no guarantee that the extra space
  // will not be wasted.
  //
  // Here is the algorithm to reclaim the unused inobject space:
  // - Detect the first constructor call for this JSFunction.
  //   When it happens enter the "in progress" state: initialize construction
  //   counter in the initial_map.
  // - While the tracking is in progress initialize unused properties of a new
  //   object with one_pointer_filler_map instead of undefined_value (the "used"
  //   part is initialized with undefined_value as usual). This way they can
  //   be resized quickly and safely.
  // - Once enough objects have been created  compute the 'slack'
  //   (traverse the map transition tree starting from the
  //   initial_map and find the lowest value of unused_property_fields).
  // - Traverse the transition tree again and decrease the instance size
  //   of every map. Existing objects will resize automatically (they are
  //   filled with one_pointer_filler_map). All further allocations will
  //   use the adjusted instance size.
  // - SharedFunctionInfo's expected_nof_properties left unmodified since
  //   allocations made using different closures could actually create different
  //   kind of objects (see prototype inheritance pattern).
  //
  //  Important: inobject slack tracking is not attempted during the snapshot
  //  creation.

  static const int kGenerousAllocationCount =
      kSlackTrackingCounterStart - kSlackTrackingCounterEnd + 1;

  // Starts the tracking by initializing object constructions countdown counter.
  void StartInobjectSlackTracking();

  // True if the object constructions countdown counter is a range
  // [kSlackTrackingCounterEnd, kSlackTrackingCounterStart].
  inline bool IsInobjectSlackTrackingInProgress() const;

  // Does the tracking step.
  inline void InobjectSlackTrackingStep(Isolate* isolate);

  // Computes inobject slack for the transition tree starting at this initial
  // map.
  int ComputeMinObjectSlack(Isolate* isolate);
  inline int InstanceSizeFromSlack(int slack) const;

  // Tells whether the Map represents a meta Map or extended Map (which
  // has more fields than a normal Map) as opposed to regular Map.
  DECL_BOOLEAN_ACCESSORS(is_extended_map)

  // Tells whether the instance has a [[Construct]] internal method.
  // This property is implemented according to ES6, section 7.2.4.
  DECL_BOOLEAN_ACCESSORS(is_constructor)

  // Tells whether the instance with this map may have properties for
  // interesting symbols on it.
  // An "interesting symbol" is one for which Name::IsInteresting()
  // returns true, i.e. a well-known symbol like @@toStringTag.
  DECL_BOOLEAN_ACCESSORS(may_have_interesting_properties)

  // Records and queries whether the instance has a named interceptor.
  DECL_BOOLEAN_ACCESSORS(has_named_interceptor)

  // Records and queries whether the instance has an indexed interceptor.
  DECL_BOOLEAN_ACCESSORS(has_indexed_interceptor)

  // Tells whether the instance is undetectable.
  // An undetectable object is a special class of JSObject: 'typeof' operator
  // returns undefined, ToBoolean returns false. Otherwise it behaves like
  // a normal JS object.  It is useful for implementing undetectable
  // document.all in Firefox & Safari.
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
  DECL_BOOLEAN_ACCESSORS(is_undetectable)

  // Tells whether the instance has a [[Call]] internal method.
  // This property is implemented according to ES6, section 7.2.3.
  DECL_BOOLEAN_ACCESSORS(is_callable)

  DECL_BOOLEAN_ACCESSORS(new_target_is_base)
  DECL_BOOLEAN_ACCESSORS(is_extensible)
  DECL_BOOLEAN_ACCESSORS(is_prototype_map)
  inline bool is_abandoned_prototype_map() const;
  inline bool has_prototype_info() const;
  inline bool TryGetPrototypeInfo(Tagged<PrototypeInfo>* result) const;
  inline bool TryGetPrototypeSharedClosureInfo(
      Tagged<PrototypeSharedClosureInfo>* result) const;
  inline void SetPrototypeSharedClosureInfo(
      Tagged<PrototypeSharedClosureInfo> closure_infos);

  // Whether the instance has been added to the retained map list by
  // Heap::AddRetainedMap.
  DECL_BOOLEAN_ACCESSORS(is_in_retained_map_list)

  DECL_PRIMITIVE_ACCESSORS(elements_kind, ElementsKind)

  // Tells whether the instance has fast elements that are only Smis.
  inline bool has_fast_smi_elements() const;

  // Tells whether the instance has fast elements.
  inline bool has_fast_object_elements() const;
  inline bool has_fast_smi_or_object_elements() const;
  inline bool has_fast_double_elements() const;
  inline bool has_fast_elements() const;
  inline bool has_fast_packed_elements() const;
  inline bool has_sloppy_arguments_elements() const;
  inline bool has_fast_sloppy_arguments_elements() const;
  inline bool has_fast_string_wrapper_elements() const;
  inline bool has_typed_array_or_rab_gsab_typed_array_elements() const;
  inline bool has_any_typed_array_or_wasm_array_elements() const;
  inline bool has_dictionary_elements() const;
  inline bool has_any_nonextensible_elements() const;
  inline bool has_nonextensible_elements() const;
  inline bool has_sealed_elements() const;
  inline bool has_frozen_elements() const;
  inline bool has_shared_array_elements() const;

  // Weakly checks whether a map is detached from all transition trees. If this
  // returns true, the map is guaranteed to be detached. If it returns false,
  // there is no guarantee it is attached.
  inline bool IsDetached(Isolate* isolate) const;

  // Returns true if there is an object with potentially read-only elements
  // in the prototype chain. It could be a Proxy, a string wrapper,
  // an object with DICTIONARY_ELEMENTS potentially containing read-only
  // elements or an object with any frozen elements, or a slow arguments object.
  bool ShouldCheckForReadOnlyElementsInPrototypeChain(Isolate* isolate);

  inline Tagged<Map> ElementsTransitionMap(Isolate* isolate,
                                           ConcurrencyMode cmode);

  inline Tagged<FixedArrayBase> GetInitialElements() const;

  // [raw_transitions]: Provides access to the transitions storage field.
  // This is a reinterpret-load of the shared transitions_or_prototype_info_
  // slot; the returned value may be any member of the full union, and
  // consumers (primarily TransitionsAccessor::GetEncoding) dispatch on the
  // runtime type.
  // Don't call set_raw_transitions() directly to overwrite transitions, use
  // the TransitionArray::ReplaceTransitions() wrapper instead!
  using RawTransitionsT = UnionOf<Smi, MaybeWeak<Map>, TransitionArray,
                                  PrototypeInfo, PrototypeSharedClosureInfo>;
  DECL_ACCESSORS(raw_transitions, Tagged<RawTransitionsT>)
  DECL_RELEASE_ACQUIRE_ACCESSORS(raw_transitions, Tagged<RawTransitionsT>)
  // [prototype_info]: Per-prototype metadata. Aliased with transitions
  // (which prototype maps don't have).
  DECL_GETTER(prototype_info,
              Tagged<UnionOf<Smi, PrototypeInfo, PrototypeSharedClosureInfo>>)

  DECL_RELEASE_ACQUIRE_ACCESSORS(
      prototype_info,
      Tagged<UnionOf<Smi, PrototypeInfo, PrototypeSharedClosureInfo>>)
  // PrototypeInfo is created lazily using this helper (which installs it on
  // the given prototype's map).
  static DirectHandle<PrototypeInfo> GetOrCreatePrototypeInfo(
      DirectHandle<JSReceiver> prototype, Isolate* isolate);
  static DirectHandle<PrototypeInfo> GetOrCreatePrototypeInfo(
      DirectHandle<Map> prototype_map, Isolate* isolate);
  inline bool should_be_fast_prototype_map() const;
  static void SetShouldBeFastPrototypeMap(DirectHandle<Map> map, bool value,
                                          Isolate* isolate);

  static inline bool TryGetValidityCellHolderMap(
      Tagged<Map> map, Isolate* isolate,
      Tagged<Map>* out_validity_cell_holder_map);

  // [prototype chain validity cell]: Associated with a prototype object,
  // stored in that object's map, indicates that prototype chains through this
  // object are currently valid. The cell will be invalidated and replaced when
  // the prototype chain changes. When there's nothing to guard (for example,
  // when direct prototype is null or Proxy) this function returns Smi
  // |kNoValidityCellSentinel| value.
  // If |out_prototype_info| is provided then the function sets it to
  // the PrototypeInfo object that corresponds to validity cell's owner.
  static Handle<UnionOf<Smi, Cell>> GetOrCreatePrototypeChainValidityCell(
      DirectHandle<Map> map, Isolate* isolate,
      DirectHandle<PrototypeInfo>* out_prototype_info = nullptr);

  // Invalid state for prototype validity cell. Everything else is considered
  // as valid state.
  static constexpr Tagged<ClearedWeakValue> kPrototypeChainInvalid =
      kClearedWeakValue;

  // This sentinel is used in IC data handlers instead of actual validity cell
  // when there's nothing to guard against (when direct prototype is null or
  // Proxy).
  static constexpr Tagged<Smi> kNoValidityCellSentinel = Smi::zero();

  // Return the map of the root of object's prototype chain.
  Tagged<Map> GetPrototypeChainRootMap(Isolate* isolate) const;

  V8_EXPORT_PRIVATE Tagged<Map> FindRootMap() const;
  V8_EXPORT_PRIVATE Tagged<Map> FindFieldOwner(InternalIndex descriptor) const;

  inline int GetInObjectPropertyOffset(int index) const;

  class FieldCounts {
   public:
    FieldCounts(int mutable_count, int const_count)
        : mutable_count_(mutable_count), const_count_(const_count) {}

    int GetTotal() const { return mutable_count() + const_count(); }

    int mutable_count() const { return mutable_count_; }
    int const_count() const { return const_count_; }

   private:
    int mutable_count_;
    int const_count_;
  };

  FieldCounts GetFieldCounts() const;
  int NumberOfFields(ConcurrencyMode cmode) const;

  // TODO(ishell): candidate with JSObject::MigrateToMap().
  bool InstancesNeedRewriting(Tagged<Map> target, ConcurrencyMode cmode) const;
  bool InstancesNeedRewriting(Tagged<Map> target, int target_number_of_fields,
                              int target_inobject, int target_unused,
                              int* old_number_of_fields,
                              ConcurrencyMode cmode) const;
  // Returns true if the |field_type| is the most general one for
  // given |representation|.
  static inline bool IsMostGeneralFieldType(Representation representation,
                                            Tagged<FieldType> field_type);

  // Generalizes representation and field_type if objects with given
  // instance type can have fast elements that can be transitioned by
  // stubs or optimized code to more general elements kind.
  // This generalization is necessary in order to ensure that elements kind
  // transitions performed by stubs / optimized code don't silently transition
  // fields with representation "Tagged" back to "Smi" or "HeapObject" or
  // fields with HeapObject representation and "Any" type back to "Class" type.
  static inline void GeneralizeIfCanHaveTransitionableFastElementsKind(
      Isolate* isolate, InstanceType instance_type,
      Representation* representation, DirectHandle<FieldType>* field_type);

  V8_EXPORT_PRIVATE static DirectHandle<Map> PrepareForDataProperty(
      Isolate* isolate, DirectHandle<Map> old_map,
      InternalIndex descriptor_number, PropertyConstness constness,
      DirectHandle<Object> value);

  V8_EXPORT_PRIVATE static Handle<Map> Normalize(
      Isolate* isolate, DirectHandle<Map> map, InstanceType new_instance_type,
      ElementsKind new_elements_kind, DirectHandle<JSPrototype> new_prototype,
      PropertyNormalizationMode mode, bool use_cache, const char* reason);
  V8_EXPORT_PRIVATE static Handle<Map> Normalize(
      Isolate* isolate, DirectHandle<Map> map, ElementsKind new_elements_kind,
      DirectHandle<JSPrototype> new_prototype, PropertyNormalizationMode mode,
      bool use_cache, const char* reason) {
    return Normalize(isolate, map, map->instance_type(), new_elements_kind,
                     new_prototype, mode, use_cache, reason);
  }
  V8_EXPORT_PRIVATE static Handle<Map> Normalize(
      Isolate* isolate, DirectHandle<Map> map, InstanceType new_instance_type,
      ElementsKind new_elements_kind, DirectHandle<JSPrototype> new_prototype,
      PropertyNormalizationMode mode, const char* reason) {
    const bool kUseCache = true;
    return Normalize(isolate, map, new_instance_type, new_elements_kind,
                     new_prototype, mode, kUseCache, reason);
  }
  V8_EXPORT_PRIVATE static Handle<Map> Normalize(
      Isolate* isolate, DirectHandle<Map> map, ElementsKind new_elements_kind,
      DirectHandle<JSPrototype> new_prototype, PropertyNormalizationMode mode,
      const char* reason) {
    const bool kUseCache = true;
    return Normalize(isolate, map, new_elements_kind, new_prototype, mode,
                     kUseCache, reason);
  }

  inline static Handle<Map> Normalize(Isolate* isolate,
                                      DirectHandle<Map> fast_map,
                                      PropertyNormalizationMode mode,
                                      const char* reason);

  // Tells whether the map is used for JSObjects in dictionary mode (ie
  // normalized objects, ie objects for which HasFastProperties returns false).
  // A map can never be used for both dictionary mode and fast mode JSObjects.
  // False by default and for HeapObjects that are not JSObjects.
  DECL_BOOLEAN_ACCESSORS(is_dictionary_map)

  // Tells whether the instance needs security checks when accessing its
  // properties.
  DECL_BOOLEAN_ACCESSORS(is_access_check_needed)

  // [prototype]: implicit prototype object.
  DECL_ACCESSORS(prototype, Tagged<JSPrototype>)
  // TODO(jkummerow): make set_prototype private.

  // {enable_prototype_setup_mode}: Switch the prototype to dictionary mode,
  // which is faster for adding multiple properties to it.
  V8_EXPORT_PRIVATE static void SetPrototype(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<JSPrototype> prototype,
      bool enable_prototype_setup_mode = true);

  // Sets prototype and constructor fields to null.
  inline void init_prototype_and_constructor_or_back_pointer(
      ReadOnlyRoots roots);
  // As above, but safe to call during early RO-heap bootstrapping
  // where GetReadOnlyRoots (used by Cast's IsNull) is not yet usable.
  inline void init_prototype_and_constructor_or_back_pointer_during_bootstrap(
      ReadOnlyRoots roots);

  // [constructor]: points back to the function or FunctionTemplateInfo
  // responsible for this map.
  // The field overlaps with the back pointer. All maps in a transition tree
  // have the same constructor, so maps with back pointers can walk the
  // back pointer chain until they find the map holding their constructor.
  // Returns null_value if there's neither a constructor function nor a
  // FunctionTemplateInfo available.
  // The field also overlaps with the native context pointer for context maps,
  // and with the Wasm type info for WebAssembly object maps.
  DECL_ACCESSORS(constructor_or_back_pointer, Tagged<Object>)
  DECL_RELAXED_ACCESSORS(constructor_or_back_pointer, Tagged<Object>)
  DECL_ACCESSORS(native_context, Tagged<NativeContext>)
  DECL_ACCESSORS(native_context_or_null, Tagged<Object>)
  DECL_GETTER(raw_native_context_or_null, Tagged<Object>)
  DECL_ACCESSORS(wasm_type_info, Tagged<WasmTypeInfo>)

  // Gets |constructor_or_back_pointer| field value from the root map.
  // The result might be null, JSFunction, FunctionTemplateInfo or a Tuple2
  // for JSFunctions with non-instance prototypes.
  inline Tagged<Object> GetConstructorRaw() const;

  // Gets constructor value from the root map.
  // The result returned might be null, JSFunction or FunctionTemplateInfo.
  inline Tagged<Object> GetConstructor() const;
  inline Tagged<FunctionTemplateInfo> GetFunctionTemplateInfo() const;
  inline void SetConstructor(Tagged<Object> constructor,
                             WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
  // Constructor getter that performs at most the given number of steps
  // in the transition tree. Returns either the constructor or the map at
  // which the walk has stopped.
  inline Tagged<Object> TryGetConstructor(int max_steps);

  // [back pointer]: points back to the parent map from which a transition
  // leads to this map. The field overlaps with the constructor (see above).
  inline Tagged<HeapObject> GetBackPointer() const;
  inline void SetBackPointer(Tagged<HeapObject> value,
                             WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
  inline bool TryGetBackPointer(Tagged<Map>* back_pointer) const;

  // [instance descriptors]: describes the object.
  DECL_ACCESSORS(instance_descriptors, Tagged<DescriptorArray>)
  DECL_ACQUIRE_GETTER(instance_descriptors, Tagged<DescriptorArray>)
  V8_EXPORT_PRIVATE void SetInstanceDescriptors(
      Tagged<DescriptorArray> descriptors, int number_of_own_descriptors,
      WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER);

#if V8_ENABLE_WEBASSEMBLY
  // Only for WasmStructs: custom descriptor instead of instance_descriptors.
  // The getter widens to the full field union since callers may speculatively
  // inspect the slot before confirming it actually holds a WasmStruct (e.g.
  // MapVerify, where a canonical-RTT wasm struct map may still hold a
  // DescriptorArray).
  DECL_GETTER(custom_descriptor, Tagged<UnionOf<DescriptorArray, WasmStruct>>)
  DECL_SETTER(custom_descriptor, Tagged<WasmStruct>)
#endif  // V8_ENABLE_WEBASSEMBLY

  inline void UpdateDescriptors(Tagged<DescriptorArray> descriptors,
                                int number_of_own_descriptors);
  inline void InitializeDescriptors(Tagged<DescriptorArray> descriptors);

  // [dependent code]: list of optimized codes that weakly embed this map.
  DECL_ACCESSORS(dependent_code, Tagged<DependentCode>)
#if V8_ENABLE_WEBASSEMBLY
  // [immediate_supertype_map]: overlaid onto the "dependent_code" field,
  // Wasm maps with custom descriptors store their immediate supertype map
  // (i.e. the canonical RTT for their static type) here, for fast access
  // from type checks in generated code.
  DECL_ACCESSORS(immediate_supertype_map, Tagged<Map>)
  inline bool has_immediate_supertype_map() const;
#endif  // V8_ENABLE_WEBASSEMBLY

  // [prototype_validity_cell]: Cell containing the validity bit for prototype
  // chains or Tagged<Smi>(0) if uninitialized.
  // The meaning of this validity cell is different for prototype maps and
  // non-prototype maps.
  // For prototype maps the validity bit "guards" modifications of prototype
  // chains going through this object. When a prototype object changes, both its
  // own validity cell and those of all "downstream" prototypes are invalidated;
  // handlers for a given receiver embed the currently valid cell for that
  // receiver's prototype during their creation and check it on execution.
  // For non-prototype maps which are used as transitioning store handlers this
  // field contains the validity cell which guards modifications of this map's
  // prototype.
  DECL_RELAXED_ACCESSORS(prototype_validity_cell, Tagged<UnionOf<Smi, Cell>>)

  // Returns true if prototype validity cell value represents "valid" prototype
  // chain state.
  inline bool IsPrototypeValidityCellValid() const;

  // Returns true if this map belongs to the same native context as given map,
  // i.e. this map's meta map is equal to other_map's meta map.
  // Returns false if this map is contextless (in case of JSObject map this
  // means that the object is remote).
  inline bool BelongsToSameNativeContextAs(Tagged<Map> other_map) const;
  inline bool BelongsToSameNativeContextAs(Tagged<Context> context) const;

  inline Tagged<Name> GetLastDescriptorName() const;
  inline PropertyDetails GetLastDescriptorDetails() const;

  inline InternalIndex LastAdded() const;

  inline int NumberOfOwnDescriptors() const;
  inline void SetNumberOfOwnDescriptors(int number);
  inline InternalIndex::Range IterateOwnDescriptors() const;

  inline Tagged<Cell> RetrieveDescriptorsPointer();

  // Checks whether all properties are stored either in the map or on the object
  // (inobject, properties, or elements backing store), requiring no special
  // checks.
  bool OnlyHasSimpleProperties() const;
  inline int EnumLength() const;
  inline void SetEnumLength(int length);

  DECL_BOOLEAN_ACCESSORS(owns_descriptors)

  inline void mark_unstable();
  inline bool is_stable() const;

  DECL_BOOLEAN_ACCESSORS(is_migration_target)

  DECL_BOOLEAN_ACCESSORS(is_immutable_proto)

  // This counter is used for in-object slack tracking.
  // The in-object slack tracking is considered enabled when the counter is
  // non zero. The counter only has a valid count for initial maps. For
  // transitioned maps only kNoSlackTracking has a meaning, namely that inobject
  // slack tracking already finished for the transition tree. Any other value
  // indicates that either inobject slack tracking is still in progress, or that
  // the map isn't part of the transition tree anymore.
  DECL_INT_ACCESSORS(construction_counter)

  DECL_BOOLEAN_ACCESSORS(is_deprecated)
  inline bool CanBeDeprecated() const;

  // Returns a non-deprecated version of the input. If the input was not
  // deprecated, it is directly returned. Otherwise, the non-deprecated version
  // is found by re-transitioning from the root of the transition tree using the
  // descriptor array of the map. Returns MaybeHandle<Map>() if no updated map
  // is found.
  V8_EXPORT_PRIVATE static MaybeHandle<Map> TryUpdate(
      Isolate* isolate, Handle<Map> map) V8_WARN_UNUSED_RESULT;

  // Returns a non-deprecated version of the input. This method may deprecate
  // existing maps along the way if encodings conflict. Not for use while
  // gathering type feedback. Use TryUpdate in those cases instead.
  V8_EXPORT_PRIVATE static DirectHandle<Map> Update(Isolate* isolate,
                                                    DirectHandle<Map> map);

  static inline Handle<Map> CopyInitialMap(Isolate* isolate,
                                           DirectHandle<Map> map);
  V8_EXPORT_PRIVATE static Handle<Map> CopyInitialMap(
      Isolate* isolate, DirectHandle<Map> map, int instance_size,
      int in_object_properties, int unused_property_fields);
  static DirectHandle<Map> CopyInitialMapNormalized(
      Isolate* isolate, DirectHandle<Map> map,
      PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES);
  static Handle<Map> CopyDropDescriptors(Isolate* isolate,
                                         DirectHandle<Map> map);
  V8_EXPORT_PRIVATE static Handle<Map> CopyInsertDescriptor(
      Isolate* isolate, DirectHandle<Map> map, Descriptor* descriptor,
      TransitionFlag flag);

  static MaybeObjectDirectHandle WrapFieldType(DirectHandle<FieldType> type);
  V8_EXPORT_PRIVATE static Tagged<FieldType> UnwrapFieldType(
      Tagged<MaybeObject> wrapped_type);

  V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle<Map> CopyWithField(
      Isolate* isolate, DirectHandle<Map> map, DirectHandle<Name> name,
      DirectHandle<FieldType> type, PropertyAttributes attributes,
      PropertyConstness constness, Representation representation,
      TransitionFlag flag);

  V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle<Map>
  CopyWithConstant(Isolate* isolate, DirectHandle<Map> map,
                   DirectHandle<Name> name, DirectHandle<Object> constant,
                   PropertyAttributes attributes, TransitionFlag flag);

  // Returns a new map with all transitions dropped from the given map and
  // the ElementsKind set.
  static DirectHandle<Map> TransitionElementsTo(Isolate* isolate,
                                                DirectHandle<Map> map,
                                                ElementsKind to_kind);

  static std::optional<Tagged<Map>> TryAsElementsKind(Isolate* isolate,
                                                      DirectHandle<Map> map,
                                                      ElementsKind kind,
                                                      ConcurrencyMode cmode);
  V8_EXPORT_PRIVATE static Handle<Map> AsElementsKind(Isolate* isolate,
                                                      DirectHandle<Map> map,
                                                      ElementsKind kind);

  static Handle<Map> CopyAsElementsKind(Isolate* isolate, DirectHandle<Map> map,
                                        ElementsKind kind, TransitionFlag flag);

  V8_EXPORT_PRIVATE static Handle<Map> AsDetachedTypedArray(
      Isolate* isolate, DirectHandle<Map> map);

  static DirectHandle<Map> AsLanguageMode(
      Isolate* isolate, DirectHandle<Map> initial_map,
      DirectHandle<SharedFunctionInfo> shared_info);

  V8_EXPORT_PRIVATE static Handle<Map> CopyForPreventExtensions(
      Isolate* isolate, DirectHandle<Map> map, PropertyAttributes attrs_to_add,
      DirectHandle<Symbol> transition_marker, const char* reason,
      bool old_map_is_dictionary_elements_kind = false);

  // Maximal number of fast properties. Used to restrict the number of map
  // transitions to avoid an explosion in the number of maps for objects used as
  // dictionaries.
  inline bool TooManyFastProperties(StoreOrigin store_origin) const;
  V8_EXPORT_PRIVATE static DirectHandle<Map> TransitionToDataProperty(
      Isolate* isolate, DirectHandle<Map> map, DirectHandle<Name> name,
      DirectHandle<Object> value, PropertyAttributes attributes,
      PropertyConstness constness, StoreOrigin store_origin);
  V8_EXPORT_PRIVATE static DirectHandle<Map> TransitionToAccessorProperty(
      Isolate* isolate, DirectHandle<Map> map, DirectHandle<Name> name,
      InternalIndex descriptor, DirectHandle<Object> getter,
      DirectHandle<Object> setter, PropertyAttributes attributes);

  inline void AppendDescriptor(Isolate* isolate, Descriptor* desc);

  // Returns a copy of the map, prepared for inserting into the transition
  // tree (if the |map| owns descriptors then the new one will share
  // descriptors with |map|).
  static Handle<Map> CopyForElementsTransition(Isolate* isolate,
                                               DirectHandle<Map> map);

  // Returns a copy of the map, prepared for inserting into the transition
  // tree as a prototype transition.
  static Handle<Map> CopyForPrototypeTransition(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<JSPrototype> prototype);

  // Returns a copy of the map, with all transitions dropped from the
  // instance descriptors.
  static Handle<Map> Copy(Isolate* isolate, DirectHandle<Map> map,
                          const char* reason,
                          TransitionKindFlag kind = SPECIAL_TRANSITION);
  V8_EXPORT_PRIVATE static Handle<Map> Create(Isolate* isolate,
                                              int inobject_properties);

  // Returns the next free property offset (only valid for FAST MODE).
  FieldStorageLocation NextFreeFieldStorageLocation() const;

  // Returns the number of enumerable properties.
  int NumberOfEnumerableProperties() const;

  static inline int SlackForArraySize(int old_size, int size_limit);

  V8_EXPORT_PRIVATE static void EnsureDescriptorSlack(Isolate* isolate,
                                                      DirectHandle<Map> map,
                                                      int slack);

  // Returns the map to be used for instances when the given {prototype} is
  // passed to an Object.create call. Might transition the given {prototype}.
  static DirectHandle<Map> GetObjectCreateMap(
      Isolate* isolate, DirectHandle<JSPrototype> prototype);

  // Returns the map to be used for instances when the given {prototype} is
  // passed to Reflect.construct or proxy constructors.
  static Handle<Map> GetDerivedMap(Isolate* isolate, DirectHandle<Map> from,
                                   DirectHandle<JSReceiver> prototype);

  // Computes a hash value for this map, to be used e.g. in HashTables. The
  // prototype value should be either the Map's prototype or another prototype
  // in case the hash is supposed to be computed for a copy of this map with a
  // changed prototype value.
  int Hash(Isolate* isolate, Tagged<HeapObject> prototype);

  // Returns the transitioned map for this map with the most generic
  // elements_kind that's found in |candidates|, or |nullptr| if no match is
  // found at all.
  V8_EXPORT_PRIVATE Tagged<Map> FindElementsKindTransitionedMap(
      Isolate* isolate, MapHandlesSpan candidates, ConcurrencyMode cmode);

  inline bool CanTransition() const;

  static constexpr std::optional<RootIndex> TryGetMapRootIdxFor(
      InstanceType type);
  static inline Tagged<Map> GetMapFor(ReadOnlyRoots roots, InstanceType type);

  bool IsMapInArrayPrototypeChain(Isolate* isolate) const;

  // Dispatched behavior.
  void MapPrint(std::ostream& os);
  DECL_VERIFIER(Map)

#ifdef VERIFY_HEAP
  void DictionaryMapVerify(Isolate* isolate);
#endif
#if defined(DEBUG) || defined(VERIFY_HEAP)
  V8_EXPORT_PRIVATE void VerifyDescriptorInObjectBits(
      Tagged<DescriptorArray> descriptors, int number_of_own_descriptors);
  V8_EXPORT_PRIVATE void VerifyPropertyDetailsInObjectBits(
      PropertyDetails details);
#endif

  DECL_PRIMITIVE_ACCESSORS(visitor_id, VisitorId)

  static constexpr ObjectFields ObjectFieldsFrom(VisitorId visitor_id) {
    return (visitor_id < kDataOnlyVisitorIdCount)
               ? ObjectFields::kDataOnly
               : ObjectFields::kMaybePointers;
  }

  V8_EXPORT_PRIVATE static Handle<Map> TransitionRootMapToPrototypeForNewObject(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<JSPrototype> prototype);
  V8_EXPORT_PRIVATE static Handle<Map> TransitionToUpdatePrototype(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<JSPrototype> prototype);

  static DirectHandle<Map> TransitionToImmutableProto(Isolate* isolate,
                                                      DirectHandle<Map> map);

  class BodyDescriptor;

  // Compares this map to another to see if they describe equivalent objects,
  // up to the given |elements_kind| and |prototype|. If |mode| is set to
  // CLEAR_INOBJECT_PROPERTIES, |other| is treated as if it had exactly zero
  // inobject properties. The "shared" flags of both this map and |other| are
  // ignored.
  bool EquivalentToForNormalization(const Tagged<Map> other,
                                    ElementsKind elements_kind,
                                    Tagged<HeapObject> prototype,
                                    PropertyNormalizationMode mode) const;
  inline bool EquivalentToForNormalization(
      const Tagged<Map> other, PropertyNormalizationMode mode) const;

  void PrintMapDetails(std::ostream& os);

  static inline DirectHandle<Map> AddMissingTransitionsForTesting(
      Isolate* isolate, DirectHandle<Map> split_map,
      DirectHandle<DescriptorArray> descriptors);

  // Fires when the layout of an object with a leaf map changes.
  // This includes adding transitions to the leaf map or changing
  // the descriptor array.
  inline void NotifyLeafMapLayoutChange(Isolate* isolate);

  V8_EXPORT_PRIVATE static VisitorId GetVisitorId(Tagged<Map> map);

  // Returns true if objects with given instance type are allowed to have
  // fast transitionable elements kinds. This predicate is used to ensure
  // that objects that can have transitionable fast elements kind will not
  // get in-place generalizable fields because the elements kind transition
  // performed by stubs or optimized code can't properly generalize such
  // fields.
  static inline bool CanHaveFastTransitionableElementsKind(
      InstanceType instance_type);
  inline bool CanHaveFastTransitionableElementsKind() const;

  // Maps for Wasm objects can use certain fields for other purposes.
  inline uint8_t WasmByte1() const;
  inline uint8_t WasmByte2() const;
  inline void SetWasmByte1(uint8_t value);
  inline void SetWasmByte2(uint8_t value);

 private:
  // This byte encodes either the instance size without the in-object slack or
  // the slack size in properties backing store.
  // Let H be JSObject::kHeaderSize / kTaggedSize.
  // If value >= H then:
  //     - all field properties are stored in the object.
  //     - there is no property array.
  //     - value * kTaggedSize is the actual object size without the slack.
  // Otherwise:
  //     - there is no slack in the object.
  //     - the property array has value slack slots.
  // Note that this encoding requires that H = JSObject::kFieldsAdded.
  DECL_INT_ACCESSORS(used_or_unused_instance_size_in_words)

  // Returns the map that this (root) map transitions to if its elements_kind
  // is changed to |elements_kind|, or |nullptr| if no such map is cached yet.
  Tagged<Map> LookupElementsTransitionMap(Isolate* isolate,
                                          ElementsKind elements_kind,
                                          ConcurrencyMode cmode);

  // Tries to replay property transitions starting from this (root) map using
  // the descriptor array of the |map|. The |root_map| is expected to have
  // proper elements kind and therefore elements kinds transitions are not
  // taken by this function. Returns |nullptr| if matching transition map is
  // not found.
  Tagged<Map> TryReplayPropertyTransitions(Isolate* isolate, Tagged<Map> map,
                                           ConcurrencyMode cmode);

  static void ConnectTransition(Isolate* isolate, DirectHandle<Map> parent,
                                DirectHandle<Map> child,
                                DirectHandle<Name> name,
                                TransitionKindFlag transition_kind,
                                bool force_connect = false);

  bool EquivalentToForTransition(
      const Tagged<Map> other, ConcurrencyMode cmode,
      DirectHandle<HeapObject> new_prototype = {},
      std::optional<InstanceType> new_instance_type = {}) const;
  bool EquivalentToForElementsKindTransition(const Tagged<Map> other,
                                             ConcurrencyMode cmode) const;
  static Handle<Map> RawCopy(Isolate* isolate, DirectHandle<Map> map,
                             int instance_size, int inobject_properties);
  static Handle<Map> ShareDescriptor(Isolate* isolate, DirectHandle<Map> map,
                                     DirectHandle<DescriptorArray> descriptors,
                                     Descriptor* descriptor);
  V8_EXPORT_PRIVATE static Handle<Map> AddMissingTransitions(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<DescriptorArray> descriptors);
  static void InstallDescriptors(Isolate* isolate, DirectHandle<Map> parent_map,
                                 DirectHandle<Map> child_map,
                                 InternalIndex new_descriptor,
                                 DirectHandle<DescriptorArray> descriptors,
                                 // force_connect is used when copying a map
                                 // tree to enforce transitions being added even
                                 // for (still) seemingly detached maps.
                                 bool force_connect = false);
  static Handle<Map> CopyAddDescriptor(Isolate* isolate, DirectHandle<Map> map,
                                       Descriptor* descriptor,
                                       TransitionFlag flag);

  template <typename InitMapCb>
  static Handle<Map> CopyReplaceDescriptors(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<DescriptorArray> descriptors, TransitionFlag flag,
      const InitMapCb& InitMap, MaybeDirectHandle<Name> maybe_name,
      const char* reason, TransitionKindFlag transition_kind);

  static Handle<Map> CopyReplaceDescriptors(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<DescriptorArray> descriptors, TransitionFlag flag,
      MaybeDirectHandle<Name> maybe_name, const char* reason,
      TransitionKindFlag transition_kind);

  static Handle<Map> CopyReplaceDescriptor(
      Isolate* isolate, DirectHandle<Map> map,
      DirectHandle<DescriptorArray> descriptors, Descriptor* descriptor,
      InternalIndex index, TransitionFlag flag);
  static Handle<Map> CopyNormalized(Isolate* isolate, DirectHandle<Map> map,
                                    PropertyNormalizationMode mode);

  void DeprecateTransitionTree(Isolate* isolate);
  void DeprecateTransitionTreeImpl(Isolate* isolate);

  void ReplaceDescriptors(Isolate* isolate,
                          Tagged<DescriptorArray> new_descriptors);

  // This is the replacement for IsMap() which avoids reading the instance type
  // but compares the object's map against given meta_map, so it can be used
  // concurrently without acquire load.
  V8_INLINE static bool ConcurrentIsHeapObjectWithMap(Tagged<Object> object,
                                                      Tagged<Map> meta_map);

  // Use the high-level instance_descriptors/SetInstanceDescriptors instead.
  DECL_RELEASE_SETTER(instance_descriptors, Tagged<DescriptorArray>)

  // Hide inherited accessors from the generated superclass.
  DECL_ACCESSORS(constructor_or_back_pointer_or_native_context, Tagged<Object>)
  DECL_ACCESSORS(transitions_or_prototype_info, Tagged<Object>)

  friend class MapUpdater;
  template <typename ConcreteVisitor>
  friend class MarkingVisitorBase;

 public:
  // Backwards-compatible offset constants. Defined out-of-line below
  // because offsetof / sizeof on Map cannot appear inside Map's own body.
  static const int kBitFieldOffsetEnd;
#if V8_ENABLE_WEBASSEMBLY
#endif
  static const int kEndOfWeakFieldsOffset;
  static const int kHeaderSize;
  static const int kSize;

  std::atomic<uint8_t> instance_size_in_words_;
  std::atomic<uint8_t> inobject_properties_start_or_constructor_function_index_;
  std::atomic<uint8_t> used_or_unused_instance_size_in_words_;
  std::atomic<uint8_t> visitor_id_;
  std::atomic<uint16_t> instance_type_;
  std::atomic<uint8_t> bit_field_;
  uint8_t bit_field2_;
  std::atomic<uint32_t> bit_field3_;
#if TAGGED_SIZE_8_BYTES
  uint32_t optional_padding_;
#endif
  TaggedMember<JSPrototype> prototype_;
  TaggedMember<Object> constructor_or_back_pointer_or_native_context_;
#if V8_ENABLE_WEBASSEMBLY
  TaggedMember<UnionOf<DescriptorArray, WasmStruct>> instance_descriptors_;
  TaggedMember<UnionOf<DependentCode, Map>> dependent_code_;
#else
  TaggedMember<DescriptorArray> instance_descriptors_;
  TaggedMember<DependentCode> dependent_code_;
#endif
  TaggedMember<UnionOf<Smi, Cell>> prototype_validity_cell_;
  TaggedMember<UnionOf<Smi, MaybeWeak<Map>, TransitionArray, PrototypeInfo,
                       PrototypeSharedClosureInfo>>
      transitions_or_prototype_info_;
} V8_OBJECT_END;

// Backwards-compatible Map offset constants. Defined out-of-line because
// offsetof / sizeof on Map cannot appear inside Map's own class body.
inline constexpr int Map::kBitFieldOffsetEnd =
    offsetof(Map, bit_field_) + sizeof(uint8_t) - 1;
#if V8_ENABLE_WEBASSEMBLY
#endif
inline constexpr int Map::kEndOfWeakFieldsOffset = sizeof(Map);
inline constexpr int Map::kHeaderSize = sizeof(Map);
inline constexpr int Map::kSize = sizeof(Map);

static_assert(offsetof(Map, instance_type_) ==
              Internals::kMapInstanceTypeOffset);

// Base class for Maps with extra fields. Subclasses must be defined with
// @hasSameInstanceTypeAsParent and define padding fields up to kTaggedSize.
V8_ABSTRACT_OBJECT class ExtendedMap : public Map {
  V8_IT_REUSE_PARENT;

 public:
  // Bit positions for |bit_field_ex|.
  struct BitsEx {
    using MapKindBits = base::BitField<ExtendedMapKind, 0, 3, uint8_t>;
    using MapSizeInWordsBits = MapKindBits::Next<uint8_t, 5>;
  };

  inline uint8_t bit_field_ex() const;
  inline void set_bit_field_ex(uint8_t value);

  inline uint8_t relaxed_bit_field_ex() const;
  inline void set_relaxed_bit_field_ex(uint8_t value);

  inline ExtendedMapKind map_kind() const;
  inline uint8_t map_size_in_words() const;
  inline int map_size() const;

  inline void set_map_kind_and_size(ExtendedMapKind kind, int size_in_bytes);

 public:
  static const int kMinimumSize;
  static const int kStartOfStrongExtendedFieldsOffset;

  std::atomic<uint8_t> bit_field_ex_;
  // Leaves kTaggedSize-1 unused bytes, they will be used by subclasses.
} V8_OBJECT_END;

constexpr int ExtendedMapSizeForKind(ExtendedMapKind kind);

// Defined out-of-line because offsetof / sizeof on ExtendedMap cannot appear
// inside ExtendedMap's own class body.
inline constexpr int ExtendedMap::kMinimumSize =
    RoundUp(sizeof(ExtendedMap), kTaggedSize);
inline constexpr int ExtendedMap::kStartOfStrongExtendedFieldsOffset =
    kMinimumSize;



// The cache for maps used by normalized (dictionary mode) objects.
// Such maps do not have property descriptors, so a typical program
// needs very limited number of distinct normalized maps.
class NormalizedMapCache : public WeakFixedArray {
  V8_IT_NO_AUTO_CHECKER;

 public:
  static DirectHandle<NormalizedMapCache> New(Isolate* isolate);

  V8_WARN_UNUSED_RESULT MaybeHandle<Map> Get(Isolate* isolate,
                                             DirectHandle<Map> fast_map,
                                             ElementsKind elements_kind,
                                             Tagged<HeapObject> prototype,
                                             PropertyNormalizationMode mode);
  void Set(Isolate* isolate, DirectHandle<Map> fast_map,
           DirectHandle<Map> normalized_map);

  DECL_VERIFIER(NormalizedMapCache)

 private:
  friend bool IsNormalizedMapCache(Tagged<HeapObject> obj);

  static const uint32_t kEntries = 64;

  static inline int GetIndex(Isolate* isolate, Tagged<Map> map,
                             Tagged<HeapObject> prototype);

  // The following declarations hide base class methods.
  Tagged<Object> get(int index);
  void set(int index, Tagged<Object> value);
};

#define DECL_TESTER(Type, ...) inline bool Is##Type##Map(Tagged<Map> map);
INSTANCE_TYPE_CHECKERS(DECL_TESTER)
#undef DECL_TESTER
inline bool IsMetaMap(Tagged<Map> map);
inline bool IsExtendedMap(Tagged<Map> map);
inline bool IsJSInterceptorMap(Tagged<Map> map);
inline bool IsNullMap(Tagged<Map> map);
inline bool IsUndefinedMap(Tagged<Map> map);
inline bool IsBooleanMap(Tagged<Map> map);
inline bool IsNullOrUndefinedMap(Tagged<Map> map);
inline bool IsPrimitiveMap(Tagged<Map> map);
inline bool IsSpecialReceiverMap(Tagged<Map> map);
inline bool IsCustomElementsReceiverMap(Tagged<Map> map);

}  // namespace v8::internal

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

#endif  // V8_OBJECTS_MAP_H_