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 Context; 19 class Data; 20 class Isolate; 21 22 namespace internal { 23 24 class Isolate; 25 26 typedef uintptr_t Address; 27 static const Address kNullAddress = 0; 28 29 /** 30 * Configuration of tagging scheme. 31 */ 32 const int kApiSystemPointerSize = sizeof(void*); 33 const int kApiDoubleSize = sizeof(double); 34 const int kApiInt32Size = sizeof(int32_t); 35 const int kApiInt64Size = sizeof(int64_t); 36 37 // Tag information for HeapObject. 38 const int kHeapObjectTag = 1; 39 const int kWeakHeapObjectTag = 3; 40 const int kHeapObjectTagSize = 2; 41 const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; 42 43 // Tag information for Smi. 44 const int kSmiTag = 0; 45 const int kSmiTagSize = 1; 46 const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1; 47 48 template <size_t tagged_ptr_size> 49 struct SmiTagging; 50 51 constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1}; 52 constexpr uintptr_t kUintptrAllBitsSet = 53 static_cast<uintptr_t>(kIntptrAllBitsSet); 54 55 // Smi constants for systems where tagged pointer is a 32-bit value. 56 template <> 57 struct SmiTagging<4> { 58 enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; 59 60 static constexpr intptr_t kSmiMinValue = 61 static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); 62 static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); 63 64 V8_INLINE static int SmiToInt(const internal::Address value) { 65 int shift_bits = kSmiTagSize + kSmiShiftSize; 66 // Truncate and shift down (requires >> to be sign extending). 67 return static_cast<int32_t>(static_cast<uint32_t>(value)) >> shift_bits; 68 } 69 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 70 // Is value in range [kSmiMinValue, kSmiMaxValue]. 71 // Use unsigned operations in order to avoid undefined behaviour in case of 72 // signed integer overflow. 73 return (static_cast<uintptr_t>(value) - 74 static_cast<uintptr_t>(kSmiMinValue)) <= 75 (static_cast<uintptr_t>(kSmiMaxValue) - 76 static_cast<uintptr_t>(kSmiMinValue)); 77 } 78 }; 79 80 // Smi constants for systems where tagged pointer is a 64-bit value. 81 template <> 82 struct SmiTagging<8> { 83 enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; 84 85 static constexpr intptr_t kSmiMinValue = 86 static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); 87 static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); 88 89 V8_INLINE static int SmiToInt(const internal::Address value) { 90 int shift_bits = kSmiTagSize + kSmiShiftSize; 91 // Shift down and throw away top 32 bits. 92 return static_cast<int>(static_cast<intptr_t>(value) >> shift_bits); 93 } 94 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 95 // To be representable as a long smi, the value must be a 32-bit integer. 96 return (value == static_cast<int32_t>(value)); 97 } 98 }; 99 100 #ifdef V8_COMPRESS_POINTERS 101 static_assert( 102 kApiSystemPointerSize == kApiInt64Size, 103 "Pointer compression can be enabled only for 64-bit architectures"); 104 const int kApiTaggedSize = kApiInt32Size; 105 #else 106 const int kApiTaggedSize = kApiSystemPointerSize; 107 #endif 108 109 constexpr bool PointerCompressionIsEnabled() { 110 return kApiTaggedSize != kApiSystemPointerSize; 111 } 112 113 constexpr bool HeapSandboxIsEnabled() { 114 #ifdef V8_HEAP_SANDBOX 115 return true; 116 #else 117 return false; 118 #endif 119 } 120 121 using ExternalPointer_t = Address; 122 123 // If the heap sandbox is enabled, these tag values will be XORed with the 124 // external pointers in the external pointer table to prevent use of pointers of 125 // the wrong type. 126 enum ExternalPointerTag : Address { 127 kExternalPointerNullTag = static_cast<Address>(0ULL), 128 kArrayBufferBackingStoreTag = static_cast<Address>(1ULL << 48), 129 kTypedArrayExternalPointerTag = static_cast<Address>(2ULL << 48), 130 kDataViewDataPointerTag = static_cast<Address>(3ULL << 48), 131 kExternalStringResourceTag = static_cast<Address>(4ULL << 48), 132 kExternalStringResourceDataTag = static_cast<Address>(5ULL << 48), 133 kForeignForeignAddressTag = static_cast<Address>(6ULL << 48), 134 kNativeContextMicrotaskQueueTag = static_cast<Address>(7ULL << 48), 135 // TODO(v8:10391, saelo): Currently has to be zero so that raw zero values are 136 // also nullptr 137 kEmbedderDataSlotPayloadTag = static_cast<Address>(0ULL << 48), 138 }; 139 140 #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH 141 using PlatformSmiTagging = SmiTagging<kApiInt32Size>; 142 #else 143 using PlatformSmiTagging = SmiTagging<kApiTaggedSize>; 144 #endif 145 146 // TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize 147 // since it's used much more often than the inividual constants. 148 const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; 149 const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; 150 const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue); 151 const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue); 152 constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; } 153 constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; } 154 155 V8_INLINE static constexpr internal::Address IntToSmi(int value) { 156 return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) | 157 kSmiTag; 158 } 159 160 // Converts encoded external pointer to address. 161 V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate, 162 ExternalPointer_t pointer, 163 ExternalPointerTag tag); 164 165 // {obj} must be the raw tagged pointer representation of a HeapObject 166 // that's guaranteed to never be in ReadOnlySpace. 167 V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); 168 169 // Returns if we need to throw when an error occurs. This infers the language 170 // mode based on the current context and the closure. This returns true if the 171 // language mode is strict. 172 V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); 173 174 /** 175 * This class exports constants and functionality from within v8 that 176 * is necessary to implement inline functions in the v8 api. Don't 177 * depend on functions and constants defined here. 178 */ 179 class Internals { 180 public: 181 // These values match non-compiler-dependent values defined within 182 // the implementation of v8. 183 static const int kHeapObjectMapOffset = 0; 184 static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size; 185 static const int kStringResourceOffset = 186 1 * kApiTaggedSize + 2 * kApiInt32Size; 187 188 static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; 189 static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; 190 static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; 191 static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; 192 static const int kEmbedderDataSlotSize = kApiSystemPointerSize; 193 #ifdef V8_HEAP_SANDBOX 194 static const int kEmbedderDataSlotRawPayloadOffset = kApiTaggedSize; 195 #endif 196 static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; 197 static const int kFullStringRepresentationMask = 0x0f; 198 static const int kStringEncodingMask = 0x8; 199 static const int kExternalTwoByteRepresentationTag = 0x02; 200 static const int kExternalOneByteRepresentationTag = 0x0a; 201 202 static const uint32_t kNumIsolateDataSlots = 4; 203 204 // IsolateData layout guarantees. 205 static const int kIsolateEmbedderDataOffset = 0; 206 static const int kIsolateFastCCallCallerFpOffset = 207 kNumIsolateDataSlots * kApiSystemPointerSize; 208 static const int kIsolateFastCCallCallerPcOffset = 209 kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; 210 static const int kIsolateStackGuardOffset = 211 kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; 212 static const int kIsolateRootsOffset = 213 kIsolateStackGuardOffset + 7 * kApiSystemPointerSize; 214 215 static const int kExternalPointerTableBufferOffset = 0; 216 static const int kExternalPointerTableLengthOffset = 217 kExternalPointerTableBufferOffset + kApiSystemPointerSize; 218 static const int kExternalPointerTableCapacityOffset = 219 kExternalPointerTableLengthOffset + kApiInt32Size; 220 221 static const int kUndefinedValueRootIndex = 4; 222 static const int kTheHoleValueRootIndex = 5; 223 static const int kNullValueRootIndex = 6; 224 static const int kTrueValueRootIndex = 7; 225 static const int kFalseValueRootIndex = 8; 226 static const int kEmptyStringRootIndex = 9; 227 228 static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; 229 static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; 230 static const int kNodeStateMask = 0x7; 231 static const int kNodeStateIsWeakValue = 2; 232 static const int kNodeStateIsPendingValue = 3; 233 234 static const int kFirstNonstringType = 0x40; 235 static const int kOddballType = 0x43; 236 static const int kForeignType = 0x46; 237 static const int kJSSpecialApiObjectType = 0x410; 238 static const int kJSApiObjectType = 0x420; 239 static const int kJSObjectType = 0x421; 240 241 static const int kUndefinedOddballKind = 5; 242 static const int kNullOddballKind = 3; 243 244 // Constants used by PropertyCallbackInfo to check if we should throw when an 245 // error occurs. 246 static const int kThrowOnError = 0; 247 static const int kDontThrow = 1; 248 static const int kInferShouldThrowMode = 2; 249 250 // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an 251 // incremental GC once the external memory reaches this limit. 252 static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024; 253 254 V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); 255 V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { 256 #ifdef V8_ENABLE_CHECKS 257 CheckInitializedImpl(isolate); 258 #endif 259 } 260 261 V8_INLINE static bool HasHeapObjectTag(const internal::Address value) { 262 return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag); 263 } 264 265 V8_INLINE static int SmiValue(const internal::Address value) { 266 return PlatformSmiTagging::SmiToInt(value); 267 } 268 269 V8_INLINE static constexpr internal::Address IntToSmi(int value) { 270 return internal::IntToSmi(value); 271 } 272 273 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 274 return PlatformSmiTagging::IsValidSmi(value); 275 } 276 277 V8_INLINE static int GetInstanceType(const internal::Address obj) { 278 typedef internal::Address A; 279 A map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); 280 return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset); 281 } 282 283 V8_INLINE static int GetOddballKind(const internal::Address obj) { 284 return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset)); 285 } 286 287 V8_INLINE static bool IsExternalTwoByteString(int instance_type) { 288 int representation = (instance_type & kFullStringRepresentationMask); 289 return representation == kExternalTwoByteRepresentationTag; 290 } 291 292 V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) { 293 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 294 return *addr & static_cast<uint8_t>(1U << shift); 295 } 296 297 V8_INLINE static void UpdateNodeFlag(internal::Address* obj, bool value, 298 int shift) { 299 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 300 uint8_t mask = static_cast<uint8_t>(1U << shift); 301 *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift)); 302 } 303 304 V8_INLINE static uint8_t GetNodeState(internal::Address* obj) { 305 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 306 return *addr & kNodeStateMask; 307 } 308 309 V8_INLINE static void UpdateNodeState(internal::Address* obj, uint8_t value) { 310 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 311 *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value); 312 } 313 314 V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot, 315 void* data) { 316 internal::Address addr = reinterpret_cast<internal::Address>(isolate) + 317 kIsolateEmbedderDataOffset + 318 slot * kApiSystemPointerSize; 319 *reinterpret_cast<void**>(addr) = data; 320 } 321 322 V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate, 323 uint32_t slot) { 324 internal::Address addr = reinterpret_cast<internal::Address>(isolate) + 325 kIsolateEmbedderDataOffset + 326 slot * kApiSystemPointerSize; 327 return *reinterpret_cast<void* const*>(addr); 328 } 329 330 V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) { 331 internal::Address addr = reinterpret_cast<internal::Address>(isolate) + 332 kIsolateRootsOffset + 333 index * kApiSystemPointerSize; 334 return reinterpret_cast<internal::Address*>(addr); 335 } 336 337 template <typename T> 338 V8_INLINE static T ReadRawField(internal::Address heap_object_ptr, 339 int offset) { 340 internal::Address addr = heap_object_ptr + offset - kHeapObjectTag; 341 #ifdef V8_COMPRESS_POINTERS 342 if (sizeof(T) > kApiTaggedSize) { 343 // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size 344 // fields (external pointers, doubles and BigInt data) are only 345 // kTaggedSize aligned so we have to use unaligned pointer friendly way of 346 // accessing them in order to avoid undefined behavior in C++ code. 347 T r; 348 memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T)); 349 return r; 350 } 351 #endif 352 return *reinterpret_cast<const T*>(addr); 353 } 354 355 V8_INLINE static internal::Address ReadTaggedPointerField( 356 internal::Address heap_object_ptr, int offset) { 357 #ifdef V8_COMPRESS_POINTERS 358 uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset); 359 internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); 360 return root + static_cast<internal::Address>(static_cast<uintptr_t>(value)); 361 #else 362 return ReadRawField<internal::Address>(heap_object_ptr, offset); 363 #endif 364 } 365 366 V8_INLINE static internal::Address ReadTaggedSignedField( 367 internal::Address heap_object_ptr, int offset) { 368 #ifdef V8_COMPRESS_POINTERS 369 uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset); 370 return static_cast<internal::Address>(static_cast<uintptr_t>(value)); 371 #else 372 return ReadRawField<internal::Address>(heap_object_ptr, offset); 373 #endif 374 } 375 376 V8_INLINE static internal::Isolate* GetIsolateForHeapSandbox( 377 internal::Address obj) { 378 #ifdef V8_HEAP_SANDBOX 379 return internal::IsolateFromNeverReadOnlySpaceObject(obj); 380 #else 381 // Not used in non-sandbox mode. 382 return nullptr; 383 #endif 384 } 385 386 V8_INLINE static Address DecodeExternalPointer( 387 const Isolate* isolate, ExternalPointer_t encoded_pointer, 388 ExternalPointerTag tag) { 389 #ifdef V8_HEAP_SANDBOX 390 return internal::DecodeExternalPointerImpl(isolate, encoded_pointer, tag); 391 #else 392 return encoded_pointer; 393 #endif 394 } 395 396 V8_INLINE static internal::Address ReadExternalPointerField( 397 internal::Isolate* isolate, internal::Address heap_object_ptr, int offset, 398 ExternalPointerTag tag) { 399 #ifdef V8_HEAP_SANDBOX 400 internal::ExternalPointer_t encoded_value = 401 ReadRawField<uint32_t>(heap_object_ptr, offset); 402 // We currently have to treat zero as nullptr in embedder slots. 403 return encoded_value ? DecodeExternalPointer(isolate, encoded_value, tag) 404 : 0; 405 #else 406 return ReadRawField<Address>(heap_object_ptr, offset); 407 #endif 408 } 409 410 #ifdef V8_COMPRESS_POINTERS 411 // See v8:7703 or src/ptr-compr.* for details about pointer compression. 412 static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32; 413 static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32; 414 415 V8_INLINE static internal::Address GetRootFromOnHeapAddress( 416 internal::Address addr) { 417 return addr & -static_cast<intptr_t>(kPtrComprIsolateRootAlignment); 418 } 419 420 V8_INLINE static internal::Address DecompressTaggedAnyField( 421 internal::Address heap_object_ptr, uint32_t value) { 422 internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); 423 return root + static_cast<internal::Address>(static_cast<uintptr_t>(value)); 424 } 425 426 #endif // V8_COMPRESS_POINTERS 427 }; 428 429 // Only perform cast check for types derived from v8::Data since 430 // other types do not implement the Cast method. 431 template <bool PerformCheck> 432 struct CastCheck { 433 template <class T> 434 static void Perform(T* data); 435 }; 436 437 template <> 438 template <class T> 439 void CastCheck<true>::Perform(T* data) { 440 T::Cast(data); 441 } 442 443 template <> 444 template <class T> 445 void CastCheck<false>::Perform(T* data) {} 446 447 template <class T> 448 V8_INLINE void PerformCastCheck(T* data) { 449 CastCheck<std::is_base_of<Data, T>::value && 450 !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); 451 } 452 453 // A base class for backing stores, which is needed due to vagaries of 454 // how static casts work with std::shared_ptr. 455 class BackingStoreBase {}; 456 457 } // namespace internal 458 } // namespace v8 459 460 #endif // INCLUDE_V8_INTERNAL_H_ 461