v8  9.4.146 (node 16.15.0)
V8 is Google's open source JavaScript engine
v8-internal.h
Go to the documentation of this file.
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(
114  "Pointer compression can be enabled only for 64-bit architectures");
115 const int kApiTaggedSize = kApiInt32Size;
116 #else
118 #endif
119 
120 constexpr bool PointerCompressionIsEnabled() {
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.
143  kExternalPointerNullTag = 0x0000000000000000,
144  kArrayBufferBackingStoreTag = 0x00ff000000000000, // 0b000000011111111
145  kTypedArrayExternalPointerTag = 0x017f000000000000, // 0b000000101111111
146  kDataViewDataPointerTag = 0x01bf000000000000, // 0b000000110111111
147  kExternalStringResourceTag = 0x01df000000000000, // 0b000000111011111
148  kExternalStringResourceDataTag = 0x01ef000000000000, // 0b000000111101111
149  kForeignForeignAddressTag = 0x01f7000000000000, // 0b000000111110111
150  kNativeContextMicrotaskQueueTag = 0x01fb000000000000, // 0b000000111111011
151  kEmbedderDataSlotPayloadTag = 0x01fd000000000000, // 0b000000111111101
152  kCodeEntryPointTag = 0x01fe000000000000, // 0b000000111111110
153 };
154 
155 constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
156 
157 #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH
159 #else
160 using PlatformSmiTagging = SmiTagging<kApiTaggedSize>;
161 #endif
162 
163 // TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize
164 // since it's used much more often than the inividual constants.
165 const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
166 const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
167 const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue);
168 const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue);
169 constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; }
170 constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; }
171 
172 V8_INLINE static constexpr internal::Address IntToSmi(int value) {
173  return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) |
174  kSmiTag;
175 }
176 
177 // Converts encoded external pointer to address.
179  ExternalPointer_t pointer,
180  ExternalPointerTag tag);
181 
182 // {obj} must be the raw tagged pointer representation of a HeapObject
183 // that's guaranteed to never be in ReadOnlySpace.
185 
186 // Returns if we need to throw when an error occurs. This infers the language
187 // mode based on the current context and the closure. This returns true if the
188 // language mode is strict.
189 V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate);
190 
191 V8_EXPORT bool CanHaveInternalField(int instance_type);
192 
193 /**
194  * This class exports constants and functionality from within v8 that
195  * is necessary to implement inline functions in the v8 api. Don't
196  * depend on functions and constants defined here.
197  */
198 class Internals {
199 #ifdef V8_MAP_PACKING
200  V8_INLINE static constexpr internal::Address UnpackMapWord(
202  // TODO(wenyuzhao): Clear header metadata.
203  return mapword ^ kMapWordXorMask;
204  }
205 #endif
206 
207  public:
208  // These values match non-compiler-dependent values defined within
209  // the implementation of v8.
210  static const int kHeapObjectMapOffset = 0;
212  static const int kStringResourceOffset =
213  1 * kApiTaggedSize + 2 * kApiInt32Size;
214 
216  static const int kJSObjectHeaderSize = 3 * kApiTaggedSize;
217  static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize;
220 #ifdef V8_HEAP_SANDBOX
222 #endif
224  static const int kFullStringRepresentationMask = 0x0f;
225  static const int kStringEncodingMask = 0x8;
226  static const int kExternalTwoByteRepresentationTag = 0x02;
227  static const int kExternalOneByteRepresentationTag = 0x0a;
228 
229  static const uint32_t kNumIsolateDataSlots = 4;
230 
231  // IsolateData layout guarantees.
232  static const int kIsolateEmbedderDataOffset = 0;
239  static const int kIsolateCageBaseOffset =
243  static const int kIsolateStackGuardOffset =
245  static const int kIsolateRootsOffset =
247 
248  static const int kExternalPointerTableBufferOffset = 0;
253 
254  static const int kUndefinedValueRootIndex = 4;
255  static const int kTheHoleValueRootIndex = 5;
256  static const int kNullValueRootIndex = 6;
257  static const int kTrueValueRootIndex = 7;
258  static const int kFalseValueRootIndex = 8;
259  static const int kEmptyStringRootIndex = 9;
260 
262  static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3;
263  static const int kNodeStateMask = 0x7;
264  static const int kNodeStateIsWeakValue = 2;
265  static const int kNodeStateIsPendingValue = 3;
266 
267  static const int kFirstNonstringType = 0x40;
268  static const int kOddballType = 0x43;
269  static const int kForeignType = 0x46;
270  static const int kJSSpecialApiObjectType = 0x410;
271  static const int kJSObjectType = 0x421;
272  static const int kFirstJSApiObjectType = 0x422;
273  static const int kLastJSApiObjectType = 0x80A;
274 
275  static const int kUndefinedOddballKind = 5;
276  static const int kNullOddballKind = 3;
277 
278  // Constants used by PropertyCallbackInfo to check if we should throw when an
279  // error occurs.
280  static const int kThrowOnError = 0;
281  static const int kDontThrow = 1;
282  static const int kInferShouldThrowMode = 2;
283 
284  // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an
285  // incremental GC once the external memory reaches this limit.
286  static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024;
287 
288 #ifdef V8_MAP_PACKING
289  static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48;
290  // The lowest two bits of mapwords are always `0b10`
291  static const uintptr_t kMapWordSignature = 0b10;
292  // XORing a (non-compressed) map with this mask ensures that the two
293  // low-order bits are 0b10. The 0 at the end makes this look like a Smi,
294  // although real Smis have all lower 32 bits unset. We only rely on these
295  // values passing as Smis in very few places.
296  static const int kMapWordXorMask = 0b11;
297 #endif
298 
299  V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate);
300  V8_INLINE static void CheckInitialized(v8::Isolate* isolate) {
301 #ifdef V8_ENABLE_CHECKS
302  CheckInitializedImpl(isolate);
303 #endif
304  }
305 
306  V8_INLINE static bool HasHeapObjectTag(const internal::Address value) {
307  return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag);
308  }
309 
310  V8_INLINE static int SmiValue(const internal::Address value) {
311  return PlatformSmiTagging::SmiToInt(value);
312  }
313 
314  V8_INLINE static constexpr internal::Address IntToSmi(int value) {
315  return internal::IntToSmi(value);
316  }
317 
318  V8_INLINE static constexpr bool IsValidSmi(intptr_t value) {
319  return PlatformSmiTagging::IsValidSmi(value);
320  }
321 
322  V8_INLINE static int GetInstanceType(const internal::Address obj) {
323  typedef internal::Address A;
325 #ifdef V8_MAP_PACKING
326  map = UnpackMapWord(map);
327 #endif
328  return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset);
329  }
330 
331  V8_INLINE static int GetOddballKind(const internal::Address obj) {
333  }
334 
335  V8_INLINE static bool IsExternalTwoByteString(int instance_type) {
336  int representation = (instance_type & kFullStringRepresentationMask);
337  return representation == kExternalTwoByteRepresentationTag;
338  }
339 
340  V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) {
341  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
342  return *addr & static_cast<uint8_t>(1U << shift);
343  }
344 
345  V8_INLINE static void UpdateNodeFlag(internal::Address* obj, bool value,
346  int shift) {
347  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
348  uint8_t mask = static_cast<uint8_t>(1U << shift);
349  *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift));
350  }
351 
352  V8_INLINE static uint8_t GetNodeState(internal::Address* obj) {
353  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
354  return *addr & kNodeStateMask;
355  }
356 
357  V8_INLINE static void UpdateNodeState(internal::Address* obj, uint8_t value) {
358  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
359  *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value);
360  }
361 
362  V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot,
363  void* data) {
364  internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
366  slot * kApiSystemPointerSize;
367  *reinterpret_cast<void**>(addr) = data;
368  }
369 
370  V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate,
371  uint32_t slot) {
372  internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
374  slot * kApiSystemPointerSize;
375  return *reinterpret_cast<void* const*>(addr);
376  }
377 
379  internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
381  ++(*reinterpret_cast<size_t*>(addr));
382  }
383 
384  V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) {
385  internal::Address addr = reinterpret_cast<internal::Address>(isolate) +
387  index * kApiSystemPointerSize;
388  return reinterpret_cast<internal::Address*>(addr);
389  }
390 
391  template <typename T>
392  V8_INLINE static T ReadRawField(internal::Address heap_object_ptr,
393  int offset) {
394  internal::Address addr = heap_object_ptr + offset - kHeapObjectTag;
395 #ifdef V8_COMPRESS_POINTERS
396  if (sizeof(T) > kApiTaggedSize) {
397  // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
398  // fields (external pointers, doubles and BigInt data) are only
399  // kTaggedSize aligned so we have to use unaligned pointer friendly way of
400  // accessing them in order to avoid undefined behavior in C++ code.
401  T r;
402  memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T));
403  return r;
404  }
405 #endif
406  return *reinterpret_cast<const T*>(addr);
407  }
408 
410  internal::Address heap_object_ptr, int offset) {
411 #ifdef V8_COMPRESS_POINTERS
412  uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
413  internal::Address base =
414  GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr);
415  return base + static_cast<internal::Address>(static_cast<uintptr_t>(value));
416 #else
417  return ReadRawField<internal::Address>(heap_object_ptr, offset);
418 #endif
419  }
420 
422  internal::Address heap_object_ptr, int offset) {
423 #ifdef V8_COMPRESS_POINTERS
424  uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset);
425  return static_cast<internal::Address>(static_cast<uintptr_t>(value));
426 #else
427  return ReadRawField<internal::Address>(heap_object_ptr, offset);
428 #endif
429  }
430 
432  internal::Address obj) {
433 #ifdef V8_HEAP_SANDBOX
434  return internal::IsolateFromNeverReadOnlySpaceObject(obj);
435 #else
436  // Not used in non-sandbox mode.
437  return nullptr;
438 #endif
439  }
440 
442  const Isolate* isolate, ExternalPointer_t encoded_pointer,
443  ExternalPointerTag tag) {
444 #ifdef V8_HEAP_SANDBOX
445  return internal::DecodeExternalPointerImpl(isolate, encoded_pointer, tag);
446 #else
447  return encoded_pointer;
448 #endif
449  }
450 
452  internal::Isolate* isolate, internal::Address heap_object_ptr, int offset,
453  ExternalPointerTag tag) {
454 #ifdef V8_HEAP_SANDBOX
455  internal::ExternalPointer_t encoded_value =
456  ReadRawField<uint32_t>(heap_object_ptr, offset);
457  // We currently have to treat zero as nullptr in embedder slots.
458  return encoded_value ? DecodeExternalPointer(isolate, encoded_value, tag)
459  : 0;
460 #else
461  return ReadRawField<Address>(heap_object_ptr, offset);
462 #endif
463  }
464 
465 #ifdef V8_COMPRESS_POINTERS
466  // See v8:7703 or src/ptr-compr.* for details about pointer compression.
467  static constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32;
468  static constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32;
469 
471  internal::Address addr) {
472  return addr & -static_cast<intptr_t>(kPtrComprCageBaseAlignment);
473  }
474 
479  return base + static_cast<internal::Address>(static_cast<uintptr_t>(value));
480  }
481 
482 #endif // V8_COMPRESS_POINTERS
483 };
484 
485 // Only perform cast check for types derived from v8::Data since
486 // other types do not implement the Cast method.
487 template <bool PerformCheck>
488 struct CastCheck {
489  template <class T>
490  static void Perform(T* data);
491 };
492 
493 template <>
494 template <class T>
495 void CastCheck<true>::Perform(T* data) {
496  T::Cast(data);
497 }
498 
499 template <>
500 template <class T>
501 void CastCheck<false>::Perform(T* data) {}
502 
503 template <class T>
505  CastCheck<std::is_base_of<Data, T>::value &&
506  !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
507 }
508 
509 // A base class for backing stores, which is needed due to vagaries of
510 // how static casts work with std::shared_ptr.
512 
513 } // namespace internal
514 
516  int32_t* dst,
517  uint32_t max_length);
518 
520  double* dst,
521  uint32_t max_length);
522 
523 } // namespace v8
524 
525 #endif // INCLUDE_V8_INTERNAL_H_