Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 09:15:48

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