From edc2bd4957a33fad32d5c173f5a5848839c9ef4d Mon Sep 17 00:00:00 2001 From: dylanahsmith Date: Sat, 20 Nov 2021 00:33:42 +0000 Subject: [PATCH] Upgrade V8 binaries for 9.6.180.12 version --- deps/include/OWNERS | 18 +- deps/include/cppgc/README.md | 17 +- deps/include/cppgc/allocation.h | 90 +- deps/include/cppgc/cross-thread-persistent.h | 208 +- deps/include/cppgc/explicit-management.h | 82 + deps/include/cppgc/garbage-collected.h | 3 +- deps/include/cppgc/heap-consistency.h | 17 + deps/include/cppgc/heap-state.h | 11 + deps/include/cppgc/heap-statistics.h | 60 +- deps/include/cppgc/heap.h | 2 + deps/include/cppgc/internal/api-constants.h | 4 +- .../cppgc/internal/caged-heap-local-data.h | 4 +- .../cppgc/internal/compiler-specific.h | 4 +- deps/include/cppgc/internal/finalizer-trait.h | 2 + deps/include/cppgc/internal/gc-info.h | 137 +- deps/include/cppgc/internal/name-trait.h | 15 +- deps/include/cppgc/internal/persistent-node.h | 86 +- .../include/cppgc/internal/pointer-policies.h | 67 +- deps/include/cppgc/internal/write-barrier.h | 61 +- deps/include/cppgc/liveness-broker.h | 5 +- deps/include/cppgc/macros.h | 2 +- deps/include/cppgc/member.h | 49 +- deps/include/cppgc/name-provider.h | 2 +- deps/include/cppgc/persistent.h | 18 +- deps/include/cppgc/platform.h | 3 + deps/include/cppgc/prefinalizer.h | 2 +- deps/include/cppgc/sentinel-pointer.h | 2 +- deps/include/cppgc/source-location.h | 1 + deps/include/cppgc/testing.h | 49 + deps/include/cppgc/type-traits.h | 19 + deps/include/cppgc/vendor.go | 2 +- deps/include/cppgc/visitor.h | 95 +- deps/include/js_protocol.pdl | 77 +- deps/include/libplatform/vendor.go | 2 +- deps/include/v8-array-buffer.h | 433 + deps/include/v8-callbacks.h | 377 + deps/include/v8-container.h | 129 + deps/include/v8-context.h | 418 + deps/include/v8-cppgc.h | 54 +- deps/include/v8-data.h | 65 + deps/include/v8-date.h | 43 + deps/include/v8-debug.h | 151 + deps/include/v8-embedder-heap.h | 238 + deps/include/v8-exception.h | 224 + deps/include/v8-extension.h | 62 + deps/include/v8-external.h | 37 + deps/include/v8-fast-api-calls.h | 715 +- deps/include/v8-forward.h | 81 + deps/include/v8-function-callback.h | 475 + deps/include/v8-function.h | 122 + deps/include/v8-initialization.h | 282 + deps/include/v8-inspector.h | 53 +- deps/include/v8-internal.h | 166 +- deps/include/v8-isolate.h | 1662 ++ deps/include/v8-json.h | 47 + deps/include/v8-local-handle.h | 459 + deps/include/v8-locker.h | 148 + deps/include/v8-maybe.h | 137 + deps/include/v8-memory-span.h | 43 + deps/include/v8-message.h | 237 + deps/include/v8-metrics.h | 67 +- deps/include/v8-microtask-queue.h | 152 + deps/include/v8-microtask.h | 28 + deps/include/v8-object.h | 770 + deps/include/v8-persistent-handle.h | 590 + deps/include/v8-platform.h | 59 +- deps/include/v8-primitive-object.h | 118 + deps/include/v8-primitive.h | 858 ++ deps/include/v8-profiler.h | 22 +- deps/include/v8-promise.h | 174 + deps/include/v8-proxy.h | 50 + deps/include/v8-regexp.h | 105 + deps/include/v8-script.h | 771 + deps/include/v8-snapshot.h | 198 + deps/include/v8-statistics.h | 215 + deps/include/v8-template.h | 1052 ++ deps/include/v8-traced-handle.h | 605 + deps/include/v8-typed-array.h | 282 + deps/include/v8-unwinder-state.h | 7 +- deps/include/v8-unwinder.h | 129 + deps/include/v8-util.h | 10 +- deps/include/v8-value-serializer.h | 249 + deps/include/v8-value.h | 526 + deps/include/v8-version.h | 6 +- deps/include/v8-wasm.h | 245 + deps/include/v8-weak-callback-info.h | 73 + deps/include/v8.h | 12467 +--------------- deps/include/v8config.h | 63 +- deps/include/vendor.go | 4 +- deps/v8 | 2 +- deps/v8_version | 2 +- 91 files changed, 15031 insertions(+), 12942 deletions(-) create mode 100644 deps/include/cppgc/explicit-management.h create mode 100644 deps/include/v8-array-buffer.h create mode 100644 deps/include/v8-callbacks.h create mode 100644 deps/include/v8-container.h create mode 100644 deps/include/v8-context.h create mode 100644 deps/include/v8-data.h create mode 100644 deps/include/v8-date.h create mode 100644 deps/include/v8-debug.h create mode 100644 deps/include/v8-embedder-heap.h create mode 100644 deps/include/v8-exception.h create mode 100644 deps/include/v8-extension.h create mode 100644 deps/include/v8-external.h create mode 100644 deps/include/v8-forward.h create mode 100644 deps/include/v8-function-callback.h create mode 100644 deps/include/v8-function.h create mode 100644 deps/include/v8-initialization.h create mode 100644 deps/include/v8-isolate.h create mode 100644 deps/include/v8-json.h create mode 100644 deps/include/v8-local-handle.h create mode 100644 deps/include/v8-locker.h create mode 100644 deps/include/v8-maybe.h create mode 100644 deps/include/v8-memory-span.h create mode 100644 deps/include/v8-message.h create mode 100644 deps/include/v8-microtask-queue.h create mode 100644 deps/include/v8-microtask.h create mode 100644 deps/include/v8-object.h create mode 100644 deps/include/v8-persistent-handle.h create mode 100644 deps/include/v8-primitive-object.h create mode 100644 deps/include/v8-primitive.h create mode 100644 deps/include/v8-promise.h create mode 100644 deps/include/v8-proxy.h create mode 100644 deps/include/v8-regexp.h create mode 100644 deps/include/v8-script.h create mode 100644 deps/include/v8-snapshot.h create mode 100644 deps/include/v8-statistics.h create mode 100644 deps/include/v8-template.h create mode 100644 deps/include/v8-traced-handle.h create mode 100644 deps/include/v8-typed-array.h create mode 100644 deps/include/v8-unwinder.h create mode 100644 deps/include/v8-value-serializer.h create mode 100644 deps/include/v8-value.h create mode 100644 deps/include/v8-wasm.h create mode 100644 deps/include/v8-weak-callback-info.h diff --git a/deps/include/OWNERS b/deps/include/OWNERS index cd5fd0535..0222513df 100644 --- a/deps/include/OWNERS +++ b/deps/include/OWNERS @@ -1,22 +1,18 @@ adamk@chromium.org cbruni@chromium.org -danno@chromium.org +leszeks@chromium.org mlippautz@chromium.org -ulan@chromium.org verwaest@chromium.org yangguo@chromium.org per-file *DEPS=file:../COMMON_OWNERS per-file v8-internal.h=file:../COMMON_OWNERS -per-file v8-inspector.h=dgozman@chromium.org -per-file v8-inspector.h=pfeldman@chromium.org -per-file v8-inspector.h=kozyatinskiy@chromium.org -per-file v8-inspector-protocol.h=dgozman@chromium.org -per-file v8-inspector-protocol.h=pfeldman@chromium.org -per-file v8-inspector-protocol.h=kozyatinskiy@chromium.org -per-file js_protocol.pdl=dgozman@chromium.org -per-file js_protocol.pdl=pfeldman@chromium.org -per-file js_protocol.pdl=bmeurer@chromium.org +per-file v8-inspector.h=file:../src/inspector/OWNERS +per-file v8-inspector-protocol.h=file:../src/inspector/OWNERS +per-file js_protocol.pdl=file:../src/inspector/OWNERS + +# Needed by the auto_tag builder +per-file v8-version.h=v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com # For branch updates: per-file v8-version.h=file:../INFRA_OWNERS diff --git a/deps/include/cppgc/README.md b/deps/include/cppgc/README.md index 3a2db6dfa..e45439985 100644 --- a/deps/include/cppgc/README.md +++ b/deps/include/cppgc/README.md @@ -1,5 +1,16 @@ -# C++ Garbage Collection +# Oilpan: C++ Garbage Collection -This directory provides an open-source garbage collection library for C++. +Oilpan is an open-source garbage collection library for C++ that can be used stand-alone or in collaboration with V8's JavaScript garbage collector. -The library is under construction, meaning that *all APIs in this directory are incomplete and considered unstable and should not be used*. \ No newline at end of file +**Key properties** +- Trace-based garbage collection; +- Precise on-heap memory layout; +- Conservative on-stack memory layout; +- Allows for collection with and without considering stack; +- Incremental and concurrent marking; +- Incremental and concurrent sweeping; +- Non-incremental and non-concurrent compaction for selected spaces; + +See the [Hello World](https://chromium.googlesource.com/v8/v8/+/main/samples/cppgc/hello-world.cc) example on how to get started using Oilpan to manage C++ code. + +Oilpan follows V8's project organization, see e.g. on how we accept [contributions](https://v8.dev/docs/contribute) and [provide a stable API](https://v8.dev/docs/api). diff --git a/deps/include/cppgc/allocation.h b/deps/include/cppgc/allocation.h index b6f9d3902..a3112dd61 100644 --- a/deps/include/cppgc/allocation.h +++ b/deps/include/cppgc/allocation.h @@ -5,24 +5,21 @@ #ifndef INCLUDE_CPPGC_ALLOCATION_H_ #define INCLUDE_CPPGC_ALLOCATION_H_ -#include - #include +#include +#include +#include +#include +#include #include "cppgc/custom-space.h" -#include "cppgc/garbage-collected.h" #include "cppgc/internal/api-constants.h" #include "cppgc/internal/gc-info.h" +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { -template -class MakeGarbageCollectedTraitBase; - -namespace internal { -class ObjectAllocator; -} // namespace internal - /** * AllocationHandle is used to allocate garbage-collected objects. */ @@ -39,10 +36,37 @@ class V8_EXPORT MakeGarbageCollectedTraitInternal { const_cast(reinterpret_cast( reinterpret_cast(payload) - api_constants::kFullyConstructedBitFieldOffsetFromPayload))); - atomic_mutable_bitfield->fetch_or(api_constants::kFullyConstructedBitMask, - std::memory_order_release); + // It's safe to split use load+store here (instead of a read-modify-write + // operation), since it's guaranteed that this 16-bit bitfield is only + // modified by a single thread. This is cheaper in terms of code bloat (on + // ARM) and performance. + uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed); + value |= api_constants::kFullyConstructedBitMask; + atomic_mutable_bitfield->store(value, std::memory_order_release); } + template + struct SpacePolicy { + static void* Allocate(AllocationHandle& handle, size_t size) { + // Custom space. + static_assert(std::is_base_of::value, + "Custom space must inherit from CustomSpaceBase."); + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, internal::GCInfoTrait::Index(), + CustomSpace::kSpaceIndex); + } + }; + + template + struct SpacePolicy { + static void* Allocate(AllocationHandle& handle, size_t size) { + // Default space. + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, internal::GCInfoTrait::Index()); + } + }; + + private: static void* Allocate(cppgc::AllocationHandle& handle, size_t size, GCInfoIndex index); static void* Allocate(cppgc::AllocationHandle& handle, size_t size, @@ -71,27 +95,6 @@ class MakeGarbageCollectedTraitBase internal::api_constants::kLargeObjectSizeThreshold, "GarbageCollectedMixin may not be a large object"); - template - struct SpacePolicy { - static void* Allocate(AllocationHandle& handle, size_t size) { - // Custom space. - static_assert(std::is_base_of::value, - "Custom space must inherit from CustomSpaceBase."); - return internal::MakeGarbageCollectedTraitInternal::Allocate( - handle, size, internal::GCInfoTrait::Index(), - CustomSpace::kSpaceIndex); - } - }; - - template - struct SpacePolicy { - static void* Allocate(AllocationHandle& handle, size_t size) { - // Default space. - return internal::MakeGarbageCollectedTraitInternal::Allocate( - handle, size, internal::GCInfoTrait::Index()); - } - }; - protected: /** * Allocates memory for an object of type T. @@ -101,9 +104,15 @@ class MakeGarbageCollectedTraitBase * \param size The size that should be reserved for the object. * \returns the memory to construct an object of type T on. */ - static void* Allocate(AllocationHandle& handle, size_t size) { - return SpacePolicy::Space>::Allocate(handle, - size); + V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) { + static_assert( + std::is_base_of::value, + "U of GarbageCollected must be a base of T. Check " + "GarbageCollected base class inheritance."); + return SpacePolicy< + typename internal::GCInfoFolding< + T, typename T::ParentMostGarbageCollectedType>::ResultType, + typename SpaceTrait::Space>::Allocate(handle, size); } /** @@ -112,7 +121,7 @@ class MakeGarbageCollectedTraitBase * * \param payload The base pointer the object is allocated at. */ - static void MarkObjectAsFullyConstructed(const void* payload) { + V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) { internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed( payload); } @@ -198,7 +207,7 @@ struct PostConstructionCallbackTrait { * \returns an instance of type T. */ template -T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { +V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { T* object = MakeGarbageCollectedTrait::Call(handle, std::forward(args)...); PostConstructionCallbackTrait::Call(object); @@ -216,8 +225,9 @@ T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { * \returns an instance of type T. */ template -T* MakeGarbageCollected(AllocationHandle& handle, - AdditionalBytes additional_bytes, Args&&... args) { +V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, + AdditionalBytes additional_bytes, + Args&&... args) { T* object = MakeGarbageCollectedTrait::Call(handle, additional_bytes, std::forward(args)...); PostConstructionCallbackTrait::Call(object); diff --git a/deps/include/cppgc/cross-thread-persistent.h b/deps/include/cppgc/cross-thread-persistent.h index 1f509d4b0..c8751e1d6 100644 --- a/deps/include/cppgc/cross-thread-persistent.h +++ b/deps/include/cppgc/cross-thread-persistent.h @@ -13,12 +13,62 @@ #include "cppgc/visitor.h" namespace cppgc { - namespace internal { +// Wrapper around PersistentBase that allows accessing poisoned memory when +// using ASAN. This is needed as the GC of the heap that owns the value +// of a CTP, may clear it (heap termination, weakness) while the object +// holding the CTP may be poisoned as itself may be deemed dead. +class CrossThreadPersistentBase : public PersistentBase { + public: + CrossThreadPersistentBase() = default; + explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {} + + V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const { + return raw_; + } + + V8_CLANG_NO_SANITIZE("address") + PersistentNode* GetNodeFromGC() const { return node_; } + + V8_CLANG_NO_SANITIZE("address") + void ClearFromGC() const { + raw_ = nullptr; + SetNodeSafe(nullptr); + } + + // GetNodeSafe() can be used for a thread-safe IsValid() check in a + // double-checked locking pattern. See ~BasicCrossThreadPersistent. + PersistentNode* GetNodeSafe() const { + return reinterpret_cast*>(&node_)->load( + std::memory_order_acquire); + } + + // The GC writes using SetNodeSafe() while holding the lock. + V8_CLANG_NO_SANITIZE("address") + void SetNodeSafe(PersistentNode* value) const { +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define V8_IS_ASAN 1 +#endif +#endif + +#ifdef V8_IS_ASAN + __atomic_store(&node_, &value, __ATOMIC_RELEASE); +#else // !V8_IS_ASAN + // Non-ASAN builds can use atomics. This also covers MSVC which does not + // have the __atomic_store intrinsic. + reinterpret_cast*>(&node_)->store( + value, std::memory_order_release); +#endif // !V8_IS_ASAN + +#undef V8_IS_ASAN + } +}; + template -class BasicCrossThreadPersistent final : public PersistentBase, +class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, public LocationPolicy, private WeaknessPolicy, private CheckingPolicy { @@ -26,37 +76,80 @@ class BasicCrossThreadPersistent final : public PersistentBase, using typename WeaknessPolicy::IsStrongPersistent; using PointeeType = T; - ~BasicCrossThreadPersistent() { Clear(); } + ~BasicCrossThreadPersistent() { + // This implements fast path for destroying empty/sentinel. + // + // Simplified version of `AssignUnsafe()` to allow calling without a + // complete type `T`. Uses double-checked locking with a simple thread-safe + // check for a valid handle based on a node. + if (GetNodeSafe()) { + PersistentRegionLock guard; + const void* old_value = GetValue(); + // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck + // validity while holding the lock to ensure the reference has not been + // cleared. + if (IsValid(old_value)) { + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); + region.FreeNode(GetNode()); + SetNode(nullptr); + } else { + CPPGC_DCHECK(!GetNode()); + } + } + // No need to call SetValue() as the handle is not used anymore. This can + // leave behind stale sentinel values but will always destroy the underlying + // node. + } - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( const SourceLocation& loc = SourceLocation::Current()) : LocationPolicy(loc) {} - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( std::nullptr_t, const SourceLocation& loc = SourceLocation::Current()) : LocationPolicy(loc) {} - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) - : PersistentBase(s), LocationPolicy(loc) {} + : CrossThreadPersistentBase(s), LocationPolicy(loc) {} - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( T* raw, const SourceLocation& loc = SourceLocation::Current()) - : PersistentBase(raw), LocationPolicy(loc) { + : CrossThreadPersistentBase(raw), LocationPolicy(loc) { + if (!IsValid(raw)) return; + PersistentRegionLock guard; + CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); + SetNode(region.AllocateNode(this, &Trace)); + this->CheckPointer(raw); + } + + class UnsafeCtorTag { + private: + UnsafeCtorTag() = default; + template + friend class BasicCrossThreadPersistent; + }; + + BasicCrossThreadPersistent( + UnsafeCtorTag, T* raw, + const SourceLocation& loc = SourceLocation::Current()) + : CrossThreadPersistentBase(raw), LocationPolicy(loc) { if (!IsValid(raw)) return; - PersistentRegion& region = this->GetPersistentRegion(raw); + CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); SetNode(region.AllocateNode(this, &Trace)); this->CheckPointer(raw); } - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( T& raw, const SourceLocation& loc = SourceLocation::Current()) : BasicCrossThreadPersistent(&raw, loc) {} template ::value>> - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( internal::BasicMember member, @@ -75,7 +168,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, template ::value>> - BasicCrossThreadPersistent( // NOLINT + BasicCrossThreadPersistent( const BasicCrossThreadPersistent& other, @@ -94,7 +187,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, BasicCrossThreadPersistent& operator=( const BasicCrossThreadPersistent& other) { PersistentRegionLock guard; - AssignUnsafe(other.Get()); + AssignSafe(guard, other.Get()); return *this; } @@ -106,7 +199,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, OtherLocationPolicy, OtherCheckingPolicy>& other) { PersistentRegionLock guard; - AssignUnsafe(other.Get()); + AssignSafe(guard, other.Get()); return *this; } @@ -120,12 +213,17 @@ class BasicCrossThreadPersistent final : public PersistentBase, GetNode()->UpdateOwner(this); other.SetValue(nullptr); other.SetNode(nullptr); - this->CheckPointer(GetValue()); + this->CheckPointer(Get()); return *this; } + /** + * Assigns a raw pointer. + * + * Note: **Not thread-safe.** + */ BasicCrossThreadPersistent& operator=(T* other) { - Assign(other); + AssignUnsafe(other); return *this; } @@ -140,13 +238,24 @@ class BasicCrossThreadPersistent final : public PersistentBase, return operator=(member.Get()); } + /** + * Assigns a nullptr. + * + * \returns the handle. + */ BasicCrossThreadPersistent& operator=(std::nullptr_t) { Clear(); return *this; } + /** + * Assigns the sentinel pointer. + * + * \returns the handle. + */ BasicCrossThreadPersistent& operator=(SentinelPointer s) { - Assign(s); + PersistentRegionLock guard; + AssignSafe(guard, s); return *this; } @@ -168,16 +277,8 @@ class BasicCrossThreadPersistent final : public PersistentBase, * Clears the stored object. */ void Clear() { - // Simplified version of `Assign()` to allow calling without a complete type - // `T`. - const void* old_value = GetValue(); - if (IsValid(old_value)) { - PersistentRegionLock guard; - PersistentRegion& region = this->GetPersistentRegion(old_value); - region.FreeNode(GetNode()); - SetNode(nullptr); - } - SetValue(nullptr); + PersistentRegionLock guard; + AssignSafe(guard, nullptr); } /** @@ -209,7 +310,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, * * \returns the object. */ - operator T*() const { return Get(); } // NOLINT + operator T*() const { return Get(); } /** * Dereferences the stored object. @@ -225,9 +326,12 @@ class BasicCrossThreadPersistent final : public PersistentBase, BasicCrossThreadPersistent To() const { + using OtherBasicCrossThreadPersistent = + BasicCrossThreadPersistent; PersistentRegionLock guard; - return BasicCrossThreadPersistent( + return OtherBasicCrossThreadPersistent( + typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(), static_cast(Get())); } @@ -250,18 +354,26 @@ class BasicCrossThreadPersistent final : public PersistentBase, v->TraceRoot(*handle, handle->Location()); } - void Assign(T* ptr) { + void AssignUnsafe(T* ptr) { const void* old_value = GetValue(); if (IsValid(old_value)) { PersistentRegionLock guard; - PersistentRegion& region = this->GetPersistentRegion(old_value); - if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { - SetValue(ptr); - this->CheckPointer(ptr); - return; + old_value = GetValue(); + // The fast path check (IsValid()) does not acquire the lock. Reload + // the value to ensure the reference has not been cleared. + if (IsValid(old_value)) { + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); + if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { + SetValue(ptr); + this->CheckPointer(ptr); + return; + } + region.FreeNode(GetNode()); + SetNode(nullptr); + } else { + CPPGC_DCHECK(!GetNode()); } - region.FreeNode(GetNode()); - SetNode(nullptr); } SetValue(ptr); if (!IsValid(ptr)) return; @@ -270,11 +382,12 @@ class BasicCrossThreadPersistent final : public PersistentBase, this->CheckPointer(ptr); } - void AssignUnsafe(T* ptr) { + void AssignSafe(PersistentRegionLock&, T* ptr) { PersistentRegionLock::AssertLocked(); const void* old_value = GetValue(); if (IsValid(old_value)) { - PersistentRegion& region = this->GetPersistentRegion(old_value); + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { SetValue(ptr); this->CheckPointer(ptr); @@ -290,12 +403,19 @@ class BasicCrossThreadPersistent final : public PersistentBase, } void ClearFromGC() const { - if (IsValid(GetValue())) { - WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); - PersistentBase::ClearFromGC(); + if (IsValid(GetValueFromGC())) { + WeaknessPolicy::GetPersistentRegion(GetValueFromGC()) + .FreeNode(GetNodeFromGC()); + CrossThreadPersistentBase::ClearFromGC(); } } + // See Get() for details. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") + T* GetFromGC() const { + return static_cast(const_cast(GetValueFromGC())); + } + friend class cppgc::Visitor; }; diff --git a/deps/include/cppgc/explicit-management.h b/deps/include/cppgc/explicit-management.h new file mode 100644 index 000000000..cdb6af485 --- /dev/null +++ b/deps/include/cppgc/explicit-management.h @@ -0,0 +1,82 @@ +// Copyright 2021 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_EXPLICIT_MANAGEMENT_H_ +#define INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ + +#include + +#include "cppgc/allocation.h" +#include "cppgc/internal/logging.h" +#include "cppgc/type-traits.h" + +namespace cppgc { + +class HeapHandle; + +namespace internal { + +V8_EXPORT void FreeUnreferencedObject(HeapHandle&, void*); +V8_EXPORT bool Resize(void*, size_t); + +} // namespace internal + +namespace subtle { + +/** + * Informs the garbage collector that `object` can be immediately reclaimed. The + * destructor may not be invoked immediately but only on next garbage + * collection. + * + * It is up to the embedder to guarantee that no other object holds a reference + * to `object` after calling `FreeUnreferencedObject()`. In case such a + * reference exists, it's use results in a use-after-free. + * + * To aid in using the API, `FreeUnreferencedObject()` may be called from + * destructors on objects that would be reclaimed in the same garbage collection + * cycle. + * + * \param heap_handle The corresponding heap. + * \param object Reference to an object that is of type `GarbageCollected` and + * should be immediately reclaimed. + */ +template +void FreeUnreferencedObject(HeapHandle& heap_handle, T& object) { + static_assert(IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + internal::FreeUnreferencedObject(heap_handle, &object); +} + +/** + * Tries to resize `object` of type `T` with additional bytes on top of + * sizeof(T). Resizing is only useful with trailing inlined storage, see e.g. + * `MakeGarbageCollected(AllocationHandle&, AdditionalBytes)`. + * + * `Resize()` performs growing or shrinking as needed and may skip the operation + * for internal reasons, see return value. + * + * It is up to the embedder to guarantee that in case of shrinking a larger + * object down, the reclaimed area is not used anymore. Any subsequent use + * results in a use-after-free. + * + * The `object` must be live when calling `Resize()`. + * + * \param object Reference to an object that is of type `GarbageCollected` and + * should be resized. + * \param additional_bytes Bytes in addition to sizeof(T) that the object should + * provide. + * \returns true when the operation was successful and the result can be relied + * on, and false otherwise. + */ +template +bool Resize(T& object, AdditionalBytes additional_bytes) { + static_assert(IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + return internal::Resize(&object, sizeof(T) + additional_bytes.value); +} + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ diff --git a/deps/include/cppgc/garbage-collected.h b/deps/include/cppgc/garbage-collected.h index d28a39074..a3839e1ba 100644 --- a/deps/include/cppgc/garbage-collected.h +++ b/deps/include/cppgc/garbage-collected.h @@ -73,10 +73,11 @@ class GarbageCollectedBase { * }; * \endcode */ -template +template class GarbageCollected : public internal::GarbageCollectedBase { public: using IsGarbageCollectedTypeMarker = void; + using ParentMostGarbageCollectedType = T; protected: GarbageCollected() = default; diff --git a/deps/include/cppgc/heap-consistency.h b/deps/include/cppgc/heap-consistency.h index 47caea184..8e603d5d8 100644 --- a/deps/include/cppgc/heap-consistency.h +++ b/deps/include/cppgc/heap-consistency.h @@ -68,6 +68,23 @@ class HeapConsistency final { return internal::WriteBarrier::GetWriteBarrierType(slot, params, callback); } + /** + * Gets the required write barrier type for a specific write. + * This version is meant to be used in conjunction with with a marking write + * barrier barrier which doesn't consider the slot. + * + * \param value The pointer to the object. May be an interior pointer to an + * interface of the actual object. + * \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. + */ + static V8_INLINE WriteBarrierType + GetWriteBarrierType(const void* value, WriteBarrierParams& params) { + return internal::WriteBarrier::GetWriteBarrierType(value, params); + } + /** * Conservative Dijkstra-style write barrier that processes an object if it * has not yet been processed. diff --git a/deps/include/cppgc/heap-state.h b/deps/include/cppgc/heap-state.h index 0157282a5..3fd6b54a8 100644 --- a/deps/include/cppgc/heap-state.h +++ b/deps/include/cppgc/heap-state.h @@ -49,6 +49,17 @@ class V8_EXPORT HeapState final { */ static bool IsInAtomicPause(const HeapHandle& heap_handle); + /** + * Returns whether the last garbage collection was finalized conservatively + * (i.e., with a non-empty stack). This API is experimental and is expected to + * be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the last garbage collection was finalized conservatively, + * and false otherwise. + */ + static bool PreviousGCWasConservative(const HeapHandle& heap_handle); + private: HeapState() = delete; }; diff --git a/deps/include/cppgc/heap-statistics.h b/deps/include/cppgc/heap-statistics.h index cf8d6633c..8e626596e 100644 --- a/deps/include/cppgc/heap-statistics.h +++ b/deps/include/cppgc/heap-statistics.h @@ -5,7 +5,8 @@ #ifndef INCLUDE_CPPGC_HEAP_STATISTICS_H_ #define INCLUDE_CPPGC_HEAP_STATISTICS_H_ -#include +#include +#include #include #include @@ -30,19 +31,17 @@ struct HeapStatistics final { }; /** - * Statistics of object types. For each type the statistics record its name, - * how many objects of that type were allocated, and the overall size used by - * these objects. + * Object statistics for a single type. */ - struct ObjectStatistics { - /** Number of distinct types in the heap. */ - size_t num_types = 0; - /** Name of each type in the heap. */ - std::vector type_name; - /** Number of allocated objects per each type. */ - std::vector type_count; - /** Overall size of allocated objects per each type. */ - std::vector type_bytes; + struct ObjectStatsEntry { + /** + * Number of allocated bytes. + */ + size_t allocated_bytes; + /** + * Number of allocated objects. + */ + size_t object_count; }; /** @@ -50,14 +49,19 @@ struct HeapStatistics final { * allocated memory size and overall used memory size for the page. */ struct PageStatistics { - /** Overall amount of memory allocated for the page. */ - size_t physical_size_bytes = 0; + /** Overall committed amount of memory for the page. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory held by the page. */ + size_t resident_size_bytes = 0; /** 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. */ + std::vector object_statistics; }; /** - * Stastistics of the freelist (used only in non-large object spaces). For + * Statistics of the freelist (used only in non-large object spaces). For * each bucket in the freelist the statistics record the bucket size, the * number of freelist entries in the bucket, and the overall allocated memory * consumed by these freelist entries. @@ -67,7 +71,7 @@ struct HeapStatistics final { std::vector bucket_size; /** number of freelist entries per bucket. */ std::vector free_count; - /** memory size concumed by freelist entries per size. */ + /** memory size consumed by freelist entries per size. */ std::vector free_size; }; @@ -80,29 +84,35 @@ struct HeapStatistics final { struct SpaceStatistics { /** The space name */ std::string name; - /** Overall amount of memory allocated for the space. */ - size_t physical_size_bytes = 0; + /** Overall committed amount of memory for the heap. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory held by the heap. */ + size_t resident_size_bytes = 0; /** Amount of memory actually used on the space. */ size_t used_size_bytes = 0; /** Statistics for each of the pages in the space. */ std::vector page_stats; /** Statistics for the freelist of the space. */ FreeListStatistics free_list_stats; - /** Statistics for object allocated on the space. Filled only when - * NameProvider::HideInternalNames() is false. */ - ObjectStatistics object_stats; }; - /** Overall amount of memory allocated for the heap. */ - size_t physical_size_bytes = 0; + /** Overall committed amount of memory for the heap. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory help by the heap. */ + size_t resident_size_bytes = 0; /** Amount of memory actually used on the heap. */ size_t used_size_bytes = 0; /** Detail level of this HeapStatistics. */ DetailLevel detail_level; /** Statistics for each of the spaces in the heap. Filled only when - * detail_level is kDetailed. */ + * `detail_level` is `DetailLevel::kDetailed`. */ std::vector space_stats; + + /** + * Vector of `cppgc::GarbageCollected` type names. + */ + std::vector type_names; }; } // namespace cppgc diff --git a/deps/include/cppgc/heap.h b/deps/include/cppgc/heap.h index fd0512f1f..136c4fb44 100644 --- a/deps/include/cppgc/heap.h +++ b/deps/include/cppgc/heap.h @@ -5,6 +5,8 @@ #ifndef INCLUDE_CPPGC_HEAP_H_ #define INCLUDE_CPPGC_HEAP_H_ +#include +#include #include #include diff --git a/deps/include/cppgc/internal/api-constants.h b/deps/include/cppgc/internal/api-constants.h index a70f00710..7253a4708 100644 --- a/deps/include/cppgc/internal/api-constants.h +++ b/deps/include/cppgc/internal/api-constants.h @@ -5,8 +5,8 @@ #ifndef INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ #define INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ -#include -#include +#include +#include #include "v8config.h" // NOLINT(build/include_directory) diff --git a/deps/include/cppgc/internal/caged-heap-local-data.h b/deps/include/cppgc/internal/caged-heap-local-data.h index 1fa60b695..5b30d6702 100644 --- a/deps/include/cppgc/internal/caged-heap-local-data.h +++ b/deps/include/cppgc/internal/caged-heap-local-data.h @@ -53,10 +53,10 @@ static_assert(sizeof(AgeTable) == 1 * api_constants::kMB, #endif // CPPGC_YOUNG_GENERATION struct CagedHeapLocalData final { - explicit CagedHeapLocalData(HeapBase* heap_base) : heap_base(heap_base) {} + CagedHeapLocalData(HeapBase&, PageAllocator&); bool is_incremental_marking_in_progress = false; - HeapBase* heap_base = nullptr; + HeapBase& heap_base; #if defined(CPPGC_YOUNG_GENERATION) AgeTable age_table; #endif diff --git a/deps/include/cppgc/internal/compiler-specific.h b/deps/include/cppgc/internal/compiler-specific.h index c580894b3..595b6398c 100644 --- a/deps/include/cppgc/internal/compiler-specific.h +++ b/deps/include/cppgc/internal/compiler-specific.h @@ -21,13 +21,13 @@ namespace cppgc { // [[no_unique_address]] comes in C++20 but supported in clang with -std >= // c++11. -#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) // NOLINTNEXTLINE +#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) #define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]] #else #define CPPGC_NO_UNIQUE_ADDRESS #endif -#if CPPGC_HAS_ATTRIBUTE(unused) // NOLINTNEXTLINE +#if CPPGC_HAS_ATTRIBUTE(unused) #define CPPGC_UNUSED __attribute__((unused)) #else #define CPPGC_UNUSED diff --git a/deps/include/cppgc/internal/finalizer-trait.h b/deps/include/cppgc/internal/finalizer-trait.h index a95126591..7bd6f83bf 100644 --- a/deps/include/cppgc/internal/finalizer-trait.h +++ b/deps/include/cppgc/internal/finalizer-trait.h @@ -76,6 +76,8 @@ struct FinalizerTrait { } public: + static constexpr bool HasFinalizer() { return kNonTrivialFinalizer; } + // The callback used to finalize an object of type T. static constexpr FinalizationCallback kCallback = kNonTrivialFinalizer ? Finalize : nullptr; diff --git a/deps/include/cppgc/internal/gc-info.h b/deps/include/cppgc/internal/gc-info.h index 9c26d6aa5..82a0d0534 100644 --- a/deps/include/cppgc/internal/gc-info.h +++ b/deps/include/cppgc/internal/gc-info.h @@ -5,7 +5,9 @@ #ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ #define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ -#include +#include +#include +#include #include "cppgc/internal/finalizer-trait.h" #include "cppgc/internal/name-trait.h" @@ -17,27 +19,134 @@ namespace internal { using GCInfoIndex = uint16_t; -class V8_EXPORT RegisteredGCInfoIndex final { - public: - RegisteredGCInfoIndex(FinalizationCallback finalization_callback, - TraceCallback trace_callback, - NameCallback name_callback, bool has_v_table); - GCInfoIndex GetIndex() const { return index_; } +struct V8_EXPORT EnsureGCInfoIndexTrait final { + // Acquires a new GC info object and returns the index. In addition, also + // updates `registered_index` atomically. + template + V8_INLINE static GCInfoIndex EnsureIndex( + std::atomic& registered_index) { + return EnsureGCInfoIndexTraitDispatch{}(registered_index); + } private: - const GCInfoIndex index_; + template ::value, + bool = FinalizerTrait::HasFinalizer(), + 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); +}; + +#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; \ + } \ + }; + +// --------------------------------------------------------------------- // +// DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) +// --------------------------------------------------------------------- // +DISPATCH(true, true, true, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback, // + NameTrait::GetName)) // +DISPATCH(true, true, false, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback)) // +DISPATCH(true, false, true, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + NameTrait::GetName)) // +DISPATCH(true, false, false, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace)) // +DISPATCH(false, true, true, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback, // + NameTrait::GetName)) // +DISPATCH(false, true, false, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback)) // +DISPATCH(false, false, true, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + NameTrait::GetName)) // +DISPATCH(false, false, false, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace)) // + +#undef DISPATCH + +// Fold types based on finalizer behavior. Note that finalizer characteristics +// align with trace behavior, i.e., destructors are virtual when trace methods +// are and vice versa. +template +struct GCInfoFolding { + static constexpr bool kHasVirtualDestructorAtBase = + std::has_virtual_destructor::value; + static constexpr bool kBothTypesAreTriviallyDestructible = + std::is_trivially_destructible::value && + std::is_trivially_destructible::value; + static constexpr bool kHasCustomFinalizerDispatchAtBase = + internal::HasFinalizeGarbageCollectedObject< + ParentMostGarbageCollectedType>::value; +#ifdef CPPGC_SUPPORTS_OBJECT_NAMES + static constexpr bool kWantsDetailedObjectNames = true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + static constexpr bool kWantsDetailedObjectNames = false; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + + // Folding would regresses name resolution when deriving names from C++ + // class names as it would just folds a name to the base class name. + using ResultType = std::conditional_t<(kHasVirtualDestructorAtBase || + kBothTypesAreTriviallyDestructible || + kHasCustomFinalizerDispatchAtBase) && + !kWantsDetailedObjectNames, + ParentMostGarbageCollectedType, T>; }; // Trait determines how the garbage collector treats objects wrt. to traversing, // finalization, and naming. template -struct GCInfoTrait { - static GCInfoIndex Index() { +struct GCInfoTrait final { + V8_INLINE static GCInfoIndex Index() { static_assert(sizeof(T), "T must be fully defined"); - static const RegisteredGCInfoIndex registered_index( - FinalizerTrait::kCallback, TraceTrait::Trace, - NameTrait::GetName, std::is_polymorphic::value); - return registered_index.GetIndex(); + 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); } }; diff --git a/deps/include/cppgc/internal/name-trait.h b/deps/include/cppgc/internal/name-trait.h index ae99d41c0..32a334785 100644 --- a/deps/include/cppgc/internal/name-trait.h +++ b/deps/include/cppgc/internal/name-trait.h @@ -6,6 +6,7 @@ #define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ #include +#include #include "cppgc/name-provider.h" #include "v8config.h" // NOLINT(build/include_directory) @@ -67,13 +68,23 @@ class V8_EXPORT NameTraitBase { template class NameTrait final : public NameTraitBase { public: + static constexpr bool HasNonHiddenName() { +#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME + return true; +#elif CPPGC_SUPPORTS_OBJECT_NAMES + return true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + return std::is_base_of::value; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + } + static HeapObjectName GetName(const void* obj) { return GetNameFor(static_cast(obj)); } private: static HeapObjectName GetNameFor(const NameProvider* name_provider) { - return {name_provider->GetName(), false}; + return {name_provider->GetHumanReadableName(), false}; } static HeapObjectName GetNameFor(...) { @@ -91,7 +102,7 @@ class NameTrait final : public NameTraitBase { static const HeapObjectName leaky_name = GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE); - return leaky_name; + return {leaky_name, false}; #undef PRETTY_FUNCTION_VALUE diff --git a/deps/include/cppgc/internal/persistent-node.h b/deps/include/cppgc/internal/persistent-node.h index 6524f326a..1fea66784 100644 --- a/deps/include/cppgc/internal/persistent-node.h +++ b/deps/include/cppgc/internal/persistent-node.h @@ -19,6 +19,8 @@ class Visitor; namespace internal { +class CrossThreadPersistentRegion; + // PersistentNode represents a variant of two states: // 1) traceable node with a back pointer to the Persistent object; // 2) freelist entry. @@ -30,6 +32,7 @@ class PersistentNode final { PersistentNode& operator=(const PersistentNode&) = delete; void InitializeAsUsedNode(void* owner, TraceCallback trace) { + CPPGC_DCHECK(trace); owner_ = owner; trace_ = trace; } @@ -72,16 +75,16 @@ class PersistentNode final { TraceCallback trace_ = nullptr; }; -class V8_EXPORT PersistentRegion final { +class V8_EXPORT PersistentRegionBase { using PersistentNodeSlots = std::array; public: - PersistentRegion() = default; + PersistentRegionBase() = default; // Clears Persistent fields to avoid stale pointers after heap teardown. - ~PersistentRegion(); + ~PersistentRegionBase(); - PersistentRegion(const PersistentRegion&) = delete; - PersistentRegion& operator=(const PersistentRegion&) = delete; + PersistentRegionBase(const PersistentRegionBase&) = delete; + PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; PersistentNode* AllocateNode(void* owner, TraceCallback trace) { if (!free_list_head_) { @@ -89,12 +92,15 @@ class V8_EXPORT PersistentRegion final { } PersistentNode* node = free_list_head_; free_list_head_ = free_list_head_->FreeListNext(); + CPPGC_DCHECK(!node->IsUsed()); node->InitializeAsUsedNode(owner, trace); nodes_in_use_++; return node; } void FreeNode(PersistentNode* node) { + CPPGC_DCHECK(node); + CPPGC_DCHECK(node->IsUsed()); node->InitializeAsFreeNode(free_list_head_); free_list_head_ = node; CPPGC_DCHECK(nodes_in_use_ > 0); @@ -110,13 +116,49 @@ class V8_EXPORT PersistentRegion final { private: void EnsureNodeSlots(); + template + void ClearAllUsedNodes(); + std::vector> nodes_; PersistentNode* free_list_head_ = nullptr; size_t nodes_in_use_ = 0; + + friend class CrossThreadPersistentRegion; }; -// CrossThreadPersistent uses PersistentRegion but protects it using this lock -// when needed. +// Variant of PersistentRegionBase that checks whether the allocation and +// freeing happens only on the thread that created the region. +class V8_EXPORT PersistentRegion final : public PersistentRegionBase { + public: + PersistentRegion(); + // Clears Persistent fields to avoid stale pointers after heap teardown. + ~PersistentRegion() = default; + + PersistentRegion(const PersistentRegion&) = delete; + PersistentRegion& operator=(const PersistentRegion&) = delete; + + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { +#if V8_ENABLE_CHECKS + CheckIsCreationThread(); +#endif // V8_ENABLE_CHECKS + return PersistentRegionBase::AllocateNode(owner, trace); + } + + V8_INLINE void FreeNode(PersistentNode* node) { +#if V8_ENABLE_CHECKS + CheckIsCreationThread(); +#endif // V8_ENABLE_CHECKS + PersistentRegionBase::FreeNode(node); + } + + private: + void CheckIsCreationThread(); + + int creation_thread_id_; +}; + +// CrossThreadPersistent uses PersistentRegionBase but protects it using this +// lock when needed. class V8_EXPORT PersistentRegionLock final { public: PersistentRegionLock(); @@ -125,6 +167,36 @@ class V8_EXPORT PersistentRegionLock final { static void AssertLocked(); }; +// Variant of PersistentRegionBase that checks whether the PersistentRegionLock +// is locked. +class V8_EXPORT CrossThreadPersistentRegion final + : protected PersistentRegionBase { + public: + CrossThreadPersistentRegion() = default; + // Clears Persistent fields to avoid stale pointers after heap teardown. + ~CrossThreadPersistentRegion(); + + CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete; + CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) = + delete; + + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { + PersistentRegionLock::AssertLocked(); + return PersistentRegionBase::AllocateNode(owner, trace); + } + + V8_INLINE void FreeNode(PersistentNode* node) { + PersistentRegionLock::AssertLocked(); + PersistentRegionBase::FreeNode(node); + } + + void Trace(Visitor*); + + size_t NodesInUse() const; + + void ClearAllUsedNodes(); +}; + } // namespace internal } // namespace cppgc diff --git a/deps/include/cppgc/internal/pointer-policies.h b/deps/include/cppgc/internal/pointer-policies.h index ea86a0a70..7c4f4a086 100644 --- a/deps/include/cppgc/internal/pointer-policies.h +++ b/deps/include/cppgc/internal/pointer-policies.h @@ -9,13 +9,17 @@ #include #include "cppgc/internal/write-barrier.h" +#include "cppgc/sentinel-pointer.h" #include "cppgc/source-location.h" +#include "cppgc/type-traits.h" #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { namespace internal { +class HeapBase; class PersistentRegion; +class CrossThreadPersistentRegion; // Tags to distinguish between strong and weak member types. class StrongMemberTag; @@ -47,25 +51,62 @@ struct NoWriteBarrierPolicy { static void AssigningBarrier(const void*, const void*) {} }; -class V8_EXPORT EnabledCheckingPolicy { +class V8_EXPORT SameThreadEnabledCheckingPolicyBase { protected: - EnabledCheckingPolicy(); - void CheckPointer(const void* ptr); + void CheckPointerImpl(const void* ptr, bool points_to_payload, + bool check_off_heap_assignments); + + const HeapBase* heap_ = nullptr; +}; + +template +class V8_EXPORT SameThreadEnabledCheckingPolicy + : private SameThreadEnabledCheckingPolicyBase { + protected: + template + void CheckPointer(const T* ptr) { + if (!ptr || (kSentinelPointer == ptr)) return; + + CheckPointersImplTrampoline::Call(this, ptr); + } private: - void* impl_; + template > + struct CheckPointersImplTrampoline { + static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { + policy->CheckPointerImpl(ptr, false, kCheckOffHeapAssignments); + } + }; + + template + struct CheckPointersImplTrampoline { + static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { + policy->CheckPointerImpl(ptr, IsGarbageCollectedTypeV, + kCheckOffHeapAssignments); + } + }; }; class DisabledCheckingPolicy { protected: - void CheckPointer(const void* raw) {} + void CheckPointer(const void*) {} }; #if V8_ENABLE_CHECKS -using DefaultCheckingPolicy = EnabledCheckingPolicy; +// Off heap members are not connected to object graph and thus cannot ressurect +// dead objects. +using DefaultMemberCheckingPolicy = + SameThreadEnabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = + SameThreadEnabledCheckingPolicy; #else -using DefaultCheckingPolicy = DisabledCheckingPolicy; +using DefaultMemberCheckingPolicy = DisabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy; #endif +// For CT(W)P neither marking information (for value), nor objectstart bitmap +// (for slot) are guaranteed to be present because there's no synchonization +// between heaps after marking. +using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy; class KeepLocationPolicy { public: @@ -115,25 +156,27 @@ struct WeakPersistentPolicy { struct StrongCrossThreadPersistentPolicy { using IsStrongPersistent = std::true_type; - static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); + static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( + const void* object); }; struct WeakCrossThreadPersistentPolicy { using IsStrongPersistent = std::false_type; - static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); + static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( + const void* object); }; // Forward declarations setting up the default policies. template + typename CheckingPolicy = DefaultCrossThreadPersistentCheckingPolicy> class BasicCrossThreadPersistent; template + typename CheckingPolicy = DefaultPersistentCheckingPolicy> class BasicPersistent; template + typename CheckingPolicy = DefaultMemberCheckingPolicy> class BasicMember; } // namespace internal diff --git a/deps/include/cppgc/internal/write-barrier.h b/deps/include/cppgc/internal/write-barrier.h index f3aaedb1b..67f039c65 100644 --- a/deps/include/cppgc/internal/write-barrier.h +++ b/deps/include/cppgc/internal/write-barrier.h @@ -5,9 +5,13 @@ #ifndef INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ #define INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ +#include +#include + #include "cppgc/heap-state.h" #include "cppgc/internal/api-constants.h" #include "cppgc/internal/atomic-entry-flag.h" +#include "cppgc/platform.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/trace-trait.h" #include "v8config.h" // NOLINT(build/include_directory) @@ -22,8 +26,11 @@ class HeapHandle; namespace internal { +#if defined(CPPGC_CAGED_HEAP) class WriteBarrierTypeForCagedHeapPolicy; +#else // !CPPGC_CAGED_HEAP class WriteBarrierTypeForNonCagedHeapPolicy; +#endif // !CPPGC_CAGED_HEAP class V8_EXPORT WriteBarrier final { public: @@ -60,6 +67,8 @@ class V8_EXPORT WriteBarrier final { template static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params, HeapHandleCallback callback); + // Returns the required write barrier for a given `value`. + static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params); template static V8_INLINE Type GetWriteBarrierTypeForExternallyReferencedObject( @@ -141,9 +150,27 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { return ValueModeDispatch::Get(slot, value, params, callback); } + template + static V8_INLINE WriteBarrier::Type Get(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return GetNoSlot(value, params, callback); + } + template static V8_INLINE WriteBarrier::Type GetForExternallyReferenced( - const void* value, WriteBarrier::Params& params, HeapHandleCallback) { + const void* value, WriteBarrier::Params& params, + HeapHandleCallback callback) { + return GetNoSlot(value, params, callback); + } + + private: + WriteBarrierTypeForCagedHeapPolicy() = delete; + + template + static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback) { if (!TryGetCagedHeap(value, value, params)) { return WriteBarrier::Type::kNone; } @@ -153,14 +180,14 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { return SetAndReturnType(params); } - private: - WriteBarrierTypeForCagedHeapPolicy() = delete; - 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 = @@ -187,6 +214,11 @@ struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< 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())) { + return SetAndReturnType(params); + } +#endif // !CPPGC_YOUNG_GENERATION bool within_cage = TryGetCagedHeap(slot, value, params); if (!within_cage) { return WriteBarrier::Type::kNone; @@ -251,6 +283,15 @@ class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { return ValueModeDispatch::Get(slot, value, params, callback); } + template + static V8_INLINE WriteBarrier::Type Get(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + // The slot will never be used in `Get()` below. + return Get(nullptr, value, params, + callback); + } + template static V8_INLINE WriteBarrier::Type GetForExternallyReferenced( const void* value, WriteBarrier::Params& params, @@ -281,7 +322,10 @@ struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< HeapHandleCallback callback) { // The following check covers nullptr as well as sentinel pointer. if (object <= static_cast(kSentinelPointer)) { - return WriteBarrier::Type::kNone; + return SetAndReturnType(params); + } + if (V8_LIKELY(!WriteBarrier::IsAnyIncrementalOrConcurrentMarking())) { + return SetAndReturnType(params); } if (IsMarking(object, ¶ms.heap)) { return SetAndReturnType(params); @@ -324,6 +368,13 @@ WriteBarrier::Type WriteBarrier::GetWriteBarrierType( slot, nullptr, params, callback); } +// static +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* value, WriteBarrier::Params& params) { + return WriteBarrierTypePolicy::Get(value, params, + []() {}); +} + // static template WriteBarrier::Type diff --git a/deps/include/cppgc/liveness-broker.h b/deps/include/cppgc/liveness-broker.h index e44909128..c94eef0d4 100644 --- a/deps/include/cppgc/liveness-broker.h +++ b/deps/include/cppgc/liveness-broker.h @@ -44,7 +44,10 @@ class V8_EXPORT LivenessBroker final { public: template bool IsHeapObjectAlive(const T* object) const { - return object && + // 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 || IsHeapObjectAliveImpl( TraceTrait::GetTraceDescriptor(object).base_object_payload); } diff --git a/deps/include/cppgc/macros.h b/deps/include/cppgc/macros.h index 70ab44c65..030f397e3 100644 --- a/deps/include/cppgc/macros.h +++ b/deps/include/cppgc/macros.h @@ -5,7 +5,7 @@ #ifndef INCLUDE_CPPGC_MACROS_H_ #define INCLUDE_CPPGC_MACROS_H_ -#include +#include #include "cppgc/internal/compiler-specific.h" diff --git a/deps/include/cppgc/member.h b/deps/include/cppgc/member.h index 7b76bc4f7..38105b8e4 100644 --- a/deps/include/cppgc/member.h +++ b/deps/include/cppgc/member.h @@ -24,8 +24,11 @@ namespace internal { // BasicMember on casting to the right type as needed. class MemberBase { protected: + struct AtomicInitializerTag {}; + MemberBase() = default; explicit MemberBase(const void* value) : raw_(value) {} + MemberBase(const void* value, AtomicInitializerTag) { SetRawAtomic(value); } const void** GetRawSlot() const { return &raw_; } const void* GetRaw() const { return raw_; } @@ -61,6 +64,20 @@ class BasicMember final : private MemberBase, private CheckingPolicy { this->CheckPointer(Get()); } BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT + // 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) + : MemberBase(nullptr, atomic) {} + BasicMember(SentinelPointer s, AtomicInitializerTag atomic) + : MemberBase(s, atomic) {} + BasicMember(T* raw, AtomicInitializerTag atomic) : MemberBase(raw, atomic) { + InitializingWriteBarrier(); + this->CheckPointer(Get()); + } + BasicMember(T& raw, AtomicInitializerTag atomic) + : BasicMember(&raw, atomic) {} // Copy ctor. BasicMember(const BasicMember& other) : BasicMember(other.Get()) {} // Allow heterogeneous construction. @@ -79,9 +96,8 @@ class BasicMember final : private MemberBase, private CheckingPolicy { template ::value>> - BasicMember( // NOLINT - BasicMember&& other) noexcept + BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) { other.Clear(); } @@ -90,10 +106,9 @@ class BasicMember final : private MemberBase, private CheckingPolicy { typename PersistentLocationPolicy, typename PersistentCheckingPolicy, typename = std::enable_if_t::value>> - BasicMember( // NOLINT - const BasicPersistent& - p) + BasicMember(const BasicPersistent& p) : BasicMember(p.Get()) {} // Copy assignment. @@ -161,7 +176,7 @@ class BasicMember final : private MemberBase, private CheckingPolicy { } explicit operator bool() const { return Get(); } - operator T*() const { return Get(); } // NOLINT + operator T*() const { return Get(); } T* operator->() const { return Get(); } T& operator*() const { return *Get(); } @@ -203,6 +218,8 @@ class BasicMember final : private MemberBase, private CheckingPolicy { void ClearFromGC() const { MemberBase::ClearFromGC(); } + T* GetFromGC() const { return Get(); } + friend class cppgc::Visitor; template friend struct cppgc::TraceTrait; @@ -211,20 +228,20 @@ class BasicMember final : private MemberBase, private CheckingPolicy { template -bool operator==( - BasicMember member1, - BasicMember - member2) { +bool operator==(const BasicMember& member1, + const BasicMember& member2) { return member1.Get() == member2.Get(); } template -bool operator!=( - BasicMember member1, - BasicMember - member2) { +bool operator!=(const BasicMember& member1, + const BasicMember& member2) { return !(member1 == member2); } diff --git a/deps/include/cppgc/name-provider.h b/deps/include/cppgc/name-provider.h index 8b70b8ea5..224dd4b5d 100644 --- a/deps/include/cppgc/name-provider.h +++ b/deps/include/cppgc/name-provider.h @@ -57,7 +57,7 @@ class V8_EXPORT NameProvider { * * @returns a human readable name for the object. */ - virtual const char* GetName() const = 0; + virtual const char* GetHumanReadableName() const = 0; }; } // namespace cppgc diff --git a/deps/include/cppgc/persistent.h b/deps/include/cppgc/persistent.h index d7aac723c..182fb0854 100644 --- a/deps/include/cppgc/persistent.h +++ b/deps/include/cppgc/persistent.h @@ -41,11 +41,11 @@ class PersistentBase { node_ = nullptr; } - private: + protected: mutable const void* raw_ = nullptr; mutable PersistentNode* node_ = nullptr; - friend class PersistentRegion; + friend class PersistentRegionBase; }; // The basic class from which all Persistent classes are generated. @@ -95,7 +95,7 @@ class BasicPersistent final : public PersistentBase, template ::value>> - BasicPersistent( // NOLINT + BasicPersistent( const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current()) @@ -118,7 +118,7 @@ class BasicPersistent final : public PersistentBase, template ::value>> - BasicPersistent(internal::BasicMember member, const SourceLocation& loc = SourceLocation::Current()) @@ -141,7 +141,7 @@ class BasicPersistent final : public PersistentBase, } // Move assignment. - BasicPersistent& operator=(BasicPersistent&& other) { + BasicPersistent& operator=(BasicPersistent&& other) noexcept { if (this == &other) return *this; Clear(); PersistentBase::operator=(std::move(other)); @@ -181,7 +181,7 @@ class BasicPersistent final : public PersistentBase, } explicit operator bool() const { return Get(); } - operator T*() const { return Get(); } // NOLINT + operator T*() const { return Get(); } T* operator->() const { return Get(); } T& operator*() const { return *Get(); } @@ -259,6 +259,12 @@ class BasicPersistent final : public PersistentBase, } } + // Set Get() for details. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") + T* GetFromGC() const { + return static_cast(const_cast(GetValue())); + } + friend class cppgc::Visitor; }; diff --git a/deps/include/cppgc/platform.h b/deps/include/cppgc/platform.h index 0d7377668..3276a26b6 100644 --- a/deps/include/cppgc/platform.h +++ b/deps/include/cppgc/platform.h @@ -5,6 +5,8 @@ #ifndef INCLUDE_CPPGC_PLATFORM_H_ #define INCLUDE_CPPGC_PLATFORM_H_ +#include + #include "v8-platform.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) @@ -146,6 +148,7 @@ namespace internal { V8_EXPORT void Abort(); } // namespace internal + } // namespace cppgc #endif // INCLUDE_CPPGC_PLATFORM_H_ diff --git a/deps/include/cppgc/prefinalizer.h b/deps/include/cppgc/prefinalizer.h index 29b18bef9..6153b37ff 100644 --- a/deps/include/cppgc/prefinalizer.h +++ b/deps/include/cppgc/prefinalizer.h @@ -38,7 +38,7 @@ class PrefinalizerRegistration final { "Only garbage collected objects can have prefinalizers"); \ Class* self = static_cast(object); \ if (liveness_broker.IsHeapObjectAlive(self)) return false; \ - self->Class::PreFinalizer(); \ + self->PreFinalizer(); \ return true; \ } \ \ diff --git a/deps/include/cppgc/sentinel-pointer.h b/deps/include/cppgc/sentinel-pointer.h index f7915834e..b049d1a2b 100644 --- a/deps/include/cppgc/sentinel-pointer.h +++ b/deps/include/cppgc/sentinel-pointer.h @@ -14,7 +14,7 @@ namespace internal { // sentinel is defined by the embedder. struct SentinelPointer { template - operator T*() const { // NOLINT + operator T*() const { static constexpr intptr_t kSentinelValue = 1; return reinterpret_cast(kSentinelValue); } diff --git a/deps/include/cppgc/source-location.h b/deps/include/cppgc/source-location.h index 29d69b0a1..da5a5ede5 100644 --- a/deps/include/cppgc/source-location.h +++ b/deps/include/cppgc/source-location.h @@ -5,6 +5,7 @@ #ifndef INCLUDE_CPPGC_SOURCE_LOCATION_H_ #define INCLUDE_CPPGC_SOURCE_LOCATION_H_ +#include #include #include "v8config.h" // NOLINT(build/include_directory) diff --git a/deps/include/cppgc/testing.h b/deps/include/cppgc/testing.h index f93897a9a..229ce140f 100644 --- a/deps/include/cppgc/testing.h +++ b/deps/include/cppgc/testing.h @@ -44,6 +44,55 @@ class V8_EXPORT V8_NODISCARD OverrideEmbedderStackStateScope final { HeapHandle& heap_handle_; }; +/** + * Testing interface for managed heaps that allows for controlling garbage + * collection timings. Embedders should use this class when testing the + * interaction of their code with incremental/concurrent garbage collection. + */ +class V8_EXPORT StandaloneTestingHeap final { + public: + explicit StandaloneTestingHeap(HeapHandle&); + + /** + * Start an incremental garbage collection. + */ + void StartGarbageCollection(); + + /** + * Perform an incremental step. This will also schedule concurrent steps if + * needed. + * + * \param stack_state The state of the stack during the step. + */ + bool PerformMarkingStep(EmbedderStackState stack_state); + + /** + * Finalize the current garbage collection cycle atomically. + * Assumes that garbage collection is in progress. + * + * \param stack_state The state of the stack for finalizing the garbage + * collection cycle. + */ + void FinalizeGarbageCollection(EmbedderStackState stack_state); + + /** + * Toggle main thread marking on/off. Allows to stress concurrent marking + * (e.g. to better detect data races). + * + * \param should_mark Denotes whether the main thread should contribute to + * marking. Defaults to true. + */ + void ToggleMainThreadMarking(bool should_mark); + + /** + * Force enable compaction for the next garbage collection cycle. + */ + void ForceCompactionForNextGarbageCollection(); + + private: + HeapHandle& heap_handle_; +}; + } // namespace testing } // namespace cppgc diff --git a/deps/include/cppgc/type-traits.h b/deps/include/cppgc/type-traits.h index 2b50a2164..56cd55d61 100644 --- a/deps/include/cppgc/type-traits.h +++ b/deps/include/cppgc/type-traits.h @@ -7,6 +7,7 @@ // This file should stay with minimal dependencies to allow embedder to check // against Oilpan types without including any other parts. +#include #include namespace cppgc { @@ -164,6 +165,18 @@ struct IsUntracedMemberType : std::false_type {}; template struct IsUntracedMemberType : std::true_type {}; +template +struct IsComplete { + private: + template + static std::true_type IsSizeOfKnown(U*); + static std::false_type IsSizeOfKnown(...); + + public: + static constexpr bool value = + decltype(IsSizeOfKnown(std::declval()))::value; +}; + } // namespace internal /** @@ -223,6 +236,12 @@ constexpr bool IsWeakMemberTypeV = internal::IsWeakMemberType::value; template constexpr bool IsWeakV = internal::IsWeak::value; +/** + * Value is true for types that are complete, and false otherwise. + */ +template +constexpr bool IsCompleteV = internal::IsComplete::value; + } // namespace cppgc #endif // INCLUDE_CPPGC_TYPE_TRAITS_H_ diff --git a/deps/include/cppgc/vendor.go b/deps/include/cppgc/vendor.go index 8cb15f7dc..2890b3c90 100644 --- a/deps/include/cppgc/vendor.go +++ b/deps/include/cppgc/vendor.go @@ -1,3 +1,3 @@ +// Generated by deps/upgrade_v8.py, DO NOT REMOVE/EDIT MANUALLY. // Package cppgc is required to provide support for vendoring modules -// DO NOT REMOVE package cppgc diff --git a/deps/include/cppgc/visitor.h b/deps/include/cppgc/visitor.h index 95fd5fc84..57e2ce396 100644 --- a/deps/include/cppgc/visitor.h +++ b/deps/include/cppgc/visitor.h @@ -12,6 +12,7 @@ #include "cppgc/internal/pointer-policies.h" #include "cppgc/liveness-broker.h" #include "cppgc/member.h" +#include "cppgc/sentinel-pointer.h" #include "cppgc/source-location.h" #include "cppgc/trace-trait.h" #include "cppgc/type-traits.h" @@ -158,22 +159,67 @@ class V8_EXPORT Visitor { } /** - * Trace method for ephemerons. Used for tracing raw ephemeron in which the - * key and value are kept separately. + * Trace method for a single ephemeron. Used for tracing a raw ephemeron in + * which the `key` and `value` are kept separately. * - * \param key WeakMember reference weakly retaining a key object. - * \param value Member reference weakly retaining a value object. + * \param weak_member_key WeakMember reference weakly retaining a key object. + * \param member_value Member reference with ephemeron semantics. */ - template - void TraceEphemeron(const WeakMember& key, const V* value) { - const K* k = key.GetRawAtomic(); - if (!k) return; - TraceDescriptor value_desc = TraceTrait::GetTraceDescriptor(value); - // `value` must always be non-null. `value_desc.base_object_payload` may be - // null in the case that value is not a garbage-collected object but only - // traceable. + template + void TraceEphemeron(const WeakMember& weak_member_key, + const Member* member_value) { + const KeyType* key = weak_member_key.GetRawAtomic(); + if (!key) return; + + // `value` must always be non-null. + CPPGC_DCHECK(member_value); + const ValueType* value = member_value->GetRawAtomic(); + if (!value) return; + + // KeyType and ValueType may refer to GarbageCollectedMixin. + TraceDescriptor value_desc = + TraceTrait::GetTraceDescriptor(value); + CPPGC_DCHECK(value_desc.base_object_payload); + const void* key_base_object_payload = + TraceTrait::GetTraceDescriptor(key).base_object_payload; + CPPGC_DCHECK(key_base_object_payload); + + VisitEphemeron(key_base_object_payload, value, value_desc); + } + + /** + * Trace method for a single ephemeron. Used for tracing a raw ephemeron in + * which the `key` and `value` are kept separately. Note that this overload + * is for non-GarbageCollected `value`s that can be traced though. + * + * \param key `WeakMember` reference weakly retaining a key object. + * \param value Reference weakly retaining a value object. Note that + * `ValueType` here should not be `Member`. It is expected that + * `TraceTrait::GetTraceDescriptor(value)` returns a + * `TraceDescriptor` with a null base pointer but a valid trace method. + */ + template + void TraceEphemeron(const WeakMember& weak_member_key, + const ValueType* value) { + static_assert(!IsGarbageCollectedOrMixinTypeV, + "garbage-collected types must use WeakMember and Member"); + const KeyType* key = weak_member_key.GetRawAtomic(); + if (!key) return; + + // `value` must always be non-null. CPPGC_DCHECK(value); - VisitEphemeron(key, value, value_desc); + TraceDescriptor value_desc = + TraceTrait::GetTraceDescriptor(value); + // `value_desc.base_object_payload` must be null as this override is only + // taken for non-garbage-collected values. + CPPGC_DCHECK(!value_desc.base_object_payload); + + // KeyType might be a GarbageCollectedMixin. + const void* key_base_object_payload = + TraceTrait::GetTraceDescriptor(key).base_object_payload; + CPPGC_DCHECK(key_base_object_payload); + + VisitEphemeron(key_base_object_payload, value, value_desc); } /** @@ -273,10 +319,10 @@ class V8_EXPORT Visitor { template 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 (*weak == kSentinelPointer) return; - const auto* raw = weak->Get(); - if (!info.IsHeapObjectAlive(raw)) { + if (raw_ptr == kSentinelPointer) return; + if (!info.IsHeapObjectAlive(raw_ptr)) { weak->ClearFromGC(); } } @@ -290,11 +336,11 @@ class V8_EXPORT Visitor { static_assert(internal::IsGarbageCollectedOrMixinType::value, "Persistent's pointee type must be GarbageCollected or " "GarbageCollectedMixin"); - if (!p.Get()) { + auto* ptr = p.GetFromGC(); + if (!ptr) { return; } - VisitRoot(p.Get(), TraceTrait::GetTraceDescriptor(p.Get()), - loc); + VisitRoot(ptr, TraceTrait::GetTraceDescriptor(ptr), loc); } template < @@ -309,7 +355,8 @@ class V8_EXPORT Visitor { "GarbageCollectedMixin"); static_assert(!internal::IsAllocatedOnCompactableSpace::value, "Weak references to compactable objects are not allowed"); - VisitWeakRoot(p.Get(), TraceTrait::GetTraceDescriptor(p.Get()), + auto* ptr = p.GetFromGC(); + VisitWeakRoot(ptr, TraceTrait::GetTraceDescriptor(ptr), &HandleWeak, &p, loc); } @@ -327,14 +374,6 @@ class V8_EXPORT Visitor { friend class internal::VisitorBase; }; -template -struct TraceTrait> { - static TraceDescriptor GetTraceDescriptor(const void* self) { - return TraceTrait::GetTraceDescriptor( - static_cast*>(self)->GetRawAtomic()); - } -}; - } // namespace cppgc #endif // INCLUDE_CPPGC_VISITOR_H_ diff --git a/deps/include/js_protocol.pdl b/deps/include/js_protocol.pdl index 666952f27..b34c8551a 100644 --- a/deps/include/js_protocol.pdl +++ b/deps/include/js_protocol.pdl @@ -175,7 +175,7 @@ domain Debugger command enable parameters # The maximum size in bytes of collected scripts (not referenced by other heap objects) - # the debugger can hold. Puts no limit if paramter is omitted. + # the debugger can hold. Puts no limit if parameter is omitted. experimental optional number maxScriptsCacheSize returns # Unique identifier of the debugger. @@ -267,7 +267,7 @@ domain Debugger BreakpointId breakpointId # Restarts particular call frame from the beginning. - command restartFrame + deprecated command restartFrame parameters # Call frame identifier to evaluate on. CallFrameId callFrameId @@ -707,13 +707,17 @@ experimental domain HeapProfiler # when the tracking is stopped. optional boolean reportProgress optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue command takeHeapSnapshot parameters # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken. optional boolean reportProgress - # If true, a raw snapshot without artifical roots will be generated + # If true, a raw snapshot without artificial roots will be generated optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue event addHeapSnapshotChunk parameters @@ -841,24 +845,6 @@ domain Profiler # Type profile entries for parameters and return values of the functions in the script. array of TypeProfileEntry entries - # Collected counter information. - experimental type CounterInfo extends object - properties - # Counter name. - string name - # Counter value. - integer value - - # Runtime call counter information. - experimental type RuntimeCallCounterInfo extends object - properties - # Counter name. - string name - # Counter value. - number value - # Counter time in seconds. - number time - command disable command enable @@ -923,30 +909,6 @@ domain Profiler # Type profile for all scripts since startTypeProfile() was turned on. array of ScriptTypeProfile result - # Enable counters collection. - experimental command enableCounters - - # Disable counters collection. - experimental command disableCounters - - # Retrieve counters. - experimental command getCounters - returns - # Collected counters information. - array of CounterInfo result - - # Enable run time call stats collection. - experimental command enableRuntimeCallStats - - # Disable run time call stats collection. - experimental command disableRuntimeCallStats - - # Retrieve run time call stats. - experimental command getRuntimeCallStats - returns - # Collected runtime call counter information. - array of RuntimeCallCounterInfo result - event consoleProfileFinished parameters string id @@ -968,13 +930,13 @@ domain Profiler # Reports coverage delta since the last poll (either from an event like this, or from # `takePreciseCoverage` for the current isolate. May only be sent if precise code # coverage has been started. This event can be trigged by the embedder to, for example, - # trigger collection of coverage data immediatelly at a certain point in time. + # trigger collection of coverage data immediately at a certain point in time. experimental event preciseCoverageDeltaUpdate parameters # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. number timestamp # Identifier for distinguishing coverage events. - string occassion + string occasion # Coverage data for the current isolate. array of ScriptCoverage result @@ -1221,7 +1183,7 @@ domain Runtime string origin # Human readable name describing given context. string name - # A system-unique execution context identifier. Unlike the id, this is unique accross + # A system-unique execution context identifier. Unlike the id, this is unique across # multiple processes, so can be reliably used to identify specific context while backend # performs a cross-process navigation. experimental string uniqueId @@ -1250,6 +1212,10 @@ domain Runtime optional RemoteObject exception # Identifier of the context where exception happened. optional ExecutionContextId executionContextId + # Dictionary with entries of meta data that the client associated + # with this exception, such as information about associated network + # requests, etc. + experimental optional object exceptionMetaData # Number of milliseconds since epoch. type Timestamp extends number @@ -1339,6 +1305,8 @@ domain Runtime # Symbolic group name that can be used to release multiple objects. If objectGroup is not # specified and objectId is, objectGroup will be inherited from object. optional string objectGroup + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + experimental optional boolean throwOnSideEffect returns # Call result. RemoteObject result @@ -1418,9 +1386,9 @@ domain Runtime # evaluation and allows unsafe-eval. Defaults to true. experimental optional boolean allowUnsafeEvalBlockedByCSP # An alternative way to specify the execution context to evaluate in. - # Compared to contextId that may be reused accross processes, this is guaranteed to be + # Compared to contextId that may be reused across processes, this is guaranteed to be # system-unique, so it can be used to prevent accidental evaluation of the expression - # in context different than intended (e.g. as a result of navigation accross process + # in context different than intended (e.g. as a result of navigation across process # boundaries). # This is mutually exclusive with `contextId`. experimental optional string uniqueContextId @@ -1459,6 +1427,8 @@ domain Runtime experimental optional boolean accessorPropertiesOnly # Whether preview should be generated for the results. experimental optional boolean generatePreview + # If true, returns non-indexed properties only. + experimental optional boolean nonIndexedPropertiesOnly returns # Object properties. array of PropertyDescriptor result @@ -1563,7 +1533,10 @@ domain Runtime # execution context. If omitted and `executionContextName` is not set, # the binding is exposed to all execution contexts of the target. # This parameter is mutually exclusive with `executionContextName`. - optional ExecutionContextId executionContextId + # Deprecated in favor of `executionContextName` due to an unclear use case + # and bugs in implementation (crbug.com/1169639). `executionContextId` will be + # removed in the future. + deprecated optional ExecutionContextId executionContextId # If specified, the binding is exposed to the executionContext with # matching name, even for contexts created after the binding is added. # See also `ExecutionContext.name` and `worldName` parameter to @@ -1659,6 +1632,8 @@ domain Runtime parameters RemoteObject object object hints + # Identifier of the context where the call was made. + experimental optional ExecutionContextId executionContextId # This domain is deprecated. deprecated domain Schema diff --git a/deps/include/libplatform/vendor.go b/deps/include/libplatform/vendor.go index fbc8d0442..12e7d71bb 100644 --- a/deps/include/libplatform/vendor.go +++ b/deps/include/libplatform/vendor.go @@ -1,3 +1,3 @@ +// Generated by deps/upgrade_v8.py, DO NOT REMOVE/EDIT MANUALLY. // Package libplatform is required to provide support for vendoring modules -// DO NOT REMOVE package libplatform diff --git a/deps/include/v8-array-buffer.h b/deps/include/v8-array-buffer.h new file mode 100644 index 000000000..0ce2b6536 --- /dev/null +++ b/deps/include/v8-array-buffer.h @@ -0,0 +1,433 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ARRAY_BUFFER_H_ +#define INCLUDE_V8_ARRAY_BUFFER_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class SharedArrayBuffer; + +#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2 +#endif + +enum class ArrayBufferCreationMode { kInternalized, kExternalized }; + +/** + * A wrapper around the backing store (i.e. the raw memory) of an array buffer. + * See a document linked in http://crbug.com/v8/9908 for more information. + * + * The allocation and destruction of backing stores is generally managed by + * V8. Clients should always use standard C++ memory ownership types (i.e. + * std::unique_ptr and std::shared_ptr) to manage lifetimes of backing stores + * properly, since V8 internal objects may alias backing stores. + * + * This object does not keep the underlying |ArrayBuffer::Allocator| alive by + * default. Use Isolate::CreateParams::array_buffer_allocator_shared when + * creating the Isolate to make it hold a reference to the allocator itself. + */ +class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { + public: + ~BackingStore(); + + /** + * Return a pointer to the beginning of the memory block for this backing + * store. The pointer is only valid as long as this backing store object + * lives. + */ + void* Data() const; + + /** + * The length (in bytes) of this backing store. + */ + size_t ByteLength() const; + + /** + * Indicates whether the backing store was created for an ArrayBuffer or + * a SharedArrayBuffer. + */ + bool IsShared() const; + + /** + * Prevent implicit instantiation of operator delete with size_t argument. + * The size_t argument would be incorrect because ptr points to the + * internal BackingStore object. + */ + void operator delete(void* ptr) { ::operator delete(ptr); } + + /** + * Wrapper around ArrayBuffer::Allocator::Reallocate that preserves IsShared. + * Assumes that the backing_store was allocated by the ArrayBuffer allocator + * of the given isolate. + */ + static std::unique_ptr Reallocate( + v8::Isolate* isolate, std::unique_ptr backing_store, + size_t byte_length); + + /** + * This callback is used only if the memory block for a BackingStore cannot be + * allocated with an ArrayBuffer::Allocator. In such cases the destructor of + * the BackingStore invokes the callback to free the memory block. + */ + using DeleterCallback = void (*)(void* data, size_t length, + void* deleter_data); + + /** + * If the memory block of a BackingStore is static or is managed manually, + * then this empty deleter along with nullptr deleter_data can be passed to + * ArrayBuffer::NewBackingStore to indicate that. + * + * The manually managed case should be used with caution and only when it + * is guaranteed that the memory block freeing happens after detaching its + * ArrayBuffer. + */ + static void EmptyDeleter(void* data, size_t length, void* deleter_data); + + private: + /** + * See [Shared]ArrayBuffer::GetBackingStore and + * [Shared]ArrayBuffer::NewBackingStore. + */ + BackingStore(); +}; + +#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) +// Use v8::BackingStore::DeleterCallback instead. +using BackingStoreDeleterCallback = void (*)(void* data, size_t length, + void* deleter_data); + +#endif + +/** + * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). + */ +class V8_EXPORT ArrayBuffer : public Object { + public: + /** + * A thread-safe allocator that V8 uses to allocate |ArrayBuffer|'s memory. + * The allocator is a global V8 setting. It has to be set via + * Isolate::CreateParams. + * + * Memory allocated through this allocator by V8 is accounted for as external + * memory by V8. Note that V8 keeps track of the memory for all internalized + * |ArrayBuffer|s. Responsibility for tracking external memory (using + * Isolate::AdjustAmountOfExternalAllocatedMemory) is handed over to the + * embedder upon externalization and taken over upon internalization (creating + * an internalized buffer from an existing buffer). + * + * Note that it is unsafe to call back into V8 from any of the allocator + * functions. + */ + class V8_EXPORT Allocator { + public: + virtual ~Allocator() = default; + + /** + * Allocate |length| bytes. Return nullptr if allocation is not successful. + * Memory should be initialized to zeroes. + */ + virtual void* Allocate(size_t length) = 0; + + /** + * Allocate |length| bytes. Return nullptr if allocation is not successful. + * Memory does not have to be initialized. + */ + virtual void* AllocateUninitialized(size_t length) = 0; + + /** + * Free the memory block of size |length|, pointed to by |data|. + * That memory is guaranteed to be previously allocated by |Allocate|. + */ + virtual void Free(void* data, size_t length) = 0; + + /** + * Reallocate the memory block of size |old_length| to a memory block of + * size |new_length| by expanding, contracting, or copying the existing + * memory block. If |new_length| > |old_length|, then the new part of + * the memory must be initialized to zeros. Return nullptr if reallocation + * is not successful. + * + * The caller guarantees that the memory block was previously allocated + * using Allocate or AllocateUninitialized. + * + * The default implementation allocates a new block and copies data. + */ + virtual void* Reallocate(void* data, size_t old_length, size_t new_length); + + /** + * ArrayBuffer allocation mode. kNormal is a malloc/free style allocation, + * while kReservation is for larger allocations with the ability to set + * access permissions. + */ + enum class AllocationMode { kNormal, kReservation }; + + /** + * Convenience allocator. + * + * When the virtual memory cage is enabled, this allocator will allocate its + * backing memory inside the cage. Otherwise, it will rely on malloc/free. + * + * Caller takes ownership, i.e. the returned object needs to be freed using + * |delete allocator| once it is no longer in use. + */ + static Allocator* NewDefaultAllocator(); + }; + + /** + * Data length in bytes. + */ + size_t ByteLength() const; + + /** + * Create a new ArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created ArrayBuffer and + * will be deallocated when it is garbage-collected, + * unless the object is externalized. + */ + static Local New(Isolate* isolate, size_t byte_length); + + /** + * Create a new ArrayBuffer with an existing backing store. + * The created array keeps a reference to the backing store until the array + * is garbage collected. Note that the IsExternal bit does not affect this + * reference from the array to the backing store. + * + * In future IsExternal bit will be removed. Until then the bit is set as + * follows. If the backing store does not own the underlying buffer, then + * the array is created in externalized state. Otherwise, the array is created + * in internalized state. In the latter case the array can be transitioned + * to the externalized state using Externalize(backing_store). + */ + static Local New(Isolate* isolate, + std::shared_ptr backing_store); + + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * ArrayBuffer::New. + * + * If the allocator returns nullptr, then the function may cause GCs in the + * given isolate and re-try the allocation. If GCs do not help, then the + * function will crash with an out-of-memory error. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to ArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 API function. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, + void* deleter_data); + + /** + * Returns true if this ArrayBuffer may be detached. + */ + bool IsDetachable() 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. + */ + void Detach(); + + /** + * 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. + */ + std::shared_ptr GetBackingStore(); + + V8_INLINE static ArrayBuffer* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + static const int kEmbedderFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + + private: + ArrayBuffer(); + static void CheckCast(Value* obj); +}; + +#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2 +#endif + +/** + * A base class for an instance of one of "views" over ArrayBuffer, + * including TypedArrays and DataView (ES6 draft 15.13). + */ +class V8_EXPORT ArrayBufferView : public Object { + public: + /** + * Returns underlying ArrayBuffer. + */ + Local Buffer(); + /** + * Byte offset in |Buffer|. + */ + size_t ByteOffset(); + /** + * Size of a view in bytes. + */ + size_t ByteLength(); + + /** + * Copy the contents of the ArrayBufferView's buffer to an embedder defined + * memory without additional overhead that calling ArrayBufferView::Buffer + * might incur. + * + * Will write at most min(|byte_length|, ByteLength) bytes starting at + * ByteOffset of the underlying buffer to the memory starting at |dest|. + * Returns the number of bytes actually written. + */ + size_t CopyContents(void* dest, size_t byte_length); + + /** + * Returns true if ArrayBufferView's backing ArrayBuffer has already been + * allocated. + */ + bool HasBuffer() const; + + V8_INLINE static ArrayBufferView* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = + V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; + static const int kEmbedderFieldCount = + V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; + + private: + ArrayBufferView(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of DataView constructor (ES6 draft 15.13.7). + */ +class V8_EXPORT DataView : public ArrayBufferView { + public: + static Local New(Local array_buffer, + size_t byte_offset, size_t length); + static Local New(Local shared_array_buffer, + size_t byte_offset, size_t length); + V8_INLINE static DataView* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + DataView(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in SharedArrayBuffer constructor. + */ +class V8_EXPORT SharedArrayBuffer : public Object { + public: + /** + * Data length in bytes. + */ + size_t ByteLength() const; + + /** + * Create a new SharedArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created SharedArrayBuffer and + * will be deallocated when it is garbage-collected, + * unless the object is externalized. + */ + static Local New(Isolate* isolate, size_t byte_length); + + /** + * Create a new SharedArrayBuffer with an existing backing store. + * The created array keeps a reference to the backing store until the array + * is garbage collected. Note that the IsExternal bit does not affect this + * reference from the array to the backing store. + * + * In future IsExternal bit will be removed. Until then the bit is set as + * follows. If the backing store does not own the underlying buffer, then + * the array is created in externalized state. Otherwise, the array is created + * in internalized state. In the latter case the array can be transitioned + * to the externalized state using Externalize(backing_store). + */ + static Local New( + Isolate* isolate, std::shared_ptr backing_store); + + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * SharedArrayBuffer::New. + * + * If the allocator returns nullptr, then the function may cause GCs in the + * given isolate and re-try the allocation. If GCs do not help, then the + * function will crash with an out-of-memory error. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to SharedArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 functions. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, + void* deleter_data); + + /** + * 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. + */ + std::shared_ptr GetBackingStore(); + + V8_INLINE static SharedArrayBuffer* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + + private: + SharedArrayBuffer(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_ARRAY_BUFFER_H_ diff --git a/deps/include/v8-callbacks.h b/deps/include/v8-callbacks.h new file mode 100644 index 000000000..870df6a82 --- /dev/null +++ b/deps/include/v8-callbacks.h @@ -0,0 +1,377 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ISOLATE_CALLBACKS_H_ +#define INCLUDE_V8_ISOLATE_CALLBACKS_H_ + +#include + +#include + +#include "cppgc/common.h" +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(V8_OS_WIN) +struct _EXCEPTION_POINTERS; +#endif + +namespace v8 { + +template +class FunctionCallbackInfo; +class Isolate; +class Message; +class Module; +class Object; +class Promise; +class ScriptOrModule; +class String; +class UnboundScript; +class Value; + +/** + * A JIT code event is issued each time code is added, moved or removed. + * + * \note removal events are not currently issued. + */ +struct JitCodeEvent { + enum EventType { + CODE_ADDED, + CODE_MOVED, + CODE_REMOVED, + CODE_ADD_LINE_POS_INFO, + CODE_START_LINE_INFO_RECORDING, + CODE_END_LINE_INFO_RECORDING + }; + // Definition of the code position type. The "POSITION" type means the place + // in the source code which are of interest when making stack traces to + // pin-point the source location of a stack frame as close as possible. + // The "STATEMENT_POSITION" means the place at the beginning of each + // statement, and is used to indicate possible break locations. + enum PositionType { POSITION, STATEMENT_POSITION }; + + // There are three different kinds of CodeType, one for JIT code generated + // by the optimizing compiler, one for byte code generated for the + // interpreter, and one for code generated from Wasm. For JIT_CODE and + // WASM_CODE, |code_start| points to the beginning of jitted assembly code, + // while for BYTE_CODE events, |code_start| points to the first bytecode of + // the interpreted function. + enum CodeType { BYTE_CODE, JIT_CODE, WASM_CODE }; + + // Type of event. + EventType type; + CodeType code_type; + // Start of the instructions. + void* code_start; + // Size of the instructions. + size_t code_len; + // Script info for CODE_ADDED event. + Local script; + // User-defined data for *_LINE_INFO_* event. It's used to hold the source + // code line information which is returned from the + // CODE_START_LINE_INFO_RECORDING event. And it's passed to subsequent + // CODE_ADD_LINE_POS_INFO and CODE_END_LINE_INFO_RECORDING events. + void* user_data; + + struct name_t { + // Name of the object associated with the code, note that the string is not + // zero-terminated. + const char* str; + // Number of chars in str. + size_t len; + }; + + struct line_info_t { + // PC offset + size_t offset; + // Code position + size_t pos; + // The position type. + PositionType position_type; + }; + + struct wasm_source_info_t { + // Source file name. + const char* filename; + // Length of filename. + size_t filename_size; + // Line number table, which maps offsets of JITted code to line numbers of + // source file. + const line_info_t* line_number_table; + // Number of entries in the line number table. + size_t line_number_table_size; + }; + + wasm_source_info_t* wasm_source_info; + + union { + // Only valid for CODE_ADDED. + struct name_t name; + + // Only valid for CODE_ADD_LINE_POS_INFO + struct line_info_t line_info; + + // New location of instructions. Only valid for CODE_MOVED. + void* new_code_start; + }; + + Isolate* isolate; +}; + +/** + * Option flags passed to the SetJitCodeEventHandler function. + */ +enum JitCodeEventOptions { + kJitCodeEventDefault = 0, + // Generate callbacks for already existent code. + kJitCodeEventEnumExisting = 1 +}; + +/** + * Callback function passed to SetJitCodeEventHandler. + * + * \param event code add, move or removal event. + */ +using JitCodeEventHandler = void (*)(const JitCodeEvent* event); + +// --- Garbage Collection Callbacks --- + +/** + * Applications can register callback functions which will be called before and + * after certain garbage collection operations. Allocations are not allowed in + * the callback functions, you therefore cannot manipulate objects (set or + * delete properties for example) since it is possible such operations will + * result in the allocation of objects. + */ +enum GCType { + kGCTypeScavenge = 1 << 0, + kGCTypeMarkSweepCompact = 1 << 1, + kGCTypeIncrementalMarking = 1 << 2, + kGCTypeProcessWeakCallbacks = 1 << 3, + kGCTypeAll = kGCTypeScavenge | kGCTypeMarkSweepCompact | + kGCTypeIncrementalMarking | kGCTypeProcessWeakCallbacks +}; + +/** + * GCCallbackFlags is used to notify additional information about the GC + * callback. + * - kGCCallbackFlagConstructRetainedObjectInfos: The GC callback is for + * constructing retained object infos. + * - kGCCallbackFlagForced: The GC callback is for a forced GC for testing. + * - kGCCallbackFlagSynchronousPhantomCallbackProcessing: The GC callback + * is called synchronously without getting posted to an idle task. + * - kGCCallbackFlagCollectAllAvailableGarbage: The GC callback is called + * in a phase where V8 is trying to collect all available garbage + * (e.g., handling a low memory notification). + * - kGCCallbackScheduleIdleGarbageCollection: The GC callback is called to + * trigger an idle garbage collection. + */ +enum GCCallbackFlags { + kNoGCCallbackFlags = 0, + kGCCallbackFlagConstructRetainedObjectInfos = 1 << 1, + kGCCallbackFlagForced = 1 << 2, + kGCCallbackFlagSynchronousPhantomCallbackProcessing = 1 << 3, + kGCCallbackFlagCollectAllAvailableGarbage = 1 << 4, + kGCCallbackFlagCollectAllExternalMemory = 1 << 5, + kGCCallbackScheduleIdleGarbageCollection = 1 << 6, +}; + +using GCCallback = void (*)(GCType type, GCCallbackFlags flags); + +using InterruptCallback = void (*)(Isolate* isolate, void* data); + +/** + * This callback is invoked when the heap size is close to the heap limit and + * V8 is likely to abort with out-of-memory error. + * The callback can extend the heap limit by returning a value that is greater + * than the current_heap_limit. The initial heap limit is the limit that was + * set after heap setup. + */ +using NearHeapLimitCallback = size_t (*)(void* data, size_t current_heap_limit, + size_t initial_heap_limit); + +/** + * Callback function passed to SetUnhandledExceptionCallback. + */ +#if defined(V8_OS_WIN) +using UnhandledExceptionCallback = + int (*)(_EXCEPTION_POINTERS* exception_pointers); +#endif + +// --- Counters Callbacks --- + +using CounterLookupCallback = int* (*)(const char* name); + +using CreateHistogramCallback = void* (*)(const char* name, int min, int max, + size_t buckets); + +using AddHistogramSampleCallback = void (*)(void* histogram, int sample); + +// --- Exceptions --- + +using FatalErrorCallback = void (*)(const char* location, const char* message); + +using OOMErrorCallback = void (*)(const char* location, bool is_heap_oom); + +using MessageCallback = void (*)(Local message, Local data); + +// --- Tracing --- + +enum LogEventStatus : int { kStart = 0, kEnd = 1, kStamp = 2 }; +using LogEventCallback = void (*)(const char* name, + int /* LogEventStatus */ status); + +// --- Crashkeys Callback --- +enum class CrashKeyId { + kIsolateAddress, + kReadonlySpaceFirstPageAddress, + kMapSpaceFirstPageAddress, + kCodeSpaceFirstPageAddress, + kDumpType, +}; + +using AddCrashKeyCallback = void (*)(CrashKeyId id, const std::string& value); + +// --- Enter/Leave Script Callback --- +using BeforeCallEnteredCallback = void (*)(Isolate*); +using CallCompletedCallback = void (*)(Isolate*); + +// --- AllowCodeGenerationFromStrings callbacks --- + +/** + * Callback to check if code generation from strings is allowed. See + * Context::AllowCodeGenerationFromStrings. + */ +using AllowCodeGenerationFromStringsCallback = bool (*)(Local context, + Local source); + +struct ModifyCodeGenerationFromStringsResult { + // If true, proceed with the codegen algorithm. Otherwise, block it. + bool codegen_allowed = false; + // Overwrite the original source with this string, if present. + // Use the original source if empty. + // This field is considered only if codegen_allowed is true. + MaybeLocal modified_source; +}; + +/** + * Access type specification. + */ +enum AccessType { + ACCESS_GET, + ACCESS_SET, + ACCESS_HAS, + ACCESS_DELETE, + ACCESS_KEYS +}; + +// --- Failed Access Check Callback --- + +using FailedAccessCheckCallback = void (*)(Local target, + AccessType type, Local data); + +/** + * Callback to check if codegen is allowed from a source object, and convert + * the source to string if necessary. See: ModifyCodeGenerationFromStrings. + */ +using ModifyCodeGenerationFromStringsCallback = + ModifyCodeGenerationFromStringsResult (*)(Local context, + Local source); +using ModifyCodeGenerationFromStringsCallback2 = + ModifyCodeGenerationFromStringsResult (*)(Local context, + Local source, + bool is_code_like); + +// --- WebAssembly compilation callbacks --- +using ExtensionCallback = bool (*)(const FunctionCallbackInfo&); + +using AllowWasmCodeGenerationCallback = bool (*)(Local context, + Local source); + +// --- Callback for APIs defined on v8-supported objects, but implemented +// by the embedder. Example: WebAssembly.{compile|instantiate}Streaming --- +using ApiImplementationCallback = void (*)(const FunctionCallbackInfo&); + +// --- Callback for WebAssembly.compileStreaming --- +using WasmStreamingCallback = void (*)(const FunctionCallbackInfo&); + +// --- Callback for loading source map file for Wasm profiling support +using WasmLoadSourceMapCallback = Local (*)(Isolate* isolate, + const char* name); + +// --- Callback for checking if WebAssembly Simd is enabled --- +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 the SharedArrayBuffer constructor is enabled --- +using SharedArrayBufferConstructorEnabledCallback = + bool (*)(Local context); + +/** + * HostImportModuleDynamicallyWithImportAssertionsCallback is called when we + * require the embedder to load a module. This is used as part of the dynamic + * import syntax. + * + * The referrer contains metadata about the script/module that calls + * import. + * + * The specifier is the name of the module that should be imported. + * + * The import_assertions are import assertions for this request in the form: + * [key1, value1, key2, value2, ...] where the keys and values are of type + * v8::String. Note, unlike the FixedArray passed to ResolveModuleCallback and + * returned from ModuleRequest::GetImportAssertions(), this array does not + * contain the source Locations of the assertions. + * + * The embedder must compile, instantiate, evaluate the Module, and + * obtain its namespace object. + * + * The Promise returned from this function is forwarded to userland + * JavaScript. The embedder must resolve this promise with the module + * namespace object. In case of an exception, the embedder must reject + * this promise with the exception. If the promise creation itself + * fails (e.g. due to stack overflow), the embedder must propagate + * that exception by returning an empty MaybeLocal. + */ +using HostImportModuleDynamicallyWithImportAssertionsCallback = + MaybeLocal (*)(Local context, + Local referrer, + Local specifier, + Local import_assertions); + +/** + * HostInitializeImportMetaObjectCallback is called the first time import.meta + * is accessed for a module. Subsequent access will reuse the same value. + * + * The method combines two implementation-defined abstract operations into one: + * HostGetImportMetaProperties and HostFinalizeImportMeta. + * + * The embedder should use v8::Object::CreateDataProperty to add properties on + * the meta object. + */ +using HostInitializeImportMetaObjectCallback = void (*)(Local context, + Local module, + Local meta); + +/** + * PrepareStackTraceCallback is called when the stack property of an error is + * first accessed. The return value will be used as the stack value. If this + * callback is registed, the |Error.prepareStackTrace| API will be disabled. + * |sites| is an array of call sites, specified in + * https://v8.dev/docs/stack-trace-api + */ +using PrepareStackTraceCallback = MaybeLocal (*)(Local context, + Local error, + Local sites); + +} // namespace v8 + +#endif // INCLUDE_V8_ISOLATE_CALLBACKS_H_ diff --git a/deps/include/v8-container.h b/deps/include/v8-container.h new file mode 100644 index 000000000..ce0686036 --- /dev/null +++ b/deps/include/v8-container.h @@ -0,0 +1,129 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_CONTAINER_H_ +#define INCLUDE_V8_CONTAINER_H_ + +#include +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; + +/** + * An instance of the built-in array constructor (ECMA-262, 15.4.2). + */ +class V8_EXPORT Array : public Object { + public: + uint32_t Length() const; + + /** + * Creates a JavaScript array with the given length. If the length + * is negative the returned array will have length 0. + */ + static Local New(Isolate* isolate, int length = 0); + + /** + * Creates a JavaScript array out of a Local array in C++ + * with a known length. + */ + static Local New(Isolate* isolate, Local* elements, + size_t length); + V8_INLINE static Array* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Array(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in Map constructor (ECMA-262, 6th Edition, 23.1.1). + */ +class V8_EXPORT Map : public Object { + public: + size_t Size() const; + void Clear(); + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + Local key); + V8_WARN_UNUSED_RESULT MaybeLocal Set(Local context, + Local key, + Local value); + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + /** + * Returns an array of length Size() * 2, where index N is the Nth key and + * index N + 1 is the Nth value. + */ + Local AsArray() const; + + /** + * Creates a new empty Map. + */ + static Local New(Isolate* isolate); + + V8_INLINE static Map* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Map(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in Set constructor (ECMA-262, 6th Edition, 23.2.1). + */ +class V8_EXPORT Set : public Object { + public: + size_t Size() const; + void Clear(); + V8_WARN_UNUSED_RESULT MaybeLocal Add(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + /** + * Returns an array of the keys in this Set. + */ + Local AsArray() const; + + /** + * Creates a new empty Set. + */ + static Local New(Isolate* isolate); + + V8_INLINE static Set* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Set(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_CONTAINER_H_ diff --git a/deps/include/v8-context.h b/deps/include/v8-context.h new file mode 100644 index 000000000..bd28c6c9c --- /dev/null +++ b/deps/include/v8-context.h @@ -0,0 +1,418 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_CONTEXT_H_ +#define INCLUDE_V8_CONTEXT_H_ + +#include + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-snapshot.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; +class MicrotaskQueue; +class Object; +class ObjectTemplate; +class Value; +class String; + +/** + * A container for extension names. + */ +class V8_EXPORT ExtensionConfiguration { + public: + ExtensionConfiguration() : name_count_(0), names_(nullptr) {} + ExtensionConfiguration(int name_count, const char* names[]) + : name_count_(name_count), names_(names) {} + + const char** begin() const { return &names_[0]; } + const char** end() const { return &names_[name_count_]; } + + private: + const int name_count_; + const char** names_; +}; + +/** + * A sandboxed execution context with its own set of built-in objects + * and functions. + */ +class V8_EXPORT Context : public Data { + public: + /** + * Returns the global proxy object. + * + * Global proxy object is a thin wrapper whose prototype points to actual + * context's global object with the properties like Object, etc. This is done + * that way for security reasons (for more details see + * https://wiki.mozilla.org/Gecko:SplitWindow). + * + * Please note that changes to global proxy object prototype most probably + * would break VM---v8 expects only global object as a prototype of global + * proxy object. + */ + Local Global(); + + /** + * Detaches the global object from its context before + * the global object can be reused to create a new context. + */ + void DetachGlobal(); + + /** + * Creates a new context and returns a handle to the newly allocated + * context. + * + * \param isolate The isolate in which to create the context. + * + * \param extensions An optional extension configuration containing + * the extensions to be installed in the newly created context. + * + * \param global_template An optional object template from which the + * global object for the newly created context will be created. + * + * \param global_object An optional global object to be reused for + * the newly created context. This global object must have been + * created by a previous call to Context::New with the same global + * template. The state of the global object will be completely reset + * and only object identify will remain. + */ + static Local New( + Isolate* isolate, ExtensionConfiguration* extensions = nullptr, + MaybeLocal global_template = MaybeLocal(), + MaybeLocal global_object = MaybeLocal(), + DeserializeInternalFieldsCallback internal_fields_deserializer = + DeserializeInternalFieldsCallback(), + MicrotaskQueue* microtask_queue = nullptr); + + /** + * Create a new context from a (non-default) context snapshot. There + * is no way to provide a global object template since we do not create + * a new global object from template, but we can reuse a global object. + * + * \param isolate See v8::Context::New. + * + * \param context_snapshot_index The index of the context snapshot to + * deserialize from. Use v8::Context::New for the default snapshot. + * + * \param embedder_fields_deserializer Optional callback to deserialize + * internal fields. It should match the SerializeInternalFieldCallback used + * to serialize. + * + * \param extensions See v8::Context::New. + * + * \param global_object See v8::Context::New. + */ + static MaybeLocal FromSnapshot( + Isolate* isolate, size_t context_snapshot_index, + DeserializeInternalFieldsCallback embedder_fields_deserializer = + DeserializeInternalFieldsCallback(), + ExtensionConfiguration* extensions = nullptr, + MaybeLocal global_object = MaybeLocal(), + MicrotaskQueue* microtask_queue = nullptr); + + /** + * Returns an global object that isn't backed by an actual context. + * + * The global template needs to have access checks with handlers installed. + * If an existing global object is passed in, the global object is detached + * from its context. + * + * Note that this is different from a detached context where all accesses to + * the global proxy will fail. Instead, the access check handlers are invoked. + * + * It is also not possible to detach an object returned by this method. + * Instead, the access check handlers need to return nothing to achieve the + * same effect. + * + * It is possible, however, to create a new context from the global object + * returned by this method. + */ + static MaybeLocal NewRemoteContext( + Isolate* isolate, Local global_template, + MaybeLocal global_object = MaybeLocal()); + + /** + * Sets the security token for the context. To access an object in + * another context, the security tokens must match. + */ + void SetSecurityToken(Local token); + + /** Restores the security token to the default value. */ + void UseDefaultSecurityToken(); + + /** Returns the security token of this context.*/ + Local GetSecurityToken(); + + /** + * Enter this context. After entering a context, all code compiled + * and run is compiled and run in this context. If another context + * is already entered, this old context is saved so it can be + * restored when the new context is exited. + */ + void Enter(); + + /** + * Exit this context. Exiting the current context restores the + * context that was in place when entering the current context. + */ + void Exit(); + + /** Returns the isolate associated with a current context. */ + Isolate* GetIsolate(); + + /** Returns the microtask queue associated with a current context. */ + MicrotaskQueue* GetMicrotaskQueue(); + + /** + * The field at kDebugIdIndex used to be reserved for the inspector. + * It now serves no purpose. + */ + enum EmbedderDataFields { kDebugIdIndex = 0 }; + + /** + * Return the number of fields allocated for embedder data. + */ + uint32_t GetNumberOfEmbedderDataFields(); + + /** + * Gets the embedder data with the given index, which must have been set by a + * previous call to SetEmbedderData with the same index. + */ + V8_INLINE Local GetEmbedderData(int index); + + /** + * Gets the binding object used by V8 extras. Extra natives get a reference + * to this object and can use it to "export" functionality by adding + * properties. Extra natives can also "import" functionality by accessing + * properties added by the embedder using the V8 API. + */ + Local GetExtrasBindingObject(); + + /** + * Sets the embedder data with the given index, growing the data as + * needed. Note that index 0 currently has a special meaning for Chrome's + * debugger. + */ + void SetEmbedderData(int index, Local value); + + /** + * Gets a 2-byte-aligned native pointer from the embedder data with the given + * index, which must have been set by a previous call to + * SetAlignedPointerInEmbedderData with the same index. Note that index 0 + * currently has a special meaning for Chrome's debugger. + */ + V8_INLINE void* GetAlignedPointerFromEmbedderData(int index); + + /** + * Sets a 2-byte-aligned native pointer in the embedder data with the given + * index, growing the data as needed. Note that index 0 currently has a + * special meaning for Chrome's debugger. + */ + void SetAlignedPointerInEmbedderData(int index, void* value); + + /** + * Control whether code generation from strings is allowed. Calling + * this method with false will disable 'eval' and the 'Function' + * constructor for code running in this context. If 'eval' or the + * 'Function' constructor are used an exception will be thrown. + * + * If code generation from strings is not allowed the + * V8::AllowCodeGenerationFromStrings callback will be invoked if + * set before blocking the call to 'eval' or the 'Function' + * constructor. If that callback returns true, the call will be + * allowed, otherwise an exception will be thrown. If no callback is + * set an exception will be thrown. + */ + void AllowCodeGenerationFromStrings(bool allow); + + /** + * Returns true if code generation from strings is allowed for the context. + * For more details see AllowCodeGenerationFromStrings(bool) documentation. + */ + bool IsCodeGenerationFromStringsAllowed() const; + + /** + * Sets the error description for the exception that is thrown when + * code generation from strings is not allowed and 'eval' or the 'Function' + * constructor are called. + */ + void SetErrorMessageForCodeGenerationFromStrings(Local message); + + /** + * Return data that was previously attached to the context snapshot via + * SnapshotCreator, and removes the reference to it. + * Repeated call with the same index returns an empty MaybeLocal. + */ + template + V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); + + /** + * If callback is set, abort any attempt to execute JavaScript in this + * context, call the specified callback, and throw an exception. + * To unset abort, pass nullptr as callback. + */ + using AbortScriptExecutionCallback = void (*)(Isolate* isolate, + Local context); + void SetAbortScriptExecution(AbortScriptExecutionCallback callback); + + /** + * Returns the value that was set or restored by + * SetContinuationPreservedEmbedderData(), if any. + */ + Local GetContinuationPreservedEmbedderData() const; + + /** + * Sets a value that will be stored on continuations and reset while the + * continuation runs. + */ + void SetContinuationPreservedEmbedderData(Local context); + + /** + * Set or clear hooks to be invoked for promise lifecycle operations. + * To clear a hook, set it to an empty v8::Function. Each function will + * receive the observed promise as the first argument. If a chaining + * operation is used on a promise, the init will additionally receive + * the parent promise as the second argument. + */ + void SetPromiseHooks(Local init_hook, Local before_hook, + Local after_hook, + Local resolve_hook); + + /** + * Stack-allocated class which sets the execution context for all + * operations executed within a local scope. + */ + class V8_NODISCARD Scope { + public: + explicit V8_INLINE Scope(Local context) : context_(context) { + context_->Enter(); + } + V8_INLINE ~Scope() { context_->Exit(); } + + private: + Local context_; + }; + + /** + * Stack-allocated class to support the backup incumbent settings object + * stack. + * https://html.spec.whatwg.org/multipage/webappapis.html#backup-incumbent-settings-object-stack + */ + class V8_EXPORT V8_NODISCARD BackupIncumbentScope final { + public: + /** + * |backup_incumbent_context| is pushed onto the backup incumbent settings + * object stack. + */ + explicit BackupIncumbentScope(Local backup_incumbent_context); + ~BackupIncumbentScope(); + + /** + * Returns address that is comparable with JS stack address. Note that JS + * stack may be allocated separately from the native stack. See also + * |TryCatch::JSStackComparableAddressPrivate| for details. + */ + V8_DEPRECATE_SOON( + "This is private V8 information that should not be exposed in the API.") + uintptr_t JSStackComparableAddress() const { + return JSStackComparableAddressPrivate(); + } + + private: + friend class internal::Isolate; + + uintptr_t JSStackComparableAddressPrivate() const { + return js_stack_comparable_address_; + } + + Local backup_incumbent_context_; + uintptr_t js_stack_comparable_address_ = 0; + const BackupIncumbentScope* prev_ = nullptr; + }; + + V8_INLINE static Context* Cast(Data* data); + + private: + friend class Value; + friend class Script; + friend class Object; + friend class Function; + + static void CheckCast(Data* obj); + + internal::Address* GetDataFromSnapshotOnce(size_t index); + Local SlowGetEmbedderData(int index); + void* SlowGetAlignedPointerFromEmbedderData(int index); +}; + +// --- Implementation --- + +Local Context::GetEmbedderData(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A ctx = *reinterpret_cast(this); + A embedder_data = + I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); + int value_offset = + I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); + A value = I::ReadRawField(embedder_data, value_offset); +#ifdef V8_COMPRESS_POINTERS + // We read the full pointer value and then decompress it in order to avoid + // dealing with potential endiannes issues. + value = + I::DecompressTaggedAnyField(embedder_data, static_cast(value)); +#endif + internal::Isolate* isolate = internal::IsolateFromNeverReadOnlySpaceObject( + *reinterpret_cast(this)); + A* result = HandleScope::CreateHandle(isolate, value); + return Local(reinterpret_cast(result)); +#else + return SlowGetEmbedderData(index); +#endif +} + +void* Context::GetAlignedPointerFromEmbedderData(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A ctx = *reinterpret_cast(this); + A embedder_data = + I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); + int value_offset = + I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); +#ifdef V8_HEAP_SANDBOX + value_offset += I::kEmbedderDataSlotRawPayloadOffset; +#endif + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(ctx); + return reinterpret_cast( + I::ReadExternalPointerField(isolate, embedder_data, value_offset, + internal::kEmbedderDataSlotPayloadTag)); +#else + return SlowGetAlignedPointerFromEmbedderData(index); +#endif +} + +template +MaybeLocal Context::GetDataFromSnapshotOnce(size_t index) { + T* data = reinterpret_cast(GetDataFromSnapshotOnce(index)); + if (data) internal::PerformCastCheck(data); + return Local(data); +} + +Context* Context::Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); +} + +} // namespace v8 + +#endif // INCLUDE_V8_CONTEXT_H_ diff --git a/deps/include/v8-cppgc.h b/deps/include/v8-cppgc.h index 2c2219304..813e0842f 100644 --- a/deps/include/v8-cppgc.h +++ b/deps/include/v8-cppgc.h @@ -9,12 +9,14 @@ #include #include +#include "cppgc/common.h" #include "cppgc/custom-space.h" #include "cppgc/heap-statistics.h" #include "cppgc/internal/write-barrier.h" #include "cppgc/visitor.h" -#include "v8-internal.h" // NOLINT(build/include_directory) -#include "v8.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8-traced-handle.h" // NOLINT(build/include_directory) namespace cppgc { class AllocationHandle; @@ -23,10 +25,14 @@ class HeapHandle; namespace v8 { +class Object; + namespace internal { class CppHeap; } // namespace internal +class CustomSpaceStatisticsReceiver; + /** * Describes how V8 wrapper objects maintain references to garbage-collected C++ * objects. @@ -118,6 +124,30 @@ class V8_EXPORT CppHeap { cppgc::HeapStatistics CollectStatistics( cppgc::HeapStatistics::DetailLevel detail_level); + /** + * Collects statistics for the given spaces and reports them to the receiver. + * + * \param custom_spaces a collection of custom space indicies. + * \param receiver an object that gets the results. + */ + void CollectCustomSpaceStatisticsAtLastGC( + std::vector custom_spaces, + std::unique_ptr receiver); + + /** + * Enables a detached mode that allows testing garbage collection using + * `cppgc::testing` APIs. Once used, the heap cannot be attached to an + * `Isolate` anymore. + */ + void EnableDetachedGarbageCollectionsForTesting(); + + /** + * Performs a stop-the-world garbage collection for testing purposes. + * + * \param stack_state The stack state to assume for the garbage collection. + */ + void CollectGarbageForTesting(cppgc::EmbedderStackState stack_state); + private: CppHeap() = default; @@ -262,6 +292,26 @@ class V8_EXPORT JSHeapConsistency final { const TracedReferenceBase& ref); }; +/** + * Provided as input to `CppHeap::CollectCustomSpaceStatisticsAtLastGC()`. + * + * Its method is invoked with the results of the statistic collection. + */ +class CustomSpaceStatisticsReceiver { + public: + virtual ~CustomSpaceStatisticsReceiver() = default; + /** + * Reports the size of a space at the last GC. It is called for each space + * that was requested in `CollectCustomSpaceStatisticsAtLastGC()`. + * + * \param space_index The index of the space. + * \param bytes The total size of live objects in the space at the last GC. + * It is zero if there was no GC yet. + */ + virtual void AllocatedBytes(cppgc::CustomSpaceIndex space_index, + size_t bytes) = 0; +}; + } // namespace v8 namespace cppgc { diff --git a/deps/include/v8-data.h b/deps/include/v8-data.h new file mode 100644 index 000000000..dbd36c9a0 --- /dev/null +++ b/deps/include/v8-data.h @@ -0,0 +1,65 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DATA_H_ +#define INCLUDE_V8_DATA_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * The superclass of objects that can reside on V8's heap. + */ +class V8_EXPORT Data { + public: + /** + * Returns true if this data is a |v8::Value|. + */ + bool IsValue() const; + + /** + * Returns true if this data is a |v8::Module|. + */ + bool IsModule() const; + + /** + * Returns true if this data is a |v8::Private|. + */ + bool IsPrivate() const; + + /** + * Returns true if this data is a |v8::ObjectTemplate|. + */ + bool IsObjectTemplate() const; + + /** + * Returns true if this data is a |v8::FunctionTemplate|. + */ + bool IsFunctionTemplate() const; + + /** + * Returns true if this data is a |v8::Context|. + */ + bool IsContext() const; + + private: + Data(); +}; + +/** + * A fixed-sized array with elements of type Data. + */ +class V8_EXPORT FixedArray : public Data { + public: + int Length() const; + Local Get(Local context, int i) const; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DATA_H_ diff --git a/deps/include/v8-date.h b/deps/include/v8-date.h new file mode 100644 index 000000000..e7a01f29b --- /dev/null +++ b/deps/include/v8-date.h @@ -0,0 +1,43 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DATE_H_ +#define INCLUDE_V8_DATE_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in Date constructor (ECMA-262, 15.9). + */ +class V8_EXPORT Date : public Object { + public: + static V8_WARN_UNUSED_RESULT MaybeLocal New(Local context, + double time); + + /** + * A specialization of Value::NumberValue that is more efficient + * because we know the structure of this object. + */ + double ValueOf() const; + + V8_INLINE static Date* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DATE_H_ diff --git a/deps/include/v8-debug.h b/deps/include/v8-debug.h new file mode 100644 index 000000000..a13ae3f6d --- /dev/null +++ b/deps/include/v8-debug.h @@ -0,0 +1,151 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DEBUG_H_ +#define INCLUDE_V8_DEBUG_H_ + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; +class String; + +/** + * A single JavaScript stack frame. + */ +class V8_EXPORT StackFrame { + public: + /** + * Returns the number, 1-based, of the line for the associate function call. + * This method will return Message::kNoLineNumberInfo if it is unable to + * retrieve the line number, or if kLineNumber was not passed as an option + * when capturing the StackTrace. + */ + int GetLineNumber() const; + + /** + * Returns the 1-based column offset on the line for the associated function + * call. + * This method will return Message::kNoColumnInfo if it is unable to retrieve + * the column number, or if kColumnOffset was not passed as an option when + * capturing the StackTrace. + */ + int GetColumn() const; + + /** + * Returns the id of the script for the function for this StackFrame. + * This method will return Message::kNoScriptIdInfo if it is unable to + * retrieve the script id, or if kScriptId was not passed as an option when + * capturing the StackTrace. + */ + int GetScriptId() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame. + */ + Local GetScriptName() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame or sourceURL value if the script name + * is undefined and its source ends with //# sourceURL=... string or + * deprecated //@ sourceURL=... string. + */ + Local GetScriptNameOrSourceURL() const; + + /** + * Returns the source of the script for the function for this StackFrame. + */ + Local GetScriptSource() const; + + /** + * Returns the source mapping URL (if one is present) of the script for + * the function for this StackFrame. + */ + Local GetScriptSourceMappingURL() const; + + /** + * Returns the name of the function associated with this stack frame. + */ + Local GetFunctionName() const; + + /** + * Returns whether or not the associated function is compiled via a call to + * eval(). + */ + bool IsEval() const; + + /** + * Returns whether or not the associated function is called as a + * constructor via "new". + */ + bool IsConstructor() const; + + /** + * Returns whether or not the associated functions is defined in wasm. + */ + bool IsWasm() const; + + /** + * Returns whether or not the associated function is defined by the user. + */ + bool IsUserJavaScript() const; +}; + +/** + * Representation of a JavaScript stack trace. The information collected is a + * snapshot of the execution stack and the information remains valid after + * execution continues. + */ +class V8_EXPORT StackTrace { + public: + /** + * Flags that determine what information is placed captured for each + * StackFrame when grabbing the current stack trace. + * Note: these options are deprecated and we always collect all available + * information (kDetailed). + */ + enum StackTraceOptions { + kLineNumber = 1, + kColumnOffset = 1 << 1 | kLineNumber, + kScriptName = 1 << 2, + kFunctionName = 1 << 3, + kIsEval = 1 << 4, + kIsConstructor = 1 << 5, + kScriptNameOrSourceURL = 1 << 6, + kScriptId = 1 << 7, + kExposeFramesAcrossSecurityOrigins = 1 << 8, + kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName, + kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL + }; + + /** + * Returns a StackFrame at a particular index. + */ + Local GetFrame(Isolate* isolate, uint32_t index) const; + + /** + * Returns the number of StackFrames. + */ + int GetFrameCount() const; + + /** + * Grab a snapshot of the current JavaScript execution stack. + * + * \param frame_limit The maximum number of stack frames we want to capture. + * \param options Enumerates the set of things we will capture for each + * StackFrame. + */ + static Local CurrentStackTrace( + Isolate* isolate, int frame_limit, StackTraceOptions options = kDetailed); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DEBUG_H_ diff --git a/deps/include/v8-embedder-heap.h b/deps/include/v8-embedder-heap.h new file mode 100644 index 000000000..501a4fc52 --- /dev/null +++ b/deps/include/v8-embedder-heap.h @@ -0,0 +1,238 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_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. + */ +class V8_EXPORT EmbedderRootsHandler { + public: + virtual ~EmbedderRootsHandler() = default; + + /** + * Returns true if the TracedGlobal handle should be considered as root for + * the currently running non-tracing garbage collection and false otherwise. + * The default implementation will keep all TracedGlobal references as roots. + * + * If this returns false, then V8 may decide that the object referred to by + * such a handle is reclaimed. In that case: + * - No action is required if handles are used with destructors, i.e., by just + * using |TracedGlobal|. + * - When run without destructors, i.e., by using |TracedReference|, V8 calls + * |ResetRoot|. + * + * Note that the |handle| is different from the handle that the embedder holds + * for retaining the object. The embedder may use |WrapperClassId()| to + * distinguish cases where it wants handles to be treated as roots from not + * being treated as roots. + */ + virtual bool IsRoot(const v8::TracedReference& handle) = 0; + virtual bool IsRoot(const v8::TracedGlobal& handle) = 0; + + /** + * Used in combination with |IsRoot|. Called by V8 when an + * object that is backed by a handle is reclaimed by a non-tracing garbage + * collection. It is up to the embedder to reset the original handle. + * + * Note that the |handle| is different from the handle that the embedder holds + * for retaining the object. It is up to the embedder to find the original + * handle via the object or class id. + */ + 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 TracedGlobal handles. + */ + class V8_EXPORT TracedGlobalHandleVisitor { + public: + virtual ~TracedGlobalHandleVisitor() = default; + virtual void VisitTracedGlobalHandle(const TracedGlobal& handle) {} + 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 TracedGlobal 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 the embedder to notify V8 of an empty execution stack. + */ + V8_DEPRECATE_SOON( + "This call only optimized internal caches which V8 is able to figure out " + "on its own now.") + void NotifyEmptyEmbedderStack(); + + /** + * 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); + virtual bool IsRootForNonTracingGC(const v8::TracedGlobal& handle); + + /** + * See documentation on EmbedderRootsHandler. + */ + virtual void ResetHandleInNonTracingGC( + const v8::TracedReference& handle); + + /* + * Called by the embedder to immediately perform a full garbage collection. + * + * Should only be used in testing code. + */ + void GarbageCollectionForTesting(EmbedderStackState stack_state); + + /* + * 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/deps/include/v8-exception.h b/deps/include/v8-exception.h new file mode 100644 index 000000000..add882da4 --- /dev/null +++ b/deps/include/v8-exception.h @@ -0,0 +1,224 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXCEPTION_H_ +#define INCLUDE_V8_EXCEPTION_H_ + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; +class Message; +class StackTrace; +class String; +class Value; + +namespace internal { +class Isolate; +class ThreadLocalTop; +} // namespace internal + +/** + * Create new error objects by calling the corresponding error object + * constructor with the message. + */ +class V8_EXPORT Exception { + public: + static Local RangeError(Local message); + static Local ReferenceError(Local message); + static Local SyntaxError(Local message); + static Local TypeError(Local message); + static Local WasmCompileError(Local message); + static Local WasmLinkError(Local message); + static Local WasmRuntimeError(Local message); + static Local Error(Local message); + + /** + * Creates an error message for the given exception. + * Will try to reconstruct the original stack trace from the exception value, + * or capture the current stack trace if not available. + */ + static Local CreateMessage(Isolate* isolate, Local exception); + + /** + * Returns the original stack trace that was captured at the creation time + * of a given exception, or an empty handle if not available. + */ + static Local GetStackTrace(Local exception); +}; + +/** + * An external exception handler. + */ +class V8_EXPORT TryCatch { + public: + /** + * Creates a new try/catch block and registers it with v8. Note that + * all TryCatch blocks should be stack allocated because the memory + * location itself is compared against JavaScript try/catch blocks. + */ + explicit TryCatch(Isolate* isolate); + + /** + * Unregisters and deletes this try/catch block. + */ + ~TryCatch(); + + /** + * Returns true if an exception has been caught by this try/catch block. + */ + bool HasCaught() const; + + /** + * For certain types of exceptions, it makes no sense to continue execution. + * + * If CanContinue returns false, the correct action is to perform any C++ + * cleanup needed and then return. If CanContinue returns false and + * HasTerminated returns true, it is possible to call + * CancelTerminateExecution in order to continue calling into the engine. + */ + bool CanContinue() const; + + /** + * Returns true if an exception has been caught due to script execution + * being terminated. + * + * There is no JavaScript representation of an execution termination + * exception. Such exceptions are thrown when the TerminateExecution + * methods are called to terminate a long-running script. + * + * If such an exception has been thrown, HasTerminated will return true, + * indicating that it is possible to call CancelTerminateExecution in order + * to continue calling into the engine. + */ + bool HasTerminated() const; + + /** + * Throws the exception caught by this TryCatch in a way that avoids + * it being caught again by this same TryCatch. As with ThrowException + * it is illegal to execute any JavaScript operations after calling + * ReThrow; the caller must return immediately to where the exception + * is caught. + */ + Local ReThrow(); + + /** + * Returns the exception caught by this try/catch block. If no exception has + * been caught an empty handle is returned. + */ + Local Exception() const; + + /** + * Returns the .stack property of an object. If no .stack + * property is present an empty handle is returned. + */ + V8_WARN_UNUSED_RESULT static MaybeLocal StackTrace( + Local context, Local exception); + + /** + * Returns the .stack property of the thrown object. If no .stack property is + * present or if this try/catch block has not caught an exception, an empty + * handle is returned. + */ + V8_WARN_UNUSED_RESULT MaybeLocal StackTrace( + Local context) const; + + /** + * Returns the message associated with this exception. If there is + * no message associated an empty handle is returned. + */ + Local Message() const; + + /** + * Clears any exceptions that may have been caught by this try/catch block. + * After this method has been called, HasCaught() will return false. Cancels + * the scheduled exception if it is caught and ReThrow() is not called before. + * + * It is not necessary to clear a try/catch block before using it again; if + * another exception is thrown the previously caught exception will just be + * overwritten. However, it is often a good idea since it makes it easier + * to determine which operation threw a given exception. + */ + void Reset(); + + /** + * Set verbosity of the external exception handler. + * + * By default, exceptions that are caught by an external exception + * handler are not reported. Call SetVerbose with true on an + * external exception handler to have exceptions caught by the + * handler reported as if they were not caught. + */ + void SetVerbose(bool value); + + /** + * Returns true if verbosity is enabled. + */ + bool IsVerbose() const; + + /** + * Set whether or not this TryCatch should capture a Message object + * which holds source information about where the exception + * occurred. True by default. + */ + void SetCaptureMessage(bool value); + + V8_DEPRECATE_SOON( + "This is private information that should not be exposed by the API") + static void* JSStackComparableAddress(TryCatch* handler) { + if (handler == nullptr) return nullptr; + return reinterpret_cast(handler->JSStackComparableAddressPrivate()); + } + + TryCatch(const TryCatch&) = delete; + void operator=(const TryCatch&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + /** + * There are cases when the raw address of C++ TryCatch object cannot be + * used for comparisons with addresses into the JS stack. The cases are: + * 1) ARM, ARM64 and MIPS simulators which have separate JS stack. + * 2) Address sanitizer allocates local C++ object in the heap when + * UseAfterReturn mode is enabled. + * This method returns address that can be used for comparisons with + * addresses into the JS stack. When neither simulator nor ASAN's + * UseAfterReturn is enabled, then the address returned will be the address + * of the C++ try catch handler itself. + */ + internal::Address JSStackComparableAddressPrivate() { + return js_stack_comparable_address_; + } + + void ResetInternal(); + + internal::Isolate* isolate_; + TryCatch* next_; + void* exception_; + void* message_obj_; + internal::Address js_stack_comparable_address_; + bool is_verbose_ : 1; + bool can_continue_ : 1; + bool capture_message_ : 1; + bool rethrow_ : 1; + bool has_terminated_ : 1; + + friend class internal::Isolate; + friend class internal::ThreadLocalTop; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EXCEPTION_H_ diff --git a/deps/include/v8-extension.h b/deps/include/v8-extension.h new file mode 100644 index 000000000..0705e2afb --- /dev/null +++ b/deps/include/v8-extension.h @@ -0,0 +1,62 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXTENSION_H_ +#define INCLUDE_V8_EXTENSION_H_ + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class FunctionTemplate; + +// --- Extensions --- + +/** + * Ignore + */ +class V8_EXPORT Extension { + public: + // Note that the strings passed into this constructor must live as long + // as the Extension itself. + Extension(const char* name, const char* source = nullptr, int dep_count = 0, + const char** deps = nullptr, int source_length = -1); + virtual ~Extension() { delete source_; } + virtual Local GetNativeFunctionTemplate( + Isolate* isolate, Local name) { + return Local(); + } + + const char* name() const { return name_; } + size_t source_length() const { return source_length_; } + const String::ExternalOneByteStringResource* source() const { + return source_; + } + int dependency_count() const { return dep_count_; } + const char** dependencies() const { return deps_; } + void set_auto_enable(bool value) { auto_enable_ = value; } + bool auto_enable() { return auto_enable_; } + + // Disallow copying and assigning. + Extension(const Extension&) = delete; + void operator=(const Extension&) = delete; + + private: + const char* name_; + size_t source_length_; // expected to initialize before source_ + String::ExternalOneByteStringResource* source_; + int dep_count_; + const char** deps_; + bool auto_enable_; +}; + +void V8_EXPORT RegisterExtension(std::unique_ptr); + +} // namespace v8 + +#endif // INCLUDE_V8_EXTENSION_H_ diff --git a/deps/include/v8-external.h b/deps/include/v8-external.h new file mode 100644 index 000000000..2e245036f --- /dev/null +++ b/deps/include/v8-external.h @@ -0,0 +1,37 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXTERNAL_H_ +#define INCLUDE_V8_EXTERNAL_H_ + +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +/** + * A JavaScript value that wraps a C++ void*. This type of value is mainly used + * to associate C++ data structures with JavaScript objects. + */ +class V8_EXPORT External : public Value { + public: + static Local New(Isolate* isolate, void* value); + V8_INLINE static External* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + void* Value() const; + + private: + static void CheckCast(v8::Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EXTERNAL_H_ diff --git a/deps/include/v8-fast-api-calls.h b/deps/include/v8-fast-api-calls.h index ca5fc764a..854f845ab 100644 --- a/deps/include/v8-fast-api-calls.h +++ b/deps/include/v8-fast-api-calls.h @@ -70,8 +70,7 @@ * return GetInternalField(wrapper); * } - * static void FastMethod(v8::ApiObject receiver_obj, int param) { - * v8::Object* v8_object = reinterpret_cast(&api_object); + * static void FastMethod(v8::Local receiver_obj, int param) { * CustomEmbedderType* receiver = static_cast( * receiver_obj->GetAlignedPointerFromInternalField( * kV8EmbedderWrapperObjectIndex)); @@ -157,6 +156,7 @@ * - float64_t * Currently supported argument types: * - pointer to an embedder type + * - JavaScript array of primitive types * - bool * - int32_t * - uint32_t @@ -177,8 +177,43 @@ * passes NaN values as-is, i.e. doesn't normalize them. * * To be supported types: - * - arrays of C types + * - TypedArrays and ArrayBuffers * - arrays of embedder types + * + * + * The API offers a limited support for function overloads: + * + * \code + * void FastMethod_2Args(int param, bool another_param); + * void FastMethod_3Args(int param, bool another_param, int third_param); + * + * v8::CFunction fast_method_2args_c_func = + * MakeV8CFunction(FastMethod_2Args); + * v8::CFunction fast_method_3args_c_func = + * MakeV8CFunction(FastMethod_3Args); + * const v8::CFunction fast_method_overloads[] = {fast_method_2args_c_func, + * fast_method_3args_c_func}; + * Local method_template = + * v8::FunctionTemplate::NewWithCFunctionOverloads( + * isolate, SlowCallback, data, signature, length, + * constructor_behavior, side_effect_type, + * {fast_method_overloads, 2}); + * \endcode + * + * In this example a single FunctionTemplate is associated to multiple C++ + * functions. The overload resolution is currently only based on the number of + * arguments passed in a call. For example, if this method_template is + * registered with a wrapper JS object as described above, a call with two + * arguments: + * obj.method(42, true); + * will result in a fast call to FastMethod_2Args, while a call with three or + * more arguments: + * obj.method(42, true, 11); + * will result in a fast call to FastMethod_3Args. Instead a call with less than + * two arguments, like: + * obj.method(42); + * would not result in a fast call but would fall back to executing the + * associated SlowCallback. */ #ifndef INCLUDE_V8_FAST_API_CALLS_H_ @@ -187,10 +222,19 @@ #include #include -#include "v8config.h" // NOLINT(build/include_directory) +#include +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-typed-array.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { +class Isolate; + class CTypeInfo { public: enum class Type : uint8_t { @@ -203,44 +247,245 @@ class CTypeInfo { kFloat32, kFloat64, kV8Value, + kApiObject, // This will be deprecated once all users have + // migrated from v8::ApiObject to v8::Local. }; - // kCallbackOptionsType and kInvalidType are not part of the Type enum - // because they are only used internally. Use values 255 and 254 that - // are larger than any valid Type enum. + // kCallbackOptionsType is not part of the Type enum + // because it is only used internally. Use value 255 that is larger + // than any valid Type enum. static constexpr Type kCallbackOptionsType = Type(255); - static constexpr Type kInvalidType = Type(254); - enum class ArgFlags : uint8_t { + enum class SequenceType : uint8_t { + kScalar, + kIsSequence, // sequence + kIsTypedArray, // TypedArray of T or any ArrayBufferView if T + // is void + kIsArrayBuffer // ArrayBuffer + }; + + enum class Flags : uint8_t { kNone = 0, + kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray + kEnforceRangeBit = 1 << 1, // T must be integral + kClampBit = 1 << 2, // T must be integral + kIsRestrictedBit = 1 << 3, // T must be float or double }; - explicit constexpr CTypeInfo(Type type, ArgFlags flags = ArgFlags::kNone) - : type_(type), flags_(flags) {} + explicit constexpr CTypeInfo( + Type type, SequenceType sequence_type = SequenceType::kScalar, + Flags flags = Flags::kNone) + : type_(type), sequence_type_(sequence_type), flags_(flags) {} + + typedef uint32_t Identifier; + explicit constexpr CTypeInfo(Identifier identifier) + : CTypeInfo(static_cast(identifier >> 16), + static_cast((identifier >> 8) & 255), + static_cast(identifier & 255)) {} + constexpr Identifier GetId() const { + return static_cast(type_) << 16 | + static_cast(sequence_type_) << 8 | + static_cast(flags_); + } constexpr Type GetType() const { return type_; } + constexpr SequenceType GetSequenceType() const { return sequence_type_; } + constexpr Flags GetFlags() const { return flags_; } - constexpr ArgFlags GetFlags() const { return flags_; } + static constexpr bool IsIntegralType(Type type) { + return type == Type::kInt32 || type == Type::kUint32 || + type == Type::kInt64 || type == Type::kUint64; + } - static const CTypeInfo& Invalid() { - static CTypeInfo invalid = CTypeInfo(kInvalidType); - return invalid; + static constexpr bool IsFloatingPointType(Type type) { + return type == Type::kFloat32 || type == Type::kFloat64; + } + + static constexpr bool IsPrimitive(Type type) { + return IsIntegralType(type) || IsFloatingPointType(type) || + type == Type::kBool; } private: Type type_; - ArgFlags flags_; + SequenceType sequence_type_; + Flags flags_; +}; + +struct FastApiTypedArrayBase { + public: + // Returns the length in number of elements. + size_t V8_EXPORT length() const { return length_; } + // Checks whether the given index is within the bounds of the collection. + void V8_EXPORT ValidateIndex(size_t index) const; + + protected: + size_t length_ = 0; +}; + +template +struct FastApiTypedArray : public FastApiTypedArrayBase { + public: + V8_INLINE T get(size_t index) const { +#ifdef DEBUG + ValidateIndex(index); +#endif // DEBUG + T tmp; + memcpy(&tmp, reinterpret_cast(data_) + index, sizeof(T)); + return tmp; + } + + bool getStorageIfAligned(T** elements) const { + if (reinterpret_cast(data_) % alignof(T) != 0) { + return false; + } + *elements = reinterpret_cast(data_); + return true; + } + + private: + // This pointer should include the typed array offset applied. + // It's not guaranteed that it's aligned to sizeof(T), it's only + // guaranteed that it's 4-byte aligned, so for 8-byte types we need to + // provide a special implementation for reading from it, which hides + // the possibly unaligned read in the `get` method. + void* data_; +}; + +// Any TypedArray. It uses kTypedArrayBit with base type void +// Overloaded args of ArrayBufferView and TypedArray are not supported +// (for now) because the generic “any” ArrayBufferView doesn’t have its +// own instance type. It could be supported if we specify that +// TypedArray always has precedence over the generic ArrayBufferView, +// but this complicates overload resolution. +struct FastApiArrayBufferView { + void* data; + size_t byte_length; }; -class CFunctionInfo { +struct FastApiArrayBuffer { + void* data; + size_t byte_length; +}; + +class V8_EXPORT CFunctionInfo { public: - virtual const CTypeInfo& ReturnInfo() const = 0; - virtual unsigned int ArgumentCount() const = 0; - virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0; - virtual bool HasOptions() const = 0; + // Construct a struct to hold a CFunction's type information. + // |return_info| describes the function's return type. + // |arg_info| is an array of |arg_count| CTypeInfos describing the + // arguments. Only the last argument may be of the special type + // CTypeInfo::kCallbackOptionsType. + CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count, + const CTypeInfo* arg_info); + + const CTypeInfo& ReturnInfo() const { return return_info_; } + + // The argument count, not including the v8::FastApiCallbackOptions + // if present. + unsigned int ArgumentCount() const { + return HasOptions() ? arg_count_ - 1 : arg_count_; + } + + // |index| must be less than ArgumentCount(). + // Note: if the last argument passed on construction of CFunctionInfo + // has type CTypeInfo::kCallbackOptionsType, it is not included in + // ArgumentCount(). + const CTypeInfo& ArgumentInfo(unsigned int index) const; + + bool HasOptions() const { + // The options arg is always the last one. + return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() == + CTypeInfo::kCallbackOptionsType; + } + + private: + const CTypeInfo return_info_; + const unsigned int arg_count_; + const CTypeInfo* arg_info_; }; -struct ApiObject { +class V8_EXPORT CFunction { + public: + constexpr CFunction() : address_(nullptr), type_info_(nullptr) {} + + const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); } + + const CTypeInfo& ArgumentInfo(unsigned int index) const { + return type_info_->ArgumentInfo(index); + } + + unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); } + + const void* GetAddress() const { return address_; } + const CFunctionInfo* GetTypeInfo() const { return type_info_; } + + enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime }; + + // Returns whether an overload between this and the given CFunction can + // be resolved at runtime by the RTTI available for the arguments or at + // compile time for functions with different number of arguments. + OverloadResolution GetOverloadResolution(const CFunction* other) { + // Runtime overload resolution can only deal with functions with the + // same number of arguments. Functions with different arity are handled + // by compile time overload resolution though. + if (ArgumentCount() != other->ArgumentCount()) { + return OverloadResolution::kAtCompileTime; + } + + // The functions can only differ by a single argument position. + int diff_index = -1; + for (unsigned int i = 0; i < ArgumentCount(); ++i) { + if (ArgumentInfo(i).GetSequenceType() != + other->ArgumentInfo(i).GetSequenceType()) { + if (diff_index >= 0) { + return OverloadResolution::kImpossible; + } + diff_index = i; + + // We only support overload resolution between sequence types. + if (ArgumentInfo(i).GetSequenceType() == + CTypeInfo::SequenceType::kScalar || + other->ArgumentInfo(i).GetSequenceType() == + CTypeInfo::SequenceType::kScalar) { + return OverloadResolution::kImpossible; + } + } + } + + return OverloadResolution::kAtRuntime; + } + + template + static CFunction Make(F* func) { + return ArgUnwrap::Make(func); + } + + template + V8_DEPRECATED("Use CFunctionBuilder instead.") + static CFunction MakeWithFallbackSupport(F* func) { + return ArgUnwrap::Make(func); + } + + CFunction(const void* address, const CFunctionInfo* type_info); + + private: + const void* address_; + const CFunctionInfo* type_info_; + + template + class ArgUnwrap { + static_assert(sizeof(F) != sizeof(F), + "CFunction must be created from a function pointer."); + }; + + template + class ArgUnwrap { + public: + static CFunction Make(R (*func)(Args...)); + }; +}; + +struct V8_DEPRECATE_SOON("Use v8::Local instead.") ApiObject { uintptr_t address; }; @@ -251,6 +496,14 @@ struct ApiObject { * \endcode */ struct FastApiCallbackOptions { + /** + * Creates a new instance of FastApiCallbackOptions for testing purpose. The + * returned instance may be filled with mock data. + */ + static FastApiCallbackOptions CreateForTesting(Isolate* isolate) { + return {false, {0}}; + } + /** * If the callback wants to signal an error condition or to perform an * allocation, it must set options.fallback to true and do an early return @@ -266,43 +519,16 @@ struct FastApiCallbackOptions { /** * The `data` passed to the FunctionTemplate constructor, or `undefined`. + * `data_ptr` allows for default constructing FastApiCallbackOptions. */ - const ApiObject data; + union { + uintptr_t data_ptr; + v8::Value data; + }; }; namespace internal { -template -struct GetCType; - -#define SPECIALIZE_GET_C_TYPE_FOR(ctype, ctypeinfo) \ - template <> \ - struct GetCType { \ - static constexpr CTypeInfo Get() { \ - return CTypeInfo(CTypeInfo::Type::ctypeinfo); \ - } \ - }; - -#define SUPPORTED_C_TYPES(V) \ - V(void, kVoid) \ - V(bool, kBool) \ - V(int32_t, kInt32) \ - V(uint32_t, kUint32) \ - V(int64_t, kInt64) \ - V(uint64_t, kUint64) \ - V(float, kFloat32) \ - V(double, kFloat64) \ - V(ApiObject, kV8Value) - -SUPPORTED_C_TYPES(SPECIALIZE_GET_C_TYPE_FOR) - -template <> -struct GetCType { - static constexpr CTypeInfo Get() { - return CTypeInfo(CTypeInfo::kCallbackOptionsType); - } -}; - // Helper to count the number of occurances of `T` in `List` template struct count : std::integral_constant {}; @@ -312,108 +538,351 @@ struct count template struct count : count {}; -template +template class CFunctionInfoImpl : public CFunctionInfo { - public: static constexpr int kOptionsArgCount = - count(); + count(); static constexpr int kReceiverCount = 1; - CFunctionInfoImpl() - : return_info_(internal::GetCType::Get()), - arg_count_(sizeof...(Args) - kOptionsArgCount), - arg_info_{internal::GetCType::Get()...} { - static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1, - "Only one options parameter is supported."); - static_assert(sizeof...(Args) >= kOptionsArgCount + kReceiverCount, - "The receiver or the fallback argument is missing."); - constexpr CTypeInfo::Type type = internal::GetCType::Get().GetType(); - static_assert(type == CTypeInfo::Type::kVoid || - type == CTypeInfo::Type::kBool || - type == CTypeInfo::Type::kInt32 || - type == CTypeInfo::Type::kUint32 || - type == CTypeInfo::Type::kFloat32 || - type == CTypeInfo::Type::kFloat64, + + static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1, + "Only one options parameter is supported."); + + static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount, + "The receiver or the options argument is missing."); + + public: + constexpr CFunctionInfoImpl() + : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders), + arg_info_storage_), + arg_info_storage_{ArgBuilders::Build()...} { + constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType(); + static_assert(kReturnType == CTypeInfo::Type::kVoid || + kReturnType == CTypeInfo::Type::kBool || + kReturnType == CTypeInfo::Type::kInt32 || + kReturnType == CTypeInfo::Type::kUint32 || + kReturnType == CTypeInfo::Type::kFloat32 || + kReturnType == CTypeInfo::Type::kFloat64, "64-bit int and api object values are not currently " "supported return types."); } - const CTypeInfo& ReturnInfo() const override { return return_info_; } - unsigned int ArgumentCount() const override { return arg_count_; } - const CTypeInfo& ArgumentInfo(unsigned int index) const override { - if (index >= ArgumentCount()) { - return CTypeInfo::Invalid(); - } - return arg_info_[index]; + private: + const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)]; +}; + +template +struct TypeInfoHelper { + static_assert(sizeof(T) != sizeof(T), "This type is not supported"); +}; + +#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \ + template <> \ + struct TypeInfoHelper { \ + static constexpr CTypeInfo::Flags Flags() { \ + return CTypeInfo::Flags::kNone; \ + } \ + \ + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ + static constexpr CTypeInfo::SequenceType SequenceType() { \ + return CTypeInfo::SequenceType::kScalar; \ + } \ + }; + +template +struct CTypeInfoTraits {}; + +#define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \ + template <> \ + struct CTypeInfoTraits { \ + using ctype = CType; \ + }; + +#define PRIMITIVE_C_TYPES(V) \ + V(bool, kBool) \ + V(int32_t, kInt32) \ + V(uint32_t, kUint32) \ + V(int64_t, kInt64) \ + V(uint64_t, kUint64) \ + V(float, kFloat32) \ + V(double, kFloat64) + +// Same as above, but includes deprecated types for compatibility. +#define ALL_C_TYPES(V) \ + PRIMITIVE_C_TYPES(V) \ + V(void, kVoid) \ + V(v8::Local, kV8Value) \ + V(v8::Local, kV8Value) \ + V(ApiObject, kApiObject) + +// ApiObject was a temporary solution to wrap the pointer to the v8::Value. +// Please use v8::Local in new code for the arguments and +// v8::Local for the receiver, as ApiObject will be deprecated. + +ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR) +PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS) + +#undef PRIMITIVE_C_TYPES +#undef ALL_C_TYPES + +#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \ + template <> \ + struct TypeInfoHelper&> { \ + static constexpr CTypeInfo::Flags Flags() { \ + return CTypeInfo::Flags::kNone; \ + } \ + \ + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ + static constexpr CTypeInfo::SequenceType SequenceType() { \ + return CTypeInfo::SequenceType::kIsTypedArray; \ + } \ + }; + +#define TYPED_ARRAY_C_TYPES(V) \ + V(int32_t, kInt32) \ + V(uint32_t, kUint32) \ + V(int64_t, kInt64) \ + V(uint64_t, kUint64) \ + V(float, kFloat32) \ + V(double, kFloat64) + +TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA) + +#undef TYPED_ARRAY_C_TYPES + +template <> +struct TypeInfoHelper> { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kIsSequence; } - bool HasOptions() const override { return kOptionsArgCount == 1; } +}; - private: - const CTypeInfo return_info_; - const unsigned int arg_count_; - const CTypeInfo arg_info_[sizeof...(Args)]; +template <> +struct TypeInfoHelper> { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kIsTypedArray; + } }; -} // namespace internal +template <> +struct TypeInfoHelper { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } -class V8_EXPORT CFunction { - public: - constexpr CFunction() : address_(nullptr), type_info_(nullptr) {} + static constexpr CTypeInfo::Type Type() { + return CTypeInfo::kCallbackOptionsType; + } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kScalar; + } +}; - const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); } +#define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \ + static_assert(((COND) == 0) || (ASSERTION), MSG) - const CTypeInfo& ArgumentInfo(unsigned int index) const { - return type_info_->ArgumentInfo(index); +template +class CTypeInfoBuilder { + public: + using BaseType = T; + + static constexpr CTypeInfo Build() { + constexpr CTypeInfo::Flags kFlags = + MergeFlags(TypeInfoHelper::Flags(), Flags...); + constexpr CTypeInfo::Type kType = TypeInfoHelper::Type(); + constexpr CTypeInfo::SequenceType kSequenceType = + TypeInfoHelper::SequenceType(); + + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit), + (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray || + kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer), + "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit), + CTypeInfo::IsIntegralType(kType), + "kEnforceRangeBit is only allowed for integral types."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit), + CTypeInfo::IsIntegralType(kType), + "kClampBit is only allowed for integral types."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit), + CTypeInfo::IsFloatingPointType(kType), + "kIsRestrictedBit is only allowed for floating point types."); + STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence, + kType == CTypeInfo::Type::kVoid, + "Sequences are only supported from void type."); + STATIC_ASSERT_IMPLIES( + kSequenceType == CTypeInfo::SequenceType::kIsTypedArray, + CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid, + "TypedArrays are only supported from primitive types or void."); + + // Return the same type with the merged flags. + return CTypeInfo(TypeInfoHelper::Type(), + TypeInfoHelper::SequenceType(), kFlags); } - unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); } + private: + template + static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags, + Rest... rest) { + return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...))); + } + static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); } +}; - const void* GetAddress() const { return address_; } - const CFunctionInfo* GetTypeInfo() const { return type_info_; } +template +class CFunctionBuilderWithFunction { + public: + explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {} - template - static CFunction Make(F* func) { - return ArgUnwrap::Make(func); + template + constexpr auto Ret() { + return CFunctionBuilderWithFunction< + CTypeInfoBuilder, + ArgBuilders...>(fn_); } - template - V8_DEPRECATED("Use CFunction::Make instead.") - static CFunction MakeWithFallbackSupport(F* func) { - return ArgUnwrap::Make(func); + template + constexpr auto Arg() { + // Return a copy of the builder with the Nth arg builder merged with + // template parameter pack Flags. + return ArgImpl( + std::make_index_sequence()); } - template - static CFunction Make(F* func, const CFunctionInfo* type_info) { - return CFunction(reinterpret_cast(func), type_info); + auto Build() { + static CFunctionInfoImpl instance; + return CFunction(fn_, &instance); } private: - const void* address_; - const CFunctionInfo* type_info_; + template + struct GetArgBuilder; + + // Returns the same ArgBuilder as the one at index N, including its flags. + // Flags in the template parameter pack are ignored. + template + struct GetArgBuilder { + using type = + typename std::tuple_element>::type; + }; - CFunction(const void* address, const CFunctionInfo* type_info); + // Returns an ArgBuilder with the same base type as the one at index N, + // but merges the flags with the flags in the template parameter pack. + template + struct GetArgBuilder { + using type = CTypeInfoBuilder< + typename std::tuple_element>::type::BaseType, + std::tuple_element>::type::Build() + .GetFlags(), + Flags...>; + }; - template - static CFunctionInfo* GetCFunctionInfo() { - static internal::CFunctionInfoImpl instance; - return &instance; + // Return a copy of the CFunctionBuilder, but merges the Flags on + // ArgBuilder index N with the new Flags passed in the template parameter + // pack. + template + constexpr auto ArgImpl(std::index_sequence) { + return CFunctionBuilderWithFunction< + RetBuilder, typename GetArgBuilder::type...>(fn_); } - template - class ArgUnwrap { - static_assert(sizeof(F) != sizeof(F), - "CFunction must be created from a function pointer."); - }; + const void* fn_; +}; + +class CFunctionBuilder { + public: + constexpr CFunctionBuilder() {} template - class ArgUnwrap { - public: - static CFunction Make(R (*func)(Args...)) { - return CFunction(reinterpret_cast(func), - GetCFunctionInfo()); - } - }; + constexpr auto Fn(R (*fn)(Args...)) { + return CFunctionBuilderWithFunction, + CTypeInfoBuilder...>( + reinterpret_cast(fn)); + } }; +} // namespace internal + +// static +template +CFunction CFunction::ArgUnwrap::Make(R (*func)(Args...)) { + return internal::CFunctionBuilder().Fn(func).Build(); +} + +using CFunctionBuilder = internal::CFunctionBuilder; + +static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32); +static constexpr CTypeInfo kTypeInfoFloat64 = + CTypeInfo(CTypeInfo::Type::kFloat64); + +/** + * Copies the contents of this JavaScript array to a C++ buffer with + * a given max_length. A CTypeInfo is passed as an argument, + * instructing different rules for conversion (e.g. restricted float/double). + * The element type T of the destination array must match the C type + * corresponding to the CTypeInfo (specified by CTypeInfoTraits). + * If the array length is larger than max_length or the array is of + * unsupported type, the operation will fail, returning false. Generally, an + * array which contains objects, undefined, null or anything not convertible + * to the requested destination type, is considered unsupported. The operation + * returns true on success. `type_info` will be used for conversions. + */ +template +V8_DEPRECATE_SOON( + "Use TryToCopyAndConvertArrayToCppBuffer()") +bool V8_EXPORT V8_WARN_UNUSED_RESULT + TryCopyAndConvertArrayToCppBuffer(Local src, T* dst, + uint32_t max_length); + +template <> +V8_DEPRECATE_SOON( + "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_DEPRECATE_SOON( + "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); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer< + internal::CTypeInfoBuilder::Build().GetId(), int32_t>( + Local src, int32_t* dst, uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer< + internal::CTypeInfoBuilder::Build().GetId(), uint32_t>( + Local src, uint32_t* dst, uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer< + internal::CTypeInfoBuilder::Build().GetId(), float>( + Local src, float* dst, uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer< + internal::CTypeInfoBuilder::Build().GetId(), double>( + Local src, double* dst, uint32_t max_length); + } // namespace v8 #endif // INCLUDE_V8_FAST_API_CALLS_H_ diff --git a/deps/include/v8-forward.h b/deps/include/v8-forward.h new file mode 100644 index 000000000..db3a2017b --- /dev/null +++ b/deps/include/v8-forward.h @@ -0,0 +1,81 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FORWARD_H_ +#define INCLUDE_V8_FORWARD_H_ + +// This header is intended to be used by headers that pass around V8 types, +// either by pointer or using Local. The full definitions can be included +// either via v8.h or the more fine-grained headers. + +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { + +class AccessorSignature; +class Array; +class ArrayBuffer; +class ArrayBufferView; +class BigInt; +class BigInt64Array; +class BigIntObject; +class BigUint64Array; +class Boolean; +class BooleanObject; +class Context; +class DataView; +class Data; +class Date; +class Extension; +class External; +class FixedArray; +class Float32Array; +class Float64Array; +class Function; +template +class FunctionCallbackInfo; +class FunctionTemplate; +class Int16Array; +class Int32; +class Int32Array; +class Int8Array; +class Integer; +class Isolate; +class Map; +class Module; +class Name; +class Number; +class NumberObject; +class Object; +class ObjectTemplate; +class Platform; +class Primitive; +class Private; +class Promise; +class Proxy; +class RegExp; +class Script; +class Set; +class SharedArrayBuffer; +class Signature; +class String; +class StringObject; +class Symbol; +class SymbolObject; +class Template; +class TryCatch; +class TypedArray; +class Uint16Array; +class Uint32; +class Uint32Array; +class Uint8Array; +class Uint8ClampedArray; +class UnboundModuleScript; +class Value; +class WasmMemoryObject; +class WasmModuleObject; + +} // namespace v8 + +#endif // INCLUDE_V8_FORWARD_H_ diff --git a/deps/include/v8-function-callback.h b/deps/include/v8-function-callback.h new file mode 100644 index 000000000..2adff99b1 --- /dev/null +++ b/deps/include/v8-function-callback.h @@ -0,0 +1,475 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FUNCTION_CALLBACK_H_ +#define INCLUDE_V8_FUNCTION_CALLBACK_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +template +class BasicTracedReference; +template +class Global; +class Object; +class Value; + +namespace internal { +class FunctionCallbackArguments; +class PropertyCallbackArguments; +} // namespace internal + +namespace debug { +class ConsoleCallArguments; +} // namespace debug + +template +class ReturnValue { + public: + template + V8_INLINE ReturnValue(const ReturnValue& that) : value_(that.value_) { + static_assert(std::is_base_of::value, "type check"); + } + // Local setters + template + V8_INLINE void Set(const Global& handle); + template + V8_INLINE void Set(const BasicTracedReference& handle); + template + V8_INLINE void Set(const Local handle); + // Fast primitive setters + V8_INLINE void Set(bool value); + V8_INLINE void Set(double i); + V8_INLINE void Set(int32_t i); + V8_INLINE void Set(uint32_t i); + // Fast JS primitive setters + V8_INLINE void SetNull(); + V8_INLINE void SetUndefined(); + V8_INLINE void SetEmptyString(); + // Convenience getter for Isolate + V8_INLINE Isolate* GetIsolate() const; + + // Pointer setter: Uncompilable to prevent inadvertent misuse. + template + V8_INLINE void Set(S* whatever); + + // Getter. Creates a new Local<> so it comes with a certain performance + // hit. If the ReturnValue was not yet set, this will return the undefined + // value. + V8_INLINE Local Get() const; + + private: + template + friend class ReturnValue; + template + friend class FunctionCallbackInfo; + template + friend class PropertyCallbackInfo; + template + friend class PersistentValueMapBase; + V8_INLINE void SetInternal(internal::Address value) { *value_ = value; } + V8_INLINE internal::Address GetDefaultValue(); + V8_INLINE explicit ReturnValue(internal::Address* slot); + internal::Address* value_; +}; + +/** + * The argument information given to function call callbacks. This + * class provides access to information about the context of the call, + * including the receiver, the number and values of arguments, and + * the holder of the function. + */ +template +class FunctionCallbackInfo { + public: + /** The number of available arguments. */ + V8_INLINE int Length() const; + /** + * Accessor for the available arguments. Returns `undefined` if the index + * is out of bounds. + */ + V8_INLINE Local operator[](int i) const; + /** Returns the receiver. This corresponds to the "this" value. */ + V8_INLINE Local This() const; + /** + * If the callback was created without a Signature, this is the same + * value as This(). If there is a signature, and the signature didn't match + * This() but one of its hidden prototypes, this will be the respective + * hidden prototype. + * + * Note that this is not the prototype of This() on which the accessor + * referencing this callback was found (which in V8 internally is often + * referred to as holder [sic]). + */ + V8_INLINE Local Holder() const; + /** For construct calls, this returns the "new.target" value. */ + V8_INLINE Local NewTarget() const; + /** Indicates whether this is a regular call or a construct call. */ + V8_INLINE bool IsConstructCall() const; + /** The data argument specified when creating the callback. */ + V8_INLINE Local Data() const; + /** The current Isolate. */ + V8_INLINE Isolate* GetIsolate() const; + /** The ReturnValue for the call. */ + V8_INLINE ReturnValue GetReturnValue() const; + // This shouldn't be public, but the arm compiler needs it. + static const int kArgsLength = 6; + + protected: + friend class internal::FunctionCallbackArguments; + friend class internal::CustomArguments; + friend class debug::ConsoleCallArguments; + static const int kHolderIndex = 0; + static const int kIsolateIndex = 1; + static const int kReturnValueDefaultValueIndex = 2; + static const int kReturnValueIndex = 3; + static const int kDataIndex = 4; + static const int kNewTargetIndex = 5; + + V8_INLINE FunctionCallbackInfo(internal::Address* implicit_args, + internal::Address* values, int length); + internal::Address* implicit_args_; + internal::Address* values_; + int length_; +}; + +/** + * The information passed to a property callback about the context + * of the property access. + */ +template +class PropertyCallbackInfo { + public: + /** + * \return The isolate of the property access. + */ + V8_INLINE Isolate* GetIsolate() const; + + /** + * \return The data set in the configuration, i.e., in + * `NamedPropertyHandlerConfiguration` or + * `IndexedPropertyHandlerConfiguration.` + */ + V8_INLINE Local Data() const; + + /** + * \return The receiver. In many cases, this is the object on which the + * property access was intercepted. When using + * `Reflect.get`, `Function.prototype.call`, or similar functions, it is the + * object passed in as receiver or thisArg. + * + * \code + * void GetterCallback(Local name, + * const v8::PropertyCallbackInfo& info) { + * auto context = info.GetIsolate()->GetCurrentContext(); + * + * v8::Local a_this = + * info.This() + * ->GetRealNamedProperty(context, v8_str("a")) + * .ToLocalChecked(); + * v8::Local a_holder = + * info.Holder() + * ->GetRealNamedProperty(context, v8_str("a")) + * .ToLocalChecked(); + * + * CHECK(v8_str("r")->Equals(context, a_this).FromJust()); + * CHECK(v8_str("obj")->Equals(context, a_holder).FromJust()); + * + * info.GetReturnValue().Set(name); + * } + * + * v8::Local templ = + * v8::FunctionTemplate::New(isolate); + * templ->InstanceTemplate()->SetHandler( + * v8::NamedPropertyHandlerConfiguration(GetterCallback)); + * LocalContext env; + * env->Global() + * ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) + * .ToLocalChecked() + * ->NewInstance(env.local()) + * .ToLocalChecked()) + * .FromJust(); + * + * CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)"); + * \endcode + */ + V8_INLINE Local This() const; + + /** + * \return The object in the prototype chain of the receiver that has the + * interceptor. Suppose you have `x` and its prototype is `y`, and `y` + * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. + * The Holder() could be a hidden object (the global object, rather + * than the global proxy). + * + * \note For security reasons, do not pass the object back into the runtime. + */ + V8_INLINE Local Holder() const; + + /** + * \return The return value of the callback. + * Can be changed by calling Set(). + * \code + * info.GetReturnValue().Set(...) + * \endcode + * + */ + V8_INLINE ReturnValue GetReturnValue() const; + + /** + * \return True if the intercepted function should throw if an error occurs. + * Usually, `true` corresponds to `'use strict'`. + * + * \note Always `false` when intercepting `Reflect.set()` + * independent of the language mode. + */ + V8_INLINE bool ShouldThrowOnError() const; + + // This shouldn't be public, but the arm compiler needs it. + static const int kArgsLength = 7; + + protected: + friend class MacroAssembler; + friend class internal::PropertyCallbackArguments; + friend class internal::CustomArguments; + static const int kShouldThrowOnErrorIndex = 0; + static const int kHolderIndex = 1; + static const int kIsolateIndex = 2; + static const int kReturnValueDefaultValueIndex = 3; + static const int kReturnValueIndex = 4; + static const int kDataIndex = 5; + static const int kThisIndex = 6; + + V8_INLINE PropertyCallbackInfo(internal::Address* args) : args_(args) {} + internal::Address* args_; +}; + +using FunctionCallback = void (*)(const FunctionCallbackInfo& info); + +// --- Implementation --- + +template +ReturnValue::ReturnValue(internal::Address* slot) : value_(slot) {} + +template +template +void ReturnValue::Set(const Global& handle) { + static_assert(std::is_base_of::value, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(*handle); + } +} + +template +template +void ReturnValue::Set(const BasicTracedReference& handle) { + static_assert(std::is_base_of::value, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(handle.val_); + } +} + +template +template +void ReturnValue::Set(const Local handle) { + static_assert(std::is_void::value || std::is_base_of::value, + "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(*handle); + } +} + +template +void ReturnValue::Set(double i) { + static_assert(std::is_base_of::value, "type check"); + Set(Number::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(int32_t i) { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + if (V8_LIKELY(I::IsValidSmi(i))) { + *value_ = I::IntToSmi(i); + return; + } + Set(Integer::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(uint32_t i) { + static_assert(std::is_base_of::value, "type check"); + // Can't simply use INT32_MAX here for whatever reason. + bool fits_into_int32_t = (i & (1U << 31)) == 0; + if (V8_LIKELY(fits_into_int32_t)) { + Set(static_cast(i)); + return; + } + Set(Integer::NewFromUnsigned(GetIsolate(), i)); +} + +template +void ReturnValue::Set(bool value) { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + int root_index; + if (value) { + root_index = I::kTrueValueRootIndex; + } else { + root_index = I::kFalseValueRootIndex; + } + *value_ = *I::GetRoot(GetIsolate(), root_index); +} + +template +void ReturnValue::SetNull() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kNullValueRootIndex); +} + +template +void ReturnValue::SetUndefined() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); +} + +template +void ReturnValue::SetEmptyString() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex); +} + +template +Isolate* ReturnValue::GetIsolate() const { + // Isolate is always the pointer below the default value on the stack. + return *reinterpret_cast(&value_[-2]); +} + +template +Local ReturnValue::Get() const { + using I = internal::Internals; + if (*value_ == *I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex)) + return Local(*Undefined(GetIsolate())); + return Local::New(GetIsolate(), reinterpret_cast(value_)); +} + +template +template +void ReturnValue::Set(S* whatever) { + static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); +} + +template +internal::Address ReturnValue::GetDefaultValue() { + // Default value is always the pointer below value_ on the stack. + return value_[-1]; +} + +template +FunctionCallbackInfo::FunctionCallbackInfo(internal::Address* implicit_args, + internal::Address* values, + int length) + : implicit_args_(implicit_args), values_(values), length_(length) {} + +template +Local FunctionCallbackInfo::operator[](int i) const { + // values_ points to the first argument (not the receiver). + if (i < 0 || length_ <= i) return Local(*Undefined(GetIsolate())); + return Local(reinterpret_cast(values_ + i)); +} + +template +Local FunctionCallbackInfo::This() const { + // values_ points to the first argument (not the receiver). + return Local(reinterpret_cast(values_ - 1)); +} + +template +Local FunctionCallbackInfo::Holder() const { + return Local( + reinterpret_cast(&implicit_args_[kHolderIndex])); +} + +template +Local FunctionCallbackInfo::NewTarget() const { + return Local( + reinterpret_cast(&implicit_args_[kNewTargetIndex])); +} + +template +Local FunctionCallbackInfo::Data() const { + return Local(reinterpret_cast(&implicit_args_[kDataIndex])); +} + +template +Isolate* FunctionCallbackInfo::GetIsolate() const { + return *reinterpret_cast(&implicit_args_[kIsolateIndex]); +} + +template +ReturnValue FunctionCallbackInfo::GetReturnValue() const { + return ReturnValue(&implicit_args_[kReturnValueIndex]); +} + +template +bool FunctionCallbackInfo::IsConstructCall() const { + return !NewTarget()->IsUndefined(); +} + +template +int FunctionCallbackInfo::Length() const { + return length_; +} + +template +Isolate* PropertyCallbackInfo::GetIsolate() const { + return *reinterpret_cast(&args_[kIsolateIndex]); +} + +template +Local PropertyCallbackInfo::Data() const { + return Local(reinterpret_cast(&args_[kDataIndex])); +} + +template +Local PropertyCallbackInfo::This() const { + return Local(reinterpret_cast(&args_[kThisIndex])); +} + +template +Local PropertyCallbackInfo::Holder() const { + return Local(reinterpret_cast(&args_[kHolderIndex])); +} + +template +ReturnValue PropertyCallbackInfo::GetReturnValue() const { + return ReturnValue(&args_[kReturnValueIndex]); +} + +template +bool PropertyCallbackInfo::ShouldThrowOnError() const { + using I = internal::Internals; + if (args_[kShouldThrowOnErrorIndex] != + I::IntToSmi(I::kInferShouldThrowMode)) { + return args_[kShouldThrowOnErrorIndex] != I::IntToSmi(I::kDontThrow); + } + return v8::internal::ShouldThrowOnError( + reinterpret_cast(GetIsolate())); +} + +} // namespace v8 + +#endif // INCLUDE_V8_FUNCTION_CALLBACK_H_ diff --git a/deps/include/v8-function.h b/deps/include/v8-function.h new file mode 100644 index 000000000..9424a86fd --- /dev/null +++ b/deps/include/v8-function.h @@ -0,0 +1,122 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FUNCTION_H_ +#define INCLUDE_V8_FUNCTION_H_ + +#include +#include + +#include "v8-function-callback.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8-template.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * A JavaScript function object (ECMA-262, 15.3). + */ +class V8_EXPORT Function : public Object { + public: + /** + * Create a function in the current execution context + * for a given FunctionCallback. + */ + static MaybeLocal New( + Local context, FunctionCallback callback, + Local data = Local(), int length = 0, + ConstructorBehavior behavior = ConstructorBehavior::kAllow, + SideEffectType side_effect_type = SideEffectType::kHasSideEffect); + + V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( + Local context, int argc, Local argv[]) const; + + V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( + Local context) const { + return NewInstance(context, 0, nullptr); + } + + /** + * When side effect checks are enabled, passing kHasNoSideEffect allows the + * constructor to be invoked without throwing. Calls made within the + * constructor are still checked. + */ + V8_WARN_UNUSED_RESULT MaybeLocal NewInstanceWithSideEffectType( + Local context, int argc, Local argv[], + SideEffectType side_effect_type = SideEffectType::kHasSideEffect) const; + + V8_WARN_UNUSED_RESULT MaybeLocal Call(Local context, + Local recv, int argc, + Local argv[]); + + void SetName(Local name); + Local GetName() const; + + /** + * Name inferred from variable or property assignment of this function. + * Used to facilitate debugging and profiling of JavaScript code written + * in an OO style, where many functions are anonymous but are assigned + * to object properties. + */ + Local GetInferredName() const; + + /** + * displayName if it is set, otherwise name if it is configured, otherwise + * function name, otherwise inferred name. + */ + Local GetDebugName() const; + + /** + * Returns zero based line number of function body and + * kLineOffsetNotFound if no information available. + */ + int GetScriptLineNumber() const; + /** + * Returns zero based column number of function body and + * kLineOffsetNotFound if no information available. + */ + int GetScriptColumnNumber() const; + + /** + * Returns scriptId. + */ + int ScriptId() const; + + /** + * Returns the original function if this function is bound, else returns + * v8::Undefined. + */ + Local GetBoundFunction() const; + + /** + * Calls builtin Function.prototype.toString on this function. + * This is different from Value::ToString() that may call a user-defined + * toString() function, and different than Object::ObjectProtoToString() which + * always serializes "[object Function]". + */ + V8_WARN_UNUSED_RESULT MaybeLocal FunctionProtoToString( + Local context); + + ScriptOrigin GetScriptOrigin() const; + V8_INLINE static Function* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kLineOffsetNotFound; + + private: + Function(); + static void CheckCast(Value* obj); +}; +} // namespace v8 + +#endif // INCLUDE_V8_FUNCTION_H_ diff --git a/deps/include/v8-initialization.h b/deps/include/v8-initialization.h new file mode 100644 index 000000000..7c9f26b89 --- /dev/null +++ b/deps/include/v8-initialization.h @@ -0,0 +1,282 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_INITIALIZATION_H_ +#define INCLUDE_V8_INITIALIZATION_H_ + +#include +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +// We reserve the V8_* prefix for macros defined in V8 public API and +// assume there are no name conflicts with the embedder's code. + +/** + * The v8 JavaScript engine. + */ +namespace v8 { + +class PageAllocator; +class Platform; +template +class PersistentValueMapBase; + +/** + * EntropySource is used as a callback function when v8 needs a source + * of entropy. + */ +using EntropySource = bool (*)(unsigned char* buffer, size_t length); + +/** + * ReturnAddressLocationResolver is used as a callback function when v8 is + * resolving the location of a return address on the stack. Profilers that + * change the return address on the stack can use this to resolve the stack + * location to wherever the profiler stashed the original return address. + * + * \param return_addr_location A location on stack where a machine + * return address resides. + * \returns Either return_addr_location, or else a pointer to the profiler's + * copy of the original return address. + * + * \note The resolver function must not cause garbage collection. + */ +using ReturnAddressLocationResolver = + uintptr_t (*)(uintptr_t return_addr_location); + +using DcheckErrorCallback = void (*)(const char* file, int line, + const char* message); + +/** + * Container class for static utility functions. + */ +class V8_EXPORT V8 { + public: + /** + * Hand startup data to V8, in case the embedder has chosen to build + * V8 with external startup data. + * + * Note: + * - By default the startup data is linked into the V8 library, in which + * case this function is not meaningful. + * - If this needs to be called, it needs to be called before V8 + * tries to make use of its built-ins. + * - To avoid unnecessary copies of data, V8 will point directly into the + * given data blob, so pretty please keep it around until V8 exit. + * - Compression of the startup blob might be useful, but needs to + * handled entirely on the embedders' side. + * - The call will abort if the data is invalid. + */ + static void SetSnapshotDataBlob(StartupData* startup_blob); + + /** Set the callback to invoke in case of Dcheck failures. */ + static void SetDcheckErrorHandler(DcheckErrorCallback that); + + /** + * Sets V8 flags from a string. + */ + static void SetFlagsFromString(const char* str); + static void SetFlagsFromString(const char* str, size_t length); + + /** + * Sets V8 flags from the command line. + */ + static void SetFlagsFromCommandLine(int* argc, char** argv, + bool remove_flags); + + /** Get the version string. */ + static const char* GetVersion(); + + /** + * Initializes V8. This function needs to be called before the first Isolate + * is created. It always returns true. + */ + V8_INLINE static bool Initialize() { + const int kBuildConfiguration = + (internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) | + (internal::SmiValuesAre31Bits() ? k31BitSmis : 0) | + (internal::HeapSandboxIsEnabled() ? kHeapSandbox : 0) | + (internal::VirtualMemoryCageIsEnabled() ? kVirtualMemoryCage : 0); + return Initialize(kBuildConfiguration); + } + + /** + * Allows the host application to provide a callback which can be used + * as a source of entropy for random number generators. + */ + static void SetEntropySource(EntropySource source); + + /** + * Allows the host application to provide a callback that allows v8 to + * cooperate with a profiler that rewrites return addresses on stack. + */ + static void SetReturnAddressLocationResolver( + ReturnAddressLocationResolver return_address_resolver); + + /** + * Releases any resources used by v8 and stops any utility threads + * that may be running. Note that disposing v8 is permanent, it + * cannot be reinitialized. + * + * It should generally not be necessary to dispose v8 before exiting + * a process, this should happen automatically. It is only necessary + * to use if the process needs the resources taken up by v8. + */ + static bool Dispose(); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. Returns true on success. + * + * If V8 was compiled with the ICU data in an external file, the location + * of the data file has to be provided. + */ + static bool InitializeICU(const char* icu_data_file = nullptr); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. If V8 was compiled with + * the ICU data in an external file and when the default location of that + * file should be used, a path to the executable must be provided. + * Returns true on success. + * + * The default is a file called icudtl.dat side-by-side with the executable. + * + * Optionally, the location of the data file can be provided to override the + * default. + */ + static bool InitializeICUDefaultLocation(const char* exec_path, + const char* icu_data_file = nullptr); + + /** + * Initialize the external startup data. The embedder only needs to + * invoke this method when external startup data was enabled in a build. + * + * If V8 was compiled with the startup data in an external file, then + * V8 needs to be given those external files during startup. There are + * three ways to do this: + * - InitializeExternalStartupData(const char*) + * This will look in the given directory for the file "snapshot_blob.bin". + * - InitializeExternalStartupDataFromFile(const char*) + * As above, but will directly use the given file name. + * - Call SetSnapshotDataBlob. + * This will read the blobs from the given data structure and will + * not perform any file IO. + */ + static void InitializeExternalStartupData(const char* directory_path); + static void InitializeExternalStartupDataFromFile(const char* snapshot_blob); + + /** + * Sets the v8::Platform to use. This should be invoked before V8 is + * initialized. + */ + static void InitializePlatform(Platform* platform); + + /** + * Clears all references to the v8::Platform. This should be invoked after + * V8 was disposed. + */ + static void ShutdownPlatform(); + +#ifdef V8_VIRTUAL_MEMORY_CAGE + // + // Virtual Memory Cage related API. + // + // This API is not yet stable and subject to changes in the future. + // + + /** + * Initializes the virtual memory cage for V8. + * + * This must be invoked after the platform was initialized but before V8 is + * initialized. The virtual memory cage is torn down during platform shutdown. + * Returns true on success, false otherwise. + * + * TODO(saelo) Once it is no longer optional to create the virtual memory + * cage when compiling with V8_VIRTUAL_MEMORY_CAGE, the cage initialization + * will likely happen as part of V8::Initialize, at which point this function + * should be removed. + */ + static bool InitializeVirtualMemoryCage(); + + /** + * Provides access to the virtual memory cage page allocator. + * + * This allocator allocates pages inside the virtual memory cage. It can for + * example be used to obtain virtual memory for ArrayBuffer backing stores, + * which must be located inside the cage. + * + * It should be assumed that an attacker can corrupt data inside the cage, + * and so in particular the contents of pages returned by this allocator, + * arbitrarily and concurrently. Due to this, it is recommended to to only + * place pure data buffers in pages obtained through this allocator. + * + * This function must only be called after initializing the virtual memory + * cage and V8. + */ + static PageAllocator* GetVirtualMemoryCagePageAllocator(); + + /** + * Returns the size of the virtual memory cage in bytes. + * + * If the cage has not been initialized, or if the initialization failed, + * this returns zero. + */ + static size_t GetVirtualMemoryCageSizeInBytes(); +#endif + + /** + * Activate trap-based bounds checking for WebAssembly. + * + * \param use_v8_signal_handler Whether V8 should install its own signal + * handler or rely on the embedder's. + */ + static bool EnableWebAssemblyTrapHandler(bool use_v8_signal_handler); + +#if defined(V8_OS_WIN) + /** + * On Win64, by default V8 does not emit unwinding data for jitted code, + * which means the OS cannot walk the stack frames and the system Structured + * Exception Handling (SEH) cannot unwind through V8-generated code: + * https://code.google.com/p/v8/issues/detail?id=3598. + * + * This function allows embedders to register a custom exception handler for + * exceptions in V8-generated code. + */ + static void SetUnhandledExceptionCallback( + UnhandledExceptionCallback unhandled_exception_callback); +#endif + + /** + * Get statistics about the shared memory usage. + */ + static void GetSharedMemoryStatistics(SharedMemoryStatistics* statistics); + + private: + V8(); + + enum BuildConfigurationFeatures { + kPointerCompression = 1 << 0, + k31BitSmis = 1 << 1, + kHeapSandbox = 1 << 2, + kVirtualMemoryCage = 1 << 3, + }; + + /** + * Checks that the embedder build configuration is compatible with + * the V8 binary and if so initializes V8. + */ + static bool Initialize(int build_config); + + friend class Context; + template + friend class PersistentValueMapBase; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_INITIALIZATION_H_ diff --git a/deps/include/v8-inspector.h b/deps/include/v8-inspector.h index a55518e45..74592fdf5 100644 --- a/deps/include/v8-inspector.h +++ b/deps/include/v8-inspector.h @@ -6,12 +6,20 @@ #define V8_V8_INSPECTOR_H_ #include -#include +#include #include -#include -#include "v8.h" // NOLINT(build/include_directory) +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { +class Context; +class Name; +class Object; +class StackTrace; +class Value; +} // namespace v8 namespace v8_inspector { @@ -105,8 +113,9 @@ class V8_EXPORT V8StackTrace { virtual StringView topSourceURL() const = 0; virtual int topLineNumber() const = 0; virtual int topColumnNumber() const = 0; - virtual StringView topScriptId() const = 0; - virtual int topScriptIdAsInteger() const = 0; + virtual int topScriptId() const = 0; + V8_DEPRECATE_SOON("Use V8::StackTrace::topScriptId() instead.") + int topScriptIdAsInteger() const { return topScriptId(); } virtual StringView topFunctionName() const = 0; virtual ~V8StackTrace() = default; @@ -130,6 +139,10 @@ class V8_EXPORT V8InspectorSession { virtual v8::Local get(v8::Local) = 0; virtual ~Inspectable() = default; }; + class V8_EXPORT CommandLineAPIScope { + public: + virtual ~CommandLineAPIScope() = default; + }; virtual void addInspectedObject(std::unique_ptr) = 0; // Dispatching protocol messages. @@ -139,6 +152,9 @@ class V8_EXPORT V8InspectorSession { virtual std::vector> supportedDomains() = 0; + virtual std::unique_ptr + initializeCommandLineAPIScope(int executionContextId) = 0; + // Debugger actions. virtual void schedulePauseOnNextStatement(StringView breakReason, StringView breakDetails) = 0; @@ -162,7 +178,7 @@ class V8_EXPORT V8InspectorSession { v8::Local*, std::unique_ptr* objectGroup) = 0; virtual void releaseObjectGroup(StringView) = 0; - virtual void triggerPreciseCoverageDeltaUpdate(StringView occassion) = 0; + virtual void triggerPreciseCoverageDeltaUpdate(StringView occasion) = 0; }; class V8_EXPORT V8InspectorClient { @@ -186,9 +202,6 @@ class V8_EXPORT V8InspectorClient { v8::Local, v8::Local) { return nullptr; } - virtual bool formatAccessorsAsProperties(v8::Local) { - return false; - } virtual bool isInspectableHeapObject(v8::Local) { return true; } virtual v8::Local ensureDefaultContextInGroup( @@ -293,6 +306,10 @@ class V8_EXPORT V8Inspector { int scriptId) = 0; virtual void exceptionRevoked(v8::Local, unsigned exceptionId, StringView message) = 0; + virtual bool associateExceptionData(v8::Local, + v8::Local exception, + v8::Local key, + v8::Local value) = 0; // Connection. class V8_EXPORT Channel { @@ -311,24 +328,6 @@ class V8_EXPORT V8Inspector { virtual std::unique_ptr createStackTrace( v8::Local) = 0; virtual std::unique_ptr captureStackTrace(bool fullStack) = 0; - - // Performance counters. - class V8_EXPORT Counters : public std::enable_shared_from_this { - public: - explicit Counters(v8::Isolate* isolate); - ~Counters(); - const std::unordered_map& getCountersMap() const { - return m_countersMap; - } - - private: - static int* getCounterPtr(const char* name); - - v8::Isolate* m_isolate; - std::unordered_map m_countersMap; - }; - - virtual std::shared_ptr enableCounters() = 0; }; } // namespace v8_inspector diff --git a/deps/include/v8-internal.h b/deps/include/v8-internal.h index 8abbcfb41..e1aee508b 100644 --- a/deps/include/v8-internal.h +++ b/deps/include/v8-internal.h @@ -15,9 +15,12 @@ namespace v8 { +class Array; class Context; class Data; class Isolate; +template +class Local; namespace internal { @@ -33,6 +36,7 @@ const int kApiSystemPointerSize = sizeof(void*); const int kApiDoubleSize = sizeof(double); const int kApiInt32Size = sizeof(int32_t); const int kApiInt64Size = sizeof(int64_t); +const int kApiSizetSize = sizeof(size_t); // Tag information for HeapObject. const int kHeapObjectTag = 1; @@ -40,6 +44,13 @@ const int kWeakHeapObjectTag = 3; const int kHeapObjectTagSize = 2; const intptr_t kHeapObjectTagMask = (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 +// forwarding pointer. +const int kForwardingTag = 0; +const int kForwardingTagSize = 2; +const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1; + // Tag information for Smi. const int kSmiTag = 0; const int kSmiTagSize = 1; @@ -120,23 +131,26 @@ constexpr bool HeapSandboxIsEnabled() { using ExternalPointer_t = Address; -// If the heap sandbox is enabled, these tag values will be XORed with the +// If the heap sandbox is 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. -enum ExternalPointerTag : Address { - kExternalPointerNullTag = static_cast
(0ULL), - kArrayBufferBackingStoreTag = static_cast
(1ULL << 48), - kTypedArrayExternalPointerTag = static_cast
(2ULL << 48), - kDataViewDataPointerTag = static_cast
(3ULL << 48), - kExternalStringResourceTag = static_cast
(4ULL << 48), - kExternalStringResourceDataTag = static_cast
(5ULL << 48), - kForeignForeignAddressTag = static_cast
(6ULL << 48), - kNativeContextMicrotaskQueueTag = static_cast
(7ULL << 48), - // TODO(v8:10391, saelo): Currently has to be zero so that raw zero values are - // also nullptr - kEmbedderDataSlotPayloadTag = static_cast
(0ULL << 48), +// 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. This construction allows +// performing the type check and removing GC marking bits from the pointer at +// the same time. +enum ExternalPointerTag : uint64_t { + kExternalPointerNullTag = 0x0000000000000000, + kExternalStringResourceTag = 0x00ff000000000000, // 0b000000011111111 + kExternalStringResourceDataTag = 0x017f000000000000, // 0b000000101111111 + kForeignForeignAddressTag = 0x01bf000000000000, // 0b000000110111111 + kNativeContextMicrotaskQueueTag = 0x01df000000000000, // 0b000000111011111 + kEmbedderDataSlotPayloadTag = 0x01ef000000000000, // 0b000000111101111 + kCodeEntryPointTag = 0x01f7000000000000, // 0b000000111110111 }; +constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000; + #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH using PlatformSmiTagging = SmiTagging; #else @@ -171,12 +185,22 @@ V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); // 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 * depend on functions and constants defined here. */ class Internals { +#ifdef V8_MAP_PACKING + V8_INLINE static constexpr internal::Address UnpackMapWord( + internal::Address mapword) { + // TODO(wenyuzhao): Clear header metadata. + return mapword ^ kMapWordXorMask; + } +#endif + public: // These values match non-compiler-dependent values defined within // the implementation of v8. @@ -200,19 +224,30 @@ class Internals { static const int kExternalOneByteRepresentationTag = 0x0a; static const uint32_t kNumIsolateDataSlots = 4; + static const int kStackGuardSize = 7 * kApiSystemPointerSize; + static const int kBuiltinTier0EntryTableSize = 13 * kApiSystemPointerSize; + static const int kBuiltinTier0TableSize = 13 * kApiSystemPointerSize; // IsolateData layout guarantees. - static const int kIsolateEmbedderDataOffset = 0; + static const int kIsolateCageBaseOffset = 0; + static const int kIsolateStackGuardOffset = + kIsolateCageBaseOffset + kApiSystemPointerSize; + static const int kBuiltinTier0EntryTableOffset = + kIsolateStackGuardOffset + kStackGuardSize; + static const int kBuiltinTier0TableOffset = + kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize; + static const int kIsolateEmbedderDataOffset = + kBuiltinTier0TableOffset + kBuiltinTier0TableSize; static const int kIsolateFastCCallCallerFpOffset = - kNumIsolateDataSlots * kApiSystemPointerSize; + kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; static const int kIsolateFastCCallCallerPcOffset = kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; static const int kIsolateFastApiCallTargetOffset = kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; - static const int kIsolateStackGuardOffset = + static const int kIsolateLongTaskStatsCounterOffset = kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; static const int kIsolateRootsOffset = - kIsolateStackGuardOffset + 7 * kApiSystemPointerSize; + kIsolateLongTaskStatsCounterOffset + kApiSizetSize; static const int kExternalPointerTableBufferOffset = 0; static const int kExternalPointerTableLengthOffset = @@ -237,8 +272,9 @@ class Internals { static const int kOddballType = 0x43; static const int kForeignType = 0x46; static const int kJSSpecialApiObjectType = 0x410; - static const int kJSApiObjectType = 0x420; static const int kJSObjectType = 0x421; + static const int kFirstJSApiObjectType = 0x422; + static const int kLastJSApiObjectType = 0x80A; static const int kUndefinedOddballKind = 5; static const int kNullOddballKind = 3; @@ -253,6 +289,17 @@ class Internals { // incremental GC once the external memory reaches this limit. static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024; +#ifdef V8_MAP_PACKING + static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48; + // The lowest two bits of mapwords are always `0b10` + static const uintptr_t kMapWordSignature = 0b10; + // XORing a (non-compressed) map with this mask ensures that the two + // low-order bits are 0b10. The 0 at the end makes this look like a Smi, + // although real Smis have all lower 32 bits unset. We only rely on these + // values passing as Smis in very few places. + static const int kMapWordXorMask = 0b11; +#endif + V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { #ifdef V8_ENABLE_CHECKS @@ -279,6 +326,9 @@ class Internals { V8_INLINE static int GetInstanceType(const internal::Address obj) { typedef internal::Address A; A map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); +#ifdef V8_MAP_PACKING + map = UnpackMapWord(map); +#endif return ReadRawField(map, kMapInstanceTypeOffset); } @@ -329,6 +379,12 @@ class Internals { return *reinterpret_cast(addr); } + V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateLongTaskStatsCounterOffset; + ++(*reinterpret_cast(addr)); + } + V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) { internal::Address addr = reinterpret_cast(isolate) + kIsolateRootsOffset + @@ -358,8 +414,9 @@ class Internals { internal::Address heap_object_ptr, int offset) { #ifdef V8_COMPRESS_POINTERS uint32_t value = ReadRawField(heap_object_ptr, offset); - internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); - return root + static_cast(static_cast(value)); + internal::Address base = + GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast(static_cast(value)); #else return ReadRawField(heap_object_ptr, offset); #endif @@ -411,23 +468,77 @@ class Internals { #ifdef V8_COMPRESS_POINTERS // See v8:7703 or src/ptr-compr.* for details about pointer compression. - static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32; - static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32; + static constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32; + static constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32; - V8_INLINE static internal::Address GetRootFromOnHeapAddress( + V8_INLINE static internal::Address GetPtrComprCageBaseFromOnHeapAddress( internal::Address addr) { - return addr & -static_cast(kPtrComprIsolateRootAlignment); + return addr & -static_cast(kPtrComprCageBaseAlignment); } V8_INLINE static internal::Address DecompressTaggedAnyField( internal::Address heap_object_ptr, uint32_t value) { - internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); - return root + static_cast(static_cast(value)); + internal::Address base = + GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast(static_cast(value)); } #endif // V8_COMPRESS_POINTERS }; +constexpr bool VirtualMemoryCageIsEnabled() { +#ifdef V8_VIRTUAL_MEMORY_CAGE + return true; +#else + return false; +#endif +} + +#ifdef V8_VIRTUAL_MEMORY_CAGE +// Size of the virtual memory cage, excluding the guard regions surrounding it. +constexpr size_t kVirtualMemoryCageSize = size_t{1} << 40; // 1 TB + +static_assert(kVirtualMemoryCageSize > Internals::kPtrComprCageReservationSize, + "The virtual memory cage must be larger than the pointer " + "compression cage contained within it."); + +// Required alignment of the virtual memory cage. For simplicity, we require the +// size of the guard regions to be a multiple of this, so that this specifies +// the alignment of the cage including and excluding surrounding guard regions. +// The alignment requirement is due to the pointer compression cage being +// located at the start of the virtual memory cage. +constexpr size_t kVirtualMemoryCageAlignment = + Internals::kPtrComprCageBaseAlignment; + +// Size of the guard regions surrounding the virtual memory cage. This assumes a +// worst-case scenario of a 32-bit unsigned index being used to access an array +// of 64-bit values. +constexpr size_t kVirtualMemoryCageGuardRegionSize = size_t{32} << 30; // 32 GB + +static_assert((kVirtualMemoryCageGuardRegionSize % + kVirtualMemoryCageAlignment) == 0, + "The size of the virtual memory cage guard region must be a " + "multiple of its required alignment."); + +// Minimum size of the virtual memory cage, excluding the guard regions +// surrounding it. If the cage reservation 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 cage. +constexpr size_t kVirtualMemoryCageMinimumSize = size_t{32} << 30; // 32 GB + +// For now, even if the virtual memory cage is enabled, we still allow backing +// stores to be allocated outside of it as fallback. This will simplify the +// initial rollout. However, if the heap sandbox is also enabled, we already use +// the "enforcing mode" of the virtual memory cage. This is useful for testing. +#ifdef V8_HEAP_SANDBOX +constexpr bool kAllowBackingStoresOutsideCage = false; +#else +constexpr bool kAllowBackingStoresOutsideCage = true; +#endif // V8_HEAP_SANDBOX + +#endif // V8_VIRTUAL_MEMORY_CAGE + // Only perform cast check for types derived from v8::Data since // other types do not implement the Cast method. template @@ -457,6 +568,7 @@ V8_INLINE void PerformCastCheck(T* data) { class BackingStoreBase {}; } // namespace internal + } // namespace v8 #endif // INCLUDE_V8_INTERNAL_H_ diff --git a/deps/include/v8-isolate.h b/deps/include/v8-isolate.h new file mode 100644 index 000000000..39276b34a --- /dev/null +++ b/deps/include/v8-isolate.h @@ -0,0 +1,1662 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ISOLATE_H_ +#define INCLUDE_V8_ISOLATE_H_ + +#include +#include + +#include +#include +#include + +#include "cppgc/common.h" +#include "v8-array-buffer.h" // NOLINT(build/include_directory) +#include "v8-callbacks.h" // NOLINT(build/include_directory) +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-debug.h" // NOLINT(build/include_directory) +#include "v8-embedder-heap.h" // NOLINT(build/include_directory) +#include "v8-function-callback.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-microtask.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8-statistics.h" // NOLINT(build/include_directory) +#include "v8-unwinder.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class CppHeap; +class HeapProfiler; +class MicrotaskQueue; +class StartupData; +class ScriptOrModule; +class SharedArrayBuffer; + +namespace internal { +class MicrotaskQueue; +class ThreadLocalTop; +} // namespace internal + +namespace metrics { +class Recorder; +} // namespace metrics + +/** + * A set of constraints that specifies the limits of the runtime's memory use. + * You must set the heap size before initializing the VM - the size cannot be + * adjusted after the VM is initialized. + * + * If you are using threads then you should hold the V8::Locker lock while + * setting the stack limit and you must set a non-default stack limit separately + * for each thread. + * + * The arguments for set_max_semi_space_size, set_max_old_space_size, + * set_max_executable_size, set_code_range_size specify limits in MB. + * + * The argument for set_max_semi_space_size_in_kb is in KB. + */ +class V8_EXPORT ResourceConstraints { + public: + /** + * Configures the constraints with reasonable default values based on the + * provided heap size limit. The heap size includes both the young and + * the old generation. + * + * \param initial_heap_size_in_bytes The initial heap size or zero. + * By default V8 starts with a small heap and dynamically grows it to + * match the set of live objects. This may lead to ineffective + * garbage collections at startup if the live set is large. + * Setting the initial heap size avoids such garbage collections. + * Note that this does not affect young generation garbage collections. + * + * \param maximum_heap_size_in_bytes The hard limit for the heap size. + * When the heap size approaches this limit, V8 will perform series of + * garbage collections and invoke the NearHeapLimitCallback. If the garbage + * collections do not help and the callback does not increase the limit, + * then V8 will crash with V8::FatalProcessOutOfMemory. + */ + void ConfigureDefaultsFromHeapSize(size_t initial_heap_size_in_bytes, + size_t maximum_heap_size_in_bytes); + + /** + * Configures the constraints with reasonable default values based on the + * capabilities of the current device the VM is running on. + * + * \param physical_memory The total amount of physical memory on the current + * device, in bytes. + * \param virtual_memory_limit The amount of virtual memory on the current + * device, in bytes, or zero, if there is no limit. + */ + void ConfigureDefaults(uint64_t physical_memory, + uint64_t virtual_memory_limit); + + /** + * The address beyond which the VM's stack may not grow. + */ + uint32_t* stack_limit() const { return stack_limit_; } + void set_stack_limit(uint32_t* value) { stack_limit_ = value; } + + /** + * The amount of virtual memory reserved for generated code. This is relevant + * for 64-bit architectures that rely on code range for calls in code. + * + * When V8_COMPRESS_POINTERS_IN_SHARED_CAGE is defined, there is a shared + * process-wide code range that is lazily initialized. This value is used to + * configure that shared code range when the first Isolate is + * created. Subsequent Isolates ignore this value. + */ + size_t code_range_size_in_bytes() const { return code_range_size_; } + void set_code_range_size_in_bytes(size_t limit) { code_range_size_ = limit; } + + /** + * The maximum size of the old generation. + * When the old generation approaches this limit, V8 will perform series of + * garbage collections and invoke the NearHeapLimitCallback. + * If the garbage collections do not help and the callback does not + * increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory. + */ + size_t max_old_generation_size_in_bytes() const { + return max_old_generation_size_; + } + void set_max_old_generation_size_in_bytes(size_t limit) { + max_old_generation_size_ = limit; + } + + /** + * The maximum size of the young generation, which consists of two semi-spaces + * and a large object space. This affects frequency of Scavenge garbage + * collections and should be typically much smaller that the old generation. + */ + size_t max_young_generation_size_in_bytes() const { + return max_young_generation_size_; + } + void set_max_young_generation_size_in_bytes(size_t limit) { + max_young_generation_size_ = limit; + } + + size_t initial_old_generation_size_in_bytes() const { + return initial_old_generation_size_; + } + void set_initial_old_generation_size_in_bytes(size_t initial_size) { + initial_old_generation_size_ = initial_size; + } + + size_t initial_young_generation_size_in_bytes() const { + return initial_young_generation_size_; + } + void set_initial_young_generation_size_in_bytes(size_t initial_size) { + initial_young_generation_size_ = initial_size; + } + + private: + static constexpr size_t kMB = 1048576u; + size_t code_range_size_ = 0; + size_t max_old_generation_size_ = 0; + size_t max_young_generation_size_ = 0; + size_t initial_old_generation_size_ = 0; + size_t initial_young_generation_size_ = 0; + uint32_t* stack_limit_ = nullptr; +}; + +/** + * Option flags passed to the SetRAILMode function. + * See documentation https://developers.google.com/web/tools/chrome-devtools/ + * profile/evaluate-performance/rail + */ +enum RAILMode : unsigned { + // Response performance mode: In this mode very low virtual machine latency + // is provided. V8 will try to avoid JavaScript execution interruptions. + // Throughput may be throttled. + PERFORMANCE_RESPONSE, + // Animation performance mode: In this mode low virtual machine latency is + // provided. V8 will try to avoid as many JavaScript execution interruptions + // as possible. Throughput may be throttled. This is the default mode. + PERFORMANCE_ANIMATION, + // Idle performance mode: The embedder is idle. V8 can complete deferred work + // in this mode. + PERFORMANCE_IDLE, + // Load performance mode: In this mode high throughput is provided. V8 may + // turn off latency optimizations. + PERFORMANCE_LOAD +}; + +/** + * Memory pressure level for the MemoryPressureNotification. + * kNone hints V8 that there is no memory pressure. + * kModerate hints V8 to speed up incremental garbage collection at the cost of + * of higher latency due to garbage collection pauses. + * kCritical hints V8 to free memory as soon as possible. Garbage collection + * pauses at this level will be large. + */ +enum class MemoryPressureLevel { kNone, kModerate, kCritical }; + +/** + * Isolate represents an isolated instance of the V8 engine. V8 isolates have + * completely separate states. Objects from one isolate must not be used in + * other isolates. The embedder can create multiple isolates and use them in + * parallel in multiple threads. An isolate can be entered by at most one + * thread at any given time. The Locker/Unlocker API must be used to + * synchronize. + */ +class V8_EXPORT Isolate { + public: + /** + * Initial configuration parameters for a new Isolate. + */ + struct V8_EXPORT CreateParams { + CreateParams(); + ~CreateParams(); + + /** + * Allows the host application to provide the address of a function that is + * notified each time code is added, moved or removed. + */ + JitCodeEventHandler code_event_handler = nullptr; + + /** + * ResourceConstraints to use for the new Isolate. + */ + ResourceConstraints constraints; + + /** + * Explicitly specify a startup snapshot blob. The embedder owns the blob. + */ + StartupData* snapshot_blob = nullptr; + + /** + * Enables the host application to provide a mechanism for recording + * statistics counters. + */ + CounterLookupCallback counter_lookup_callback = nullptr; + + /** + * Enables the host application to provide a mechanism for recording + * histograms. The CreateHistogram function returns a + * histogram which will later be passed to the AddHistogramSample + * function. + */ + CreateHistogramCallback create_histogram_callback = nullptr; + AddHistogramSampleCallback add_histogram_sample_callback = nullptr; + + /** + * The ArrayBuffer::Allocator to use for allocating and freeing the backing + * store of ArrayBuffers. + * + * If the shared_ptr version is used, the Isolate instance and every + * |BackingStore| allocated using this allocator hold a std::shared_ptr + * to the allocator, in order to facilitate lifetime + * management for the allocator instance. + */ + ArrayBuffer::Allocator* array_buffer_allocator = nullptr; + std::shared_ptr array_buffer_allocator_shared; + + /** + * Specifies an optional nullptr-terminated array of raw addresses in the + * embedder that V8 can match against during serialization and use for + * deserialization. This array and its content must stay valid for the + * entire lifetime of the isolate. + */ + const intptr_t* external_references = nullptr; + + /** + * Whether calling Atomics.wait (a function that may block) is allowed in + * this isolate. This can also be configured via SetAllowAtomicsWait. + */ + bool allow_atomics_wait = true; + + /** + * Termination is postponed when there is no active SafeForTerminationScope. + */ + bool only_terminate_in_safe_scope = false; + + /** + * The following parameters describe the offsets for addressing type info + * for wrapped API objects and are used by the fast C API + * (for details see v8-fast-api-calls.h). + */ + int embedder_wrapper_type_index = -1; + int embedder_wrapper_object_index = -1; + }; + + /** + * Stack-allocated class which sets the isolate for all operations + * executed within a local scope. + */ + class V8_EXPORT V8_NODISCARD Scope { + public: + explicit Scope(Isolate* isolate) : isolate_(isolate) { isolate->Enter(); } + + ~Scope() { isolate_->Exit(); } + + // Prevent copying of Scope objects. + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; + + private: + Isolate* const isolate_; + }; + + /** + * Assert that no Javascript code is invoked. + */ + class V8_EXPORT V8_NODISCARD DisallowJavascriptExecutionScope { + public: + enum OnFailure { CRASH_ON_FAILURE, THROW_ON_FAILURE, DUMP_ON_FAILURE }; + + DisallowJavascriptExecutionScope(Isolate* isolate, OnFailure on_failure); + ~DisallowJavascriptExecutionScope(); + + // Prevent copying of Scope objects. + DisallowJavascriptExecutionScope(const DisallowJavascriptExecutionScope&) = + delete; + DisallowJavascriptExecutionScope& operator=( + const DisallowJavascriptExecutionScope&) = delete; + + private: + OnFailure on_failure_; + Isolate* isolate_; + + bool was_execution_allowed_assert_; + bool was_execution_allowed_throws_; + bool was_execution_allowed_dump_; + }; + + /** + * Introduce exception to DisallowJavascriptExecutionScope. + */ + class V8_EXPORT V8_NODISCARD AllowJavascriptExecutionScope { + public: + explicit AllowJavascriptExecutionScope(Isolate* isolate); + ~AllowJavascriptExecutionScope(); + + // Prevent copying of Scope objects. + AllowJavascriptExecutionScope(const AllowJavascriptExecutionScope&) = + delete; + AllowJavascriptExecutionScope& operator=( + const AllowJavascriptExecutionScope&) = delete; + + private: + Isolate* isolate_; + bool was_execution_allowed_assert_; + bool was_execution_allowed_throws_; + bool was_execution_allowed_dump_; + }; + + /** + * Do not run microtasks while this scope is active, even if microtasks are + * automatically executed otherwise. + */ + class V8_EXPORT V8_NODISCARD SuppressMicrotaskExecutionScope { + public: + explicit SuppressMicrotaskExecutionScope( + Isolate* isolate, MicrotaskQueue* microtask_queue = nullptr); + ~SuppressMicrotaskExecutionScope(); + + // Prevent copying of Scope objects. + SuppressMicrotaskExecutionScope(const SuppressMicrotaskExecutionScope&) = + delete; + SuppressMicrotaskExecutionScope& operator=( + const SuppressMicrotaskExecutionScope&) = delete; + + private: + internal::Isolate* const isolate_; + internal::MicrotaskQueue* const microtask_queue_; + internal::Address previous_stack_height_; + + friend class internal::ThreadLocalTop; + }; + + /** + * This scope allows terminations inside direct V8 API calls and forbid them + * inside any recursive API calls without explicit SafeForTerminationScope. + */ + class V8_EXPORT V8_NODISCARD SafeForTerminationScope { + public: + explicit SafeForTerminationScope(v8::Isolate* isolate); + ~SafeForTerminationScope(); + + // Prevent copying of Scope objects. + SafeForTerminationScope(const SafeForTerminationScope&) = delete; + SafeForTerminationScope& operator=(const SafeForTerminationScope&) = delete; + + private: + internal::Isolate* isolate_; + bool prev_value_; + }; + + /** + * Types of garbage collections that can be requested via + * RequestGarbageCollectionForTesting. + */ + enum GarbageCollectionType { + kFullGarbageCollection, + kMinorGarbageCollection + }; + + /** + * Features reported via the SetUseCounterCallback callback. Do not change + * assigned numbers of existing items; add new features to the end of this + * list. + */ + enum UseCounterFeature { + kUseAsm = 0, + kBreakIterator = 1, + kLegacyConst = 2, + kMarkDequeOverflow = 3, + kStoreBufferOverflow = 4, + kSlotsBufferOverflow = 5, + kObjectObserve = 6, + kForcedGC = 7, + kSloppyMode = 8, + kStrictMode = 9, + kStrongMode = 10, + kRegExpPrototypeStickyGetter = 11, + kRegExpPrototypeToString = 12, + kRegExpPrototypeUnicodeGetter = 13, + kIntlV8Parse = 14, + kIntlPattern = 15, + kIntlResolved = 16, + kPromiseChain = 17, + kPromiseAccept = 18, + kPromiseDefer = 19, + kHtmlCommentInExternalScript = 20, + kHtmlComment = 21, + kSloppyModeBlockScopedFunctionRedefinition = 22, + kForInInitializer = 23, + kArrayProtectorDirtied = 24, + kArraySpeciesModified = 25, + kArrayPrototypeConstructorModified = 26, + kArrayInstanceProtoModified = 27, + kArrayInstanceConstructorModified = 28, + kLegacyFunctionDeclaration = 29, + kRegExpPrototypeSourceGetter = 30, // Unused. + kRegExpPrototypeOldFlagGetter = 31, // Unused. + kDecimalWithLeadingZeroInStrictMode = 32, + kLegacyDateParser = 33, + kDefineGetterOrSetterWouldThrow = 34, + kFunctionConstructorReturnedUndefined = 35, + kAssigmentExpressionLHSIsCallInSloppy = 36, + kAssigmentExpressionLHSIsCallInStrict = 37, + kPromiseConstructorReturnedUndefined = 38, + kConstructorNonUndefinedPrimitiveReturn = 39, + kLabeledExpressionStatement = 40, + kLineOrParagraphSeparatorAsLineTerminator = 41, + kIndexAccessor = 42, + kErrorCaptureStackTrace = 43, + kErrorPrepareStackTrace = 44, + kErrorStackTraceLimit = 45, + kWebAssemblyInstantiation = 46, + kDeoptimizerDisableSpeculation = 47, + kArrayPrototypeSortJSArrayModifiedPrototype = 48, + kFunctionTokenOffsetTooLongForToString = 49, + kWasmSharedMemory = 50, + kWasmThreadOpcodes = 51, + kAtomicsNotify = 52, // Unused. + kAtomicsWake = 53, // Unused. + kCollator = 54, + kNumberFormat = 55, + kDateTimeFormat = 56, + kPluralRules = 57, + kRelativeTimeFormat = 58, + kLocale = 59, + kListFormat = 60, + kSegmenter = 61, + kStringLocaleCompare = 62, + kStringToLocaleUpperCase = 63, + kStringToLocaleLowerCase = 64, + kNumberToLocaleString = 65, + kDateToLocaleString = 66, + kDateToLocaleDateString = 67, + kDateToLocaleTimeString = 68, + kAttemptOverrideReadOnlyOnPrototypeSloppy = 69, + kAttemptOverrideReadOnlyOnPrototypeStrict = 70, + kOptimizedFunctionWithOneShotBytecode = 71, // Unused. + kRegExpMatchIsTrueishOnNonJSRegExp = 72, + kRegExpMatchIsFalseishOnJSRegExp = 73, + kDateGetTimezoneOffset = 74, // Unused. + kStringNormalize = 75, + kCallSiteAPIGetFunctionSloppyCall = 76, + kCallSiteAPIGetThisSloppyCall = 77, + kRegExpMatchAllWithNonGlobalRegExp = 78, + kRegExpExecCalledOnSlowRegExp = 79, + kRegExpReplaceCalledOnSlowRegExp = 80, + kDisplayNames = 81, + kSharedArrayBufferConstructed = 82, + kArrayPrototypeHasElements = 83, + kObjectPrototypeHasElements = 84, + kNumberFormatStyleUnit = 85, + kDateTimeFormatRange = 86, + kDateTimeFormatDateTimeStyle = 87, + kBreakIteratorTypeWord = 88, + kBreakIteratorTypeLine = 89, + kInvalidatedArrayBufferDetachingProtector = 90, + kInvalidatedArrayConstructorProtector = 91, + kInvalidatedArrayIteratorLookupChainProtector = 92, + kInvalidatedArraySpeciesLookupChainProtector = 93, + kInvalidatedIsConcatSpreadableLookupChainProtector = 94, + kInvalidatedMapIteratorLookupChainProtector = 95, + kInvalidatedNoElementsProtector = 96, + kInvalidatedPromiseHookProtector = 97, + kInvalidatedPromiseResolveLookupChainProtector = 98, + kInvalidatedPromiseSpeciesLookupChainProtector = 99, + kInvalidatedPromiseThenLookupChainProtector = 100, + kInvalidatedRegExpSpeciesLookupChainProtector = 101, + kInvalidatedSetIteratorLookupChainProtector = 102, + kInvalidatedStringIteratorLookupChainProtector = 103, + kInvalidatedStringLengthOverflowLookupChainProtector = 104, + kInvalidatedTypedArraySpeciesLookupChainProtector = 105, + kWasmSimdOpcodes = 106, + kVarRedeclaredCatchBinding = 107, + kWasmRefTypes = 108, + kWasmBulkMemory = 109, // Unused. + kWasmMultiValue = 110, + kWasmExceptionHandling = 111, + kInvalidatedMegaDOMProtector = 112, + + // 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 + // this list need to be landed first, then changes on the Chromium side. + kUseCounterFeatureCount // This enum value must be last. + }; + + enum MessageErrorLevel { + kMessageLog = (1 << 0), + kMessageDebug = (1 << 1), + kMessageInfo = (1 << 2), + kMessageError = (1 << 3), + kMessageWarning = (1 << 4), + kMessageAll = kMessageLog | kMessageDebug | kMessageInfo | kMessageError | + kMessageWarning, + }; + + using UseCounterCallback = void (*)(Isolate* isolate, + UseCounterFeature feature); + + /** + * Allocates a new isolate but does not initialize it. Does not change the + * currently entered isolate. + * + * Only Isolate::GetData() and Isolate::SetData(), which access the + * embedder-controlled parts of the isolate, are allowed to be called on the + * uninitialized isolate. To initialize the isolate, call + * Isolate::Initialize(). + * + * When an isolate is no longer used its resources should be freed + * by calling Dispose(). Using the delete operator is not allowed. + * + * V8::Initialize() must have run prior to this. + */ + static Isolate* Allocate(); + + /** + * Initialize an Isolate previously allocated by Isolate::Allocate(). + */ + static void Initialize(Isolate* isolate, const CreateParams& params); + + /** + * Creates a new isolate. Does not change the currently entered + * isolate. + * + * When an isolate is no longer used its resources should be freed + * by calling Dispose(). Using the delete operator is not allowed. + * + * V8::Initialize() must have run prior to this. + */ + static Isolate* New(const CreateParams& params); + + /** + * Returns the entered isolate for the current thread or NULL in + * case there is no current isolate. + * + * This method must not be invoked before V8::Initialize() was invoked. + */ + static Isolate* GetCurrent(); + + /** + * Returns the entered isolate for the current thread or NULL in + * case there is no current isolate. + * + * No checks are performed by this method. + */ + static Isolate* TryGetCurrent(); + + /** + * Clears the set of objects held strongly by the heap. This set of + * objects are originally built when a WeakRef is created or + * successfully dereferenced. + * + * This is invoked automatically after microtasks are run. See + * MicrotasksPolicy for when microtasks are run. + * + * This needs to be manually invoked only if the embedder is manually running + * microtasks via a custom MicrotaskQueue class's PerformCheckpoint. In that + * case, it is the embedder's responsibility to make this call at a time which + * does not interrupt synchronous ECMAScript code execution. + */ + void ClearKeptObjects(); + + /** + * Custom callback used by embedders to help V8 determine if it should abort + * when it throws and no internal handler is predicted to catch the + * exception. If --abort-on-uncaught-exception is used on the command line, + * then V8 will abort if either: + * - no custom callback is set. + * - the custom callback set returns true. + * Otherwise, the custom callback will not be called and V8 will not abort. + */ + using AbortOnUncaughtExceptionCallback = bool (*)(Isolate*); + void SetAbortOnUncaughtExceptionCallback( + AbortOnUncaughtExceptionCallback callback); + + /** + * This specifies the callback called by the upcoming dynamic + * import() language feature to load modules. + */ + void SetHostImportModuleDynamicallyCallback( + HostImportModuleDynamicallyWithImportAssertionsCallback callback); + + /** + * This specifies the callback called by the upcoming import.meta + * language feature to retrieve host-defined meta data for a module. + */ + void SetHostInitializeImportMetaObjectCallback( + HostInitializeImportMetaObjectCallback callback); + + /** + * This specifies the callback called when the stack property of Error + * is accessed. + */ + void SetPrepareStackTraceCallback(PrepareStackTraceCallback callback); + + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to guide heuristics. + * It is allowed to call this function from another thread while + * the isolate is executing long running JavaScript code. + */ + void MemoryPressureNotification(MemoryPressureLevel level); + + /** + * Drop non-essential caches. Should only be called from testing code. + * The method can potentially block for a long time and does not necessarily + * trigger GC. + */ + void ClearCachesForTesting(); + + /** + * Methods below this point require holding a lock (using Locker) in + * a multi-threaded environment. + */ + + /** + * Sets this isolate as the entered one for the current thread. + * Saves the previously entered one (if any), so that it can be + * restored when exiting. Re-entering an isolate is allowed. + */ + void Enter(); + + /** + * Exits this isolate by restoring the previously entered one in the + * current thread. The isolate may still stay the same, if it was + * entered more than once. + * + * Requires: this == Isolate::GetCurrent(). + */ + void Exit(); + + /** + * Disposes the isolate. The isolate must not be entered by any + * thread to be disposable. + */ + void Dispose(); + + /** + * Dumps activated low-level V8 internal stats. This can be used instead + * of performing a full isolate disposal. + */ + void DumpAndResetStats(); + + /** + * Discards all V8 thread-specific data for the Isolate. Should be used + * if a thread is terminating and it has used an Isolate that will outlive + * the thread -- all thread-specific data for an Isolate is discarded when + * an Isolate is disposed so this call is pointless if an Isolate is about + * to be Disposed. + */ + void DiscardThreadSpecificMetadata(); + + /** + * Associate embedder-specific data with the isolate. |slot| has to be + * between 0 and GetNumberOfDataSlots() - 1. + */ + V8_INLINE void SetData(uint32_t slot, void* data); + + /** + * Retrieve embedder-specific data from the isolate. + * Returns NULL if SetData has never been called for the given |slot|. + */ + V8_INLINE void* GetData(uint32_t slot); + + /** + * Returns the maximum number of available embedder data slots. Valid slots + * are in the range of 0 - GetNumberOfDataSlots() - 1. + */ + V8_INLINE static uint32_t GetNumberOfDataSlots(); + + /** + * Return data that was previously attached to the isolate snapshot via + * SnapshotCreator, and removes the reference to it. + * Repeated call with the same index returns an empty MaybeLocal. + */ + template + V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); + + /** + * Get statistics about the heap memory usage. + */ + void GetHeapStatistics(HeapStatistics* heap_statistics); + + /** + * Returns the number of spaces in the heap. + */ + size_t NumberOfHeapSpaces(); + + /** + * Get the memory usage of a space in the heap. + * + * \param space_statistics The HeapSpaceStatistics object to fill in + * statistics. + * \param index The index of the space to get statistics from, which ranges + * from 0 to NumberOfHeapSpaces() - 1. + * \returns true on success. + */ + bool GetHeapSpaceStatistics(HeapSpaceStatistics* space_statistics, + size_t index); + + /** + * Returns the number of types of objects tracked in the heap at GC. + */ + size_t NumberOfTrackedHeapObjectTypes(); + + /** + * Get statistics about objects in the heap. + * + * \param object_statistics The HeapObjectStatistics object to fill in + * statistics of objects of given type, which were live in the previous GC. + * \param type_index The index of the type of object to fill details about, + * which ranges from 0 to NumberOfTrackedHeapObjectTypes() - 1. + * \returns true on success. + */ + bool GetHeapObjectStatisticsAtLastGC(HeapObjectStatistics* object_statistics, + size_t type_index); + + /** + * Get statistics about code and its metadata in the heap. + * + * \param object_statistics The HeapCodeStatistics object to fill in + * statistics of code, bytecode and their metadata. + * \returns true on success. + */ + bool GetHeapCodeAndMetadataStatistics(HeapCodeStatistics* object_statistics); + + /** + * This API is experimental and may change significantly. + * + * Enqueues a memory measurement request and invokes the delegate with the + * results. + * + * \param delegate the delegate that defines which contexts to measure and + * reports the results. + * + * \param execution promptness executing the memory measurement. + * The kEager value is expected to be used only in tests. + */ + bool MeasureMemory( + std::unique_ptr delegate, + MeasureMemoryExecution execution = MeasureMemoryExecution::kDefault); + + /** + * Get a call stack sample from the isolate. + * \param state Execution state. + * \param frames Caller allocated buffer to store stack frames. + * \param frames_limit Maximum number of frames to capture. The buffer must + * be large enough to hold the number of frames. + * \param sample_info The sample info is filled up by the function + * provides number of actual captured stack frames and + * the current VM state. + * \note GetStackSample should only be called when the JS thread is paused or + * interrupted. Otherwise the behavior is undefined. + */ + void GetStackSample(const RegisterState& state, void** frames, + size_t frames_limit, SampleInfo* sample_info); + + /** + * Adjusts the amount of registered external memory. Used to give V8 an + * indication of the amount of externally allocated memory that is kept alive + * by JavaScript objects. V8 uses this to decide when to perform global + * garbage collections. Registering externally allocated memory will trigger + * global garbage collections more often than it would otherwise in an attempt + * to garbage collect the JavaScript objects that keep the externally + * allocated memory alive. + * + * \param change_in_bytes the change in externally allocated memory that is + * kept alive by JavaScript objects. + * \returns the adjusted value. + */ + 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. + */ + HeapProfiler* GetHeapProfiler(); + + /** + * Tells the VM whether the embedder is idle or not. + */ + void SetIdle(bool is_idle); + + /** Returns the ArrayBuffer::Allocator used in this isolate. */ + ArrayBuffer::Allocator* GetArrayBufferAllocator(); + + /** Returns true if this isolate has a current context. */ + bool InContext(); + + /** + * Returns the context of the currently running JavaScript, or the context + * on the top of the stack if no JavaScript is running. + */ + Local GetCurrentContext(); + + /** + * Returns either the last context entered through V8's C++ API, or the + * context of the currently running microtask while processing microtasks. + * If a context is entered while executing a microtask, that context is + * returned. + */ + Local GetEnteredOrMicrotaskContext(); + + /** + * Returns the Context that corresponds to the Incumbent realm in HTML spec. + * https://html.spec.whatwg.org/multipage/webappapis.html#incumbent + */ + Local GetIncumbentContext(); + + /** + * Schedules a v8::Exception::Error with the given message. + * See ThrowException for more details. Templatized to provide compile-time + * errors in case of too long strings (see v8::String::NewFromUtf8Literal). + */ + template + Local ThrowError(const char (&message)[N]) { + return ThrowError(String::NewFromUtf8Literal(this, message)); + } + Local ThrowError(Local message); + + /** + * Schedules an exception to be thrown when returning to JavaScript. When an + * exception has been scheduled it is illegal to invoke any JavaScript + * operation; the caller must return immediately and only after the exception + * has been handled does it become legal to invoke JavaScript operations. + */ + Local ThrowException(Local exception); + + using GCCallback = void (*)(Isolate* isolate, GCType type, + GCCallbackFlags flags); + using GCCallbackWithData = void (*)(Isolate* isolate, GCType type, + GCCallbackFlags flags, void* data); + + /** + * Enables the host application to receive a notification before a + * garbage collection. Allocations are allowed in the callback function, + * but the callback is not re-entrant: if the allocation inside it will + * trigger the garbage collection, the callback won't be called again. + * It is possible to specify the GCType filter for your callback. But it is + * not possible to register the same callback function two times with + * different GCType filters. + */ + void AddGCPrologueCallback(GCCallbackWithData callback, void* data = nullptr, + GCType gc_type_filter = kGCTypeAll); + void AddGCPrologueCallback(GCCallback callback, + GCType gc_type_filter = kGCTypeAll); + + /** + * This function removes callback which was installed by + * AddGCPrologueCallback function. + */ + void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr); + void RemoveGCPrologueCallback(GCCallback callback); + + /** + * Sets the embedder heap tracer for the isolate. + */ + void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer); + + /* + * Gets the currently active heap tracer for the isolate. + */ + 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()`. + * + * V8 does not take ownership of the handler. + */ + void SetEmbedderRootsHandler(EmbedderRootsHandler* handler); + + /** + * 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. + * + * This is an experimental feature and may still change significantly. + */ + 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()`. + */ + CppHeap* GetCppHeap() const; + + /** + * Use for |AtomicsWaitCallback| to indicate the type of event it receives. + */ + enum class AtomicsWaitEvent { + /** Indicates that this call is happening before waiting. */ + kStartWait, + /** `Atomics.wait()` finished because of an `Atomics.wake()` call. */ + kWokenUp, + /** `Atomics.wait()` finished because it timed out. */ + kTimedOut, + /** `Atomics.wait()` was interrupted through |TerminateExecution()|. */ + kTerminatedExecution, + /** `Atomics.wait()` was stopped through |AtomicsWaitWakeHandle|. */ + kAPIStopped, + /** `Atomics.wait()` did not wait, as the initial condition was not met. */ + kNotEqual + }; + + /** + * Passed to |AtomicsWaitCallback| as a means of stopping an ongoing + * `Atomics.wait` call. + */ + class V8_EXPORT AtomicsWaitWakeHandle { + public: + /** + * Stop this `Atomics.wait()` call and call the |AtomicsWaitCallback| + * with |kAPIStopped|. + * + * This function may be called from another thread. The caller has to ensure + * through proper synchronization that it is not called after + * the finishing |AtomicsWaitCallback|. + * + * Note that the ECMAScript specification does not plan for the possibility + * of wakeups that are neither coming from a timeout or an `Atomics.wake()` + * call, so this may invalidate assumptions made by existing code. + * The embedder may accordingly wish to schedule an exception in the + * finishing |AtomicsWaitCallback|. + */ + void Wake(); + }; + + /** + * Embedder callback for `Atomics.wait()` that can be added through + * |SetAtomicsWaitCallback|. + * + * This will be called just before starting to wait with the |event| value + * |kStartWait| and after finishing waiting with one of the other + * values of |AtomicsWaitEvent| inside of an `Atomics.wait()` call. + * + * |array_buffer| will refer to the underlying SharedArrayBuffer, + * |offset_in_bytes| to the location of the waited-on memory address inside + * the SharedArrayBuffer. + * + * |value| and |timeout_in_ms| will be the values passed to + * the `Atomics.wait()` call. If no timeout was used, |timeout_in_ms| + * will be `INFINITY`. + * + * In the |kStartWait| callback, |stop_handle| will be an object that + * is only valid until the corresponding finishing callback and that + * can be used to stop the wait process while it is happening. + * + * This callback may schedule exceptions, *unless* |event| is equal to + * |kTerminatedExecution|. + */ + using AtomicsWaitCallback = void (*)(AtomicsWaitEvent event, + Local array_buffer, + size_t offset_in_bytes, int64_t value, + double timeout_in_ms, + AtomicsWaitWakeHandle* stop_handle, + void* data); + + /** + * Set a new |AtomicsWaitCallback|. This overrides an earlier + * |AtomicsWaitCallback|, if there was any. If |callback| is nullptr, + * this unsets the callback. |data| will be passed to the callback + * as its last parameter. + */ + void SetAtomicsWaitCallback(AtomicsWaitCallback callback, void* data); + + /** + * Enables the host application to receive a notification after a + * garbage collection. Allocations are allowed in the callback function, + * but the callback is not re-entrant: if the allocation inside it will + * trigger the garbage collection, the callback won't be called again. + * It is possible to specify the GCType filter for your callback. But it is + * not possible to register the same callback function two times with + * different GCType filters. + */ + void AddGCEpilogueCallback(GCCallbackWithData callback, void* data = nullptr, + GCType gc_type_filter = kGCTypeAll); + void AddGCEpilogueCallback(GCCallback callback, + GCType gc_type_filter = kGCTypeAll); + + /** + * This function removes callback which was installed by + * AddGCEpilogueCallback function. + */ + void RemoveGCEpilogueCallback(GCCallbackWithData callback, + void* data = nullptr); + void RemoveGCEpilogueCallback(GCCallback callback); + + using GetExternallyAllocatedMemoryInBytesCallback = size_t (*)(); + + /** + * Set the callback that tells V8 how much memory is currently allocated + * externally of the V8 heap. Ideally this memory is somehow connected to V8 + * objects and may get freed-up when the corresponding V8 objects get + * collected by a V8 garbage collection. + */ + void SetGetExternallyAllocatedMemoryInBytesCallback( + GetExternallyAllocatedMemoryInBytesCallback callback); + + /** + * Forcefully terminate the current thread of JavaScript execution + * in the given isolate. + * + * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + void TerminateExecution(); + + /** + * Is V8 terminating JavaScript execution. + * + * Returns true if JavaScript execution is currently terminating + * because of a call to TerminateExecution. In that case there are + * still JavaScript frames on the stack and the termination + * exception is still active. + */ + bool IsExecutionTerminating(); + + /** + * Resume execution capability in the given isolate, whose execution + * was previously forcefully terminated using TerminateExecution(). + * + * When execution is forcefully terminated using TerminateExecution(), + * the isolate can not resume execution until all JavaScript frames + * have propagated the uncatchable exception which is generated. This + * method allows the program embedding the engine to handle the + * termination event and resume execution capability, even if + * JavaScript frames remain on the stack. + * + * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + void CancelTerminateExecution(); + + /** + * Request V8 to interrupt long running JavaScript code and invoke + * the given |callback| passing the given |data| to it. After |callback| + * returns control will be returned to the JavaScript code. + * There may be a number of interrupt requests in flight. + * Can be called from another thread without acquiring a |Locker|. + * Registered |callback| must not reenter interrupted Isolate. + */ + void RequestInterrupt(InterruptCallback callback, void* data); + + /** + * Returns true if there is ongoing background work within V8 that will + * eventually post a foreground task, like asynchronous WebAssembly + * compilation. + */ + bool HasPendingBackgroundTasks(); + + /** + * Request garbage collection in this Isolate. It is only valid to call this + * function if --expose_gc was specified. + * + * This should only be used for testing purposes and not to enforce a garbage + * collection schedule. It has strong negative impact on the garbage + * collection performance. Use IdleNotificationDeadline() or + * LowMemoryNotification() instead to influence the garbage collection + * schedule. + */ + void RequestGarbageCollectionForTesting(GarbageCollectionType type); + + /** + * Set the callback to invoke for logging event. + */ + void SetEventLogger(LogEventCallback that); + + /** + * Adds a callback to notify the host application right before a script + * is about to run. If a script re-enters the runtime during executing, the + * BeforeCallEnteredCallback is invoked for each re-entrance. + * Executing scripts inside the callback will re-trigger the callback. + */ + void AddBeforeCallEnteredCallback(BeforeCallEnteredCallback callback); + + /** + * Removes callback that was installed by AddBeforeCallEnteredCallback. + */ + void RemoveBeforeCallEnteredCallback(BeforeCallEnteredCallback callback); + + /** + * Adds a callback to notify the host application when a script finished + * running. If a script re-enters the runtime during executing, the + * CallCompletedCallback is only invoked when the outer-most script + * execution ends. Executing scripts inside the callback do not trigger + * further callbacks. + */ + void AddCallCompletedCallback(CallCompletedCallback callback); + + /** + * Removes callback that was installed by AddCallCompletedCallback. + */ + void RemoveCallCompletedCallback(CallCompletedCallback callback); + + /** + * Set the PromiseHook callback for various promise lifecycle + * events. + */ + void SetPromiseHook(PromiseHook hook); + + /** + * Set callback to notify about promise reject with no handler, or + * revocation of such a previous notification once the handler is added. + */ + void SetPromiseRejectCallback(PromiseRejectCallback callback); + + /** + * Runs the default MicrotaskQueue until it gets empty and perform other + * microtask checkpoint steps, such as calling ClearKeptObjects. Asserts that + * the MicrotasksPolicy is not kScoped. Any exceptions thrown by microtask + * callbacks are swallowed. + */ + void PerformMicrotaskCheckpoint(); + + /** + * Enqueues the callback to the default MicrotaskQueue + */ + void EnqueueMicrotask(Local microtask); + + /** + * Enqueues the callback to the default MicrotaskQueue + */ + void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr); + + /** + * Controls how Microtasks are invoked. See MicrotasksPolicy for details. + */ + void SetMicrotasksPolicy(MicrotasksPolicy policy); + + /** + * Returns the policy controlling how Microtasks are invoked. + */ + MicrotasksPolicy GetMicrotasksPolicy() const; + + /** + * Adds a callback to notify the host application after + * microtasks were run on the default MicrotaskQueue. The callback is + * triggered by explicit RunMicrotasks call or automatic microtasks execution + * (see SetMicrotaskPolicy). + * + * Callback will trigger even if microtasks were attempted to run, + * but the microtasks queue was empty and no single microtask was actually + * executed. + * + * Executing scripts inside the callback will not re-trigger microtasks and + * the callback. + */ + void AddMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr); + + /** + * Removes callback that was installed by AddMicrotasksCompletedCallback. + */ + void RemoveMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr); + + /** + * Sets a callback for counting the number of times a feature of V8 is used. + */ + void SetUseCounterCallback(UseCounterCallback callback); + + /** + * Enables the host application to provide a mechanism for recording + * statistics counters. + */ + void SetCounterFunction(CounterLookupCallback); + + /** + * Enables the host application to provide a mechanism for recording + * histograms. The CreateHistogram function returns a + * histogram which will later be passed to the AddHistogramSample + * function. + */ + void SetCreateHistogramFunction(CreateHistogramCallback); + void SetAddHistogramSampleFunction(AddHistogramSampleCallback); + + /** + * Enables the host application to provide a mechanism for recording + * event based metrics. In order to use this interface + * include/v8-metrics.h + * needs to be included and the recorder needs to be derived from the + * Recorder base class defined there. + * This method can only be called once per isolate and must happen during + * isolate initialization before background threads are spawned. + */ + void SetMetricsRecorder( + const std::shared_ptr& metrics_recorder); + + /** + * Enables the host application to provide a mechanism for recording a + * predefined set of data as crash keys to be used in postmortem debugging in + * case of a crash. + */ + void SetAddCrashKeyCallback(AddCrashKeyCallback); + + /** + * Optional notification that the embedder is idle. + * V8 uses the notification to perform garbage collection. + * This call can be used repeatedly if the embedder remains idle. + * Returns true if the embedder should stop calling IdleNotificationDeadline + * until real work has been done. This indicates that V8 has done + * as much cleanup as it will be able to do. + * + * The deadline_in_seconds argument specifies the deadline V8 has to finish + * garbage collection work. deadline_in_seconds is compared with + * MonotonicallyIncreasingTime() and should be based on the same timebase as + * that function. There is no guarantee that the actual work will be done + * within the time limit. + */ + bool IdleNotificationDeadline(double deadline_in_seconds); + + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to attempt to free memory. + */ + void LowMemoryNotification(); + + /** + * Optional notification that a context has been disposed. V8 uses these + * notifications to guide the GC heuristic and cancel FinalizationRegistry + * cleanup tasks. Returns the number of context disposals - including this one + * - since the last time V8 had a chance to clean up. + * + * The optional parameter |dependant_context| specifies whether the disposed + * context was depending on state from other contexts or not. + */ + int ContextDisposedNotification(bool dependant_context = true); + + /** + * Optional notification that the isolate switched to the foreground. + * V8 uses these notifications to guide heuristics. + */ + void IsolateInForegroundNotification(); + + /** + * Optional notification that the isolate switched to the background. + * V8 uses these notifications to guide heuristics. + */ + void IsolateInBackgroundNotification(); + + /** + * Optional notification which will enable the memory savings mode. + * V8 uses this notification to guide heuristics which may result in a + * smaller memory footprint at the cost of reduced runtime performance. + */ + void EnableMemorySavingsMode(); + + /** + * Optional notification which will disable the memory savings mode. + */ + void DisableMemorySavingsMode(); + + /** + * Optional notification to tell V8 the current performance requirements + * of the embedder based on RAIL. + * V8 uses these notifications to guide heuristics. + * This is an unfinished experimental feature. Semantics and implementation + * may change frequently. + */ + void SetRAILMode(RAILMode rail_mode); + + /** + * Update load start time of the RAIL mode + */ + void UpdateLoadStartTime(); + + /** + * Optional notification to tell V8 the current isolate is used for debugging + * and requires higher heap limit. + */ + void IncreaseHeapLimitForDebugging(); + + /** + * Restores the original heap limit after IncreaseHeapLimitForDebugging(). + */ + void RestoreOriginalHeapLimit(); + + /** + * Returns true if the heap limit was increased for debugging and the + * original heap limit was not restored yet. + */ + bool IsHeapLimitIncreasedForDebugging(); + + /** + * Allows the host application to provide the address of a function that is + * notified each time code is added, moved or removed. + * + * \param options options for the JIT code event handler. + * \param event_handler the JIT code event handler, which will be invoked + * each time code is added, moved or removed. + * \note \p event_handler won't get notified of existent code. + * \note since code removal notifications are not currently issued, the + * \p event_handler may get notifications of code that overlaps earlier + * code notifications. This happens when code areas are reused, and the + * earlier overlapping code areas should therefore be discarded. + * \note the events passed to \p event_handler and the strings they point to + * are not guaranteed to live past each call. The \p event_handler must + * copy strings and other parameters it needs to keep around. + * \note the set of events declared in JitCodeEvent::EventType is expected to + * grow over time, and the JitCodeEvent structure is expected to accrue + * new members. The \p event_handler function must ignore event codes + * it does not recognize to maintain future compatibility. + * \note Use Isolate::CreateParams to get events for code executed during + * Isolate setup. + */ + void SetJitCodeEventHandler(JitCodeEventOptions options, + JitCodeEventHandler event_handler); + + /** + * Modifies the stack limit for this Isolate. + * + * \param stack_limit An address beyond which the Vm's stack may not grow. + * + * \note If you are using threads then you should hold the V8::Locker lock + * while setting the stack limit and you must set a non-default stack + * limit separately for each thread. + */ + void SetStackLimit(uintptr_t stack_limit); + + /** + * Returns a memory range that can potentially contain jitted code. Code for + * V8's 'builtins' will not be in this range if embedded builtins is enabled. + * + * On Win64, embedders are advised to install function table callbacks for + * these ranges, as default SEH won't be able to unwind through jitted code. + * The first page of the code range is reserved for the embedder and is + * committed, writable, and executable, to be used to store unwind data, as + * documented in + * https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64. + * + * Might be empty on other platforms. + * + * https://code.google.com/p/v8/issues/detail?id=3598 + */ + void GetCodeRange(void** start, size_t* length_in_bytes); + + /** + * As GetCodeRange, but for embedded builtins (these live in a distinct + * memory region from other V8 Code objects). + */ + void GetEmbeddedCodeRange(const void** start, size_t* length_in_bytes); + + /** + * Returns the JSEntryStubs necessary for use with the Unwinder API. + */ + JSEntryStubs GetJSEntryStubs(); + + static constexpr size_t kMinCodePagesBufferSize = 32; + + /** + * Copies the code heap pages currently in use by V8 into |code_pages_out|. + * |code_pages_out| must have at least kMinCodePagesBufferSize capacity and + * must be empty. + * + * Signal-safe, does not allocate, does not access the V8 heap. + * No code on the stack can rely on pages that might be missing. + * + * Returns the number of pages available to be copied, which might be greater + * than |capacity|. In this case, only |capacity| pages will be copied into + * |code_pages_out|. The caller should provide a bigger buffer on the next + * call in order to get all available code pages, but this is not required. + */ + size_t CopyCodePages(size_t capacity, MemoryRange* code_pages_out); + + /** Set the callback to invoke in case of fatal errors. */ + void SetFatalErrorHandler(FatalErrorCallback that); + + /** Set the callback to invoke in case of OOM errors. */ + void SetOOMErrorHandler(OOMErrorCallback that); + + /** + * Add a callback to invoke in case the heap size is close to the heap limit. + * If multiple callbacks are added, only the most recently added callback is + * invoked. + */ + void AddNearHeapLimitCallback(NearHeapLimitCallback callback, void* data); + + /** + * Remove the given callback and restore the heap limit to the + * given limit. If the given limit is zero, then it is ignored. + * If the current heap size is greater than the given limit, + * then the heap limit is restored to the minimal limit that + * is possible for the current heap size. + */ + void RemoveNearHeapLimitCallback(NearHeapLimitCallback callback, + size_t heap_limit); + + /** + * If the heap limit was changed by the NearHeapLimitCallback, then the + * initial heap limit will be restored once the heap size falls below the + * given threshold percentage of the initial heap limit. + * The threshold percentage is a number in (0.0, 1.0) range. + */ + void AutomaticallyRestoreInitialHeapLimit(double threshold_percent = 0.5); + + /** + * Set the callback to invoke to check if code generation from + * strings should be allowed. + */ + void SetModifyCodeGenerationFromStringsCallback( + ModifyCodeGenerationFromStringsCallback2 callback); + + /** + * Set the callback to invoke to check if wasm code generation should + * be allowed. + */ + void SetAllowWasmCodeGenerationCallback( + AllowWasmCodeGenerationCallback callback); + + /** + * Embedder over{ride|load} injection points for wasm APIs. The expectation + * is that the embedder sets them at most once. + */ + void SetWasmModuleCallback(ExtensionCallback callback); + void SetWasmInstanceCallback(ExtensionCallback callback); + + void SetWasmStreamingCallback(WasmStreamingCallback callback); + + void SetWasmLoadSourceMapCallback(WasmLoadSourceMapCallback callback); + + void SetWasmSimdEnabledCallback(WasmSimdEnabledCallback callback); + + void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback); + + void SetWasmDynamicTieringEnabledCallback( + WasmDynamicTieringEnabledCallback callback); + + void SetSharedArrayBufferConstructorEnabledCallback( + SharedArrayBufferConstructorEnabledCallback callback); + + /** + * This function can be called by the embedder to signal V8 that the dynamic + * enabling of features has finished. V8 can now set up dynamically added + * features. + */ + void InstallConditionalFeatures(Local context); + + /** + * Check if V8 is dead and therefore unusable. This is the case after + * fatal errors such as out-of-memory situations. + */ + bool IsDead(); + + /** + * Adds a message listener (errors only). + * + * The same message listener can be added more than once and in that + * case it will be called more than once for each message. + * + * If data is specified, it will be passed to the callback when it is called. + * Otherwise, the exception object will be passed to the callback instead. + */ + bool AddMessageListener(MessageCallback that, + Local data = Local()); + + /** + * Adds a message listener. + * + * The same message listener can be added more than once and in that + * case it will be called more than once for each message. + * + * If data is specified, it will be passed to the callback when it is called. + * Otherwise, the exception object will be passed to the callback instead. + * + * A listener can listen for particular error levels by providing a mask. + */ + bool AddMessageListenerWithErrorLevel(MessageCallback that, + int message_levels, + Local data = Local()); + + /** + * Remove all message listeners from the specified callback function. + */ + void RemoveMessageListeners(MessageCallback that); + + /** Callback function for reporting failed access checks.*/ + void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback); + + /** + * Tells V8 to capture current stack trace when uncaught exception occurs + * and report it to the message listeners. The option is off by default. + */ + void SetCaptureStackTraceForUncaughtExceptions( + bool capture, int frame_limit = 10, + StackTrace::StackTraceOptions options = StackTrace::kOverview); + + /** + * Iterates through all external resources referenced from current isolate + * heap. GC is not invoked prior to iterating, therefore there is no + * guarantee that visited objects are still alive. + */ + 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. + */ + bool IsInUse(); + + /** + * Set whether calling Atomics.wait (a function that may block) is allowed in + * this isolate. This can also be configured via + * CreateParams::allow_atomics_wait. + */ + void SetAllowAtomicsWait(bool allow); + + /** + * Time zone redetection indicator for + * DateTimeConfigurationChangeNotification. + * + * kSkip indicates V8 that the notification should not trigger redetecting + * host time zone. kRedetect indicates V8 that host time zone should be + * redetected, and used to set the default time zone. + * + * The host time zone detection may require file system access or similar + * operations unlikely to be available inside a sandbox. If v8 is run inside a + * sandbox, the host time zone has to be detected outside the sandbox before + * calling DateTimeConfigurationChangeNotification function. + */ + enum class TimeZoneDetection { kSkip, kRedetect }; + + /** + * Notification that the embedder has changed the time zone, daylight savings + * time or other date / time configuration parameters. V8 keeps a cache of + * various values used for date / time computation. This notification will + * reset those cached values for the current context so that date / time + * configuration changes would be reflected. + * + * This API should not be called more than needed as it will negatively impact + * the performance of date operations. + */ + void DateTimeConfigurationChangeNotification( + TimeZoneDetection time_zone_detection = TimeZoneDetection::kSkip); + + /** + * Notification that the embedder has changed the locale. V8 keeps a cache of + * various values used for locale computation. This notification will reset + * those cached values for the current context so that locale configuration + * changes would be reflected. + * + * This API should not be called more than needed as it will negatively impact + * the performance of locale operations. + */ + void LocaleConfigurationChangeNotification(); + + Isolate() = delete; + ~Isolate() = delete; + Isolate(const Isolate&) = delete; + Isolate& operator=(const Isolate&) = delete; + // Deleting operator new and delete here is allowed as ctor and dtor is also + // deleted. + void* operator new(size_t size) = delete; + void* operator new[](size_t size) = delete; + void operator delete(void*, size_t) = delete; + void operator delete[](void*, size_t) = delete; + + private: + template + friend class PersistentValueMapBase; + + internal::Address* GetDataFromSnapshotOnce(size_t index); + void ReportExternalAllocationLimitReached(); +}; + +void Isolate::SetData(uint32_t slot, void* data) { + using I = internal::Internals; + I::SetEmbedderData(this, slot, data); +} + +void* Isolate::GetData(uint32_t slot) { + using I = internal::Internals; + return I::GetEmbedderData(this, slot); +} + +uint32_t Isolate::GetNumberOfDataSlots() { + using I = internal::Internals; + return I::kNumIsolateDataSlots; +} + +template +MaybeLocal Isolate::GetDataFromSnapshotOnce(size_t index) { + T* data = reinterpret_cast(GetDataFromSnapshotOnce(index)); + if (data) internal::PerformCastCheck(data); + return Local(data); +} + +} // namespace v8 + +#endif // INCLUDE_V8_ISOLATE_H_ diff --git a/deps/include/v8-json.h b/deps/include/v8-json.h new file mode 100644 index 000000000..23d918fc9 --- /dev/null +++ b/deps/include/v8-json.h @@ -0,0 +1,47 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_JSON_H_ +#define INCLUDE_V8_JSON_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Value; +class String; + +/** + * A JSON Parser and Stringifier. + */ +class V8_EXPORT JSON { + public: + /** + * Tries to parse the string |json_string| and returns it as value if + * successful. + * + * \param the context in which to parse and create the value. + * \param json_string The string to parse. + * \return The corresponding value if successfully parsed. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal Parse( + Local context, Local json_string); + + /** + * Tries to stringify the JSON-serializable object |json_object| and returns + * it as string if successful. + * + * \param json_object The JSON-serializable object to stringify. + * \return The corresponding string if successfully stringified. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal Stringify( + Local context, Local json_object, + Local gap = Local()); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_JSON_H_ diff --git a/deps/include/v8-local-handle.h b/deps/include/v8-local-handle.h new file mode 100644 index 000000000..66a8e93af --- /dev/null +++ b/deps/include/v8-local-handle.h @@ -0,0 +1,459 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_LOCAL_HANDLE_H_ +#define INCLUDE_V8_LOCAL_HANDLE_H_ + +#include + +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Boolean; +template +class BasicTracedReference; +class Context; +class EscapableHandleScope; +template +class Eternal; +template +class FunctionCallbackInfo; +class Isolate; +template +class MaybeLocal; +template +class NonCopyablePersistentTraits; +class Object; +template > +class Persistent; +template +class PersistentBase; +template +class PersistentValueMapBase; +template +class PersistentValueVector; +class Primitive; +class Private; +template +class PropertyCallbackInfo; +template +class ReturnValue; +class String; +template +class Traced; +template +class TracedGlobal; +template +class TracedReference; +class TracedReferenceBase; +class Utils; + +namespace internal { +template +class CustomArguments; +} // namespace internal + +namespace api_internal { +// Called when ToLocalChecked is called on an empty Local. +V8_EXPORT void ToLocalEmpty(); +} // namespace api_internal + +/** + * A stack-allocated class that governs a number of local handles. + * After a handle scope has been created, all local handles will be + * allocated within that handle scope until either the handle scope is + * deleted or another handle scope is created. If there is already a + * handle scope and a new one is created, all allocations will take + * place in the new handle scope until it is deleted. After that, + * new handles will again be allocated in the original handle scope. + * + * After the handle scope of a local handle has been deleted the + * garbage collector will no longer track the object stored in the + * handle and may deallocate it. The behavior of accessing a handle + * for which the handle scope has been deleted is undefined. + */ +class V8_EXPORT V8_NODISCARD HandleScope { + public: + explicit HandleScope(Isolate* isolate); + + ~HandleScope(); + + /** + * Counts the number of allocated handles. + */ + static int NumberOfHandles(Isolate* isolate); + + V8_INLINE Isolate* GetIsolate() const { + return reinterpret_cast(isolate_); + } + + HandleScope(const HandleScope&) = delete; + void operator=(const HandleScope&) = delete; + + protected: + V8_INLINE HandleScope() = default; + + void Initialize(Isolate* isolate); + + static internal::Address* CreateHandle(internal::Isolate* isolate, + internal::Address value); + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Isolate* isolate_; + internal::Address* prev_next_; + internal::Address* prev_limit_; + + // Local::New uses CreateHandle with an Isolate* parameter. + template + friend class Local; + + // Object::GetInternalField and Context::GetEmbedderData use CreateHandle with + // a HeapObject in their shortcuts. + friend class Object; + friend class Context; +}; + +/** + * An object reference managed by the v8 garbage collector. + * + * All objects returned from v8 have to be tracked by the garbage collector so + * that it knows that the objects are still alive. Also, because the garbage + * collector may move objects, it is unsafe to point directly to an object. + * Instead, all objects are stored in handles which are known by the garbage + * collector and updated whenever an object moves. Handles should always be + * passed by value (except in cases like out-parameters) and they should never + * be allocated on the heap. + * + * There are two types of handles: local and persistent handles. + * + * Local handles are light-weight and transient and typically used in local + * operations. They are managed by HandleScopes. That means that a HandleScope + * must exist on the stack when they are created and that they are only valid + * inside of the HandleScope active during their creation. For passing a local + * handle to an outer HandleScope, an EscapableHandleScope and its Escape() + * method must be used. + * + * Persistent handles can be used when storing objects across several + * independent operations and have to be explicitly deallocated when they're no + * longer used. + * + * It is safe to extract the object stored in the handle by dereferencing the + * handle (for instance, to extract the Object* from a Local); the value + * will still be governed by a handle behind the scenes and the same rules apply + * to these values as to their handles. + */ +template +class Local { + public: + V8_INLINE Local() : val_(nullptr) {} + template + V8_INLINE Local(Local that) : val_(reinterpret_cast(*that)) { + /** + * This check fails when trying to convert between incompatible + * handles. For example, converting from a Local to a + * Local. + */ + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Returns true if the handle is empty. + */ + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + /** + * Sets the handle to be empty. IsEmpty() will then return true. + */ + V8_INLINE void Clear() { val_ = nullptr; } + + V8_INLINE T* operator->() const { return val_; } + + V8_INLINE T* operator*() const { return val_; } + + /** + * Checks whether two handles are the same. + * Returns true if both are empty, or if the objects to which they refer + * are identical. + * + * If both handles refer to JS objects, this is the same as strict equality. + * For primitives, such as numbers or strings, a `false` return value does not + * indicate that the values aren't equal in the JavaScript sense. + * Use `Value::StrictEquals()` to check primitives for equality. + */ + template + V8_INLINE bool operator==(const Local& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator==(const PersistentBase& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + /** + * Checks whether two handles are different. + * Returns true if only one of the handles is empty, or if + * the objects to which they refer are different. + * + * If both handles refer to JS objects, this is the same as strict + * non-equality. For primitives, such as numbers or strings, a `true` return + * value does not indicate that the values aren't equal in the JavaScript + * sense. Use `Value::StrictEquals()` to check primitives for equality. + */ + template + V8_INLINE bool operator!=(const Local& that) const { + return !operator==(that); + } + + template + V8_INLINE bool operator!=(const Persistent& that) const { + return !operator==(that); + } + + /** + * Cast a handle to a subclass, e.g. Local to Local. + * This is only valid if the handle actually refers to a value of the + * target type. + */ + template + V8_INLINE static Local Cast(Local that) { +#ifdef V8_ENABLE_CHECKS + // If we're going to perform the type check then we have to check + // that the handle isn't empty before doing the checked cast. + if (that.IsEmpty()) return Local(); +#endif + return Local(T::Cast(*that)); + } + + /** + * Calling this is equivalent to Local::Cast(). + * In particular, this is only valid if the handle actually refers to a value + * of the target type. + */ + template + V8_INLINE Local As() const { + return Local::Cast(*this); + } + + /** + * Create a local handle for the content of another handle. + * The referee is kept alive by the local handle even when + * the original handle is destroyed/disposed. + */ + V8_INLINE static Local New(Isolate* isolate, Local that) { + return New(isolate, that.val_); + } + + V8_INLINE static Local New(Isolate* isolate, + const PersistentBase& that) { + return New(isolate, that.val_); + } + + V8_INLINE static Local New(Isolate* isolate, + const BasicTracedReference& that) { + return New(isolate, *that); + } + + private: + friend class TracedReferenceBase; + friend class Utils; + template + friend class Eternal; + template + friend class PersistentBase; + template + friend class Persistent; + template + friend class Local; + template + friend class MaybeLocal; + template + friend class FunctionCallbackInfo; + template + friend class PropertyCallbackInfo; + friend class String; + friend class Object; + friend class Context; + friend class Isolate; + friend class Private; + template + friend class internal::CustomArguments; + friend Local Undefined(Isolate* isolate); + friend Local Null(Isolate* isolate); + friend Local True(Isolate* isolate); + friend Local False(Isolate* isolate); + friend class HandleScope; + friend class EscapableHandleScope; + template + friend class PersistentValueMapBase; + template + friend class PersistentValueVector; + template + friend class ReturnValue; + template + friend class Traced; + template + friend class TracedGlobal; + template + friend class BasicTracedReference; + template + friend class TracedReference; + + explicit V8_INLINE Local(T* that) : val_(that) {} + V8_INLINE static Local New(Isolate* isolate, T* that) { + if (that == nullptr) return Local(); + T* that_ptr = that; + internal::Address* p = reinterpret_cast(that_ptr); + return Local(reinterpret_cast(HandleScope::CreateHandle( + reinterpret_cast(isolate), *p))); + } + T* val_; +}; + +#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) +// Handle is an alias for Local for historical reasons. +template +using Handle = Local; +#endif + +/** + * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether + * the Local<> is empty before it can be used. + * + * If an API method returns a MaybeLocal<>, the API method can potentially fail + * either because an exception is thrown, or because an exception is pending, + * e.g. because a previous API call threw an exception that hasn't been caught + * yet, or because a TerminateExecution exception was thrown. In that case, an + * empty MaybeLocal is returned. + */ +template +class MaybeLocal { + public: + V8_INLINE MaybeLocal() : val_(nullptr) {} + template + V8_INLINE MaybeLocal(Local that) : val_(reinterpret_cast(*that)) { + static_assert(std::is_base_of::value, "type check"); + } + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + /** + * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, + * |false| is returned and |out| is left untouched. + */ + template + V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local* out) const { + out->val_ = IsEmpty() ? nullptr : this->val_; + return !IsEmpty(); + } + + /** + * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, + * V8 will crash the process. + */ + V8_INLINE Local ToLocalChecked() { + if (V8_UNLIKELY(val_ == nullptr)) api_internal::ToLocalEmpty(); + return Local(val_); + } + + /** + * Converts this MaybeLocal<> to a Local<>, using a default value if this + * MaybeLocal<> is empty. + */ + template + V8_INLINE Local FromMaybe(Local default_value) const { + return IsEmpty() ? default_value : Local(val_); + } + + private: + T* val_; +}; + +/** + * A HandleScope which first allocates a handle in the current scope + * which will be later filled with the escape value. + */ +class V8_EXPORT V8_NODISCARD EscapableHandleScope : public HandleScope { + public: + explicit EscapableHandleScope(Isolate* isolate); + V8_INLINE ~EscapableHandleScope() = default; + + /** + * Pushes the value into the previous scope and returns a handle to it. + * Cannot be called twice. + */ + template + V8_INLINE Local Escape(Local value) { + internal::Address* slot = + Escape(reinterpret_cast(*value)); + return Local(reinterpret_cast(slot)); + } + + template + V8_INLINE MaybeLocal EscapeMaybe(MaybeLocal value) { + return Escape(value.FromMaybe(Local())); + } + + EscapableHandleScope(const EscapableHandleScope&) = delete; + void operator=(const EscapableHandleScope&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Address* Escape(internal::Address* escape_value); + internal::Address* escape_slot_; +}; + +/** + * A SealHandleScope acts like a handle scope in which no handle allocations + * are allowed. It can be useful for debugging handle leaks. + * Handles can be allocated within inner normal HandleScopes. + */ +class V8_EXPORT V8_NODISCARD SealHandleScope { + public: + explicit SealHandleScope(Isolate* isolate); + ~SealHandleScope(); + + SealHandleScope(const SealHandleScope&) = delete; + void operator=(const SealHandleScope&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Isolate* const isolate_; + internal::Address* prev_limit_; + int prev_sealed_level_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_LOCAL_HANDLE_H_ diff --git a/deps/include/v8-locker.h b/deps/include/v8-locker.h new file mode 100644 index 000000000..360022b7d --- /dev/null +++ b/deps/include/v8-locker.h @@ -0,0 +1,148 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_LOCKER_H_ +#define INCLUDE_V8_LOCKER_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +namespace internal { +class Isolate; +} // namespace internal + +class Isolate; + +/** + * Multiple threads in V8 are allowed, but only one thread at a time is allowed + * to use any given V8 isolate, see the comments in the Isolate class. The + * definition of 'using a V8 isolate' includes accessing handles or holding onto + * object pointers obtained from V8 handles while in the particular V8 isolate. + * It is up to the user of V8 to ensure, perhaps with locking, that this + * constraint is not violated. In addition to any other synchronization + * mechanism that may be used, the v8::Locker and v8::Unlocker classes must be + * used to signal thread switches to V8. + * + * v8::Locker is a scoped lock object. While it's active, i.e. between its + * construction and destruction, the current thread is allowed to use the locked + * isolate. V8 guarantees that an isolate can be locked by at most one thread at + * any time. In other words, the scope of a v8::Locker is a critical section. + * + * Sample usage: + * \code + * ... + * { + * v8::Locker locker(isolate); + * v8::Isolate::Scope isolate_scope(isolate); + * ... + * // Code using V8 and isolate goes here. + * ... + * } // Destructor called here + * \endcode + * + * If you wish to stop using V8 in a thread A you can do this either by + * destroying the v8::Locker object as above or by constructing a v8::Unlocker + * object: + * + * \code + * { + * isolate->Exit(); + * v8::Unlocker unlocker(isolate); + * ... + * // Code not using V8 goes here while V8 can run in another thread. + * ... + * } // Destructor called here. + * isolate->Enter(); + * \endcode + * + * The Unlocker object is intended for use in a long-running callback from V8, + * where you want to release the V8 lock for other threads to use. + * + * The v8::Locker is a recursive lock, i.e. you can lock more than once in a + * given thread. This can be useful if you have code that can be called either + * from code that holds the lock or from code that does not. The Unlocker is + * not recursive so you can not have several Unlockers on the stack at once, and + * you cannot use an Unlocker in a thread that is not inside a Locker's scope. + * + * An unlocker will unlock several lockers if it has to and reinstate the + * correct depth of locking on its destruction, e.g.: + * + * \code + * // V8 not locked. + * { + * v8::Locker locker(isolate); + * Isolate::Scope isolate_scope(isolate); + * // V8 locked. + * { + * v8::Locker another_locker(isolate); + * // V8 still locked (2 levels). + * { + * isolate->Exit(); + * v8::Unlocker unlocker(isolate); + * // V8 not locked. + * } + * isolate->Enter(); + * // V8 locked again (2 levels). + * } + * // V8 still locked (1 level). + * } + * // V8 Now no longer locked. + * \endcode + */ +class V8_EXPORT Unlocker { + public: + /** + * Initialize Unlocker for a given Isolate. + */ + V8_INLINE explicit Unlocker(Isolate* isolate) { Initialize(isolate); } + + ~Unlocker(); + + private: + void Initialize(Isolate* isolate); + + internal::Isolate* isolate_; +}; + +class V8_EXPORT Locker { + public: + /** + * Initialize Locker for a given Isolate. + */ + V8_INLINE explicit Locker(Isolate* isolate) { Initialize(isolate); } + + ~Locker(); + + /** + * Returns whether or not the locker for a given isolate, is locked by the + * current thread. + */ + 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. + */ + static bool WasEverUsed(); + V8_DEPRECATE_SOON("Use WasEverUsed instead") + static bool IsActive(); + + // Disallow copying and assigning. + Locker(const Locker&) = delete; + void operator=(const Locker&) = delete; + + private: + void Initialize(Isolate* isolate); + + bool has_lock_; + bool top_level_; + internal::Isolate* isolate_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_LOCKER_H_ diff --git a/deps/include/v8-maybe.h b/deps/include/v8-maybe.h new file mode 100644 index 000000000..0532a5100 --- /dev/null +++ b/deps/include/v8-maybe.h @@ -0,0 +1,137 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MAYBE_H_ +#define INCLUDE_V8_MAYBE_H_ + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +namespace api_internal { +// Called when ToChecked is called on an empty Maybe. +V8_EXPORT void FromJustIsNothing(); +} // namespace api_internal + +/** + * A simple Maybe type, representing an object which may or may not have a + * value, see https://hackage.haskell.org/package/base/docs/Data-Maybe.html. + * + * If an API method returns a Maybe<>, the API method can potentially fail + * either because an exception is thrown, or because an exception is pending, + * e.g. because a previous API call threw an exception that hasn't been caught + * yet, or because a TerminateExecution exception was thrown. In that case, a + * "Nothing" value is returned. + */ +template +class Maybe { + public: + V8_INLINE bool IsNothing() const { return !has_value_; } + V8_INLINE bool IsJust() const { return has_value_; } + + /** + * An alias for |FromJust|. Will crash if the Maybe<> is nothing. + */ + V8_INLINE T ToChecked() const { return FromJust(); } + + /** + * Short-hand for ToChecked(), which doesn't return a value. To be used, where + * the actual value of the Maybe is not needed like Object::Set. + */ + V8_INLINE void Check() const { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + } + + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), |false| is returned and |out| is left untouched. + */ + V8_WARN_UNUSED_RESULT V8_INLINE bool To(T* out) const { + if (V8_LIKELY(IsJust())) *out = value_; + return IsJust(); + } + + /** + * 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 { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + return value_; + } + + /** + * Converts this Maybe<> to a value of type T, using a default value if this + * Maybe<> is nothing (empty). + */ + V8_INLINE T FromMaybe(const T& default_value) const { + return has_value_ ? value_ : default_value; + } + + V8_INLINE bool operator==(const Maybe& other) const { + return (IsJust() == other.IsJust()) && + (!IsJust() || FromJust() == other.FromJust()); + } + + V8_INLINE bool operator!=(const Maybe& other) const { + return !operator==(other); + } + + private: + Maybe() : has_value_(false) {} + explicit Maybe(const T& t) : has_value_(true), value_(t) {} + + bool has_value_; + T value_; + + template + friend Maybe Nothing(); + template + friend Maybe Just(const U& u); +}; + +template +inline Maybe Nothing() { + return Maybe(); +} + +template +inline Maybe Just(const T& t) { + return Maybe(t); +} + +// A template specialization of Maybe for the case of T = void. +template <> +class Maybe { + public: + V8_INLINE bool IsNothing() const { return !is_valid_; } + V8_INLINE bool IsJust() const { return is_valid_; } + + V8_INLINE bool operator==(const Maybe& other) const { + return IsJust() == other.IsJust(); + } + + V8_INLINE bool operator!=(const Maybe& other) const { + return !operator==(other); + } + + private: + struct JustTag {}; + + Maybe() : is_valid_(false) {} + explicit Maybe(JustTag) : is_valid_(true) {} + + bool is_valid_; + + template + friend Maybe Nothing(); + friend Maybe JustVoid(); +}; + +inline Maybe JustVoid() { return Maybe(Maybe::JustTag()); } + +} // namespace v8 + +#endif // INCLUDE_V8_MAYBE_H_ diff --git a/deps/include/v8-memory-span.h b/deps/include/v8-memory-span.h new file mode 100644 index 000000000..b26af4f70 --- /dev/null +++ b/deps/include/v8-memory-span.h @@ -0,0 +1,43 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MEMORY_SPAN_H_ +#define INCLUDE_V8_MEMORY_SPAN_H_ + +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +/** + * Points to an unowned continous buffer holding a known number of elements. + * + * This is similar to std::span (under consideration for C++20), but does not + * require advanced C++ support. In the (far) future, this may be replaced with + * or aliased to std::span. + * + * To facilitate future migration, this class exposes a subset of the interface + * implemented by std::span. + */ +template +class V8_EXPORT MemorySpan { + public: + /** The default constructor creates an empty span. */ + constexpr MemorySpan() = default; + + constexpr MemorySpan(T* data, size_t size) : data_(data), size_(size) {} + + /** Returns a pointer to the beginning of the buffer. */ + constexpr T* data() const { return data_; } + /** Returns the number of elements that the buffer holds. */ + constexpr size_t size() const { return size_; } + + private: + T* data_ = nullptr; + size_t size_ = 0; +}; + +} // namespace v8 +#endif // INCLUDE_V8_MEMORY_SPAN_H_ diff --git a/deps/include/v8-message.h b/deps/include/v8-message.h new file mode 100644 index 000000000..62b6bd92f --- /dev/null +++ b/deps/include/v8-message.h @@ -0,0 +1,237 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MESSAGE_H_ +#define INCLUDE_V8_MESSAGE_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Integer; +class PrimitiveArray; +class StackTrace; +class String; +class Value; + +/** + * The optional attributes of ScriptOrigin. + */ +class ScriptOriginOptions { + public: + V8_INLINE ScriptOriginOptions(bool is_shared_cross_origin = false, + bool is_opaque = false, bool is_wasm = false, + bool is_module = false) + : flags_((is_shared_cross_origin ? kIsSharedCrossOrigin : 0) | + (is_wasm ? kIsWasm : 0) | (is_opaque ? kIsOpaque : 0) | + (is_module ? kIsModule : 0)) {} + V8_INLINE ScriptOriginOptions(int flags) + : flags_(flags & + (kIsSharedCrossOrigin | kIsOpaque | kIsWasm | kIsModule)) {} + + bool IsSharedCrossOrigin() const { + return (flags_ & kIsSharedCrossOrigin) != 0; + } + bool IsOpaque() const { return (flags_ & kIsOpaque) != 0; } + bool IsWasm() const { return (flags_ & kIsWasm) != 0; } + bool IsModule() const { return (flags_ & kIsModule) != 0; } + + int Flags() const { return flags_; } + + private: + enum { + kIsSharedCrossOrigin = 1, + kIsOpaque = 1 << 1, + kIsWasm = 1 << 2, + kIsModule = 1 << 3 + }; + const int flags_; +}; + +/** + * The origin, within a file, of a script. + */ +class V8_EXPORT ScriptOrigin { + public: + V8_DEPRECATE_SOON("Use constructor with primitive C++ types") + ScriptOrigin( + Local resource_name, Local resource_line_offset, + Local resource_column_offset, + Local resource_is_shared_cross_origin = Local(), + Local script_id = Local(), + Local source_map_url = Local(), + Local resource_is_opaque = Local(), + Local is_wasm = Local(), + Local is_module = Local(), + Local host_defined_options = Local()); + V8_DEPRECATE_SOON("Use constructor that takes an isolate") + explicit ScriptOrigin( + Local resource_name, int resource_line_offset = 0, + int resource_column_offset = 0, + bool resource_is_shared_cross_origin = false, int script_id = -1, + Local source_map_url = Local(), + bool resource_is_opaque = false, bool is_wasm = false, + bool is_module = false, + Local host_defined_options = Local()); + V8_INLINE ScriptOrigin( + Isolate* isolate, Local resource_name, + int resource_line_offset = 0, int resource_column_offset = 0, + bool resource_is_shared_cross_origin = false, int script_id = -1, + Local source_map_url = Local(), + bool resource_is_opaque = false, bool is_wasm = false, + bool is_module = false, + Local host_defined_options = Local()) + : isolate_(isolate), + resource_name_(resource_name), + resource_line_offset_(resource_line_offset), + resource_column_offset_(resource_column_offset), + options_(resource_is_shared_cross_origin, resource_is_opaque, is_wasm, + is_module), + script_id_(script_id), + source_map_url_(source_map_url), + host_defined_options_(host_defined_options) {} + + V8_INLINE Local ResourceName() const; + V8_DEPRECATE_SOON("Use getter with primitive C++ types.") + V8_INLINE Local ResourceLineOffset() const; + V8_DEPRECATE_SOON("Use getter with primitive C++ types.") + V8_INLINE Local ResourceColumnOffset() const; + V8_DEPRECATE_SOON("Use getter with primitive C++ types.") + V8_INLINE Local ScriptID() const; + V8_INLINE int LineOffset() const; + V8_INLINE int ColumnOffset() const; + V8_INLINE int ScriptId() const; + V8_INLINE Local SourceMapUrl() const; + V8_INLINE Local HostDefinedOptions() const; + V8_INLINE ScriptOriginOptions Options() const { return options_; } + + private: + Isolate* isolate_; + Local resource_name_; + int resource_line_offset_; + int resource_column_offset_; + ScriptOriginOptions options_; + int script_id_; + Local source_map_url_; + Local host_defined_options_; +}; + +/** + * An error message. + */ +class V8_EXPORT Message { + public: + Local Get() const; + + /** + * Return the isolate to which the Message belongs. + */ + Isolate* GetIsolate() const; + + V8_WARN_UNUSED_RESULT MaybeLocal GetSource( + Local context) const; + V8_WARN_UNUSED_RESULT MaybeLocal GetSourceLine( + Local context) const; + + /** + * Returns the origin for the script from where the function causing the + * error originates. + */ + ScriptOrigin GetScriptOrigin() const; + + /** + * Returns the resource name for the script from where the function causing + * the error originates. + */ + Local GetScriptResourceName() const; + + /** + * Exception stack trace. By default stack traces are not captured for + * uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows + * to change this option. + */ + Local GetStackTrace() const; + + /** + * Returns the number, 1-based, of the line where the error occurred. + */ + V8_WARN_UNUSED_RESULT Maybe GetLineNumber(Local context) const; + + /** + * Returns the index within the script of the first character where + * the error occurred. + */ + int GetStartPosition() const; + + /** + * Returns the index within the script of the last character where + * the error occurred. + */ + int GetEndPosition() const; + + /** + * Returns the Wasm function index where the error occurred. Returns -1 if + * message is not from a Wasm script. + */ + int GetWasmFunctionIndex() const; + + /** + * Returns the error level of the message. + */ + int ErrorLevel() const; + + /** + * Returns the index within the line of the first character where + * the error occurred. + */ + int GetStartColumn() const; + V8_WARN_UNUSED_RESULT Maybe GetStartColumn(Local context) const; + + /** + * Returns the index within the line of the last character where + * the error occurred. + */ + int GetEndColumn() const; + V8_WARN_UNUSED_RESULT Maybe GetEndColumn(Local context) const; + + /** + * Passes on the value set by the embedder when it fed the script from which + * this Message was generated to V8. + */ + bool IsSharedCrossOrigin() const; + bool IsOpaque() const; + + V8_DEPRECATE_SOON("Use the version that takes a std::ostream&.") + static void PrintCurrentStackTrace(Isolate* isolate, FILE* out); + static void PrintCurrentStackTrace(Isolate* isolate, std::ostream& out); + + static const int kNoLineNumberInfo = 0; + static const int kNoColumnInfo = 0; + static const int kNoScriptIdInfo = 0; + static const int kNoWasmFunctionIndexInfo = -1; +}; + +Local ScriptOrigin::ResourceName() const { return resource_name_; } + +Local ScriptOrigin::HostDefinedOptions() const { + return host_defined_options_; +} + +int ScriptOrigin::LineOffset() const { return resource_line_offset_; } + +int ScriptOrigin::ColumnOffset() const { return resource_column_offset_; } + +int ScriptOrigin::ScriptId() const { return script_id_; } + +Local ScriptOrigin::SourceMapUrl() const { return source_map_url_; } + +} // namespace v8 + +#endif // INCLUDE_V8_MESSAGE_H_ diff --git a/deps/include/v8-metrics.h b/deps/include/v8-metrics.h index 0217f40d6..29e544010 100644 --- a/deps/include/v8-metrics.h +++ b/deps/include/v8-metrics.h @@ -5,9 +5,19 @@ #ifndef V8_METRICS_H_ #define V8_METRICS_H_ -#include "v8.h" // NOLINT(build/include_directory) +#include +#include + +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) namespace v8 { + +class Context; +class Isolate; + namespace metrics { struct GarbageCollectionPhases { @@ -49,11 +59,19 @@ struct GarbageCollectionFullMainThreadIncrementalMark { int64_t cpp_wall_clock_duration_in_us = -1; }; +struct GarbageCollectionFullMainThreadBatchedIncrementalMark { + std::vector events; +}; + struct GarbageCollectionFullMainThreadIncrementalSweep { int64_t wall_clock_duration_in_us = -1; int64_t cpp_wall_clock_duration_in_us = -1; }; +struct GarbageCollectionFullMainThreadBatchedIncrementalSweep { + std::vector events; +}; + struct GarbageCollectionYoungCycle { int64_t total_wall_clock_duration_in_us = -1; int64_t main_thread_wall_clock_duration_in_us = -1; @@ -69,6 +87,7 @@ struct WasmModuleDecoded { size_t module_size_in_bytes = 0; size_t function_count = 0; int64_t wall_clock_duration_in_us = -1; + int64_t cpu_duration_in_us = -1; }; struct WasmModuleCompiled { @@ -81,6 +100,7 @@ struct WasmModuleCompiled { size_t code_size_in_bytes = 0; size_t liftoff_bailout_count = 0; int64_t wall_clock_duration_in_us = -1; + int64_t cpu_duration_in_us = -1; }; struct WasmModuleInstantiated { @@ -94,20 +114,23 @@ 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(GarbageCollectionFullMainThreadIncrementalSweep) \ - V(GarbageCollectionYoungCycle) \ - V(WasmModuleDecoded) \ - V(WasmModuleCompiled) \ - V(WasmModuleInstantiated) \ +#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) @@ -183,6 +206,32 @@ class V8_EXPORT Recorder { static ContextId GetContextId(Local context); }; +/** + * Experimental API intended for the LongTasks UKM (crbug.com/1173527). + * The Reset() method should be called at the start of a potential + * long task. The Get() method returns durations of V8 work that + * happened during the task. + * + * This API is experimental and may be removed/changed in the future. + */ +struct V8_EXPORT LongTaskStats { + /** + * Resets durations of V8 work for the new task. + */ + V8_INLINE static void Reset(Isolate* isolate) { + v8::internal::Internals::IncrementLongTasksStatsCounter(isolate); + } + + /** + * Returns durations of V8 work that happened since the last Reset(). + */ + static LongTaskStats Get(Isolate* isolate); + + int64_t gc_full_atomic_wall_clock_duration_us = 0; + int64_t gc_full_incremental_wall_clock_duration_us = 0; + int64_t gc_young_wall_clock_duration_us = 0; +}; + } // namespace metrics } // namespace v8 diff --git a/deps/include/v8-microtask-queue.h b/deps/include/v8-microtask-queue.h new file mode 100644 index 000000000..af9caa54a --- /dev/null +++ b/deps/include/v8-microtask-queue.h @@ -0,0 +1,152 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MICROTASKS_QUEUE_H_ +#define INCLUDE_V8_MICROTASKS_QUEUE_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-microtask.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; + +namespace internal { +class Isolate; +class MicrotaskQueue; +} // namespace internal + +/** + * Represents the microtask queue, where microtasks are stored and processed. + * https://html.spec.whatwg.org/multipage/webappapis.html#microtask-queue + * https://html.spec.whatwg.org/multipage/webappapis.html#enqueuejob(queuename,-job,-arguments) + * https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint + * + * A MicrotaskQueue instance may be associated to multiple Contexts by passing + * it to Context::New(), and they can be detached by Context::DetachGlobal(). + * The embedder must keep the MicrotaskQueue instance alive until all associated + * Contexts are gone or detached. + * + * Use the same instance of MicrotaskQueue for all Contexts that may access each + * other synchronously. E.g. for Web embedding, use the same instance for all + * origins that share the same URL scheme and eTLD+1. + */ +class V8_EXPORT MicrotaskQueue { + public: + /** + * Creates an empty MicrotaskQueue instance. + */ + static std::unique_ptr New( + Isolate* isolate, MicrotasksPolicy policy = MicrotasksPolicy::kAuto); + + virtual ~MicrotaskQueue() = default; + + /** + * Enqueues the callback to the queue. + */ + virtual void EnqueueMicrotask(Isolate* isolate, + Local microtask) = 0; + + /** + * Enqueues the callback to the queue. + */ + virtual void EnqueueMicrotask(v8::Isolate* isolate, + MicrotaskCallback callback, + void* data = nullptr) = 0; + + /** + * Adds a callback to notify the embedder after microtasks were run. The + * callback is triggered by explicit RunMicrotasks call or automatic + * microtasks execution (see Isolate::SetMicrotasksPolicy). + * + * Callback will trigger even if microtasks were attempted to run, + * but the microtasks queue was empty and no single microtask was actually + * executed. + * + * Executing scripts inside the callback will not re-trigger microtasks and + * the callback. + */ + virtual void AddMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0; + + /** + * Removes callback that was installed by AddMicrotasksCompletedCallback. + */ + virtual void RemoveMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0; + + /** + * Runs microtasks if no microtask is running on this MicrotaskQueue instance. + */ + virtual void PerformCheckpoint(Isolate* isolate) = 0; + + /** + * Returns true if a microtask is running on this MicrotaskQueue instance. + */ + virtual bool IsRunningMicrotasks() const = 0; + + /** + * Returns the current depth of nested MicrotasksScope that has + * kRunMicrotasks. + */ + virtual int GetMicrotasksScopeDepth() const = 0; + + MicrotaskQueue(const MicrotaskQueue&) = delete; + MicrotaskQueue& operator=(const MicrotaskQueue&) = delete; + + private: + friend class internal::MicrotaskQueue; + MicrotaskQueue() = default; +}; + +/** + * This scope is used to control microtasks when MicrotasksPolicy::kScoped + * is used on Isolate. In this mode every non-primitive call to V8 should be + * done inside some MicrotasksScope. + * Microtasks are executed when topmost MicrotasksScope marked as kRunMicrotasks + * exits. + * kDoNotRunMicrotasks should be used to annotate calls not intended to trigger + * microtasks. + */ +class V8_EXPORT V8_NODISCARD MicrotasksScope { + public: + enum Type { kRunMicrotasks, kDoNotRunMicrotasks }; + + MicrotasksScope(Isolate* isolate, Type type); + MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type); + ~MicrotasksScope(); + + /** + * Runs microtasks if no kRunMicrotasks scope is currently active. + */ + static void PerformCheckpoint(Isolate* isolate); + + /** + * Returns current depth of nested kRunMicrotasks scopes. + */ + static int GetCurrentDepth(Isolate* isolate); + + /** + * Returns true while microtasks are being executed. + */ + static bool IsRunningMicrotasks(Isolate* isolate); + + // Prevent copying. + MicrotasksScope(const MicrotasksScope&) = delete; + MicrotasksScope& operator=(const MicrotasksScope&) = delete; + + private: + internal::Isolate* const isolate_; + internal::MicrotaskQueue* const microtask_queue_; + bool run_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_MICROTASKS_QUEUE_H_ diff --git a/deps/include/v8-microtask.h b/deps/include/v8-microtask.h new file mode 100644 index 000000000..c15920360 --- /dev/null +++ b/deps/include/v8-microtask.h @@ -0,0 +1,28 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MICROTASK_H_ +#define INCLUDE_V8_MICROTASK_H_ + +namespace v8 { + +class Isolate; + +// --- Microtasks Callbacks --- +using MicrotasksCompletedCallbackWithData = void (*)(Isolate*, void*); +using MicrotaskCallback = void (*)(void* data); + +/** + * Policy for running microtasks: + * - explicit: microtasks are invoked with the + * Isolate::PerformMicrotaskCheckpoint() method; + * - scoped: microtasks invocation is controlled by MicrotasksScope objects; + * - auto: microtasks are invoked when the script call depth decrements + * to zero. + */ +enum class MicrotasksPolicy { kExplicit, kScoped, kAuto }; + +} // namespace v8 + +#endif // INCLUDE_V8_MICROTASK_H_ diff --git a/deps/include/v8-object.h b/deps/include/v8-object.h new file mode 100644 index 000000000..114e452a3 --- /dev/null +++ b/deps/include/v8-object.h @@ -0,0 +1,770 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_OBJECT_H_ +#define INCLUDE_V8_OBJECT_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8-traced-handle.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Array; +class Function; +class FunctionTemplate; +template +class PropertyCallbackInfo; + +/** + * A private symbol + * + * This is an experimental feature. Use at your own risk. + */ +class V8_EXPORT Private : public Data { + public: + /** + * Returns the print name string of the private symbol, or undefined if none. + */ + Local Name() const; + + /** + * Create a private symbol. If name is not empty, it will be the description. + */ + static Local New(Isolate* isolate, + Local name = Local()); + + /** + * Retrieve a global private symbol. If a symbol with this name has not + * been retrieved in the same isolate before, it is created. + * Note that private symbols created this way are never collected, so + * they should only be used for statically fixed properties. + * Also, there is only one global name space for the names used as keys. + * To minimize the potential for clashes, use qualified names as keys, + * e.g., "Class#property". + */ + static Local ForApi(Isolate* isolate, Local name); + + V8_INLINE static Private* Cast(Data* data); + + private: + Private(); + + static void CheckCast(Data* that); +}; + +/** + * An instance of a Property Descriptor, see Ecma-262 6.2.4. + * + * Properties in a descriptor are present or absent. If you do not set + * `enumerable`, `configurable`, and `writable`, they are absent. If `value`, + * `get`, or `set` are absent, but you must specify them in the constructor, use + * empty handles. + * + * Accessors `get` and `set` must be callable or undefined if they are present. + * + * \note Only query properties if they are present, i.e., call `x()` only if + * `has_x()` returns true. + * + * \code + * // var desc = {writable: false} + * v8::PropertyDescriptor d(Local()), false); + * d.value(); // error, value not set + * if (d.has_writable()) { + * d.writable(); // false + * } + * + * // var desc = {value: undefined} + * v8::PropertyDescriptor d(v8::Undefined(isolate)); + * + * // var desc = {get: undefined} + * v8::PropertyDescriptor d(v8::Undefined(isolate), Local())); + * \endcode + */ +class V8_EXPORT PropertyDescriptor { + public: + // GenericDescriptor + PropertyDescriptor(); + + // DataDescriptor + explicit PropertyDescriptor(Local value); + + // DataDescriptor with writable property + PropertyDescriptor(Local value, bool writable); + + // AccessorDescriptor + PropertyDescriptor(Local get, Local set); + + ~PropertyDescriptor(); + + Local value() const; + bool has_value() const; + + Local get() const; + bool has_get() const; + Local set() const; + bool has_set() const; + + void set_enumerable(bool enumerable); + bool enumerable() const; + bool has_enumerable() const; + + void set_configurable(bool configurable); + bool configurable() const; + bool has_configurable() const; + + bool writable() const; + bool has_writable() const; + + struct PrivateData; + PrivateData* get_private() const { return private_; } + + PropertyDescriptor(const PropertyDescriptor&) = delete; + void operator=(const PropertyDescriptor&) = delete; + + private: + PrivateData* private_; +}; + +/** + * PropertyAttribute. + */ +enum PropertyAttribute { + /** None. **/ + None = 0, + /** ReadOnly, i.e., not writable. **/ + ReadOnly = 1 << 0, + /** DontEnum, i.e., not enumerable. **/ + DontEnum = 1 << 1, + /** DontDelete, i.e., not configurable. **/ + DontDelete = 1 << 2 +}; + +/** + * Accessor[Getter|Setter] are used as callback functions when + * setting|getting a particular property. See Object and ObjectTemplate's + * method SetAccessor. + */ +using AccessorGetterCallback = + void (*)(Local property, const PropertyCallbackInfo& info); +using AccessorNameGetterCallback = + void (*)(Local property, const PropertyCallbackInfo& info); + +using AccessorSetterCallback = void (*)(Local property, + Local value, + const PropertyCallbackInfo& info); +using AccessorNameSetterCallback = + void (*)(Local property, Local value, + const PropertyCallbackInfo& info); + +/** + * Access control specifications. + * + * Some accessors should be accessible across contexts. These + * accessors have an explicit access control parameter which specifies + * the kind of cross-context access that should be allowed. + * + * TODO(dcarney): Remove PROHIBITS_OVERWRITING as it is now unused. + */ +enum AccessControl { + DEFAULT = 0, + ALL_CAN_READ = 1, + ALL_CAN_WRITE = 1 << 1, + PROHIBITS_OVERWRITING = 1 << 2 +}; + +/** + * Property filter bits. They can be or'ed to build a composite filter. + */ +enum PropertyFilter { + ALL_PROPERTIES = 0, + ONLY_WRITABLE = 1, + ONLY_ENUMERABLE = 2, + ONLY_CONFIGURABLE = 4, + SKIP_STRINGS = 8, + SKIP_SYMBOLS = 16 +}; + +/** + * Options for marking whether callbacks may trigger JS-observable side effects. + * Side-effect-free callbacks are allowlisted during debug evaluation with + * throwOnSideEffect. It applies when calling a Function, FunctionTemplate, + * or an Accessor callback. For Interceptors, please see + * PropertyHandlerFlags's kHasNoSideEffect. + * Callbacks that only cause side effects to the receiver are allowlisted if + * invoked on receiver objects that are created within the same debug-evaluate + * call, as these objects are temporary and the side effect does not escape. + */ +enum class SideEffectType { + kHasSideEffect, + kHasNoSideEffect, + kHasSideEffectToReceiver +}; + +/** + * Keys/Properties filter enums: + * + * KeyCollectionMode limits the range of collected properties. kOwnOnly limits + * the collected properties to the given Object only. kIncludesPrototypes will + * include all keys of the objects's prototype chain as well. + */ +enum class KeyCollectionMode { kOwnOnly, kIncludePrototypes }; + +/** + * kIncludesIndices allows for integer indices to be collected, while + * kSkipIndices will exclude integer indices from being collected. + */ +enum class IndexFilter { kIncludeIndices, kSkipIndices }; + +/** + * kConvertToString will convert integer indices to strings. + * kKeepNumbers will return numbers for integer indices. + */ +enum class KeyConversionMode { kConvertToString, kKeepNumbers, kNoNumbers }; + +/** + * Integrity level for objects. + */ +enum class IntegrityLevel { kFrozen, kSealed }; + +/** + * A JavaScript object (ECMA-262, 4.3.3) + */ +class V8_EXPORT Object : public Value { + public: + /** + * Set only return Just(true) or Empty(), so if it should never fail, use + * result.Check(). + */ + V8_WARN_UNUSED_RESULT Maybe Set(Local context, + Local key, Local value); + + V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index, + Local value); + + // Implements CreateDataProperty (ECMA-262, 7.3.4). + // + // Defines a configurable, writable, enumerable property with the given value + // on the object unless the property already exists and is not configurable + // or the object is not extensible. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, + Local key, + Local value); + V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, + uint32_t index, + Local value); + + // Implements DefineOwnProperty. + // + // In general, CreateDataProperty will be faster, however, does not allow + // for specifying attributes. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe DefineOwnProperty( + Local context, Local key, Local value, + PropertyAttribute attributes = None); + + // Implements Object.DefineProperty(O, P, Attributes), see Ecma-262 19.1.2.4. + // + // The defineProperty function is used to add an own property or + // update the attributes of an existing own property of an object. + // + // Both data and accessor descriptors can be used. + // + // In general, CreateDataProperty is faster, however, does not allow + // for specifying attributes or an accessor descriptor. + // + // The PropertyDescriptor can change when redefining a property. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe DefineProperty( + Local context, Local key, PropertyDescriptor& descriptor); + + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + Local key); + + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + uint32_t index); + + /** + * Gets the property attributes of a property which can be None or + * any combination of ReadOnly, DontEnum and DontDelete. Returns + * None when the property doesn't exist. + */ + V8_WARN_UNUSED_RESULT Maybe GetPropertyAttributes( + Local context, Local key); + + /** + * Returns Object.getOwnPropertyDescriptor as per ES2016 section 19.1.2.6. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyDescriptor( + Local context, Local key); + + /** + * Object::Has() calls the abstract operation HasProperty(O, P) described + * in ECMA-262, 7.3.10. Has() returns + * true, if the object has the property, either own or on the prototype chain. + * Interceptors, i.e., PropertyQueryCallbacks, are called if present. + * + * Has() has the same side effects as JavaScript's `variable in object`. + * For example, calling Has() on a revoked proxy will throw an exception. + * + * \note Has() converts the key to a name, which possibly calls back into + * JavaScript. + * + * See also v8::Object::HasOwnProperty() and + * v8::Object::HasRealNamedProperty(). + */ + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + V8_WARN_UNUSED_RESULT Maybe Has(Local context, uint32_t index); + + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + uint32_t index); + + /** + * Note: SideEffectType affects the getter only, not the setter. + */ + V8_WARN_UNUSED_RESULT Maybe SetAccessor( + Local context, Local name, + AccessorNameGetterCallback getter, + AccessorNameSetterCallback setter = nullptr, + MaybeLocal data = MaybeLocal(), + AccessControl settings = DEFAULT, PropertyAttribute attribute = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + void SetAccessorProperty(Local name, Local getter, + Local setter = Local(), + PropertyAttribute attribute = None, + AccessControl settings = DEFAULT); + + /** + * Sets a native data property like Template::SetNativeDataProperty, but + * this method sets on this object directly. + */ + V8_WARN_UNUSED_RESULT Maybe SetNativeDataProperty( + Local context, Local name, + AccessorNameGetterCallback getter, + AccessorNameSetterCallback setter = nullptr, + Local data = Local(), PropertyAttribute attributes = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + /** + * Attempts to create a property with the given name which behaves like a data + * property, except that the provided getter is invoked (and provided with the + * data value) to supply its value the first time it is read. After the + * property is accessed once, it is replaced with an ordinary data property. + * + * Analogous to Template::SetLazyDataProperty. + */ + V8_WARN_UNUSED_RESULT Maybe SetLazyDataProperty( + Local context, Local name, + AccessorNameGetterCallback getter, Local data = Local(), + PropertyAttribute attributes = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + /** + * Functionality for private properties. + * This is an experimental feature, use at your own risk. + * Note: Private properties are not inherited. Do not rely on this, since it + * may change. + */ + Maybe HasPrivate(Local context, Local key); + Maybe SetPrivate(Local context, Local key, + Local value); + Maybe DeletePrivate(Local context, Local key); + MaybeLocal GetPrivate(Local context, Local key); + + /** + * Returns an array containing the names of the enumerable properties + * of this object, including properties from prototype objects. The + * array returned by this method contains the same values as would + * be enumerated by a for-in statement over this object. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( + Local context); + V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( + Local context, KeyCollectionMode mode, + PropertyFilter property_filter, IndexFilter index_filter, + KeyConversionMode key_conversion = KeyConversionMode::kKeepNumbers); + + /** + * This function has the same functionality as GetPropertyNames but + * the returned array doesn't contain the names of properties from + * prototype objects. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( + Local context); + + /** + * Returns an array containing the names of the filtered properties + * of this object, including properties from prototype objects. The + * array returned by this method contains the same values as would + * be enumerated by a for-in statement over this object. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( + Local context, PropertyFilter filter, + KeyConversionMode key_conversion = KeyConversionMode::kKeepNumbers); + + /** + * Get the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + Local GetPrototype(); + + /** + * Set the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + V8_WARN_UNUSED_RESULT Maybe SetPrototype(Local context, + Local prototype); + + /** + * Finds an instance of the given function template in the prototype + * chain. + */ + Local FindInstanceInPrototypeChain(Local tmpl); + + /** + * Call builtin Object.prototype.toString on this object. + * This is different from Value::ToString() that may call + * user-defined toString function. This one does not. + */ + V8_WARN_UNUSED_RESULT MaybeLocal ObjectProtoToString( + Local context); + + /** + * Returns the name of the function invoked as a constructor for this object. + */ + Local GetConstructorName(); + + /** + * Sets the integrity level of the object. + */ + Maybe SetIntegrityLevel(Local context, IntegrityLevel level); + + /** Gets the number of internal fields for this Object. */ + int InternalFieldCount() const; + + /** Same as above, but works for PersistentBase. */ + V8_INLINE static int InternalFieldCount( + const PersistentBase& object) { + return object.val_->InternalFieldCount(); + } + + /** Same as above, but works for BasicTracedReference. */ + V8_INLINE static int InternalFieldCount( + const BasicTracedReference& object) { + return object->InternalFieldCount(); + } + + /** Gets the value from an internal field. */ + V8_INLINE Local GetInternalField(int index); + + /** Sets the value in an internal field. */ + void SetInternalField(int index, Local value); + + /** + * Gets a 2-byte-aligned native pointer from an internal field. This field + * must have been set by SetAlignedPointerInInternalField, everything else + * leads to undefined behavior. + */ + V8_INLINE void* GetAlignedPointerFromInternalField(int index); + + /** Same as above, but works for PersistentBase. */ + V8_INLINE static void* GetAlignedPointerFromInternalField( + const PersistentBase& object, int index) { + return object.val_->GetAlignedPointerFromInternalField(index); + } + + /** Same as above, but works for TracedGlobal. */ + V8_INLINE static void* GetAlignedPointerFromInternalField( + const BasicTracedReference& object, int index) { + return object->GetAlignedPointerFromInternalField(index); + } + + /** + * Sets a 2-byte-aligned native pointer in an internal field. To retrieve such + * a field, GetAlignedPointerFromInternalField must be used, everything else + * leads to undefined behavior. + */ + void SetAlignedPointerInInternalField(int index, void* value); + void SetAlignedPointerInInternalFields(int argc, int indices[], + void* values[]); + + /** + * HasOwnProperty() is like JavaScript's Object.prototype.hasOwnProperty(). + * + * See also v8::Object::Has() and v8::Object::HasRealNamedProperty(). + */ + V8_WARN_UNUSED_RESULT Maybe HasOwnProperty(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe HasOwnProperty(Local context, + uint32_t index); + /** + * Use HasRealNamedProperty() if you want to check if an object has an own + * property without causing side effects, i.e., without calling interceptors. + * + * This function is similar to v8::Object::HasOwnProperty(), but it does not + * call interceptors. + * + * \note Consider using non-masking interceptors, i.e., the interceptors are + * not called if the receiver has the real named property. See + * `v8::PropertyHandlerFlags::kNonMasking`. + * + * See also v8::Object::Has(). + */ + V8_WARN_UNUSED_RESULT Maybe HasRealNamedProperty(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe HasRealIndexedProperty( + Local context, uint32_t index); + V8_WARN_UNUSED_RESULT Maybe HasRealNamedCallbackProperty( + Local context, Local key); + + /** + * If result.IsEmpty() no real property was located in the prototype chain. + * This means interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetRealNamedPropertyInPrototypeChain( + Local context, Local key); + + /** + * Gets the property attributes of a real property in the prototype chain, + * which can be None or any combination of ReadOnly, DontEnum and DontDelete. + * Interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT Maybe + GetRealNamedPropertyAttributesInPrototypeChain(Local context, + Local key); + + /** + * If result.IsEmpty() no real property was located on the object or + * in the prototype chain. + * This means interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetRealNamedProperty( + Local context, Local key); + + /** + * Gets the property attributes of a real property which can be + * None or any combination of ReadOnly, DontEnum and DontDelete. + * Interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT Maybe GetRealNamedPropertyAttributes( + Local context, Local key); + + /** Tests for a named lookup interceptor.*/ + bool HasNamedLookupInterceptor() const; + + /** Tests for an index lookup interceptor.*/ + bool HasIndexedLookupInterceptor() const; + + /** + * Returns the identity hash for this object. The current implementation + * uses a hidden property on the object to store the identity hash. + * + * The return value will never be 0. Also, it is not guaranteed to be + * unique. + */ + int GetIdentityHash(); + + /** + * Clone this object with a fast but shallow copy. Values will point + * to the same values as the original object. + */ + // TODO(dcarney): take an isolate and optionally bail out? + Local Clone(); + + /** + * Returns the context in which the object was created. + */ + V8_DEPRECATE_SOON("Use MaybeLocal GetCreationContext()") + Local CreationContext(); + MaybeLocal GetCreationContext(); + + /** Same as above, but works for Persistents */ + V8_DEPRECATE_SOON( + "Use MaybeLocal GetCreationContext(const " + "PersistentBase& object)") + static Local CreationContext(const PersistentBase& object); + V8_INLINE static MaybeLocal GetCreationContext( + const PersistentBase& object) { + return object.val_->GetCreationContext(); + } + + /** + * Checks whether a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * When an Object is callable this method returns true. + */ + bool IsCallable() const; + + /** + * True if this object is a constructor. + */ + bool IsConstructor() const; + + /** + * True if this object can carry information relevant to the embedder in its + * embedder fields, false otherwise. This is generally true for objects + * constructed through function templates but also holds for other types where + * V8 automatically adds internal fields at compile time, such as e.g. + * v8::ArrayBuffer. + */ + bool IsApiWrapper() const; + + /** + * True if this object was created from an object template which was marked + * as undetectable. See v8::ObjectTemplate::MarkAsUndetectable for more + * information. + */ + bool IsUndetectable() const; + + /** + * Call an Object as a function if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + */ + V8_WARN_UNUSED_RESULT MaybeLocal CallAsFunction(Local context, + Local recv, + int argc, + Local argv[]); + + /** + * Call an Object as a constructor if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * Note: This method behaves like the Function::NewInstance method. + */ + V8_WARN_UNUSED_RESULT MaybeLocal CallAsConstructor( + Local context, int argc, Local argv[]); + + /** + * Return the isolate to which the Object belongs to. + */ + Isolate* GetIsolate(); + + /** + * If this object is a Set, Map, WeakSet or WeakMap, this returns a + * representation of the elements of this object as an array. + * If this object is a SetIterator or MapIterator, this returns all + * elements of the underlying collection, starting at the iterator's current + * position. + * For other types, this will return an empty MaybeLocal (without + * scheduling an exception). + */ + MaybeLocal PreviewEntries(bool* is_key_value); + + static Local New(Isolate* isolate); + + /** + * Creates a JavaScript object with the given properties, and + * a the given prototype_or_null (which can be any JavaScript + * value, and if it's null, the newly created object won't have + * a prototype at all). This is similar to Object.create(). + * All properties will be created as enumerable, configurable + * and writable properties. + */ + static Local New(Isolate* isolate, Local prototype_or_null, + Local* names, Local* values, + size_t length); + + V8_INLINE static Object* Cast(Value* obj); + + /** + * Support for TC39 "dynamic code brand checks" proposal. + * + * This API allows to query whether an object was constructed from a + * "code like" ObjectTemplate. + * + * See also: v8::ObjectTemplate::SetCodeLike + */ + bool IsCodeLike(Isolate* isolate) const; + + private: + Object(); + static void CheckCast(Value* obj); + Local SlowGetInternalField(int index); + void* SlowGetAlignedPointerFromInternalField(int index); +}; + +// --- Implementation --- + +Local Object::GetInternalField(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + // 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)) { + int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); + A value = I::ReadRawField(obj, offset); +#ifdef V8_COMPRESS_POINTERS + // We read the full pointer value and then decompress it in order to avoid + // dealing with potential endiannes issues. + value = I::DecompressTaggedAnyField(obj, static_cast(value)); +#endif + internal::Isolate* isolate = + internal::IsolateFromNeverReadOnlySpaceObject(obj); + A* result = HandleScope::CreateHandle(isolate, value); + return Local(reinterpret_cast(result)); + } +#endif + return SlowGetInternalField(index); +} + +void* Object::GetAlignedPointerFromInternalField(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + // 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_HEAP_SANDBOX + offset += I::kEmbedderDataSlotRawPayloadOffset; +#endif + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, offset, internal::kEmbedderDataSlotPayloadTag); + return reinterpret_cast(value); + } +#endif + return SlowGetAlignedPointerFromInternalField(index); +} + +Private* Private::Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return reinterpret_cast(data); +} + +Object* Object::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + +} // namespace v8 + +#endif // INCLUDE_V8_OBJECT_H_ diff --git a/deps/include/v8-persistent-handle.h b/deps/include/v8-persistent-handle.h new file mode 100644 index 000000000..a6c21268d --- /dev/null +++ b/deps/include/v8-persistent-handle.h @@ -0,0 +1,590 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PERSISTENT_HANDLE_H_ +#define INCLUDE_V8_PERSISTENT_HANDLE_H_ + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-weak-callback-info.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; +template +class PersistentValueMapBase; +template +class PersistentValueVector; +template +class Global; +template +class PersistentBase; +template +class PersistentValueMap; +class Value; + +namespace api_internal { +V8_EXPORT Value* Eternalize(v8::Isolate* isolate, Value* handle); +V8_EXPORT internal::Address* CopyGlobalReference(internal::Address* from); +V8_EXPORT void DisposeGlobal(internal::Address* global_handle); +V8_EXPORT void MakeWeak(internal::Address** location_addr); +V8_EXPORT void* ClearWeak(internal::Address* location); +V8_EXPORT void AnnotateStrongRetainer(internal::Address* location, + const char* label); +V8_EXPORT internal::Address* GlobalizeReference(internal::Isolate* isolate, + internal::Address* handle); +V8_EXPORT void MoveGlobalReference(internal::Address** from, + internal::Address** to); +} // namespace api_internal + +/** + * Eternal handles are set-once handles that live for the lifetime of the + * isolate. + */ +template +class Eternal { + public: + V8_INLINE Eternal() : val_(nullptr) {} + template + V8_INLINE Eternal(Isolate* isolate, Local handle) : val_(nullptr) { + Set(isolate, handle); + } + // Can only be safely called if already set. + V8_INLINE Local Get(Isolate* isolate) const { + // The eternal handle will never go away, so as with the roots, we don't + // even need to open a handle. + return Local(val_); + } + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + template + void Set(Isolate* isolate, Local handle) { + static_assert(std::is_base_of::value, "type check"); + val_ = reinterpret_cast( + api_internal::Eternalize(isolate, reinterpret_cast(*handle))); + } + + private: + T* val_; +}; + +namespace api_internal { +V8_EXPORT void MakeWeak(internal::Address* location, void* data, + WeakCallbackInfo::Callback weak_callback, + WeakCallbackType type); +} // namespace api_internal + +/** + * An object reference that is independent of any handle scope. Where + * a Local handle only lives as long as the HandleScope in which it was + * allocated, a PersistentBase handle remains valid until it is explicitly + * disposed using Reset(). + * + * A persistent handle contains a reference to a storage cell within + * the V8 engine which holds an object value and which is updated by + * the garbage collector whenever the object is moved. A new storage + * cell can be created using the constructor or PersistentBase::Reset and + * existing handles can be disposed using PersistentBase::Reset. + * + */ +template +class PersistentBase { + public: + /** + * If non-empty, destroy the underlying storage cell + * IsEmpty() will return true after this call. + */ + V8_INLINE void Reset(); + + /** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + template + V8_INLINE void Reset(Isolate* isolate, const Local& other); + + /** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + template + V8_INLINE void Reset(Isolate* isolate, const PersistentBase& other); + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + V8_INLINE void Empty() { val_ = 0; } + + V8_INLINE Local Get(Isolate* isolate) const { + return Local::New(isolate, *this); + } + + template + V8_INLINE bool operator==(const PersistentBase& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator==(const Local& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator!=(const PersistentBase& that) const { + return !operator==(that); + } + + template + V8_INLINE bool operator!=(const Local& that) const { + return !operator==(that); + } + + /** + * Install a finalization callback on this object. + * NOTE: There is no guarantee as to *when* or even *if* the callback is + * invoked. The invocation is performed solely on a best effort basis. + * As always, GC-based finalization should *not* be relied upon for any + * critical form of resource management! + * + * The callback is supposed to reset the handle. No further V8 API may be + * called in this callback. In case additional work involving V8 needs to be + * done, a second callback can be scheduled using + * WeakCallbackInfo::SetSecondPassCallback. + */ + template + V8_INLINE void SetWeak(P* parameter, + typename WeakCallbackInfo

::Callback callback, + WeakCallbackType type); + + /** + * 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(); + + template + V8_INLINE P* ClearWeak(); + + // TODO(dcarney): remove this. + V8_INLINE void ClearWeak() { ClearWeak(); } + + /** + * Annotates the strong handle with the given label, which is then used by the + * heap snapshot generator as a name of the edge from the root to the handle. + * The function does not take ownership of the label and assumes that the + * label is valid as long as the handle is valid. + */ + V8_INLINE void AnnotateStrongRetainer(const char* label); + + /** Returns true if the handle's reference is weak. */ + V8_INLINE bool IsWeak() const; + + /** + * Assigns a wrapper class ID to the handle. + */ + V8_INLINE void SetWrapperClassId(uint16_t class_id); + + /** + * Returns the class ID previously assigned to this handle or 0 if no class ID + * was previously assigned. + */ + V8_INLINE uint16_t WrapperClassId() const; + + PersistentBase(const PersistentBase& other) = delete; + void operator=(const PersistentBase&) = delete; + + private: + friend class Isolate; + friend class Utils; + template + friend class Local; + template + friend class Persistent; + template + friend class Global; + template + friend class PersistentBase; + template + friend class ReturnValue; + template + friend class PersistentValueMapBase; + template + friend class PersistentValueVector; + friend class Object; + + explicit V8_INLINE PersistentBase(T* val) : val_(val) {} + V8_INLINE static T* New(Isolate* isolate, T* that); + + T* val_; +}; + +/** + * Default traits for Persistent. This class does not allow + * use of the copy constructor or assignment operator. + * At present kResetInDestructor is not set, but that will change in a future + * version. + */ +template +class NonCopyablePersistentTraits { + public: + using NonCopyablePersistent = Persistent>; + static const bool kResetInDestructor = false; + template + V8_INLINE static void Copy(const Persistent& source, + NonCopyablePersistent* dest) { + static_assert(sizeof(S) < 0, + "NonCopyablePersistentTraits::Copy is not instantiable"); + } +}; + +/** + * Helper class traits to allow copying and assignment of Persistent. + * This will clone the contents of storage cell, but not any of the flags, etc. + */ +template +struct CopyablePersistentTraits { + using CopyablePersistent = Persistent>; + static const bool kResetInDestructor = true; + template + static V8_INLINE void Copy(const Persistent& source, + CopyablePersistent* dest) { + // do nothing, just allow copy + } +}; + +/** + * A PersistentBase which allows copy and assignment. + * + * Copy, assignment and destructor behavior is controlled by the traits + * class M. + * + * Note: Persistent class hierarchy is subject to future changes. + */ +template +class Persistent : public PersistentBase { + public: + /** + * A Persistent with no storage cell. + */ + V8_INLINE Persistent() : PersistentBase(nullptr) {} + /** + * Construct a Persistent from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Persistent(Isolate* isolate, Local that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + /** + * Construct a Persistent from a Persistent. + * When the Persistent is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Persistent(Isolate* isolate, const Persistent& that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + /** + * The copy constructors and assignment operator create a Persistent + * exactly as the Persistent constructor, but the Copy function from the + * traits class is called, allowing the setting of flags based on the + * copied Persistent. + */ + V8_INLINE Persistent(const Persistent& that) : PersistentBase(nullptr) { + Copy(that); + } + template + V8_INLINE Persistent(const Persistent& that) : PersistentBase(0) { + Copy(that); + } + V8_INLINE Persistent& operator=(const Persistent& that) { + Copy(that); + return *this; + } + template + V8_INLINE Persistent& operator=(const Persistent& that) { + Copy(that); + return *this; + } + /** + * The destructor will dispose the Persistent based on the + * kResetInDestructor flags in the traits class. Since not calling dispose + * can result in a memory leak, it is recommended to always set this flag. + */ + V8_INLINE ~Persistent() { + if (M::kResetInDestructor) this->Reset(); + } + + // TODO(dcarney): this is pretty useless, fix or remove + template + V8_INLINE static Persistent& Cast(const Persistent& that) { +#ifdef V8_ENABLE_CHECKS + // If we're going to perform the type check then we have to check + // that the handle isn't empty before doing the checked cast. + if (!that.IsEmpty()) T::Cast(*that); +#endif + return reinterpret_cast&>(const_cast&>(that)); + } + + // TODO(dcarney): this is pretty useless, fix or remove + template + V8_INLINE Persistent& As() const { + return Persistent::Cast(*this); + } + + private: + friend class Isolate; + friend class Utils; + template + friend class Local; + template + friend class Persistent; + template + friend class ReturnValue; + + explicit V8_INLINE Persistent(T* that) : PersistentBase(that) {} + V8_INLINE T* operator*() const { return this->val_; } + template + V8_INLINE void Copy(const Persistent& that); +}; + +/** + * A PersistentBase which has move semantics. + * + * Note: Persistent class hierarchy is subject to future changes. + */ +template +class Global : public PersistentBase { + public: + /** + * A Global with no storage cell. + */ + V8_INLINE Global() : PersistentBase(nullptr) {} + + /** + * Construct a Global from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Global(Isolate* isolate, Local that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Construct a Global from a PersistentBase. + * When the Persistent is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Global(Isolate* isolate, const PersistentBase& that) + : PersistentBase(PersistentBase::New(isolate, that.val_)) { + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Move constructor. + */ + V8_INLINE Global(Global&& other); + + V8_INLINE ~Global() { this->Reset(); } + + /** + * Move via assignment. + */ + template + V8_INLINE Global& operator=(Global&& rhs); + + /** + * Pass allows returning uniques from functions, etc. + */ + Global Pass() { return static_cast(*this); } + + /* + * For compatibility with Chromium's base::Bind (base::Passed). + */ + using MoveOnlyTypeForCPP03 = void; + + Global(const Global&) = delete; + void operator=(const Global&) = delete; + + private: + template + friend class ReturnValue; + V8_INLINE T* operator*() const { return this->val_; } +}; + +// UniquePersistent is an alias for Global for historical reason. +template +using UniquePersistent = Global; + +/** + * Interface for iterating through all the persistent handles in the heap. + */ +class V8_EXPORT PersistentHandleVisitor { + public: + virtual ~PersistentHandleVisitor() = default; + virtual void VisitPersistentHandle(Persistent* value, + uint16_t class_id) {} +}; + +template +T* PersistentBase::New(Isolate* isolate, T* that) { + if (that == nullptr) return nullptr; + internal::Address* p = reinterpret_cast(that); + return reinterpret_cast(api_internal::GlobalizeReference( + reinterpret_cast(isolate), p)); +} + +template +template +void Persistent::Copy(const Persistent& that) { + static_assert(std::is_base_of::value, "type check"); + this->Reset(); + if (that.IsEmpty()) return; + internal::Address* p = reinterpret_cast(that.val_); + this->val_ = reinterpret_cast(api_internal::CopyGlobalReference(p)); + M::Copy(that, this); +} + +template +bool PersistentBase::IsWeak() const { + using I = internal::Internals; + if (this->IsEmpty()) return false; + return I::GetNodeState(reinterpret_cast(this->val_)) == + I::kNodeStateIsWeakValue; +} + +template +void PersistentBase::Reset() { + if (this->IsEmpty()) return; + api_internal::DisposeGlobal(reinterpret_cast(this->val_)); + val_ = nullptr; +} + +/** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ +template +template +void PersistentBase::Reset(Isolate* isolate, const Local& other) { + static_assert(std::is_base_of::value, "type check"); + Reset(); + if (other.IsEmpty()) return; + this->val_ = New(isolate, other.val_); +} + +/** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ +template +template +void PersistentBase::Reset(Isolate* isolate, + const PersistentBase& other) { + static_assert(std::is_base_of::value, "type check"); + Reset(); + if (other.IsEmpty()) return; + this->val_ = New(isolate, other.val_); +} + +template +template +V8_INLINE void PersistentBase::SetWeak( + P* parameter, typename WeakCallbackInfo

::Callback callback, + WeakCallbackType type) { + using Callback = WeakCallbackInfo::Callback; +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + api_internal::MakeWeak(reinterpret_cast(this->val_), + parameter, reinterpret_cast(callback), type); +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +template +void PersistentBase::SetWeak() { + api_internal::MakeWeak(reinterpret_cast(&this->val_)); +} + +template +template +P* PersistentBase::ClearWeak() { + return reinterpret_cast(api_internal::ClearWeak( + reinterpret_cast(this->val_))); +} + +template +void PersistentBase::AnnotateStrongRetainer(const char* label) { + api_internal::AnnotateStrongRetainer( + reinterpret_cast(this->val_), label); +} + +template +void PersistentBase::SetWrapperClassId(uint16_t class_id) { + using I = internal::Internals; + if (this->IsEmpty()) return; + internal::Address* obj = reinterpret_cast(this->val_); + uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + *reinterpret_cast(addr) = class_id; +} + +template +uint16_t PersistentBase::WrapperClassId() const { + using I = internal::Internals; + if (this->IsEmpty()) return 0; + internal::Address* obj = reinterpret_cast(this->val_); + uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + return *reinterpret_cast(addr); +} + +template +Global::Global(Global&& other) : PersistentBase(other.val_) { + if (other.val_ != nullptr) { + api_internal::MoveGlobalReference( + reinterpret_cast(&other.val_), + reinterpret_cast(&this->val_)); + other.val_ = nullptr; + } +} + +template +template +Global& Global::operator=(Global&& rhs) { + static_assert(std::is_base_of::value, "type check"); + if (this != &rhs) { + this->Reset(); + if (rhs.val_ != nullptr) { + this->val_ = rhs.val_; + api_internal::MoveGlobalReference( + reinterpret_cast(&rhs.val_), + reinterpret_cast(&this->val_)); + rhs.val_ = nullptr; + } + } + return *this; +} + +} // namespace v8 + +#endif // INCLUDE_V8_PERSISTENT_HANDLE_H_ diff --git a/deps/include/v8-platform.h b/deps/include/v8-platform.h index e27d26cb6..e60e1757b 100644 --- a/deps/include/v8-platform.h +++ b/deps/include/v8-platform.h @@ -181,9 +181,8 @@ class JobDelegate { /** * Returns true if the current task is called from the thread currently * running JobHandle::Join(). - * TODO(etiennep): Make pure virtual once custom embedders implement it. */ - virtual bool IsJoiningThread() const { return false; } + virtual bool IsJoiningThread() const = 0; }; /** @@ -220,19 +219,14 @@ class JobHandle { * Forces all existing workers to yield ASAP but doesn’t wait for them. * Warning, this is dangerous if the Job's callback is bound to or has access * to state which may be deleted after this call. - * TODO(etiennep): Cleanup once implemented by all embedders. */ - virtual void CancelAndDetach() { Cancel(); } + virtual void CancelAndDetach() = 0; /** * Returns true if there's any work pending or any worker running. */ virtual bool IsActive() = 0; - // TODO(etiennep): Clean up once all overrides are removed. - V8_DEPRECATED("Use !IsActive() instead.") - virtual bool IsCompleted() { return !IsActive(); } - /** * Returns true if associated with a Job and other methods may be called. * Returns false after Join() or Cancel() was called. This may return true @@ -240,10 +234,6 @@ class JobHandle { */ virtual bool IsValid() = 0; - // TODO(etiennep): Clean up once all overrides are removed. - V8_DEPRECATED("Use IsValid() instead.") - virtual bool IsRunning() { return IsValid(); } - /** * Returns true if job priority can be changed. */ @@ -272,10 +262,6 @@ class JobTask { * it must not call back any JobHandle methods. */ virtual size_t GetMaxConcurrency(size_t worker_count) const = 0; - - // TODO(1114823): Clean up once all overrides are removed. - V8_DEPRECATED("Use the version that takes |worker_count|.") - virtual size_t GetMaxConcurrency() const { return 0; } }; /** @@ -408,7 +394,6 @@ class PageAllocator { kNoAccess, kRead, kReadWrite, - // TODO(hpayer): Remove this flag. Memory should never be rwx. kReadWriteExecute, kReadExecute, // Set this when reserving memory that will later require kReadWriteExecute @@ -445,10 +430,28 @@ class PageAllocator { /** * Frees memory in the given [address, address + size) range. address and size * should be operating system page-aligned. The next write to this - * memory area brings the memory transparently back. + * memory area brings the memory transparently back. This should be treated as + * a hint to the OS that the pages are no longer needed. It does not guarantee + * that the pages will be discarded immediately or at all. */ virtual bool DiscardSystemPages(void* address, size_t size) { return true; } + /** + * Decommits any wired memory pages in the given range, allowing the OS to + * reclaim them, and marks the region as inacessible (kNoAccess). The address + * range stays reserved and can be accessed again later by changing its + * permissions. However, in that case the memory content is guaranteed to be + * zero-initialized again. The memory must have been previously allocated by a + * call to AllocatePages. Returns true on success, false otherwise. + */ +#ifdef V8_VIRTUAL_MEMORY_CAGE + // Implementing this API is required when the virtual memory cage is enabled. + virtual bool DecommitPages(void* address, size_t size) = 0; +#else + // Otherwise, it is optional for now. + virtual bool DecommitPages(void* address, size_t size) { return false; } +#endif + /** * INTERNAL ONLY: This interface has not been stabilised and may change * without notice from one release to another without being deprecated first. @@ -513,6 +516,18 @@ class PageAllocator { virtual bool CanAllocateSharedPages() { return false; } }; +/** + * V8 Allocator used for allocating zone backings. + */ +class ZoneBackingAllocator { + public: + using MallocFn = void* (*)(size_t); + using FreeFn = void (*)(void*); + + virtual MallocFn GetMallocFn() const { return ::malloc; } + virtual FreeFn GetFreeFn() const { return ::free; } +}; + /** * V8 Platform abstraction layer. * @@ -531,6 +546,14 @@ class Platform { return nullptr; } + /** + * Allows the embedder to specify a custom allocator used for zones. + */ + virtual ZoneBackingAllocator* GetZoneBackingAllocator() { + static ZoneBackingAllocator default_allocator; + return &default_allocator; + } + /** * Enables the embedder to respond in cases where V8 can't allocate large * blocks of memory. V8 retries the failed allocation once after calling this diff --git a/deps/include/v8-primitive-object.h b/deps/include/v8-primitive-object.h new file mode 100644 index 000000000..573932d07 --- /dev/null +++ b/deps/include/v8-primitive-object.h @@ -0,0 +1,118 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PRIMITIVE_OBJECT_H_ +#define INCLUDE_V8_PRIMITIVE_OBJECT_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +/** + * A Number object (ECMA-262, 4.3.21). + */ +class V8_EXPORT NumberObject : public Object { + public: + static Local New(Isolate* isolate, double value); + + double ValueOf() const; + + V8_INLINE static NumberObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A BigInt object (https://tc39.github.io/proposal-bigint) + */ +class V8_EXPORT BigIntObject : public Object { + public: + static Local New(Isolate* isolate, int64_t value); + + Local ValueOf() const; + + V8_INLINE static BigIntObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A Boolean object (ECMA-262, 4.3.15). + */ +class V8_EXPORT BooleanObject : public Object { + public: + static Local New(Isolate* isolate, bool value); + + bool ValueOf() const; + + V8_INLINE static BooleanObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A String object (ECMA-262, 4.3.18). + */ +class V8_EXPORT StringObject : public Object { + public: + static Local New(Isolate* isolate, Local value); + + Local ValueOf() const; + + V8_INLINE static StringObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A Symbol object (ECMA-262 edition 6). + */ +class V8_EXPORT SymbolObject : public Object { + public: + static Local New(Isolate* isolate, Local value); + + Local ValueOf() const; + + V8_INLINE static SymbolObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_PRIMITIVE_OBJECT_H_ diff --git a/deps/include/v8-primitive.h b/deps/include/v8-primitive.h new file mode 100644 index 000000000..59d959da0 --- /dev/null +++ b/deps/include/v8-primitive.h @@ -0,0 +1,858 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PRIMITIVE_H_ +#define INCLUDE_V8_PRIMITIVE_H_ + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; +class String; + +namespace internal { +class ExternalString; +class ScopedExternalStringLock; +} // namespace internal + +/** + * The superclass of primitive values. See ECMA-262 4.3.2. + */ +class V8_EXPORT Primitive : public Value {}; + +/** + * A primitive boolean value (ECMA-262, 4.3.14). Either the true + * or false value. + */ +class V8_EXPORT Boolean : public Primitive { + public: + bool Value() const; + V8_INLINE static Boolean* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + V8_INLINE static Local New(Isolate* isolate, bool value); + + private: + static void CheckCast(v8::Data* that); +}; + +/** + * An array to hold Primitive values. This is used by the embedder to + * pass host defined options to the ScriptOptions during compilation. + * + * This is passed back to the embedder as part of + * HostImportModuleDynamicallyCallback for module loading. + */ +class V8_EXPORT PrimitiveArray { + public: + static Local New(Isolate* isolate, int length); + int Length() const; + void Set(Isolate* isolate, int index, Local item); + Local Get(Isolate* isolate, int index); +}; + +/** + * A superclass for symbols and strings. + */ +class V8_EXPORT Name : public Primitive { + public: + /** + * Returns the identity hash for this object. The current implementation + * uses an inline property on the object to store the identity hash. + * + * The return value will never be 0. Also, it is not guaranteed to be + * unique. + */ + int GetIdentityHash(); + + V8_INLINE static Name* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + static void CheckCast(Data* that); +}; + +/** + * A flag describing different modes of string creation. + * + * Aside from performance implications there are no differences between the two + * creation modes. + */ +enum class NewStringType { + /** + * Create a new string, always allocating new storage memory. + */ + kNormal, + + /** + * Acts as a hint that the string should be created in the + * old generation heap space and be deduplicated if an identical string + * already exists. + */ + kInternalized +}; + +/** + * A JavaScript string value (ECMA-262, 4.3.17). + */ +class V8_EXPORT String : public Name { + public: + static constexpr int kMaxLength = + internal::kApiSystemPointerSize == 4 ? (1 << 28) - 16 : (1 << 29) - 24; + + enum Encoding { + UNKNOWN_ENCODING = 0x1, + TWO_BYTE_ENCODING = 0x0, + ONE_BYTE_ENCODING = 0x8 + }; + /** + * Returns the number of characters (UTF-16 code units) in this string. + */ + int Length() const; + + /** + * Returns the number of bytes in the UTF-8 encoded + * representation of this string. + */ + int Utf8Length(Isolate* isolate) const; + + /** + * Returns whether this string is known to contain only one byte data, + * i.e. ISO-8859-1 code points. + * Does not read the string. + * False negatives are possible. + */ + bool IsOneByte() const; + + /** + * Returns whether this string contain only one byte data, + * i.e. ISO-8859-1 code points. + * Will read the entire string in some cases. + */ + bool ContainsOnlyOneByte() const; + + /** + * Write the contents of the string to an external buffer. + * If no arguments are given, expects the buffer to be large + * enough to hold the entire string and NULL terminator. Copies + * the contents of the string and the NULL terminator into the + * buffer. + * + * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop + * before the end of the buffer. + * + * Copies up to length characters into the output buffer. + * Only null-terminates if there is enough space in the buffer. + * + * \param buffer The buffer into which the string will be copied. + * \param start The starting position within the string at which + * copying begins. + * \param length The number of characters to copy from the string. For + * WriteUtf8 the number of bytes in the buffer. + * \param nchars_ref The number of characters written, can be NULL. + * \param options Various options that might affect performance of this or + * subsequent operations. + * \return The number of characters copied to the buffer excluding the null + * terminator. For WriteUtf8: The number of bytes copied to the buffer + * including the null terminator (if written). + */ + enum WriteOptions { + NO_OPTIONS = 0, + HINT_MANY_WRITES_EXPECTED = 1, + NO_NULL_TERMINATION = 2, + PRESERVE_ONE_BYTE_NULL = 4, + // Used by WriteUtf8 to replace orphan surrogate code units with the + // unicode replacement character. Needs to be set to guarantee valid UTF-8 + // output. + REPLACE_INVALID_UTF8 = 8 + }; + + // 16-bit character codes. + int Write(Isolate* isolate, uint16_t* buffer, int start = 0, int length = -1, + int options = NO_OPTIONS) const; + // One byte characters. + int WriteOneByte(Isolate* isolate, uint8_t* buffer, int start = 0, + int length = -1, int options = NO_OPTIONS) const; + // UTF-8 encoded characters. + int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, + int* nchars_ref = nullptr, int options = NO_OPTIONS) const; + + /** + * A zero length string. + */ + V8_INLINE static Local Empty(Isolate* isolate); + + /** + * Returns true if the string is external. + */ + bool IsExternal() const; + + /** + * Returns true if the string is both external and two-byte. + */ + bool IsExternalTwoByte() const; + + /** + * Returns true if the string is both external and one-byte. + */ + bool IsExternalOneByte() const; + + class V8_EXPORT ExternalStringResourceBase { + public: + virtual ~ExternalStringResourceBase() = default; + + /** + * If a string is cacheable, the value returned by + * ExternalStringResource::data() may be cached, otherwise it is not + * expected to be stable beyond the current top-level task. + */ + virtual bool IsCacheable() const { return true; } + + // Disallow copying and assigning. + ExternalStringResourceBase(const ExternalStringResourceBase&) = delete; + void operator=(const ExternalStringResourceBase&) = delete; + + protected: + ExternalStringResourceBase() = default; + + /** + * Internally V8 will call this Dispose method when the external string + * resource is no longer needed. The default implementation will use the + * delete operator. This method can be overridden in subclasses to + * control how allocated external string resources are disposed. + */ + virtual void Dispose() { delete this; } + + /** + * For a non-cacheable string, the value returned by + * |ExternalStringResource::data()| has to be stable between |Lock()| and + * |Unlock()|, that is the string must behave as is |IsCacheable()| returned + * true. + * + * These two functions must be thread-safe, and can be called from anywhere. + * They also must handle lock depth, in the sense that each can be called + * several times, from different threads, and unlocking should only happen + * when the balance of Lock() and Unlock() calls is 0. + */ + virtual void Lock() const {} + + /** + * Unlocks the string. + */ + virtual void Unlock() const {} + + private: + friend class internal::ExternalString; + friend class v8::String; + friend class internal::ScopedExternalStringLock; + }; + + /** + * An ExternalStringResource is a wrapper around a two-byte string + * buffer that resides outside V8's heap. Implement an + * ExternalStringResource to manage the life cycle of the underlying + * buffer. Note that the string data must be immutable. + */ + class V8_EXPORT ExternalStringResource : public ExternalStringResourceBase { + public: + /** + * Override the destructor to manage the life cycle of the underlying + * buffer. + */ + ~ExternalStringResource() override = default; + + /** + * The string data from the underlying buffer. If the resource is cacheable + * then data() must return the same value for all invocations. + */ + virtual const uint16_t* data() const = 0; + + /** + * The length of the string. That is, the number of two-byte characters. + */ + virtual size_t length() const = 0; + + /** + * Returns the cached data from the underlying buffer. This method can be + * called only for cacheable resources (i.e. IsCacheable() == true) and only + * after UpdateDataCache() was called. + */ + const uint16_t* cached_data() const { + CheckCachedDataInvariants(); + return cached_data_; + } + + /** + * Update {cached_data_} with the data from the underlying buffer. This can + * be called only for cacheable resources. + */ + void UpdateDataCache(); + + protected: + ExternalStringResource() = default; + + private: + void CheckCachedDataInvariants() const; + + const uint16_t* cached_data_ = nullptr; + }; + + /** + * An ExternalOneByteStringResource is a wrapper around an one-byte + * string buffer that resides outside V8's heap. Implement an + * ExternalOneByteStringResource to manage the life cycle of the + * underlying buffer. Note that the string data must be immutable + * and that the data must be Latin-1 and not UTF-8, which would require + * special treatment internally in the engine and do not allow efficient + * indexing. Use String::New or convert to 16 bit data for non-Latin1. + */ + + class V8_EXPORT ExternalOneByteStringResource + : public ExternalStringResourceBase { + public: + /** + * Override the destructor to manage the life cycle of the underlying + * buffer. + */ + ~ExternalOneByteStringResource() override = default; + + /** + * The string data from the underlying buffer. If the resource is cacheable + * then data() must return the same value for all invocations. + */ + virtual const char* data() const = 0; + + /** The number of Latin-1 characters in the string.*/ + virtual size_t length() const = 0; + + /** + * Returns the cached data from the underlying buffer. If the resource is + * uncacheable or if UpdateDataCache() was not called before, it has + * undefined behaviour. + */ + const char* cached_data() const { + CheckCachedDataInvariants(); + return cached_data_; + } + + /** + * Update {cached_data_} with the data from the underlying buffer. This can + * be called only for cacheable resources. + */ + void UpdateDataCache(); + + protected: + ExternalOneByteStringResource() = default; + + private: + void CheckCachedDataInvariants() const; + + const char* cached_data_ = nullptr; + }; + + /** + * If the string is an external string, return the ExternalStringResourceBase + * regardless of the encoding, otherwise return NULL. The encoding of the + * string is returned in encoding_out. + */ + V8_INLINE ExternalStringResourceBase* GetExternalStringResourceBase( + Encoding* encoding_out) const; + + /** + * Get the ExternalStringResource for an external string. Returns + * NULL if IsExternal() doesn't return true. + */ + V8_INLINE ExternalStringResource* GetExternalStringResource() const; + + /** + * Get the ExternalOneByteStringResource for an external one-byte string. + * Returns NULL if IsExternalOneByte() doesn't return true. + */ + const ExternalOneByteStringResource* GetExternalOneByteStringResource() const; + + V8_INLINE static String* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + /** + * Allocates a new string from a UTF-8 literal. This is equivalent to calling + * String::NewFromUtf(isolate, "...").ToLocalChecked(), but without the check + * overhead. + * + * When called on a string literal containing '\0', the inferred length is the + * length of the input array minus 1 (for the final '\0') and not the value + * returned by strlen. + **/ + template + static V8_WARN_UNUSED_RESULT Local NewFromUtf8Literal( + Isolate* isolate, const char (&literal)[N], + NewStringType type = NewStringType::kNormal) { + static_assert(N <= kMaxLength, "String is too long"); + return NewFromUtf8Literal(isolate, literal, type, N - 1); + } + + /** Allocates a new string from UTF-8 data. Only returns an empty value when + * length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromUtf8( + Isolate* isolate, const char* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** Allocates a new string from Latin-1 data. Only returns an empty value + * when length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromOneByte( + Isolate* isolate, const uint8_t* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** Allocates a new string from UTF-16 data. Only returns an empty value when + * length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromTwoByte( + Isolate* isolate, const uint16_t* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** + * Creates a new string by concatenating the left and the right strings + * passed in as parameters. + */ + static Local Concat(Isolate* isolate, Local left, + Local right); + + /** + * Creates a new external string using the data defined in the given + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed by calling its Dispose method. The caller of + * this function should not otherwise delete or modify the resource. Neither + * should the underlying buffer be deallocated or modified except through the + * destructor of the external string resource. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewExternalTwoByte( + Isolate* isolate, ExternalStringResource* resource); + + /** + * Associate an external string resource with this string by transforming it + * in place so that existing references to this string in the JavaScript heap + * will use the external string resource. The external string resource's + * character contents need to be equivalent to this string. + * Returns true if the string has been changed to be an external string. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. + */ + bool MakeExternal(ExternalStringResource* resource); + + /** + * Creates a new external string using the one-byte data defined in the given + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed by calling its Dispose method. The caller of + * this function should not otherwise delete or modify the resource. Neither + * should the underlying buffer be deallocated or modified except through the + * destructor of the external string resource. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewExternalOneByte( + Isolate* isolate, ExternalOneByteStringResource* resource); + + /** + * Associate an external string resource with this string by transforming it + * in place so that existing references to this string in the JavaScript heap + * will use the external string resource. The external string resource's + * character contents need to be equivalent to this string. + * Returns true if the string has been changed to be an external string. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. + */ + bool MakeExternal(ExternalOneByteStringResource* resource); + + /** + * Returns true if this string can be made external. + */ + bool CanMakeExternal() const; + + /** + * Returns true if the strings values are equal. Same as JS ==/===. + */ + bool StringEquals(Local str) const; + + /** + * Converts an object to a UTF-8-encoded character array. Useful if + * you want to print the object. If conversion to a string fails + * (e.g. due to an exception in the toString() method of the object) + * then the length() method returns 0 and the * operator returns + * NULL. + */ + class V8_EXPORT Utf8Value { + public: + Utf8Value(Isolate* isolate, Local obj); + ~Utf8Value(); + char* operator*() { return str_; } + const char* operator*() const { return str_; } + int length() const { return length_; } + + // Disallow copying and assigning. + Utf8Value(const Utf8Value&) = delete; + void operator=(const Utf8Value&) = delete; + + private: + char* str_; + int length_; + }; + + /** + * Converts an object to a two-byte (UTF-16-encoded) string. + * If conversion to a string fails (eg. due to an exception in the toString() + * method of the object) then the length() method returns 0 and the * operator + * returns NULL. + */ + class V8_EXPORT Value { + public: + Value(Isolate* isolate, Local obj); + ~Value(); + uint16_t* operator*() { return str_; } + const uint16_t* operator*() const { return str_; } + int length() const { return length_; } + + // Disallow copying and assigning. + Value(const Value&) = delete; + void operator=(const Value&) = delete; + + private: + uint16_t* str_; + int length_; + }; + + private: + void VerifyExternalStringResourceBase(ExternalStringResourceBase* v, + Encoding encoding) const; + void VerifyExternalStringResource(ExternalStringResource* val) const; + ExternalStringResource* GetExternalStringResourceSlow() const; + ExternalStringResourceBase* GetExternalStringResourceBaseSlow( + String::Encoding* encoding_out) const; + + static Local NewFromUtf8Literal(Isolate* isolate, + const char* literal, + NewStringType type, int length); + + static void CheckCast(v8::Data* that); +}; + +// Zero-length string specialization (templated string size includes +// terminator). +template <> +inline V8_WARN_UNUSED_RESULT Local String::NewFromUtf8Literal( + Isolate* isolate, const char (&literal)[1], NewStringType type) { + return String::Empty(isolate); +} + +/** + * Interface for iterating through all external resources in the heap. + */ +class V8_EXPORT ExternalResourceVisitor { + public: + virtual ~ExternalResourceVisitor() = default; + virtual void VisitExternalString(Local string) {} +}; + +/** + * A JavaScript symbol (ECMA-262 edition 6) + */ +class V8_EXPORT Symbol : public Name { + public: + /** + * Returns the description string of the symbol, or undefined if none. + */ + V8_DEPRECATE_SOON("Use Symbol::Description(isolate)") + Local Description() const; + Local Description(Isolate* isolate) const; + + /** + * Create a symbol. If description is not empty, it will be used as the + * description. + */ + static Local New(Isolate* isolate, + Local description = Local()); + + /** + * Access global symbol registry. + * Note that symbols created this way are never collected, so + * they should only be used for statically fixed properties. + * Also, there is only one global name space for the descriptions used as + * keys. + * To minimize the potential for clashes, use qualified names as keys. + */ + static Local For(Isolate* isolate, Local description); + + /** + * Retrieve a global symbol. Similar to |For|, but using a separate + * registry that is not accessible by (and cannot clash with) JavaScript code. + */ + static Local ForApi(Isolate* isolate, Local description); + + // Well-known symbols + static Local GetAsyncIterator(Isolate* isolate); + static Local GetHasInstance(Isolate* isolate); + static Local GetIsConcatSpreadable(Isolate* isolate); + static Local GetIterator(Isolate* isolate); + static Local GetMatch(Isolate* isolate); + static Local GetReplace(Isolate* isolate); + static Local GetSearch(Isolate* isolate); + static Local GetSplit(Isolate* isolate); + static Local GetToPrimitive(Isolate* isolate); + static Local GetToStringTag(Isolate* isolate); + static Local GetUnscopables(Isolate* isolate); + + V8_INLINE static Symbol* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Symbol(); + static void CheckCast(Data* that); +}; + +/** + * A JavaScript number value (ECMA-262, 4.3.20) + */ +class V8_EXPORT Number : public Primitive { + public: + double Value() const; + static Local New(Isolate* isolate, double value); + V8_INLINE static Number* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Number(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a signed integer. + */ +class V8_EXPORT Integer : public Number { + public: + static Local New(Isolate* isolate, int32_t value); + static Local NewFromUnsigned(Isolate* isolate, uint32_t value); + int64_t Value() const; + V8_INLINE static Integer* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Integer(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a 32-bit signed integer. + */ +class V8_EXPORT Int32 : public Integer { + public: + int32_t Value() const; + V8_INLINE static Int32* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Int32(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a 32-bit unsigned integer. + */ +class V8_EXPORT Uint32 : public Integer { + public: + uint32_t Value() const; + V8_INLINE static Uint32* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Uint32(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript BigInt value (https://tc39.github.io/proposal-bigint) + */ +class V8_EXPORT BigInt : public Primitive { + public: + static Local New(Isolate* isolate, int64_t value); + static Local NewFromUnsigned(Isolate* isolate, uint64_t value); + /** + * Creates a new BigInt object using a specified sign bit and a + * specified list of digits/words. + * The resulting number is calculated as: + * + * (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) + */ + static MaybeLocal NewFromWords(Local context, int sign_bit, + int word_count, const uint64_t* words); + + /** + * Returns the value of this BigInt as an unsigned 64-bit integer. + * If `lossless` is provided, it will reflect whether the return value was + * truncated or wrapped around. In particular, it is set to `false` if this + * BigInt is negative. + */ + uint64_t Uint64Value(bool* lossless = nullptr) const; + + /** + * Returns the value of this BigInt as a signed 64-bit integer. + * If `lossless` is provided, it will reflect whether this BigInt was + * truncated or not. + */ + int64_t Int64Value(bool* lossless = nullptr) const; + + /** + * Returns the number of 64-bit words needed to store the result of + * ToWordsArray(). + */ + int WordCount() const; + + /** + * Writes the contents of this BigInt to a specified memory location. + * `sign_bit` must be provided and will be set to 1 if this BigInt is + * negative. + * `*word_count` has to be initialized to the length of the `words` array. + * Upon return, it will be set to the actual number of words that would + * be needed to store this BigInt (i.e. the return value of `WordCount()`). + */ + void ToWordsArray(int* sign_bit, int* word_count, uint64_t* words) const; + + V8_INLINE static BigInt* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + BigInt(); + static void CheckCast(v8::Data* that); +}; + +Local String::Empty(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kEmptyStringRootIndex); + return Local(reinterpret_cast(slot)); +} + +String::ExternalStringResource* String::GetExternalStringResource() const { + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + + ExternalStringResource* result; + if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) { + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, + internal::kExternalStringResourceTag); + result = reinterpret_cast(value); + } else { + result = GetExternalStringResourceSlow(); + } +#ifdef V8_ENABLE_CHECKS + VerifyExternalStringResource(result); +#endif + return result; +} + +String::ExternalStringResourceBase* String::GetExternalStringResourceBase( + String::Encoding* encoding_out) const { + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + int type = I::GetInstanceType(obj) & I::kFullStringRepresentationMask; + *encoding_out = static_cast(type & I::kStringEncodingMask); + ExternalStringResourceBase* resource; + if (type == I::kExternalOneByteRepresentationTag || + type == I::kExternalTwoByteRepresentationTag) { + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, + internal::kExternalStringResourceTag); + resource = reinterpret_cast(value); + } else { + resource = GetExternalStringResourceBaseSlow(encoding_out); + } +#ifdef V8_ENABLE_CHECKS + VerifyExternalStringResourceBase(resource, *encoding_out); +#endif + return resource; +} + +// --- Statics --- + +V8_INLINE Local Undefined(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kUndefinedValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local Null(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kNullValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local True(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kTrueValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local False(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kFalseValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +Local Boolean::New(Isolate* isolate, bool value) { + return value ? True(isolate) : False(isolate); +} + +} // namespace v8 + +#endif // INCLUDE_V8_PRIMITIVE_H_ diff --git a/deps/include/v8-profiler.h b/deps/include/v8-profiler.h index 85d3f8a48..f2354cac3 100644 --- a/deps/include/v8-profiler.h +++ b/deps/include/v8-profiler.h @@ -11,7 +11,9 @@ #include #include -#include "v8.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) /** * Profiler support for the V8 JavaScript engine. @@ -20,6 +22,7 @@ namespace v8 { class HeapGraphNode; struct HeapStatsUpdate; +class Object; using NativeObject = void*; using SnapshotObjectId = uint32_t; @@ -289,8 +292,8 @@ class V8_EXPORT CpuProfilingOptions { * interval, set via SetSamplingInterval(). If * zero, the sampling interval will be equal to * the profiler's sampling interval. - * \param filter_context Deprecated option to filter by context, currently a - * no-op. + * \param filter_context If specified, profiles will only contain frames + * using this context. Other frames will be elided. */ CpuProfilingOptions( CpuProfilingMode mode = kLeafNodeLineNumbers, @@ -304,9 +307,13 @@ class V8_EXPORT CpuProfilingOptions { private: friend class internal::CpuProfile; + bool has_filter_context() const { return !filter_context_.IsEmpty(); } + void* raw_filter_context() const; + CpuProfilingMode mode_; unsigned max_samples_; int sampling_interval_us_; + CopyablePersistentTraits::CopyablePersistent filter_context_; }; /** @@ -492,7 +499,7 @@ class V8_EXPORT HeapGraphNode { /** * An interface for exporting data from V8, using "push" model. */ -class V8_EXPORT OutputStream { // NOLINT +class V8_EXPORT OutputStream { public: enum WriteResult { kContinue = 0, @@ -519,7 +526,6 @@ class V8_EXPORT OutputStream { // NOLINT } }; - /** * HeapSnapshots record the state of the JS heap at some moment. */ @@ -586,7 +592,7 @@ class V8_EXPORT HeapSnapshot { * An interface for reporting progress and controlling long-running * activities. */ -class V8_EXPORT ActivityControl { // NOLINT +class V8_EXPORT ActivityControl { public: enum ControlOption { kContinue = 0, @@ -600,7 +606,6 @@ class V8_EXPORT ActivityControl { // NOLINT virtual ControlOption ReportProgressValue(int done, int total) = 0; }; - /** * AllocationProfile is a sampled profile of allocations done by the program. * This is structured as a call-graph. @@ -900,7 +905,8 @@ class V8_EXPORT HeapProfiler { const HeapSnapshot* TakeHeapSnapshot( ActivityControl* control = nullptr, ObjectNameResolver* global_object_name_resolver = nullptr, - bool treat_global_objects_as_roots = true); + bool treat_global_objects_as_roots = true, + bool capture_numeric_value = false); /** * Starts tracking of heap objects population statistics. After calling diff --git a/deps/include/v8-promise.h b/deps/include/v8-promise.h new file mode 100644 index 000000000..9da8e4b4e --- /dev/null +++ b/deps/include/v8-promise.h @@ -0,0 +1,174 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PROMISE_H_ +#define INCLUDE_V8_PROMISE_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +#ifndef V8_PROMISE_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_PROMISE_INTERNAL_FIELD_COUNT 0 +#endif + +/** + * An instance of the built-in Promise constructor (ES6 draft). + */ +class V8_EXPORT Promise : public Object { + public: + /** + * State of the promise. Each value corresponds to one of the possible values + * of the [[PromiseState]] field. + */ + enum PromiseState { kPending, kFulfilled, kRejected }; + + class V8_EXPORT Resolver : public Object { + public: + /** + * Create a new resolver, along with an associated promise in pending state. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal New( + Local context); + + /** + * Extract the associated promise. + */ + Local GetPromise(); + + /** + * Resolve/reject the associated promise with a given value. + * Ignored if the promise is no longer pending. + */ + V8_WARN_UNUSED_RESULT Maybe Resolve(Local context, + Local value); + + V8_WARN_UNUSED_RESULT Maybe Reject(Local context, + Local value); + + V8_INLINE static Resolver* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Resolver(); + static void CheckCast(Value* obj); + }; + + /** + * Register a resolution/rejection handler with a promise. + * The handler is given the respective resolution/rejection value as + * an argument. If the promise is already resolved/rejected, the handler is + * invoked at the end of turn. + */ + V8_WARN_UNUSED_RESULT MaybeLocal Catch(Local context, + Local handler); + + V8_WARN_UNUSED_RESULT MaybeLocal Then(Local context, + Local handler); + + V8_WARN_UNUSED_RESULT MaybeLocal Then(Local context, + Local on_fulfilled, + Local on_rejected); + + /** + * Returns true if the promise has at least one derived promise, and + * therefore resolve/reject handlers (including default handler). + */ + bool HasHandler() const; + + /** + * Returns the content of the [[PromiseResult]] field. The Promise must not + * be pending. + */ + Local Result(); + + /** + * Returns the value of the [[PromiseState]] field. + */ + PromiseState State(); + + /** + * Marks this promise as handled to avoid reporting unhandled rejections. + */ + void MarkAsHandled(); + + /** + * Marks this promise as silent to prevent pausing the debugger when the + * promise is rejected. + */ + void MarkAsSilent(); + + V8_INLINE static Promise* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kEmbedderFieldCount = V8_PROMISE_INTERNAL_FIELD_COUNT; + + private: + Promise(); + static void CheckCast(Value* obj); +}; + +/** + * PromiseHook with type kInit is called when a new promise is + * created. When a new promise is created as part of the chain in the + * case of Promise.then or in the intermediate promises created by + * Promise.{race, all}/AsyncFunctionAwait, we pass the parent promise + * otherwise we pass undefined. + * + * PromiseHook with type kResolve is called at the beginning of + * resolve or reject function defined by CreateResolvingFunctions. + * + * PromiseHook with type kBefore is called at the beginning of the + * PromiseReactionJob. + * + * PromiseHook with type kAfter is called right at the end of the + * PromiseReactionJob. + */ +enum class PromiseHookType { kInit, kResolve, kBefore, kAfter }; + +using PromiseHook = void (*)(PromiseHookType type, Local promise, + Local parent); + +// --- Promise Reject Callback --- +enum PromiseRejectEvent { + kPromiseRejectWithNoHandler = 0, + kPromiseHandlerAddedAfterReject = 1, + kPromiseRejectAfterResolved = 2, + kPromiseResolveAfterResolved = 3, +}; + +class PromiseRejectMessage { + public: + PromiseRejectMessage(Local promise, PromiseRejectEvent event, + Local value) + : promise_(promise), event_(event), value_(value) {} + + V8_INLINE Local GetPromise() const { return promise_; } + V8_INLINE PromiseRejectEvent GetEvent() const { return event_; } + V8_INLINE Local GetValue() const { return value_; } + + private: + Local promise_; + PromiseRejectEvent event_; + Local value_; +}; + +using PromiseRejectCallback = void (*)(PromiseRejectMessage message); + +} // namespace v8 + +#endif // INCLUDE_V8_PROMISE_H_ diff --git a/deps/include/v8-proxy.h b/deps/include/v8-proxy.h new file mode 100644 index 000000000..a08db8805 --- /dev/null +++ b/deps/include/v8-proxy.h @@ -0,0 +1,50 @@ + +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PROXY_H_ +#define INCLUDE_V8_PROXY_H_ + +#include "v8-context.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in Proxy constructor (ECMA-262, 6th Edition, + * 26.2.1). + */ +class V8_EXPORT Proxy : public Object { + public: + Local GetTarget(); + Local GetHandler(); + bool IsRevoked() const; + void Revoke(); + + /** + * Creates a new Proxy for the target object. + */ + static MaybeLocal New(Local context, + Local local_target, + Local local_handler); + + V8_INLINE static Proxy* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Proxy(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_PROXY_H_ diff --git a/deps/include/v8-regexp.h b/deps/include/v8-regexp.h new file mode 100644 index 000000000..3791bc036 --- /dev/null +++ b/deps/include/v8-regexp.h @@ -0,0 +1,105 @@ + +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_REGEXP_H_ +#define INCLUDE_V8_REGEXP_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in RegExp constructor (ECMA-262, 15.10). + */ +class V8_EXPORT RegExp : public Object { + public: + /** + * Regular expression flag bits. They can be or'ed to enable a set + * of flags. + * The kLinear value ('l') is experimental and can only be used with + * --enable-experimental-regexp-engine. RegExps with kLinear flag are + * guaranteed to be executed in asymptotic linear time wrt. the length of + * the subject string. + */ + enum Flags { + kNone = 0, + kGlobal = 1 << 0, + kIgnoreCase = 1 << 1, + kMultiline = 1 << 2, + kSticky = 1 << 3, + kUnicode = 1 << 4, + kDotAll = 1 << 5, + kLinear = 1 << 6, + kHasIndices = 1 << 7, + }; + + static constexpr int kFlagCount = 8; + + /** + * Creates a regular expression from the given pattern string and + * the flags bit field. May throw a JavaScript exception as + * described in ECMA-262, 15.10.4.1. + * + * For example, + * RegExp::New(v8::String::New("foo"), + * static_cast(kGlobal | kMultiline)) + * is equivalent to evaluating "/foo/gm". + */ + static V8_WARN_UNUSED_RESULT MaybeLocal New(Local context, + Local pattern, + Flags flags); + + /** + * Like New, but additionally specifies a backtrack limit. If the number of + * backtracks done in one Exec call hits the limit, a match failure is + * immediately returned. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewWithBacktrackLimit( + Local context, Local pattern, Flags flags, + uint32_t backtrack_limit); + + /** + * Executes the current RegExp instance on the given subject string. + * Equivalent to RegExp.prototype.exec as described in + * + * https://tc39.es/ecma262/#sec-regexp.prototype.exec + * + * On success, an Array containing the matched strings is returned. On + * failure, returns Null. + * + * Note: modifies global context state, accessible e.g. through RegExp.input. + */ + V8_WARN_UNUSED_RESULT MaybeLocal Exec(Local context, + Local subject); + + /** + * Returns the value of the source property: a string representing + * the regular expression. + */ + Local GetSource() const; + + /** + * Returns the flags bit field. + */ + Flags GetFlags() const; + + V8_INLINE static RegExp* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_REGEXP_H_ diff --git a/deps/include/v8-script.h b/deps/include/v8-script.h new file mode 100644 index 000000000..d17089932 --- /dev/null +++ b/deps/include/v8-script.h @@ -0,0 +1,771 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_SCRIPT_H_ +#define INCLUDE_V8_SCRIPT_H_ + +#include +#include + +#include +#include + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; +class Object; +class PrimitiveArray; +class Script; + +namespace internal { +class BackgroundDeserializeTask; +struct ScriptStreamingData; +} // namespace internal + +/** + * A container type that holds relevant metadata for module loading. + * + * This is passed back to the embedder as part of + * HostImportModuleDynamicallyCallback for module loading. + */ +class V8_EXPORT ScriptOrModule { + public: + /** + * The name that was passed by the embedder as ResourceName to the + * ScriptOrigin. This can be either a v8::String or v8::Undefined. + */ + Local GetResourceName(); + + /** + * The options that were passed by the embedder as HostDefinedOptions to + * the ScriptOrigin. + */ + Local GetHostDefinedOptions(); +}; + +/** + * A compiled JavaScript script, not yet tied to a Context. + */ +class V8_EXPORT UnboundScript { + public: + /** + * Binds the script to the currently entered context. + */ + Local