| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef INCLUDE_V8_INTERNAL_H_ |
| #define INCLUDE_V8_INTERNAL_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <type_traits> |
| |
| #include "v8-version.h" // NOLINT(build/include) |
| #include "v8config.h" // NOLINT(build/include) |
| |
| namespace v8 { |
| |
| class Context; |
| class Data; |
| class Isolate; |
| |
| namespace internal { |
| |
| class Isolate; |
| |
| typedef uintptr_t Address; |
| static const Address kNullAddress = 0; |
| |
| /** |
| * Configuration of tagging scheme. |
| */ |
| const int kApiSystemPointerSize = sizeof(void*); |
| const int kApiDoubleSize = sizeof(double); |
| const int kApiInt32Size = sizeof(int32_t); |
| const int kApiInt64Size = sizeof(int64_t); |
| |
| // Tag information for HeapObject. |
| const int kHeapObjectTag = 1; |
| const int kWeakHeapObjectTag = 3; |
| const int kHeapObjectTagSize = 2; |
| const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; |
| |
| // Tag information for Smi. |
| const int kSmiTag = 0; |
| const int kSmiTagSize = 1; |
| const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1; |
| |
| template <size_t tagged_ptr_size> |
| struct SmiTagging; |
| |
| constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1}; |
| constexpr uintptr_t kUintptrAllBitsSet = |
| static_cast<uintptr_t>(kIntptrAllBitsSet); |
| |
| // Smi constants for systems where tagged pointer is a 32-bit value. |
| template <> |
| struct SmiTagging<4> { |
| enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; |
| |
| static constexpr intptr_t kSmiMinValue = |
| static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); |
| static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); |
| |
| V8_INLINE static int SmiToInt(const internal::Address value) { |
| int shift_bits = kSmiTagSize + kSmiShiftSize; |
| // Shift down (requires >> to be sign extending). |
| return static_cast<int>(static_cast<intptr_t>(value)) >> shift_bits; |
| } |
| V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { |
| // Is value in range [kSmiMinValue, kSmiMaxValue]. |
| // Use unsigned operations in order to avoid undefined behaviour in case of |
| // signed integer overflow. |
| return (static_cast<uintptr_t>(value) - |
| static_cast<uintptr_t>(kSmiMinValue)) <= |
| (static_cast<uintptr_t>(kSmiMaxValue) - |
| static_cast<uintptr_t>(kSmiMinValue)); |
| } |
| }; |
| |
| // Smi constants for systems where tagged pointer is a 64-bit value. |
| template <> |
| struct SmiTagging<8> { |
| enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; |
| |
| static constexpr intptr_t kSmiMinValue = |
| static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); |
| static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); |
| |
| V8_INLINE static int SmiToInt(const internal::Address value) { |
| int shift_bits = kSmiTagSize + kSmiShiftSize; |
| // Shift down and throw away top 32 bits. |
| return static_cast<int>(static_cast<intptr_t>(value) >> shift_bits); |
| } |
| V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { |
| // To be representable as a long smi, the value must be a 32-bit integer. |
| return (value == static_cast<int32_t>(value)); |
| } |
| }; |
| |
| #ifdef V8_COMPRESS_POINTERS |
| static_assert( |
| kApiSystemPointerSize == kApiInt64Size, |
| "Pointer compression can be enabled only for 64-bit architectures"); |
| const int kApiTaggedSize = kApiInt32Size; |
| #else |
| const int kApiTaggedSize = kApiSystemPointerSize; |
| #endif |
| |
| #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH |
| using PlatformSmiTagging = SmiTagging<kApiInt32Size>; |
| #else |
| using PlatformSmiTagging = SmiTagging<kApiTaggedSize>; |
| #endif |
| |
| const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; |
| const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; |
| const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue); |
| const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue); |
| constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; } |
| constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; } |
| |
| V8_INLINE static constexpr internal::Address IntToSmi(int value) { |
| return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) | |
| kSmiTag; |
| } |
| |
| /** |
| * This class exports constants and functionality from within v8 that |
| * is necessary to implement inline functions in the v8 api. Don't |
| * depend on functions and constants defined here. |
| */ |
| class Internals { |
| public: |
| // These values match non-compiler-dependent values defined within |
| // the implementation of v8. |
| static const int kHeapObjectMapOffset = 0; |
| static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size; |
| static const int kStringResourceOffset = |
| 1 * kApiTaggedSize + 2 * kApiInt32Size; |
| |
| static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; |
| static const int kForeignAddressOffset = kApiTaggedSize; |
| static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; |
| static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; |
| static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; |
| static const int kEmbedderDataSlotSize = kApiSystemPointerSize; |
| static const int kNativeContextEmbedderDataOffset = 7 * kApiTaggedSize; |
| static const int kFullStringRepresentationMask = 0x0f; |
| static const int kStringEncodingMask = 0x8; |
| static const int kExternalTwoByteRepresentationTag = 0x02; |
| static const int kExternalOneByteRepresentationTag = 0x0a; |
| |
| static const uint32_t kNumIsolateDataSlots = 4; |
| |
| static const int kIsolateEmbedderDataOffset = 0; |
| static const int kExternalMemoryOffset = |
| kNumIsolateDataSlots * kApiSystemPointerSize; |
| static const int kExternalMemoryLimitOffset = |
| kExternalMemoryOffset + kApiInt64Size; |
| static const int kExternalMemoryAtLastMarkCompactOffset = |
| kExternalMemoryLimitOffset + kApiInt64Size; |
| static const int kIsolateRootsOffset = |
| kExternalMemoryAtLastMarkCompactOffset + kApiInt64Size; |
| |
| static const int kUndefinedValueRootIndex = 4; |
| static const int kTheHoleValueRootIndex = 5; |
| static const int kNullValueRootIndex = 6; |
| static const int kTrueValueRootIndex = 7; |
| static const int kFalseValueRootIndex = 8; |
| static const int kEmptyStringRootIndex = 9; |
| |
| static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; |
| static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; |
| static const int kNodeStateMask = 0x7; |
| static const int kNodeStateIsWeakValue = 2; |
| static const int kNodeStateIsPendingValue = 3; |
| |
| static const int kFirstNonstringType = 0x40; |
| static const int kOddballType = 0x43; |
| static const int kForeignType = 0x47; |
| static const int kJSSpecialApiObjectType = 0x410; |
| static const int kJSApiObjectType = 0x420; |
| static const int kJSObjectType = 0x421; |
| |
| static const int kUndefinedOddballKind = 5; |
| static const int kNullOddballKind = 3; |
| |
| // Constants used by PropertyCallbackInfo to check if we should throw when an |
| // error occurs. |
| static const int kThrowOnError = 0; |
| static const int kDontThrow = 1; |
| static const int kInferShouldThrowMode = 2; |
| |
| // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an |
| // incremental GC once the external memory reaches this limit. |
| static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024; |
| |
| V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); |
| V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { |
| #ifdef V8_ENABLE_CHECKS |
| CheckInitializedImpl(isolate); |
| #endif |
| } |
| |
| V8_INLINE static bool HasHeapObjectTag(const internal::Address value) { |
| return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag); |
| } |
| |
| V8_INLINE static int SmiValue(const internal::Address value) { |
| return PlatformSmiTagging::SmiToInt(value); |
| } |
| |
| V8_INLINE static constexpr internal::Address IntToSmi(int value) { |
| return internal::IntToSmi(value); |
| } |
| |
| V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { |
| return PlatformSmiTagging::IsValidSmi(value); |
| } |
| |
| V8_INLINE static int GetInstanceType(const internal::Address obj) { |
| typedef internal::Address A; |
| A map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); |
| return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset); |
| } |
| |
| V8_INLINE static int GetOddballKind(const internal::Address obj) { |
| return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset)); |
| } |
| |
| V8_INLINE static bool IsExternalTwoByteString(int instance_type) { |
| int representation = (instance_type & kFullStringRepresentationMask); |
| return representation == kExternalTwoByteRepresentationTag; |
| } |
| |
| V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) { |
| uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; |
| return *addr & static_cast<uint8_t>(1U << shift); |
| } |
| |
| V8_INLINE static void UpdateNodeFlag(internal::Address* obj, bool value, |
| int shift) { |
| uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; |
| uint8_t mask = static_cast<uint8_t>(1U << shift); |
| *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift)); |
| } |
| |
| V8_INLINE static uint8_t GetNodeState(internal::Address* obj) { |
| uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; |
| return *addr & kNodeStateMask; |
| } |
| |
| V8_INLINE static void UpdateNodeState(internal::Address* obj, uint8_t value) { |
| uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; |
| *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value); |
| } |
| |
| V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot, |
| void* data) { |
| internal::Address addr = reinterpret_cast<internal::Address>(isolate) + |
| kIsolateEmbedderDataOffset + |
| slot * kApiSystemPointerSize; |
| *reinterpret_cast<void**>(addr) = data; |
| } |
| |
| V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate, |
| uint32_t slot) { |
| internal::Address addr = reinterpret_cast<internal::Address>(isolate) + |
| kIsolateEmbedderDataOffset + |
| slot * kApiSystemPointerSize; |
| return *reinterpret_cast<void* const*>(addr); |
| } |
| |
| V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) { |
| internal::Address addr = reinterpret_cast<internal::Address>(isolate) + |
| kIsolateRootsOffset + |
| index * kApiSystemPointerSize; |
| return reinterpret_cast<internal::Address*>(addr); |
| } |
| |
| template <typename T> |
| V8_INLINE static T ReadRawField(internal::Address heap_object_ptr, |
| int offset) { |
| internal::Address addr = heap_object_ptr + offset - kHeapObjectTag; |
| #ifdef V8_COMPRESS_POINTERS |
| if (sizeof(T) > kApiTaggedSize) { |
| // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size |
| // fields (external pointers, doubles and BigInt data) are only |
| // kTaggedSize aligned so we have to use unaligned pointer friendly way of |
| // accessing them in order to avoid undefined behavior in C++ code. |
| T r; |
| memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T)); |
| return r; |
| } |
| #endif |
| return *reinterpret_cast<const T*>(addr); |
| } |
| |
| V8_INLINE static internal::Address ReadTaggedPointerField( |
| internal::Address heap_object_ptr, int offset) { |
| #ifdef V8_COMPRESS_POINTERS |
| int32_t value = ReadRawField<int32_t>(heap_object_ptr, offset); |
| internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); |
| return root + static_cast<internal::Address>(static_cast<intptr_t>(value)); |
| #else |
| return ReadRawField<internal::Address>(heap_object_ptr, offset); |
| #endif |
| } |
| |
| V8_INLINE static internal::Address ReadTaggedSignedField( |
| internal::Address heap_object_ptr, int offset) { |
| #ifdef V8_COMPRESS_POINTERS |
| int32_t value = ReadRawField<int32_t>(heap_object_ptr, offset); |
| return static_cast<internal::Address>(static_cast<intptr_t>(value)); |
| #else |
| return ReadRawField<internal::Address>(heap_object_ptr, offset); |
| #endif |
| } |
| |
| #ifdef V8_COMPRESS_POINTERS |
| // See v8:7703 or src/ptr-compr.* for details about pointer compression. |
| static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32; |
| static constexpr size_t kPtrComprIsolateRootBias = |
| kPtrComprHeapReservationSize / 2; |
| static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32; |
| |
| V8_INLINE static internal::Address GetRootFromOnHeapAddress( |
| internal::Address addr) { |
| return (addr + kPtrComprIsolateRootBias) & |
| -static_cast<intptr_t>(kPtrComprIsolateRootAlignment); |
| } |
| |
| V8_INLINE static internal::Address DecompressTaggedAnyField( |
| internal::Address heap_object_ptr, int32_t value) { |
| internal::Address root_mask = static_cast<internal::Address>( |
| -static_cast<intptr_t>(value & kSmiTagMask)); |
| internal::Address root_or_zero = |
| root_mask & GetRootFromOnHeapAddress(heap_object_ptr); |
| return root_or_zero + |
| static_cast<internal::Address>(static_cast<intptr_t>(value)); |
| } |
| #endif // V8_COMPRESS_POINTERS |
| }; |
| |
| // Only perform cast check for types derived from v8::Data since |
| // other types do not implement the Cast method. |
| template <bool PerformCheck> |
| struct CastCheck { |
| template <class T> |
| static void Perform(T* data); |
| }; |
| |
| template <> |
| template <class T> |
| void CastCheck<true>::Perform(T* data) { |
| T::Cast(data); |
| } |
| |
| template <> |
| template <class T> |
| void CastCheck<false>::Perform(T* data) {} |
| |
| template <class T> |
| V8_INLINE void PerformCastCheck(T* data) { |
| CastCheck<std::is_base_of<Data, T>::value>::Perform(data); |
| } |
| |
| // {obj} must be the raw tagged pointer representation of a HeapObject |
| // that's guaranteed to never be in ReadOnlySpace. |
| V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); |
| |
| // Returns if we need to throw when an error occurs. This infers the language |
| // mode based on the current context and the closure. This returns true if the |
| // language mode is strict. |
| V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // INCLUDE_V8_INTERNAL_H_ |