diff --git a/CHANGELOG.md b/CHANGELOG.md index 7819d8e0..6af4f3cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# [8.6.0-alpha.1](https://github.com/NativeScript/ios/compare/v8.5.2...v8.6.0-alpha.1) (2023-06-20) + + +### Bug Fixes + +* delay isolate disposal when isolate is in use ([5a6c2ee](https://github.com/NativeScript/ios/commit/5a6c2ee5efa0c557c94ae56da0d3b3a31911d1b8)) + + +### Reverts + +* Revert "Misc changes that might not be needed" ([ad9670b](https://github.com/NativeScript/ios/commit/ad9670b9c7fb347b5d59698adbadbf40c31fe86c)) +* Revert "Garbage changes to revert" ([3b0e697](https://github.com/NativeScript/ios/commit/3b0e697b2ec9f4d5fc14f2c218fe25b16159f32f)) +* Revert "Add Clear() method to ConcurrentMap" ([bfbd301](https://github.com/NativeScript/ios/commit/bfbd30198510d7526c7eef1e6949408a0aad992e)) + + + +# [8.6.0-alpha.0](https://github.com/NativeScript/ios/compare/v8.5.2...v8.6.0-alpha.0) (2023-06-20) + + +### Bug Fixes + +* delay isolate disposal when isolate is in use ([5a6c2ee](https://github.com/NativeScript/ios/commit/5a6c2ee5efa0c557c94ae56da0d3b3a31911d1b8)) + + +### Features + +* Support v8 11.1.277.17 ([20be18a](https://github.com/NativeScript/ios/pull/218/commits/20be18ac6fe44a4cc517ccdb776f249f98a31b27)) + + ## [8.5.2](https://github.com/NativeScript/ios/compare/v8.5.1...v8.5.2) (2023-05-24) diff --git a/NativeScript/include/cppgc/README.md b/NativeScript/include/cppgc/README.md index a7d08f86..d825ea5b 100644 --- a/NativeScript/include/cppgc/README.md +++ b/NativeScript/include/cppgc/README.md @@ -26,6 +26,8 @@ This allows Oilpan to run garbage collection in parallel with mutators running i References to objects belonging to another thread's heap are modeled using cross-thread roots. This is even true for on-heap to on-heap references. +Oilpan heaps may generally not be accessed from different threads unless otherwise noted. + ## Heap partitioning Oilpan's heaps are partitioned into spaces. diff --git a/NativeScript/include/cppgc/common.h b/NativeScript/include/cppgc/common.h index b6dbff3d..96103836 100644 --- a/NativeScript/include/cppgc/common.h +++ b/NativeScript/include/cppgc/common.h @@ -5,7 +5,6 @@ #ifndef INCLUDE_CPPGC_COMMON_H_ #define INCLUDE_CPPGC_COMMON_H_ -// TODO(chromium:1056170): Remove dependency on v8. #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { diff --git a/NativeScript/include/cppgc/cross-thread-persistent.h b/NativeScript/include/cppgc/cross-thread-persistent.h index c8751e1d..1fa28afa 100644 --- a/NativeScript/include/cppgc/cross-thread-persistent.h +++ b/NativeScript/include/cppgc/cross-thread-persistent.h @@ -120,7 +120,7 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, if (!IsValid(raw)) return; PersistentRegionLock guard; CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); - SetNode(region.AllocateNode(this, &Trace)); + SetNode(region.AllocateNode(this, &TraceAsRoot)); this->CheckPointer(raw); } @@ -138,7 +138,7 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, : CrossThreadPersistentBase(raw), LocationPolicy(loc) { if (!IsValid(raw)) return; CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); - SetNode(region.AllocateNode(this, &Trace)); + SetNode(region.AllocateNode(this, &TraceAsRoot)); this->CheckPointer(raw); } @@ -349,9 +349,8 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, return ptr && ptr != kSentinelPointer; } - static void Trace(Visitor* v, const void* ptr) { - const auto* handle = static_cast(ptr); - v->TraceRoot(*handle, handle->Location()); + static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { + root_visitor.Trace(*static_cast(ptr)); } void AssignUnsafe(T* ptr) { @@ -378,7 +377,7 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, SetValue(ptr); if (!IsValid(ptr)) return; PersistentRegionLock guard; - SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace)); + SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); this->CheckPointer(ptr); } @@ -398,7 +397,7 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, } SetValue(ptr); if (!IsValid(ptr)) return; - SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace)); + SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); this->CheckPointer(ptr); } @@ -416,7 +415,7 @@ class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, return static_cast(const_cast(GetValueFromGC())); } - friend class cppgc::Visitor; + friend class internal::RootVisitor; }; template diff --git a/NativeScript/include/cppgc/heap-consistency.h b/NativeScript/include/cppgc/heap-consistency.h index 54a4dbc2..35c59ed1 100644 --- a/NativeScript/include/cppgc/heap-consistency.h +++ b/NativeScript/include/cppgc/heap-consistency.h @@ -9,6 +9,7 @@ #include "cppgc/internal/write-barrier.h" #include "cppgc/macros.h" +#include "cppgc/member.h" #include "cppgc/trace-trait.h" #include "v8config.h" // NOLINT(build/include_directory) @@ -47,6 +48,29 @@ class HeapConsistency final { return internal::WriteBarrier::GetWriteBarrierType(slot, value, params); } + /** + * Gets the required write barrier type for a specific write. This override is + * only used for all the BasicMember types. + * + * \param slot Slot containing the pointer to the object. The slot itself + * must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + * \param value The pointer to the object held via `BasicMember`. + * \param params Parameters that may be used for actual write barrier calls. + * Only filled if return value indicates that a write barrier is needed. The + * contents of the `params` are an implementation detail. + * \returns whether a write barrier is needed and which barrier to invoke. + */ + template + static V8_INLINE WriteBarrierType GetWriteBarrierType( + const internal::BasicMember& value, + WriteBarrierParams& params) { + return internal::WriteBarrier::GetWriteBarrierType( + value.GetRawSlot(), value.GetRawStorage(), params); + } + /** * Gets the required write barrier type for a specific write. * @@ -146,7 +170,25 @@ class HeapConsistency final { */ static V8_INLINE void GenerationalBarrier(const WriteBarrierParams& params, const void* slot) { - internal::WriteBarrier::GenerationalBarrier(params, slot); + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, + slot); + } + + /** + * Generational barrier for maintaining consistency when running with multiple + * generations. This version is used when slot contains uncompressed pointer. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param slot Uncompressed slot containing the direct pointer to the object. + * The slot itself must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + */ + static V8_INLINE void GenerationalBarrierForUncompressedSlot( + const WriteBarrierParams& params, const void* uncompressed_slot) { + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType:: + kPreciseUncompressedSlot>(params, uncompressed_slot); } /** @@ -158,8 +200,9 @@ class HeapConsistency final { */ static V8_INLINE void GenerationalBarrierForSourceObject( const WriteBarrierParams& params, const void* inner_pointer) { - internal::WriteBarrier::GenerationalBarrierForSourceObject(params, - inner_pointer); + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType::kImpreciseSlot>( + params, inner_pointer); } private: diff --git a/NativeScript/include/cppgc/heap-handle.h b/NativeScript/include/cppgc/heap-handle.h new file mode 100644 index 00000000..0d1d21e6 --- /dev/null +++ b/NativeScript/include/cppgc/heap-handle.h @@ -0,0 +1,48 @@ +// Copyright 2022 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_CPPGC_HEAP_HANDLE_H_ +#define INCLUDE_CPPGC_HEAP_HANDLE_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace internal { +class HeapBase; +class WriteBarrierTypeForCagedHeapPolicy; +class WriteBarrierTypeForNonCagedHeapPolicy; +} // namespace internal + +/** + * Opaque handle used for additional heap APIs. + */ +class HeapHandle { + public: + // Deleted copy ctor to avoid treating the type by value. + HeapHandle(const HeapHandle&) = delete; + HeapHandle& operator=(const HeapHandle&) = delete; + + private: + HeapHandle() = default; + + V8_INLINE bool is_incremental_marking_in_progress() const { + return is_incremental_marking_in_progress_; + } + + V8_INLINE bool is_young_generation_enabled() const { + return is_young_generation_enabled_; + } + + bool is_incremental_marking_in_progress_ = false; + bool is_young_generation_enabled_ = false; + + friend class internal::HeapBase; + friend class internal::WriteBarrierTypeForCagedHeapPolicy; + friend class internal::WriteBarrierTypeForNonCagedHeapPolicy; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_HANDLE_H_ diff --git a/NativeScript/include/cppgc/heap-statistics.h b/NativeScript/include/cppgc/heap-statistics.h index 8e626596..5e389874 100644 --- a/NativeScript/include/cppgc/heap-statistics.h +++ b/NativeScript/include/cppgc/heap-statistics.h @@ -56,7 +56,7 @@ struct HeapStatistics final { /** Amount of memory actually used on the page. */ size_t used_size_bytes = 0; /** Statistics for object allocated on the page. Filled only when - * NameProvider::HideInternalNames() is false. */ + * NameProvider::SupportsCppClassNamesAsObjectNames() is true. */ std::vector object_statistics; }; @@ -98,7 +98,7 @@ struct HeapStatistics final { /** Overall committed amount of memory for the heap. */ size_t committed_size_bytes = 0; - /** Resident amount of memory help by the heap. */ + /** Resident amount of memory held by the heap. */ size_t resident_size_bytes = 0; /** Amount of memory actually used on the heap. */ size_t used_size_bytes = 0; diff --git a/NativeScript/include/cppgc/heap.h b/NativeScript/include/cppgc/heap.h index aa3c6f46..02ee12ea 100644 --- a/NativeScript/include/cppgc/heap.h +++ b/NativeScript/include/cppgc/heap.h @@ -21,6 +21,7 @@ namespace cppgc { class AllocationHandle; +class HeapHandle; /** * Implementation details of cppgc. Those details are considered internal and @@ -31,11 +32,6 @@ namespace internal { class Heap; } // namespace internal -/** - * Used for additional heap APIs. - */ -class HeapHandle; - class V8_EXPORT Heap { public: /** @@ -59,7 +55,7 @@ class V8_EXPORT Heap { }; /** - * Specifies supported marking types + * Specifies supported marking types. */ enum class MarkingType : uint8_t { /** @@ -79,7 +75,7 @@ class V8_EXPORT Heap { }; /** - * Specifies supported sweeping types + * Specifies supported sweeping types. */ enum class SweepingType : uint8_t { /** diff --git a/NativeScript/include/cppgc/internal/api-constants.h b/NativeScript/include/cppgc/internal/api-constants.h index a50d4d04..023426e9 100644 --- a/NativeScript/include/cppgc/internal/api-constants.h +++ b/NativeScript/include/cppgc/internal/api-constants.h @@ -32,12 +32,22 @@ static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1}; static constexpr size_t kPageSize = size_t{1} << 17; +#if defined(V8_TARGET_ARCH_ARM64) && defined(V8_OS_MACOS) +constexpr size_t kGuardPageSize = 0; +#else +constexpr size_t kGuardPageSize = 4096; +#endif + static constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2; #if defined(CPPGC_CAGED_HEAP) +#if defined(CPPGC_2GB_CAGE) +constexpr size_t kCagedHeapReservationSize = static_cast(2) * kGB; +#else // !defined(CPPGC_2GB_CAGE) constexpr size_t kCagedHeapReservationSize = static_cast(4) * kGB; +#endif // !defined(CPPGC_2GB_CAGE) constexpr size_t kCagedHeapReservationAlignment = kCagedHeapReservationSize; -#endif +#endif // defined(CPPGC_CAGED_HEAP) static constexpr size_t kDefaultAlignment = sizeof(void*); diff --git a/NativeScript/include/cppgc/internal/base-page-handle.h b/NativeScript/include/cppgc/internal/base-page-handle.h new file mode 100644 index 00000000..9c690755 --- /dev/null +++ b/NativeScript/include/cppgc/internal/base-page-handle.h @@ -0,0 +1,45 @@ +// Copyright 2022 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_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ +#define INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ + +#include "cppgc/heap-handle.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/logging.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// The class is needed in the header to allow for fast access to HeapHandle in +// the write barrier. +class BasePageHandle { + public: + static V8_INLINE BasePageHandle* FromPayload(void* payload) { + return reinterpret_cast( + (reinterpret_cast(payload) & + ~(api_constants::kPageSize - 1)) + + api_constants::kGuardPageSize); + } + static V8_INLINE const BasePageHandle* FromPayload(const void* payload) { + return FromPayload(const_cast(payload)); + } + + HeapHandle& heap_handle() { return heap_handle_; } + const HeapHandle& heap_handle() const { return heap_handle_; } + + protected: + explicit BasePageHandle(HeapHandle& heap_handle) : heap_handle_(heap_handle) { + CPPGC_DCHECK(reinterpret_cast(this) % api_constants::kPageSize == + api_constants::kGuardPageSize); + } + + HeapHandle& heap_handle_; +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ diff --git a/NativeScript/include/cppgc/internal/caged-heap-local-data.h b/NativeScript/include/cppgc/internal/caged-heap-local-data.h index a27649c1..7d689f87 100644 --- a/NativeScript/include/cppgc/internal/caged-heap-local-data.h +++ b/NativeScript/include/cppgc/internal/caged-heap-local-data.h @@ -10,46 +10,76 @@ #include #include "cppgc/internal/api-constants.h" +#include "cppgc/internal/caged-heap.h" #include "cppgc/internal/logging.h" #include "cppgc/platform.h" #include "v8config.h" // NOLINT(build/include_directory) +#if __cpp_lib_bitopts +#include +#endif // __cpp_lib_bitopts + +#if defined(CPPGC_CAGED_HEAP) + namespace cppgc { namespace internal { class HeapBase; +class HeapBaseHandle; #if defined(CPPGC_YOUNG_GENERATION) // AgeTable is the bytemap needed for the fast generation check in the write -// barrier. AgeTable contains entries that correspond to 512 bytes memory +// barrier. AgeTable contains entries that correspond to 4096 bytes memory // regions (cards). Each entry in the table represents generation of the objects // that reside on the corresponding card (young, old or mixed). -class AgeTable final { +class V8_EXPORT AgeTable final { static constexpr size_t kRequiredSize = 1 * api_constants::kMB; static constexpr size_t kAllocationGranularity = api_constants::kAllocationGranularity; public: + // Represents age of the objects living on a single card. enum class Age : uint8_t { kOld, kYoung, kMixed }; + // When setting age for a range, consider or ignore ages of the adjacent + // cards. + enum class AdjacentCardsPolicy : uint8_t { kConsider, kIgnore }; static constexpr size_t kCardSizeInBytes = - (api_constants::kCagedHeapReservationSize / kAllocationGranularity) / - kRequiredSize; + api_constants::kCagedHeapReservationSize / kRequiredSize; void SetAge(uintptr_t cage_offset, Age age) { table_[card(cage_offset)] = age; } + V8_INLINE Age GetAge(uintptr_t cage_offset) const { return table_[card(cage_offset)]; } - void Reset(PageAllocator* allocator); + void SetAgeForRange(uintptr_t cage_offset_begin, uintptr_t cage_offset_end, + Age age, AdjacentCardsPolicy adjacent_cards_policy); + + Age GetAgeForRange(uintptr_t cage_offset_begin, + uintptr_t cage_offset_end) const; + + void ResetForTesting(); private: V8_INLINE size_t card(uintptr_t offset) const { constexpr size_t kGranularityBits = +#if __cpp_lib_bitopts + std::countr_zero(static_cast(kCardSizeInBytes)); +#elif V8_HAS_BUILTIN_CTZ __builtin_ctz(static_cast(kCardSizeInBytes)); +#else //! V8_HAS_BUILTIN_CTZ + // Hardcode and check with assert. +#if defined(CPPGC_2GB_CAGE) + 11; +#else // !defined(CPPGC_2GB_CAGE) + 12; +#endif // !defined(CPPGC_2GB_CAGE) +#endif // !V8_HAS_BUILTIN_CTZ + static_assert((1 << kGranularityBits) == kCardSizeInBytes); const size_t entry = offset >> kGranularityBits; CPPGC_DCHECK(table_.size() > entry); return entry; @@ -64,10 +94,10 @@ static_assert(sizeof(AgeTable) == 1 * api_constants::kMB, #endif // CPPGC_YOUNG_GENERATION struct CagedHeapLocalData final { - CagedHeapLocalData(HeapBase&, PageAllocator&); + V8_INLINE static CagedHeapLocalData& Get() { + return *reinterpret_cast(CagedHeapBase::GetBase()); + } - bool is_incremental_marking_in_progress = false; - HeapBase& heap_base; #if defined(CPPGC_YOUNG_GENERATION) AgeTable age_table; #endif @@ -76,4 +106,6 @@ struct CagedHeapLocalData final { } // namespace internal } // namespace cppgc +#endif // defined(CPPGC_CAGED_HEAP) + #endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ diff --git a/NativeScript/include/cppgc/internal/caged-heap.h b/NativeScript/include/cppgc/internal/caged-heap.h new file mode 100644 index 00000000..4db42aee --- /dev/null +++ b/NativeScript/include/cppgc/internal/caged-heap.h @@ -0,0 +1,61 @@ +// Copyright 2022 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_CPPGC_INTERNAL_CAGED_HEAP_H_ +#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ + +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/base-page-handle.h" +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(CPPGC_CAGED_HEAP) + +namespace cppgc { +namespace internal { + +class V8_EXPORT CagedHeapBase { + public: + V8_INLINE static uintptr_t OffsetFromAddress(const void* address) { + return reinterpret_cast(address) & + (api_constants::kCagedHeapReservationAlignment - 1); + } + + V8_INLINE static bool IsWithinCage(const void* address) { + CPPGC_DCHECK(g_heap_base_); + return (reinterpret_cast(address) & + ~(api_constants::kCagedHeapReservationAlignment - 1)) == + g_heap_base_; + } + + V8_INLINE static bool AreWithinCage(const void* addr1, const void* addr2) { +#if defined(CPPGC_2GB_CAGE) + static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT - 1; +#else //! defined(CPPGC_2GB_CAGE) + static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT; +#endif //! defined(CPPGC_2GB_CAGE) + static_assert((static_cast(1) << kHalfWordShift) == + api_constants::kCagedHeapReservationSize); + CPPGC_DCHECK(g_heap_base_); + return !(((reinterpret_cast(addr1) ^ g_heap_base_) | + (reinterpret_cast(addr2) ^ g_heap_base_)) >> + kHalfWordShift); + } + + V8_INLINE static uintptr_t GetBase() { return g_heap_base_; } + + private: + friend class CagedHeap; + + static uintptr_t g_heap_base_; +}; + +} // namespace internal +} // namespace cppgc + +#endif // defined(CPPGC_CAGED_HEAP) + +#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ diff --git a/NativeScript/include/cppgc/internal/gc-info.h b/NativeScript/include/cppgc/internal/gc-info.h index 82a0d053..08ffd411 100644 --- a/NativeScript/include/cppgc/internal/gc-info.h +++ b/NativeScript/include/cppgc/internal/gc-info.h @@ -10,6 +10,7 @@ #include #include "cppgc/internal/finalizer-trait.h" +#include "cppgc/internal/logging.h" #include "cppgc/internal/name-trait.h" #include "cppgc/trace-trait.h" #include "v8config.h" // NOLINT(build/include_directory) @@ -20,12 +21,12 @@ namespace internal { using GCInfoIndex = uint16_t; struct V8_EXPORT EnsureGCInfoIndexTrait final { - // Acquires a new GC info object and returns the index. In addition, also - // updates `registered_index` atomically. + // Acquires a new GC info object and updates `registered_index` with the index + // that identifies that new info accordingly. template - V8_INLINE static GCInfoIndex EnsureIndex( + V8_INLINE static void EnsureIndex( std::atomic& registered_index) { - return EnsureGCInfoIndexTraitDispatch{}(registered_index); + EnsureGCInfoIndexTraitDispatch{}(registered_index); } private: @@ -34,39 +35,32 @@ struct V8_EXPORT EnsureGCInfoIndexTrait final { bool = NameTrait::HasNonHiddenName()> struct EnsureGCInfoIndexTraitDispatch; - static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, - TraceCallback, - FinalizationCallback, - NameCallback); - static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, - TraceCallback, - FinalizationCallback); - static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, - TraceCallback, NameCallback); - static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, - TraceCallback); - static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, - TraceCallback, - FinalizationCallback, - - NameCallback); - static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, - TraceCallback, - FinalizationCallback); - static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, - TraceCallback, - NameCallback); - static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, - TraceCallback); + static void V8_PRESERVE_MOST + EnsureGCInfoIndexPolymorphic(std::atomic&, TraceCallback, + FinalizationCallback, NameCallback); + static void V8_PRESERVE_MOST EnsureGCInfoIndexPolymorphic( + std::atomic&, TraceCallback, FinalizationCallback); + static void V8_PRESERVE_MOST EnsureGCInfoIndexPolymorphic( + std::atomic&, TraceCallback, NameCallback); + static void V8_PRESERVE_MOST + EnsureGCInfoIndexPolymorphic(std::atomic&, TraceCallback); + static void V8_PRESERVE_MOST + EnsureGCInfoIndexNonPolymorphic(std::atomic&, TraceCallback, + FinalizationCallback, NameCallback); + static void V8_PRESERVE_MOST EnsureGCInfoIndexNonPolymorphic( + std::atomic&, TraceCallback, FinalizationCallback); + static void V8_PRESERVE_MOST EnsureGCInfoIndexNonPolymorphic( + std::atomic&, TraceCallback, NameCallback); + static void V8_PRESERVE_MOST + EnsureGCInfoIndexNonPolymorphic(std::atomic&, TraceCallback); }; #define DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) \ template \ struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \ T, is_polymorphic, has_finalizer, has_non_hidden_name> { \ - V8_INLINE GCInfoIndex \ - operator()(std::atomic& registered_index) { \ - return function; \ + V8_INLINE void operator()(std::atomic& registered_index) { \ + function; \ } \ }; @@ -144,9 +138,16 @@ struct GCInfoTrait final { static_assert(sizeof(T), "T must be fully defined"); static std::atomic registered_index; // Uses zero initialization. - const GCInfoIndex index = registered_index.load(std::memory_order_acquire); - return index ? index - : EnsureGCInfoIndexTrait::EnsureIndex(registered_index); + GCInfoIndex index = registered_index.load(std::memory_order_acquire); + if (V8_UNLIKELY(!index)) { + EnsureGCInfoIndexTrait::EnsureIndex(registered_index); + // Slow path call uses V8_PRESERVE_MOST which does not support return + // values (also preserves RAX). Avoid out parameter by just reloading the + // value here which at this point is guaranteed to be set. + index = registered_index.load(std::memory_order_acquire); + CPPGC_DCHECK(index != 0); + } + return index; } }; diff --git a/NativeScript/include/cppgc/internal/member-storage.h b/NativeScript/include/cppgc/internal/member-storage.h new file mode 100644 index 00000000..0eb63820 --- /dev/null +++ b/NativeScript/include/cppgc/internal/member-storage.h @@ -0,0 +1,236 @@ +// Copyright 2022 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_CPPGC_INTERNAL_MEMBER_STORAGE_H_ +#define INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ + +#include +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/logging.h" +#include "cppgc/sentinel-pointer.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +#if defined(CPPGC_POINTER_COMPRESSION) + +#if defined(__clang__) +// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_ +// doesn't change (e.g. across calls) and thereby avoid redundant loads. +#define CPPGC_CONST __attribute__((const)) +#define CPPGC_REQUIRE_CONSTANT_INIT \ + __attribute__((require_constant_initialization)) +#else // defined(__clang__) +#define CPPGC_CONST +#define CPPGC_REQUIRE_CONSTANT_INIT +#endif // defined(__clang__) + +class CageBaseGlobal final { + public: + V8_INLINE CPPGC_CONST static uintptr_t Get() { + CPPGC_DCHECK(IsBaseConsistent()); + return g_base_; + } + + V8_INLINE CPPGC_CONST static bool IsSet() { + CPPGC_DCHECK(IsBaseConsistent()); + return (g_base_ & ~kLowerHalfWordMask) != 0; + } + + private: + // We keep the lower halfword as ones to speed up decompression. + static constexpr uintptr_t kLowerHalfWordMask = + (api_constants::kCagedHeapReservationAlignment - 1); + + static V8_EXPORT uintptr_t g_base_ CPPGC_REQUIRE_CONSTANT_INIT; + + CageBaseGlobal() = delete; + + V8_INLINE static bool IsBaseConsistent() { + return kLowerHalfWordMask == (g_base_ & kLowerHalfWordMask); + } + + friend class CageBaseGlobalUpdater; +}; + +#undef CPPGC_REQUIRE_CONSTANT_INIT +#undef CPPGC_CONST + +class V8_TRIVIAL_ABI CompressedPointer final { + public: + using IntegralType = uint32_t; + + V8_INLINE CompressedPointer() : value_(0u) {} + V8_INLINE explicit CompressedPointer(const void* ptr) + : value_(Compress(ptr)) {} + V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {} + V8_INLINE explicit CompressedPointer(SentinelPointer) + : value_(kCompressedSentinel) {} + + V8_INLINE const void* Load() const { return Decompress(value_); } + V8_INLINE const void* LoadAtomic() const { + return Decompress( + reinterpret_cast&>(value_).load( + std::memory_order_relaxed)); + } + + V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); } + V8_INLINE void StoreAtomic(const void* value) { + reinterpret_cast&>(value_).store( + Compress(value), std::memory_order_relaxed); + } + + V8_INLINE void Clear() { value_ = 0u; } + V8_INLINE bool IsCleared() const { return !value_; } + + V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; } + + V8_INLINE uint32_t GetAsInteger() const { return value_; } + + V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) { + return a.value_ == b.value_; + } + V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) { + return a.value_ != b.value_; + } + V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) { + return a.value_ < b.value_; + } + V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) { + return a.value_ <= b.value_; + } + V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) { + return a.value_ > b.value_; + } + V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) { + return a.value_ >= b.value_; + } + + static V8_INLINE IntegralType Compress(const void* ptr) { + static_assert( + SentinelPointer::kSentinelValue == 0b10, + "The compression scheme relies on the sentinel encoded as 0b10"); + static constexpr size_t kGigaCageMask = + ~(api_constants::kCagedHeapReservationAlignment - 1); + + CPPGC_DCHECK(CageBaseGlobal::IsSet()); + const uintptr_t base = CageBaseGlobal::Get(); + CPPGC_DCHECK(!ptr || ptr == kSentinelPointer || + (base & kGigaCageMask) == + (reinterpret_cast(ptr) & kGigaCageMask)); + +#if defined(CPPGC_2GB_CAGE) + // Truncate the pointer. + auto compressed = + static_cast(reinterpret_cast(ptr)); +#else // !defined(CPPGC_2GB_CAGE) + const auto uptr = reinterpret_cast(ptr); + // Shift the pointer by one and truncate. + auto compressed = static_cast(uptr >> 1); +#endif // !defined(CPPGC_2GB_CAGE) + // Normal compressed pointers must have the MSB set. + CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) || + (compressed & (1 << 31))); + return compressed; + } + + static V8_INLINE void* Decompress(IntegralType ptr) { + CPPGC_DCHECK(CageBaseGlobal::IsSet()); + const uintptr_t base = CageBaseGlobal::Get(); + // Treat compressed pointer as signed and cast it to uint64_t, which will + // sign-extend it. +#if defined(CPPGC_2GB_CAGE) + const uint64_t mask = static_cast(static_cast(ptr)); +#else // !defined(CPPGC_2GB_CAGE) + // Then, shift the result by one. It's important to shift the unsigned + // value, as otherwise it would result in undefined behavior. + const uint64_t mask = static_cast(static_cast(ptr)) << 1; +#endif // !defined(CPPGC_2GB_CAGE) + return reinterpret_cast(mask & base); + } + + private: +#if defined(CPPGC_2GB_CAGE) + static constexpr IntegralType kCompressedSentinel = + SentinelPointer::kSentinelValue; +#else // !defined(CPPGC_2GB_CAGE) + static constexpr IntegralType kCompressedSentinel = + SentinelPointer::kSentinelValue >> 1; +#endif // !defined(CPPGC_2GB_CAGE) + // All constructors initialize `value_`. Do not add a default value here as it + // results in a non-atomic write on some builds, even when the atomic version + // of the constructor is used. + IntegralType value_; +}; + +#endif // defined(CPPGC_POINTER_COMPRESSION) + +class V8_TRIVIAL_ABI RawPointer final { + public: + using IntegralType = uintptr_t; + + V8_INLINE RawPointer() : ptr_(nullptr) {} + V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {} + + V8_INLINE const void* Load() const { return ptr_; } + V8_INLINE const void* LoadAtomic() const { + return reinterpret_cast&>(ptr_).load( + std::memory_order_relaxed); + } + + V8_INLINE void Store(const void* ptr) { ptr_ = ptr; } + V8_INLINE void StoreAtomic(const void* ptr) { + reinterpret_cast&>(ptr_).store( + ptr, std::memory_order_relaxed); + } + + V8_INLINE void Clear() { ptr_ = nullptr; } + V8_INLINE bool IsCleared() const { return !ptr_; } + + V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; } + + V8_INLINE uintptr_t GetAsInteger() const { + return reinterpret_cast(ptr_); + } + + V8_INLINE friend bool operator==(RawPointer a, RawPointer b) { + return a.ptr_ == b.ptr_; + } + V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) { + return a.ptr_ != b.ptr_; + } + V8_INLINE friend bool operator<(RawPointer a, RawPointer b) { + return a.ptr_ < b.ptr_; + } + V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) { + return a.ptr_ <= b.ptr_; + } + V8_INLINE friend bool operator>(RawPointer a, RawPointer b) { + return a.ptr_ > b.ptr_; + } + V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) { + return a.ptr_ >= b.ptr_; + } + + private: + // All constructors initialize `ptr_`. Do not add a default value here as it + // results in a non-atomic write on some builds, even when the atomic version + // of the constructor is used. + const void* ptr_; +}; + +#if defined(CPPGC_POINTER_COMPRESSION) +using MemberStorage = CompressedPointer; +#else // !defined(CPPGC_POINTER_COMPRESSION) +using MemberStorage = RawPointer; +#endif // !defined(CPPGC_POINTER_COMPRESSION) + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ diff --git a/NativeScript/include/cppgc/internal/name-trait.h b/NativeScript/include/cppgc/internal/name-trait.h index 32a33478..1d927a9d 100644 --- a/NativeScript/include/cppgc/internal/name-trait.h +++ b/NativeScript/include/cppgc/internal/name-trait.h @@ -6,6 +6,7 @@ #define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ #include +#include #include #include "cppgc/name-provider.h" @@ -58,6 +59,11 @@ struct HeapObjectName { bool name_was_hidden; }; +enum class HeapObjectNameForUnnamedObject : uint8_t { + kUseClassNameIfSupported, + kUseHiddenName, +}; + class V8_EXPORT NameTraitBase { protected: static HeapObjectName GetNameFromTypeSignature(const char*); @@ -78,16 +84,24 @@ class NameTrait final : public NameTraitBase { #endif // !CPPGC_SUPPORTS_OBJECT_NAMES } - static HeapObjectName GetName(const void* obj) { - return GetNameFor(static_cast(obj)); + static HeapObjectName GetName( + const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode) { + return GetNameFor(static_cast(obj), name_retrieval_mode); } private: - static HeapObjectName GetNameFor(const NameProvider* name_provider) { + static HeapObjectName GetNameFor(const NameProvider* name_provider, + HeapObjectNameForUnnamedObject) { + // Objects inheriting from `NameProvider` are not considered unnamed as + // users already provided a name for them. return {name_provider->GetHumanReadableName(), false}; } - static HeapObjectName GetNameFor(...) { + static HeapObjectName GetNameFor( + const void*, HeapObjectNameForUnnamedObject name_retrieval_mode) { + if (name_retrieval_mode == HeapObjectNameForUnnamedObject::kUseHiddenName) + return {NameProvider::kHiddenName, true}; + #if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME return {GetTypename(), false}; #elif CPPGC_SUPPORTS_OBJECT_NAMES @@ -102,7 +116,7 @@ class NameTrait final : public NameTraitBase { static const HeapObjectName leaky_name = GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE); - return {leaky_name, false}; + return leaky_name; #undef PRETTY_FUNCTION_VALUE @@ -112,7 +126,8 @@ class NameTrait final : public NameTraitBase { } }; -using NameCallback = HeapObjectName (*)(const void*); +using NameCallback = HeapObjectName (*)(const void*, + HeapObjectNameForUnnamedObject); } // namespace internal } // namespace cppgc diff --git a/NativeScript/include/cppgc/internal/persistent-node.h b/NativeScript/include/cppgc/internal/persistent-node.h index 22b4cf09..d22692a7 100644 --- a/NativeScript/include/cppgc/internal/persistent-node.h +++ b/NativeScript/include/cppgc/internal/persistent-node.h @@ -14,13 +14,11 @@ #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { - -class Visitor; - namespace internal { class CrossThreadPersistentRegion; class FatalOutOfMemoryHandler; +class RootVisitor; // PersistentNode represents a variant of two states: // 1) traceable node with a back pointer to the Persistent object; @@ -32,7 +30,7 @@ class PersistentNode final { PersistentNode(const PersistentNode&) = delete; PersistentNode& operator=(const PersistentNode&) = delete; - void InitializeAsUsedNode(void* owner, TraceCallback trace) { + void InitializeAsUsedNode(void* owner, TraceRootCallback trace) { CPPGC_DCHECK(trace); owner_ = owner; trace_ = trace; @@ -53,9 +51,9 @@ class PersistentNode final { return next_; } - void Trace(Visitor* visitor) const { + void Trace(RootVisitor& root_visitor) const { CPPGC_DCHECK(IsUsed()); - trace_(visitor, owner_); + trace_(root_visitor, owner_); } bool IsUsed() const { return trace_; } @@ -73,7 +71,7 @@ class PersistentNode final { void* owner_ = nullptr; PersistentNode* next_; }; - TraceCallback trace_ = nullptr; + TraceRootCallback trace_ = nullptr; }; class V8_EXPORT PersistentRegionBase { @@ -86,7 +84,7 @@ class V8_EXPORT PersistentRegionBase { PersistentRegionBase(const PersistentRegionBase&) = delete; PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; - void Trace(Visitor*); + void Iterate(RootVisitor&); size_t NodesInUse() const; @@ -96,7 +94,7 @@ class V8_EXPORT PersistentRegionBase { explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler); PersistentNode* TryAllocateNodeFromFreeList(void* owner, - TraceCallback trace) { + TraceRootCallback trace) { PersistentNode* node = nullptr; if (V8_LIKELY(free_list_head_)) { node = free_list_head_; @@ -118,7 +116,7 @@ class V8_EXPORT PersistentRegionBase { } PersistentNode* RefillFreeListAndAllocateNode(void* owner, - TraceCallback trace); + TraceRootCallback trace); private: template @@ -145,7 +143,7 @@ class V8_EXPORT PersistentRegion final : public PersistentRegionBase { PersistentRegion(const PersistentRegion&) = delete; PersistentRegion& operator=(const PersistentRegion&) = delete; - V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { CPPGC_DCHECK(IsCreationThread()); auto* node = TryAllocateNodeFromFreeList(owner, trace); if (V8_LIKELY(node)) return node; @@ -189,7 +187,7 @@ class V8_EXPORT CrossThreadPersistentRegion final CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) = delete; - V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { PersistentRegionLock::AssertLocked(); auto* node = TryAllocateNodeFromFreeList(owner, trace); if (V8_LIKELY(node)) return node; @@ -202,7 +200,7 @@ class V8_EXPORT CrossThreadPersistentRegion final PersistentRegionBase::FreeNode(node); } - void Trace(Visitor*); + void Iterate(RootVisitor&); size_t NodesInUse() const; diff --git a/NativeScript/include/cppgc/internal/pointer-policies.h b/NativeScript/include/cppgc/internal/pointer-policies.h index 853d7031..e67da040 100644 --- a/NativeScript/include/cppgc/internal/pointer-policies.h +++ b/NativeScript/include/cppgc/internal/pointer-policies.h @@ -8,6 +8,7 @@ #include #include +#include "cppgc/internal/member-storage.h" #include "cppgc/internal/write-barrier.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/source-location.h" @@ -27,15 +28,44 @@ class WeakMemberTag; class UntracedMemberTag; struct DijkstraWriteBarrierPolicy { - static void InitializingBarrier(const void*, const void*) { + V8_INLINE static void InitializingBarrier(const void*, const void*) { // Since in initializing writes the source object is always white, having no // barrier doesn't break the tri-color invariant. } - static void AssigningBarrier(const void* slot, const void* value) { + + V8_INLINE static void AssigningBarrier(const void* slot, const void* value) { +#ifdef CPPGC_SLIM_WRITE_BARRIER + if (V8_UNLIKELY(WriteBarrier::IsEnabled())) + WriteBarrier::CombinedWriteBarrierSlow(slot); +#else // !CPPGC_SLIM_WRITE_BARRIER + WriteBarrier::Params params; + const WriteBarrier::Type type = + WriteBarrier::GetWriteBarrierType(slot, value, params); + WriteBarrier(type, params, slot, value); +#endif // !CPPGC_SLIM_WRITE_BARRIER + } + + V8_INLINE static void AssigningBarrier(const void* slot, + MemberStorage storage) { +#ifdef CPPGC_SLIM_WRITE_BARRIER + if (V8_UNLIKELY(WriteBarrier::IsEnabled())) + WriteBarrier::CombinedWriteBarrierSlow(slot); +#else // !CPPGC_SLIM_WRITE_BARRIER WriteBarrier::Params params; - switch (WriteBarrier::GetWriteBarrierType(slot, value, params)) { + const WriteBarrier::Type type = + WriteBarrier::GetWriteBarrierType(slot, storage, params); + WriteBarrier(type, params, slot, storage.Load()); +#endif // !CPPGC_SLIM_WRITE_BARRIER + } + + private: + V8_INLINE static void WriteBarrier(WriteBarrier::Type type, + const WriteBarrier::Params& params, + const void* slot, const void* value) { + switch (type) { case WriteBarrier::Type::kGenerational: - WriteBarrier::GenerationalBarrier(params, slot); + WriteBarrier::GenerationalBarrier< + WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot); break; case WriteBarrier::Type::kMarking: WriteBarrier::DijkstraMarkingBarrier(params, value); @@ -47,8 +77,9 @@ struct DijkstraWriteBarrierPolicy { }; struct NoWriteBarrierPolicy { - static void InitializingBarrier(const void*, const void*) {} - static void AssigningBarrier(const void*, const void*) {} + V8_INLINE static void InitializingBarrier(const void*, const void*) {} + V8_INLINE static void AssigningBarrier(const void*, const void*) {} + V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {} }; class V8_EXPORT SameThreadEnabledCheckingPolicyBase { @@ -89,7 +120,7 @@ class V8_EXPORT SameThreadEnabledCheckingPolicy class DisabledCheckingPolicy { protected: - void CheckPointer(const void*) {} + V8_INLINE void CheckPointer(const void*) {} }; #ifdef DEBUG diff --git a/NativeScript/include/cppgc/internal/write-barrier.h b/NativeScript/include/cppgc/internal/write-barrier.h index bfabc31e..80c6ee33 100644 --- a/NativeScript/include/cppgc/internal/write-barrier.h +++ b/NativeScript/include/cppgc/internal/write-barrier.h @@ -8,9 +8,12 @@ #include #include +#include "cppgc/heap-handle.h" #include "cppgc/heap-state.h" #include "cppgc/internal/api-constants.h" #include "cppgc/internal/atomic-entry-flag.h" +#include "cppgc/internal/base-page-handle.h" +#include "cppgc/internal/member-storage.h" #include "cppgc/platform.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/trace-trait.h" @@ -18,6 +21,7 @@ #if defined(CPPGC_CAGED_HEAP) #include "cppgc/internal/caged-heap-local-data.h" +#include "cppgc/internal/caged-heap.h" #endif namespace cppgc { @@ -40,16 +44,18 @@ class V8_EXPORT WriteBarrier final { kGenerational, }; + enum class GenerationalBarrierType : uint8_t { + kPreciseSlot, + kPreciseUncompressedSlot, + kImpreciseSlot, + }; + struct Params { HeapHandle* heap = nullptr; #if V8_ENABLE_CHECKS Type type = Type::kNone; #endif // !V8_ENABLE_CHECKS #if defined(CPPGC_CAGED_HEAP) - uintptr_t start = 0; - CagedHeapLocalData& caged_heap() const { - return *reinterpret_cast(start); - } uintptr_t slot_offset = 0; uintptr_t value_offset = 0; #endif // CPPGC_CAGED_HEAP @@ -63,6 +69,9 @@ class V8_EXPORT WriteBarrier final { // Returns the required write barrier for a given `slot` and `value`. static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value, Params& params); + // Returns the required write barrier for a given `slot` and `value`. + static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage, + Params& params); // Returns the required write barrier for a given `slot`. template static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params, @@ -70,6 +79,14 @@ class V8_EXPORT WriteBarrier final { // Returns the required write barrier for a given `value`. static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params); +#ifdef CPPGC_SLIM_WRITE_BARRIER + // A write barrier that combines `GenerationalBarrier()` and + // `DijkstraMarkingBarrier()`. We only pass a single parameter here to clobber + // as few registers as possible. + static V8_NOINLINE void V8_PRESERVE_MOST + CombinedWriteBarrierSlow(const void* slot); +#endif // CPPGC_SLIM_WRITE_BARRIER + static V8_INLINE void DijkstraMarkingBarrier(const Params& params, const void* object); static V8_INLINE void DijkstraMarkingBarrierRange( @@ -78,15 +95,13 @@ class V8_EXPORT WriteBarrier final { static V8_INLINE void SteeleMarkingBarrier(const Params& params, const void* object); #if defined(CPPGC_YOUNG_GENERATION) + template static V8_INLINE void GenerationalBarrier(const Params& params, const void* slot); - static V8_INLINE void GenerationalBarrierForSourceObject( - const Params& params, const void* inner_pointer); #else // !CPPGC_YOUNG_GENERATION + template static V8_INLINE void GenerationalBarrier(const Params& params, - const void* slot) {} - static V8_INLINE void GenerationalBarrierForSourceObject( - const Params& params, const void* inner_pointer) {} + const void* slot){} #endif // CPPGC_YOUNG_GENERATION #if V8_ENABLE_CHECKS @@ -95,12 +110,10 @@ class V8_EXPORT WriteBarrier final { static void CheckParams(Type expected_type, const Params& params) {} #endif // !V8_ENABLE_CHECKS - // The IncrementalOrConcurrentUpdater class allows cppgc internal to update - // |incremental_or_concurrent_marking_flag_|. - class IncrementalOrConcurrentMarkingFlagUpdater; - static bool IsAnyIncrementalOrConcurrentMarking() { - return incremental_or_concurrent_marking_flag_.MightBeEntered(); - } + // The FlagUpdater class allows cppgc internal to update + // |write_barrier_enabled_|. + class FlagUpdater; + static bool IsEnabled() { return write_barrier_enabled_.MightBeEntered(); } private: WriteBarrier() = delete; @@ -125,17 +138,23 @@ class V8_EXPORT WriteBarrier final { static CagedHeapLocalData& GetLocalData(HeapHandle&); static void GenerationalBarrierSlow(const CagedHeapLocalData& local_data, const AgeTable& age_table, - const void* slot, uintptr_t value_offset); + const void* slot, uintptr_t value_offset, + HeapHandle* heap_handle); + static void GenerationalBarrierForUncompressedSlotSlow( + const CagedHeapLocalData& local_data, const AgeTable& age_table, + const void* slot, uintptr_t value_offset, HeapHandle* heap_handle); static void GenerationalBarrierForSourceObjectSlow( - const CagedHeapLocalData& local_data, const void* object); + const CagedHeapLocalData& local_data, const void* object, + HeapHandle* heap_handle); #endif // CPPGC_YOUNG_GENERATION - static AtomicEntryFlag incremental_or_concurrent_marking_flag_; + static AtomicEntryFlag write_barrier_enabled_; }; template V8_INLINE WriteBarrier::Type SetAndReturnType(WriteBarrier::Params& params) { - if (type == WriteBarrier::Type::kNone) return WriteBarrier::Type::kNone; + if constexpr (type == WriteBarrier::Type::kNone) + return WriteBarrier::Type::kNone; #if V8_ENABLE_CHECKS params.type = type; #endif // !V8_ENABLE_CHECKS @@ -152,6 +171,13 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { return ValueModeDispatch::Get(slot, value, params, callback); } + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return ValueModeDispatch::Get(slot, value, params, callback); + } + template static V8_INLINE WriteBarrier::Type Get(const void* value, WriteBarrier::Params& params, @@ -166,69 +192,77 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value, WriteBarrier::Params& params, HeapHandleCallback) { - if (!TryGetCagedHeap(value, value, params)) { - return WriteBarrier::Type::kNone; - } - if (V8_UNLIKELY(params.caged_heap().is_incremental_marking_in_progress)) { + const bool within_cage = CagedHeapBase::IsWithinCage(value); + if (!within_cage) return WriteBarrier::Type::kNone; + + // We know that |value| points either within the normal page or to the + // beginning of large-page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(value)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_UNLIKELY(heap_handle.is_incremental_marking_in_progress())) { return SetAndReturnType(params); } + return SetAndReturnType(params); } template struct ValueModeDispatch; - - static V8_INLINE bool TryGetCagedHeap(const void* slot, const void* value, - WriteBarrier::Params& params) { - // TODO(chromium:1056170): Check if the null check can be folded in with - // the rest of the write barrier. - if (!value) return false; - params.start = reinterpret_cast(value) & - ~(api_constants::kCagedHeapReservationAlignment - 1); - const uintptr_t slot_offset = - reinterpret_cast(slot) - params.start; - if (slot_offset > api_constants::kCagedHeapReservationSize) { - // Check if slot is on stack or value is sentinel or nullptr. This relies - // on the fact that kSentinelPointer is encoded as 0x1. - return false; - } - return true; - } - - // Returns whether marking is in progress. If marking is not in progress - // sets the start of the cage accordingly. - // - // TODO(chromium:1056170): Create fast path on API. - static bool IsMarking(const HeapHandle&, WriteBarrier::Params&); }; template <> struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< WriteBarrier::ValueMode::kValuePresent> { + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, + MemberStorage storage, + WriteBarrier::Params& params, + HeapHandleCallback) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) + return SetAndReturnType(params); + + return BarrierEnabledGet(slot, storage.Load(), params); + } + template static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, WriteBarrier::Params& params, HeapHandleCallback) { -#if !defined(CPPGC_YOUNG_GENERATION) - if (V8_LIKELY(!WriteBarrier::IsAnyIncrementalOrConcurrentMarking())) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) return SetAndReturnType(params); - } -#endif // !CPPGC_YOUNG_GENERATION - bool within_cage = TryGetCagedHeap(slot, value, params); - if (!within_cage) { - return WriteBarrier::Type::kNone; - } - if (V8_LIKELY(!params.caged_heap().is_incremental_marking_in_progress)) { + + return BarrierEnabledGet(slot, value, params); + } + + private: + static V8_INLINE WriteBarrier::Type BarrierEnabledGet( + const void* slot, const void* value, WriteBarrier::Params& params) { + const bool within_cage = CagedHeapBase::AreWithinCage(slot, value); + if (!within_cage) return WriteBarrier::Type::kNone; + + // We know that |value| points either within the normal page or to the + // beginning of large-page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(value)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_LIKELY(!heap_handle.is_incremental_marking_in_progress())) { #if defined(CPPGC_YOUNG_GENERATION) - params.heap = reinterpret_cast(params.start); - params.slot_offset = reinterpret_cast(slot) - params.start; - params.value_offset = reinterpret_cast(value) - params.start; + if (!heap_handle.is_young_generation_enabled()) + return WriteBarrier::Type::kNone; + params.heap = &heap_handle; + params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); + params.value_offset = CagedHeapBase::OffsetFromAddress(value); return SetAndReturnType(params); #else // !CPPGC_YOUNG_GENERATION return SetAndReturnType(params); #endif // !CPPGC_YOUNG_GENERATION } - params.heap = reinterpret_cast(params.start); + + // Use marking barrier. + params.heap = &heap_handle; return SetAndReturnType(params); } }; @@ -240,28 +274,28 @@ struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< static V8_INLINE WriteBarrier::Type Get(const void* slot, const void*, WriteBarrier::Params& params, HeapHandleCallback callback) { -#if defined(CPPGC_YOUNG_GENERATION) + if (V8_LIKELY(!WriteBarrier::IsEnabled())) + return SetAndReturnType(params); + HeapHandle& handle = callback(); - if (V8_LIKELY(!IsMarking(handle, params))) { - // params.start is populated by IsMarking(). +#if defined(CPPGC_YOUNG_GENERATION) + if (V8_LIKELY(!handle.is_incremental_marking_in_progress())) { + if (!handle.is_young_generation_enabled()) { + return WriteBarrier::Type::kNone; + } params.heap = &handle; - params.slot_offset = reinterpret_cast(slot) - params.start; - // params.value_offset stays 0. - if (params.slot_offset > api_constants::kCagedHeapReservationSize) { - // Check if slot is on stack. + // Check if slot is on stack. + if (V8_UNLIKELY(!CagedHeapBase::IsWithinCage(slot))) { return SetAndReturnType(params); } + params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); return SetAndReturnType(params); } -#else // !CPPGC_YOUNG_GENERATION - if (V8_LIKELY(!WriteBarrier::IsAnyIncrementalOrConcurrentMarking())) { - return SetAndReturnType(params); - } - HeapHandle& handle = callback(); - if (V8_UNLIKELY(!subtle::HeapState::IsMarking(handle))) { +#else // !defined(CPPGC_YOUNG_GENERATION) + if (V8_UNLIKELY(!handle.is_incremental_marking_in_progress())) { return SetAndReturnType(params); } -#endif // !CPPGC_YOUNG_GENERATION +#endif // !defined(CPPGC_YOUNG_GENERATION) params.heap = &handle; return SetAndReturnType(params); } @@ -278,6 +312,16 @@ class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { return ValueModeDispatch::Get(slot, value, params, callback); } + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + // `MemberStorage` will always be `RawPointer` for non-caged heap builds. + // Just convert to `void*` in this case. + return ValueModeDispatch::Get(slot, value.Load(), params, + callback); + } + template static V8_INLINE WriteBarrier::Type Get(const void* value, WriteBarrier::Params& params, @@ -291,11 +335,6 @@ class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { template struct ValueModeDispatch; - // TODO(chromium:1056170): Create fast path on API. - static bool IsMarking(const void*, HeapHandle**); - // TODO(chromium:1056170): Create fast path on API. - static bool IsMarking(HeapHandle&); - WriteBarrierTypeForNonCagedHeapPolicy() = delete; }; @@ -310,10 +349,16 @@ struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< if (object <= static_cast(kSentinelPointer)) { return SetAndReturnType(params); } - if (V8_LIKELY(!WriteBarrier::IsAnyIncrementalOrConcurrentMarking())) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) { return SetAndReturnType(params); } - if (IsMarking(object, ¶ms.heap)) { + // We know that |object| is within the normal page or in the beginning of a + // large page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(object)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_LIKELY(heap_handle.is_incremental_marking_in_progress())) { return SetAndReturnType(params); } return SetAndReturnType(params); @@ -327,9 +372,9 @@ struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< static V8_INLINE WriteBarrier::Type Get(const void*, const void*, WriteBarrier::Params& params, HeapHandleCallback callback) { - if (V8_UNLIKELY(WriteBarrier::IsAnyIncrementalOrConcurrentMarking())) { + if (V8_UNLIKELY(WriteBarrier::IsEnabled())) { HeapHandle& handle = callback(); - if (IsMarking(handle)) { + if (V8_LIKELY(handle.is_incremental_marking_in_progress())) { params.heap = &handle; return SetAndReturnType(params); } @@ -345,6 +390,13 @@ WriteBarrier::Type WriteBarrier::GetWriteBarrierType( params, []() {}); } +// static +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* slot, MemberStorage value, WriteBarrier::Params& params) { + return WriteBarrierTypePolicy::Get(slot, value, + params, []() {}); +} + // static template WriteBarrier::Type WriteBarrier::GetWriteBarrierType( @@ -397,34 +449,32 @@ void WriteBarrier::SteeleMarkingBarrier(const Params& params, } #if defined(CPPGC_YOUNG_GENERATION) -// static -void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) { - CheckParams(Type::kGenerational, params); - - const CagedHeapLocalData& local_data = params.caged_heap(); - const AgeTable& age_table = local_data.age_table; - - // Bail out if the slot is in young generation. - if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung)) - return; - - GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset); -} // static -void WriteBarrier::GenerationalBarrierForSourceObject( - const Params& params, const void* inner_pointer) { +template +void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) { CheckParams(Type::kGenerational, params); - const CagedHeapLocalData& local_data = params.caged_heap(); + const CagedHeapLocalData& local_data = CagedHeapLocalData::Get(); const AgeTable& age_table = local_data.age_table; - // Assume that if the first element is in young generation, the whole range is - // in young generation. + // Bail out if the slot (precise or imprecise) is in young generation. if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung)) return; - GenerationalBarrierForSourceObjectSlow(local_data, inner_pointer); + // Dispatch between different types of barriers. + // TODO(chromium:1029379): Consider reload local_data in the slow path to + // reduce register pressure. + if constexpr (type == GenerationalBarrierType::kPreciseSlot) { + GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset, + params.heap); + } else if constexpr (type == + GenerationalBarrierType::kPreciseUncompressedSlot) { + GenerationalBarrierForUncompressedSlotSlow( + local_data, age_table, slot, params.value_offset, params.heap); + } else { + GenerationalBarrierForSourceObjectSlow(local_data, slot, params.heap); + } } #endif // !CPPGC_YOUNG_GENERATION diff --git a/NativeScript/include/cppgc/liveness-broker.h b/NativeScript/include/cppgc/liveness-broker.h index c94eef0d..2c94f1c0 100644 --- a/NativeScript/include/cppgc/liveness-broker.h +++ b/NativeScript/include/cppgc/liveness-broker.h @@ -7,6 +7,7 @@ #include "cppgc/heap.h" #include "cppgc/member.h" +#include "cppgc/sentinel-pointer.h" #include "cppgc/trace-trait.h" #include "v8config.h" // NOLINT(build/include_directory) @@ -44,24 +45,24 @@ class V8_EXPORT LivenessBroker final { public: template bool IsHeapObjectAlive(const T* object) const { - // nullptr objects are considered alive to allow weakness to be used from + // - nullptr objects are considered alive to allow weakness to be used from // stack while running into a conservative GC. Treating nullptr as dead - // would mean that e.g. custom collectins could not be strongified on stack. - return !object || + // would mean that e.g. custom collections could not be strongified on + // stack. + // - Sentinel pointers are also preserved in weakness and not cleared. + return !object || object == kSentinelPointer || IsHeapObjectAliveImpl( TraceTrait::GetTraceDescriptor(object).base_object_payload); } template bool IsHeapObjectAlive(const WeakMember& weak_member) const { - return (weak_member != kSentinelPointer) && - IsHeapObjectAlive(weak_member.Get()); + return IsHeapObjectAlive(weak_member.Get()); } template bool IsHeapObjectAlive(const UntracedMember& untraced_member) const { - return (untraced_member != kSentinelPointer) && - IsHeapObjectAlive(untraced_member.Get()); + return IsHeapObjectAlive(untraced_member.Get()); } private: diff --git a/NativeScript/include/cppgc/member.h b/NativeScript/include/cppgc/member.h index 66a8cfd8..9bc38363 100644 --- a/NativeScript/include/cppgc/member.h +++ b/NativeScript/include/cppgc/member.h @@ -9,6 +9,8 @@ #include #include +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/member-storage.h" #include "cppgc/internal/pointer-policies.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/type-traits.h" @@ -16,177 +18,247 @@ namespace cppgc { +namespace subtle { +class HeapConsistency; +} // namespace subtle + class Visitor; namespace internal { // MemberBase always refers to the object as const object and defers to // BasicMember on casting to the right type as needed. -class MemberBase { +class V8_TRIVIAL_ABI MemberBase { + public: +#if defined(CPPGC_POINTER_COMPRESSION) + using RawStorage = CompressedPointer; +#else // !defined(CPPGC_POINTER_COMPRESSION) + using RawStorage = RawPointer; +#endif // !defined(CPPGC_POINTER_COMPRESSION) protected: struct AtomicInitializerTag {}; - MemberBase() : raw_(nullptr) {} - explicit MemberBase(const void* value) : raw_(value) {} - MemberBase(const void* value, AtomicInitializerTag) { SetRawAtomic(value); } + V8_INLINE MemberBase() = default; + V8_INLINE explicit MemberBase(const void* value) : raw_(value) {} + V8_INLINE MemberBase(const void* value, AtomicInitializerTag) { + SetRawAtomic(value); + } - const void** GetRawSlot() const { return &raw_; } - const void* GetRaw() const { return raw_; } - void SetRaw(void* value) { raw_ = value; } + V8_INLINE explicit MemberBase(RawStorage raw) : raw_(raw) {} + V8_INLINE explicit MemberBase(std::nullptr_t) : raw_(nullptr) {} + V8_INLINE explicit MemberBase(SentinelPointer s) : raw_(s) {} - const void* GetRawAtomic() const { - return reinterpret_cast*>(&raw_)->load( - std::memory_order_relaxed); + V8_INLINE const void** GetRawSlot() const { + return reinterpret_cast(const_cast(this)); } - void SetRawAtomic(const void* value) { - reinterpret_cast*>(&raw_)->store( - value, std::memory_order_relaxed); + V8_INLINE const void* GetRaw() const { return raw_.Load(); } + V8_INLINE void SetRaw(void* value) { raw_.Store(value); } + + V8_INLINE const void* GetRawAtomic() const { return raw_.LoadAtomic(); } + V8_INLINE void SetRawAtomic(const void* value) { raw_.StoreAtomic(value); } + + V8_INLINE RawStorage GetRawStorage() const { return raw_; } + V8_INLINE void SetRawStorageAtomic(RawStorage other) { + reinterpret_cast&>(raw_).store( + other, std::memory_order_relaxed); } - void ClearFromGC() const { raw_ = nullptr; } + V8_INLINE bool IsCleared() const { return raw_.IsCleared(); } + + V8_INLINE void ClearFromGC() const { raw_.Clear(); } private: - // All constructors initialize `raw_`. Do not add a default value here as it - // results in a non-atomic write on some builds, even when the atomic version - // of the constructor is used. - mutable const void* raw_; + friend class MemberDebugHelper; + + mutable RawStorage raw_; }; // The basic class from which all Member classes are 'generated'. template -class BasicMember final : private MemberBase, private CheckingPolicy { +class V8_TRIVIAL_ABI BasicMember final : private MemberBase, + private CheckingPolicy { public: using PointeeType = T; - constexpr BasicMember() = default; - constexpr BasicMember(std::nullptr_t) {} // NOLINT - BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT - BasicMember(T* raw) : MemberBase(raw) { // NOLINT - InitializingWriteBarrier(); + V8_INLINE constexpr BasicMember() = default; + V8_INLINE constexpr BasicMember(std::nullptr_t) {} // NOLINT + V8_INLINE BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT + V8_INLINE BasicMember(T* raw) : MemberBase(raw) { // NOLINT + InitializingWriteBarrier(raw); this->CheckPointer(Get()); } - BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT + V8_INLINE BasicMember(T& raw) // NOLINT + : BasicMember(&raw) {} + // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to // initialize using atomic assignments. This is required for preventing // data races with concurrent marking. using AtomicInitializerTag = MemberBase::AtomicInitializerTag; - BasicMember(std::nullptr_t, AtomicInitializerTag atomic) + V8_INLINE BasicMember(std::nullptr_t, AtomicInitializerTag atomic) : MemberBase(nullptr, atomic) {} - BasicMember(SentinelPointer s, AtomicInitializerTag atomic) + V8_INLINE BasicMember(SentinelPointer s, AtomicInitializerTag atomic) : MemberBase(s, atomic) {} - BasicMember(T* raw, AtomicInitializerTag atomic) : MemberBase(raw, atomic) { - InitializingWriteBarrier(); + V8_INLINE BasicMember(T* raw, AtomicInitializerTag atomic) + : MemberBase(raw, atomic) { + InitializingWriteBarrier(raw); this->CheckPointer(Get()); } - BasicMember(T& raw, AtomicInitializerTag atomic) + V8_INLINE BasicMember(T& raw, AtomicInitializerTag atomic) : BasicMember(&raw, atomic) {} + // Copy ctor. - BasicMember(const BasicMember& other) : BasicMember(other.Get()) {} - // Allow heterogeneous construction. + V8_INLINE BasicMember(const BasicMember& other) + : BasicMember(other.GetRawStorage()) {} + + // Heterogeneous copy constructors. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. template ::value>> - BasicMember( // NOLINT + std::enable_if_t>* = nullptr> + V8_INLINE BasicMember( // NOLINT + const BasicMember& other) + : BasicMember(other.GetRawStorage()) {} + + template >* = nullptr> + V8_INLINE BasicMember( // NOLINT const BasicMember& other) : BasicMember(other.Get()) {} + // Move ctor. - BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) { + V8_INLINE BasicMember(BasicMember&& other) noexcept + : BasicMember(other.GetRawStorage()) { other.Clear(); } - // Allow heterogeneous move construction. + + // Heterogeneous move constructors. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. template ::value>> - BasicMember(BasicMember&& other) noexcept + std::enable_if_t>* = nullptr> + V8_INLINE BasicMember(BasicMember&& other) noexcept + : BasicMember(other.GetRawStorage()) { + other.Clear(); + } + + template >* = nullptr> + V8_INLINE BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) { other.Clear(); } + // Construction from Persistent. template ::value>> - BasicMember(const BasicPersistent& p) + V8_INLINE BasicMember(const BasicPersistent& p) : BasicMember(p.Get()) {} // Copy assignment. - BasicMember& operator=(const BasicMember& other) { - return operator=(other.Get()); + V8_INLINE BasicMember& operator=(const BasicMember& other) { + return operator=(other.GetRawStorage()); } - // Allow heterogeneous copy assignment. + + // Heterogeneous copy assignment. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. template ::value>> - BasicMember& operator=( + typename OtherCheckingPolicy> + V8_INLINE BasicMember& operator=( const BasicMember& other) { - return operator=(other.Get()); + if constexpr (internal::IsDecayedSameV) { + return operator=(other.GetRawStorage()); + } else { + static_assert(internal::IsStrictlyBaseOfV); + return operator=(other.Get()); + } } + // Move assignment. - BasicMember& operator=(BasicMember&& other) noexcept { - operator=(other.Get()); + V8_INLINE BasicMember& operator=(BasicMember&& other) noexcept { + operator=(other.GetRawStorage()); other.Clear(); return *this; } - // Heterogeneous move assignment. + + // Heterogeneous move assignment. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. template ::value>> - BasicMember& operator=(BasicMember&& other) noexcept { - operator=(other.Get()); + typename OtherCheckingPolicy> + V8_INLINE BasicMember& operator=( + BasicMember&& other) noexcept { + if constexpr (internal::IsDecayedSameV) { + operator=(other.GetRawStorage()); + } else { + static_assert(internal::IsStrictlyBaseOfV); + operator=(other.Get()); + } other.Clear(); return *this; } + // Assignment from Persistent. template ::value>> - BasicMember& operator=( + V8_INLINE BasicMember& operator=( const BasicPersistent& other) { return operator=(other.Get()); } - BasicMember& operator=(T* other) { + + V8_INLINE BasicMember& operator=(T* other) { SetRawAtomic(other); - AssigningWriteBarrier(); + AssigningWriteBarrier(other); this->CheckPointer(Get()); return *this; } - BasicMember& operator=(std::nullptr_t) { + + V8_INLINE BasicMember& operator=(std::nullptr_t) { Clear(); return *this; } - BasicMember& operator=(SentinelPointer s) { + V8_INLINE BasicMember& operator=(SentinelPointer s) { SetRawAtomic(s); return *this; } template - void Swap(BasicMember& other) { - T* tmp = Get(); + V8_INLINE void Swap(BasicMember& other) { + auto tmp = GetRawStorage(); *this = other; other = tmp; } - explicit operator bool() const { return Get(); } - operator T*() const { return Get(); } - T* operator->() const { return Get(); } - T& operator*() const { return *Get(); } + V8_INLINE explicit operator bool() const { return !IsCleared(); } + V8_INLINE operator T*() const { return Get(); } + V8_INLINE T* operator->() const { return Get(); } + V8_INLINE T& operator*() const { return *Get(); } // CFI cast exemption to allow passing SentinelPointer through T* and support // heterogeneous assignments between different Member and Persistent handles // based on their actual types. - V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { + V8_INLINE V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { // Executed by the mutator, hence non atomic load. // // The const_cast below removes the constness from MemberBase storage. The @@ -195,59 +267,262 @@ class BasicMember final : private MemberBase, private CheckingPolicy { return static_cast(const_cast(MemberBase::GetRaw())); } - void Clear() { SetRawAtomic(nullptr); } + V8_INLINE void Clear() { SetRawStorageAtomic(RawStorage{}); } - T* Release() { + V8_INLINE T* Release() { T* result = Get(); Clear(); return result; } - const T** GetSlotForTesting() const { + V8_INLINE const T** GetSlotForTesting() const { return reinterpret_cast(GetRawSlot()); } + V8_INLINE RawStorage GetRawStorage() const { + return MemberBase::GetRawStorage(); + } + private: - const T* GetRawAtomic() const { + V8_INLINE explicit BasicMember(RawStorage raw) : MemberBase(raw) { + InitializingWriteBarrier(Get()); + this->CheckPointer(Get()); + } + + V8_INLINE BasicMember& operator=(RawStorage other) { + SetRawStorageAtomic(other); + AssigningWriteBarrier(); + this->CheckPointer(Get()); + return *this; + } + + V8_INLINE const T* GetRawAtomic() const { return static_cast(MemberBase::GetRawAtomic()); } - void InitializingWriteBarrier() const { - WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw()); + V8_INLINE void InitializingWriteBarrier(T* value) const { + WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), value); } - void AssigningWriteBarrier() const { - WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw()); + V8_INLINE void AssigningWriteBarrier(T* value) const { + WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), value); + } + V8_INLINE void AssigningWriteBarrier() const { + WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRawStorage()); } - void ClearFromGC() const { MemberBase::ClearFromGC(); } + V8_INLINE void ClearFromGC() const { MemberBase::ClearFromGC(); } - T* GetFromGC() const { return Get(); } + V8_INLINE T* GetFromGC() const { return Get(); } + friend class cppgc::subtle::HeapConsistency; friend class cppgc::Visitor; template friend struct cppgc::TraceTrait; + template + friend class BasicMember; }; +// Member equality operators. template -bool operator==(const BasicMember& member1, - const BasicMember& member2) { - return member1.Get() == member2.Get(); +V8_INLINE bool operator==( + const BasicMember& + member1, + const BasicMember& + member2) { + if constexpr (internal::IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member1.GetRawStorage() == member2.GetRawStorage(); + } else { + static_assert(internal::IsStrictlyBaseOfV || + internal::IsStrictlyBaseOfV); + // Otherwise, check decompressed pointers. + return member1.Get() == member2.Get(); + } } template -bool operator!=(const BasicMember& member1, - const BasicMember& member2) { +V8_INLINE bool operator!=( + const BasicMember& + member1, + const BasicMember& + member2) { return !(member1 == member2); } +// Equality with raw pointers. +template +V8_INLINE bool operator==(const BasicMember& member, + U* raw) { + // Never allow comparison with erased pointers. + static_assert(!internal::IsDecayedSameV); + + if constexpr (internal::IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member.GetRawStorage() == MemberBase::RawStorage(raw); + } else if constexpr (internal::IsStrictlyBaseOfV) { + // Cast the raw pointer to T, which may adjust the pointer. + return member.GetRawStorage() == + MemberBase::RawStorage(static_cast(raw)); + } else { + // Otherwise, decompressed the member. + return member.Get() == raw; + } +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + U* raw) { + return !(member == raw); +} + +template +V8_INLINE bool operator==(T* raw, + const BasicMember& member) { + return member == raw; +} + +template +V8_INLINE bool operator!=(T* raw, + const BasicMember& member) { + return !(raw == member); +} + +// Equality with sentinel. +template +V8_INLINE bool operator==(const BasicMember& member, + SentinelPointer) { + return member.GetRawStorage().IsSentinel(); +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + SentinelPointer s) { + return !(member == s); +} + +template +V8_INLINE bool operator==(SentinelPointer s, + const BasicMember& member) { + return member == s; +} + +template +V8_INLINE bool operator!=(SentinelPointer s, + const BasicMember& member) { + return !(s == member); +} + +// Equality with nullptr. +template +V8_INLINE bool operator==(const BasicMember& member, + std::nullptr_t) { + return !static_cast(member); +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + std::nullptr_t n) { + return !(member == n); +} + +template +V8_INLINE bool operator==(std::nullptr_t n, + const BasicMember& member) { + return member == n; +} + +template +V8_INLINE bool operator!=(std::nullptr_t n, + const BasicMember& member) { + return !(n == member); +} + +// Relational operators. +template +V8_INLINE bool operator<( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() < member2.GetRawStorage(); +} + +template +V8_INLINE bool operator<=( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() <= member2.GetRawStorage(); +} + +template +V8_INLINE bool operator>( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() > member2.GetRawStorage(); +} + +template +V8_INLINE bool operator>=( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() >= member2.GetRawStorage(); +} + template struct IsWeak< internal::BasicMember> diff --git a/NativeScript/include/cppgc/name-provider.h b/NativeScript/include/cppgc/name-provider.h index 224dd4b5..216f6098 100644 --- a/NativeScript/include/cppgc/name-provider.h +++ b/NativeScript/include/cppgc/name-provider.h @@ -37,15 +37,15 @@ class V8_EXPORT NameProvider { static constexpr const char kNoNameDeducible[] = ""; /** - * Indicating whether internal names are hidden or not. + * Indicating whether the build supports extracting C++ names as object names. * * @returns true if C++ names should be hidden and represented by kHiddenName. */ - static constexpr bool HideInternalNames() { + static constexpr bool SupportsCppClassNamesAsObjectNames() { #if CPPGC_SUPPORTS_OBJECT_NAMES - return false; -#else // !CPPGC_SUPPORTS_OBJECT_NAMES return true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + return false; #endif // !CPPGC_SUPPORTS_OBJECT_NAMES } diff --git a/NativeScript/include/cppgc/persistent.h b/NativeScript/include/cppgc/persistent.h index 244f94c8..3a66ccc0 100644 --- a/NativeScript/include/cppgc/persistent.h +++ b/NativeScript/include/cppgc/persistent.h @@ -16,9 +16,6 @@ #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { - -class Visitor; - namespace internal { // PersistentBase always refers to the object as const object and defers to @@ -78,7 +75,7 @@ class BasicPersistent final : public PersistentBase, : PersistentBase(raw), LocationPolicy(loc) { if (!IsValid()) return; SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) - .AllocateNode(this, &BasicPersistent::Trace)); + .AllocateNode(this, &TraceAsRoot)); this->CheckPointer(Get()); } @@ -221,9 +218,8 @@ class BasicPersistent final : public PersistentBase, } private: - static void Trace(Visitor* v, const void* ptr) { - const auto* persistent = static_cast(ptr); - v->TraceRoot(*persistent, persistent->Location()); + static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { + root_visitor.Trace(*static_cast(ptr)); } bool IsValid() const { @@ -247,7 +243,7 @@ class BasicPersistent final : public PersistentBase, SetValue(ptr); if (!IsValid()) return; SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) - .AllocateNode(this, &BasicPersistent::Trace)); + .AllocateNode(this, &TraceAsRoot)); this->CheckPointer(Get()); } @@ -264,7 +260,7 @@ class BasicPersistent final : public PersistentBase, return static_cast(const_cast(GetValue())); } - friend class cppgc::Visitor; + friend class internal::RootVisitor; }; template operator T*() const { - static constexpr intptr_t kSentinelValue = 1; return reinterpret_cast(kSentinelValue); } // Hidden friends. diff --git a/NativeScript/include/cppgc/trace-trait.h b/NativeScript/include/cppgc/trace-trait.h index 83619b1d..694fbfdc 100644 --- a/NativeScript/include/cppgc/trace-trait.h +++ b/NativeScript/include/cppgc/trace-trait.h @@ -16,6 +16,10 @@ class Visitor; namespace internal { +class RootVisitor; + +using TraceRootCallback = void (*)(RootVisitor&, const void* object); + // Implementation of the default TraceTrait handling GarbageCollected and // GarbageCollectedMixin. template ()))::value; }; +template +constexpr bool IsDecayedSameV = + std::is_same_v, std::decay_t>; + +template +constexpr bool IsStrictlyBaseOfV = + std::is_base_of_v, std::decay_t> && + !IsDecayedSameV; + } // namespace internal /** diff --git a/NativeScript/include/cppgc/visitor.h b/NativeScript/include/cppgc/visitor.h index 57e2ce39..704aabcd 100644 --- a/NativeScript/include/cppgc/visitor.h +++ b/NativeScript/include/cppgc/visitor.h @@ -62,22 +62,6 @@ class V8_EXPORT Visitor { virtual ~Visitor() = default; - /** - * Trace method for raw pointers. Prefer the versions for managed pointers. - * - * \param member Reference retaining an object. - */ - template - void Trace(const T* t) { - static_assert(sizeof(T), "Pointee type must be fully defined."); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "T must be GarbageCollected or GarbageCollectedMixin type"); - if (!t) { - return; - } - Visit(t, TraceTrait::GetTraceDescriptor(t)); - } - /** * Trace method for Member. * @@ -87,7 +71,7 @@ class V8_EXPORT Visitor { void Trace(const Member& member) { const T* value = member.GetRawAtomic(); CPPGC_DCHECK(value != kSentinelPointer); - Trace(value); + TraceImpl(value); } /** @@ -231,23 +215,34 @@ class V8_EXPORT Visitor { void TraceStrongly(const WeakMember& weak_member) { const T* value = weak_member.GetRawAtomic(); CPPGC_DCHECK(value != kSentinelPointer); - Trace(value); + TraceImpl(value); + } + + /** + * Trace method for retaining containers strongly. + * + * \param object reference to the container. + */ + template + void TraceStrongContainer(const T* object) { + TraceImpl(object); } /** - * Trace method for weak containers. + * Trace method for retaining containers weakly. Note that weak containers + * should emit write barriers. * - * \param object reference of the weak container. + * \param object reference to the container. * \param callback to be invoked. - * \param data custom data that is passed to the callback. + * \param callback_data custom data that is passed to the callback. */ template void TraceWeakContainer(const T* object, WeakCallback callback, - const void* data) { + const void* callback_data) { if (!object) return; VisitWeakContainer(object, TraceTrait::GetTraceDescriptor(object), TraceTrait::GetWeakTraceDescriptor(object), callback, - data); + callback_data); } /** @@ -255,6 +250,7 @@ class V8_EXPORT Visitor { * compactable space. Such references maybe be arbitrarily moved by the GC. * * \param slot location of reference to object that might be moved by the GC. + * The slot must contain an uncompressed pointer. */ template void RegisterMovableReference(const T** slot) { @@ -297,9 +293,6 @@ class V8_EXPORT Visitor { virtual void Visit(const void* self, TraceDescriptor) {} virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, const void* weak_member) {} - virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} - virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, - const void* weak_root, const SourceLocation&) {} virtual void VisitEphemeron(const void* key, const void* value, TraceDescriptor value_desc) {} virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, @@ -320,44 +313,20 @@ class V8_EXPORT Visitor { static void HandleWeak(const LivenessBroker& info, const void* object) { const PointerType* weak = static_cast(object); auto* raw_ptr = weak->GetFromGC(); - // Sentinel values are preserved for weak pointers. - if (raw_ptr == kSentinelPointer) return; if (!info.IsHeapObjectAlive(raw_ptr)) { weak->ClearFromGC(); } } - template * = nullptr> - void TraceRoot(const Persistent& p, const SourceLocation& loc) { - using PointeeType = typename Persistent::PointeeType; - static_assert(sizeof(PointeeType), - "Persistent's pointee type must be fully defined"); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "Persistent's pointee type must be GarbageCollected or " - "GarbageCollectedMixin"); - auto* ptr = p.GetFromGC(); - if (!ptr) { + template + void TraceImpl(const T* t) { + static_assert(sizeof(T), "Pointee type must be fully defined."); + static_assert(internal::IsGarbageCollectedOrMixinType::value, + "T must be GarbageCollected or GarbageCollectedMixin type"); + if (!t) { return; } - VisitRoot(ptr, TraceTrait::GetTraceDescriptor(ptr), loc); - } - - template < - typename WeakPersistent, - std::enable_if_t* = nullptr> - void TraceRoot(const WeakPersistent& p, const SourceLocation& loc) { - using PointeeType = typename WeakPersistent::PointeeType; - static_assert(sizeof(PointeeType), - "Persistent's pointee type must be fully defined"); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "Persistent's pointee type must be GarbageCollected or " - "GarbageCollectedMixin"); - static_assert(!internal::IsAllocatedOnCompactableSpace::value, - "Weak references to compactable objects are not allowed"); - auto* ptr = p.GetFromGC(); - VisitWeakRoot(ptr, TraceTrait::GetTraceDescriptor(ptr), - &HandleWeak, &p, loc); + Visit(t, TraceTrait::GetTraceDescriptor(t)); } #if V8_ENABLE_CHECKS @@ -374,6 +343,70 @@ class V8_EXPORT Visitor { friend class internal::VisitorBase; }; +namespace internal { + +class V8_EXPORT RootVisitor { + public: + explicit RootVisitor(Visitor::Key) {} + + virtual ~RootVisitor() = default; + + template * = nullptr> + void Trace(const AnyStrongPersistentType& p) { + using PointeeType = typename AnyStrongPersistentType::PointeeType; + const void* object = Extract(p); + if (!object) { + return; + } + VisitRoot(object, TraceTrait::GetTraceDescriptor(object), + p.Location()); + } + + template * = nullptr> + void Trace(const AnyWeakPersistentType& p) { + using PointeeType = typename AnyWeakPersistentType::PointeeType; + static_assert(!internal::IsAllocatedOnCompactableSpace::value, + "Weak references to compactable objects are not allowed"); + const void* object = Extract(p); + if (!object) { + return; + } + VisitWeakRoot(object, TraceTrait::GetTraceDescriptor(object), + &HandleWeak, &p, p.Location()); + } + + protected: + virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} + virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, + const void* weak_root, const SourceLocation&) {} + + private: + template + static const void* Extract(AnyPersistentType& p) { + using PointeeType = typename AnyPersistentType::PointeeType; + static_assert(sizeof(PointeeType), + "Persistent's pointee type must be fully defined"); + static_assert(internal::IsGarbageCollectedOrMixinType::value, + "Persistent's pointee type must be GarbageCollected or " + "GarbageCollectedMixin"); + return p.GetFromGC(); + } + + template + static void HandleWeak(const LivenessBroker& info, const void* object) { + const PointerType* weak = static_cast(object); + auto* raw_ptr = weak->GetFromGC(); + if (!info.IsHeapObjectAlive(raw_ptr)) { + weak->ClearFromGC(); + } + } +}; + +} // namespace internal } // namespace cppgc #endif // INCLUDE_CPPGC_VISITOR_H_ diff --git a/NativeScript/include/js_protocol-1.3.json b/NativeScript/include/js_protocol-1.3.json index ea573d11..a998d461 100644 --- a/NativeScript/include/js_protocol-1.3.json +++ b/NativeScript/include/js_protocol-1.3.json @@ -946,34 +946,6 @@ { "name": "url", "type": "string", "description": "JavaScript script name or url." }, { "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." } ] - }, - { "id": "TypeObject", - "type": "object", - "description": "Describes a type collected during runtime.", - "properties": [ - { "name": "name", "type": "string", "description": "Name of a type collected with type profiling." } - ], - "experimental": true - }, - { "id": "TypeProfileEntry", - "type": "object", - "description": "Source offset and types for a parameter or return value.", - "properties": [ - { "name": "offset", "type": "integer", "description": "Source offset of the parameter or end of function for return values." }, - { "name": "types", "type": "array", "items": {"$ref": "TypeObject"}, "description": "The types for this parameter or return value."} - ], - "experimental": true - }, - { - "id": "ScriptTypeProfile", - "type": "object", - "description": "Type profile data collected during runtime for a JavaScript script.", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "entries", "type": "array", "items": { "$ref": "TypeProfileEntry" }, "description": "Type profile entries for parameters and return values of the functions in the script." } - ], - "experimental": true } ], "commands": [ @@ -1024,24 +996,6 @@ { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." } ], "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection." - }, - { - "name": "startTypeProfile", - "description": "Enable type profile.", - "experimental": true - }, - { - "name": "stopTypeProfile", - "description": "Disable type profile. Disabling releases type profile data collected so far.", - "experimental": true - }, - { - "name": "takeTypeProfile", - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "ScriptTypeProfile" }, "description": "Type profile for all scripts since startTypeProfile() was turned on." } - ], - "description": "Collect type profile.", - "experimental": true } ], "events": [ diff --git a/NativeScript/include/js_protocol.pdl b/NativeScript/include/js_protocol.pdl index bd277eb0..d4102f5c 100644 --- a/NativeScript/include/js_protocol.pdl +++ b/NativeScript/include/js_protocol.pdl @@ -244,6 +244,40 @@ domain Debugger # Wasm bytecode. optional binary bytecode + experimental type WasmDisassemblyChunk extends object + properties + # The next chunk of disassembled lines. + array of string lines + # The bytecode offsets describing the start of each line. + array of integer bytecodeOffsets + + experimental command disassembleWasmModule + parameters + # Id of the script to disassemble + Runtime.ScriptId scriptId + returns + # For large modules, return a stream from which additional chunks of + # disassembly can be read successively. + optional string streamId + # The total number of lines in the disassembly text. + integer totalNumberOfLines + # The offsets of all function bodies, in the format [start1, end1, + # start2, end2, ...] where all ends are exclusive. + array of integer functionBodyOffsets + # The first chunk of disassembly. + WasmDisassemblyChunk chunk + + # Disassemble the next chunk of lines for the module corresponding to the + # stream. If disassembly is complete, this API will invalidate the streamId + # and return an empty chunk. Any subsequent calls for the now invalid stream + # will return errors. + experimental command nextWasmDisassemblyChunk + parameters + string streamId + returns + # The next chunk of disassembly. + WasmDisassemblyChunk chunk + # This command is deprecated. Use getScriptSource instead. deprecated command getWasmBytecode parameters @@ -273,18 +307,35 @@ domain Debugger parameters BreakpointId breakpointId - # Restarts particular call frame from the beginning. - deprecated command restartFrame + # Restarts particular call frame from the beginning. The old, deprecated + # behavior of `restartFrame` is to stay paused and allow further CDP commands + # after a restart was scheduled. This can cause problems with restarting, so + # we now continue execution immediatly after it has been scheduled until we + # reach the beginning of the restarted frame. + # + # To stay back-wards compatible, `restartFrame` now expects a `mode` + # parameter to be present. If the `mode` parameter is missing, `restartFrame` + # errors out. + # + # The various return values are deprecated and `callFrames` is always empty. + # Use the call frames from the `Debugger#paused` events instead, that fires + # once V8 pauses at the beginning of the restarted function. + command restartFrame parameters # Call frame identifier to evaluate on. CallFrameId callFrameId + # The `mode` parameter must be present and set to 'StepInto', otherwise + # `restartFrame` will error out. + experimental optional enum mode + # Pause at the beginning of the restarted function + StepInto returns # New stack trace. - array of CallFrame callFrames + deprecated array of CallFrame callFrames # Async stack trace, if any. - optional Runtime.StackTrace asyncStackTrace + deprecated optional Runtime.StackTrace asyncStackTrace # Async stack trace, if any. - experimental optional Runtime.StackTraceId asyncStackTraceId + deprecated optional Runtime.StackTraceId asyncStackTraceId # Resumes JavaScript execution. command resume @@ -407,13 +458,14 @@ domain Debugger # New value for breakpoints active state. boolean active - # Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or - # no exceptions. Initial pause on exceptions state is `none`. + # Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions, + # or caught exceptions, no exceptions. Initial pause on exceptions state is `none`. command setPauseOnExceptions parameters # Pause on exceptions mode. enum state none + caught uncaught all @@ -424,6 +476,12 @@ domain Debugger Runtime.CallArgument newValue # Edits JavaScript source live. + # + # In general, functions that are currently on the stack can not be edited with + # a single exception: If the edited function is the top-most stack frame and + # that is the only activation of that function on the stack. In this case + # the live edit will be successful and a `Debugger.restartFrame` for the + # top-most function is automatically triggered. command setScriptSource parameters # Id of the script to edit. @@ -433,16 +491,27 @@ domain Debugger # If true the change will not actually be applied. Dry run may be used to get result # description without actually modifying the code. optional boolean dryRun + # If true, then `scriptSource` is allowed to change the function on top of the stack + # as long as the top-most stack frame is the only activation of that function. + experimental optional boolean allowTopFrameEditing returns # New stack trace in case editing has happened while VM was stopped. - optional array of CallFrame callFrames + deprecated optional array of CallFrame callFrames # Whether current call stack was modified after applying the changes. - optional boolean stackChanged + deprecated optional boolean stackChanged # Async stack trace, if any. - optional Runtime.StackTrace asyncStackTrace + deprecated optional Runtime.StackTrace asyncStackTrace # Async stack trace, if any. - experimental optional Runtime.StackTraceId asyncStackTraceId - # Exception details if any. + deprecated optional Runtime.StackTraceId asyncStackTraceId + # Whether the operation was successful or not. Only `Ok` denotes a + # successful live edit while the other enum variants denote why + # the live edit failed. + experimental enum status + Ok + CompileError + BlockedByActiveGenerator + BlockedByActiveFunction + # Exception details if any. Only present when `status` is `CompileError`. optional Runtime.ExceptionDetails exceptionDetails # Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc). @@ -559,7 +628,7 @@ domain Debugger integer endColumn # Specifies script creation context. Runtime.ExecutionContextId executionContextId - # Content hash of the script. + # Content hash of the script, SHA-256. string hash # Embedder-specific auxiliary data. optional object executionContextAuxData @@ -598,7 +667,7 @@ domain Debugger integer endColumn # Specifies script creation context. Runtime.ExecutionContextId executionContextId - # Content hash of the script. + # Content hash of the script, SHA-256. string hash # Embedder-specific auxiliary data. optional object executionContextAuxData @@ -698,6 +767,22 @@ experimental domain HeapProfiler # Average sample interval in bytes. Poisson distribution is used for the intervals. The # default value is 32768 bytes. optional number samplingInterval + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # major GC, which will show which functions cause large temporary memory + # usage or long GC pauses. + optional boolean includeObjectsCollectedByMajorGC + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # minor GC, which is useful when tuning a latency-sensitive application + # for minimal GC activity. + optional boolean includeObjectsCollectedByMinorGC command startTrackingHeapObjects parameters @@ -713,18 +798,24 @@ experimental domain HeapProfiler # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken # when the tracking is stopped. optional boolean reportProgress - optional boolean treatGlobalObjectsAsRoots + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots # If true, numerical values are included in the snapshot optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals command takeHeapSnapshot parameters # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken. optional boolean reportProgress - # If true, a raw snapshot without artificial roots will be generated - optional boolean treatGlobalObjectsAsRoots + # If true, a raw snapshot without artificial roots will be generated. + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots # If true, numerical values are included in the snapshot optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals event addHeapSnapshotChunk parameters @@ -828,30 +919,6 @@ domain Profiler # Functions contained in the script that has coverage data. array of FunctionCoverage functions - # Describes a type collected during runtime. - experimental type TypeObject extends object - properties - # Name of a type collected with type profiling. - string name - - # Source offset and types for a parameter or return value. - experimental type TypeProfileEntry extends object - properties - # Source offset of the parameter or end of function for return values. - integer offset - # The types for this parameter or return value. - array of TypeObject types - - # Type profile data collected during runtime for a JavaScript script. - experimental type ScriptTypeProfile extends object - properties - # JavaScript script id. - Runtime.ScriptId scriptId - # JavaScript script name or url. - string url - # Type profile entries for parameters and return values of the functions in the script. - array of TypeProfileEntry entries - command disable command enable @@ -886,9 +953,6 @@ domain Profiler # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. number timestamp - # Enable type profile. - experimental command startTypeProfile - command stop returns # Recorded profile. @@ -898,9 +962,6 @@ domain Profiler # executing optimized code. command stopPreciseCoverage - # Disable type profile. Disabling releases type profile data collected so far. - experimental command stopTypeProfile - # Collect coverage data for the current isolate, and resets execution counters. Precise code # coverage needs to have started. command takePreciseCoverage @@ -910,12 +971,6 @@ domain Profiler # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. number timestamp - # Collect type profile. - experimental command takeTypeProfile - returns - # Type profile for all scripts since startTypeProfile() was turned on. - array of ScriptTypeProfile result - event consoleProfileFinished parameters string id @@ -1347,7 +1402,16 @@ domain Runtime optional string objectGroup # Whether to throw an exception if side effect cannot be ruled out during evaluation. experimental optional boolean throwOnSideEffect - # Whether the result should be serialized according to https://w3c.github.io/webdriver-bidi. + # An alternative way to specify the execution context to call function on. + # Compared to contextId that may be reused across processes, this is guaranteed to be + # system-unique, so it can be used to prevent accidental function call + # in context different than intended (e.g. as a result of navigation across process + # boundaries). + # This is mutually exclusive with `executionContextId`. + experimental optional string uniqueContextId + # Whether the result should contain `webDriverValue`, serialized according to + # https://w3c.github.io/webdriver-bidi. This is mutually exclusive with `returnByValue`, but + # resulting `objectId` is still provided. experimental optional boolean generateWebDriverValue returns # Call result. @@ -1677,7 +1741,9 @@ domain Runtime event executionContextDestroyed parameters # Id of the destroyed context - ExecutionContextId executionContextId + deprecated ExecutionContextId executionContextId + # Unique Id of the destroyed context + experimental string executionContextUniqueId # Issued when all executionContexts were cleared in browser event executionContextsCleared diff --git a/NativeScript/include/libplatform/v8-tracing.h b/NativeScript/include/libplatform/v8-tracing.h index 12489327..6039a9c5 100644 --- a/NativeScript/include/libplatform/v8-tracing.h +++ b/NativeScript/include/libplatform/v8-tracing.h @@ -282,12 +282,12 @@ class V8_PLATFORM_EXPORT TracingController const char* name, uint64_t handle) override; static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag); -#endif // !defined(V8_USE_PERFETTO) void AddTraceStateObserver( v8::TracingController::TraceStateObserver* observer) override; void RemoveTraceStateObserver( v8::TracingController::TraceStateObserver* observer) override; +#endif // !defined(V8_USE_PERFETTO) void StartTracing(TraceConfig* trace_config); void StopTracing(); @@ -307,7 +307,6 @@ class V8_PLATFORM_EXPORT TracingController std::unique_ptr mutex_; std::unique_ptr trace_config_; std::atomic_bool recording_{false}; - std::unordered_set observers_; #if defined(V8_USE_PERFETTO) std::ostream* output_stream_ = nullptr; @@ -316,6 +315,7 @@ class V8_PLATFORM_EXPORT TracingController TraceEventListener* listener_for_testing_ = nullptr; std::unique_ptr tracing_session_; #else // !defined(V8_USE_PERFETTO) + std::unordered_set observers_; std::unique_ptr trace_buffer_; #endif // !defined(V8_USE_PERFETTO) diff --git a/NativeScript/include/v8-array-buffer.h b/NativeScript/include/v8-array-buffer.h index e9047b79..804fc42c 100644 --- a/NativeScript/include/v8-array-buffer.h +++ b/NativeScript/include/v8-array-buffer.h @@ -53,12 +53,28 @@ class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { */ size_t ByteLength() const; + /** + * The maximum length (in bytes) that this backing store may grow to. + * + * If this backing store was created for a resizable ArrayBuffer or a growable + * SharedArrayBuffer, it is >= ByteLength(). Otherwise it is == + * ByteLength(). + */ + size_t MaxByteLength() const; + /** * Indicates whether the backing store was created for an ArrayBuffer or * a SharedArrayBuffer. */ bool IsShared() const; + /** + * Indicates whether the backing store was created for a resizable ArrayBuffer + * or a growable SharedArrayBuffer, and thus may be resized by user JavaScript + * code. + */ + bool IsResizableByUserJavaScript() const; + /** * Prevent implicit instantiation of operator delete with size_t argument. * The size_t argument would be incorrect because ptr points to the @@ -189,6 +205,11 @@ class V8_EXPORT ArrayBuffer : public Object { */ size_t ByteLength() const; + /** + * Maximum length in bytes. + */ + size_t MaxByteLength() const; + /** * Create a new ArrayBuffer. Allocate |byte_length| bytes. * Allocated memory will be owned by a created ArrayBuffer and @@ -235,27 +256,74 @@ class V8_EXPORT ArrayBuffer : public Object { void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, void* deleter_data); + /** + * Returns a new resizable standalone BackingStore that is allocated using the + * array buffer allocator of the isolate. The result can be later passed to + * ArrayBuffer::New. + * + * |byte_length| must be <= |max_byte_length|. + * + * This function is usable without an isolate. Unlike |NewBackingStore| calls + * with an isolate, GCs cannot be triggered, and there are no + * retries. Allocation failure will cause the function to crash with an + * out-of-memory error. + */ + static std::unique_ptr NewResizableBackingStore( + size_t byte_length, size_t max_byte_length); + /** * Returns true if this ArrayBuffer may be detached. */ bool IsDetachable() const; + /** + * Returns true if this ArrayBuffer has been detached. + */ + bool WasDetached() const; + /** * Detaches this ArrayBuffer and all its views (typed arrays). * Detaching sets the byte length of the buffer and all typed arrays to zero, * preventing JavaScript from ever accessing underlying backing store. * ArrayBuffer should have been externalized and must be detachable. */ + V8_DEPRECATE_SOON( + "Use the version which takes a key parameter (passing a null handle is " + "ok).") void Detach(); + /** + * Detaches this ArrayBuffer and all its views (typed arrays). + * Detaching sets the byte length of the buffer and all typed arrays to zero, + * preventing JavaScript from ever accessing underlying backing store. + * ArrayBuffer should have been externalized and must be detachable. Returns + * Nothing if the key didn't pass the [[ArrayBufferDetachKey]] check, + * Just(true) otherwise. + */ + V8_WARN_UNUSED_RESULT Maybe Detach(v8::Local key); + + /** + * Sets the ArrayBufferDetachKey. + */ + void SetDetachKey(v8::Local key); + /** * Get a shared pointer to the backing store of this array buffer. This * pointer coordinates the lifetime management of the internal storage * with any live ArrayBuffers on the heap, even across isolates. The embedder * should not attempt to manage lifetime of the storage through other means. + * + * The returned shared pointer will not be empty, even if the ArrayBuffer has + * been detached. Use |WasDetached| to tell if it has been detached instead. */ std::shared_ptr GetBackingStore(); + /** + * More efficient shortcut for GetBackingStore()->Data(). The returned pointer + * is valid as long as the ArrayBuffer is alive. + */ + void* Data() const; + V8_INLINE static ArrayBuffer* Cast(Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); @@ -360,6 +428,11 @@ class V8_EXPORT SharedArrayBuffer : public Object { */ size_t ByteLength() const; + /** + * Maximum length in bytes. + */ + size_t MaxByteLength() const; + /** * Create a new SharedArrayBuffer. Allocate |byte_length| bytes. * Allocated memory will be owned by a created SharedArrayBuffer and @@ -414,6 +487,12 @@ class V8_EXPORT SharedArrayBuffer : public Object { */ std::shared_ptr GetBackingStore(); + /** + * More efficient shortcut for GetBackingStore()->Data(). The returned pointer + * is valid as long as the ArrayBuffer is alive. + */ + void* Data() const; + V8_INLINE static SharedArrayBuffer* Cast(Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/NativeScript/include/v8-callbacks.h b/NativeScript/include/v8-callbacks.h index 70b9c2ae..f3e96c37 100644 --- a/NativeScript/include/v8-callbacks.h +++ b/NativeScript/include/v8-callbacks.h @@ -12,6 +12,7 @@ #include "cppgc/common.h" #include "v8-data.h" // NOLINT(build/include_directory) #include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-promise.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) #if defined(V8_OS_WIN) @@ -105,7 +106,7 @@ struct JitCodeEvent { size_t line_number_table_size; }; - wasm_source_info_t* wasm_source_info; + wasm_source_info_t* wasm_source_info = nullptr; union { // Only valid for CODE_ADDED. @@ -216,7 +217,13 @@ using AddHistogramSampleCallback = void (*)(void* histogram, int sample); using FatalErrorCallback = void (*)(const char* location, const char* message); -using OOMErrorCallback = void (*)(const char* location, bool is_heap_oom); +struct OOMDetails { + bool is_heap_oom = false; + const char* detail = nullptr; +}; + +using OOMErrorCallback = void (*)(const char* location, + const OOMDetails& details); using MessageCallback = void (*)(Local message, Local data); @@ -230,9 +237,13 @@ using LogEventCallback = void (*)(const char* name, enum class CrashKeyId { kIsolateAddress, kReadonlySpaceFirstPageAddress, - kMapSpaceFirstPageAddress, + kMapSpaceFirstPageAddress V8_ENUM_DEPRECATE_SOON("Map space got removed"), + kOldSpaceFirstPageAddress, + kCodeRangeBaseAddress, kCodeSpaceFirstPageAddress, kDumpType, + kSnapshotChecksumCalculated, + kSnapshotChecksumExpected, }; using AddCrashKeyCallback = void (*)(CrashKeyId id, const std::string& value); @@ -300,6 +311,13 @@ using ApiImplementationCallback = void (*)(const FunctionCallbackInfo&); // --- Callback for WebAssembly.compileStreaming --- using WasmStreamingCallback = void (*)(const FunctionCallbackInfo&); +enum class WasmAsyncSuccess { kSuccess, kFail }; + +// --- Callback called when async WebAssembly operations finish --- +using WasmAsyncResolvePromiseCallback = void (*)( + Isolate* isolate, Local context, Local resolver, + Local result, WasmAsyncSuccess success); + // --- Callback for loading source map file for Wasm profiling support using WasmLoadSourceMapCallback = Local (*)(Isolate* isolate, const char* name); @@ -310,8 +328,9 @@ using WasmSimdEnabledCallback = bool (*)(Local context); // --- Callback for checking if WebAssembly exceptions are enabled --- using WasmExceptionsEnabledCallback = bool (*)(Local context); -// --- Callback for checking if WebAssembly dynamic tiering is enabled --- -using WasmDynamicTieringEnabledCallback = bool (*)(Local context); +// --- Callback for checking if WebAssembly GC is enabled --- +// If the callback returns true, it will also enable Wasm stringrefs. +using WasmGCEnabledCallback = bool (*)(Local context); // --- Callback for checking if the SharedArrayBuffer constructor is enabled --- using SharedArrayBufferConstructorEnabledCallback = diff --git a/NativeScript/include/v8-context.h b/NativeScript/include/v8-context.h index 72dfbaad..3ce0eb0a 100644 --- a/NativeScript/include/v8-context.h +++ b/NativeScript/include/v8-context.h @@ -169,6 +169,9 @@ class V8_EXPORT Context : public Data { /** Returns the microtask queue associated with a current context. */ MicrotaskQueue* GetMicrotaskQueue(); + /** Sets the microtask queue associated with the current context. */ + void SetMicrotaskQueue(MicrotaskQueue* queue); + /** * The field at kDebugIdIndex used to be reserved for the inspector. * It now serves no purpose. @@ -244,6 +247,12 @@ class V8_EXPORT Context : public Data { */ void SetErrorMessageForCodeGenerationFromStrings(Local message); + /** + * Sets the error description for the exception that is thrown when + * wasm code generation is not allowed. + */ + void SetErrorMessageForWasmCodeGeneration(Local message); + /** * Return data that was previously attached to the context snapshot via * SnapshotCreator, and removes the reference to it. @@ -284,6 +293,7 @@ class V8_EXPORT Context : public Data { Local after_hook, Local resolve_hook); + bool HasTemplateLiteralObject(Local object); /** * Stack-allocated class which sets the execution context for all * operations executed within a local scope. @@ -374,15 +384,13 @@ void* Context::GetAlignedPointerFromEmbedderData(int index) { A ctx = *reinterpret_cast(this); A embedder_data = I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); - int value_offset = - I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - value_offset += I::kEmbedderDataSlotRawPayloadOffset; -#endif - internal::Isolate* isolate = I::GetIsolateForSandbox(ctx); + int value_offset = I::kEmbedderDataArrayHeaderSize + + (I::kEmbedderDataSlotSize * index) + + I::kEmbedderDataSlotExternalPointerOffset; + Isolate* isolate = I::GetIsolateForSandbox(ctx); return reinterpret_cast( - I::ReadExternalPointerField(isolate, embedder_data, value_offset, - internal::kEmbedderDataSlotPayloadTag)); + I::ReadExternalPointerField( + isolate, embedder_data, value_offset)); #else return SlowGetAlignedPointerFromEmbedderData(index); #endif diff --git a/NativeScript/include/v8-cppgc.h b/NativeScript/include/v8-cppgc.h index 41215493..4a457027 100644 --- a/NativeScript/include/v8-cppgc.h +++ b/NativeScript/include/v8-cppgc.h @@ -77,12 +77,37 @@ struct WrapperDescriptor final { }; struct V8_EXPORT CppHeapCreateParams { + CppHeapCreateParams( + std::vector> custom_spaces, + WrapperDescriptor wrapper_descriptor) + : custom_spaces(std::move(custom_spaces)), + wrapper_descriptor(wrapper_descriptor) {} + + CppHeapCreateParams(const CppHeapCreateParams&) = delete; + CppHeapCreateParams& operator=(const CppHeapCreateParams&) = delete; + std::vector> custom_spaces; WrapperDescriptor wrapper_descriptor; + /** + * Specifies which kind of marking are supported by the heap. The type may be + * further reduced via runtime flags when attaching the heap to an Isolate. + */ + cppgc::Heap::MarkingType marking_support = + cppgc::Heap::MarkingType::kIncrementalAndConcurrent; + /** + * Specifies which kind of sweeping is supported by the heap. The type may be + * further reduced via runtime flags when attaching the heap to an Isolate. + */ + cppgc::Heap::SweepingType sweeping_support = + cppgc::Heap::SweepingType::kIncrementalAndConcurrent; }; /** * A heap for allocating managed C++ objects. + * + * Similar to v8::Isolate, the heap may only be accessed from one thread at a + * time. The heap may be used from different threads using the + * v8::Locker/v8::Unlocker APIs which is different from generic Oilpan. */ class V8_EXPORT CppHeap { public: diff --git a/NativeScript/include/v8-data.h b/NativeScript/include/v8-data.h index cc51fefe..fc4dea92 100644 --- a/NativeScript/include/v8-data.h +++ b/NativeScript/include/v8-data.h @@ -53,7 +53,7 @@ class V8_EXPORT Data { bool IsContext() const; private: - Data(); + Data() = delete; }; /** diff --git a/NativeScript/include/v8-date.h b/NativeScript/include/v8-date.h index e7a01f29..8d82ccc9 100644 --- a/NativeScript/include/v8-date.h +++ b/NativeScript/include/v8-date.h @@ -27,6 +27,11 @@ class V8_EXPORT Date : public Object { */ double ValueOf() const; + /** + * Generates ISO string representation. + */ + v8::Local ToISOString() const; + V8_INLINE static Date* Cast(Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/NativeScript/include/v8-embedder-heap.h b/NativeScript/include/v8-embedder-heap.h index 09dbae1f..9e2e3ef5 100644 --- a/NativeScript/include/v8-embedder-heap.h +++ b/NativeScript/include/v8-embedder-heap.h @@ -5,27 +5,14 @@ #ifndef INCLUDE_V8_EMBEDDER_HEAP_H_ #define INCLUDE_V8_EMBEDDER_HEAP_H_ -#include -#include - -#include -#include - -#include "cppgc/common.h" -#include "v8-local-handle.h" // NOLINT(build/include_directory) #include "v8-traced-handle.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) namespace v8 { -class Data; class Isolate; class Value; -namespace internal { -class LocalEmbedderHeapTracer; -} // namespace internal - /** * Handler for embedder roots on non-unified heap garbage collections. */ @@ -62,157 +49,6 @@ class V8_EXPORT EmbedderRootsHandler { virtual void ResetRoot(const v8::TracedReference& handle) = 0; }; -/** - * Interface for tracing through the embedder heap. During a V8 garbage - * collection, V8 collects hidden fields of all potential wrappers, and at the - * end of its marking phase iterates the collection and asks the embedder to - * trace through its heap and use reporter to report each JavaScript object - * reachable from any of the given wrappers. - */ -class V8_EXPORT EmbedderHeapTracer { - public: - using EmbedderStackState = cppgc::EmbedderStackState; - - enum TraceFlags : uint64_t { - kNoFlags = 0, - kReduceMemory = 1 << 0, - kForced = 1 << 2, - }; - - /** - * Interface for iterating through |TracedReference| handles. - */ - class V8_EXPORT TracedGlobalHandleVisitor { - public: - virtual ~TracedGlobalHandleVisitor() = default; - virtual void VisitTracedReference(const TracedReference& handle) {} - }; - - /** - * Summary of a garbage collection cycle. See |TraceEpilogue| on how the - * summary is reported. - */ - struct TraceSummary { - /** - * Time spent managing the retained memory in milliseconds. This can e.g. - * include the time tracing through objects in the embedder. - */ - double time = 0.0; - - /** - * Memory retained by the embedder through the |EmbedderHeapTracer| - * mechanism in bytes. - */ - size_t allocated_size = 0; - }; - - virtual ~EmbedderHeapTracer() = default; - - /** - * Iterates all |TracedReference| handles created for the |v8::Isolate| the - * tracer is attached to. - */ - void IterateTracedGlobalHandles(TracedGlobalHandleVisitor* visitor); - - /** - * Called by the embedder to set the start of the stack which is e.g. used by - * V8 to determine whether handles are used from stack or heap. - */ - void SetStackStart(void* stack_start); - - /** - * Called by v8 to register internal fields of found wrappers. - * - * The embedder is expected to store them somewhere and trace reachable - * wrappers from them when called through |AdvanceTracing|. - */ - virtual void RegisterV8References( - const std::vector>& embedder_fields) = 0; - - void RegisterEmbedderReference(const BasicTracedReference& ref); - - /** - * Called at the beginning of a GC cycle. - */ - virtual void TracePrologue(TraceFlags flags) {} - - /** - * Called to advance tracing in the embedder. - * - * The embedder is expected to trace its heap starting from wrappers reported - * by RegisterV8References method, and report back all reachable wrappers. - * Furthermore, the embedder is expected to stop tracing by the given - * deadline. A deadline of infinity means that tracing should be finished. - * - * Returns |true| if tracing is done, and false otherwise. - */ - virtual bool AdvanceTracing(double deadline_in_ms) = 0; - - /* - * Returns true if there no more tracing work to be done (see AdvanceTracing) - * and false otherwise. - */ - virtual bool IsTracingDone() = 0; - - /** - * Called at the end of a GC cycle. - * - * Note that allocation is *not* allowed within |TraceEpilogue|. Can be - * overriden to fill a |TraceSummary| that is used by V8 to schedule future - * garbage collections. - */ - virtual void TraceEpilogue(TraceSummary* trace_summary) {} - - /** - * Called upon entering the final marking pause. No more incremental marking - * steps will follow this call. - */ - virtual void EnterFinalPause(EmbedderStackState stack_state) = 0; - - /* - * Called by the embedder to request immediate finalization of the currently - * running tracing phase that has been started with TracePrologue and not - * yet finished with TraceEpilogue. - * - * Will be a noop when currently not in tracing. - * - * This is an experimental feature. - */ - void FinalizeTracing(); - - /** - * See documentation on EmbedderRootsHandler. - */ - virtual bool IsRootForNonTracingGC( - const v8::TracedReference& handle); - - /** - * See documentation on EmbedderRootsHandler. - */ - virtual void ResetHandleInNonTracingGC( - const v8::TracedReference& handle); - - /* - * Called by the embedder to signal newly allocated or freed memory. Not bound - * to tracing phases. Embedders should trade off when increments are reported - * as V8 may consult global heuristics on whether to trigger garbage - * collection on this change. - */ - void IncreaseAllocatedSize(size_t bytes); - void DecreaseAllocatedSize(size_t bytes); - - /* - * Returns the v8::Isolate this tracer is attached too and |nullptr| if it - * is not attached to any v8::Isolate. - */ - v8::Isolate* isolate() const { return isolate_; } - - protected: - v8::Isolate* isolate_ = nullptr; - - friend class internal::LocalEmbedderHeapTracer; -}; - } // namespace v8 #endif // INCLUDE_V8_EMBEDDER_HEAP_H_ diff --git a/NativeScript/include/v8-exception.h b/NativeScript/include/v8-exception.h index 64126c42..bc058e3f 100644 --- a/NativeScript/include/v8-exception.h +++ b/NativeScript/include/v8-exception.h @@ -197,7 +197,7 @@ class V8_EXPORT TryCatch { void ResetInternal(); - internal::Isolate* isolate_; + internal::Isolate* i_isolate_; TryCatch* next_; void* exception_; void* message_obj_; diff --git a/NativeScript/include/v8-fast-api-calls.h b/NativeScript/include/v8-fast-api-calls.h index 3403de93..0fe7cd24 100644 --- a/NativeScript/include/v8-fast-api-calls.h +++ b/NativeScript/include/v8-fast-api-calls.h @@ -240,13 +240,16 @@ class CTypeInfo { enum class Type : uint8_t { kVoid, kBool, + kUint8, kInt32, kUint32, kInt64, kUint64, kFloat32, kFloat64, + kPointer, kV8Value, + kSeqOneByteString, kApiObject, // This will be deprecated once all users have // migrated from v8::ApiObject to v8::Local. kAny, // This is added to enable untyped representation of fast @@ -302,8 +305,9 @@ class CTypeInfo { constexpr Flags GetFlags() const { return flags_; } static constexpr bool IsIntegralType(Type type) { - return type == Type::kInt32 || type == Type::kUint32 || - type == Type::kInt64 || type == Type::kUint64; + return type == Type::kUint8 || type == Type::kInt32 || + type == Type::kUint32 || type == Type::kInt64 || + type == Type::kUint64; } static constexpr bool IsFloatingPointType(Type type) { @@ -377,6 +381,11 @@ struct FastApiArrayBuffer { size_t byte_length; }; +struct FastOneByteString { + const char* data; + uint32_t length; +}; + class V8_EXPORT CFunctionInfo { public: // Construct a struct to hold a CFunction's type information. @@ -427,14 +436,17 @@ struct AnyCType { uint64_t uint64_value; float float_value; double double_value; + void* pointer_value; Local object_value; Local sequence_value; + const FastApiTypedArray* uint8_ta_value; const FastApiTypedArray* int32_ta_value; const FastApiTypedArray* uint32_ta_value; const FastApiTypedArray* int64_ta_value; const FastApiTypedArray* uint64_ta_value; const FastApiTypedArray* float_ta_value; const FastApiTypedArray* double_ta_value; + const FastOneByteString* string_value; FastApiCallbackOptions* options_value; }; }; @@ -544,7 +556,7 @@ struct FastApiCallbackOptions { * returned instance may be filled with mock data. */ static FastApiCallbackOptions CreateForTesting(Isolate* isolate) { - return {false, {0}}; + return {false, {0}, nullptr}; } /** @@ -566,8 +578,13 @@ struct FastApiCallbackOptions { */ union { uintptr_t data_ptr; - v8::Value data; + v8::Local data; }; + + /** + * When called from WebAssembly, a view of the calling module's memory. + */ + FastApiTypedArray* const wasm_memory; }; namespace internal { @@ -605,8 +622,9 @@ class CFunctionInfoImpl : public CFunctionInfo { kReturnType == CTypeInfo::Type::kUint32 || kReturnType == CTypeInfo::Type::kFloat32 || kReturnType == CTypeInfo::Type::kFloat64 || + kReturnType == CTypeInfo::Type::kPointer || kReturnType == CTypeInfo::Type::kAny, - "64-bit int and api object values are not currently " + "64-bit int, string and api object values are not currently " "supported return types."); } @@ -643,12 +661,14 @@ struct CTypeInfoTraits {}; #define PRIMITIVE_C_TYPES(V) \ V(bool, kBool) \ + V(uint8_t, kUint8) \ V(int32_t, kInt32) \ V(uint32_t, kUint32) \ V(int64_t, kInt64) \ V(uint64_t, kUint64) \ V(float, kFloat32) \ - V(double, kFloat64) + V(double, kFloat64) \ + V(void*, kPointer) // Same as above, but includes deprecated types for compatibility. #define ALL_C_TYPES(V) \ @@ -682,6 +702,7 @@ PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS) }; #define TYPED_ARRAY_C_TYPES(V) \ + V(uint8_t, kUint8) \ V(int32_t, kInt32) \ V(uint32_t, kUint32) \ V(int64_t, kInt64) \ @@ -725,6 +746,18 @@ struct TypeInfoHelper { } }; +template <> +struct TypeInfoHelper { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { + return CTypeInfo::Type::kSeqOneByteString; + } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kScalar; + } +}; + #define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \ static_assert(((COND) == 0) || (ASSERTION), MSG) @@ -802,6 +835,16 @@ class CFunctionBuilderWithFunction { std::make_index_sequence()); } + // Provided for testing purposes. + template + auto Patch(Ret (*patching_func)(Args...)) { + static_assert( + sizeof...(Args) == sizeof...(ArgBuilders), + "The patching function must have the same number of arguments."); + fn_ = reinterpret_cast(patching_func); + return *this; + } + auto Build() { static CFunctionInfoImpl instance; return CFunction(fn_, &instance); @@ -881,31 +924,6 @@ static constexpr CTypeInfo kTypeInfoFloat64 = * to the requested destination type, is considered unsupported. The operation * returns true on success. `type_info` will be used for conversions. */ -template -V8_DEPRECATED( - "Use TryToCopyAndConvertArrayToCppBuffer()") -bool V8_EXPORT V8_WARN_UNUSED_RESULT - TryCopyAndConvertArrayToCppBuffer(Local src, T* dst, - uint32_t max_length); - -template <> -V8_DEPRECATED( - "Use TryToCopyAndConvertArrayToCppBuffer()") -inline bool V8_WARN_UNUSED_RESULT - TryCopyAndConvertArrayToCppBuffer<&kTypeInfoInt32, int32_t>( - Local src, int32_t* dst, uint32_t max_length) { - return false; -} - -template <> -V8_DEPRECATED( - "Use TryToCopyAndConvertArrayToCppBuffer()") -inline bool V8_WARN_UNUSED_RESULT - TryCopyAndConvertArrayToCppBuffer<&kTypeInfoFloat64, double>( - Local src, double* dst, uint32_t max_length) { - return false; -} - template bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer( Local src, T* dst, uint32_t max_length); diff --git a/NativeScript/include/v8-function.h b/NativeScript/include/v8-function.h index 897e6ed6..2dc7e722 100644 --- a/NativeScript/include/v8-function.h +++ b/NativeScript/include/v8-function.h @@ -106,6 +106,14 @@ class V8_EXPORT Function : public Object { V8_WARN_UNUSED_RESULT MaybeLocal FunctionProtoToString( Local context); + /** + * Returns true if the function does nothing. + * The function returns false on error. + * Note that this function is experimental. Embedders should not rely on + * this existing. We may remove this function in the future. + */ + V8_WARN_UNUSED_RESULT bool Experimental_IsNopFunction() const; + ScriptOrigin GetScriptOrigin() const; V8_INLINE static Function* Cast(Value* value) { #ifdef V8_ENABLE_CHECKS diff --git a/NativeScript/include/v8-initialization.h b/NativeScript/include/v8-initialization.h index 3d59c73f..d3e35d6e 100644 --- a/NativeScript/include/v8-initialization.h +++ b/NativeScript/include/v8-initialization.h @@ -100,9 +100,6 @@ class V8_EXPORT V8 { const int kBuildConfiguration = (internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) | (internal::SmiValuesAre31Bits() ? k31BitSmis : 0) | - (internal::SandboxedExternalPointersAreEnabled() - ? kSandboxedExternalPointers - : 0) | (internal::SandboxIsEnabled() ? kSandbox : 0); return Initialize(kBuildConfiguration); } @@ -184,30 +181,19 @@ class V8_EXPORT V8 { * V8 was disposed. */ static void DisposePlatform(); - V8_DEPRECATED("Use DisposePlatform()") - static void ShutdownPlatform() { DisposePlatform(); } - -#ifdef V8_SANDBOX - // - // Sandbox related API. - // - // This API is not yet stable and subject to changes in the future. - // +#if defined(V8_ENABLE_SANDBOX) /** - * Initializes the V8 sandbox. - * - * This must be invoked after the platform was initialized but before V8 is - * initialized. The sandbox is torn down during platform shutdown. - * Returns true on success, false otherwise. + * Returns true if the sandbox is configured securely. * - * TODO(saelo) Once it is no longer optional to initialize the sandbox when - * compiling with V8_SANDBOX, the sandbox initialization will likely happen - * as part of V8::Initialize, at which point this function should be removed. + * If V8 cannot create a regular sandbox during initialization, for example + * because not enough virtual address space can be reserved, it will instead + * create a fallback sandbox that still allows it to function normally but + * does not have the same security properties as a regular sandbox. This API + * can be used to determine if such a fallback sandbox is being used, in + * which case it will return false. */ - static bool InitializeSandbox(); - V8_DEPRECATE_SOON("Use InitializeSandbox()") - static bool InitializeVirtualMemoryCage() { return InitializeSandbox(); } + static bool IsSandboxConfiguredSecurely(); /** * Provides access to the virtual address subspace backing the sandbox. @@ -220,39 +206,29 @@ class V8_EXPORT V8 { * and so in particular the contents of pages allocagted in this virtual * address space, arbitrarily and concurrently. Due to this, it is * recommended to to only place pure data buffers in them. - * - * This function must only be called after initializing the sandbox. */ static VirtualAddressSpace* GetSandboxAddressSpace(); - V8_DEPRECATE_SOON("Use GetSandboxAddressSpace()") - static PageAllocator* GetVirtualMemoryCagePageAllocator(); /** * Returns the size of the sandbox in bytes. * - * If the sandbox has not been initialized, or if the initialization failed, - * this returns zero. + * This represents the size of the address space that V8 can directly address + * and in which it allocates its objects. */ static size_t GetSandboxSizeInBytes(); - V8_DEPRECATE_SOON("Use GetSandboxSizeInBytes()") - static size_t GetVirtualMemoryCageSizeInBytes() { - return GetSandboxSizeInBytes(); - } /** - * Returns whether the sandbox is configured securely. + * Returns the size of the address space reservation backing the sandbox. * - * If V8 cannot create a proper sandbox, it will fall back to creating a - * sandbox that doesn't have the desired security properties but at least - * still allows V8 to function. This API can be used to determine if such an - * insecure sandbox is being used, in which case it will return false. + * This may be larger than the sandbox (i.e. |GetSandboxSizeInBytes()|) due + * to surrounding guard regions, or may be smaller than the sandbox in case a + * fallback sandbox is being used, which will use a smaller virtual address + * space reservation. In the latter case this will also be different from + * |GetSandboxAddressSpace()->size()| as that will cover a larger part of the + * address space than what has actually been reserved. */ - static bool IsSandboxConfiguredSecurely(); - V8_DEPRECATE_SOON("Use IsSandboxConfiguredSecurely()") - static bool IsUsingSecureVirtualMemoryCage() { - return IsSandboxConfiguredSecurely(); - } -#endif + static size_t GetSandboxReservationSizeInBytes(); +#endif // V8_ENABLE_SANDBOX /** * Activate trap-based bounds checking for WebAssembly. @@ -273,7 +249,7 @@ class V8_EXPORT V8 { * exceptions in V8-generated code. */ static void SetUnhandledExceptionCallback( - UnhandledExceptionCallback unhandled_exception_callback); + UnhandledExceptionCallback callback); #endif /** @@ -281,8 +257,7 @@ class V8_EXPORT V8 { * v8 has encountered a fatal failure to allocate memory and is about to * terminate. */ - - static void SetFatalMemoryErrorCallback(OOMErrorCallback oom_error_callback); + static void SetFatalMemoryErrorCallback(OOMErrorCallback callback); /** * Get statistics about the shared memory usage. @@ -295,8 +270,7 @@ class V8_EXPORT V8 { enum BuildConfigurationFeatures { kPointerCompression = 1 << 0, k31BitSmis = 1 << 1, - kSandboxedExternalPointers = 1 << 2, - kSandbox = 1 << 3, + kSandbox = 1 << 2, }; /** diff --git a/NativeScript/include/v8-inspector.h b/NativeScript/include/v8-inspector.h index ce5430bd..563ad196 100644 --- a/NativeScript/include/v8-inspector.h +++ b/NativeScript/include/v8-inspector.h @@ -32,19 +32,19 @@ namespace Debugger { namespace API { class SearchMatch; } -} +} // namespace Debugger namespace Runtime { namespace API { class RemoteObject; class StackTrace; class StackTraceId; -} -} +} // namespace API +} // namespace Runtime namespace Schema { namespace API { class Domain; } -} +} // namespace Schema } // namespace protocol class V8_EXPORT StringView { @@ -134,6 +134,13 @@ class V8_EXPORT V8DebuggerId { int64_t m_second = 0; }; +struct V8_EXPORT V8StackFrame { + StringView sourceURL; + StringView functionName; + int lineNumber; + int columnNumber; +}; + class V8_EXPORT V8StackTrace { public: virtual StringView firstNonEmptySourceURL() const = 0; @@ -151,6 +158,8 @@ class V8_EXPORT V8StackTrace { // Safe to pass between threads, drops async chain. virtual std::unique_ptr clone() = 0; + + virtual std::vector frames() const = 0; }; class V8_EXPORT V8InspectorSession { @@ -203,14 +212,17 @@ class V8_EXPORT V8InspectorSession { std::unique_ptr* objectGroup) = 0; virtual void releaseObjectGroup(StringView) = 0; virtual void triggerPreciseCoverageDeltaUpdate(StringView occasion) = 0; + + // Prepare for shutdown (disables debugger pausing, etc.). + virtual void stop() = 0; }; class V8_EXPORT WebDriverValue { public: - explicit WebDriverValue(StringView type, v8::MaybeLocal value = {}) - : type(type), value(value) {} - - StringView type; + explicit WebDriverValue(std::unique_ptr type, + v8::MaybeLocal value = {}) + : type(std::move(type)), value(value) {} + std::unique_ptr type; v8::MaybeLocal value; }; @@ -219,6 +231,9 @@ class V8_EXPORT V8InspectorClient { virtual ~V8InspectorClient() = default; virtual void runMessageLoopOnPause(int contextGroupId) {} + virtual void runMessageLoopOnInstrumentationPause(int contextGroupId) { + runMessageLoopOnPause(contextGroupId); + } virtual void quitMessageLoopOnPause() {} virtual void runIfWaitingForDebugger(int contextGroupId) {} @@ -361,9 +376,15 @@ class V8_EXPORT V8Inspector { virtual void sendNotification(std::unique_ptr message) = 0; virtual void flushProtocolNotifications() = 0; }; - virtual std::unique_ptr connect(int contextGroupId, - Channel*, - StringView state) = 0; + enum ClientTrustLevel { kUntrusted, kFullyTrusted }; + enum SessionPauseState { kWaitingForDebugger, kNotWaitingForDebugger }; + // TODO(chromium:1352175): remove default value once downstream change lands. + virtual std::unique_ptr connect( + int contextGroupId, Channel*, StringView state, + ClientTrustLevel client_trust_level, + SessionPauseState = kNotWaitingForDebugger) { + return nullptr; + } // API methods. virtual std::unique_ptr createStackTrace( diff --git a/NativeScript/include/v8-internal.h b/NativeScript/include/v8-internal.h index 37c5b336..53837aa5 100644 --- a/NativeScript/include/v8-internal.h +++ b/NativeScript/include/v8-internal.h @@ -8,6 +8,8 @@ #include #include #include + +#include #include #include "v8-version.h" // NOLINT(build/include_directory) @@ -50,6 +52,7 @@ const int kHeapObjectTag = 1; const int kWeakHeapObjectTag = 3; const int kHeapObjectTagSize = 2; const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; +const intptr_t kHeapObjectReferenceTagMask = 1 << (kHeapObjectTagSize - 1); // Tag information for fowarding pointers stored in object headers. // 0b00 at the lowest 2 bits in the header indicates that the map word is a @@ -157,15 +160,7 @@ V8_INLINE static constexpr internal::Address IntToSmi(int value) { * Sandbox related types, constants, and functions. */ constexpr bool SandboxIsEnabled() { -#ifdef V8_SANDBOX - return true; -#else - return false; -#endif -} - -constexpr bool SandboxedExternalPointersAreEnabled() { -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS +#ifdef V8_ENABLE_SANDBOX return true; #else return false; @@ -176,19 +171,18 @@ constexpr bool SandboxedExternalPointersAreEnabled() { // for example by storing them as offset rather than as raw pointers. using SandboxedPointer_t = Address; -// ExternalPointers point to objects located outside the sandbox. When sandboxed -// external pointers are enabled, these are stored in an external pointer table -// and referenced from HeapObjects through indices. -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS -using ExternalPointer_t = uint32_t; -#else -using ExternalPointer_t = Address; -#endif - -#ifdef V8_SANDBOX_IS_AVAILABLE +#ifdef V8_ENABLE_SANDBOX // Size of the sandbox, excluding the guard regions surrounding it. +#ifdef V8_TARGET_OS_ANDROID +// On Android, most 64-bit devices seem to be configured with only 39 bits of +// virtual address space for userspace. As such, limit the sandbox to 128GB (a +// quarter of the total available address space). +constexpr size_t kSandboxSizeLog2 = 37; // 128 GB +#else +// Everywhere else use a 1TB sandbox. constexpr size_t kSandboxSizeLog2 = 40; // 1 TB +#endif // V8_TARGET_OS_ANDROID constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2; // Required alignment of the sandbox. For simplicity, we require the @@ -213,20 +207,6 @@ static_assert((kSandboxGuardRegionSize % kSandboxAlignment) == 0, "The size of the guard regions around the sandbox must be a " "multiple of its required alignment."); -// Minimum size of the sandbox, excluding the guard regions surrounding it. If -// the virtual memory reservation for the sandbox fails, its size is currently -// halved until either the reservation succeeds or the minimum size is reached. -// A minimum of 32GB allows the 4GB pointer compression region as well as the -// ArrayBuffer partition and two 10GB Wasm memory cages to fit into the -// sandbox. 32GB should also be the minimum possible size of the userspace -// address space as there are some machine configurations with only 36 virtual -// address bits. -constexpr size_t kSandboxMinimumSize = 32ULL * GB; - -static_assert(kSandboxMinimumSize <= kSandboxSize, - "The minimal size of the sandbox must be smaller or equal to the " - "regular size."); - // On OSes where reserving virtual memory is too expensive to reserve the // entire address space backing the sandbox, notably Windows pre 8.1, we create // a partially reserved sandbox that doesn't actually reserve most of the @@ -239,82 +219,258 @@ static_assert(kSandboxMinimumSize <= kSandboxSize, // well as the ArrayBuffer partition. constexpr size_t kSandboxMinimumReservationSize = 8ULL * GB; -static_assert(kSandboxMinimumSize > kPtrComprCageReservationSize, - "The sandbox must be larger than the pointer compression cage " - "contained within it."); static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize, "The minimum reservation size for a sandbox must be larger than " "the pointer compression cage contained within it."); -// For now, even if the sandbox is enabled, we still allow backing stores to be -// allocated outside of it as fallback. This will simplify the initial rollout. -// However, if sandboxed pointers are also enabled, we must always place -// backing stores inside the sandbox as they will be referenced though them. -#ifdef V8_SANDBOXED_POINTERS -constexpr bool kAllowBackingStoresOutsideSandbox = false; -#else -constexpr bool kAllowBackingStoresOutsideSandbox = true; -#endif // V8_SANDBOXED_POINTERS +// The maximum buffer size allowed inside the sandbox. This is mostly dependent +// on the size of the guard regions around the sandbox: an attacker must not be +// able to construct a buffer that appears larger than the guard regions and +// thereby "reach out of" the sandbox. +constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1; +static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize, + "The maximum allowed buffer size must not be larger than the " + "sandbox's guard regions"); + +constexpr size_t kBoundedSizeShift = 29; +static_assert(1ULL << (64 - kBoundedSizeShift) == + kMaxSafeBufferSizeForSandbox + 1, + "The maximum size of a BoundedSize must be synchronized with the " + "kMaxSafeBufferSizeForSandbox"); + +#endif // V8_ENABLE_SANDBOX + +#ifdef V8_COMPRESS_POINTERS +#ifdef V8_TARGET_OS_ANDROID // The size of the virtual memory reservation for an external pointer table. // This determines the maximum number of entries in a table. Using a maximum // size allows omitting bounds checks on table accesses if the indices are // guaranteed (e.g. through shifting) to be below the maximum index. This // value must be a power of two. -static const size_t kExternalPointerTableReservationSize = 128 * MB; - -// The maximum number of entries in an external pointer table. -static const size_t kMaxSandboxedExternalPointers = - kExternalPointerTableReservationSize / kApiSystemPointerSize; +static const size_t kExternalPointerTableReservationSize = 512 * MB; // The external pointer table indices stored in HeapObjects as external // pointers are shifted to the left by this amount to guarantee that they are // smaller than the maximum table size. -static const uint32_t kExternalPointerIndexShift = 8; -static_assert((1 << (32 - kExternalPointerIndexShift)) == - kMaxSandboxedExternalPointers, +static const uint32_t kExternalPointerIndexShift = 6; +#else +static const size_t kExternalPointerTableReservationSize = 1024 * MB; +static const uint32_t kExternalPointerIndexShift = 5; +#endif // V8_TARGET_OS_ANDROID + +// The maximum number of entries in an external pointer table. +static const size_t kMaxExternalPointers = + kExternalPointerTableReservationSize / kApiSystemPointerSize; +static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers, "kExternalPointerTableReservationSize and " "kExternalPointerIndexShift don't match"); -#endif // V8_SANDBOX_IS_AVAILABLE - -// If sandboxed external pointers are enabled, these tag values will be ORed -// with the external pointers in the external pointer table to prevent use of -// pointers of the wrong type. When a pointer is loaded, it is ANDed with the -// inverse of the expected type's tag. The tags are constructed in a way that -// guarantees that a failed type check will result in one or more of the top -// bits of the pointer to be set, rendering the pointer inacessible. Besides -// the type tag bits (48 through 62), the tags also have the GC mark bit (63) -// set, so that the mark bit is automatically set when a pointer is written -// into the external pointer table (in which case it is clearly alive) and is -// cleared when the pointer is loaded. The exception to this is the free entry -// tag, which doesn't have the mark bit set, as the entry is not alive. This +#else // !V8_COMPRESS_POINTERS + +// Needed for the V8.SandboxedExternalPointersCount histogram. +static const size_t kMaxExternalPointers = 0; + +#endif // V8_COMPRESS_POINTERS + +// A ExternalPointerHandle represents a (opaque) reference to an external +// pointer that can be stored inside the sandbox. A ExternalPointerHandle has +// meaning only in combination with an (active) Isolate as it references an +// external pointer stored in the currently active Isolate's +// ExternalPointerTable. Internally, an ExternalPointerHandles is simply an +// index into an ExternalPointerTable that is shifted to the left to guarantee +// that it is smaller than the size of the table. +using ExternalPointerHandle = uint32_t; + +// ExternalPointers point to objects located outside the sandbox. When +// sandboxed external pointers are enabled, these are stored on heap as +// ExternalPointerHandles, otherwise they are simply raw pointers. +#ifdef V8_ENABLE_SANDBOX +using ExternalPointer_t = ExternalPointerHandle; +#else +using ExternalPointer_t = Address; +#endif + +// When the sandbox is enabled, external pointers are stored in an external +// pointer table and are referenced from HeapObjects through an index (a +// "handle"). When stored in the table, the pointers are tagged with per-type +// tags to prevent type confusion attacks between different external objects. +// Besides type information bits, these tags also contain the GC marking bit +// which indicates whether the pointer table entry is currently alive. When a +// pointer is written into the table, the tag is ORed into the top bits. When +// that pointer is later loaded from the table, it is ANDed with the inverse of +// the expected tag. If the expected and actual type differ, this will leave +// some of the top bits of the pointer set, rendering the pointer inaccessible. +// The AND operation also removes the GC marking bit from the pointer. +// +// The tags are constructed such that UNTAG(TAG(0, T1), T2) != 0 for any two +// (distinct) tags T1 and T2. In practice, this is achieved by generating tags +// that all have the same number of zeroes and ones but different bit patterns. +// With N type tag bits, this allows for (N choose N/2) possible type tags. +// Besides the type tag bits, the tags also have the GC marking bit set so that +// the marking bit is automatically set when a pointer is written into the +// external pointer table (in which case it is clearly alive) and is cleared +// when the pointer is loaded. The exception to this is the free entry tag, +// which doesn't have the mark bit set, as the entry is not alive. This // construction allows performing the type check and removing GC marking bits -// (the MSB) from the pointer at the same time. -// Note: this scheme assumes a 48-bit address space and will likely break if -// more virtual address bits are used. -constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000; +// from the pointer in one efficient operation (bitwise AND). The number of +// available bits is limited in the following way: on x64, bits [47, 64) are +// generally available for tagging (userspace has 47 address bits available). +// On Arm64, userspace typically has a 40 or 48 bit address space. However, due +// to top-byte ignore (TBI) and memory tagging (MTE), the top byte is unusable +// for type checks as type-check failures would go unnoticed or collide with +// MTE bits. Some bits of the top byte can, however, still be used for the GC +// marking bit. The bits available for the type tags are therefore limited to +// [48, 56), i.e. (8 choose 4) = 70 different types. +// The following options exist to increase the number of possible types: +// - Using multiple ExternalPointerTables since tags can safely be reused +// across different tables +// - Using "extended" type checks, where additional type information is stored +// either in an adjacent pointer table entry or at the pointed-to location +// - Using a different tagging scheme, for example based on XOR which would +// allow for 2**8 different tags but require a separate operation to remove +// the marking bit +// +// The external pointer sandboxing mechanism ensures that every access to an +// external pointer field will result in a valid pointer of the expected type +// even in the presence of an attacker able to corrupt memory inside the +// sandbox. However, if any data related to the external object is stored +// inside the sandbox it may still be corrupted and so must be validated before +// use or moved into the external object. Further, an attacker will always be +// able to substitute different external pointers of the same type for each +// other. Therefore, code using external pointers must be written in a +// "substitution-safe" way, i.e. it must always be possible to substitute +// external pointers of the same type without causing memory corruption outside +// of the sandbox. Generally this is achieved by referencing any group of +// related external objects through a single external pointer. +// +// Currently we use bit 62 for the marking bit which should always be unused as +// it's part of the non-canonical address range. When Arm's top-byte ignore +// (TBI) is enabled, this bit will be part of the ignored byte, and we assume +// that the Embedder is not using this byte (really only this one bit) for any +// other purpose. This bit also does not collide with the memory tagging +// extension (MTE) which would use bits [56, 60). +// +// External pointer tables are also available even when the sandbox is off but +// pointer compression is on. In that case, the mechanism can be used to easy +// alignment requirements as it turns unaligned 64-bit raw pointers into +// aligned 32-bit indices. To "opt-in" to the external pointer table mechanism +// for this purpose, instead of using the ExternalPointer accessors one needs to +// use ExternalPointerHandles directly and use them to access the pointers in an +// ExternalPointerTable. +constexpr uint64_t kExternalPointerMarkBit = 1ULL << 62; +constexpr uint64_t kExternalPointerTagMask = 0x40ff000000000000; constexpr uint64_t kExternalPointerTagShift = 48; -#define MAKE_TAG(v) (static_cast(v) << kExternalPointerTagShift) + +// All possible 8-bit type tags. +// These are sorted so that tags can be grouped together and it can efficiently +// be checked if a tag belongs to a given group. See for example the +// IsSharedExternalPointerType routine. +constexpr uint64_t kAllExternalPointerTypeTags[] = { + 0b00001111, 0b00010111, 0b00011011, 0b00011101, 0b00011110, 0b00100111, + 0b00101011, 0b00101101, 0b00101110, 0b00110011, 0b00110101, 0b00110110, + 0b00111001, 0b00111010, 0b00111100, 0b01000111, 0b01001011, 0b01001101, + 0b01001110, 0b01010011, 0b01010101, 0b01010110, 0b01011001, 0b01011010, + 0b01011100, 0b01100011, 0b01100101, 0b01100110, 0b01101001, 0b01101010, + 0b01101100, 0b01110001, 0b01110010, 0b01110100, 0b01111000, 0b10000111, + 0b10001011, 0b10001101, 0b10001110, 0b10010011, 0b10010101, 0b10010110, + 0b10011001, 0b10011010, 0b10011100, 0b10100011, 0b10100101, 0b10100110, + 0b10101001, 0b10101010, 0b10101100, 0b10110001, 0b10110010, 0b10110100, + 0b10111000, 0b11000011, 0b11000101, 0b11000110, 0b11001001, 0b11001010, + 0b11001100, 0b11010001, 0b11010010, 0b11010100, 0b11011000, 0b11100001, + 0b11100010, 0b11100100, 0b11101000, 0b11110000}; + +#define TAG(i) \ + ((kAllExternalPointerTypeTags[i] << kExternalPointerTagShift) | \ + kExternalPointerMarkBit) + // clang-format off + +// When adding new tags, please ensure that the code using these tags is +// "substitution-safe", i.e. still operate safely if external pointers of the +// same type are swapped by an attacker. See comment above for more details. + +// Shared external pointers are owned by the shared Isolate and stored in the +// shared external pointer table associated with that Isolate, where they can +// be accessed from multiple threads at the same time. The objects referenced +// in this way must therefore always be thread-safe. +#define SHARED_EXTERNAL_POINTER_TAGS(V) \ + V(kFirstSharedTag, TAG(0)) \ + V(kWaiterQueueNodeTag, TAG(0)) \ + V(kExternalStringResourceTag, TAG(1)) \ + V(kExternalStringResourceDataTag, TAG(2)) \ + V(kLastSharedTag, TAG(2)) + +// External pointers using these tags are kept in a per-Isolate external +// pointer table and can only be accessed when this Isolate is active. +#define PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) \ + V(kForeignForeignAddressTag, TAG(10)) \ + V(kNativeContextMicrotaskQueueTag, TAG(11)) \ + V(kEmbedderDataSlotPayloadTag, TAG(12)) \ +/* This tag essentially stands for a `void*` pointer in the V8 API, and */ \ +/* it is the Embedder's responsibility to ensure type safety (against */ \ +/* substitution) and lifetime validity of these objects. */ \ + V(kExternalObjectValueTag, TAG(13)) \ + V(kCallHandlerInfoCallbackTag, TAG(14)) \ + V(kAccessorInfoGetterTag, TAG(15)) \ + V(kAccessorInfoSetterTag, TAG(16)) \ + V(kWasmInternalFunctionCallTargetTag, TAG(17)) \ + V(kWasmTypeInfoNativeTypeTag, TAG(18)) \ + V(kWasmExportedFunctionDataSignatureTag, TAG(19)) \ + V(kWasmContinuationJmpbufTag, TAG(20)) \ + V(kArrayBufferExtensionTag, TAG(21)) + +// All external pointer tags. +#define ALL_EXTERNAL_POINTER_TAGS(V) \ + SHARED_EXTERNAL_POINTER_TAGS(V) \ + PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) + +#define EXTERNAL_POINTER_TAG_ENUM(Name, Tag) Name = Tag, +#define MAKE_TAG(HasMarkBit, TypeTag) \ + ((static_cast(TypeTag) << kExternalPointerTagShift) | \ + (HasMarkBit ? kExternalPointerMarkBit : 0)) enum ExternalPointerTag : uint64_t { - kExternalPointerNullTag = MAKE_TAG(0b0000000000000000), - kExternalPointerFreeEntryTag = MAKE_TAG(0b0111111110000000), - kExternalStringResourceTag = MAKE_TAG(0b1000000011111111), - kExternalStringResourceDataTag = MAKE_TAG(0b1000000101111111), - kForeignForeignAddressTag = MAKE_TAG(0b1000000110111111), - kNativeContextMicrotaskQueueTag = MAKE_TAG(0b1000000111011111), - kEmbedderDataSlotPayloadTag = MAKE_TAG(0b1000000111101111), - kCodeEntryPointTag = MAKE_TAG(0b1000000111110111), - kExternalObjectValueTag = MAKE_TAG(0b1000000111111011), + // Empty tag value. Mostly used as placeholder. + kExternalPointerNullTag = MAKE_TAG(0, 0b00000000), + // External pointer tag that will match any external pointer. Use with care! + kAnyExternalPointerTag = MAKE_TAG(1, 0b11111111), + // The free entry tag has all type bits set so every type check with a + // different type fails. It also doesn't have the mark bit set as free + // entries are (by definition) not alive. + kExternalPointerFreeEntryTag = MAKE_TAG(0, 0b11111111), + // Evacuation entries are used during external pointer table compaction. + kExternalPointerEvacuationEntryTag = MAKE_TAG(1, 0b11100111), + + ALL_EXTERNAL_POINTER_TAGS(EXTERNAL_POINTER_TAG_ENUM) }; -// clang-format on + #undef MAKE_TAG +#undef TAG +#undef EXTERNAL_POINTER_TAG_ENUM + +// clang-format on + +// True if the external pointer must be accessed from the shared isolate's +// external pointer table. +V8_INLINE static constexpr bool IsSharedExternalPointerType( + ExternalPointerTag tag) { + return tag >= kFirstSharedTag && tag <= kLastSharedTag; +} -// Converts encoded external pointer to address. -V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate, - ExternalPointer_t pointer, - ExternalPointerTag tag); +// Sanity checks. +#define CHECK_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ + static_assert(IsSharedExternalPointerType(Tag)); +#define CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ + static_assert(!IsSharedExternalPointerType(Tag)); + +SHARED_EXTERNAL_POINTER_TAGS(CHECK_SHARED_EXTERNAL_POINTER_TAGS) +PER_ISOLATE_EXTERNAL_POINTER_TAGS(CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS) + +#undef CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS +#undef CHECK_SHARED_EXTERNAL_POINTER_TAGS + +#undef SHARED_EXTERNAL_POINTER_TAGS +#undef EXTERNAL_POINTER_TAGS // {obj} must be the raw tagged pointer representation of a HeapObject // that's guaranteed to never be in ReadOnlySpace. @@ -324,9 +480,6 @@ V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); // 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); - -V8_EXPORT bool CanHaveInternalField(int instance_type); - /** * This class exports constants and functionality from within v8 that * is necessary to implement inline functions in the v8 api. Don't @@ -354,8 +507,10 @@ class Internals { static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; static const int kEmbedderDataSlotSize = kApiSystemPointerSize; -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - static const int kEmbedderDataSlotRawPayloadOffset = kApiTaggedSize; +#ifdef V8_ENABLE_SANDBOX + static const int kEmbedderDataSlotExternalPointerOffset = kApiTaggedSize; +#else + static const int kEmbedderDataSlotExternalPointerOffset = 0; #endif static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; static const int kStringRepresentationAndEncodingMask = 0x0f; @@ -365,15 +520,21 @@ class Internals { static const uint32_t kNumIsolateDataSlots = 4; static const int kStackGuardSize = 7 * kApiSystemPointerSize; - static const int kBuiltinTier0EntryTableSize = 9 * kApiSystemPointerSize; - static const int kBuiltinTier0TableSize = 9 * kApiSystemPointerSize; + static const int kBuiltinTier0EntryTableSize = 7 * kApiSystemPointerSize; + static const int kBuiltinTier0TableSize = 7 * kApiSystemPointerSize; + + // ExternalPointerTable layout guarantees. + static const int kExternalPointerTableBufferOffset = 0; + static const int kExternalPointerTableSize = 4 * kApiSystemPointerSize; // IsolateData layout guarantees. static const int kIsolateCageBaseOffset = 0; static const int kIsolateStackGuardOffset = kIsolateCageBaseOffset + kApiSystemPointerSize; - static const int kBuiltinTier0EntryTableOffset = + static const int kVariousBooleanFlagsOffset = kIsolateStackGuardOffset + kStackGuardSize; + static const int kBuiltinTier0EntryTableOffset = + kVariousBooleanFlagsOffset + 8; static const int kBuiltinTier0TableOffset = kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize; static const int kIsolateEmbedderDataOffset = @@ -386,14 +547,17 @@ class Internals { kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; static const int kIsolateLongTaskStatsCounterOffset = kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; +#ifdef V8_COMPRESS_POINTERS + static const int kIsolateExternalPointerTableOffset = + kIsolateLongTaskStatsCounterOffset + kApiSizetSize; + static const int kIsolateSharedExternalPointerTableAddressOffset = + kIsolateExternalPointerTableOffset + kExternalPointerTableSize; + static const int kIsolateRootsOffset = + kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize; +#else static const int kIsolateRootsOffset = kIsolateLongTaskStatsCounterOffset + kApiSizetSize; - - static const int kExternalPointerTableBufferOffset = 0; - static const int kExternalPointerTableCapacityOffset = - kExternalPointerTableBufferOffset + kApiSystemPointerSize; - static const int kExternalPointerTableFreelistHeadOffset = - kExternalPointerTableCapacityOffset + kApiInt32Size; +#endif static const int kUndefinedValueRootIndex = 4; static const int kTheHoleValueRootIndex = 5; @@ -404,9 +568,10 @@ class Internals { static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; - static const int kNodeStateMask = 0x7; + static const int kNodeStateMask = 0x3; static const int kNodeStateIsWeakValue = 2; - static const int kNodeStateIsPendingValue = 3; + + static const int kTracedNodeClassIdOffset = kApiSystemPointerSize; static const int kFirstNonstringType = 0x80; static const int kOddballType = 0x83; @@ -481,6 +646,18 @@ class Internals { return representation == kExternalTwoByteRepresentationTag; } + V8_INLINE static constexpr bool CanHaveInternalField(int instance_type) { + static_assert(kJSObjectType + 1 == kFirstJSApiObjectType); + static_assert(kJSObjectType < kLastJSApiObjectType); + static_assert(kFirstJSApiObjectType < kLastJSApiObjectType); + // Check for IsJSObject() || IsJSSpecialApiObject() || IsJSApiObject() + return instance_type == kJSSpecialApiObjectType || + // inlined version of base::IsInRange + (static_cast(static_cast(instance_type) - + static_cast(kJSObjectType)) <= + static_cast(kLastJSApiObjectType - kJSObjectType)); + } + V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) { uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; return *addr & static_cast(1U << shift); @@ -532,6 +709,25 @@ class Internals { return reinterpret_cast(addr); } +#ifdef V8_ENABLE_SANDBOX + V8_INLINE static internal::Address* GetExternalPointerTableBase( + v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateExternalPointerTableOffset + + kExternalPointerTableBufferOffset; + return *reinterpret_cast(addr); + } + + V8_INLINE static internal::Address* GetSharedExternalPointerTableBase( + v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateSharedExternalPointerTableAddressOffset; + addr = *reinterpret_cast(addr); + addr += kExternalPointerTableBufferOffset; + return *reinterpret_cast(addr); + } +#endif + template V8_INLINE static T ReadRawField(internal::Address heap_object_ptr, int offset) { @@ -572,38 +768,37 @@ class Internals { #endif } - V8_INLINE static internal::Isolate* GetIsolateForSandbox( - internal::Address obj) { -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - return internal::IsolateFromNeverReadOnlySpaceObject(obj); + V8_INLINE static v8::Isolate* GetIsolateForSandbox(internal::Address obj) { +#ifdef V8_ENABLE_SANDBOX + return reinterpret_cast( + internal::IsolateFromNeverReadOnlySpaceObject(obj)); #else // Not used in non-sandbox mode. return nullptr; #endif } - V8_INLINE static Address DecodeExternalPointer( - const Isolate* isolate, ExternalPointer_t encoded_pointer, - ExternalPointerTag tag) { -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - return internal::DecodeExternalPointerImpl(isolate, encoded_pointer, tag); -#else - return encoded_pointer; -#endif - } - + template V8_INLINE static internal::Address ReadExternalPointerField( - internal::Isolate* isolate, internal::Address heap_object_ptr, int offset, - ExternalPointerTag tag) { -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - internal::ExternalPointer_t encoded_value = - ReadRawField(heap_object_ptr, offset); - // We currently have to treat zero as nullptr in embedder slots. - return encoded_value ? DecodeExternalPointer(isolate, encoded_value, tag) - : 0; + v8::Isolate* isolate, internal::Address heap_object_ptr, int offset) { +#ifdef V8_ENABLE_SANDBOX + static_assert(tag != kExternalPointerNullTag); + // See src/sandbox/external-pointer-table-inl.h. Logic duplicated here so + // it can be inlined and doesn't require an additional call. + internal::Address* table = IsSharedExternalPointerType(tag) + ? GetSharedExternalPointerTableBase(isolate) + : GetExternalPointerTableBase(isolate); + internal::ExternalPointerHandle handle = + ReadRawField(heap_object_ptr, offset); + uint32_t index = handle >> kExternalPointerIndexShift; + std::atomic* ptr = + reinterpret_cast*>(&table[index]); + internal::Address entry = + std::atomic_load_explicit(ptr, std::memory_order_relaxed); + return entry & ~tag; #else return ReadRawField
(heap_object_ptr, offset); -#endif +#endif // V8_ENABLE_SANDBOX } #ifdef V8_COMPRESS_POINTERS @@ -652,7 +847,7 @@ class BackingStoreBase {}; // The maximum value in enum GarbageCollectionReason, defined in heap.h. // This is needed for histograms sampling garbage collection reasons. -constexpr int kGarbageCollectionReasonMaxValue = 25; +constexpr int kGarbageCollectionReasonMaxValue = 27; } // namespace internal diff --git a/NativeScript/include/v8-isolate.h b/NativeScript/include/v8-isolate.h index 2849d7ca..4571b2c3 100644 --- a/NativeScript/include/v8-isolate.h +++ b/NativeScript/include/v8-isolate.h @@ -194,6 +194,11 @@ enum RAILMode : unsigned { */ enum class MemoryPressureLevel { kNone, kModerate, kCritical }; +/** + * Indicator for the stack state. + */ +using StackState = cppgc::EmbedderStackState; + /** * Isolate represents an isolated instance of the V8 engine. V8 isolates have * completely separate states. Objects from one isolate must not be used in @@ -211,6 +216,8 @@ class V8_EXPORT Isolate { CreateParams(); ~CreateParams(); + ALLOW_COPY_AND_MOVE_WITH_DEPRECATED_FIELDS(CreateParams) + /** * Allows the host application to provide the address of a function that is * notified each time code is added, moved or removed. @@ -226,7 +233,7 @@ class V8_EXPORT Isolate { * Explicitly specify a startup snapshot blob. The embedder owns the blob. * The embedder *must* ensure that the snapshot is from a trusted source. */ - StartupData* snapshot_blob = nullptr; + const StartupData* snapshot_blob = nullptr; /** * Enables the host application to provide a mechanism for recording @@ -287,12 +294,6 @@ class V8_EXPORT Isolate { */ FatalErrorCallback fatal_error_callback = nullptr; OOMErrorCallback oom_error_callback = nullptr; - - /** - * The following parameter is experimental and may change significantly. - * This is currently for internal testing. - */ - Isolate* experimental_attach_to_shared_isolate = nullptr; }; /** @@ -301,16 +302,18 @@ class V8_EXPORT Isolate { */ class V8_EXPORT V8_NODISCARD Scope { public: - explicit Scope(Isolate* isolate) : isolate_(isolate) { isolate->Enter(); } + explicit Scope(Isolate* isolate) : v8_isolate_(isolate) { + v8_isolate_->Enter(); + } - ~Scope() { isolate_->Exit(); } + ~Scope() { v8_isolate_->Exit(); } // Prevent copying of Scope objects. Scope(const Scope&) = delete; Scope& operator=(const Scope&) = delete; private: - Isolate* const isolate_; + Isolate* const v8_isolate_; }; /** @@ -331,7 +334,7 @@ class V8_EXPORT Isolate { private: OnFailure on_failure_; - Isolate* isolate_; + v8::Isolate* v8_isolate_; bool was_execution_allowed_assert_; bool was_execution_allowed_throws_; @@ -353,7 +356,7 @@ class V8_EXPORT Isolate { const AllowJavascriptExecutionScope&) = delete; private: - Isolate* isolate_; + Isolate* v8_isolate_; bool was_execution_allowed_assert_; bool was_execution_allowed_throws_; bool was_execution_allowed_dump_; @@ -376,7 +379,7 @@ class V8_EXPORT Isolate { const SuppressMicrotaskExecutionScope&) = delete; private: - internal::Isolate* const isolate_; + internal::Isolate* const i_isolate_; internal::MicrotaskQueue* const microtask_queue_; internal::Address previous_stack_height_; @@ -389,7 +392,7 @@ class V8_EXPORT Isolate { */ class V8_EXPORT V8_NODISCARD SafeForTerminationScope { public: - explicit SafeForTerminationScope(v8::Isolate* isolate); + explicit SafeForTerminationScope(v8::Isolate* v8_isolate); ~SafeForTerminationScope(); // Prevent copying of Scope objects. @@ -397,7 +400,7 @@ class V8_EXPORT Isolate { SafeForTerminationScope& operator=(const SafeForTerminationScope&) = delete; private: - internal::Isolate* isolate_; + internal::Isolate* i_isolate_; bool prev_value_; }; @@ -531,6 +534,10 @@ class V8_EXPORT Isolate { kInvalidatedMegaDOMProtector = 112, kFunctionPrototypeArguments = 113, kFunctionPrototypeCaller = 114, + kTurboFanOsrCompileStarted = 115, + kAsyncStackTaggingCreateTaskCall = 116, + kDurationFormat = 117, + kInvalidatedNumberStringPrototypeNoReplaceProtector = 118, // If you add new values here, you'll also need to update Chromium's: // web_feature.mojom, use_counter_callback.cc, and enums.xml. V8 changes to @@ -636,9 +643,6 @@ class V8_EXPORT Isolate { * This specifies the callback called by the upcoming dynamic * import() language feature to load modules. */ - V8_DEPRECATED("Use HostImportModuleDynamicallyCallback") - void SetHostImportModuleDynamicallyCallback( - HostImportModuleDynamicallyWithImportAssertionsCallback callback); void SetHostImportModuleDynamicallyCallback( HostImportModuleDynamicallyCallback callback); @@ -839,12 +843,6 @@ class V8_EXPORT Isolate { */ int64_t AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes); - /** - * Returns the number of phantom handles without callbacks that were reset - * by the garbage collector since the last call to this function. - */ - size_t NumberOfPhantomHandleResetsSinceLastCall(); - /** * Returns heap profiler for this isolate. Will return NULL until the isolate * is initialized. @@ -927,25 +925,10 @@ class V8_EXPORT Isolate { void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr); void RemoveGCPrologueCallback(GCCallback callback); - /** - * Sets the embedder heap tracer for the isolate. - * SetEmbedderHeapTracer cannot be used simultaneously with AttachCppHeap. - */ - void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer); - - /* - * Gets the currently active heap tracer for the isolate that was set with - * SetEmbedderHeapTracer. - */ - EmbedderHeapTracer* GetEmbedderHeapTracer(); - /** * Sets an embedder roots handle that V8 should consider when performing - * non-unified heap garbage collections. - * - * Using only EmbedderHeapTracer automatically sets up a default handler. - * The intended use case is for setting a custom handler after invoking - * `AttachCppHeap()`. + * non-unified heap garbage collections. The intended use case is for setting + * a custom handler after invoking `AttachCppHeap()`. * * V8 does not take ownership of the handler. */ @@ -955,22 +938,18 @@ class V8_EXPORT Isolate { * Attaches a managed C++ heap as an extension to the JavaScript heap. The * embedder maintains ownership of the CppHeap. At most one C++ heap can be * attached to V8. - * AttachCppHeap cannot be used simultaneously with SetEmbedderHeapTracer. * - * This is an experimental feature and may still change significantly. + * Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see + * CppHeap. */ void AttachCppHeap(CppHeap*); /** * Detaches a managed C++ heap if one was attached using `AttachCppHeap()`. - * - * This is an experimental feature and may still change significantly. */ void DetachCppHeap(); /** - * This is an experimental feature and may still change significantly. - * \returns the C++ heap managed by V8. Only available if such a heap has been * attached using `AttachCppHeap()`. */ @@ -1163,9 +1142,8 @@ class V8_EXPORT Isolate { * LowMemoryNotification() instead to influence the garbage collection * schedule. */ - void RequestGarbageCollectionForTesting( - GarbageCollectionType type, - EmbedderHeapTracer::EmbedderStackState stack_state); + void RequestGarbageCollectionForTesting(GarbageCollectionType type, + StackState stack_state); /** * Set the callback to invoke for logging event. @@ -1350,11 +1328,13 @@ class V8_EXPORT Isolate { * V8 uses this notification to guide heuristics which may result in a * smaller memory footprint at the cost of reduced runtime performance. */ + V8_DEPRECATED("Use IsolateInBackgroundNotification() instead") void EnableMemorySavingsMode(); /** * Optional notification which will disable the memory savings mode. */ + V8_DEPRECATED("Use IsolateInBackgroundNotification() instead") void DisableMemorySavingsMode(); /** @@ -1523,14 +1503,23 @@ class V8_EXPORT Isolate { void SetWasmStreamingCallback(WasmStreamingCallback callback); + void SetWasmAsyncResolvePromiseCallback( + WasmAsyncResolvePromiseCallback callback); + void SetWasmLoadSourceMapCallback(WasmLoadSourceMapCallback callback); + V8_DEPRECATED("Wasm SIMD is always enabled") void SetWasmSimdEnabledCallback(WasmSimdEnabledCallback callback); + V8_DEPRECATED("Wasm exceptions are always enabled") void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback); - void SetWasmDynamicTieringEnabledCallback( - WasmDynamicTieringEnabledCallback callback); + /** + * Register callback to control whehter Wasm GC is enabled. + * The callback overwrites the value of the flag. + * If the callback returns true, it will also enable Wasm stringrefs. + */ + void SetWasmGCEnabledCallback(WasmGCEnabledCallback callback); void SetSharedArrayBufferConstructorEnabledCallback( SharedArrayBufferConstructorEnabledCallback callback); @@ -1598,19 +1587,6 @@ class V8_EXPORT Isolate { */ void VisitExternalResources(ExternalResourceVisitor* visitor); - /** - * Iterates through all the persistent handles in the current isolate's heap - * that have class_ids. - */ - void VisitHandlesWithClassIds(PersistentHandleVisitor* visitor); - - /** - * Iterates through all the persistent handles in the current isolate's heap - * that have class_ids and are weak to be marked as inactive if there is no - * pending activity for the handle. - */ - void VisitWeakHandles(PersistentHandleVisitor* visitor); - /** * Check if this isolate is in use. * True if at least one thread Enter'ed this isolate. diff --git a/NativeScript/include/v8-local-handle.h b/NativeScript/include/v8-local-handle.h index 5ae97408..633c5633 100644 --- a/NativeScript/include/v8-local-handle.h +++ b/NativeScript/include/v8-local-handle.h @@ -53,6 +53,7 @@ class Utils; namespace internal { template class CustomArguments; +class SamplingHeapProfiler; } // namespace internal namespace api_internal { @@ -86,7 +87,7 @@ class V8_EXPORT V8_NODISCARD HandleScope { static int NumberOfHandles(Isolate* isolate); V8_INLINE Isolate* GetIsolate() const { - return reinterpret_cast(isolate_); + return reinterpret_cast(i_isolate_); } HandleScope(const HandleScope&) = delete; @@ -97,7 +98,7 @@ class V8_EXPORT V8_NODISCARD HandleScope { void Initialize(Isolate* isolate); - static internal::Address* CreateHandle(internal::Isolate* isolate, + static internal::Address* CreateHandle(internal::Isolate* i_isolate, internal::Address value); private: @@ -108,7 +109,7 @@ class V8_EXPORT V8_NODISCARD HandleScope { void operator delete(void*, size_t); void operator delete[](void*, size_t); - internal::Isolate* isolate_; + internal::Isolate* i_isolate_; internal::Address* prev_next_; internal::Address* prev_limit_; @@ -313,6 +314,7 @@ class Local { friend class BasicTracedReference; template friend class TracedReference; + friend class v8::internal::SamplingHeapProfiler; explicit V8_INLINE Local(T* that) : val_(that) {} V8_INLINE static Local New(Isolate* isolate, T* that) { @@ -354,7 +356,7 @@ class MaybeLocal { /** * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, - * |false| is returned and |out| is left untouched. + * |false| is returned and |out| is assigned with nullptr. */ template V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local* out) const { @@ -445,7 +447,7 @@ class V8_EXPORT V8_NODISCARD SealHandleScope { void operator delete(void*, size_t); void operator delete[](void*, size_t); - internal::Isolate* const isolate_; + internal::Isolate* const i_isolate_; internal::Address* prev_limit_; int prev_sealed_level_; }; diff --git a/NativeScript/include/v8-locker.h b/NativeScript/include/v8-locker.h index 7ca5bf6e..22b7a876 100644 --- a/NativeScript/include/v8-locker.h +++ b/NativeScript/include/v8-locker.h @@ -121,17 +121,6 @@ class V8_EXPORT Locker { */ static bool IsLocked(Isolate* isolate); - /** - * Returns whether any v8::Locker has ever been used in this process. - * TODO(cbruni, chromium:1240851): Fix locking checks on a per-thread basis. - * The current implementation is quite confusing and leads to unexpected - * results if anybody uses v8::Locker in the current process. - */ - V8_DEPRECATE_SOON("This method will be removed.") - static bool WasEverUsed(); - V8_DEPRECATED("Use WasEverUsed instead") - static bool IsActive(); - // Disallow copying and assigning. Locker(const Locker&) = delete; void operator=(const Locker&) = delete; diff --git a/NativeScript/include/v8-maybe.h b/NativeScript/include/v8-maybe.h index 0532a510..8d3aeabe 100644 --- a/NativeScript/include/v8-maybe.h +++ b/NativeScript/include/v8-maybe.h @@ -5,6 +5,9 @@ #ifndef INCLUDE_V8_MAYBE_H_ #define INCLUDE_V8_MAYBE_H_ +#include +#include + #include "v8-internal.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) @@ -57,11 +60,20 @@ class Maybe { * Converts this Maybe<> to a value of type T. If this Maybe<> is * nothing (empty), V8 will crash the process. */ - V8_INLINE T FromJust() const { + V8_INLINE T FromJust() const& { if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); return value_; } + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), V8 will crash the process. + */ + V8_INLINE T FromJust() && { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + return std::move(value_); + } + /** * Converts this Maybe<> to a value of type T, using a default value if this * Maybe<> is nothing (empty). @@ -82,6 +94,7 @@ class Maybe { private: Maybe() : has_value_(false) {} explicit Maybe(const T& t) : has_value_(true), value_(t) {} + explicit Maybe(T&& t) : has_value_(true), value_(std::move(t)) {} bool has_value_; T value_; @@ -90,6 +103,8 @@ class Maybe { friend Maybe Nothing(); template friend Maybe Just(const U& u); + template >*> + friend Maybe Just(U&& u); }; template @@ -102,6 +117,14 @@ inline Maybe Just(const T& t) { return Maybe(t); } +// Don't use forwarding references here but instead use two overloads. +// Forwarding references only work when type deduction takes place, which is not +// the case for callsites such as Just(t). +template >* = nullptr> +inline Maybe Just(T&& t) { + return Maybe(std::move(t)); +} + // A template specialization of Maybe for the case of T = void. template <> class Maybe { diff --git a/NativeScript/include/v8-message.h b/NativeScript/include/v8-message.h index a1327641..09f9a0a9 100644 --- a/NativeScript/include/v8-message.h +++ b/NativeScript/include/v8-message.h @@ -70,7 +70,7 @@ class V8_EXPORT ScriptOrigin { bool resource_is_opaque = false, bool is_wasm = false, bool is_module = false, Local host_defined_options = Local()) - : isolate_(isolate), + : v8_isolate_(isolate), resource_name_(resource_name), resource_line_offset_(resource_line_offset), resource_column_offset_(resource_column_offset), @@ -87,14 +87,12 @@ class V8_EXPORT ScriptOrigin { V8_INLINE int ColumnOffset() const; V8_INLINE int ScriptId() const; V8_INLINE Local SourceMapUrl() const; - V8_DEPRECATE_SOON("Use GetHostDefinedOptions") - Local HostDefinedOptions() const; V8_INLINE Local GetHostDefinedOptions() const; V8_INLINE ScriptOriginOptions Options() const { return options_; } private: void VerifyHostDefinedOptions() const; - Isolate* isolate_; + Isolate* v8_isolate_; Local resource_name_; int resource_line_offset_; int resource_column_offset_; diff --git a/NativeScript/include/v8-metrics.h b/NativeScript/include/v8-metrics.h index d8e8bd86..418531e2 100644 --- a/NativeScript/include/v8-metrics.h +++ b/NativeScript/include/v8-metrics.h @@ -12,6 +12,7 @@ #include "v8-internal.h" // NOLINT(build/include_directory) #include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { @@ -96,16 +97,88 @@ struct GarbageCollectionYoungCycle { }; struct WasmModuleDecoded { + WasmModuleDecoded() = default; + WasmModuleDecoded(bool async, bool streamed, bool success, + size_t module_size_in_bytes, size_t function_count, + int64_t wall_clock_duration_in_us) + : async(async), + streamed(streamed), + success(success), + module_size_in_bytes(module_size_in_bytes), + function_count(function_count), + wall_clock_duration_in_us(wall_clock_duration_in_us) {} + + V8_DEPRECATED("Use the version without cpu_duration_in_us") + WasmModuleDecoded(bool async, bool streamed, bool success, + size_t module_size_in_bytes, size_t function_count, + int64_t wall_clock_duration_in_us, + int64_t cpu_duration_in_us) + : async(async), + streamed(streamed), + success(success), + module_size_in_bytes(module_size_in_bytes), + function_count(function_count), + wall_clock_duration_in_us(wall_clock_duration_in_us), + cpu_duration_in_us(cpu_duration_in_us) {} + + START_ALLOW_USE_DEPRECATED() + // Copy constructor and copy assignment operator are allowed to copy the + // {cpu_duration_in_us} field. + WasmModuleDecoded(const WasmModuleDecoded&) = default; + WasmModuleDecoded& operator=(const WasmModuleDecoded&) = default; + END_ALLOW_USE_DEPRECATED() + bool async = false; bool streamed = false; bool success = false; size_t module_size_in_bytes = 0; size_t function_count = 0; int64_t wall_clock_duration_in_us = -1; + V8_DEPRECATED("We do not collect cpu times any more") int64_t cpu_duration_in_us = -1; }; struct WasmModuleCompiled { + WasmModuleCompiled() = default; + + WasmModuleCompiled(bool async, bool streamed, bool cached, bool deserialized, + bool lazy, bool success, size_t code_size_in_bytes, + size_t liftoff_bailout_count, + int64_t wall_clock_duration_in_us) + : async(async), + streamed(streamed), + cached(cached), + deserialized(deserialized), + lazy(lazy), + success(success), + code_size_in_bytes(code_size_in_bytes), + liftoff_bailout_count(liftoff_bailout_count), + wall_clock_duration_in_us(wall_clock_duration_in_us) {} + + V8_DEPRECATED("Use the version without cpu_duration_in_us") + WasmModuleCompiled(bool async, bool streamed, bool cached, bool deserialized, + bool lazy, bool success, size_t code_size_in_bytes, + size_t liftoff_bailout_count, + int64_t wall_clock_duration_in_us, + int64_t cpu_duration_in_us) + : async(async), + streamed(streamed), + cached(cached), + deserialized(deserialized), + lazy(lazy), + success(success), + code_size_in_bytes(code_size_in_bytes), + liftoff_bailout_count(liftoff_bailout_count), + wall_clock_duration_in_us(wall_clock_duration_in_us), + cpu_duration_in_us(cpu_duration_in_us) {} + + START_ALLOW_USE_DEPRECATED() + // Copy constructor and copy assignment operator are allowed to copy the + // {cpu_duration_in_us} field. + WasmModuleCompiled(const WasmModuleCompiled&) = default; + WasmModuleCompiled& operator=(const WasmModuleCompiled&) = default; + END_ALLOW_USE_DEPRECATED() + bool async = false; bool streamed = false; bool cached = false; @@ -115,6 +188,7 @@ struct WasmModuleCompiled { size_t code_size_in_bytes = 0; size_t liftoff_bailout_count = 0; int64_t wall_clock_duration_in_us = -1; + V8_DEPRECATED("We do not collect cpu times any more") int64_t cpu_duration_in_us = -1; }; @@ -125,31 +199,10 @@ struct WasmModuleInstantiated { int64_t wall_clock_duration_in_us = -1; }; -struct WasmModuleTieredUp { - bool lazy = false; - size_t code_size_in_bytes = 0; - int64_t wall_clock_duration_in_us = -1; - int64_t cpu_duration_in_us = -1; -}; - struct WasmModulesPerIsolate { size_t count = 0; }; -#define V8_MAIN_THREAD_METRICS_EVENTS(V) \ - V(GarbageCollectionFullCycle) \ - V(GarbageCollectionFullMainThreadIncrementalMark) \ - V(GarbageCollectionFullMainThreadBatchedIncrementalMark) \ - V(GarbageCollectionFullMainThreadIncrementalSweep) \ - V(GarbageCollectionFullMainThreadBatchedIncrementalSweep) \ - V(GarbageCollectionYoungCycle) \ - V(WasmModuleDecoded) \ - V(WasmModuleCompiled) \ - V(WasmModuleInstantiated) \ - V(WasmModuleTieredUp) - -#define V8_THREAD_SAFE_METRICS_EVENTS(V) V(WasmModulesPerIsolate) - /** * This class serves as a base class for recording event-based metrics in V8. * There a two kinds of metrics, those which are expected to be thread-safe and @@ -159,19 +212,6 @@ struct WasmModulesPerIsolate { * background thread, it will be delayed and executed by the foreground task * runner. * - * The thread-safe events are listed in the V8_THREAD_SAFE_METRICS_EVENTS - * macro above while the main thread event are listed in - * V8_MAIN_THREAD_METRICS_EVENTS above. For the former, a virtual method - * AddMainThreadEvent(const E& event, v8::Context::Token token) will be - * generated and for the latter AddThreadSafeEvent(const E& event). - * - * Thread-safe events are not allowed to access the context and therefore do - * not carry a context ID with them. These IDs can be generated using - * Recorder::GetContextId() and the ID will be valid throughout the lifetime - * of the isolate. It is not guaranteed that the ID will still resolve to - * a valid context using Recorder::GetContext() at the time the metric is - * recorded. In this case, an empty handle will be returned. - * * The embedder is expected to call v8::Isolate::SetMetricsRecorder() * providing its implementation and have the virtual methods overwritten * for the events it cares about. @@ -202,14 +242,30 @@ class V8_EXPORT Recorder { virtual ~Recorder() = default; + // Main thread events. Those are only triggered on the main thread, and hence + // can access the context. #define ADD_MAIN_THREAD_EVENT(E) \ - virtual void AddMainThreadEvent(const E& event, ContextId context_id) {} - V8_MAIN_THREAD_METRICS_EVENTS(ADD_MAIN_THREAD_EVENT) + virtual void AddMainThreadEvent(const E&, ContextId) {} + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullCycle) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadIncrementalMark) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadBatchedIncrementalMark) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadIncrementalSweep) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadBatchedIncrementalSweep) + ADD_MAIN_THREAD_EVENT(GarbageCollectionYoungCycle) + ADD_MAIN_THREAD_EVENT(WasmModuleDecoded) + ADD_MAIN_THREAD_EVENT(WasmModuleCompiled) + ADD_MAIN_THREAD_EVENT(WasmModuleInstantiated) #undef ADD_MAIN_THREAD_EVENT + // Thread-safe events are not allowed to access the context and therefore do + // not carry a context ID with them. These IDs can be generated using + // Recorder::GetContextId() and the ID will be valid throughout the lifetime + // of the isolate. It is not guaranteed that the ID will still resolve to + // a valid context using Recorder::GetContext() at the time the metric is + // recorded. In this case, an empty handle will be returned. #define ADD_THREAD_SAFE_EVENT(E) \ - virtual void AddThreadSafeEvent(const E& event) {} - V8_THREAD_SAFE_METRICS_EVENTS(ADD_THREAD_SAFE_EVENT) + virtual void AddThreadSafeEvent(const E&) {} + ADD_THREAD_SAFE_EVENT(WasmModulesPerIsolate) #undef ADD_THREAD_SAFE_EVENT virtual void NotifyIsolateDisposal() {} diff --git a/NativeScript/include/v8-microtask-queue.h b/NativeScript/include/v8-microtask-queue.h index af9caa54..85d227fa 100644 --- a/NativeScript/include/v8-microtask-queue.h +++ b/NativeScript/include/v8-microtask-queue.h @@ -118,7 +118,12 @@ class V8_EXPORT V8_NODISCARD MicrotasksScope { public: enum Type { kRunMicrotasks, kDoNotRunMicrotasks }; + V8_DEPRECATE_SOON( + "May be incorrect if context was created with non-default microtask " + "queue") MicrotasksScope(Isolate* isolate, Type type); + + MicrotasksScope(Local context, Type type); MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type); ~MicrotasksScope(); @@ -142,7 +147,7 @@ class V8_EXPORT V8_NODISCARD MicrotasksScope { MicrotasksScope& operator=(const MicrotasksScope&) = delete; private: - internal::Isolate* const isolate_; + internal::Isolate* const i_isolate_; internal::MicrotaskQueue* const microtask_queue_; bool run_; }; diff --git a/NativeScript/include/v8-object.h b/NativeScript/include/v8-object.h index bad299fc..d7332ba0 100644 --- a/NativeScript/include/v8-object.h +++ b/NativeScript/include/v8-object.h @@ -594,8 +594,6 @@ class V8_EXPORT Object : public Value { /** * Returns the context in which the object was created. */ - V8_DEPRECATED("Use MaybeLocal GetCreationContext()") - Local CreationContext(); MaybeLocal GetCreationContext(); /** @@ -604,10 +602,6 @@ class V8_EXPORT Object : public Value { Local GetCreationContextChecked(); /** Same as above, but works for Persistents */ - V8_DEPRECATED( - "Use MaybeLocal GetCreationContext(const " - "PersistentBase& object)") - static Local CreationContext(const PersistentBase& object); V8_INLINE static MaybeLocal GetCreationContext( const PersistentBase& object) { return object.val_->GetCreationContext(); @@ -717,7 +711,7 @@ Local Object::GetInternalField(int index) { // Fast path: If the object is a plain JSObject, which is the common case, we // know where to find the internal fields and can return the value directly. int instance_type = I::GetInstanceType(obj); - if (v8::internal::CanHaveInternalField(instance_type)) { + if (I::CanHaveInternalField(instance_type)) { int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); A value = I::ReadRawField(obj, offset); #ifdef V8_COMPRESS_POINTERS @@ -742,14 +736,13 @@ void* Object::GetAlignedPointerFromInternalField(int index) { // Fast path: If the object is a plain JSObject, which is the common case, we // know where to find the internal fields and can return the value directly. auto instance_type = I::GetInstanceType(obj); - if (v8::internal::CanHaveInternalField(instance_type)) { - int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); -#ifdef V8_SANDBOXED_EXTERNAL_POINTERS - offset += I::kEmbedderDataSlotRawPayloadOffset; -#endif - internal::Isolate* isolate = I::GetIsolateForSandbox(obj); - A value = I::ReadExternalPointerField( - isolate, obj, offset, internal::kEmbedderDataSlotPayloadTag); + if (I::CanHaveInternalField(instance_type)) { + int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index) + + I::kEmbedderDataSlotExternalPointerOffset; + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = + I::ReadExternalPointerField( + isolate, obj, offset); return reinterpret_cast(value); } #endif diff --git a/NativeScript/include/v8-persistent-handle.h b/NativeScript/include/v8-persistent-handle.h index a6c21268..4fe79862 100644 --- a/NativeScript/include/v8-persistent-handle.h +++ b/NativeScript/include/v8-persistent-handle.h @@ -169,8 +169,6 @@ class PersistentBase { * Turns this handle into a weak phantom handle without finalization callback. * The handle will be reset automatically when the garbage collector detects * that the object is no longer reachable. - * A related function Isolate::NumberOfPhantomHandleResetsSinceLastCall - * returns how many phantom handles were reset by the garbage collector. */ V8_INLINE void SetWeak(); @@ -254,7 +252,7 @@ class NonCopyablePersistentTraits { * This will clone the contents of storage cell, but not any of the flags, etc. */ template -struct CopyablePersistentTraits { +struct V8_DEPRECATED("Use v8::Global instead") CopyablePersistentTraits { using CopyablePersistent = Persistent>; static const bool kResetInDestructor = true; template diff --git a/NativeScript/include/v8-platform.h b/NativeScript/include/v8-platform.h index 91b3fd9c..32898e7e 100644 --- a/NativeScript/include/v8-platform.h +++ b/NativeScript/include/v8-platform.h @@ -158,9 +158,10 @@ class TaskRunner { class JobDelegate { public: /** - * Returns true if this thread should return from the worker task on the + * Returns true if this thread *must* return from the worker task on the * current thread ASAP. Workers should periodically invoke ShouldYield (or * YieldIfNeeded()) as often as is reasonable. + * After this method returned true, ShouldYield must not be called again. */ virtual bool ShouldYield() = 0; @@ -284,6 +285,8 @@ class ConvertableToTraceFormat { * V8 Tracing controller. * * Can be implemented by an embedder to record trace events from V8. + * + * Will become obsolete in Perfetto SDK build (v8_use_perfetto = true). */ class TracingController { public: @@ -347,10 +350,16 @@ class TracingController { virtual void OnTraceDisabled() = 0; }; - /** Adds tracing state change observer. */ + /** + * Adds tracing state change observer. + * Does nothing in Perfetto SDK build (v8_use_perfetto = true). + */ virtual void AddTraceStateObserver(TraceStateObserver*) {} - /** Removes tracing state change observer. */ + /** + * Removes tracing state change observer. + * Does nothing in Perfetto SDK build (v8_use_perfetto = true). + */ virtual void RemoveTraceStateObserver(TraceStateObserver*) {} }; @@ -429,6 +438,17 @@ class PageAllocator { virtual bool SetPermissions(void* address, size_t length, Permission permissions) = 0; + /** + * Recommits discarded pages in the given range with given permissions. + * Discarded pages must be recommitted with their original permissions + * before they are used again. + */ + virtual bool RecommitPages(void* address, size_t length, + Permission permissions) { + // TODO(v8:12797): make it pure once it's implemented on Chromium side. + return false; + } + /** * Frees memory in the given [address, address + size) range. address and size * should be operating system page-aligned. The next write to this @@ -698,6 +718,10 @@ class VirtualAddressSpace { /** * Sets permissions of all allocated pages in the given range. * + * This operation can fail due to OOM, in which case false is returned. If + * the operation fails for a reason other than OOM, this function will + * terminate the process as this implies a bug in the client. + * * \param address The start address of the range. Must be aligned to * page_size(). * @@ -706,7 +730,7 @@ class VirtualAddressSpace { * * \param permissions The new permissions for the range. * - * \returns true on success, false otherwise. + * \returns true on success, false on OOM. */ virtual V8_WARN_UNUSED_RESULT bool SetPagePermissions( Address address, size_t size, PagePermissions permissions) = 0; @@ -820,6 +844,24 @@ class VirtualAddressSpace { // takes a command enum as parameter. // + /** + * Recommits discarded pages in the given range with given permissions. + * Discarded pages must be recommitted with their original permissions + * before they are used again. + * + * \param address The start address of the range. Must be aligned to + * page_size(). + * + * \param size The size in bytes of the range. Must be a multiple + * of page_size(). + * + * \param permissions The permissions for the range that the pages must have. + * + * \returns true on success, false otherwise. + */ + virtual V8_WARN_UNUSED_RESULT bool RecommitPages( + Address address, size_t size, PagePermissions permissions) = 0; + /** * Frees memory in the given [address, address + size) range. address and * size should be aligned to the page_size(). The next write to this memory @@ -889,11 +931,9 @@ class Platform { /** * Allows the embedder to manage memory page allocations. + * Returning nullptr will cause V8 to use the default page allocator. */ - virtual PageAllocator* GetPageAllocator() { - // TODO(bbudge) Make this abstract after all embedders implement this. - return nullptr; - } + virtual PageAllocator* GetPageAllocator() = 0; /** * Allows the embedder to specify a custom allocator used for zones. @@ -910,21 +950,7 @@ class Platform { * error. * Embedder overrides of this function must NOT call back into V8. */ - virtual void OnCriticalMemoryPressure() { - // TODO(bbudge) Remove this when embedders override the following method. - // See crbug.com/634547. - } - - /** - * Enables the embedder to respond in cases where V8 can't allocate large - * memory regions. The |length| parameter is the amount of memory needed. - * Returns true if memory is now available. Returns false if no memory could - * be made available. V8 will retry allocations until this method returns - * false. - * - * Embedder overrides of this function must NOT call back into V8. - */ - virtual bool OnCriticalMemoryPressure(size_t length) { return false; } + virtual void OnCriticalMemoryPressure() {} /** * Gets the number of worker threads used by @@ -1022,16 +1048,28 @@ class Platform { * thread (A=>B/B=>A deadlock) and [2] JobTask::Run or * JobTask::GetMaxConcurrency may be invoked synchronously from JobHandle * (B=>JobHandle::foo=>B deadlock). + */ + virtual std::unique_ptr PostJob( + TaskPriority priority, std::unique_ptr job_task) { + auto handle = CreateJob(priority, std::move(job_task)); + handle->NotifyConcurrencyIncrease(); + return handle; + } + + /** + * Creates and returns a JobHandle associated with a Job. Unlike PostJob(), + * this doesn't immediately schedules |worker_task| to run; the Job is then + * scheduled by calling either NotifyConcurrencyIncrease() or Join(). * - * A sufficient PostJob() implementation that uses the default Job provided in - * libplatform looks like: - * std::unique_ptr PostJob( + * A sufficient CreateJob() implementation that uses the default Job provided + * in libplatform looks like: + * std::unique_ptr CreateJob( * TaskPriority priority, std::unique_ptr job_task) override { * return v8::platform::NewDefaultJobHandle( * this, priority, std::move(job_task), NumberOfWorkerThreads()); * } */ - virtual std::unique_ptr PostJob( + virtual std::unique_ptr CreateJob( TaskPriority priority, std::unique_ptr job_task) = 0; /** diff --git a/NativeScript/include/v8-primitive.h b/NativeScript/include/v8-primitive.h index 1b6de166..4fef8da7 100644 --- a/NativeScript/include/v8-primitive.h +++ b/NativeScript/include/v8-primitive.h @@ -20,6 +20,7 @@ class String; namespace internal { class ExternalString; class ScopedExternalStringLock; +class StringForwardingTable; } // namespace internal /** @@ -269,6 +270,7 @@ class V8_EXPORT String : public Name { private: friend class internal::ExternalString; friend class v8::String; + friend class internal::StringForwardingTable; friend class internal::ScopedExternalStringLock; }; @@ -785,10 +787,9 @@ String::ExternalStringResource* String::GetExternalStringResource() const { ExternalStringResource* result; if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) { - internal::Isolate* isolate = I::GetIsolateForSandbox(obj); - A value = - I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, - internal::kExternalStringResourceTag); + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, I::kStringResourceOffset); result = reinterpret_cast(value); } else { result = GetExternalStringResourceSlow(); @@ -809,10 +810,9 @@ String::ExternalStringResourceBase* String::GetExternalStringResourceBase( ExternalStringResourceBase* resource; if (type == I::kExternalOneByteRepresentationTag || type == I::kExternalTwoByteRepresentationTag) { - internal::Isolate* isolate = I::GetIsolateForSandbox(obj); - A value = - I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, - internal::kExternalStringResourceTag); + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, I::kStringResourceOffset); resource = reinterpret_cast(value); } else { resource = GetExternalStringResourceBaseSlow(encoding_out); diff --git a/NativeScript/include/v8-profiler.h b/NativeScript/include/v8-profiler.h index 26810407..d3941512 100644 --- a/NativeScript/include/v8-profiler.h +++ b/NativeScript/include/v8-profiler.h @@ -175,6 +175,32 @@ class V8_EXPORT CpuProfileNode { static const int kNoColumnNumberInfo = Message::kNoColumnInfo; }; +/** + * An interface for exporting data from V8, using "push" model. + */ +class V8_EXPORT OutputStream { + public: + enum WriteResult { kContinue = 0, kAbort = 1 }; + virtual ~OutputStream() = default; + /** Notify about the end of stream. */ + virtual void EndOfStream() = 0; + /** Get preferred output chunk size. Called only once. */ + virtual int GetChunkSize() { return 1024; } + /** + * Writes the next chunk of snapshot data into the stream. Writing + * can be stopped by returning kAbort as function result. EndOfStream + * will not be called in case writing was aborted. + */ + virtual WriteResult WriteAsciiChunk(char* data, int size) = 0; + /** + * Writes the next chunk of heap stats data into the stream. Writing + * can be stopped by returning kAbort as function result. EndOfStream + * will not be called in case writing was aborted. + */ + virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) { + return kAbort; + } +}; /** * CpuProfile contains a CPU profile in a form of top-down call tree @@ -182,6 +208,9 @@ class V8_EXPORT CpuProfileNode { */ class V8_EXPORT CpuProfile { public: + enum SerializationFormat { + kJSON = 0 // See format description near 'Serialize' method. + }; /** Returns CPU profile title. */ Local GetTitle() const; @@ -235,6 +264,25 @@ class V8_EXPORT CpuProfile { * All pointers to nodes previously returned become invalid. */ void Delete(); + + /** + * Prepare a serialized representation of the profile. The result + * is written into the stream provided in chunks of specified size. + * + * For the JSON format, heap contents are represented as an object + * with the following structure: + * + * { + * nodes: [nodes array], + * startTime: number, + * endTime: number + * samples: [strings array] + * timeDeltas: [numbers array] + * } + * + */ + void Serialize(OutputStream* stream, + SerializationFormat format = kJSON) const; }; enum CpuProfilingMode { @@ -331,6 +379,9 @@ class V8_EXPORT CpuProfilingOptions { unsigned max_samples = kNoSampleLimit, int sampling_interval_us = 0, MaybeLocal filter_context = MaybeLocal()); + CpuProfilingOptions(CpuProfilingOptions&&) = default; + CpuProfilingOptions& operator=(CpuProfilingOptions&&) = default; + CpuProfilingMode mode() const { return mode_; } unsigned max_samples() const { return max_samples_; } int sampling_interval_us() const { return sampling_interval_us_; } @@ -344,7 +395,7 @@ class V8_EXPORT CpuProfilingOptions { CpuProfilingMode mode_; unsigned max_samples_; int sampling_interval_us_; - CopyablePersistentTraits::CopyablePersistent filter_context_; + Global filter_context_; }; /** @@ -542,7 +593,10 @@ class V8_EXPORT HeapGraphNode { kConsString = 10, // Concatenated string. A pair of pointers to strings. kSlicedString = 11, // Sliced string. A fragment of another string. kSymbol = 12, // A Symbol (ES6). - kBigInt = 13 // BigInt. + kBigInt = 13, // BigInt. + kObjectShape = 14, // Internal data used for tracking the shapes (or + // "hidden classes") of JS objects. + kWasmObject = 15, // A WasmGC struct or array. }; /** Returns node type (see HeapGraphNode::Type). */ @@ -571,37 +625,6 @@ class V8_EXPORT HeapGraphNode { const HeapGraphEdge* GetChild(int index) const; }; - -/** - * An interface for exporting data from V8, using "push" model. - */ -class V8_EXPORT OutputStream { - public: - enum WriteResult { - kContinue = 0, - kAbort = 1 - }; - virtual ~OutputStream() = default; - /** Notify about the end of stream. */ - virtual void EndOfStream() = 0; - /** Get preferred output chunk size. Called only once. */ - virtual int GetChunkSize() { return 1024; } - /** - * Writes the next chunk of snapshot data into the stream. Writing - * can be stopped by returning kAbort as function result. EndOfStream - * will not be called in case writing was aborted. - */ - virtual WriteResult WriteAsciiChunk(char* data, int size) = 0; - /** - * Writes the next chunk of heap stats data into the stream. Writing - * can be stopped by returning kAbort as function result. EndOfStream - * will not be called in case writing was aborted. - */ - virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) { - return kAbort; - } -}; - /** * HeapSnapshots record the state of the JS heap at some moment. */ @@ -898,6 +921,8 @@ class V8_EXPORT HeapProfiler { enum SamplingFlags { kSamplingNoFlags = 0, kSamplingForceGC = 1 << 0, + kSamplingIncludeObjectsCollectedByMajorGC = 1 << 1, + kSamplingIncludeObjectsCollectedByMinorGC = 1 << 2, }; /** @@ -975,14 +1000,71 @@ class V8_EXPORT HeapProfiler { virtual ~ObjectNameResolver() = default; }; + enum class HeapSnapshotMode { + /** + * Heap snapshot for regular developers. + */ + kRegular, + /** + * Heap snapshot is exposing internals that may be useful for experts. + */ + kExposeInternals, + }; + + enum class NumericsMode { + /** + * Numeric values are hidden as they are values of the corresponding + * objects. + */ + kHideNumericValues, + /** + * Numeric values are exposed in artificial fields. + */ + kExposeNumericValues + }; + + struct HeapSnapshotOptions final { + // Manually define default constructor here to be able to use it in + // `TakeSnapshot()` below. + // NOLINTNEXTLINE + HeapSnapshotOptions() {} + + /** + * The control used to report intermediate progress to. + */ + ActivityControl* control = nullptr; + /** + * The resolver used by the snapshot generator to get names for V8 objects. + */ + ObjectNameResolver* global_object_name_resolver = nullptr; + /** + * Mode for taking the snapshot, see `HeapSnapshotMode`. + */ + HeapSnapshotMode snapshot_mode = HeapSnapshotMode::kRegular; + /** + * Mode for dealing with numeric values, see `NumericsMode`. + */ + NumericsMode numerics_mode = NumericsMode::kHideNumericValues; + }; + + /** + * Takes a heap snapshot. + * + * \returns the snapshot. + */ + const HeapSnapshot* TakeHeapSnapshot( + const HeapSnapshotOptions& options = HeapSnapshotOptions()); + /** - * Takes a heap snapshot and returns it. + * Takes a heap snapshot. See `HeapSnapshotOptions` for details on the + * parameters. + * + * \returns the snapshot. */ const HeapSnapshot* TakeHeapSnapshot( - ActivityControl* control = nullptr, + ActivityControl* control, ObjectNameResolver* global_object_name_resolver = nullptr, - bool treat_global_objects_as_roots = true, - bool capture_numeric_value = false); + bool hide_internals = true, bool capture_numeric_value = false); /** * Starts tracking of heap objects population statistics. After calling @@ -1035,10 +1117,8 @@ class V8_EXPORT HeapProfiler { * |stack_depth| parameter controls the maximum number of stack frames to be * captured on each allocation. * - * NOTE: This is a proof-of-concept at this point. Right now we only sample - * newspace allocations. Support for paged space allocation (e.g. pre-tenured - * objects, large objects, code objects, etc.) and native allocations - * doesn't exist yet, but is anticipated in the future. + * NOTE: Support for native allocations doesn't exist yet, but is anticipated + * in the future. * * Objects allocated before the sampling is started will not be included in * the profile. @@ -1101,18 +1181,18 @@ struct HeapStatsUpdate { uint32_t size; // New value of size field for the interval with this index. }; -#define CODE_EVENTS_LIST(V) \ - V(Builtin) \ - V(Callback) \ - V(Eval) \ - V(Function) \ - V(InterpretedFunction) \ - V(Handler) \ - V(BytecodeHandler) \ - V(LazyCompile) \ - V(RegExp) \ - V(Script) \ - V(Stub) \ +#define CODE_EVENTS_LIST(V) \ + V(Builtin) \ + V(Callback) \ + V(Eval) \ + V(Function) \ + V(InterpretedFunction) \ + V(Handler) \ + V(BytecodeHandler) \ + V(LazyCompile) /* Unused, use kFunction instead */ \ + V(RegExp) \ + V(Script) \ + V(Stub) \ V(Relocation) /** diff --git a/NativeScript/include/v8-regexp.h b/NativeScript/include/v8-regexp.h index 3791bc03..135977bf 100644 --- a/NativeScript/include/v8-regexp.h +++ b/NativeScript/include/v8-regexp.h @@ -37,9 +37,10 @@ class V8_EXPORT RegExp : public Object { kDotAll = 1 << 5, kLinear = 1 << 6, kHasIndices = 1 << 7, + kUnicodeSets = 1 << 8, }; - static constexpr int kFlagCount = 8; + static constexpr int kFlagCount = 9; /** * Creates a regular expression from the given pattern string and diff --git a/NativeScript/include/v8-script.h b/NativeScript/include/v8-script.h index 88252ac1..ea4933dc 100644 --- a/NativeScript/include/v8-script.h +++ b/NativeScript/include/v8-script.h @@ -20,6 +20,7 @@ namespace v8 { class Function; +class Message; class Object; class PrimitiveArray; class Script; @@ -47,8 +48,6 @@ class V8_EXPORT ScriptOrModule { * The options that were passed by the embedder as HostDefinedOptions to * the ScriptOrigin. */ - V8_DEPRECATED("Use HostDefinedOptions") - Local GetHostDefinedOptions(); Local HostDefinedOptions(); }; @@ -78,7 +77,13 @@ class V8_EXPORT UnboundScript { * Returns zero based line number of the code_pos location in the script. * -1 will be returned if no information available. */ - int GetLineNumber(int code_pos); + int GetLineNumber(int code_pos = 0); + + /** + * Returns zero based column number of the code_pos location in the script. + * -1 will be returned if no information available. + */ + int GetColumnNumber(int code_pos = 0); static const int kNoScriptId = 0; }; @@ -87,7 +92,15 @@ class V8_EXPORT UnboundScript { * A compiled JavaScript module, not yet tied to a Context. */ class V8_EXPORT UnboundModuleScript : public Data { - // Only used as a container for code caching. + public: + /** + * Data read from magic sourceURL comments. + */ + Local GetSourceURL(); + /** + * Data read from magic sourceMappingURL comments. + */ + Local GetSourceMappingURL(); }; /** @@ -286,6 +299,16 @@ class V8_EXPORT Module : public Data { V8_WARN_UNUSED_RESULT Maybe SetSyntheticModuleExport( Isolate* isolate, Local export_name, Local export_value); + /** + * Search the modules requested directly or indirectly by the module for + * any top-level await that has not yet resolved. If there is any, the + * returned vector contains a tuple of the unresolved module and a message + * with the pending top-level await. + * An embedder may call this before exiting to improve error messages. + */ + std::vector, Local>> + GetStalledTopLevelAwaitMessage(Isolate* isolate); + V8_INLINE static Module* Cast(Data* data); private: @@ -324,6 +347,12 @@ class V8_EXPORT Script { * ScriptOrigin. This can be either a v8::String or v8::Undefined. */ Local GetResourceName(); + + /** + * If the script was compiled, returns the positions of lazy functions which + * were eventually compiled and executed. + */ + std::vector GetProducedCompileHints() const; }; enum class ScriptType { kClassic, kModule }; @@ -489,7 +518,7 @@ class V8_EXPORT ScriptCompiler { /** * A task which the embedder must run on a background thread to * consume a V8 code cache. Returned by - * ScriptCompiler::StarConsumingCodeCache. + * ScriptCompiler::StartConsumingCodeCache. */ class V8_EXPORT ConsumeCodeCacheTask final { public: @@ -497,6 +526,36 @@ class V8_EXPORT ScriptCompiler { void Run(); + /** + * Provides the source text string and origin information to the consumption + * task. May be called before, during, or after Run(). This step checks + * whether the script matches an existing script in the Isolate's + * compilation cache. To check whether such a script was found, call + * ShouldMergeWithExistingScript. + * + * The Isolate provided must be the same one used during + * StartConsumingCodeCache and must be currently entered on the thread that + * calls this function. The source text and origin provided in this step + * must precisely match those used later in the ScriptCompiler::Source that + * will contain this ConsumeCodeCacheTask. + */ + void SourceTextAvailable(Isolate* isolate, Local source_text, + const ScriptOrigin& origin); + + /** + * Returns whether the embedder should call MergeWithExistingScript. This + * function may be called from any thread, any number of times, but its + * return value is only meaningful after SourceTextAvailable has completed. + */ + bool ShouldMergeWithExistingScript() const; + + /** + * Merges newly deserialized data into an existing script which was found + * during SourceTextAvailable. May be called only after Run() has completed. + * Can execute on any thread, like Run(). + */ + void MergeWithExistingScript(); + private: friend class ScriptCompiler; @@ -509,7 +568,8 @@ class V8_EXPORT ScriptCompiler { enum CompileOptions { kNoCompileOptions = 0, kConsumeCodeCache, - kEagerCompile + kEagerCompile, + kProduceCompileHints }; /** @@ -581,7 +641,8 @@ class V8_EXPORT ScriptCompiler { */ static ScriptStreamingTask* StartStreaming( Isolate* isolate, StreamedSource* source, - ScriptType type = ScriptType::kClassic); + ScriptType type = ScriptType::kClassic, + CompileOptions options = kNoCompileOptions); static ConsumeCodeCacheTask* StartConsumingCodeCache( Isolate* isolate, std::unique_ptr source); @@ -658,6 +719,7 @@ class V8_EXPORT ScriptCompiler { CompileOptions options = kNoCompileOptions, NoCacheReason no_cache_reason = kNoCacheNoReason, Local* script_or_module_out = nullptr); + static V8_WARN_UNUSED_RESULT MaybeLocal CompileFunction( Local context, Source* source, size_t arguments_count = 0, Local arguments[] = nullptr, size_t context_extension_count = 0, diff --git a/NativeScript/include/v8-snapshot.h b/NativeScript/include/v8-snapshot.h index 2400357c..b15a2b19 100644 --- a/NativeScript/include/v8-snapshot.h +++ b/NativeScript/include/v8-snapshot.h @@ -91,7 +91,7 @@ class V8_EXPORT SnapshotCreator { */ SnapshotCreator(Isolate* isolate, const intptr_t* external_references = nullptr, - StartupData* existing_blob = nullptr); + const StartupData* existing_blob = nullptr); /** * Create and enter an isolate, and set it up for serialization. @@ -102,7 +102,7 @@ class V8_EXPORT SnapshotCreator { * that must be equivalent to CreateParams::external_references. */ SnapshotCreator(const intptr_t* external_references = nullptr, - StartupData* existing_blob = nullptr); + const StartupData* existing_blob = nullptr); /** * Destroy the snapshot creator, and exit and dispose of the Isolate diff --git a/NativeScript/include/v8-template.h b/NativeScript/include/v8-template.h index 0afdccaa..11296cd4 100644 --- a/NativeScript/include/v8-template.h +++ b/NativeScript/include/v8-template.h @@ -14,7 +14,6 @@ namespace v8 { -class AccessorSignature; class CFunction; class FunctionTemplate; class ObjectTemplate; @@ -31,7 +30,9 @@ class Signature; F(AsyncIteratorPrototype, initial_async_iterator_prototype) \ F(ErrorPrototype, initial_error_prototype) \ F(IteratorPrototype, initial_iterator_prototype) \ - F(ObjProto_valueOf, object_value_of_function) + F(MapIteratorPrototype, initial_map_iterator_prototype) \ + F(ObjProto_valueOf, object_value_of_function) \ + F(SetIteratorPrototype, initial_set_iterator_prototype) enum Intrinsic { #define V8_DECL_INTRINSIC(name, iname) k##name, @@ -83,28 +84,7 @@ class V8_EXPORT Template : public Data { * cross-context access. * \param attribute The attributes of the property for which an accessor * is added. - * \param signature The signature describes valid receivers for the accessor - * and is used to perform implicit instance checks against them. If the - * receiver is incompatible (i.e. is not an instance of the constructor as - * defined by FunctionTemplate::HasInstance()), an implicit TypeError is - * thrown and no callback is invoked. */ - V8_DEPRECATED("Do signature check in accessor") - void SetNativeDataProperty( - Local name, AccessorGetterCallback getter, - AccessorSetterCallback setter, Local data, - PropertyAttribute attribute, Local signature, - AccessControl settings = DEFAULT, - SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, - SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); - V8_DEPRECATED("Do signature check in accessor") - void SetNativeDataProperty( - Local name, AccessorNameGetterCallback getter, - AccessorNameSetterCallback setter, Local data, - PropertyAttribute attribute, Local signature, - AccessControl settings = DEFAULT, - SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, - SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); void SetNativeDataProperty( Local name, AccessorGetterCallback getter, AccessorSetterCallback setter = nullptr, @@ -151,7 +131,8 @@ class V8_EXPORT Template : public Data { * Interceptor for get requests on an object. * * Use `info.GetReturnValue().Set()` to set the return value of the - * intercepted get request. + * intercepted get request. If the property does not exist the callback should + * not set the result and must not produce side effects. * * \param property The name of the property for which the request was * intercepted. @@ -192,9 +173,9 @@ using GenericNamedPropertyGetterCallback = * Use `info.GetReturnValue()` to indicate whether the request was intercepted * or not. If the setter successfully intercepts the request, i.e., if the * request should not be further executed, call - * `info.GetReturnValue().Set(value)`. If the setter - * did not intercept the request, i.e., if the request should be handled as - * if no interceptor is present, do not not call `Set()`. + * `info.GetReturnValue().Set(value)`. If the setter did not intercept the + * request, i.e., if the request should be handled as if no interceptor is + * present, do not not call `Set()` and do not produce side effects. * * \param property The name of the property for which the request was * intercepted. @@ -217,7 +198,9 @@ using GenericNamedPropertySetterCallback = * defineProperty(). * * Use `info.GetReturnValue().Set(value)` to set the property attributes. The - * value is an integer encoding a `v8::PropertyAttribute`. + * value is an integer encoding a `v8::PropertyAttribute`. If the property does + * not exist the callback should not set the result and must not produce side + * effects. * * \param property The name of the property for which the request was * intercepted. @@ -242,7 +225,8 @@ using GenericNamedPropertyQueryCallback = * or not. If the deleter successfully intercepts the request, i.e., if the * request should not be further executed, call * `info.GetReturnValue().Set(value)` with a boolean `value`. The `value` is - * used as the return value of `delete`. + * used as the return value of `delete`. If the deleter does not intercept the + * request then it should not set the result and must not produce side effects. * * \param property The name of the property for which the request was * intercepted. @@ -274,9 +258,9 @@ using GenericNamedPropertyEnumeratorCallback = * Use `info.GetReturnValue()` to indicate whether the request was intercepted * or not. If the definer successfully intercepts the request, i.e., if the * request should not be further executed, call - * `info.GetReturnValue().Set(value)`. If the definer - * did not intercept the request, i.e., if the request should be handled as - * if no interceptor is present, do not not call `Set()`. + * `info.GetReturnValue().Set(value)`. If the definer did not intercept the + * request, i.e., if the request should be handled as if no interceptor is + * present, do not not call `Set()` and do not produce side effects. * * \param property The name of the property for which the request was * intercepted. @@ -821,27 +805,7 @@ class V8_EXPORT ObjectTemplate : public Template { * cross-context access. * \param attribute The attributes of the property for which an accessor * is added. - * \param signature The signature describes valid receivers for the accessor - * and is used to perform implicit instance checks against them. If the - * receiver is incompatible (i.e. is not an instance of the constructor as - * defined by FunctionTemplate::HasInstance()), an implicit TypeError is - * thrown and no callback is invoked. */ - V8_DEPRECATED("Do signature check in accessor") - void SetAccessor( - Local name, AccessorGetterCallback getter, - AccessorSetterCallback setter, Local data, AccessControl settings, - PropertyAttribute attribute, Local signature, - SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, - SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); - V8_DEPRECATED("Do signature check in accessor") - void SetAccessor( - Local name, AccessorNameGetterCallback getter, - AccessorNameSetterCallback setter, Local data, - AccessControl settings, PropertyAttribute attribute, - Local signature, - SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, - SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); void SetAccessor( Local name, AccessorGetterCallback getter, AccessorSetterCallback setter = nullptr, @@ -1019,24 +983,6 @@ class V8_EXPORT Signature : public Data { static void CheckCast(Data* that); }; -/** - * An AccessorSignature specifies which receivers are valid parameters - * to an accessor callback. - */ -class V8_EXPORT AccessorSignature : public Data { - public: - static Local New( - Isolate* isolate, - Local receiver = Local()); - - V8_INLINE static AccessorSignature* Cast(Data* data); - - private: - AccessorSignature(); - - static void CheckCast(Data* that); -}; - // --- Implementation --- void Template::Set(Isolate* isolate, const char* name, Local value, @@ -1067,13 +1013,6 @@ Signature* Signature::Cast(Data* data) { return reinterpret_cast(data); } -AccessorSignature* AccessorSignature::Cast(Data* data) { -#ifdef V8_ENABLE_CHECKS - CheckCast(data); -#endif - return reinterpret_cast(data); -} - } // namespace v8 #endif // INCLUDE_V8_TEMPLATE_H_ diff --git a/NativeScript/include/v8-traced-handle.h b/NativeScript/include/v8-traced-handle.h index 7719b9bc..784016b3 100644 --- a/NativeScript/include/v8-traced-handle.h +++ b/NativeScript/include/v8-traced-handle.h @@ -117,11 +117,11 @@ class TracedReferenceBase { /** * A traced handle with copy and move semantics. The handle is to be used - * together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects - * (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript. + * together as part of GarbageCollected objects (see v8-cppgc.h) or from stack + * and specifies edges from C++ objects to JavaScript. * * The exact semantics are: - * - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc. + * - Tracing garbage collections using CppHeap. * - Non-tracing garbage collections refer to * |v8::EmbedderRootsHandler::IsRoot()| whether the handle should * be treated as root or not. @@ -166,7 +166,6 @@ class BasicTracedReference : public TracedReferenceBase { Isolate* isolate, T* that, void* slot, internal::GlobalHandleStoreMode store_mode); - friend class EmbedderHeapTracer; template friend class Local; friend class Object; @@ -181,13 +180,7 @@ class BasicTracedReference : public TracedReferenceBase { /** * A traced handle without destructor that clears the handle. The embedder needs * to ensure that the handle is not accessed once the V8 object has been - * reclaimed. This can happen when the handle is not passed through the - * EmbedderHeapTracer. For more details see BasicTracedReference. - * - * The reference assumes the embedder has precise knowledge about references at - * all times. In case V8 needs to separately handle on-stack references, the - * embedder is required to set the stack start through - * |EmbedderHeapTracer::SetStackStart|. + * reclaimed. For more details see BasicTracedReference. */ template class TracedReference : public BasicTracedReference { @@ -403,7 +396,7 @@ void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) { using I = internal::Internals; if (IsEmpty()) return; internal::Address* obj = reinterpret_cast(val_); - uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + uint8_t* addr = reinterpret_cast(obj) + I::kTracedNodeClassIdOffset; *reinterpret_cast(addr) = class_id; } @@ -411,7 +404,7 @@ uint16_t TracedReferenceBase::WrapperClassId() const { using I = internal::Internals; if (IsEmpty()) return 0; internal::Address* obj = reinterpret_cast(val_); - uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + uint8_t* addr = reinterpret_cast(obj) + I::kTracedNodeClassIdOffset; return *reinterpret_cast(addr); } diff --git a/NativeScript/include/v8-unwinder-state.h b/NativeScript/include/v8-unwinder-state.h index a30f7325..18bb410d 100644 --- a/NativeScript/include/v8-unwinder-state.h +++ b/NativeScript/include/v8-unwinder-state.h @@ -17,10 +17,10 @@ struct CalleeSavedRegisters { void* arm_r9; void* arm_r10; }; -#elif V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM64 || \ - V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC || \ - V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_S390 || \ - V8_TARGET_ARCH_LOONG64 +#elif V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM64 || \ + V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_LOONG64 || \ + V8_TARGET_ARCH_RISCV32 struct CalleeSavedRegisters {}; #else #error Target architecture was not detected as supported by v8 diff --git a/NativeScript/include/v8-util.h b/NativeScript/include/v8-util.h index c54418aa..159027d3 100644 --- a/NativeScript/include/v8-util.h +++ b/NativeScript/include/v8-util.h @@ -537,7 +537,6 @@ class StdGlobalValueMap : public GlobalValueMap { : GlobalValueMap(isolate) {} }; - class DefaultPersistentValueVectorTraits { public: typedef std::vector Impl; @@ -562,7 +561,6 @@ class DefaultPersistentValueVectorTraits { } }; - /** * A vector wrapper that safely stores Global values. * C++11 embedders don't need this class, as they can use Global @@ -573,8 +571,8 @@ class DefaultPersistentValueVectorTraits { * PersistentContainerValue, with all conversion into and out of V8 * handles being transparently handled by this class. */ -template -class PersistentValueVector { +template +class V8_DEPRECATE_SOON("Use std::vector>.") PersistentValueVector { public: explicit PersistentValueVector(Isolate* isolate) : isolate_(isolate) { } diff --git a/NativeScript/include/v8-value-serializer.h b/NativeScript/include/v8-value-serializer.h index 078f367c..729730c6 100644 --- a/NativeScript/include/v8-value-serializer.h +++ b/NativeScript/include/v8-value-serializer.h @@ -8,6 +8,7 @@ #include #include +#include #include #include "v8-local-handle.h" // NOLINT(build/include_directory) @@ -26,8 +27,37 @@ class Value; namespace internal { struct ScriptStreamingData; +class SharedObjectConveyorHandles; +class ValueDeserializer; +class ValueSerializer; } // namespace internal +/** + * A move-only class for managing the lifetime of shared value conveyors used + * by V8 to keep JS shared values alive in transit when serialized. + * + * This class is not directly constructible and is always passed to a + * ValueSerializer::Delegate via ValueSerializer::SetSharedValueConveyor. + * + * The embedder must not destruct the SharedValueConveyor until the associated + * serialized data will no longer be deserialized. + */ +class V8_EXPORT SharedValueConveyor final { + public: + SharedValueConveyor(SharedValueConveyor&&) noexcept; + ~SharedValueConveyor(); + + SharedValueConveyor& operator=(SharedValueConveyor&&) noexcept; + + private: + friend class internal::ValueSerializer; + friend class internal::ValueDeserializer; + + explicit SharedValueConveyor(Isolate* isolate); + + std::unique_ptr private_; +}; + /** * Value serialization compatible with the HTML structured clone algorithm. * The format is backward-compatible (i.e. safe to store to disk). @@ -69,20 +99,20 @@ class V8_EXPORT ValueSerializer { Isolate* isolate, Local module); /** - * Returns whether shared values are supported. GetSharedValueId is only - * called if SupportsSharedValues() returns true. - */ - virtual bool SupportsSharedValues() const; - - /** - * Called when the ValueSerializer serializes a value that is shared across - * Isolates. The embedder must return an ID for the object. This function - * must be idempotent for the same object. When deserializing, the ID will - * be passed to ValueDeserializer::Delegate::GetSharedValueFromId as - * |shared_value_id|. + * Called when the first shared value is serialized. All subsequent shared + * values will use the same conveyor. + * + * The embedder must ensure the lifetime of the conveyor matches the + * lifetime of the serialized data. + * + * If the embedder supports serializing shared values, this method should + * return true. Otherwise the embedder should throw an exception and return + * false. + * + * This method is called at most once per serializer. */ - virtual Maybe GetSharedValueId(Isolate* isolate, - Local shared_value); + virtual bool AdoptSharedValueConveyor(Isolate* isolate, + SharedValueConveyor&& conveyor); /** * Allocates memory for the buffer of at least the size provided. The actual @@ -196,17 +226,10 @@ class V8_EXPORT ValueDeserializer { Isolate* isolate, uint32_t clone_id); /** - * Returns whether shared values are supported. GetSharedValueFromId is only - * called if SupportsSharedValues() returns true. - */ - virtual bool SupportsSharedValues() const; - - /** - * Get a value shared across Isolates given a shared_value_id provided by - * ValueSerializer::Delegate::GetSharedValueId. + * Get the SharedValueConveyor previously provided by + * ValueSerializer::Delegate::AdoptSharedValueConveyor. */ - virtual MaybeLocal GetSharedValueFromId(Isolate* isolate, - uint32_t shared_value_id); + virtual const SharedValueConveyor* GetSharedValueConveyor(Isolate* isolate); }; ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size); diff --git a/NativeScript/include/v8-value.h b/NativeScript/include/v8-value.h index adca989e..866da201 100644 --- a/NativeScript/include/v8-value.h +++ b/NativeScript/include/v8-value.h @@ -244,6 +244,11 @@ class V8_EXPORT Value : public Data { */ bool IsWeakSet() const; + /** + * Returns true if this value is a WeakRef. + */ + bool IsWeakRef() const; + /** * Returns true if this value is an ArrayBuffer. */ diff --git a/NativeScript/include/v8-version.h b/NativeScript/include/v8-version.h index fb95e7ab..7874db63 100644 --- a/NativeScript/include/v8-version.h +++ b/NativeScript/include/v8-version.h @@ -8,10 +8,10 @@ // These macros define the version number for the current version. // NOTE these macros are used by some of the tool scripts and the build // system so their names cannot be changed without changing the scripts. -#define V8_MAJOR_VERSION 10 -#define V8_MINOR_VERSION 3 -#define V8_BUILD_NUMBER 22 -#define V8_PATCH_LEVEL 0 +#define V8_MAJOR_VERSION 11 +#define V8_MINOR_VERSION 1 +#define V8_BUILD_NUMBER 277 +#define V8_PATCH_LEVEL 17 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/NativeScript/include/v8-wasm.h b/NativeScript/include/v8-wasm.h index 59b2a69b..05acd2e8 100644 --- a/NativeScript/include/v8-wasm.h +++ b/NativeScript/include/v8-wasm.h @@ -5,6 +5,7 @@ #ifndef INCLUDE_V8_WASM_H_ #define INCLUDE_V8_WASM_H_ +#include #include #include @@ -130,19 +131,6 @@ class V8_EXPORT WasmStreaming final { public: class WasmStreamingImpl; - /** - * Client to receive streaming event notifications. - */ - class Client { - public: - virtual ~Client() = default; - /** - * Passes the fully compiled module to the client. This can be used to - * implement code caching. - */ - virtual void OnModuleCompiled(CompiledWasmModule compiled_module) = 0; - }; - explicit WasmStreaming(std::unique_ptr impl); ~WasmStreaming(); @@ -183,10 +171,11 @@ class V8_EXPORT WasmStreaming final { bool SetCompiledModuleBytes(const uint8_t* bytes, size_t size); /** - * Sets the client object that will receive streaming event notifications. - * This must be called before {OnBytesReceived}, {Finish}, or {Abort}. + * Sets a callback which is called whenever a significant number of new + * functions are ready for serialization. */ - void SetClient(std::shared_ptr client); + void SetMoreFunctionsCanBeSerializedCallback( + std::function); /* * Sets the UTF-8 encoded source URL for the {Script} object. This must be @@ -206,52 +195,6 @@ class V8_EXPORT WasmStreaming final { std::unique_ptr impl_; }; -// TODO(mtrofin): when streaming compilation is done, we can rename this -// to simply WasmModuleObjectBuilder -class V8_EXPORT WasmModuleObjectBuilderStreaming final { - public: - explicit WasmModuleObjectBuilderStreaming(Isolate* isolate); - /** - * The buffer passed into OnBytesReceived is owned by the caller. - */ - void OnBytesReceived(const uint8_t*, size_t size); - void Finish(); - /** - * Abort streaming compilation. If {exception} has a value, then the promise - * associated with streaming compilation is rejected with that value. If - * {exception} does not have value, the promise does not get rejected. - */ - void Abort(MaybeLocal exception); - Local GetPromise(); - - ~WasmModuleObjectBuilderStreaming() = default; - - private: - WasmModuleObjectBuilderStreaming(const WasmModuleObjectBuilderStreaming&) = - delete; - WasmModuleObjectBuilderStreaming(WasmModuleObjectBuilderStreaming&&) = - default; - WasmModuleObjectBuilderStreaming& operator=( - const WasmModuleObjectBuilderStreaming&) = delete; - WasmModuleObjectBuilderStreaming& operator=( - WasmModuleObjectBuilderStreaming&&) = default; - Isolate* isolate_ = nullptr; - -#if V8_CC_MSVC - /** - * We don't need the static Copy API, so the default - * NonCopyablePersistentTraits would be sufficient, however, - * MSVC eagerly instantiates the Copy. - * We ensure we don't use Copy, however, by compiling with the - * defaults everywhere else. - */ - Persistent> promise_; -#else - Persistent promise_; -#endif - std::shared_ptr streaming_decoder_; -}; - } // namespace v8 #endif // INCLUDE_V8_WASM_H_ diff --git a/NativeScript/include/v8-weak-callback-info.h b/NativeScript/include/v8-weak-callback-info.h index 750eff19..df4dcb8e 100644 --- a/NativeScript/include/v8-weak-callback-info.h +++ b/NativeScript/include/v8-weak-callback-info.h @@ -63,12 +63,6 @@ enum class WeakCallbackType { * Passes the first two internal fields of the object back to the callback. */ kInternalFields, - /** - * Passes a user-defined void* parameter back to the callback. Will do so - * before the object is actually reclaimed, allowing it to be resurrected. In - * this case it is not possible to set a second-pass callback. - */ - kFinalizer }; template diff --git a/NativeScript/include/v8config.h b/NativeScript/include/v8config.h index 77fd65c6..b44995e7 100644 --- a/NativeScript/include/v8config.h +++ b/NativeScript/include/v8config.h @@ -288,6 +288,9 @@ path. Add it with -I to the command line // // V8_HAS_ATTRIBUTE_ALWAYS_INLINE - __attribute__((always_inline)) // supported +// V8_HAS_ATTRIBUTE_CONSTINIT - __attribute__((require_constant_ +// initialization)) +// supported // V8_HAS_ATTRIBUTE_NONNULL - __attribute__((nonnull)) supported // V8_HAS_ATTRIBUTE_NOINLINE - __attribute__((noinline)) supported // V8_HAS_ATTRIBUTE_UNUSED - __attribute__((unused)) supported @@ -305,9 +308,13 @@ path. Add it with -I to the command line // V8_HAS_BUILTIN_EXPECT - __builtin_expect() supported // V8_HAS_BUILTIN_FRAME_ADDRESS - __builtin_frame_address() supported // V8_HAS_BUILTIN_POPCOUNT - __builtin_popcount() supported +// V8_HAS_BUILTIN_ADD_OVERFLOW - __builtin_add_overflow() supported +// V8_HAS_BUILTIN_SUB_OVERFLOW - __builtin_sub_overflow() supported +// V8_HAS_BUILTIN_MUL_OVERFLOW - __builtin_mul_overflow() supported // V8_HAS_BUILTIN_SADD_OVERFLOW - __builtin_sadd_overflow() supported // V8_HAS_BUILTIN_SSUB_OVERFLOW - __builtin_ssub_overflow() supported // V8_HAS_BUILTIN_UADD_OVERFLOW - __builtin_uadd_overflow() supported +// V8_HAS_BUILTIN_SMUL_OVERFLOW - __builtin_smul_overflow() supported // V8_HAS_COMPUTED_GOTO - computed goto/labels as values // supported // V8_HAS_DECLSPEC_NOINLINE - __declspec(noinline) supported @@ -333,9 +340,23 @@ path. Add it with -I to the command line #endif # define V8_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(always_inline)) +# define V8_HAS_ATTRIBUTE_CONSTINIT \ + (__has_attribute(require_constant_initialization)) +# define V8_HAS_ATTRIBUTE_CONST (__has_attribute(const)) # define V8_HAS_ATTRIBUTE_NONNULL (__has_attribute(nonnull)) # define V8_HAS_ATTRIBUTE_NOINLINE (__has_attribute(noinline)) # define V8_HAS_ATTRIBUTE_UNUSED (__has_attribute(unused)) +// Support for the "preserve_most" attribute is limited: +// - 32-bit platforms do not implement it, +// - component builds fail because _dl_runtime_resolve clobbers registers, +// - we see crashes on arm64 on Windows (https://crbug.com/1409934), which can +// hopefully be fixed in the future. +#if (defined(_M_X64) || defined(__x86_64__) /* x64 (everywhere) */ \ + || ((defined(__AARCH64EL__) || defined(_M_ARM64)) /* arm64, but ... */ \ + && !defined(_WIN32))) /* not on windows */ \ + && !defined(COMPONENT_BUILD) /* no component build */ +# define V8_HAS_ATTRIBUTE_PRESERVE_MOST (__has_attribute(preserve_most)) +#endif # define V8_HAS_ATTRIBUTE_VISIBILITY (__has_attribute(visibility)) # define V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT \ (__has_attribute(warn_unused_result)) @@ -344,6 +365,7 @@ path. Add it with -I to the command line # define V8_HAS_CPP_ATTRIBUTE_NO_UNIQUE_ADDRESS \ (V8_HAS_CPP_ATTRIBUTE(no_unique_address)) +# define V8_HAS_BUILTIN_ASSUME (__has_builtin(__builtin_assume)) # define V8_HAS_BUILTIN_ASSUME_ALIGNED (__has_builtin(__builtin_assume_aligned)) # define V8_HAS_BUILTIN_BSWAP16 (__has_builtin(__builtin_bswap16)) # define V8_HAS_BUILTIN_BSWAP32 (__has_builtin(__builtin_bswap32)) @@ -353,9 +375,14 @@ path. Add it with -I to the command line # define V8_HAS_BUILTIN_EXPECT (__has_builtin(__builtin_expect)) # define V8_HAS_BUILTIN_FRAME_ADDRESS (__has_builtin(__builtin_frame_address)) # define V8_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount)) +# define V8_HAS_BUILTIN_ADD_OVERFLOW (__has_builtin(__builtin_add_overflow)) +# define V8_HAS_BUILTIN_SUB_OVERFLOW (__has_builtin(__builtin_sub_overflow)) +# define V8_HAS_BUILTIN_MUL_OVERFLOW (__has_builtin(__builtin_mul_overflow)) # define V8_HAS_BUILTIN_SADD_OVERFLOW (__has_builtin(__builtin_sadd_overflow)) # define V8_HAS_BUILTIN_SSUB_OVERFLOW (__has_builtin(__builtin_ssub_overflow)) # define V8_HAS_BUILTIN_UADD_OVERFLOW (__has_builtin(__builtin_uadd_overflow)) +# define V8_HAS_BUILTIN_SMUL_OVERFLOW (__has_builtin(__builtin_smul_overflow)) +# define V8_HAS_BUILTIN_UNREACHABLE (__has_builtin(__builtin_unreachable)) // Clang has no __has_feature for computed gotos. // GCC doc: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html @@ -394,6 +421,7 @@ path. Add it with -I to the command line # define V8_HAS_BUILTIN_EXPECT 1 # define V8_HAS_BUILTIN_FRAME_ADDRESS 1 # define V8_HAS_BUILTIN_POPCOUNT 1 +# define V8_HAS_BUILTIN_UNREACHABLE 1 // GCC doc: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html #define V8_HAS_COMPUTED_GOTO 1 @@ -425,6 +453,18 @@ path. Add it with -I to the command line # define V8_INLINE inline #endif +#ifdef DEBUG +// In debug mode, check assumptions instead of actually adding annotations. +# define V8_ASSUME(condition) DCHECK(condition) +#elif V8_HAS_BUILTIN_ASSUME +# define V8_ASSUME(condition) __builtin_assume(condition) +#elif V8_HAS_BUILTIN_UNREACHABLE +# define V8_ASSUME(condition) \ + do { if (!(condition)) __builtin_unreachable(); } while (false) +#else +# define V8_ASSUME(condition) +#endif + #if V8_HAS_BUILTIN_ASSUME_ALIGNED # define V8_ASSUME_ALIGNED(ptr, alignment) \ __builtin_assume_aligned((ptr), (alignment)) @@ -433,6 +473,26 @@ path. Add it with -I to the command line #endif +// A macro to mark functions whose values don't change (e.g. across calls) +// and thereby compiler is free to hoist and fold multiple calls together. +// Use like: +// V8_CONST int foo() { ... } +#if V8_HAS_ATTRIBUTE_CONST +# define V8_CONST __attribute__((const)) +#else +# define V8_CONST +#endif + +// A macro to mark a declaration as requiring constant initialization. +// Use like: +// int* foo V8_CONSTINIT; +#if V8_HAS_ATTRIBUTE_CONSTINIT +# define V8_CONSTINIT __attribute__((require_constant_initialization)) +#else +# define V8_CONSTINIT +#endif + + // A macro to mark specific arguments as non-null. // Use like: // int add(int* x, int y, int* z) V8_NONNULL(1, 3) { return *x + y + *z; } @@ -455,6 +515,21 @@ path. Add it with -I to the command line #endif +// A macro used to change the calling conventions to preserve all registers (no +// caller-saved registers). Use this for cold functions called from hot +// functions. +// Note: The attribute is considered experimental, so apply with care. Also, +// "preserve_most" is currently not handling the return value correctly, so only +// use it for functions returning void (see https://reviews.llvm.org/D141020). +// Use like: +// V8_NOINLINE V8_PRESERVE_MOST void UnlikelyMethod(); +#if V8_HAS_ATTRIBUTE_PRESERVE_MOST +# define V8_PRESERVE_MOST __attribute__((preserve_most)) +#else +# define V8_PRESERVE_MOST /* NOT SUPPORTED */ +#endif + + // A macro (V8_DEPRECATED) to mark classes or functions as deprecated. #if defined(V8_DEPRECATION_WARNINGS) # define V8_DEPRECATED(message) [[deprecated(message)]] @@ -471,6 +546,34 @@ path. Add it with -I to the command line #endif +#if defined(V8_IMMINENT_DEPRECATION_WARNINGS) || \ + defined(V8_DEPRECATION_WARNINGS) +#if defined(V8_CC_MSVC) +# define START_ALLOW_USE_DEPRECATED() \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) +# define END_ALLOW_USE_DEPRECATED() __pragma(warning(pop)) +#else // !defined(V8_CC_MSVC) +# define START_ALLOW_USE_DEPRECATED() \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define END_ALLOW_USE_DEPRECATED() _Pragma("GCC diagnostic pop") +#endif // !defined(V8_CC_MSVC) +#else // !(defined(V8_IMMINENT_DEPRECATION_WARNINGS) || + // defined(V8_DEPRECATION_WARNINGS)) +#define START_ALLOW_USE_DEPRECATED() +#define END_ALLOW_USE_DEPRECATED() +#endif // !(defined(V8_IMMINENT_DEPRECATION_WARNINGS) || + // defined(V8_DEPRECATION_WARNINGS)) +#define ALLOW_COPY_AND_MOVE_WITH_DEPRECATED_FIELDS(ClassName) \ + START_ALLOW_USE_DEPRECATED() \ + ClassName(const ClassName&) = default; \ + ClassName(ClassName&&) = default; \ + ClassName& operator=(const ClassName&) = default; \ + ClassName& operator=(ClassName&&) = default; \ + END_ALLOW_USE_DEPRECATED() + + #if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 6) # define V8_ENUM_DEPRECATED(message) # define V8_ENUM_DEPRECATE_SOON(message) @@ -534,6 +637,37 @@ path. Add it with -I to the command line #define V8_NO_UNIQUE_ADDRESS /* NOT SUPPORTED */ #endif +// Marks a type as being eligible for the "trivial" ABI despite having a +// non-trivial destructor or copy/move constructor. Such types can be relocated +// after construction by simply copying their memory, which makes them eligible +// to be passed in registers. The canonical example is std::unique_ptr. +// +// Use with caution; this has some subtle effects on constructor/destructor +// ordering and will be very incorrect if the type relies on its address +// remaining constant. When used as a function argument (by value), the value +// may be constructed in the caller's stack frame, passed in a register, and +// then used and destructed in the callee's stack frame. A similar thing can +// occur when values are returned. +// +// TRIVIAL_ABI is not needed for types which have a trivial destructor and +// copy/move constructors, since those are automatically trivial by the ABI +// spec. +// +// It is also not likely to be effective on types too large to be passed in one +// or two registers on typical target ABIs. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html +#if defined(__clang__) && defined(__has_attribute) +#if __has_attribute(trivial_abi) +#define V8_TRIVIAL_ABI [[clang::trivial_abi]] +#endif // __has_attribute(trivial_abi) +#endif // defined(__clang__) && defined(__has_attribute) +#if !defined(V8_TRIVIAL_ABI) +#define V8_TRIVIAL_ABI +#endif //!defined(V8_TRIVIAL_ABI) + // Helper macro to define no_sanitize attributes only with clang. #if defined(__clang__) && defined(__has_attribute) #if __has_attribute(no_sanitize) @@ -580,25 +714,216 @@ V8 shared library set USING_V8_SHARED. #endif // V8_OS_WIN -// The sandbox is available (i.e. defined) when pointer compression -// is enabled, but it is only used when V8_SANDBOX is enabled as -// well. This allows better test coverage of the sandbox. -#if defined(V8_COMPRESS_POINTERS) -#define V8_SANDBOX_IS_AVAILABLE +// clang-format on + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +// The V8_HOST_ARCH_* macros correspond to the architecture on which V8, as a +// virtual machine and compiler, runs. Don't confuse this with the architecture +// on which V8 is built. +#if defined(_M_X64) || defined(__x86_64__) +#define V8_HOST_ARCH_X64 1 +#if defined(__x86_64__) && __SIZEOF_POINTER__ == 4 // Check for x32. +#define V8_HOST_ARCH_32_BIT 1 +#else +#define V8_HOST_ARCH_64_BIT 1 +#endif +#elif defined(_M_IX86) || defined(__i386__) +#define V8_HOST_ARCH_IA32 1 +#define V8_HOST_ARCH_32_BIT 1 +#elif defined(__AARCH64EL__) || defined(_M_ARM64) +#define V8_HOST_ARCH_ARM64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif defined(__ARMEL__) +#define V8_HOST_ARCH_ARM 1 +#define V8_HOST_ARCH_32_BIT 1 +#elif defined(__mips64) +#define V8_HOST_ARCH_MIPS64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif defined(__loongarch64) +#define V8_HOST_ARCH_LOONG64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif defined(__PPC64__) || defined(_ARCH_PPC64) +#define V8_HOST_ARCH_PPC64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif defined(__PPC__) || defined(_ARCH_PPC) +#define V8_HOST_ARCH_PPC 1 +#define V8_HOST_ARCH_32_BIT 1 +#elif defined(__s390__) || defined(__s390x__) +#define V8_HOST_ARCH_S390 1 +#if defined(__s390x__) +#define V8_HOST_ARCH_64_BIT 1 +#else +#define V8_HOST_ARCH_32_BIT 1 +#endif +#elif defined(__riscv) || defined(__riscv__) +#if __riscv_xlen == 64 +#define V8_HOST_ARCH_RISCV64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif __riscv_xlen == 32 +#define V8_HOST_ARCH_RISCV32 1 +#define V8_HOST_ARCH_32_BIT 1 +#else +#error "Cannot detect Riscv's bitwidth" +#endif +#else +#error "Host architecture was not detected as supported by v8" +#endif + +// Target architecture detection. This corresponds to the architecture for which +// V8's JIT will generate code (the last stage of the canadian cross-compiler). +// The macros may be set externally. If not, detect in the same way as the host +// architecture, that is, target the native environment as presented by the +// compiler. +#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && \ + !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_PPC && \ + !V8_TARGET_ARCH_PPC64 && !V8_TARGET_ARCH_S390 && \ + !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_LOONG64 && \ + !V8_TARGET_ARCH_RISCV32 +#if defined(_M_X64) || defined(__x86_64__) +#define V8_TARGET_ARCH_X64 1 +#elif defined(_M_IX86) || defined(__i386__) +#define V8_TARGET_ARCH_IA32 1 +#elif defined(__AARCH64EL__) || defined(_M_ARM64) +#define V8_TARGET_ARCH_ARM64 1 +#elif defined(__ARMEL__) +#define V8_TARGET_ARCH_ARM 1 +#elif defined(__mips64) +#define V8_TARGET_ARCH_MIPS64 1 +#elif defined(__loongarch64) +#define V8_TARGET_ARCH_LOONG64 1 +#elif defined(_ARCH_PPC64) +#define V8_TARGET_ARCH_PPC64 1 +#elif defined(_ARCH_PPC) +#define V8_TARGET_ARCH_PPC 1 +#elif defined(__s390__) +#define V8_TARGET_ARCH_S390 1 +#if defined(__s390x__) +#define V8_TARGET_ARCH_S390X 1 +#endif +#elif defined(__riscv) || defined(__riscv__) +#if __riscv_xlen == 64 +#define V8_TARGET_ARCH_RISCV64 1 +#elif __riscv_xlen == 32 +#define V8_TARGET_ARCH_RISCV32 1 +#endif +#else +#error Target architecture was not detected as supported by v8 +#endif #endif -#if defined(V8_SANDBOX) && !defined(V8_SANDBOX_IS_AVAILABLE) -#error Inconsistent configuration: sandbox is enabled but not available +// Determine architecture pointer size. +#if V8_TARGET_ARCH_IA32 +#define V8_TARGET_ARCH_32_BIT 1 +#elif V8_TARGET_ARCH_X64 +#if !V8_TARGET_ARCH_32_BIT && !V8_TARGET_ARCH_64_BIT +#if defined(__x86_64__) && __SIZEOF_POINTER__ == 4 // Check for x32. +#define V8_TARGET_ARCH_32_BIT 1 +#else +#define V8_TARGET_ARCH_64_BIT 1 +#endif +#endif +#elif V8_TARGET_ARCH_ARM +#define V8_TARGET_ARCH_32_BIT 1 +#elif V8_TARGET_ARCH_ARM64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_MIPS +#define V8_TARGET_ARCH_32_BIT 1 +#elif V8_TARGET_ARCH_MIPS64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_LOONG64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_PPC +#define V8_TARGET_ARCH_32_BIT 1 +#elif V8_TARGET_ARCH_PPC64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_S390 +#if V8_TARGET_ARCH_S390X +#define V8_TARGET_ARCH_64_BIT 1 +#else +#define V8_TARGET_ARCH_32_BIT 1 +#endif +#elif V8_TARGET_ARCH_RISCV64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_RISCV32 +#define V8_TARGET_ARCH_32_BIT 1 +#else +#error Unknown target architecture pointer size #endif -// From C++17 onwards, static constexpr member variables are defined to be -// "inline", and adding a separate definition for them can trigger deprecation -// warnings. For C++14 and below, however, these definitions are required. -#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) -#define V8_STATIC_CONSTEXPR_VARIABLES_NEED_DEFINITIONS +// Check for supported combinations of host and target architectures. +#if V8_TARGET_ARCH_IA32 && !V8_HOST_ARCH_IA32 +#error Target architecture ia32 is only supported on ia32 host +#endif +#if (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT && \ + !((V8_HOST_ARCH_X64 || V8_HOST_ARCH_ARM64) && V8_HOST_ARCH_64_BIT)) +#error Target architecture x64 is only supported on x64 and arm64 host +#endif +#if (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT && \ + !(V8_HOST_ARCH_X64 && V8_HOST_ARCH_32_BIT)) +#error Target architecture x32 is only supported on x64 host with x32 support +#endif +#if (V8_TARGET_ARCH_ARM && !(V8_HOST_ARCH_IA32 || V8_HOST_ARCH_ARM)) +#error Target architecture arm is only supported on arm and ia32 host +#endif +#if (V8_TARGET_ARCH_ARM64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_ARM64)) +#error Target architecture arm64 is only supported on arm64 and x64 host +#endif +#if (V8_TARGET_ARCH_MIPS64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_MIPS64)) +#error Target architecture mips64 is only supported on mips64 and x64 host +#endif +#if (V8_TARGET_ARCH_RISCV64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_RISCV64)) +#error Target architecture riscv64 is only supported on riscv64 and x64 host +#endif +#if (V8_TARGET_ARCH_RISCV32 && !(V8_HOST_ARCH_IA32 || V8_HOST_ARCH_RISCV32)) +#error Target architecture riscv32 is only supported on riscv32 and ia32 host +#endif +#if (V8_TARGET_ARCH_LOONG64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_LOONG64)) +#error Target architecture loong64 is only supported on loong64 and x64 host #endif -// clang-format on +// Determine architecture endianness. +#if V8_TARGET_ARCH_IA32 +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_X64 +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_ARM +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_ARM64 +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_LOONG64 +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_MIPS64 +#if defined(__MIPSEB__) || defined(V8_TARGET_ARCH_MIPS64_BE) +#define V8_TARGET_BIG_ENDIAN 1 +#else +#define V8_TARGET_LITTLE_ENDIAN 1 +#endif +#elif defined(__BIG_ENDIAN__) // FOR PPCGR on AIX +#define V8_TARGET_BIG_ENDIAN 1 +#elif V8_TARGET_ARCH_PPC_LE +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif V8_TARGET_ARCH_PPC_BE +#define V8_TARGET_BIG_ENDIAN 1 +#elif V8_TARGET_ARCH_S390 +#if V8_TARGET_ARCH_S390_LE_SIM +#define V8_TARGET_LITTLE_ENDIAN 1 +#else +#define V8_TARGET_BIG_ENDIAN 1 +#endif +#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64 +#define V8_TARGET_LITTLE_ENDIAN 1 +#elif defined(__BYTE_ORDER__) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define V8_TARGET_BIG_ENDIAN 1 +#else +#define V8_TARGET_LITTLE_ENDIAN 1 +#endif +#else +#error Unknown target architecture endianness +#endif #undef V8_HAS_CPP_ATTRIBUTE diff --git a/NativeScript/inspector/JsV8InspectorClient.mm b/NativeScript/inspector/JsV8InspectorClient.mm index 25c0c54f..1b9447f7 100644 --- a/NativeScript/inspector/JsV8InspectorClient.mm +++ b/NativeScript/inspector/JsV8InspectorClient.mm @@ -163,7 +163,8 @@ } void JsV8InspectorClient::createInspectorSession() { - this->session_ = this->inspector_->connect(JsV8InspectorClient::contextGroupId, this, {}); + // FIXME: Consider a more restrictive default approach, or perhaps implement a blacklist of untrusted connections? + this->session_ = this->inspector_->connect(JsV8InspectorClient::contextGroupId, this, {}, V8Inspector::kFullyTrusted); } void JsV8InspectorClient::disconnect() { @@ -205,9 +206,9 @@ shouldWait = true; } - std::shared_ptr platform = tns::Runtime::GetPlatform(); + v8::Platform* platform = tns::Runtime::GetPlatform(); Isolate* isolate = isolate_; - platform::PumpMessageLoop(platform.get(), isolate, platform::MessageLoopBehavior::kDoNotWait); + platform::PumpMessageLoop(platform, isolate, platform::MessageLoopBehavior::kDoNotWait); if(shouldWait && !terminated_) { dispatch_semaphore_wait(messageArrived_, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_MSEC)); // 1ms } diff --git a/NativeScript/inspector/src/debug/debug-coverage.h b/NativeScript/inspector/src/debug/debug-coverage.h index 81b17818..bb0922f5 100644 --- a/NativeScript/inspector/src/debug/debug-coverage.h +++ b/NativeScript/inspector/src/debug/debug-coverage.h @@ -10,7 +10,6 @@ #include "src/debug/debug-interface.h" #include "src/handles/handles.h" -#include "src/objects/objects.h" namespace v8 { namespace internal { diff --git a/NativeScript/inspector/src/debug/debug-evaluate.h b/NativeScript/inspector/src/debug/debug-evaluate.h index 469d4a74..3962d00e 100644 --- a/NativeScript/inspector/src/debug/debug-evaluate.h +++ b/NativeScript/inspector/src/debug/debug-evaluate.h @@ -10,8 +10,8 @@ #include "src/base/macros.h" #include "src/common/globals.h" #include "src/debug/debug-frames.h" +#include "src/debug/debug-interface.h" #include "src/debug/debug-scopes.h" -#include "src/debug/debug.h" #include "src/execution/frames.h" #include "src/objects/objects.h" #include "src/objects/shared-function-info.h" diff --git a/NativeScript/inspector/src/debug/debug-frames.h b/NativeScript/inspector/src/debug/debug-frames.h index 5197f862..3afa4ce7 100644 --- a/NativeScript/inspector/src/debug/debug-frames.h +++ b/NativeScript/inspector/src/debug/debug-frames.h @@ -19,7 +19,7 @@ class JavaScriptFrame; class CommonFrame; class WasmFrame; -class FrameInspector { +class V8_EXPORT_PRIVATE FrameInspector { public: FrameInspector(CommonFrame* frame, int inlined_frame_index, Isolate* isolate); FrameInspector(const FrameInspector&) = delete; diff --git a/NativeScript/inspector/src/debug/debug-interface.h b/NativeScript/inspector/src/debug/debug-interface.h index 0cac2ad9..c0f9f34f 100644 --- a/NativeScript/inspector/src/debug/debug-interface.h +++ b/NativeScript/inspector/src/debug/debug-interface.h @@ -11,6 +11,7 @@ #include "include/v8-date.h" #include "include/v8-debug.h" #include "include/v8-embedder-heap.h" +#include "include/v8-isolate.h" #include "include/v8-local-handle.h" #include "include/v8-memory-span.h" #include "include/v8-promise.h" @@ -33,13 +34,10 @@ namespace internal { struct CoverageBlock; struct CoverageFunction; struct CoverageScript; -struct TypeProfileEntry; -struct TypeProfileScript; class Coverage; class DisableBreak; class PostponeInterruptsScope; class Script; -class TypeProfile; } // namespace internal namespace debug { @@ -50,6 +48,9 @@ int GetContextId(Local context); void SetInspector(Isolate* isolate, v8_inspector::V8Inspector*); v8_inspector::V8Inspector* GetInspector(Isolate* isolate); +// Returns a debug string representation of the bigint without tailing `n`. +Local GetBigIntStringValue(Isolate* isolate, Local bigint); + // Returns a debug string representation of the bigint. Local GetBigIntDescription(Isolate* isolate, Local bigint); @@ -98,8 +99,9 @@ MaybeLocal GetCreationContext(Local value); enum ExceptionBreakState { NoBreakOnException = 0, - BreakOnUncaughtException = 1, - BreakOnAnyException = 2 + BreakOnCaughtException = 1, + BreakOnUncaughtException = 2, + BreakOnAnyException = 3, }; /** @@ -135,6 +137,7 @@ enum class BreakReason : uint8_t { typedef base::EnumSet BreakReasons; void PrepareStep(Isolate* isolate, StepAction action); +bool PrepareRestartFrame(Isolate* isolate, int callFrameOrdinal); void ClearStepping(Isolate* isolate); V8_EXPORT_PRIVATE void BreakRightNow( Isolate* isolate, base::EnumSet break_reason = {}); @@ -161,6 +164,7 @@ struct LiveEditResult { bool stack_changed = false; // Available only for OK. v8::Local script; + bool restart_top_frame_required = false; // Fields below are available only for COMPILE_ERROR. v8::Local message; int line_number = -1; @@ -207,6 +211,7 @@ class V8_EXPORT_PRIVATE Script { MaybeLocal Name() const; MaybeLocal SourceURL() const; MaybeLocal SourceMappingURL() const; + MaybeLocal GetSha256Hash() const; Maybe ContextId() const; Local Source() const; bool IsModule() const; @@ -214,9 +219,13 @@ class V8_EXPORT_PRIVATE Script { const debug::Location& start, const debug::Location& end, bool restrict_to_function, std::vector* locations) const; - int GetSourceOffset(const debug::Location& location) const; + enum class GetSourceOffsetMode { kStrict, kClamp }; + Maybe GetSourceOffset( + const debug::Location& location, + GetSourceOffsetMode mode = GetSourceOffsetMode::kStrict) const; v8::debug::Location GetSourceLocation(int offset) const; bool SetScriptSource(v8::Local newSource, bool preview, + bool allow_top_frame_live_editing, LiveEditResult* result) const; bool SetBreakpoint(v8::Local condition, debug::Location* location, BreakpointId* id) const; @@ -227,6 +236,13 @@ class V8_EXPORT_PRIVATE Script { bool SetInstrumentationBreakpoint(BreakpointId* id) const; }; +class DisassemblyCollector { + public: + virtual void ReserveLineCount(size_t count) = 0; + virtual void AddLine(const char* src, size_t length, + uint32_t bytecode_offset) = 0; +}; + #if V8_ENABLE_WEBASSEMBLY // Specialization for wasm Scripts. class WasmScript : public Script { @@ -242,6 +258,9 @@ class WasmScript : public Script { std::pair GetFunctionRange(int function_index) const; int GetContainingFunction(int byte_offset) const; + void Disassemble(DisassemblyCollector* collector, + std::vector* function_body_offsets); + uint32_t GetFunctionHash(int function_index); int CodeOffset() const; @@ -249,8 +268,8 @@ class WasmScript : public Script { }; #endif // V8_ENABLE_WEBASSEMBLY -V8_EXPORT_PRIVATE void GetLoadedScripts(Isolate* isolate, - PersistentValueVector