You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
14 KiB
373 lines
14 KiB
// 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; |
|
|
|
// Smi constants for systems where tagged pointer is a 32-bit value. |
|
template <> |
|
struct SmiTagging<4> { |
|
enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; |
|
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) { |
|
// To be representable as an tagged small integer, the two |
|
// most-significant bits of 'value' must be either 00 or 11 due to |
|
// sign-extension. To check this we add 01 to the two |
|
// most-significant bits, and check if the most-significant bit is 0. |
|
// |
|
// CAUTION: The original code below: |
|
// bool result = ((value + 0x40000000) & 0x80000000) == 0; |
|
// may lead to incorrect results according to the C language spec, and |
|
// in fact doesn't work correctly with gcc4.1.1 in some cases: The |
|
// compiler may produce undefined results in case of signed integer |
|
// overflow. The computation must be done w/ unsigned ints. |
|
return static_cast<uintptr_t>(value) + 0x40000000U < 0x80000000U; |
|
} |
|
}; |
|
|
|
// Smi constants for systems where tagged pointer is a 64-bit value. |
|
template <> |
|
struct SmiTagging<8> { |
|
enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; |
|
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 |
|
typedef SmiTagging<kApiInt32Size> PlatformSmiTagging; |
|
#else |
|
typedef SmiTagging<kApiTaggedSize> PlatformSmiTagging; |
|
#endif |
|
|
|
const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; |
|
const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; |
|
const int kSmiMinValue = (static_cast<unsigned int>(-1)) << (kSmiValueSize - 1); |
|
const int kSmiMaxValue = -(kSmiMinValue + 1); |
|
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 kNodeIsIndependentShift = 3; |
|
static const int kNodeIsActiveShift = 4; |
|
|
|
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_
|
|
|