Commit 36774683 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[api, heap] Implement TracedReference

TracedGlobalTrait was unable to override v8::TracedGlobal<v8::Object> for
avoiding the destructor because it is needed on the API surface itself and C++
ODR which prohibits specialization after template instantiation.

Avoid this problem by providing a separate type TracedReference
that, similar to TracedGlobal, is purely traced but avoids the destructor
completely. This only works for embedders that have their memory management
tied to V8 as it is prone to accessing already reclaimed objects otherwise.

Bug: chromium:995684
Change-Id: Iab4332ed417b26c58638a8f9389174cc355a305b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1840972
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64150}
parent a0bd6602
......@@ -97,6 +97,10 @@ template <class T>
class Global;
template <class T>
class TracedGlobal;
template <class T>
class TracedReference;
template <class T>
class TracedReferenceBase;
template<class K, class V, class T> class PersistentValueMap;
template <class K, class V, class T>
class PersistentValueMapBase;
......@@ -282,7 +286,8 @@ class Local {
V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that);
V8_INLINE static Local<T> New(Isolate* isolate,
const PersistentBase<T>& that);
V8_INLINE static Local<T> New(Isolate* isolate, const TracedGlobal<T>& that);
V8_INLINE static Local<T> New(Isolate* isolate,
const TracedReferenceBase<T>& that);
private:
friend class Utils;
......@@ -312,7 +317,13 @@ class Local {
template <class F>
friend class ReturnValue;
template <class F>
friend class Traced;
template <class F>
friend class TracedGlobal;
template <class F>
friend class TracedReferenceBase;
template <class F>
friend class TracedReference;
explicit V8_INLINE Local(T* that) : val_(that) {}
V8_INLINE static Local<T> New(Isolate* isolate, T* that);
......@@ -793,22 +804,10 @@ template <class T>
using UniquePersistent = Global<T>;
/**
* Trait specifying behavior of |TracedGlobal<T>|.
* Deprecated. Use |TracedReference<T>| instead.
*/
template <typename T>
struct TracedGlobalTrait {
/**
* Specifies whether |TracedGlobal<T>| should clear its handle on destruction.
*
* V8 will *not* clear the embedder-side memory of the handle. The embedder is
* expected to report all |TracedGlobal<T>| handles through
* |EmbedderHeapTracer| upon garabge collection.
*
* See |EmbedderHeapTracer::IsRootForNonTracingGC| for handling with
* non-tracing GCs in V8.
*/
static constexpr bool kRequiresExplicitDestruction = true;
};
struct TracedGlobalTrait {};
/**
* A traced handle with copy and move semantics. The handle is to be used
......@@ -821,15 +820,131 @@ struct TracedGlobalTrait {
* |v8::EmbedderHeapTracer::IsRootForNonTracingGC()| whether the handle should
* be treated as root or not.
*
* For destruction semantics see |TracedGlobalTrait<T>|.
* Note that the base class cannot be instantiated itself. Choose from
* - TracedGlobal
* - TracedReference
*/
template <typename T>
class TracedGlobal {
class TracedReferenceBase {
public:
/**
* Returns true if this TracedReferenceBase is empty, i.e., has not been
* assigned an object.
*/
bool IsEmpty() const { return val_ == nullptr; }
/**
* If non-empty, destroy the underlying storage cell. |IsEmpty| will return
* true after this call.
*/
V8_INLINE void Reset();
/**
* Construct a Local<T> from this handle.
*/
Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
template <class S>
V8_INLINE bool operator==(const TracedReferenceBase<S>& that) const {
internal::Address* a = reinterpret_cast<internal::Address*>(val_);
internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
}
template <class S>
V8_INLINE bool operator==(const Local<S>& that) const {
internal::Address* a = reinterpret_cast<internal::Address*>(val_);
internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
}
template <class S>
V8_INLINE bool operator!=(const TracedReferenceBase<S>& that) const {
return !operator==(that);
}
template <class S>
V8_INLINE bool operator!=(const Local<S>& that) const {
return !operator==(that);
}
/**
* 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;
/**
* Adds a finalization callback to the handle. The type of this callback is
* similar to WeakCallbackType::kInternalFields, i.e., it will pass the
* parameter and the first two internal fields of the object.
*
* The callback is then supposed to reset the handle in the callback. 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<void>::SetSecondPassCallback.
*/
V8_INLINE void SetFinalizationCallback(
void* parameter, WeakCallbackInfo<void>::Callback callback);
template <class S>
V8_INLINE TracedReferenceBase<S>& As() const {
return reinterpret_cast<TracedReferenceBase<S>&>(
const_cast<TracedReferenceBase<T>&>(*this));
}
private:
enum DestructionMode { kWithDestructor, kWithoutDestructor };
/**
* An empty TracedReferenceBase without storage cell.
*/
TracedReferenceBase() = default;
V8_INLINE static T* New(Isolate* isolate, T* that, void* slot,
DestructionMode destruction_mode);
T* val_ = nullptr;
friend class EmbedderHeapTracer;
template <typename F>
friend class Local;
friend class Object;
template <typename F>
friend class TracedGlobal;
template <typename F>
friend class TracedReference;
template <typename F>
friend class ReturnValue;
};
/**
* A traced handle with destructor that clears the handle. For more details see
* TracedReferenceBase.
*/
template <typename T>
class TracedGlobal : public TracedReferenceBase<T> {
public:
using TracedReferenceBase<T>::Reset;
/**
* Destructor resetting the handle.
*/
~TracedGlobal() { this->Reset(); }
/**
* An empty TracedGlobal without storage cell.
*/
TracedGlobal() = default;
TracedGlobal() : TracedReferenceBase<T>() {}
/**
* Construct a TracedGlobal from a Local.
......@@ -838,8 +953,9 @@ class TracedGlobal {
* pointing to the same object.
*/
template <class S>
TracedGlobal(Isolate* isolate, Local<S> that)
: val_(New(isolate, *that, &val_)) {
TracedGlobal(Isolate* isolate, Local<S> that) : TracedReferenceBase<T>() {
this->val_ = this->New(isolate, that.val_, &this->val_,
TracedReferenceBase<T>::kWithDestructor);
TYPE_CHECK(T, S);
}
......@@ -905,18 +1021,6 @@ class TracedGlobal {
template <class S>
V8_INLINE TracedGlobal& operator=(const TracedGlobal<S>& rhs);
/**
* Returns true if this TracedGlobal is empty, i.e., has not been assigned an
* object.
*/
bool IsEmpty() const { return val_ == nullptr; }
/**
* 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
......@@ -924,103 +1028,120 @@ class TracedGlobal {
template <class S>
V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
/**
* Construct a Local<T> from this handle.
*/
Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
template <class S>
V8_INLINE TracedGlobal<S>& As() const {
return reinterpret_cast<TracedGlobal<S>&>(
const_cast<TracedGlobal<T>&>(*this));
}
};
template <class S>
V8_INLINE bool operator==(const TracedGlobal<S>& that) const {
internal::Address* a = reinterpret_cast<internal::Address*>(**this);
internal::Address* b = reinterpret_cast<internal::Address*>(*that);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
}
/**
* A traced handle without destructor that clears the handle. The embedder needs
* to ensure that the handle is not accessed once the V8 object has been
* reclaimed. This can happen when the handle is not passed through the
* EmbedderHeapTracer. For more details see TracedReferenceBase.
*/
template <typename T>
class TracedReference : public TracedReferenceBase<T> {
public:
using TracedReferenceBase<T>::Reset;
/**
* An empty TracedReference without storage cell.
*/
TracedReference() : TracedReferenceBase<T>() {}
/**
* Construct a TracedReference from a Local.
*
* When the Local is non-empty, a new storage cell is created
* pointing to the same object.
*/
template <class S>
V8_INLINE bool operator==(const Local<S>& that) const {
internal::Address* a = reinterpret_cast<internal::Address*>(**this);
internal::Address* b = reinterpret_cast<internal::Address*>(*that);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
TracedReference(Isolate* isolate, Local<S> that) : TracedReferenceBase<T>() {
this->val_ = this->New(isolate, that.val_, &this->val_,
TracedReferenceBase<T>::kWithoutDestructor);
TYPE_CHECK(T, S);
}
template <class S>
V8_INLINE bool operator!=(const TracedGlobal<S>& that) const {
return !operator==(that);
/**
* Move constructor initializing TracedReference from an
* existing one.
*/
V8_INLINE TracedReference(TracedReference&& other) {
// Forward to operator=.
*this = std::move(other);
}
template <class S>
V8_INLINE bool operator!=(const Local<S>& that) const {
return !operator==(that);
/**
* Move constructor initializing TracedReference from an
* existing one.
*/
template <typename S>
V8_INLINE TracedReference(TracedReference<S>&& other) {
// Forward to operator=.
*this = std::move(other);
}
/**
* Assigns a wrapper class ID to the handle.
* Copy constructor initializing TracedReference from an
* existing one.
*/
V8_INLINE void SetWrapperClassId(uint16_t class_id);
V8_INLINE TracedReference(const TracedReference& other) {
// Forward to operator=;
*this = other;
}
/**
* Returns the class ID previously assigned to this handle or 0 if no class ID
* was previously assigned.
* Copy constructor initializing TracedReference from an
* existing one.
*/
V8_INLINE uint16_t WrapperClassId() const;
template <typename S>
V8_INLINE TracedReference(const TracedReference<S>& other) {
// Forward to operator=;
*this = other;
}
/**
* Adds a finalization callback to the handle. The type of this callback is
* similar to WeakCallbackType::kInternalFields, i.e., it will pass the
* parameter and the first two internal fields of the object.
*
* The callback is then supposed to reset the handle in the callback. 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<void>::SetSecondPassCallback.
* Move assignment operator initializing TracedGlobal from an existing one.
*/
V8_INLINE void SetFinalizationCallback(
void* parameter, WeakCallbackInfo<void>::Callback callback);
V8_INLINE TracedReference& operator=(TracedReference&& rhs);
private:
// Wrapping type used when clearing on destruction is required.
struct WrappedForDestruction {
T* value;
explicit WrappedForDestruction(T* val) : value(val) {}
~WrappedForDestruction();
operator T*() const { return value; }
T* operator*() const { return value; }
T* operator->() const { return value; }
WrappedForDestruction& operator=(const WrappedForDestruction& other) {
value = other.value;
return *this;
}
WrappedForDestruction& operator=(T* val) {
value = val;
return *this;
}
};
/**
* Move assignment operator initializing TracedGlobal from an existing one.
*/
template <class S>
V8_INLINE TracedReference& operator=(TracedReference<S>&& rhs);
V8_INLINE static T* New(Isolate* isolate, T* that, void* slot);
/**
* Copy assignment operator initializing TracedGlobal from an existing one.
*
* Note: Prohibited when |other| has a finalization callback set through
* |SetFinalizationCallback|.
*/
V8_INLINE TracedReference& operator=(const TracedReference& rhs);
T* operator*() const { return this->val_; }
/**
* Copy assignment operator initializing TracedGlobal from an existing one.
*
* Note: Prohibited when |other| has a finalization callback set through
* |SetFinalizationCallback|.
*/
template <class S>
V8_INLINE TracedReference& operator=(const TracedReference<S>& rhs);
typename std::conditional<
TracedGlobalTrait<TracedGlobal<T>>::kRequiresExplicitDestruction,
WrappedForDestruction, T*>::type 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 <class S>
V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
friend class EmbedderHeapTracer;
template <typename F>
friend class Local;
friend class Object;
template <typename F>
friend class ReturnValue;
template <class S>
V8_INLINE TracedReference<S>& As() const {
return reinterpret_cast<TracedReference<S>&>(
const_cast<TracedReference<T>&>(*this));
}
};
/**
......@@ -3641,8 +3762,9 @@ class V8_EXPORT Object : public Value {
return object.val_->InternalFieldCount();
}
/** Same as above, but works for TracedGlobal. */
V8_INLINE static int InternalFieldCount(const TracedGlobal<Object>& object) {
/** Same as above, but works for TracedReferenceBase. */
V8_INLINE static int InternalFieldCount(
const TracedReferenceBase<Object>& object) {
return object.val_->InternalFieldCount();
}
......@@ -3667,7 +3789,7 @@ class V8_EXPORT Object : public Value {
/** Same as above, but works for TracedGlobal. */
V8_INLINE static void* GetAlignedPointerFromInternalField(
const TracedGlobal<Object>& object, int index) {
const TracedReferenceBase<Object>& object, int index) {
return object.val_->GetAlignedPointerFromInternalField(index);
}
......@@ -3957,7 +4079,7 @@ class ReturnValue {
template <typename S>
V8_INLINE void Set(const Global<S>& handle);
template <typename S>
V8_INLINE void Set(const TracedGlobal<S>& handle);
V8_INLINE void Set(const TracedReferenceBase<S>& handle);
template <typename S>
V8_INLINE void Set(const Local<S> handle);
// Fast primitive setters
......@@ -7465,7 +7587,8 @@ class V8_EXPORT EmbedderHeapTracer {
class V8_EXPORT TracedGlobalHandleVisitor {
public:
virtual ~TracedGlobalHandleVisitor() = default;
virtual void VisitTracedGlobalHandle(const TracedGlobal<Value>& value) = 0;
virtual void VisitTracedGlobalHandle(const TracedGlobal<Value>& handle) {}
virtual void VisitTracedReference(const TracedReference<Value>& handle) {}
};
/**
......@@ -7503,7 +7626,7 @@ class V8_EXPORT EmbedderHeapTracer {
virtual void RegisterV8References(
const std::vector<std::pair<void*, void*> >& embedder_fields) = 0;
void RegisterEmbedderReference(const TracedGlobal<v8::Value>& ref);
void RegisterEmbedderReference(const TracedReferenceBase<v8::Value>& ref);
/**
* Called at the beginning of a GC cycle.
......@@ -7562,32 +7685,35 @@ class V8_EXPORT EmbedderHeapTracer {
*
* 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.
* - When run without destructors (by specializing
* |TracedGlobalTrait::kRequiresExplicitDestruction|) V8 calls
* |ResetHandleInNonTracingGC|.
* - 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 |ResetHandleInNonTracingGC|.
*
* Note that the |handle| is different from the |TracedGlobal<T>| handle that
* the embedder holds for retaining the object. The embedder may use
* |TracedGlobal<T>::WrapperClassId()| to distinguish cases where it wants
* handles to be treated as roots from not being treated as roots.
* 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 IsRootForNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) {
return true;
}
const v8::TracedReference<v8::Value>& handle);
virtual bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle);
/**
* Used in combination with |IsRootForNonTracingGC|. 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 |TracedGlobal<T>| handle that
* the embedder holds for retaining the object. It is up to the embedder to
* find the orignal |TracedGlobal<T>| handle via the object or class id.
* 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 ResetHandleInNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) {}
const v8::TracedReference<v8::Value>& handle);
V8_DEPRECATE_SOON(
"Use TracedReference version when not requiring destructors.",
virtual void ResetHandleInNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle));
/*
* Called by the embedder to immediately perform a full garbage collection.
......@@ -9207,8 +9333,12 @@ class V8_EXPORT V8 {
template <class T>
friend class Maybe;
template <class T>
friend class TracedReferenceBase;
template <class T>
friend class TracedGlobal;
template <class T>
friend class TracedReference;
template <class T>
friend class WeakCallbackInfo;
template <class T> friend class Eternal;
template <class T> friend class PersistentBase;
......@@ -10079,7 +10209,7 @@ Local<T> Local<T>::New(Isolate* isolate, const PersistentBase<T>& that) {
}
template <class T>
Local<T> Local<T>::New(Isolate* isolate, const TracedGlobal<T>& that) {
Local<T> Local<T>::New(Isolate* isolate, const TracedReferenceBase<T>& that) {
return New(isolate, that.val_);
}
......@@ -10260,26 +10390,20 @@ Global<T>& Global<T>::operator=(Global<S>&& rhs) {
}
template <class T>
TracedGlobal<T>::WrappedForDestruction::~WrappedForDestruction() {
if (value == nullptr) return;
V8::DisposeTracedGlobal(reinterpret_cast<internal::Address*>(value));
value = nullptr;
}
template <class T>
T* TracedGlobal<T>::New(Isolate* isolate, T* that, void* slot) {
T* TracedReferenceBase<T>::New(Isolate* isolate, T* that, void* slot,
DestructionMode destruction_mode) {
if (that == nullptr) return nullptr;
internal::Address* p = reinterpret_cast<internal::Address*>(that);
return reinterpret_cast<T*>(V8::GlobalizeTracedReference(
reinterpret_cast<internal::Isolate*>(isolate), p,
reinterpret_cast<internal::Address*>(slot),
TracedGlobalTrait<TracedGlobal<T>>::kRequiresExplicitDestruction));
destruction_mode == kWithDestructor));
}
template <class T>
void TracedGlobal<T>::Reset() {
void TracedReferenceBase<T>::Reset() {
if (IsEmpty()) return;
V8::DisposeTracedGlobal(reinterpret_cast<internal::Address*>(**this));
V8::DisposeTracedGlobal(reinterpret_cast<internal::Address*>(val_));
val_ = nullptr;
}
......@@ -10289,7 +10413,8 @@ void TracedGlobal<T>::Reset(Isolate* isolate, const Local<S>& other) {
TYPE_CHECK(T, S);
Reset();
if (other.IsEmpty()) return;
this->val_ = New(isolate, other.val_, &val_);
this->val_ = this->New(isolate, other.val_, &this->val_,
TracedReferenceBase<T>::kWithDestructor);
}
template <class T>
......@@ -10337,28 +10462,83 @@ TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal& rhs) {
}
template <class T>
void TracedGlobal<T>::SetWrapperClassId(uint16_t class_id) {
template <class S>
void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) {
TYPE_CHECK(T, S);
Reset();
if (other.IsEmpty()) return;
this->val_ = this->New(isolate, other.val_, &this->val_,
TracedReferenceBase<T>::kWithoutDestructor);
}
template <class T>
template <class S>
TracedReference<T>& TracedReference<T>::operator=(TracedReference<S>&& rhs) {
TYPE_CHECK(T, S);
*this = std::move(rhs.template As<T>());
return *this;
}
template <class T>
template <class S>
TracedReference<T>& TracedReference<T>::operator=(
const TracedReference<S>& rhs) {
TYPE_CHECK(T, S);
*this = rhs.template As<T>();
return *this;
}
template <class T>
TracedReference<T>& TracedReference<T>::operator=(TracedReference&& rhs) {
if (this != &rhs) {
this->Reset();
if (rhs.val_ != nullptr) {
this->val_ = rhs.val_;
V8::MoveTracedGlobalReference(
reinterpret_cast<internal::Address**>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
rhs.val_ = nullptr;
}
}
return *this;
}
template <class T>
TracedReference<T>& TracedReference<T>::operator=(const TracedReference& rhs) {
if (this != &rhs) {
this->Reset();
if (rhs.val_ != nullptr) {
V8::CopyTracedGlobalReference(
reinterpret_cast<const internal::Address* const*>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
}
}
return *this;
}
template <class T>
void TracedReferenceBase<T>::SetWrapperClassId(uint16_t class_id) {
typedef internal::Internals I;
if (IsEmpty()) return;
internal::Address* obj = reinterpret_cast<internal::Address*>(**this);
internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
*reinterpret_cast<uint16_t*>(addr) = class_id;
}
template <class T>
uint16_t TracedGlobal<T>::WrapperClassId() const {
uint16_t TracedReferenceBase<T>::WrapperClassId() const {
typedef internal::Internals I;
if (IsEmpty()) return 0;
internal::Address* obj = reinterpret_cast<internal::Address*>(**this);
internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
return *reinterpret_cast<uint16_t*>(addr);
}
template <class T>
void TracedGlobal<T>::SetFinalizationCallback(
void TracedReferenceBase<T>::SetFinalizationCallback(
void* parameter, typename WeakCallbackInfo<void>::Callback callback) {
V8::SetFinalizationCallbackTraced(
reinterpret_cast<internal::Address*>(**this), parameter, callback);
V8::SetFinalizationCallbackTraced(reinterpret_cast<internal::Address*>(val_),
parameter, callback);
}
template <typename T>
......@@ -10377,12 +10557,12 @@ void ReturnValue<T>::Set(const Global<S>& handle) {
template <typename T>
template <typename S>
void ReturnValue<T>::Set(const TracedGlobal<S>& handle) {
void ReturnValue<T>::Set(const TracedReferenceBase<S>& handle) {
TYPE_CHECK(T, S);
if (V8_UNLIKELY(handle.IsEmpty())) {
*value_ = GetDefaultValue();
} else {
*value_ = *reinterpret_cast<internal::Address*>(*handle);
*value_ = *reinterpret_cast<internal::Address*>(handle.val_);
}
}
......
......@@ -10589,11 +10589,12 @@ void EmbedderHeapTracer::DecreaseAllocatedSize(size_t bytes) {
}
void EmbedderHeapTracer::RegisterEmbedderReference(
const TracedGlobal<v8::Value>& ref) {
const TracedReferenceBase<v8::Value>& ref) {
if (ref.IsEmpty()) return;
i::Heap* const heap = reinterpret_cast<i::Isolate*>(isolate_)->heap();
heap->RegisterExternallyReferencedObject(reinterpret_cast<i::Address*>(*ref));
heap->RegisterExternallyReferencedObject(
reinterpret_cast<i::Address*>(ref.val_));
}
void EmbedderHeapTracer::IterateTracedGlobalHandles(
......@@ -10603,6 +10604,26 @@ void EmbedderHeapTracer::IterateTracedGlobalHandles(
isolate->global_handles()->IterateTracedNodes(visitor);
}
bool EmbedderHeapTracer::IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) {
return true;
}
bool EmbedderHeapTracer::IsRootForNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) {
return true;
}
void EmbedderHeapTracer::ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) {
UNREACHABLE();
}
void EmbedderHeapTracer::ResetHandleInNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) {
UNREACHABLE();
}
namespace internal {
const size_t HandleScopeImplementer::kEnteredContextsOffset =
......
......@@ -901,8 +901,13 @@ void GlobalHandles::IdentifyWeakUnmodifiedObjects(
DCHECK(node->is_root());
if (is_unmodified(node->location())) {
v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) {
node->set_root(tracer->IsRootForNonTracingGC(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value)));
} else {
node->set_root(tracer->IsRootForNonTracingGC(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value)));
}
}
}
}
......@@ -990,7 +995,7 @@ void GlobalHandles::IterateYoungWeakUnmodifiedRootsForPhantomHandles(
} else {
v8::Value* value = ToApi<v8::Value>(node->handle());
tracer->ResetHandleInNonTracingGC(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value));
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
DCHECK(!node->IsInUse());
}
......@@ -1271,8 +1276,13 @@ void GlobalHandles::IterateTracedNodes(
for (TracedNode* node : *traced_nodes_) {
if (node->IsInUse()) {
v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) {
visitor->VisitTracedGlobalHandle(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value));
} else {
visitor->VisitTracedReference(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
}
}
}
}
......
......@@ -57,7 +57,12 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) {
return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
}
void ResetHandleInNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) {
bool IsRootForNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
}
void ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
// Resetting is only called when IsRootForNonTracingGC returns false which
// can only happen the EmbedderHeapTracer is set on API level.
DCHECK(InUse());
......
......@@ -17,12 +17,6 @@
namespace v8 {
// See test below: TracedGlobalNoDestructor.
template <>
struct TracedGlobalTrait<v8::TracedGlobal<v8::Value>> {
static constexpr bool kRequiresExplicitDestruction = false;
};
namespace internal {
namespace heap {
......@@ -293,13 +287,14 @@ void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
CHECK(!global->IsEmpty());
}
template <typename T>
void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
v8::TracedGlobal<v8::Object>* global) {
T* global) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(
ConstructTraceableJSApiObject(context, nullptr, nullptr));
CHECK(!object.IsEmpty());
*global = v8::TracedGlobal<v8::Object>(isolate, object);
*global = T(isolate, object);
CHECK(!global->IsEmpty());
}
......@@ -360,10 +355,6 @@ TEST(TracedGlobalCopyWithDestructor) {
v8::HandleScope scope(isolate);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
static_assert(TracedGlobalTrait<
v8::TracedGlobal<v8::Object>>::kRequiresExplicitDestruction,
"destructor expected");
const size_t initial_count = global_handles->handles_count();
v8::TracedGlobal<v8::Object> global1;
{
......@@ -401,18 +392,14 @@ TEST(TracedGlobalCopyNoDestructor) {
v8::HandleScope scope(isolate);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
static_assert(!TracedGlobalTrait<
v8::TracedGlobal<v8::Value>>::kRequiresExplicitDestruction,
"no destructor expected");
const size_t initial_count = global_handles->handles_count();
v8::TracedGlobal<v8::Value> global1;
v8::TracedReference<v8::Value> global1;
{
v8::HandleScope scope(isolate);
global1.Reset(isolate, v8::Object::New(isolate));
}
v8::TracedGlobal<v8::Value> global2(global1);
v8::TracedGlobal<v8::Value> global3;
v8::TracedReference<v8::Value> global2(global1);
v8::TracedReference<v8::Value> global3;
global3 = global2;
CHECK_EQ(initial_count + 3, global_handles->handles_count());
CHECK(!global1.IsEmpty());
......@@ -500,7 +487,7 @@ TEST(TracedGlobalToUnmodifiedJSApiObjectSurvivesScavengePerDefault) {
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(true);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject,
CcTest::isolate(), ConstructJSApiObject<TracedGlobal<v8::Object>>,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kSurvives);
}
......@@ -513,7 +500,7 @@ TEST(TracedGlobalToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) {
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(false);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject,
CcTest::isolate(), ConstructJSApiObject<TracedGlobal<v8::Object>>,
[](const TracedGlobal<v8::Object>& global) {}, InvokeScavenge,
SurvivalMode::kDies);
}
......@@ -671,9 +658,6 @@ TEST(TracedGlobalWithDestructor) {
CHECK(!traced->IsEmpty());
CHECK_EQ(initial_count + 1, global_handles->handles_count());
}
static_assert(TracedGlobalTrait<
v8::TracedGlobal<v8::Object>>::kRequiresExplicitDestruction,
"destructor expected");
delete traced;
CHECK_EQ(initial_count, global_handles->handles_count());
// GC should not need to clear the handle.
......@@ -691,21 +675,18 @@ TEST(TracedGlobalNoDestructor) {
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
const size_t initial_count = global_handles->handles_count();
char* memory = new char[sizeof(v8::TracedGlobal<v8::Value>)];
auto* traced = new (memory) v8::TracedGlobal<v8::Value>();
char* memory = new char[sizeof(v8::TracedReference<v8::Value>)];
auto* traced = new (memory) v8::TracedReference<v8::Value>();
{
v8::HandleScope scope(isolate);
v8::Local<v8::Value> object(ConstructTraceableJSApiObject(
isolate->GetCurrentContext(), nullptr, nullptr));
CHECK(traced->IsEmpty());
*traced = v8::TracedGlobal<v8::Value>(isolate, object);
*traced = v8::TracedReference<v8::Value>(isolate, object);
CHECK(!traced->IsEmpty());
CHECK_EQ(initial_count + 1, global_handles->handles_count());
}
static_assert(!TracedGlobalTrait<
v8::TracedGlobal<v8::Value>>::kRequiresExplicitDestruction,
"no destructor expected");
traced->~TracedGlobal<v8::Value>();
traced->~TracedReference<v8::Value>();
CHECK_EQ(initial_count + 1, global_handles->handles_count());
// GC should clear the handle.
heap::InvokeMarkSweep();
......@@ -759,18 +740,19 @@ class EmbedderHeapTracerNoDestructorNonTracingClearing final
uint16_t class_id_to_optimize)
: class_id_to_optimize_(class_id_to_optimize) {}
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
return handle.WrapperClassId() != class_id_to_optimize_;
}
void ResetHandleInNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) final {
const v8::TracedReference<v8::Value>& handle) final {
if (handle.WrapperClassId() != class_id_to_optimize_) return;
// Convention (for test): Objects that are optimized have their first field
// set as a back pointer.
TracedGlobal<v8::Value>* original_handle =
reinterpret_cast<TracedGlobal<v8::Value>*>(
TracedReferenceBase<v8::Value>* original_handle =
reinterpret_cast<TracedReferenceBase<v8::Value>*>(
v8::Object::GetAlignedPointerFromInternalField(
handle.As<v8::Object>(), 0));
original_handle->Reset();
......@@ -781,23 +763,23 @@ class EmbedderHeapTracerNoDestructorNonTracingClearing final
};
template <typename T>
void SetupOptimizedAndNonOptimizedHandle(
v8::Isolate* isolate, uint16_t optimized_class_id,
v8::TracedGlobal<T>* optimized_handle,
v8::TracedGlobal<T>* non_optimized_handle) {
void SetupOptimizedAndNonOptimizedHandle(v8::Isolate* isolate,
uint16_t optimized_class_id,
T* optimized_handle,
T* non_optimized_handle) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> optimized_object(ConstructTraceableJSApiObject(
isolate->GetCurrentContext(), optimized_handle, nullptr));
CHECK(optimized_handle->IsEmpty());
*optimized_handle = v8::TracedGlobal<T>(isolate, optimized_object);
*optimized_handle = T(isolate, optimized_object);
CHECK(!optimized_handle->IsEmpty());
optimized_handle->SetWrapperClassId(optimized_class_id);
v8::Local<v8::Object> non_optimized_object(ConstructTraceableJSApiObject(
isolate->GetCurrentContext(), nullptr, nullptr));
CHECK(non_optimized_handle->IsEmpty());
*non_optimized_handle = v8::TracedGlobal<T>(isolate, non_optimized_object);
*non_optimized_handle = T(isolate, non_optimized_object);
CHECK(!non_optimized_handle->IsEmpty());
}
......@@ -813,9 +795,6 @@ TEST(TracedGlobalDestructorReclaimedOnScavenge) {
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
static_assert(TracedGlobalTrait<
v8::TracedGlobal<v8::Object>>::kRequiresExplicitDestruction,
"destructor expected");
const size_t initial_count = global_handles->handles_count();
auto* optimized_handle = new v8::TracedGlobal<v8::Object>();
auto* non_optimized_handle = new v8::TracedGlobal<v8::Object>();
......@@ -841,12 +820,9 @@ TEST(TracedGlobalNoDestructorReclaimedOnScavenge) {
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
static_assert(!TracedGlobalTrait<
v8::TracedGlobal<v8::Value>>::kRequiresExplicitDestruction,
"no destructor expected");
const size_t initial_count = global_handles->handles_count();
auto* optimized_handle = new v8::TracedGlobal<v8::Value>();
auto* non_optimized_handle = new v8::TracedGlobal<v8::Value>();
auto* optimized_handle = new v8::TracedReference<v8::Value>();
auto* non_optimized_handle = new v8::TracedReference<v8::Value>();
SetupOptimizedAndNonOptimizedHandle(isolate, kClassIdToOptimize,
optimized_handle, non_optimized_handle);
CHECK_EQ(initial_count + 2, global_handles->handles_count());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment