// Copyright 2023 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_TAGGED_H_ #define V8_OBJECTS_TAGGED_H_ #include <type_traits> #include "src/common/globals.h" #include "src/objects/tagged-impl.h" #include "src/objects/union.h" namespace v8 { namespace internal { class BigInt; class FieldType; class HeapObject; class HeapNumber; class HeapObjectLayout; class Object; class TaggedIndex; class Smi; // Tagged<T> represents an uncompressed V8 tagged pointer. // // The tagged pointer is a pointer-sized value with a tag in the LSB. The value // is either: // // * A small integer (Smi), shifted right, with the tag set to 0 // * A strong pointer to an object on the V8 heap, with the tag set to 01 // * A weak pointer to an object on the V8 heap, with the tag set to 11 // * A cleared weak pointer, with the value 11 // // The exact encoding differs depending on 32- vs 64-bit architectures, and in // the latter case, whether or not pointer compression is enabled. // // On 32-bit architectures, this is: // |----- 32 bits -----| // Pointer: |______address____w1| // Smi: |____int31_value___0| // // On 64-bit architectures with pointer compression: // |----- 32 bits -----|----- 32 bits -----| // Pointer: |________base_______|______offset_____w1| // Smi: |......garbage......|____int31_value___0| // // On 64-bit architectures without pointer compression: // |----- 32 bits -----|----- 32 bits -----| // Pointer: |________________address______________w1| // Smi: |____int32_value____|00...............00| // // where `w` is the "weak" bit. // // We specialise Tagged separately for Object, Smi and HeapObject, and then all // other types T, so that: // // Tagged<Object> -> StrongTaggedBase // Tagged<Smi> -> StrongTaggedBase // Tagged<T> -> Tagged<HeapObject> -> StrongTaggedBase // // We also specialize it separately for MaybeWeak types, with a parallel // hierarchy: // // Tagged<MaybeWeak<Object>> -> WeakTaggedBase // Tagged<MaybeWeak<Smi>> -> WeakTaggedBase // Tagged<MaybeWeak<T>> -> Tagged<MaybeWeak<HeapObject>> -> WeakTaggedBase template <typename T> class Tagged; // MaybeWeak<T> represents a reference to T that may be either a strong or weak. // // MaybeWeak doesn't really exist by itself, but is rather a sentinel type for // templates on tagged interfaces (like Tagged). For example, where Tagged<T> // represents a strong reference to T, Tagged<MaybeWeak<T>> represents a // potentially weak reference to T, and it is the responsibility of the Tagged // interface to provide some mechanism (likely template specialization) to // distinguish between the two and provide accessors to the T reference itself // (which will always be strong). template <typename T> class MaybeWeak { … }; template <typename T> struct is_maybe_weak : public std::false_type { … }; is_maybe_weak<MaybeWeak<T>>; is_maybe_weak_v; // ClearedWeakValue is a sentinel type for cleared weak values. class ClearedWeakValue { … }; // Convert a strong reference to T into a weak reference to T. template <typename T> inline Tagged<MaybeWeak<T>> MakeWeak(Tagged<T> value); template <typename T> inline Tagged<MaybeWeak<T>> MakeWeak(Tagged<MaybeWeak<T>> value); // Convert a weak reference to T into a strong reference to T. template <typename T> inline Tagged<T> MakeStrong(Tagged<T> value); template <typename T> inline Tagged<T> MakeStrong(Tagged<MaybeWeak<T>> value); // Base class for all Tagged<T> classes. StrongTaggedBase; WeakTaggedBase; // `is_subtype<Derived, Base>::value` is true when Derived is a subtype of Base // according to our object hierarchy. In particular, Smi is considered a // subtype of Object. template <typename Derived, typename Base, typename Enabled = void> struct is_subtype; is_subtype_v; namespace detail { template <typename Derived, typename Base, typename Enabled = void> struct is_simple_subtype; template <typename Derived, typename Base, typename Enabled = void> struct is_complex_subtype; } // namespace detail // `is_subtype<Derived, Base>` tries is_simple_subtype first, and if that fails, // is_complex_subtype. This is to prevent instantiating the is_complex_subtype // template when is_simple_subtype, to avoid trying std::is_base_of. This allows // subtype checks to pass, for simple subtypes, with forward declarations. template <typename Derived, typename Base, typename Enabled> struct is_subtype : public std::disjunction<detail::is_simple_subtype<Derived, Base>, detail::is_complex_subtype<Derived, Base>> { … }; // Forward declarations for is_simple_subtype hack, remove once those // specializations are removed. class FixedArrayBase; class FixedArray; class FixedDoubleArray; class ByteArray; class NameDictionary; class NumberDictionary; class OrderedHashMap; class OrderedHashSet; class OrderedNameDictionary; class ScriptContextTable; class ArrayList; detail // namespace detail static_assert …; static_assert …; static_assert …; // `is_taggable<T>::value` is true when T is a valid type for Tagged. This means // de-facto being a subtype of Object. is_taggable; is_taggable_v; // `is_castable<From, To>::value` is true when you can use `::cast` to cast from // From to To. This means an upcast or downcast, which in practice means // checking `is_subtype` symmetrically. is_castable; is_castable_v; // TODO(leszeks): Remove this once there are no more conversions between // Tagged<Foo> and Foo. static constexpr bool kTaggedCanConvertToRawObjects = …; namespace detail { // {TaggedOperatorArrowRef} is returned by {Tagged::operator->}. It should never // be stored anywhere or used in any other code; no one should ever have to // spell out {TaggedOperatorArrowRef} in code. Its only purpose is to be // dereferenced immediately by "operator-> chaining". Returning the address of // the field is valid because this objects lifetime only ends at the end of the // full statement. template <typename T> class TaggedOperatorArrowRef { … }; template <typename T> struct BaseForTagged { … }; BaseForTagged<MaybeWeak<T>>; BaseForTagged<Union<T...>>; // FieldType is special, since it can be Smi or Map. It could probably even be // its own specialization, to avoid exposing an operator->. template <> struct BaseForTagged<FieldType> { … }; } // namespace detail // Specialization for Object, where it's unknown whether this is a Smi or a // HeapObject. template <> class Tagged<Object> : public StrongTaggedBase { … }; // Specialization for Smi disallowing any implicit creation or access via ->, // but offering instead a cast from Object and an int32_t value() method. template <> class Tagged<Smi> : public StrongTaggedBase { … }; // Specialization for TaggedIndex disallowing any implicit creation or access // via ->, but offering instead a cast from Object and an intptr_t value() // method. template <> class Tagged<TaggedIndex> : public StrongTaggedBase { … }; // Specialization for HeapObject, to group together functions shared between all // HeapObjects template <> class Tagged<HeapObject> : public StrongTaggedBase { … }; static_assert …; // Specialization for MaybeWeak<Object>, where it's unknown whether this is a // Smi, a strong HeapObject, or a weak HeapObject template <> class Tagged<MaybeWeak<Object>> : public WeakTaggedBase { … }; // Specialization for MaybeWeak<HeapObject>, to group together functions shared // between all HeapObjects template <> class Tagged<MaybeWeak<HeapObject>> : public WeakTaggedBase { … }; // Generic Tagged<T> for Unions. This doesn't allow direct access to the object, // aside from casting. Tagged<Union<Ts...>>; // Generic Tagged<T> for any T that is a subclass of HeapObject. There are // separate Tagged<T> specialaizations for T==Smi and T==Object, so we know that // all other Tagged<T> are definitely pointers and not Smis. template <typename T> class Tagged : public detail::BaseForTagged<T>::type { … }; // Specialized Tagged<T> for cleared weak values. This is only used, in // practice, for conversions from Tagged<ClearedWeakValue> to a // Tagged<MaybeWeak<T>>, where subtyping rules mean that this works for // aribitrary T. template <> class Tagged<ClearedWeakValue> : public WeakTaggedBase { … }; // Generic Tagged<T> for any T that is a subclass of HeapObject. There are // separate Tagged<T> specializations for T==Smi and T==Object, so we know that // all other Tagged<T> are definitely pointers and not Smis. Tagged<MaybeWeak<T>>; MaybeObject; HeapObjectReference; template <typename T> inline Tagged<MaybeWeak<T>> MakeWeak(Tagged<T> value) { … } template <typename T> inline Tagged<MaybeWeak<T>> MakeWeak(Tagged<MaybeWeak<T>> value) { … } template <typename T> inline Tagged<T> MakeStrong(Tagged<T> value) { … } template <typename T> inline Tagged<T> MakeStrong(Tagged<MaybeWeak<T>> value) { … } // Deduction guide to simplify Foo->Tagged<Foo> transition. // TODO(leszeks): Remove once we're using Tagged everywhere. static_assert …; template <class T> Tagged(T object) -> Tagged<T>; <deduction guide for Tagged>(const HeapObjectLayout *); template <class T> Tagged(const T* object) -> Tagged<T>; template <class T> Tagged(T* object) -> Tagged<T>; template <typename T> struct RemoveTagged { … }; RemoveTagged<Tagged<T>>; } // namespace internal } // namespace v8 namespace std { // Template specialize std::common_type to always return Object when compared // against a subtype of Object. // // This is an incomplete specialization for objects and common_type, but // sufficient for existing use-cases. A proper specialization would need to be // conditionally enabled via `requires`, which is C++20, or with `enable_if`, // which would require a custom common_type implementation. common_type<T, i::Object>; } // namespace std #endif // V8_OBJECTS_TAGGED_H_