Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-22 08:28:36

0001 // Copyright 2018 the V8 project authors. All rights reserved.
0002 // Use of this source code is governed by a BSD-style license that can be
0003 // found in the LICENSE file.
0004 
0005 #ifndef INCLUDE_V8_INTERNAL_H_
0006 #define INCLUDE_V8_INTERNAL_H_
0007 
0008 #include <stddef.h>
0009 #include <stdint.h>
0010 #include <string.h>
0011 
0012 #include <atomic>
0013 #include <iterator>
0014 #include <limits>
0015 #include <memory>
0016 #include <optional>
0017 #include <type_traits>
0018 
0019 #include "v8config.h"  // NOLINT(build/include_directory)
0020 
0021 // TODO(pkasting): Use <compare>/spaceship unconditionally after dropping
0022 // support for old libstdc++ versions.
0023 #if __has_include(<version>)
0024 #include <version>
0025 #endif
0026 #if defined(__cpp_lib_three_way_comparison) &&   \
0027     __cpp_lib_three_way_comparison >= 201711L && \
0028     defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L
0029 #include <compare>
0030 #include <concepts>
0031 
0032 #define V8_HAVE_SPACESHIP_OPERATOR 1
0033 #else
0034 #define V8_HAVE_SPACESHIP_OPERATOR 0
0035 #endif
0036 
0037 namespace v8 {
0038 
0039 class Array;
0040 class Context;
0041 class Data;
0042 class Isolate;
0043 
0044 namespace internal {
0045 
0046 class Heap;
0047 class LocalHeap;
0048 class Isolate;
0049 class IsolateGroup;
0050 class LocalIsolate;
0051 
0052 typedef uintptr_t Address;
0053 static constexpr Address kNullAddress = 0;
0054 
0055 constexpr int KB = 1024;
0056 constexpr int MB = KB * 1024;
0057 constexpr int GB = MB * 1024;
0058 #ifdef V8_TARGET_ARCH_X64
0059 constexpr size_t TB = size_t{GB} * 1024;
0060 #endif
0061 
0062 /**
0063  * Configuration of tagging scheme.
0064  */
0065 const int kApiSystemPointerSize = sizeof(void*);
0066 const int kApiDoubleSize = sizeof(double);
0067 const int kApiInt32Size = sizeof(int32_t);
0068 const int kApiInt64Size = sizeof(int64_t);
0069 const int kApiSizetSize = sizeof(size_t);
0070 
0071 // Tag information for HeapObject.
0072 const int kHeapObjectTag = 1;
0073 const int kWeakHeapObjectTag = 3;
0074 const int kHeapObjectTagSize = 2;
0075 const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1;
0076 const intptr_t kHeapObjectReferenceTagMask = 1 << (kHeapObjectTagSize - 1);
0077 
0078 // Tag information for fowarding pointers stored in object headers.
0079 // 0b00 at the lowest 2 bits in the header indicates that the map word is a
0080 // forwarding pointer.
0081 const int kForwardingTag = 0;
0082 const int kForwardingTagSize = 2;
0083 const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1;
0084 
0085 // Tag information for Smi.
0086 const int kSmiTag = 0;
0087 const int kSmiTagSize = 1;
0088 const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
0089 
0090 template <size_t tagged_ptr_size>
0091 struct SmiTagging;
0092 
0093 constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1};
0094 constexpr uintptr_t kUintptrAllBitsSet =
0095     static_cast<uintptr_t>(kIntptrAllBitsSet);
0096 
0097 // Smi constants for systems where tagged pointer is a 32-bit value.
0098 template <>
0099 struct SmiTagging<4> {
0100   enum { kSmiShiftSize = 0, kSmiValueSize = 31 };
0101 
0102   static constexpr intptr_t kSmiMinValue =
0103       static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1));
0104   static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1);
0105 
0106   V8_INLINE static constexpr int SmiToInt(Address value) {
0107     int shift_bits = kSmiTagSize + kSmiShiftSize;
0108     // Truncate and shift down (requires >> to be sign extending).
0109     return static_cast<int32_t>(static_cast<uint32_t>(value)) >> shift_bits;
0110   }
0111 
0112   template <class T, typename std::enable_if_t<std::is_integral_v<T> &&
0113                                                std::is_signed_v<T>>* = nullptr>
0114   V8_INLINE static constexpr bool IsValidSmi(T value) {
0115     // Is value in range [kSmiMinValue, kSmiMaxValue].
0116     // Use unsigned operations in order to avoid undefined behaviour in case of
0117     // signed integer overflow.
0118     return (static_cast<uintptr_t>(value) -
0119             static_cast<uintptr_t>(kSmiMinValue)) <=
0120            (static_cast<uintptr_t>(kSmiMaxValue) -
0121             static_cast<uintptr_t>(kSmiMinValue));
0122   }
0123 
0124   template <class T,
0125             typename std::enable_if_t<std::is_integral_v<T> &&
0126                                       std::is_unsigned_v<T>>* = nullptr>
0127   V8_INLINE static constexpr bool IsValidSmi(T value) {
0128     static_assert(kSmiMaxValue <= std::numeric_limits<uintptr_t>::max());
0129     return value <= static_cast<uintptr_t>(kSmiMaxValue);
0130   }
0131 
0132   // Same as the `intptr_t` version but works with int64_t on 32-bit builds
0133   // without slowing down anything else.
0134   V8_INLINE static constexpr bool IsValidSmi(int64_t value) {
0135     return (static_cast<uint64_t>(value) -
0136             static_cast<uint64_t>(kSmiMinValue)) <=
0137            (static_cast<uint64_t>(kSmiMaxValue) -
0138             static_cast<uint64_t>(kSmiMinValue));
0139   }
0140 
0141   V8_INLINE static constexpr bool IsValidSmi(uint64_t value) {
0142     static_assert(kSmiMaxValue <= std::numeric_limits<uint64_t>::max());
0143     return value <= static_cast<uint64_t>(kSmiMaxValue);
0144   }
0145 };
0146 
0147 // Smi constants for systems where tagged pointer is a 64-bit value.
0148 template <>
0149 struct SmiTagging<8> {
0150   enum { kSmiShiftSize = 31, kSmiValueSize = 32 };
0151 
0152   static constexpr intptr_t kSmiMinValue =
0153       static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1));
0154   static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1);
0155 
0156   V8_INLINE static constexpr int SmiToInt(Address value) {
0157     int shift_bits = kSmiTagSize + kSmiShiftSize;
0158     // Shift down and throw away top 32 bits.
0159     return static_cast<int>(static_cast<intptr_t>(value) >> shift_bits);
0160   }
0161 
0162   template <class T, typename std::enable_if_t<std::is_integral_v<T> &&
0163                                                std::is_signed_v<T>>* = nullptr>
0164   V8_INLINE static constexpr bool IsValidSmi(T value) {
0165     // To be representable as a long smi, the value must be a 32-bit integer.
0166     return std::numeric_limits<int32_t>::min() <= value &&
0167            value <= std::numeric_limits<int32_t>::max();
0168   }
0169 
0170   template <class T,
0171             typename std::enable_if_t<std::is_integral_v<T> &&
0172                                       std::is_unsigned_v<T>>* = nullptr>
0173   V8_INLINE static constexpr bool IsValidSmi(T value) {
0174     return value <= std::numeric_limits<int32_t>::max();
0175   }
0176 };
0177 
0178 #ifdef V8_COMPRESS_POINTERS
0179 // See v8:7703 or src/common/ptr-compr-inl.h for details about pointer
0180 // compression.
0181 constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32;
0182 constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32;
0183 
0184 static_assert(
0185     kApiSystemPointerSize == kApiInt64Size,
0186     "Pointer compression can be enabled only for 64-bit architectures");
0187 const int kApiTaggedSize = kApiInt32Size;
0188 #else
0189 const int kApiTaggedSize = kApiSystemPointerSize;
0190 #endif
0191 
0192 constexpr bool PointerCompressionIsEnabled() {
0193   return kApiTaggedSize != kApiSystemPointerSize;
0194 }
0195 
0196 #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH
0197 using PlatformSmiTagging = SmiTagging<kApiInt32Size>;
0198 #else
0199 using PlatformSmiTagging = SmiTagging<kApiTaggedSize>;
0200 #endif
0201 
0202 // TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize
0203 // since it's used much more often than the inividual constants.
0204 const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
0205 const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
0206 const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue);
0207 const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue);
0208 constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; }
0209 constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; }
0210 constexpr bool Is64() { return kApiSystemPointerSize == sizeof(int64_t); }
0211 
0212 V8_INLINE static constexpr Address IntToSmi(int value) {
0213   return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) |
0214          kSmiTag;
0215 }
0216 
0217 /*
0218  * Sandbox related types, constants, and functions.
0219  */
0220 constexpr bool SandboxIsEnabled() {
0221 #ifdef V8_ENABLE_SANDBOX
0222   return true;
0223 #else
0224   return false;
0225 #endif
0226 }
0227 
0228 // SandboxedPointers are guaranteed to point into the sandbox. This is achieved
0229 // for example by storing them as offset rather than as raw pointers.
0230 using SandboxedPointer_t = Address;
0231 
0232 #ifdef V8_ENABLE_SANDBOX
0233 
0234 // Size of the sandbox, excluding the guard regions surrounding it.
0235 #if defined(V8_TARGET_OS_ANDROID)
0236 // On Android, most 64-bit devices seem to be configured with only 39 bits of
0237 // virtual address space for userspace. As such, limit the sandbox to 128GB (a
0238 // quarter of the total available address space).
0239 constexpr size_t kSandboxSizeLog2 = 37;  // 128 GB
0240 #else
0241 // Everywhere else use a 1TB sandbox.
0242 constexpr size_t kSandboxSizeLog2 = 40;  // 1 TB
0243 #endif  // V8_TARGET_OS_ANDROID
0244 constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2;
0245 
0246 // Required alignment of the sandbox. For simplicity, we require the
0247 // size of the guard regions to be a multiple of this, so that this specifies
0248 // the alignment of the sandbox including and excluding surrounding guard
0249 // regions. The alignment requirement is due to the pointer compression cage
0250 // being located at the start of the sandbox.
0251 constexpr size_t kSandboxAlignment = kPtrComprCageBaseAlignment;
0252 
0253 // Sandboxed pointers are stored inside the heap as offset from the sandbox
0254 // base shifted to the left. This way, it is guaranteed that the offset is
0255 // smaller than the sandbox size after shifting it to the right again. This
0256 // constant specifies the shift amount.
0257 constexpr uint64_t kSandboxedPointerShift = 64 - kSandboxSizeLog2;
0258 
0259 // Size of the guard regions surrounding the sandbox. This assumes a worst-case
0260 // scenario of a 32-bit unsigned index used to access an array of 64-bit values
0261 // with an additional 4GB (compressed pointer) offset. In particular, accesses
0262 // to TypedArrays are effectively computed as
0263 // `entry_pointer = array->base + array->offset + index * array->element_size`.
0264 // See also https://crbug.com/40070746 for more details.
0265 constexpr size_t kSandboxGuardRegionSize = 32ULL * GB + 4ULL * GB;
0266 
0267 static_assert((kSandboxGuardRegionSize % kSandboxAlignment) == 0,
0268               "The size of the guard regions around the sandbox must be a "
0269               "multiple of its required alignment.");
0270 
0271 // On OSes where reserving virtual memory is too expensive to reserve the
0272 // entire address space backing the sandbox, notably Windows pre 8.1, we create
0273 // a partially reserved sandbox that doesn't actually reserve most of the
0274 // memory, and so doesn't have the desired security properties as unrelated
0275 // memory allocations could end up inside of it, but which still ensures that
0276 // objects that should be located inside the sandbox are allocated within
0277 // kSandboxSize bytes from the start of the sandbox. The minimum size of the
0278 // region that is actually reserved for such a sandbox is specified by this
0279 // constant and should be big enough to contain the pointer compression cage as
0280 // well as the ArrayBuffer partition.
0281 constexpr size_t kSandboxMinimumReservationSize = 8ULL * GB;
0282 
0283 static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize,
0284               "The minimum reservation size for a sandbox must be larger than "
0285               "the pointer compression cage contained within it.");
0286 
0287 // The maximum buffer size allowed inside the sandbox. This is mostly dependent
0288 // on the size of the guard regions around the sandbox: an attacker must not be
0289 // able to construct a buffer that appears larger than the guard regions and
0290 // thereby "reach out of" the sandbox.
0291 constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1;
0292 static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize,
0293               "The maximum allowed buffer size must not be larger than the "
0294               "sandbox's guard regions");
0295 
0296 constexpr size_t kBoundedSizeShift = 29;
0297 static_assert(1ULL << (64 - kBoundedSizeShift) ==
0298                   kMaxSafeBufferSizeForSandbox + 1,
0299               "The maximum size of a BoundedSize must be synchronized with the "
0300               "kMaxSafeBufferSizeForSandbox");
0301 
0302 #endif  // V8_ENABLE_SANDBOX
0303 
0304 #ifdef V8_COMPRESS_POINTERS
0305 
0306 #ifdef V8_TARGET_OS_ANDROID
0307 // The size of the virtual memory reservation for an external pointer table.
0308 // This determines the maximum number of entries in a table. Using a maximum
0309 // size allows omitting bounds checks on table accesses if the indices are
0310 // guaranteed (e.g. through shifting) to be below the maximum index. This
0311 // value must be a power of two.
0312 constexpr size_t kExternalPointerTableReservationSize = 256 * MB;
0313 
0314 // The external pointer table indices stored in HeapObjects as external
0315 // pointers are shifted to the left by this amount to guarantee that they are
0316 // smaller than the maximum table size even after the C++ compiler multiplies
0317 // them by 8 to be used as indexes into a table of 64 bit pointers.
0318 constexpr uint32_t kExternalPointerIndexShift = 7;
0319 #else
0320 constexpr size_t kExternalPointerTableReservationSize = 512 * MB;
0321 constexpr uint32_t kExternalPointerIndexShift = 6;
0322 #endif  // V8_TARGET_OS_ANDROID
0323 
0324 // The maximum number of entries in an external pointer table.
0325 constexpr int kExternalPointerTableEntrySize = 8;
0326 constexpr int kExternalPointerTableEntrySizeLog2 = 3;
0327 constexpr size_t kMaxExternalPointers =
0328     kExternalPointerTableReservationSize / kExternalPointerTableEntrySize;
0329 static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers,
0330               "kExternalPointerTableReservationSize and "
0331               "kExternalPointerIndexShift don't match");
0332 
0333 #else  // !V8_COMPRESS_POINTERS
0334 
0335 // Needed for the V8.SandboxedExternalPointersCount histogram.
0336 constexpr size_t kMaxExternalPointers = 0;
0337 
0338 #endif  // V8_COMPRESS_POINTERS
0339 
0340 constexpr uint64_t kExternalPointerMarkBit = 1ULL << 48;
0341 constexpr uint64_t kExternalPointerTagShift = 49;
0342 constexpr uint64_t kExternalPointerTagMask = 0x00fe000000000000ULL;
0343 constexpr uint64_t kExternalPointerShiftedTagMask =
0344     kExternalPointerTagMask >> kExternalPointerTagShift;
0345 static_assert(kExternalPointerShiftedTagMask << kExternalPointerTagShift ==
0346               kExternalPointerTagMask);
0347 constexpr uint64_t kExternalPointerTagAndMarkbitMask = 0x00ff000000000000ULL;
0348 constexpr uint64_t kExternalPointerPayloadMask = 0xff00ffffffffffffULL;
0349 
0350 // A ExternalPointerHandle represents a (opaque) reference to an external
0351 // pointer that can be stored inside the sandbox. A ExternalPointerHandle has
0352 // meaning only in combination with an (active) Isolate as it references an
0353 // external pointer stored in the currently active Isolate's
0354 // ExternalPointerTable. Internally, an ExternalPointerHandles is simply an
0355 // index into an ExternalPointerTable that is shifted to the left to guarantee
0356 // that it is smaller than the size of the table.
0357 using ExternalPointerHandle = uint32_t;
0358 
0359 // ExternalPointers point to objects located outside the sandbox. When the V8
0360 // sandbox is enabled, these are stored on heap as ExternalPointerHandles,
0361 // otherwise they are simply raw pointers.
0362 #ifdef V8_ENABLE_SANDBOX
0363 using ExternalPointer_t = ExternalPointerHandle;
0364 #else
0365 using ExternalPointer_t = Address;
0366 #endif
0367 
0368 constexpr ExternalPointer_t kNullExternalPointer = 0;
0369 constexpr ExternalPointerHandle kNullExternalPointerHandle = 0;
0370 
0371 // See `ExternalPointerHandle` for the main documentation. The difference to
0372 // `ExternalPointerHandle` is that the handle does not represent an arbitrary
0373 // external pointer but always refers to an object managed by `CppHeap`. The
0374 // handles are using in combination with a dedicated table for `CppHeap`
0375 // references.
0376 using CppHeapPointerHandle = uint32_t;
0377 
0378 // The actual pointer to objects located on the `CppHeap`. When pointer
0379 // compression is enabled these pointers are stored as `CppHeapPointerHandle`.
0380 // In non-compressed configurations the pointers are simply stored as raw
0381 // pointers.
0382 #ifdef V8_COMPRESS_POINTERS
0383 using CppHeapPointer_t = CppHeapPointerHandle;
0384 #else
0385 using CppHeapPointer_t = Address;
0386 #endif
0387 
0388 constexpr CppHeapPointer_t kNullCppHeapPointer = 0;
0389 constexpr CppHeapPointerHandle kNullCppHeapPointerHandle = 0;
0390 
0391 constexpr uint64_t kCppHeapPointerMarkBit = 1ULL;
0392 constexpr uint64_t kCppHeapPointerTagShift = 1;
0393 constexpr uint64_t kCppHeapPointerPayloadShift = 16;
0394 
0395 #ifdef V8_COMPRESS_POINTERS
0396 // CppHeapPointers use a dedicated pointer table. These constants control the
0397 // size and layout of the table. See the corresponding constants for the
0398 // external pointer table for further details.
0399 constexpr size_t kCppHeapPointerTableReservationSize =
0400     kExternalPointerTableReservationSize;
0401 constexpr uint32_t kCppHeapPointerIndexShift = kExternalPointerIndexShift;
0402 
0403 constexpr int kCppHeapPointerTableEntrySize = 8;
0404 constexpr int kCppHeapPointerTableEntrySizeLog2 = 3;
0405 constexpr size_t kMaxCppHeapPointers =
0406     kCppHeapPointerTableReservationSize / kCppHeapPointerTableEntrySize;
0407 static_assert((1 << (32 - kCppHeapPointerIndexShift)) == kMaxCppHeapPointers,
0408               "kCppHeapPointerTableReservationSize and "
0409               "kCppHeapPointerIndexShift don't match");
0410 
0411 #else  // !V8_COMPRESS_POINTERS
0412 
0413 // Needed for the V8.SandboxedCppHeapPointersCount histogram.
0414 constexpr size_t kMaxCppHeapPointers = 0;
0415 
0416 #endif  // V8_COMPRESS_POINTERS
0417 
0418 // Generic tag range struct to represent ranges of type tags.
0419 //
0420 // When referencing external objects via pointer tables, type tags are
0421 // frequently necessary to guarantee type safety for the external objects. When
0422 // support for subtyping is necessary, range-based type checks are used in
0423 // which all subtypes of a given supertype use contiguous tags. This struct can
0424 // then be used to represent such a type range.
0425 //
0426 // In addition, there is an option for performance tweaks: if the size of the
0427 // type range corresponding to a supertype is a power of two and starts at a
0428 // power of two (e.g. [0x100, 0x13f]), then the compiler can often optimize
0429 // the type check to use even fewer instructions (essentially replace a AND +
0430 // SUB with a single AND).
0431 //
0432 template <typename Tag>
0433 struct TagRange {
0434   static_assert(std::is_enum_v<Tag> &&
0435                     std::is_same_v<std::underlying_type_t<Tag>, uint16_t>,
0436                 "Tag parameter must be an enum with base type uint16_t");
0437 
0438   // Construct the inclusive tag range [first, last].
0439   constexpr TagRange(Tag first, Tag last) : first(first), last(last) {}
0440 
0441   // Construct a tag range consisting of a single tag.
0442   //
0443   // A single tag is always implicitly convertible to a tag range. This greatly
0444   // increases readability as most of the time, the exact tag of a field is
0445   // known and so no tag range needs to explicitly be created for it.
0446   constexpr TagRange(Tag tag)  // NOLINT(runtime/explicit)
0447       : first(tag), last(tag) {}
0448 
0449   // Construct an empty tag range.
0450   constexpr TagRange() : TagRange(static_cast<Tag>(0)) {}
0451 
0452   // A tag range is considered empty if it only contains the null tag.
0453   constexpr bool IsEmpty() const { return first == 0 && last == 0; }
0454 
0455   constexpr size_t Size() const {
0456     if (IsEmpty()) {
0457       return 0;
0458     } else {
0459       return last - first + 1;
0460     }
0461   }
0462 
0463   constexpr bool Contains(Tag tag) const {
0464     // Need to perform the math with uint32_t. Otherwise, the uint16_ts would
0465     // be promoted to (signed) int, allowing the compiler to (wrongly) assume
0466     // that an underflow cannot happen as that would be undefined behavior.
0467     return static_cast<uint32_t>(tag) - first <=
0468            static_cast<uint32_t>(last) - first;
0469   }
0470 
0471   constexpr bool Contains(TagRange tag_range) const {
0472     return tag_range.first >= first && tag_range.last <= last;
0473   }
0474 
0475   constexpr bool operator==(const TagRange other) const {
0476     return first == other.first && last == other.last;
0477   }
0478 
0479   constexpr size_t hash_value() const {
0480     static_assert(std::is_same_v<std::underlying_type_t<Tag>, uint16_t>);
0481     return (static_cast<size_t>(first) << 16) | last;
0482   }
0483 
0484   // Internally we represent tag ranges as half-open ranges [first, last).
0485   const Tag first;
0486   const Tag last;
0487 };
0488 
0489 //
0490 // External Pointers.
0491 //
0492 // When the sandbox is enabled, external pointers are stored in an external
0493 // pointer table and are referenced from HeapObjects through an index (a
0494 // "handle"). When stored in the table, the pointers are tagged with per-type
0495 // tags to prevent type confusion attacks between different external objects.
0496 //
0497 // When loading an external pointer, a range of allowed tags can be specified.
0498 // This way, type hierarchies can be supported. The main requirement for that
0499 // is that all (transitive) child classes of a given parent class have type ids
0500 // in the same range, and that there are no unrelated types in that range. For
0501 // more details about how to assign type tags to types, see the TagRange class.
0502 //
0503 // The external pointer sandboxing mechanism ensures that every access to an
0504 // external pointer field will result in a valid pointer of the expected type
0505 // even in the presence of an attacker able to corrupt memory inside the
0506 // sandbox. However, if any data related to the external object is stored
0507 // inside the sandbox it may still be corrupted and so must be validated before
0508 // use or moved into the external object. Further, an attacker will always be
0509 // able to substitute different external pointers of the same type for each
0510 // other. Therefore, code using external pointers must be written in a
0511 // "substitution-safe" way, i.e. it must always be possible to substitute
0512 // external pointers of the same type without causing memory corruption outside
0513 // of the sandbox. Generally this is achieved by referencing any group of
0514 // related external objects through a single external pointer.
0515 //
0516 // Currently we use bit 62 for the marking bit which should always be unused as
0517 // it's part of the non-canonical address range. When Arm's top-byte ignore
0518 // (TBI) is enabled, this bit will be part of the ignored byte, and we assume
0519 // that the Embedder is not using this byte (really only this one bit) for any
0520 // other purpose. This bit also does not collide with the memory tagging
0521 // extension (MTE) which would use bits [56, 60).
0522 //
0523 // External pointer tables are also available even when the sandbox is off but
0524 // pointer compression is on. In that case, the mechanism can be used to ease
0525 // alignment requirements as it turns unaligned 64-bit raw pointers into
0526 // aligned 32-bit indices. To "opt-in" to the external pointer table mechanism
0527 // for this purpose, instead of using the ExternalPointer accessors one needs to
0528 // use ExternalPointerHandles directly and use them to access the pointers in an
0529 // ExternalPointerTable.
0530 //
0531 // The tag is currently in practice limited to 15 bits since it needs to fit
0532 // together with a marking bit into the unused parts of a pointer.
0533 enum ExternalPointerTag : uint16_t {
0534   kFirstExternalPointerTag = 0,
0535   kExternalPointerNullTag = 0,
0536 
0537   // When adding new tags, please ensure that the code using these tags is
0538   // "substitution-safe", i.e. still operate safely if external pointers of the
0539   // same type are swapped by an attacker. See comment above for more details.
0540 
0541   // Shared external pointers are owned by the shared Isolate and stored in the
0542   // shared external pointer table associated with that Isolate, where they can
0543   // be accessed from multiple threads at the same time. The objects referenced
0544   // in this way must therefore always be thread-safe.
0545   kFirstSharedExternalPointerTag,
0546   kWaiterQueueNodeTag = kFirstSharedExternalPointerTag,
0547   kExternalStringResourceTag,
0548   kExternalStringResourceDataTag,
0549   kLastSharedExternalPointerTag = kExternalStringResourceDataTag,
0550 
0551   // External pointers using these tags are kept in a per-Isolate external
0552   // pointer table and can only be accessed when this Isolate is active.
0553   kNativeContextMicrotaskQueueTag,
0554   kEmbedderDataSlotPayloadTag,
0555   // This tag essentially stands for a `void*` pointer in the V8 API, and it is
0556   // the Embedder's responsibility to ensure type safety (against substitution)
0557   // and lifetime validity of these objects.
0558   kExternalObjectValueTag,
0559   kFirstMaybeReadOnlyExternalPointerTag,
0560   kFunctionTemplateInfoCallbackTag = kFirstMaybeReadOnlyExternalPointerTag,
0561   kAccessorInfoGetterTag,
0562   kAccessorInfoSetterTag,
0563   kLastMaybeReadOnlyExternalPointerTag = kAccessorInfoSetterTag,
0564   kWasmInternalFunctionCallTargetTag,
0565   kWasmTypeInfoNativeTypeTag,
0566   kWasmExportedFunctionDataSignatureTag,
0567   kWasmStackMemoryTag,
0568   kWasmIndirectFunctionTargetTag,
0569 
0570   // Foreigns
0571   kFirstForeignExternalPointerTag,
0572   kGenericForeignTag = kFirstForeignExternalPointerTag,
0573   kApiNamedPropertyQueryCallbackTag,
0574   kApiNamedPropertyGetterCallbackTag,
0575   kApiNamedPropertySetterCallbackTag,
0576   kApiNamedPropertyDescriptorCallbackTag,
0577   kApiNamedPropertyDefinerCallbackTag,
0578   kApiNamedPropertyDeleterCallbackTag,
0579   kApiIndexedPropertyQueryCallbackTag,
0580   kApiIndexedPropertyGetterCallbackTag,
0581   kApiIndexedPropertySetterCallbackTag,
0582   kApiIndexedPropertyDescriptorCallbackTag,
0583   kApiIndexedPropertyDefinerCallbackTag,
0584   kApiIndexedPropertyDeleterCallbackTag,
0585   kApiIndexedPropertyEnumeratorCallbackTag,
0586   kApiAccessCheckCallbackTag,
0587   kApiAbortScriptExecutionCallbackTag,
0588   kSyntheticModuleTag,
0589   kMicrotaskCallbackTag,
0590   kMicrotaskCallbackDataTag,
0591   kCFunctionTag,
0592   kCFunctionInfoTag,
0593   kMessageListenerTag,
0594   kWaiterQueueForeignTag,
0595 
0596   // Managed
0597   kFirstManagedResourceTag,
0598   kFirstManagedExternalPointerTag = kFirstManagedResourceTag,
0599   kGenericManagedTag = kFirstManagedExternalPointerTag,
0600   kWasmWasmStreamingTag,
0601   kWasmFuncDataTag,
0602   kWasmManagedDataTag,
0603   kWasmNativeModuleTag,
0604   kIcuBreakIteratorTag,
0605   kIcuUnicodeStringTag,
0606   kIcuListFormatterTag,
0607   kIcuLocaleTag,
0608   kIcuSimpleDateFormatTag,
0609   kIcuDateIntervalFormatTag,
0610   kIcuRelativeDateTimeFormatterTag,
0611   kIcuLocalizedNumberFormatterTag,
0612   kIcuPluralRulesTag,
0613   kIcuCollatorTag,
0614   kDisplayNamesInternalTag,
0615   kD8WorkerTag,
0616   kD8ModuleEmbedderDataTag,
0617   kLastForeignExternalPointerTag = kD8ModuleEmbedderDataTag,
0618   kLastManagedExternalPointerTag = kLastForeignExternalPointerTag,
0619   // External resources whose lifetime is tied to their entry in the external
0620   // pointer table but which are not referenced via a Managed
0621   kArrayBufferExtensionTag,
0622   kLastManagedResourceTag = kArrayBufferExtensionTag,
0623 
0624   kExternalPointerZappedEntryTag = 0x7d,
0625   kExternalPointerEvacuationEntryTag = 0x7e,
0626   kExternalPointerFreeEntryTag = 0x7f,
0627   // The tags are limited to 7 bits, so the last tag is 0x7f.
0628   kLastExternalPointerTag = 0x7f,
0629 };
0630 
0631 using ExternalPointerTagRange = TagRange<ExternalPointerTag>;
0632 
0633 constexpr ExternalPointerTagRange kAnyExternalPointerTagRange(
0634     kFirstExternalPointerTag, kLastExternalPointerTag);
0635 constexpr ExternalPointerTagRange kAnySharedExternalPointerTagRange(
0636     kFirstSharedExternalPointerTag, kLastSharedExternalPointerTag);
0637 constexpr ExternalPointerTagRange kAnyForeignExternalPointerTagRange(
0638     kFirstForeignExternalPointerTag, kLastForeignExternalPointerTag);
0639 constexpr ExternalPointerTagRange kAnyManagedExternalPointerTagRange(
0640     kFirstManagedExternalPointerTag, kLastManagedExternalPointerTag);
0641 constexpr ExternalPointerTagRange kAnyMaybeReadOnlyExternalPointerTagRange(
0642     kFirstMaybeReadOnlyExternalPointerTag,
0643     kLastMaybeReadOnlyExternalPointerTag);
0644 constexpr ExternalPointerTagRange kAnyManagedResourceExternalPointerTag(
0645     kFirstManagedResourceTag, kLastManagedResourceTag);
0646 
0647 // True if the external pointer must be accessed from the shared isolate's
0648 // external pointer table.
0649 V8_INLINE static constexpr bool IsSharedExternalPointerType(
0650     ExternalPointerTagRange tag_range) {
0651   return kAnySharedExternalPointerTagRange.Contains(tag_range);
0652 }
0653 
0654 // True if the external pointer may live in a read-only object, in which case
0655 // the table entry will be in the shared read-only segment of the external
0656 // pointer table.
0657 V8_INLINE static constexpr bool IsMaybeReadOnlyExternalPointerType(
0658     ExternalPointerTagRange tag_range) {
0659   return kAnyMaybeReadOnlyExternalPointerTagRange.Contains(tag_range);
0660 }
0661 
0662 // True if the external pointer references an external object whose lifetime is
0663 // tied to the entry in the external pointer table.
0664 // In this case, the entry in the ExternalPointerTable always points to an
0665 // object derived from ExternalPointerTable::ManagedResource.
0666 V8_INLINE static constexpr bool IsManagedExternalPointerType(
0667     ExternalPointerTagRange tag_range) {
0668   return kAnyManagedResourceExternalPointerTag.Contains(tag_range);
0669 }
0670 
0671 // When an external poiner field can contain the null external pointer handle,
0672 // the type checking mechanism needs to also check for null.
0673 // TODO(saelo): this is mostly a temporary workaround to introduce range-based
0674 // type checks. In the future, we should either (a) change the type tagging
0675 // scheme so that null always passes or (b) (more likely) introduce dedicated
0676 // null entries for those tags that need them (similar to other well-known
0677 // empty value constants such as the empty fixed array).
0678 V8_INLINE static constexpr bool ExternalPointerCanBeEmpty(
0679     ExternalPointerTagRange tag_range) {
0680   return tag_range.Contains(kArrayBufferExtensionTag) ||
0681          tag_range.Contains(kEmbedderDataSlotPayloadTag);
0682 }
0683 
0684 // Indirect Pointers.
0685 //
0686 // When the sandbox is enabled, indirect pointers are used to reference
0687 // HeapObjects that live outside of the sandbox (but are still managed by V8's
0688 // garbage collector). When object A references an object B through an indirect
0689 // pointer, object A will contain a IndirectPointerHandle, i.e. a shifted
0690 // 32-bit index, which identifies an entry in a pointer table (either the
0691 // trusted pointer table for TrustedObjects, or the code pointer table if it is
0692 // a Code object). This table entry then contains the actual pointer to object
0693 // B. Further, object B owns this pointer table entry, and it is responsible
0694 // for updating the "self-pointer" in the entry when it is relocated in memory.
0695 // This way, in contrast to "normal" pointers, indirect pointers never need to
0696 // be tracked by the GC (i.e. there is no remembered set for them).
0697 // These pointers do not exist when the sandbox is disabled.
0698 
0699 // An IndirectPointerHandle represents a 32-bit index into a pointer table.
0700 using IndirectPointerHandle = uint32_t;
0701 
0702 // A null handle always references an entry that contains nullptr.
0703 constexpr IndirectPointerHandle kNullIndirectPointerHandle = 0;
0704 
0705 // When the sandbox is enabled, indirect pointers are used to implement:
0706 // - TrustedPointers: an indirect pointer using the trusted pointer table (TPT)
0707 //   and referencing a TrustedObject in one of the trusted heap spaces.
0708 // - CodePointers, an indirect pointer using the code pointer table (CPT) and
0709 //   referencing a Code object together with its instruction stream.
0710 
0711 //
0712 // Trusted Pointers.
0713 //
0714 // A pointer to a TrustedObject.
0715 // When the sandbox is enabled, these are indirect pointers using the trusted
0716 // pointer table (TPT). They are used to reference trusted objects (located in
0717 // one of V8's trusted heap spaces, outside of the sandbox) from inside the
0718 // sandbox in a memory-safe way. When the sandbox is disabled, these are
0719 // regular tagged pointers.
0720 using TrustedPointerHandle = IndirectPointerHandle;
0721 
0722 // The size of the virtual memory reservation for the trusted pointer table.
0723 // As with the external pointer table, a maximum table size in combination with
0724 // shifted indices allows omitting bounds checks.
0725 constexpr size_t kTrustedPointerTableReservationSize = 64 * MB;
0726 
0727 // The trusted pointer handles are stored shifted to the left by this amount
0728 // to guarantee that they are smaller than the maximum table size.
0729 constexpr uint32_t kTrustedPointerHandleShift = 9;
0730 
0731 // A null handle always references an entry that contains nullptr.
0732 constexpr TrustedPointerHandle kNullTrustedPointerHandle =
0733     kNullIndirectPointerHandle;
0734 
0735 // The maximum number of entries in an trusted pointer table.
0736 constexpr int kTrustedPointerTableEntrySize = 8;
0737 constexpr int kTrustedPointerTableEntrySizeLog2 = 3;
0738 constexpr size_t kMaxTrustedPointers =
0739     kTrustedPointerTableReservationSize / kTrustedPointerTableEntrySize;
0740 static_assert((1 << (32 - kTrustedPointerHandleShift)) == kMaxTrustedPointers,
0741               "kTrustedPointerTableReservationSize and "
0742               "kTrustedPointerHandleShift don't match");
0743 
0744 //
0745 // Code Pointers.
0746 //
0747 // A pointer to a Code object.
0748 // Essentially a specialized version of a trusted pointer that (when the
0749 // sandbox is enabled) uses the code pointer table (CPT) instead of the TPT.
0750 // Each entry in the CPT contains both a pointer to a Code object as well as a
0751 // pointer to the Code's entrypoint. This allows calling/jumping into Code with
0752 // one fewer memory access (compared to the case where the entrypoint pointer
0753 // first needs to be loaded from the Code object). As such, a CodePointerHandle
0754 // can be used both to obtain the referenced Code object and to directly load
0755 // its entrypoint.
0756 //
0757 // When the sandbox is disabled, these are regular tagged pointers.
0758 using CodePointerHandle = IndirectPointerHandle;
0759 
0760 // The size of the virtual memory reservation for the code pointer table.
0761 // As with the other tables, a maximum table size in combination with shifted
0762 // indices allows omitting bounds checks.
0763 constexpr size_t kCodePointerTableReservationSize = 128 * MB;
0764 
0765 // Code pointer handles are shifted by a different amount than indirect pointer
0766 // handles as the tables have a different maximum size.
0767 constexpr uint32_t kCodePointerHandleShift = 9;
0768 
0769 // A null handle always references an entry that contains nullptr.
0770 constexpr CodePointerHandle kNullCodePointerHandle = kNullIndirectPointerHandle;
0771 
0772 // It can sometimes be necessary to distinguish a code pointer handle from a
0773 // trusted pointer handle. A typical example would be a union trusted pointer
0774 // field that can refer to both Code objects and other trusted objects. To
0775 // support these use-cases, we use a simple marking scheme where some of the
0776 // low bits of a code pointer handle are set, while they will be unset on a
0777 // trusted pointer handle. This way, the correct table to resolve the handle
0778 // can be determined even in the absence of a type tag.
0779 constexpr uint32_t kCodePointerHandleMarker = 0x1;
0780 static_assert(kCodePointerHandleShift > 0);
0781 static_assert(kTrustedPointerHandleShift > 0);
0782 
0783 // The maximum number of entries in a code pointer table.
0784 constexpr int kCodePointerTableEntrySize = 16;
0785 constexpr int kCodePointerTableEntrySizeLog2 = 4;
0786 constexpr size_t kMaxCodePointers =
0787     kCodePointerTableReservationSize / kCodePointerTableEntrySize;
0788 static_assert(
0789     (1 << (32 - kCodePointerHandleShift)) == kMaxCodePointers,
0790     "kCodePointerTableReservationSize and kCodePointerHandleShift don't match");
0791 
0792 constexpr int kCodePointerTableEntryEntrypointOffset = 0;
0793 constexpr int kCodePointerTableEntryCodeObjectOffset = 8;
0794 
0795 // Constants that can be used to mark places that should be modified once
0796 // certain types of objects are moved out of the sandbox and into trusted space.
0797 constexpr bool kRuntimeGeneratedCodeObjectsLiveInTrustedSpace = true;
0798 constexpr bool kBuiltinCodeObjectsLiveInTrustedSpace = false;
0799 constexpr bool kAllCodeObjectsLiveInTrustedSpace =
0800     kRuntimeGeneratedCodeObjectsLiveInTrustedSpace &&
0801     kBuiltinCodeObjectsLiveInTrustedSpace;
0802 
0803 // {obj} must be the raw tagged pointer representation of a HeapObject
0804 // that's guaranteed to never be in ReadOnlySpace.
0805 V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj);
0806 
0807 // Returns if we need to throw when an error occurs. This infers the language
0808 // mode based on the current context and the closure. This returns true if the
0809 // language mode is strict.
0810 V8_EXPORT bool ShouldThrowOnError(internal::Isolate* isolate);
0811 /**
0812  * This class exports constants and functionality from within v8 that
0813  * is necessary to implement inline functions in the v8 api.  Don't
0814  * depend on functions and constants defined here.
0815  */
0816 class Internals {
0817 #ifdef V8_MAP_PACKING
0818   V8_INLINE static constexpr Address UnpackMapWord(Address mapword) {
0819     // TODO(wenyuzhao): Clear header metadata.
0820     return mapword ^ kMapWordXorMask;
0821   }
0822 #endif
0823 
0824  public:
0825   // These values match non-compiler-dependent values defined within
0826   // the implementation of v8.
0827   static const int kHeapObjectMapOffset = 0;
0828   static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size;
0829   static const int kStringResourceOffset =
0830       1 * kApiTaggedSize + 2 * kApiInt32Size;
0831 
0832   static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize;
0833   static const int kJSObjectHeaderSize = 3 * kApiTaggedSize;
0834 #ifdef V8_COMPRESS_POINTERS
0835   static const int kJSAPIObjectWithEmbedderSlotsHeaderSize =
0836       kJSObjectHeaderSize + kApiInt32Size;
0837 #else   // !V8_COMPRESS_POINTERS
0838   static const int kJSAPIObjectWithEmbedderSlotsHeaderSize =
0839       kJSObjectHeaderSize + kApiTaggedSize;
0840 #endif  // !V8_COMPRESS_POINTERS
0841   static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize;
0842   static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize;
0843   static const int kEmbedderDataSlotSize = kApiSystemPointerSize;
0844 #ifdef V8_ENABLE_SANDBOX
0845   static const int kEmbedderDataSlotExternalPointerOffset = kApiTaggedSize;
0846 #else
0847   static const int kEmbedderDataSlotExternalPointerOffset = 0;
0848 #endif
0849   static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize;
0850   static const int kStringRepresentationAndEncodingMask = 0x0f;
0851   static const int kStringEncodingMask = 0x8;
0852   static const int kExternalTwoByteRepresentationTag = 0x02;
0853   static const int kExternalOneByteRepresentationTag = 0x0a;
0854 
0855   static const uint32_t kNumIsolateDataSlots = 4;
0856   static const int kStackGuardSize = 8 * kApiSystemPointerSize;
0857   static const int kNumberOfBooleanFlags = 6;
0858   static const int kErrorMessageParamSize = 1;
0859   static const int kTablesAlignmentPaddingSize = 1;
0860   static const int kRegExpStaticResultOffsetsVectorSize = kApiSystemPointerSize;
0861   static const int kBuiltinTier0EntryTableSize = 7 * kApiSystemPointerSize;
0862   static const int kBuiltinTier0TableSize = 7 * kApiSystemPointerSize;
0863   static const int kLinearAllocationAreaSize = 3 * kApiSystemPointerSize;
0864   static const int kThreadLocalTopSize = 30 * kApiSystemPointerSize;
0865   static const int kHandleScopeDataSize =
0866       2 * kApiSystemPointerSize + 2 * kApiInt32Size;
0867 
0868   // ExternalPointerTable and TrustedPointerTable layout guarantees.
0869   static const int kExternalPointerTableBasePointerOffset = 0;
0870   static const int kExternalPointerTableSize = 2 * kApiSystemPointerSize;
0871   static const int kTrustedPointerTableSize = 2 * kApiSystemPointerSize;
0872   static const int kTrustedPointerTableBasePointerOffset = 0;
0873 
0874   // IsolateData layout guarantees.
0875   static const int kIsolateCageBaseOffset = 0;
0876   static const int kIsolateStackGuardOffset =
0877       kIsolateCageBaseOffset + kApiSystemPointerSize;
0878   static const int kVariousBooleanFlagsOffset =
0879       kIsolateStackGuardOffset + kStackGuardSize;
0880   static const int kErrorMessageParamOffset =
0881       kVariousBooleanFlagsOffset + kNumberOfBooleanFlags;
0882   static const int kBuiltinTier0EntryTableOffset =
0883       kErrorMessageParamOffset + kErrorMessageParamSize +
0884       kTablesAlignmentPaddingSize + kRegExpStaticResultOffsetsVectorSize;
0885   static const int kBuiltinTier0TableOffset =
0886       kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize;
0887   static const int kNewAllocationInfoOffset =
0888       kBuiltinTier0TableOffset + kBuiltinTier0TableSize;
0889   static const int kOldAllocationInfoOffset =
0890       kNewAllocationInfoOffset + kLinearAllocationAreaSize;
0891 
0892   static const int kFastCCallAlignmentPaddingSize =
0893       kApiSystemPointerSize == 8 ? 5 * kApiSystemPointerSize
0894                                  : 1 * kApiSystemPointerSize;
0895   static const int kIsolateFastCCallCallerPcOffset =
0896       kOldAllocationInfoOffset + kLinearAllocationAreaSize +
0897       kFastCCallAlignmentPaddingSize;
0898   static const int kIsolateFastCCallCallerFpOffset =
0899       kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize;
0900   static const int kIsolateFastApiCallTargetOffset =
0901       kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize;
0902   static const int kIsolateLongTaskStatsCounterOffset =
0903       kIsolateFastApiCallTargetOffset + kApiSystemPointerSize;
0904   static const int kIsolateThreadLocalTopOffset =
0905       kIsolateLongTaskStatsCounterOffset + kApiSizetSize;
0906   static const int kIsolateHandleScopeDataOffset =
0907       kIsolateThreadLocalTopOffset + kThreadLocalTopSize;
0908   static const int kIsolateEmbedderDataOffset =
0909       kIsolateHandleScopeDataOffset + kHandleScopeDataSize;
0910 #ifdef V8_COMPRESS_POINTERS
0911   static const int kIsolateExternalPointerTableOffset =
0912       kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize;
0913   static const int kIsolateSharedExternalPointerTableAddressOffset =
0914       kIsolateExternalPointerTableOffset + kExternalPointerTableSize;
0915   static const int kIsolateCppHeapPointerTableOffset =
0916       kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize;
0917 #ifdef V8_ENABLE_SANDBOX
0918   static const int kIsolateTrustedCageBaseOffset =
0919       kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize;
0920   static const int kIsolateTrustedPointerTableOffset =
0921       kIsolateTrustedCageBaseOffset + kApiSystemPointerSize;
0922   static const int kIsolateSharedTrustedPointerTableAddressOffset =
0923       kIsolateTrustedPointerTableOffset + kTrustedPointerTableSize;
0924   static const int kIsolateTrustedPointerPublishingScopeOffset =
0925       kIsolateSharedTrustedPointerTableAddressOffset + kApiSystemPointerSize;
0926   static const int kIsolateCodePointerTableBaseAddressOffset =
0927       kIsolateTrustedPointerPublishingScopeOffset + kApiSystemPointerSize;
0928   static const int kIsolateApiCallbackThunkArgumentOffset =
0929       kIsolateCodePointerTableBaseAddressOffset + kApiSystemPointerSize;
0930 #else
0931   static const int kIsolateApiCallbackThunkArgumentOffset =
0932       kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize;
0933 #endif  // V8_ENABLE_SANDBOX
0934 #else
0935   static const int kIsolateApiCallbackThunkArgumentOffset =
0936       kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize;
0937 #endif  // V8_COMPRESS_POINTERS
0938   static const int kIsolateRegexpExecVectorArgumentOffset =
0939       kIsolateApiCallbackThunkArgumentOffset + kApiSystemPointerSize;
0940   static const int kContinuationPreservedEmbedderDataOffset =
0941       kIsolateRegexpExecVectorArgumentOffset + kApiSystemPointerSize;
0942   static const int kIsolateRootsOffset =
0943       kContinuationPreservedEmbedderDataOffset + kApiSystemPointerSize;
0944 
0945   // Assert scopes
0946   static const int kDisallowGarbageCollectionAlign = alignof(uint32_t);
0947   static const int kDisallowGarbageCollectionSize = sizeof(uint32_t);
0948 
0949 #if V8_STATIC_ROOTS_BOOL
0950 
0951 // These constants are copied from static-roots.h and guarded by static asserts.
0952 #define EXPORTED_STATIC_ROOTS_PTR_LIST(V) \
0953   V(UndefinedValue, 0x11)                 \
0954   V(NullValue, 0x2d)                      \
0955   V(TrueValue, 0x71)                      \
0956   V(FalseValue, 0x55)                     \
0957   V(EmptyString, 0x49)                    \
0958   V(TheHoleValue, 0x761)
0959 
0960   using Tagged_t = uint32_t;
0961   struct StaticReadOnlyRoot {
0962 #define DEF_ROOT(name, value) static constexpr Tagged_t k##name = value;
0963     EXPORTED_STATIC_ROOTS_PTR_LIST(DEF_ROOT)
0964 #undef DEF_ROOT
0965 
0966     // Use 0 for kStringMapLowerBound since string maps are the first maps.
0967     static constexpr Tagged_t kStringMapLowerBound = 0;
0968     static constexpr Tagged_t kStringMapUpperBound = 0x425;
0969 
0970 #define PLUSONE(...) +1
0971     static constexpr size_t kNumberOfExportedStaticRoots =
0972         2 + EXPORTED_STATIC_ROOTS_PTR_LIST(PLUSONE);
0973 #undef PLUSONE
0974   };
0975 
0976 #endif  // V8_STATIC_ROOTS_BOOL
0977 
0978   static const int kUndefinedValueRootIndex = 4;
0979   static const int kTheHoleValueRootIndex = 5;
0980   static const int kNullValueRootIndex = 6;
0981   static const int kTrueValueRootIndex = 7;
0982   static const int kFalseValueRootIndex = 8;
0983   static const int kEmptyStringRootIndex = 9;
0984 
0985   static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize;
0986   static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3;
0987   static const int kNodeStateMask = 0x3;
0988   static const int kNodeStateIsWeakValue = 2;
0989 
0990   static const int kFirstNonstringType = 0x80;
0991   static const int kOddballType = 0x83;
0992   static const int kForeignType = 0xcc;
0993   static const int kJSSpecialApiObjectType = 0x410;
0994   static const int kJSObjectType = 0x421;
0995   static const int kFirstJSApiObjectType = 0x422;
0996   static const int kLastJSApiObjectType = 0x80A;
0997   // Defines a range [kFirstEmbedderJSApiObjectType, kJSApiObjectTypesCount]
0998   // of JSApiObject instance type values that an embedder can use.
0999   static const int kFirstEmbedderJSApiObjectType = 0;
1000   static const int kLastEmbedderJSApiObjectType =
1001       kLastJSApiObjectType - kFirstJSApiObjectType;
1002 
1003   static const int kUndefinedOddballKind = 4;
1004   static const int kNullOddballKind = 3;
1005 
1006   // Constants used by PropertyCallbackInfo to check if we should throw when an
1007   // error occurs.
1008   static const int kDontThrow = 0;
1009   static const int kThrowOnError = 1;
1010   static const int kInferShouldThrowMode = 2;
1011 
1012   // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an
1013   // incremental GC once the external memory reaches this limit.
1014   static constexpr size_t kExternalAllocationSoftLimit = 64 * 1024 * 1024;
1015 
1016 #ifdef V8_MAP_PACKING
1017   static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48;
1018   // The lowest two bits of mapwords are always `0b10`
1019   static const uintptr_t kMapWordSignature = 0b10;
1020   // XORing a (non-compressed) map with this mask ensures that the two
1021   // low-order bits are 0b10. The 0 at the end makes this look like a Smi,
1022   // although real Smis have all lower 32 bits unset. We only rely on these
1023   // values passing as Smis in very few places.
1024   static const int kMapWordXorMask = 0b11;
1025 #endif
1026 
1027   V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate);
1028   V8_INLINE static void CheckInitialized(v8::Isolate* isolate) {
1029 #ifdef V8_ENABLE_CHECKS
1030     CheckInitializedImpl(isolate);
1031 #endif
1032   }
1033 
1034   V8_INLINE static constexpr bool HasHeapObjectTag(Address value) {
1035     return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag);
1036   }
1037 
1038   V8_INLINE static constexpr int SmiValue(Address value) {
1039     return PlatformSmiTagging::SmiToInt(value);
1040   }
1041 
1042   V8_INLINE static constexpr Address AddressToSmi(Address value) {
1043     return (value << (kSmiTagSize + PlatformSmiTagging::kSmiShiftSize)) |
1044            kSmiTag;
1045   }
1046 
1047   V8_INLINE static constexpr Address IntToSmi(int value) {
1048     return AddressToSmi(static_cast<Address>(value));
1049   }
1050 
1051   template <typename T,
1052             typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
1053   V8_INLINE static constexpr Address IntegralToSmi(T value) {
1054     return AddressToSmi(static_cast<Address>(value));
1055   }
1056 
1057   template <typename T,
1058             typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
1059   V8_INLINE static constexpr bool IsValidSmi(T value) {
1060     return PlatformSmiTagging::IsValidSmi(value);
1061   }
1062 
1063   template <typename T,
1064             typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
1065   static constexpr std::optional<Address> TryIntegralToSmi(T value) {
1066     if (V8_LIKELY(PlatformSmiTagging::IsValidSmi(value))) {
1067       return {AddressToSmi(static_cast<Address>(value))};
1068     }
1069     return {};
1070   }
1071 
1072 #if V8_STATIC_ROOTS_BOOL
1073   V8_INLINE static bool is_identical(Address obj, Tagged_t constant) {
1074     return static_cast<Tagged_t>(obj) == constant;
1075   }
1076 
1077   V8_INLINE static bool CheckInstanceMapRange(Address obj, Tagged_t first_map,
1078                                               Tagged_t last_map) {
1079     auto map = ReadRawField<Tagged_t>(obj, kHeapObjectMapOffset);
1080 #ifdef V8_MAP_PACKING
1081     map = UnpackMapWord(map);
1082 #endif
1083     return map >= first_map && map <= last_map;
1084   }
1085 #endif
1086 
1087   V8_INLINE static int GetInstanceType(Address obj) {
1088     Address map = ReadTaggedPointerField(obj, kHeapObjectMapOffset);
1089 #ifdef V8_MAP_PACKING
1090     map = UnpackMapWord(map);
1091 #endif
1092     return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset);
1093   }
1094 
1095   V8_INLINE static Address LoadMap(Address obj) {
1096     if (!HasHeapObjectTag(obj)) return kNullAddress;
1097     Address map = ReadTaggedPointerField(obj, kHeapObjectMapOffset);
1098 #ifdef V8_MAP_PACKING
1099     map = UnpackMapWord(map);
1100 #endif
1101     return map;
1102   }
1103 
1104   V8_INLINE static int GetOddballKind(Address obj) {
1105     return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset));
1106   }
1107 
1108   V8_INLINE static bool IsExternalTwoByteString(int instance_type) {
1109     int representation = (instance_type & kStringRepresentationAndEncodingMask);
1110     return representation == kExternalTwoByteRepresentationTag;
1111   }
1112 
1113   V8_INLINE static constexpr bool CanHaveInternalField(int instance_type) {
1114     static_assert(kJSObjectType + 1 == kFirstJSApiObjectType);
1115     static_assert(kJSObjectType < kLastJSApiObjectType);
1116     static_assert(kFirstJSApiObjectType < kLastJSApiObjectType);
1117     // Check for IsJSObject() || IsJSSpecialApiObject() || IsJSApiObject()
1118     return instance_type == kJSSpecialApiObjectType ||
1119            // inlined version of base::IsInRange
1120            (static_cast<unsigned>(static_cast<unsigned>(instance_type) -
1121                                   static_cast<unsigned>(kJSObjectType)) <=
1122             static_cast<unsigned>(kLastJSApiObjectType - kJSObjectType));
1123   }
1124 
1125   V8_INLINE static uint8_t GetNodeFlag(Address* obj, int shift) {
1126     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
1127     return *addr & static_cast<uint8_t>(1U << shift);
1128   }
1129 
1130   V8_INLINE static void UpdateNodeFlag(Address* obj, bool value, int shift) {
1131     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
1132     uint8_t mask = static_cast<uint8_t>(1U << shift);
1133     *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift));
1134   }
1135 
1136   V8_INLINE static uint8_t GetNodeState(Address* obj) {
1137     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
1138     return *addr & kNodeStateMask;
1139   }
1140 
1141   V8_INLINE static void UpdateNodeState(Address* obj, uint8_t value) {
1142     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
1143     *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value);
1144   }
1145 
1146   V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot,
1147                                         void* data) {
1148     Address addr = reinterpret_cast<Address>(isolate) +
1149                    kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize;
1150     *reinterpret_cast<void**>(addr) = data;
1151   }
1152 
1153   V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate,
1154                                          uint32_t slot) {
1155     Address addr = reinterpret_cast<Address>(isolate) +
1156                    kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize;
1157     return *reinterpret_cast<void* const*>(addr);
1158   }
1159 
1160   V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) {
1161     Address addr =
1162         reinterpret_cast<Address>(isolate) + kIsolateLongTaskStatsCounterOffset;
1163     ++(*reinterpret_cast<size_t*>(addr));
1164   }
1165 
1166   V8_INLINE static Address* GetRootSlot(v8::Isolate* isolate, int index) {
1167     Address addr = reinterpret_cast<Address>(isolate) + kIsolateRootsOffset +
1168                    index * kApiSystemPointerSize;
1169     return reinterpret_cast<Address*>(addr);
1170   }
1171 
1172   V8_INLINE static Address GetRoot(v8::Isolate* isolate, int index) {
1173 #if V8_STATIC_ROOTS_BOOL
1174     Address base = *reinterpret_cast<Address*>(
1175         reinterpret_cast<uintptr_t>(isolate) + kIsolateCageBaseOffset);
1176     switch (index) {
1177 #define DECOMPRESS_ROOT(name, ...) \
1178   case k##name##RootIndex:         \
1179     return base + StaticReadOnlyRoot::k##name;
1180       EXPORTED_STATIC_ROOTS_PTR_LIST(DECOMPRESS_ROOT)
1181 #undef DECOMPRESS_ROOT
1182 #undef EXPORTED_STATIC_ROOTS_PTR_LIST
1183       default:
1184         break;
1185     }
1186 #endif  // V8_STATIC_ROOTS_BOOL
1187     return *GetRootSlot(isolate, index);
1188   }
1189 
1190 #ifdef V8_ENABLE_SANDBOX
1191   V8_INLINE static Address* GetExternalPointerTableBase(v8::Isolate* isolate) {
1192     Address addr = reinterpret_cast<Address>(isolate) +
1193                    kIsolateExternalPointerTableOffset +
1194                    kExternalPointerTableBasePointerOffset;
1195     return *reinterpret_cast<Address**>(addr);
1196   }
1197 
1198   V8_INLINE static Address* GetSharedExternalPointerTableBase(
1199       v8::Isolate* isolate) {
1200     Address addr = reinterpret_cast<Address>(isolate) +
1201                    kIsolateSharedExternalPointerTableAddressOffset;
1202     addr = *reinterpret_cast<Address*>(addr);
1203     addr += kExternalPointerTableBasePointerOffset;
1204     return *reinterpret_cast<Address**>(addr);
1205   }
1206 #endif
1207 
1208   template <typename T>
1209   V8_INLINE static T ReadRawField(Address heap_object_ptr, int offset) {
1210     Address addr = heap_object_ptr + offset - kHeapObjectTag;
1211 #ifdef V8_COMPRESS_POINTERS
1212     if (sizeof(T) > kApiTaggedSize) {
1213       // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
1214       // fields (external pointers, doubles and BigInt data) are only
1215       // kTaggedSize aligned so we have to use unaligned pointer friendly way of
1216       // accessing them in order to avoid undefined behavior in C++ code.
1217       T r;
1218       memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T));
1219       return r;
1220     }
1221 #endif
1222     return *reinterpret_cast<const T*>(addr);
1223   }
1224 
1225   V8_INLINE static Address ReadTaggedPointerField(Address heap_object_ptr,
1226                                                   int offset) {
1227 #ifdef V8_COMPRESS_POINTERS
1228     uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
1229     Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr);
1230     return base + static_cast<Address>(static_cast<uintptr_t>(value));
1231 #else
1232     return ReadRawField<Address>(heap_object_ptr, offset);
1233 #endif
1234   }
1235 
1236   V8_INLINE static Address ReadTaggedSignedField(Address heap_object_ptr,
1237                                                  int offset) {
1238 #ifdef V8_COMPRESS_POINTERS
1239     uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
1240     return static_cast<Address>(static_cast<uintptr_t>(value));
1241 #else
1242     return ReadRawField<Address>(heap_object_ptr, offset);
1243 #endif
1244   }
1245 
1246   V8_INLINE static v8::Isolate* GetIsolateForSandbox(Address obj) {
1247 #ifdef V8_ENABLE_SANDBOX
1248     return reinterpret_cast<v8::Isolate*>(
1249         internal::IsolateFromNeverReadOnlySpaceObject(obj));
1250 #else
1251     // Not used in non-sandbox mode.
1252     return nullptr;
1253 #endif
1254   }
1255 
1256   template <ExternalPointerTagRange tag_range>
1257   V8_INLINE static Address ReadExternalPointerField(v8::Isolate* isolate,
1258                                                     Address heap_object_ptr,
1259                                                     int offset) {
1260 #ifdef V8_ENABLE_SANDBOX
1261     static_assert(!tag_range.IsEmpty());
1262     // See src/sandbox/external-pointer-table.h. Logic duplicated here so
1263     // it can be inlined and doesn't require an additional call.
1264     Address* table = IsSharedExternalPointerType(tag_range)
1265                          ? GetSharedExternalPointerTableBase(isolate)
1266                          : GetExternalPointerTableBase(isolate);
1267     internal::ExternalPointerHandle handle =
1268         ReadRawField<ExternalPointerHandle>(heap_object_ptr, offset);
1269     uint32_t index = handle >> kExternalPointerIndexShift;
1270     std::atomic<Address>* ptr =
1271         reinterpret_cast<std::atomic<Address>*>(&table[index]);
1272     Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed);
1273     ExternalPointerTag actual_tag = static_cast<ExternalPointerTag>(
1274         (entry & kExternalPointerTagMask) >> kExternalPointerTagShift);
1275     if (V8_LIKELY(tag_range.Contains(actual_tag))) {
1276       return entry & kExternalPointerPayloadMask;
1277     } else {
1278       return 0;
1279     }
1280     return entry;
1281 #else
1282     return ReadRawField<Address>(heap_object_ptr, offset);
1283 #endif  // V8_ENABLE_SANDBOX
1284   }
1285 
1286 #ifdef V8_COMPRESS_POINTERS
1287   V8_INLINE static Address GetPtrComprCageBaseFromOnHeapAddress(Address addr) {
1288     return addr & -static_cast<intptr_t>(kPtrComprCageBaseAlignment);
1289   }
1290 
1291   V8_INLINE static uint32_t CompressTagged(Address value) {
1292     return static_cast<uint32_t>(value);
1293   }
1294 
1295   V8_INLINE static Address DecompressTaggedField(Address heap_object_ptr,
1296                                                  uint32_t value) {
1297     Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr);
1298     return base + static_cast<Address>(static_cast<uintptr_t>(value));
1299   }
1300 
1301 #endif  // V8_COMPRESS_POINTERS
1302 };
1303 
1304 // Only perform cast check for types derived from v8::Data since
1305 // other types do not implement the Cast method.
1306 template <bool PerformCheck>
1307 struct CastCheck {
1308   template <class T>
1309   static void Perform(T* data);
1310 };
1311 
1312 template <>
1313 template <class T>
1314 void CastCheck<true>::Perform(T* data) {
1315   T::Cast(data);
1316 }
1317 
1318 template <>
1319 template <class T>
1320 void CastCheck<false>::Perform(T* data) {}
1321 
1322 template <class T>
1323 V8_INLINE void PerformCastCheck(T* data) {
1324   CastCheck<std::is_base_of<Data, T>::value &&
1325             !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
1326 }
1327 
1328 // A base class for backing stores, which is needed due to vagaries of
1329 // how static casts work with std::shared_ptr.
1330 class BackingStoreBase {};
1331 
1332 // The maximum value in enum GarbageCollectionReason, defined in heap.h.
1333 // This is needed for histograms sampling garbage collection reasons.
1334 constexpr int kGarbageCollectionReasonMaxValue = 29;
1335 
1336 // Base class for the address block allocator compatible with standard
1337 // containers, which registers its allocated range as strong roots.
1338 class V8_EXPORT StrongRootAllocatorBase {
1339  public:
1340   Heap* heap() const { return heap_; }
1341 
1342   friend bool operator==(const StrongRootAllocatorBase& a,
1343                          const StrongRootAllocatorBase& b) {
1344     // TODO(pkasting): Replace this body with `= default` after dropping support
1345     // for old gcc versions.
1346     return a.heap_ == b.heap_;
1347   }
1348 
1349  protected:
1350   explicit StrongRootAllocatorBase(Heap* heap) : heap_(heap) {}
1351   explicit StrongRootAllocatorBase(LocalHeap* heap);
1352   explicit StrongRootAllocatorBase(Isolate* isolate);
1353   explicit StrongRootAllocatorBase(v8::Isolate* isolate);
1354   explicit StrongRootAllocatorBase(LocalIsolate* isolate);
1355 
1356   // Allocate/deallocate a range of n elements of type internal::Address.
1357   Address* allocate_impl(size_t n);
1358   void deallocate_impl(Address* p, size_t n) noexcept;
1359 
1360  private:
1361   Heap* heap_;
1362 };
1363 
1364 // The general version of this template behaves just as std::allocator, with
1365 // the exception that the constructor takes the isolate as parameter. Only
1366 // specialized versions, e.g., internal::StrongRootAllocator<internal::Address>
1367 // and internal::StrongRootAllocator<v8::Local<T>> register the allocated range
1368 // as strong roots.
1369 template <typename T>
1370 class StrongRootAllocator : private std::allocator<T> {
1371  public:
1372   using value_type = T;
1373 
1374   template <typename HeapOrIsolateT>
1375   explicit StrongRootAllocator(HeapOrIsolateT*) {}
1376   template <typename U>
1377   StrongRootAllocator(const StrongRootAllocator<U>& other) noexcept {}
1378 
1379   using std::allocator<T>::allocate;
1380   using std::allocator<T>::deallocate;
1381 };
1382 
1383 // TODO(pkasting): Replace with `requires` clauses after dropping support for
1384 // old gcc versions.
1385 template <typename Iterator, typename = void>
1386 inline constexpr bool kHaveIteratorConcept = false;
1387 template <typename Iterator>
1388 inline constexpr bool kHaveIteratorConcept<
1389     Iterator, std::void_t<typename Iterator::iterator_concept>> = true;
1390 
1391 template <typename Iterator, typename = void>
1392 inline constexpr bool kHaveIteratorCategory = false;
1393 template <typename Iterator>
1394 inline constexpr bool kHaveIteratorCategory<
1395     Iterator, std::void_t<typename Iterator::iterator_category>> = true;
1396 
1397 // Helper struct that contains an `iterator_concept` type alias only when either
1398 // `Iterator` or `std::iterator_traits<Iterator>` do.
1399 // Default: no alias.
1400 template <typename Iterator, typename = void>
1401 struct MaybeDefineIteratorConcept {};
1402 // Use `Iterator::iterator_concept` if available.
1403 template <typename Iterator>
1404 struct MaybeDefineIteratorConcept<
1405     Iterator, std::enable_if_t<kHaveIteratorConcept<Iterator>>> {
1406   using iterator_concept = typename Iterator::iterator_concept;
1407 };
1408 // Otherwise fall back to `std::iterator_traits<Iterator>` if possible.
1409 template <typename Iterator>
1410 struct MaybeDefineIteratorConcept<
1411     Iterator, std::enable_if_t<kHaveIteratorCategory<Iterator> &&
1412                                !kHaveIteratorConcept<Iterator>>> {
1413   // There seems to be no feature-test macro covering this, so use the
1414   // presence of `<ranges>` as a crude proxy, since it was added to the
1415   // standard as part of the Ranges papers.
1416   // TODO(pkasting): Add this unconditionally after dropping support for old
1417   // libstdc++ versions.
1418 #if __has_include(<ranges>)
1419   using iterator_concept =
1420       typename std::iterator_traits<Iterator>::iterator_concept;
1421 #endif
1422 };
1423 
1424 // A class of iterators that wrap some different iterator type.
1425 // If specified, ElementType is the type of element accessed by the wrapper
1426 // iterator; in this case, the actual reference and pointer types of Iterator
1427 // must be convertible to ElementType& and ElementType*, respectively.
1428 template <typename Iterator, typename ElementType = void>
1429 class WrappedIterator : public MaybeDefineIteratorConcept<Iterator> {
1430  public:
1431   static_assert(
1432       std::is_void_v<ElementType> ||
1433       (std::is_convertible_v<typename std::iterator_traits<Iterator>::pointer,
1434                              std::add_pointer_t<ElementType>> &&
1435        std::is_convertible_v<typename std::iterator_traits<Iterator>::reference,
1436                              std::add_lvalue_reference_t<ElementType>>));
1437 
1438   using difference_type =
1439       typename std::iterator_traits<Iterator>::difference_type;
1440   using value_type =
1441       std::conditional_t<std::is_void_v<ElementType>,
1442                          typename std::iterator_traits<Iterator>::value_type,
1443                          ElementType>;
1444   using pointer =
1445       std::conditional_t<std::is_void_v<ElementType>,
1446                          typename std::iterator_traits<Iterator>::pointer,
1447                          std::add_pointer_t<ElementType>>;
1448   using reference =
1449       std::conditional_t<std::is_void_v<ElementType>,
1450                          typename std::iterator_traits<Iterator>::reference,
1451                          std::add_lvalue_reference_t<ElementType>>;
1452   using iterator_category =
1453       typename std::iterator_traits<Iterator>::iterator_category;
1454 
1455   constexpr WrappedIterator() noexcept = default;
1456   constexpr explicit WrappedIterator(Iterator it) noexcept : it_(it) {}
1457 
1458   // TODO(pkasting): Switch to `requires` and concepts after dropping support
1459   // for old gcc and libstdc++ versions.
1460   template <typename OtherIterator, typename OtherElementType,
1461             typename = std::enable_if_t<
1462                 std::is_convertible_v<OtherIterator, Iterator>>>
1463   constexpr WrappedIterator(
1464       const WrappedIterator<OtherIterator, OtherElementType>& other) noexcept
1465       : it_(other.base()) {}
1466 
1467   [[nodiscard]] constexpr reference operator*() const noexcept { return *it_; }
1468   [[nodiscard]] constexpr pointer operator->() const noexcept {
1469     if constexpr (std::is_pointer_v<Iterator>) {
1470       return it_;
1471     } else {
1472       return it_.operator->();
1473     }
1474   }
1475 
1476   template <typename OtherIterator, typename OtherElementType>
1477   [[nodiscard]] constexpr bool operator==(
1478       const WrappedIterator<OtherIterator, OtherElementType>& other)
1479       const noexcept {
1480     return it_ == other.base();
1481   }
1482 #if V8_HAVE_SPACESHIP_OPERATOR
1483   template <typename OtherIterator, typename OtherElementType>
1484   [[nodiscard]] constexpr auto operator<=>(
1485       const WrappedIterator<OtherIterator, OtherElementType>& other)
1486       const noexcept {
1487     if constexpr (std::three_way_comparable_with<Iterator, OtherIterator>) {
1488       return it_ <=> other.base();
1489     } else if constexpr (std::totally_ordered_with<Iterator, OtherIterator>) {
1490       if (it_ < other.base()) {
1491         return std::strong_ordering::less;
1492       }
1493       return (it_ > other.base()) ? std::strong_ordering::greater
1494                                   : std::strong_ordering::equal;
1495     } else {
1496       if (it_ < other.base()) {
1497         return std::partial_ordering::less;
1498       }
1499       if (other.base() < it_) {
1500         return std::partial_ordering::greater;
1501       }
1502       return (it_ == other.base()) ? std::partial_ordering::equivalent
1503                                    : std::partial_ordering::unordered;
1504     }
1505   }
1506 #else
1507   // Assume that if spaceship isn't present, operator rewriting might not be
1508   // either.
1509   template <typename OtherIterator, typename OtherElementType>
1510   [[nodiscard]] constexpr bool operator!=(
1511       const WrappedIterator<OtherIterator, OtherElementType>& other)
1512       const noexcept {
1513     return it_ != other.base();
1514   }
1515 
1516   template <typename OtherIterator, typename OtherElementType>
1517   [[nodiscard]] constexpr bool operator<(
1518       const WrappedIterator<OtherIterator, OtherElementType>& other)
1519       const noexcept {
1520     return it_ < other.base();
1521   }
1522   template <typename OtherIterator, typename OtherElementType>
1523   [[nodiscard]] constexpr bool operator<=(
1524       const WrappedIterator<OtherIterator, OtherElementType>& other)
1525       const noexcept {
1526     return it_ <= other.base();
1527   }
1528   template <typename OtherIterator, typename OtherElementType>
1529   [[nodiscard]] constexpr bool operator>(
1530       const WrappedIterator<OtherIterator, OtherElementType>& other)
1531       const noexcept {
1532     return it_ > other.base();
1533   }
1534   template <typename OtherIterator, typename OtherElementType>
1535   [[nodiscard]] constexpr bool operator>=(
1536       const WrappedIterator<OtherIterator, OtherElementType>& other)
1537       const noexcept {
1538     return it_ >= other.base();
1539   }
1540 #endif
1541 
1542   constexpr WrappedIterator& operator++() noexcept {
1543     ++it_;
1544     return *this;
1545   }
1546   constexpr WrappedIterator operator++(int) noexcept {
1547     WrappedIterator result(*this);
1548     ++(*this);
1549     return result;
1550   }
1551 
1552   constexpr WrappedIterator& operator--() noexcept {
1553     --it_;
1554     return *this;
1555   }
1556   constexpr WrappedIterator operator--(int) noexcept {
1557     WrappedIterator result(*this);
1558     --(*this);
1559     return result;
1560   }
1561   [[nodiscard]] constexpr WrappedIterator operator+(
1562       difference_type n) const noexcept {
1563     WrappedIterator result(*this);
1564     result += n;
1565     return result;
1566   }
1567   [[nodiscard]] friend constexpr WrappedIterator operator+(
1568       difference_type n, const WrappedIterator& x) noexcept {
1569     return x + n;
1570   }
1571   constexpr WrappedIterator& operator+=(difference_type n) noexcept {
1572     it_ += n;
1573     return *this;
1574   }
1575   [[nodiscard]] constexpr WrappedIterator operator-(
1576       difference_type n) const noexcept {
1577     return *this + -n;
1578   }
1579   constexpr WrappedIterator& operator-=(difference_type n) noexcept {
1580     return *this += -n;
1581   }
1582   template <typename OtherIterator, typename OtherElementType>
1583   [[nodiscard]] constexpr auto operator-(
1584       const WrappedIterator<OtherIterator, OtherElementType>& other)
1585       const noexcept {
1586     return it_ - other.base();
1587   }
1588   [[nodiscard]] constexpr reference operator[](
1589       difference_type n) const noexcept {
1590     return it_[n];
1591   }
1592 
1593   [[nodiscard]] constexpr const Iterator& base() const noexcept { return it_; }
1594 
1595  private:
1596   Iterator it_;
1597 };
1598 
1599 // Helper functions about values contained in handles.
1600 // A value is either an indirect pointer or a direct pointer, depending on
1601 // whether direct local support is enabled.
1602 class ValueHelper final {
1603  public:
1604   // ValueHelper::InternalRepresentationType is an abstract type that
1605   // corresponds to the internal representation of v8::Local and essentially
1606   // to what T* really is (these two are always in sync). This type is used in
1607   // methods like GetDataFromSnapshotOnce that need access to a handle's
1608   // internal representation. In particular, if `x` is a `v8::Local<T>`, then
1609   // `v8::Local<T>::FromRepr(x.repr())` gives exactly the same handle as `x`.
1610 #ifdef V8_ENABLE_DIRECT_HANDLE
1611   static constexpr Address kTaggedNullAddress = 1;
1612 
1613   using InternalRepresentationType = internal::Address;
1614   static constexpr InternalRepresentationType kEmpty = kTaggedNullAddress;
1615 #else
1616   using InternalRepresentationType = internal::Address*;
1617   static constexpr InternalRepresentationType kEmpty = nullptr;
1618 #endif  // V8_ENABLE_DIRECT_HANDLE
1619 
1620   template <typename T>
1621   V8_INLINE static bool IsEmpty(T* value) {
1622     return ValueAsRepr(value) == kEmpty;
1623   }
1624 
1625   // Returns a handle's "value" for all kinds of abstract handles. For Local,
1626   // it is equivalent to `*handle`. The variadic parameters support handle
1627   // types with extra type parameters, like `Persistent<T, M>`.
1628   template <template <typename T, typename... Ms> typename H, typename T,
1629             typename... Ms>
1630   V8_INLINE static T* HandleAsValue(const H<T, Ms...>& handle) {
1631     return handle.template value<T>();
1632   }
1633 
1634 #ifdef V8_ENABLE_DIRECT_HANDLE
1635 
1636   template <typename T>
1637   V8_INLINE static Address ValueAsAddress(const T* value) {
1638     return reinterpret_cast<Address>(value);
1639   }
1640 
1641   template <typename T, bool check_null = true, typename S>
1642   V8_INLINE static T* SlotAsValue(S* slot) {
1643     if (check_null && slot == nullptr) {
1644       return reinterpret_cast<T*>(kTaggedNullAddress);
1645     }
1646     return *reinterpret_cast<T**>(slot);
1647   }
1648 
1649   template <typename T>
1650   V8_INLINE static InternalRepresentationType ValueAsRepr(const T* value) {
1651     return reinterpret_cast<InternalRepresentationType>(value);
1652   }
1653 
1654   template <typename T>
1655   V8_INLINE static T* ReprAsValue(InternalRepresentationType repr) {
1656     return reinterpret_cast<T*>(repr);
1657   }
1658 
1659 #else  // !V8_ENABLE_DIRECT_HANDLE
1660 
1661   template <typename T>
1662   V8_INLINE static Address ValueAsAddress(const T* value) {
1663     return *reinterpret_cast<const Address*>(value);
1664   }
1665 
1666   template <typename T, bool check_null = true, typename S>
1667   V8_INLINE static T* SlotAsValue(S* slot) {
1668     return reinterpret_cast<T*>(slot);
1669   }
1670 
1671   template <typename T>
1672   V8_INLINE static InternalRepresentationType ValueAsRepr(const T* value) {
1673     return const_cast<InternalRepresentationType>(
1674         reinterpret_cast<const Address*>(value));
1675   }
1676 
1677   template <typename T>
1678   V8_INLINE static T* ReprAsValue(InternalRepresentationType repr) {
1679     return reinterpret_cast<T*>(repr);
1680   }
1681 
1682 #endif  // V8_ENABLE_DIRECT_HANDLE
1683 };
1684 
1685 /**
1686  * Helper functions about handles.
1687  */
1688 class HandleHelper final {
1689  public:
1690   /**
1691    * Checks whether two handles are equal.
1692    * They are equal iff they are both empty or they are both non-empty and the
1693    * objects to which they refer are physically equal.
1694    *
1695    * If both handles refer to JS objects, this is the same as strict equality.
1696    * For primitives, such as numbers or strings, a `false` return value does not
1697    * indicate that the values aren't equal in the JavaScript sense.
1698    * Use `Value::StrictEquals()` to check primitives for equality.
1699    */
1700   template <typename T1, typename T2>
1701   V8_INLINE static bool EqualHandles(const T1& lhs, const T2& rhs) {
1702     if (lhs.IsEmpty()) return rhs.IsEmpty();
1703     if (rhs.IsEmpty()) return false;
1704     return lhs.ptr() == rhs.ptr();
1705   }
1706 };
1707 
1708 V8_EXPORT void VerifyHandleIsNonEmpty(bool is_empty);
1709 
1710 // These functions are here just to match friend declarations in
1711 // XxxCallbackInfo classes allowing these functions to access the internals
1712 // of the info objects. These functions are supposed to be called by debugger
1713 // macros.
1714 void PrintFunctionCallbackInfo(void* function_callback_info);
1715 void PrintPropertyCallbackInfo(void* property_callback_info);
1716 
1717 }  // namespace internal
1718 }  // namespace v8
1719 
1720 #endif  // INCLUDE_V8_INTERNAL_H_