1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef INCLUDE_V8_INTERNAL_H_
6 #define INCLUDE_V8_INTERNAL_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <type_traits>
12 
13 #include "v8-version.h"  // NOLINT(build/include_directory)
14 #include "v8config.h"    // NOLINT(build/include_directory)
15 
16 namespace v8 {
17 
18 class Array;
19 class Context;
20 class Data;
21 class Isolate;
22 template <typename T>
23 class Local;
24 
25 namespace internal {
26 
27 class Isolate;
28 
29 typedef uintptr_t Address;
30 static const Address kNullAddress = 0;
31 
32 /**
33  * Configuration of tagging scheme.
34  */
35 const int kApiSystemPointerSize = sizeof(void*);
36 const int kApiDoubleSize = sizeof(double);
37 const int kApiInt32Size = sizeof(int32_t);
38 const int kApiInt64Size = sizeof(int64_t);
39 const int kApiSizetSize = sizeof(size_t);
40 
41 // Tag information for HeapObject.
42 const int kHeapObjectTag = 1;
43 const int kWeakHeapObjectTag = 3;
44 const int kHeapObjectTagSize = 2;
45 const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1;
46 
47 // Tag information for fowarding pointers stored in object headers.
48 // 0b00 at the lowest 2 bits in the header indicates that the map word is a
49 // forwarding pointer.
50 const int kForwardingTag = 0;
51 const int kForwardingTagSize = 2;
52 const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1;
53 
54 // Tag information for Smi.
55 const int kSmiTag = 0;
56 const int kSmiTagSize = 1;
57 const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
58 
59 template <size_t tagged_ptr_size>
60 struct SmiTagging;
61 
62 constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1};
63 constexpr uintptr_t kUintptrAllBitsSet =
64     static_cast<uintptr_t>(kIntptrAllBitsSet);
65 
66 // Smi constants for systems where tagged pointer is a 32-bit value.
67 template <>
68 struct SmiTagging<4> {
69   enum { kSmiShiftSize = 0, kSmiValueSize = 31 };
70 
71   static constexpr intptr_t kSmiMinValue =
72       static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1));
73   static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1);
74 
75   V8_INLINE static int SmiToInt(const internal::Address value) {
76     int shift_bits = kSmiTagSize + kSmiShiftSize;
77     // Truncate and shift down (requires >> to be sign extending).
78     return static_cast<int32_t>(static_cast<uint32_t>(value)) >> shift_bits;
79   }
80   V8_INLINE static constexpr bool IsValidSmi(intptr_t value) {
81     // Is value in range [kSmiMinValue, kSmiMaxValue].
82     // Use unsigned operations in order to avoid undefined behaviour in case of
83     // signed integer overflow.
84     return (static_cast<uintptr_t>(value) -
85             static_cast<uintptr_t>(kSmiMinValue)) <=
86            (static_cast<uintptr_t>(kSmiMaxValue) -
87             static_cast<uintptr_t>(kSmiMinValue));
88   }
89 };
90 
91 // Smi constants for systems where tagged pointer is a 64-bit value.
92 template <>
93 struct SmiTagging<8> {
94   enum { kSmiShiftSize = 31, kSmiValueSize = 32 };
95 
96   static constexpr intptr_t kSmiMinValue =
97       static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1));
98   static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1);
99 
100   V8_INLINE static int SmiToInt(const internal::Address value) {
101     int shift_bits = kSmiTagSize + kSmiShiftSize;
102     // Shift down and throw away top 32 bits.
103     return static_cast<int>(static_cast<intptr_t>(value) >> shift_bits);
104   }
105   V8_INLINE static constexpr bool IsValidSmi(intptr_t value) {
106     // To be representable as a long smi, the value must be a 32-bit integer.
107     return (value == static_cast<int32_t>(value));
108   }
109 };
110 
111 #ifdef V8_COMPRESS_POINTERS
112 static_assert(
113     kApiSystemPointerSize == kApiInt64Size,
114     "Pointer compression can be enabled only for 64-bit architectures");
115 const int kApiTaggedSize = kApiInt32Size;
116 #else
117 const int kApiTaggedSize = kApiSystemPointerSize;
118 #endif
119 
120 constexpr bool PointerCompressionIsEnabled() {
121   return kApiTaggedSize != kApiSystemPointerSize;
122 }
123 
124 constexpr bool HeapSandboxIsEnabled() {
125 #ifdef V8_HEAP_SANDBOX
126   return true;
127 #else
128   return false;
129 #endif
130 }
131 
132 using ExternalPointer_t = Address;
133 
134 // If the heap sandbox is enabled, these tag values will be ORed with the
135 // external pointers in the external pointer table to prevent use of pointers of
136 // the wrong type. When a pointer is loaded, it is ANDed with the inverse of the
137 // expected type's tag. The tags are constructed in a way that guarantees that a
138 // failed type check will result in one or more of the top bits of the pointer
139 // to be set, rendering the pointer inacessible. This construction allows
140 // performing the type check and removing GC marking bits from the pointer at
141 // the same time.
142 enum ExternalPointerTag : uint64_t {
143   kExternalPointerNullTag = 0x0000000000000000,
144   kExternalStringResourceTag = 0x00ff000000000000,       // 0b000000011111111
145   kExternalStringResourceDataTag = 0x017f000000000000,   // 0b000000101111111
146   kForeignForeignAddressTag = 0x01bf000000000000,        // 0b000000110111111
147   kNativeContextMicrotaskQueueTag = 0x01df000000000000,  // 0b000000111011111
148   kEmbedderDataSlotPayloadTag = 0x01ef000000000000,      // 0b000000111101111
149   kCodeEntryPointTag = 0x01f7000000000000,               // 0b000000111110111
150 };
151 
152 constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
153 
154 #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH
155 using PlatformSmiTagging = SmiTagging<kApiInt32Size>;
156 #else
157 using PlatformSmiTagging = SmiTagging<kApiTaggedSize>;
158 #endif
159 
160 // TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize
161 // since it's used much more often than the inividual constants.
162 const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
163 const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
164 const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue);
165 const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue);
166 constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; }
167 constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; }
168 
169 V8_INLINE static constexpr internal::Address IntToSmi(int value) {
170   return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) |
171          kSmiTag;
172 }
173 
174 // Converts encoded external pointer to address.
175 V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate,
176                                             ExternalPointer_t pointer,
177                                             ExternalPointerTag tag);
178 
179 // {obj} must be the raw tagged pointer representation of a HeapObject
180 // that's guaranteed to never be in ReadOnlySpace.
181 V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj);
182 
183 // Returns if we need to throw when an error occurs. This infers the language
184 // mode based on the current context and the closure. This returns true if the
185 // language mode is strict.
186 V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate);
187 
188 V8_EXPORT bool CanHaveInternalField(int instance_type);
189 
190 /**
191  * This class exports constants and functionality from within v8 that
192  * is necessary to implement inline functions in the v8 api.  Don't
193  * depend on functions and constants defined here.
194  */
195 class Internals {
196 #ifdef V8_MAP_PACKING
197   V8_INLINE static constexpr internal::Address UnpackMapWord(
198       internal::Address mapword) {
199     // TODO(wenyuzhao): Clear header metadata.
200     return mapword ^ kMapWordXorMask;
201   }
202 #endif
203 
204  public:
205   // These values match non-compiler-dependent values defined within
206   // the implementation of v8.
207   static const int kHeapObjectMapOffset = 0;
208   static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size;
209   static const int kStringResourceOffset =
210       1 * kApiTaggedSize + 2 * kApiInt32Size;
211 
212   static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize;
213   static const int kJSObjectHeaderSize = 3 * kApiTaggedSize;
214   static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize;
215   static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize;
216   static const int kEmbedderDataSlotSize = kApiSystemPointerSize;
217 #ifdef V8_HEAP_SANDBOX
218   static const int kEmbedderDataSlotRawPayloadOffset = kApiTaggedSize;
219 #endif
220   static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize;
221   static const int kFullStringRepresentationMask = 0x0f;
222   static const int kStringEncodingMask = 0x8;
223   static const int kExternalTwoByteRepresentationTag = 0x02;
224   static const int kExternalOneByteRepresentationTag = 0x0a;
225 
226   static const uint32_t kNumIsolateDataSlots = 4;
227   static const int kStackGuardSize = 7 * kApiSystemPointerSize;
228   static const int kBuiltinTier0EntryTableSize = 13 * kApiSystemPointerSize;
229   static const int kBuiltinTier0TableSize = 13 * kApiSystemPointerSize;
230 
231   // IsolateData layout guarantees.
232   static const int kIsolateCageBaseOffset = 0;
233   static const int kIsolateStackGuardOffset =
234       kIsolateCageBaseOffset + kApiSystemPointerSize;
235   static const int kBuiltinTier0EntryTableOffset =
236       kIsolateStackGuardOffset + kStackGuardSize;
237   static const int kBuiltinTier0TableOffset =
238       kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize;
239   static const int kIsolateEmbedderDataOffset =
240       kBuiltinTier0TableOffset + kBuiltinTier0TableSize;
241   static const int kIsolateFastCCallCallerFpOffset =
242       kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize;
243   static const int kIsolateFastCCallCallerPcOffset =
244       kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize;
245   static const int kIsolateFastApiCallTargetOffset =
246       kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize;
247   static const int kIsolateLongTaskStatsCounterOffset =
248       kIsolateFastApiCallTargetOffset + kApiSystemPointerSize;
249   static const int kIsolateRootsOffset =
250       kIsolateLongTaskStatsCounterOffset + kApiSizetSize;
251 
252   static const int kExternalPointerTableBufferOffset = 0;
253   static const int kExternalPointerTableLengthOffset =
254       kExternalPointerTableBufferOffset + kApiSystemPointerSize;
255   static const int kExternalPointerTableCapacityOffset =
256       kExternalPointerTableLengthOffset + kApiInt32Size;
257 
258   static const int kUndefinedValueRootIndex = 4;
259   static const int kTheHoleValueRootIndex = 5;
260   static const int kNullValueRootIndex = 6;
261   static const int kTrueValueRootIndex = 7;
262   static const int kFalseValueRootIndex = 8;
263   static const int kEmptyStringRootIndex = 9;
264 
265   static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize;
266   static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3;
267   static const int kNodeStateMask = 0x7;
268   static const int kNodeStateIsWeakValue = 2;
269   static const int kNodeStateIsPendingValue = 3;
270 
271   static const int kFirstNonstringType = 0x40;
272   static const int kOddballType = 0x43;
273   static const int kForeignType = 0x46;
274   static const int kJSSpecialApiObjectType = 0x410;
275   static const int kJSObjectType = 0x421;
276   static const int kFirstJSApiObjectType = 0x422;
277   static const int kLastJSApiObjectType = 0x80A;
278 
279   static const int kUndefinedOddballKind = 5;
280   static const int kNullOddballKind = 3;
281 
282   // Constants used by PropertyCallbackInfo to check if we should throw when an
283   // error occurs.
284   static const int kThrowOnError = 0;
285   static const int kDontThrow = 1;
286   static const int kInferShouldThrowMode = 2;
287 
288   // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an
289   // incremental GC once the external memory reaches this limit.
290   static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024;
291 
292 #ifdef V8_MAP_PACKING
293   static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48;
294   // The lowest two bits of mapwords are always `0b10`
295   static const uintptr_t kMapWordSignature = 0b10;
296   // XORing a (non-compressed) map with this mask ensures that the two
297   // low-order bits are 0b10. The 0 at the end makes this look like a Smi,
298   // although real Smis have all lower 32 bits unset. We only rely on these
299   // values passing as Smis in very few places.
300   static const int kMapWordXorMask = 0b11;
301 #endif
302 
303   V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate);
304   V8_INLINE static void CheckInitialized(v8::Isolate* isolate) {
305 #ifdef V8_ENABLE_CHECKS
306     CheckInitializedImpl(isolate);
307 #endif
308   }
309 
310   V8_INLINE static bool HasHeapObjectTag(const internal::Address value) {
311     return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag);
312   }
313 
314   V8_INLINE static int SmiValue(const internal::Address value) {
315     return PlatformSmiTagging::SmiToInt(value);
316   }
317 
318   V8_INLINE static constexpr internal::Address IntToSmi(int value) {
319     return internal::IntToSmi(value);
320   }
321 
322   V8_INLINE static constexpr bool IsValidSmi(intptr_t value) {
323     return PlatformSmiTagging::IsValidSmi(value);
324   }
325 
326   V8_INLINE static int GetInstanceType(const internal::Address obj) {
327     typedef internal::Address A;
328     A map = ReadTaggedPointerField(obj, kHeapObjectMapOffset);
329 #ifdef V8_MAP_PACKING
330     map = UnpackMapWord(map);
331 #endif
332     return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset);
333   }
334 
335   V8_INLINE static int GetOddballKind(const internal::Address obj) {
336     return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset));
337   }
338 
339   V8_INLINE static bool IsExternalTwoByteString(int instance_type) {
340     int representation = (instance_type & kFullStringRepresentationMask);
341     return representation == kExternalTwoByteRepresentationTag;
342   }
343 
344   V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) {
345     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
346     return *addr & static_cast<uint8_t>(1U << shift);
347   }
348 
349   V8_INLINE static void UpdateNodeFlag(internal::Address* obj, bool value,
350                                        int shift) {
351     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
352     uint8_t mask = static_cast<uint8_t>(1U << shift);
353     *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift));
354   }
355 
356   V8_INLINE static uint8_t GetNodeState(internal::Address* obj) {
357     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
358     return *addr & kNodeStateMask;
359   }
360 
361   V8_INLINE static void UpdateNodeState(internal::Address* obj, uint8_t value) {
362     uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
363     *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value);
364   }
365 
366   V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot,
367                                         void* data) {
368     internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
369                              kIsolateEmbedderDataOffset +
370                              slot * kApiSystemPointerSize;
371     *reinterpret_cast<void**>(addr) = data;
372   }
373 
374   V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate,
375                                          uint32_t slot) {
376     internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
377                              kIsolateEmbedderDataOffset +
378                              slot * kApiSystemPointerSize;
379     return *reinterpret_cast<void* const*>(addr);
380   }
381 
382   V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) {
383     internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
384                              kIsolateLongTaskStatsCounterOffset;
385     ++(*reinterpret_cast<size_t*>(addr));
386   }
387 
388   V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) {
389     internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
390                              kIsolateRootsOffset +
391                              index * kApiSystemPointerSize;
392     return reinterpret_cast<internal::Address*>(addr);
393   }
394 
395   template <typename T>
396   V8_INLINE static T ReadRawField(internal::Address heap_object_ptr,
397                                   int offset) {
398     internal::Address addr = heap_object_ptr + offset - kHeapObjectTag;
399 #ifdef V8_COMPRESS_POINTERS
400     if (sizeof(T) > kApiTaggedSize) {
401       // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
402       // fields (external pointers, doubles and BigInt data) are only
403       // kTaggedSize aligned so we have to use unaligned pointer friendly way of
404       // accessing them in order to avoid undefined behavior in C++ code.
405       T r;
406       memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T));
407       return r;
408     }
409 #endif
410     return *reinterpret_cast<const T*>(addr);
411   }
412 
413   V8_INLINE static internal::Address ReadTaggedPointerField(
414       internal::Address heap_object_ptr, int offset) {
415 #ifdef V8_COMPRESS_POINTERS
416     uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
417     internal::Address base =
418         GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr);
419     return base + static_cast<internal::Address>(static_cast<uintptr_t>(value));
420 #else
421     return ReadRawField<internal::Address>(heap_object_ptr, offset);
422 #endif
423   }
424 
425   V8_INLINE static internal::Address ReadTaggedSignedField(
426       internal::Address heap_object_ptr, int offset) {
427 #ifdef V8_COMPRESS_POINTERS
428     uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
429     return static_cast<internal::Address>(static_cast<uintptr_t>(value));
430 #else
431     return ReadRawField<internal::Address>(heap_object_ptr, offset);
432 #endif
433   }
434 
435   V8_INLINE static internal::Isolate* GetIsolateForHeapSandbox(
436       internal::Address obj) {
437 #ifdef V8_HEAP_SANDBOX
438     return internal::IsolateFromNeverReadOnlySpaceObject(obj);
439 #else
440     // Not used in non-sandbox mode.
441     return nullptr;
442 #endif
443   }
444 
445   V8_INLINE static Address DecodeExternalPointer(
446       const Isolate* isolate, ExternalPointer_t encoded_pointer,
447       ExternalPointerTag tag) {
448 #ifdef V8_HEAP_SANDBOX
449     return internal::DecodeExternalPointerImpl(isolate, encoded_pointer, tag);
450 #else
451     return encoded_pointer;
452 #endif
453   }
454 
455   V8_INLINE static internal::Address ReadExternalPointerField(
456       internal::Isolate* isolate, internal::Address heap_object_ptr, int offset,
457       ExternalPointerTag tag) {
458 #ifdef V8_HEAP_SANDBOX
459     internal::ExternalPointer_t encoded_value =
460         ReadRawField<uint32_t>(heap_object_ptr, offset);
461     // We currently have to treat zero as nullptr in embedder slots.
462     return encoded_value ? DecodeExternalPointer(isolate, encoded_value, tag)
463                          : 0;
464 #else
465     return ReadRawField<Address>(heap_object_ptr, offset);
466 #endif
467   }
468 
469 #ifdef V8_COMPRESS_POINTERS
470   // See v8:7703 or src/ptr-compr.* for details about pointer compression.
471   static constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32;
472   static constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32;
473 
474   V8_INLINE static internal::Address GetPtrComprCageBaseFromOnHeapAddress(
475       internal::Address addr) {
476     return addr & -static_cast<intptr_t>(kPtrComprCageBaseAlignment);
477   }
478 
479   V8_INLINE static internal::Address DecompressTaggedAnyField(
480       internal::Address heap_object_ptr, uint32_t value) {
481     internal::Address base =
482         GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr);
483     return base + static_cast<internal::Address>(static_cast<uintptr_t>(value));
484   }
485 
486 #endif  // V8_COMPRESS_POINTERS
487 };
488 
489 constexpr bool VirtualMemoryCageIsEnabled() {
490 #ifdef V8_VIRTUAL_MEMORY_CAGE
491   return true;
492 #else
493   return false;
494 #endif
495 }
496 
497 #ifdef V8_VIRTUAL_MEMORY_CAGE
498 // Size of the virtual memory cage, excluding the guard regions surrounding it.
499 constexpr size_t kVirtualMemoryCageSize = size_t{1} << 40;  // 1 TB
500 
501 static_assert(kVirtualMemoryCageSize > Internals::kPtrComprCageReservationSize,
502               "The virtual memory cage must be larger than the pointer "
503               "compression cage contained within it.");
504 
505 // Required alignment of the virtual memory cage. For simplicity, we require the
506 // size of the guard regions to be a multiple of this, so that this specifies
507 // the alignment of the cage including and excluding surrounding guard regions.
508 // The alignment requirement is due to the pointer compression cage being
509 // located at the start of the virtual memory cage.
510 constexpr size_t kVirtualMemoryCageAlignment =
511     Internals::kPtrComprCageBaseAlignment;
512 
513 // Size of the guard regions surrounding the virtual memory cage. This assumes a
514 // worst-case scenario of a 32-bit unsigned index being used to access an array
515 // of 64-bit values.
516 constexpr size_t kVirtualMemoryCageGuardRegionSize = size_t{32} << 30;  // 32 GB
517 
518 static_assert((kVirtualMemoryCageGuardRegionSize %
519                kVirtualMemoryCageAlignment) == 0,
520               "The size of the virtual memory cage guard region must be a "
521               "multiple of its required alignment.");
522 
523 // Minimum size of the virtual memory cage, excluding the guard regions
524 // surrounding it. If the cage reservation fails, its size is currently halved
525 // until either the reservation succeeds or the minimum size is reached. A
526 // minimum of 32GB allows the 4GB pointer compression region as well as the
527 // ArrayBuffer partition and two 10GB WASM memory cages to fit into the cage.
528 constexpr size_t kVirtualMemoryCageMinimumSize = size_t{32} << 30;  // 32 GB
529 
530 // For now, even if the virtual memory cage is enabled, we still allow backing
531 // stores to be allocated outside of it as fallback. This will simplify the
532 // initial rollout. However, if the heap sandbox is also enabled, we already use
533 // the "enforcing mode" of the virtual memory cage. This is useful for testing.
534 #ifdef V8_HEAP_SANDBOX
535 constexpr bool kAllowBackingStoresOutsideCage = false;
536 #else
537 constexpr bool kAllowBackingStoresOutsideCage = true;
538 #endif  // V8_HEAP_SANDBOX
539 
540 #endif  // V8_VIRTUAL_MEMORY_CAGE
541 
542 // Only perform cast check for types derived from v8::Data since
543 // other types do not implement the Cast method.
544 template <bool PerformCheck>
545 struct CastCheck {
546   template <class T>
547   static void Perform(T* data);
548 };
549 
550 template <>
551 template <class T>
552 void CastCheck<true>::Perform(T* data) {
553   T::Cast(data);
554 }
555 
556 template <>
557 template <class T>
558 void CastCheck<false>::Perform(T* data) {}
559 
560 template <class T>
561 V8_INLINE void PerformCastCheck(T* data) {
562   CastCheck<std::is_base_of<Data, T>::value &&
563             !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
564 }
565 
566 // A base class for backing stores, which is needed due to vagaries of
567 // how static casts work with std::shared_ptr.
568 class BackingStoreBase {};
569 
570 }  // namespace internal
571 
572 }  // namespace v8
573 
574 #endif  // INCLUDE_V8_INTERNAL_H_
575