Commit f6386018 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[api] Remove TracedGlobal<>

Remove deprecated TracedGlobal<>, greatly simplifying handling of
traced references in general.

Also saves a word per v8::TracedReference as there's no need to keep a
possible callback around.

Bug: v8:12603
Change-Id: Ice35d7906775b912d02e97a27a722b3e1cec28d9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3532251Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79589}
parent 574c2809
......@@ -34,29 +34,22 @@ class V8_EXPORT EmbedderRootsHandler {
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.
* Returns true if the |TracedReference| handle should be considered as root
* for the currently running non-tracing garbage collection and false
* otherwise. The default implementation will keep all |TracedReference|
* 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|.
* such a handle is reclaimed. In that case, V8 calls |ResetRoot()| for the
* |TracedReference|.
*
* Note that the |handle| is different from the handle that the embedder holds
* 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<v8::Value>& handle) = 0;
V8_DEPRECATED("See v8::TracedGlobal class comment.")
virtual bool IsRoot(const v8::TracedGlobal<v8::Value>& handle) {
return true;
}
/**
* Used in combination with |IsRoot|. Called by V8 when an
* object that is backed by a handle is reclaimed by a non-tracing garbage
......@@ -87,13 +80,11 @@ class V8_EXPORT EmbedderHeapTracer {
};
/**
* Interface for iterating through TracedGlobal handles.
* Interface for iterating through |TracedReference| handles.
*/
class V8_EXPORT TracedGlobalHandleVisitor {
public:
virtual ~TracedGlobalHandleVisitor() = default;
V8_DEPRECATED("See v8::TracedGlobal class comment.")
virtual void VisitTracedGlobalHandle(const TracedGlobal<Value>& handle) {}
virtual void VisitTracedReference(const TracedReference<Value>& handle) {}
};
......@@ -118,8 +109,8 @@ class V8_EXPORT EmbedderHeapTracer {
virtual ~EmbedderHeapTracer() = default;
/**
* Iterates all TracedGlobal handles created for the v8::Isolate the tracer is
* attached to.
* Iterates all |TracedReference| handles created for the |v8::Isolate| the
* tracer is attached to.
*/
void IterateTracedGlobalHandles(TracedGlobalHandleVisitor* visitor);
......@@ -194,8 +185,6 @@ class V8_EXPORT EmbedderHeapTracer {
*/
virtual bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle);
V8_DEPRECATED("See v8::TracedGlobal class comment.")
virtual bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle);
/**
* See documentation on EmbedderRootsHandler.
......
......@@ -46,8 +46,6 @@ class String;
template <class F>
class Traced;
template <class F>
class TracedGlobal;
template <class F>
class TracedReference;
class TracedReferenceBase;
class Utils;
......@@ -312,8 +310,6 @@ class Local {
template <class F>
friend class Traced;
template <class F>
friend class TracedGlobal;
template <class F>
friend class BasicTracedReference;
template <class F>
friend class TracedReference;
......
......@@ -493,7 +493,7 @@ class V8_EXPORT Object : public Value {
return object.val_->GetAlignedPointerFromInternalField(index);
}
/** Same as above, but works for TracedGlobal. */
/** Same as above, but works for TracedReference. */
V8_INLINE static void* GetAlignedPointerFromInternalField(
const BasicTracedReference<Object>& object, int index) {
return object->GetAlignedPointerFromInternalField(index);
......
......@@ -27,8 +27,6 @@ namespace internal {
class BasicTracedReferenceExtractor;
enum class GlobalHandleDestructionMode { kWithDestructor, kWithoutDestructor };
enum class GlobalHandleStoreMode {
kInitializingStore,
kAssigningStore,
......@@ -36,25 +34,15 @@ enum class GlobalHandleStoreMode {
V8_EXPORT internal::Address* GlobalizeTracedReference(
internal::Isolate* isolate, internal::Address* handle,
internal::Address* slot, GlobalHandleDestructionMode destruction_mode,
GlobalHandleStoreMode store_mode);
V8_EXPORT void MoveTracedGlobalReference(internal::Address** from,
internal::Address* slot, GlobalHandleStoreMode store_mode);
V8_EXPORT void MoveTracedReference(internal::Address** from,
internal::Address** to);
V8_EXPORT void CopyTracedGlobalReference(const internal::Address* const* from,
V8_EXPORT void CopyTracedReference(const internal::Address* const* from,
internal::Address** to);
V8_EXPORT void DisposeTracedGlobal(internal::Address* global_handle);
V8_EXPORT void SetFinalizationCallbackTraced(
internal::Address* location, void* parameter,
WeakCallbackInfo<void>::Callback callback);
V8_EXPORT void DisposeTracedReference(internal::Address* global_handle);
} // namespace internal
/**
* Deprecated. Use |TracedReference<T>| instead.
*/
template <typename T>
struct TracedGlobalTrait {};
class TracedReferenceBase {
public:
/**
......@@ -138,9 +126,8 @@ class TracedReferenceBase {
* |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
* be treated as root or not.
*
* Note that the base class cannot be instantiated itself. Choose from
* - TracedGlobal
* - TracedReference
* Note that the base class cannot be instantiated itself, use |TracedReference|
* instead.
*/
template <typename T>
class BasicTracedReference : public TracedReferenceBase {
......@@ -177,7 +164,6 @@ class BasicTracedReference : public TracedReferenceBase {
V8_INLINE static internal::Address* New(
Isolate* isolate, T* that, void* slot,
internal::GlobalHandleDestructionMode destruction_mode,
internal::GlobalHandleStoreMode store_mode);
friend class EmbedderHeapTracer;
......@@ -185,8 +171,6 @@ class BasicTracedReference : public TracedReferenceBase {
friend class Local;
friend class Object;
template <typename F>
friend class TracedGlobal;
template <typename F>
friend class TracedReference;
template <typename F>
friend class BasicTracedReference;
......@@ -194,146 +178,6 @@ class BasicTracedReference : public TracedReferenceBase {
friend class ReturnValue;
};
/**
* A traced handle with destructor that clears the handle. For more details see
* BasicTracedReference.
*
* This type is being deprecated and embedders are encouraged to use
* `v8::TracedReference` in combination with `v8::CppHeap`. If this is not
* possible, the following provides feature parity:
*
* \code
* template <typename T>
* struct TracedGlobalPolyfill {
* v8::TracedReference<T> traced_reference;
* v8::Global<T> weak_reference_for_callback;
* };
* \endcode
*
* In this example, `weak_reference_for_callback` can be used to emulate
* `SetFinalizationCallback()`.
*/
template <typename T>
class TracedGlobal : public BasicTracedReference<T> {
public:
using BasicTracedReference<T>::Reset;
/**
* Destructor resetting the handle.Is
*/
~TracedGlobal() { this->Reset(); }
/**
* An empty TracedGlobal without storage cell.
*/
V8_DEPRECATED("See class comment.")
TracedGlobal() : BasicTracedReference<T>() {}
/**
* Construct a TracedGlobal from a Local.
*
* When the Local is non-empty, a new storage cell is created
* pointing to the same object.
*/
template <class S>
V8_DEPRECATED("See class comment.")
TracedGlobal(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
this->val_ =
this->New(isolate, that.val_, &this->val_,
internal::GlobalHandleDestructionMode::kWithDestructor,
internal::GlobalHandleStoreMode::kInitializingStore);
static_assert(std::is_base_of<T, S>::value, "type check");
}
/**
* Move constructor initializing TracedGlobal from an existing one.
*/
V8_INLINE TracedGlobal(TracedGlobal&& other) noexcept {
// Forward to operator=.
*this = std::move(other);
}
/**
* Move constructor initializing TracedGlobal from an existing one.
*/
template <typename S>
V8_INLINE TracedGlobal(TracedGlobal<S>&& other) noexcept {
// Forward to operator=.
*this = std::move(other);
}
/**
* Copy constructor initializing TracedGlobal from an existing one.
*/
V8_INLINE TracedGlobal(const TracedGlobal& other) {
// Forward to operator=;
*this = other;
}
/**
* Copy constructor initializing TracedGlobal from an existing one.
*/
template <typename S>
V8_INLINE TracedGlobal(const TracedGlobal<S>& other) {
// Forward to operator=;
*this = other;
}
/**
* Move assignment operator initializing TracedGlobal from an existing one.
*/
V8_INLINE TracedGlobal& operator=(TracedGlobal&& rhs) noexcept;
/**
* Move assignment operator initializing TracedGlobal from an existing one.
*/
template <class S>
V8_INLINE TracedGlobal& operator=(TracedGlobal<S>&& rhs) noexcept;
/**
* Copy assignment operator initializing TracedGlobal from an existing one.
*
* Note: Prohibited when |other| has a finalization callback set through
* |SetFinalizationCallback|.
*/
V8_INLINE TracedGlobal& operator=(const TracedGlobal& rhs);
/**
* 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 TracedGlobal& operator=(const TracedGlobal<S>& rhs);
/**
* 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);
template <class S>
V8_INLINE TracedGlobal<S>& As() const {
return reinterpret_cast<TracedGlobal<S>&>(
const_cast<TracedGlobal<T>&>(*this));
}
/**
* 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);
};
/**
* 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
......@@ -363,9 +207,7 @@ class TracedReference : public BasicTracedReference<T> {
*/
template <class S>
TracedReference(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
this->val_ =
this->New(isolate, that.val_, &this->val_,
internal::GlobalHandleDestructionMode::kWithoutDestructor,
this->val_ = this->New(isolate, that.val_, &this->val_,
internal::GlobalHandleStoreMode::kInitializingStore);
static_assert(std::is_base_of<T, S>::value, "type check");
}
......@@ -409,23 +251,23 @@ class TracedReference : public BasicTracedReference<T> {
}
/**
* Move assignment operator initializing TracedGlobal from an existing one.
* Move assignment operator initializing TracedReference from an existing one.
*/
V8_INLINE TracedReference& operator=(TracedReference&& rhs) noexcept;
/**
* Move assignment operator initializing TracedGlobal from an existing one.
* Move assignment operator initializing TracedReference from an existing one.
*/
template <class S>
V8_INLINE TracedReference& operator=(TracedReference<S>&& rhs) noexcept;
/**
* Copy assignment operator initializing TracedGlobal from an existing one.
* Copy assignment operator initializing TracedReference from an existing one.
*/
V8_INLINE TracedReference& operator=(const TracedReference& rhs);
/**
* Copy assignment operator initializing TracedGlobal from an existing one.
* Copy assignment operator initializing TracedReference from an existing one.
*/
template <class S>
V8_INLINE TracedReference& operator=(const TracedReference<S>& rhs);
......@@ -448,18 +290,17 @@ class TracedReference : public BasicTracedReference<T> {
template <class T>
internal::Address* BasicTracedReference<T>::New(
Isolate* isolate, T* that, void* slot,
internal::GlobalHandleDestructionMode destruction_mode,
internal::GlobalHandleStoreMode store_mode) {
if (that == nullptr) return nullptr;
internal::Address* p = reinterpret_cast<internal::Address*>(that);
return internal::GlobalizeTracedReference(
reinterpret_cast<internal::Isolate*>(isolate), p,
reinterpret_cast<internal::Address*>(slot), destruction_mode, store_mode);
reinterpret_cast<internal::Address*>(slot), store_mode);
}
void TracedReferenceBase::Reset() {
if (IsEmpty()) return;
internal::DisposeTracedGlobal(reinterpret_cast<internal::Address*>(val_));
internal::DisposeTracedReference(reinterpret_cast<internal::Address*>(val_));
SetSlotThreadSafe(nullptr);
}
......@@ -513,7 +354,6 @@ void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) {
if (other.IsEmpty()) return;
this->SetSlotThreadSafe(
this->New(isolate, other.val_, &this->val_,
internal::GlobalHandleDestructionMode::kWithoutDestructor,
internal::GlobalHandleStoreMode::kAssigningStore));
}
......@@ -539,7 +379,7 @@ template <class T>
TracedReference<T>& TracedReference<T>::operator=(
TracedReference&& rhs) noexcept {
if (this != &rhs) {
internal::MoveTracedGlobalReference(
internal::MoveTracedReference(
reinterpret_cast<internal::Address**>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
}
......@@ -551,7 +391,7 @@ TracedReference<T>& TracedReference<T>::operator=(const TracedReference& rhs) {
if (this != &rhs) {
this->Reset();
if (rhs.val_ != nullptr) {
internal::CopyTracedGlobalReference(
internal::CopyTracedReference(
reinterpret_cast<const internal::Address* const*>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
}
......@@ -575,63 +415,6 @@ uint16_t TracedReferenceBase::WrapperClassId() const {
return *reinterpret_cast<uint16_t*>(addr);
}
template <class T>
template <class S>
void TracedGlobal<T>::Reset(Isolate* isolate, const Local<S>& other) {
static_assert(std::is_base_of<T, S>::value, "type check");
Reset();
if (other.IsEmpty()) return;
this->val_ = this->New(isolate, other.val_, &this->val_,
internal::GlobalHandleDestructionMode::kWithDestructor,
internal::GlobalHandleStoreMode::kAssigningStore);
}
template <class T>
template <class S>
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal<S>&& rhs) noexcept {
static_assert(std::is_base_of<T, S>::value, "type check");
*this = std::move(rhs.template As<T>());
return *this;
}
template <class T>
template <class S>
TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal<S>& rhs) {
static_assert(std::is_base_of<T, S>::value, "type check");
*this = rhs.template As<T>();
return *this;
}
template <class T>
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) noexcept {
if (this != &rhs) {
internal::MoveTracedGlobalReference(
reinterpret_cast<internal::Address**>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
}
return *this;
}
template <class T>
TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal& rhs) {
if (this != &rhs) {
this->Reset();
if (rhs.val_ != nullptr) {
internal::CopyTracedGlobalReference(
reinterpret_cast<const internal::Address* const*>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_));
}
}
return *this;
}
template <class T>
void TracedGlobal<T>::SetFinalizationCallback(
void* parameter, typename WeakCallbackInfo<void>::Callback callback) {
internal::SetFinalizationCallbackTraced(
reinterpret_cast<internal::Address*>(this->val_), parameter, callback);
}
} // namespace v8
#endif // INCLUDE_V8_TRACED_HANDLE_H_
......@@ -807,17 +807,16 @@ void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory,
namespace internal {
i::Address* GlobalizeTracedReference(
i::Isolate* isolate, i::Address* obj, internal::Address* slot,
GlobalHandleDestructionMode destruction_mode,
i::Address* GlobalizeTracedReference(i::Isolate* isolate, i::Address* obj,
internal::Address* slot,
GlobalHandleStoreMode store_mode) {
LOG_API(isolate, TracedGlobal, New);
#ifdef DEBUG
Utils::ApiCheck((slot != nullptr), "v8::GlobalizeTracedReference",
"the address slot must be not null");
#endif
i::Handle<i::Object> result = isolate->global_handles()->CreateTraced(
*obj, slot, destruction_mode, store_mode);
i::Handle<i::Object> result =
isolate->global_handles()->CreateTraced(*obj, slot, store_mode);
#ifdef VERIFY_HEAP
if (i::FLAG_verify_heap) {
i::Object(*obj).ObjectVerify(isolate);
......@@ -826,24 +825,17 @@ i::Address* GlobalizeTracedReference(
return result.location();
}
void MoveTracedGlobalReference(internal::Address** from,
internal::Address** to) {
GlobalHandles::MoveTracedGlobal(from, to);
void MoveTracedReference(internal::Address** from, internal::Address** to) {
GlobalHandles::MoveTracedReference(from, to);
}
void CopyTracedGlobalReference(const internal::Address* const* from,
void CopyTracedReference(const internal::Address* const* from,
internal::Address** to) {
GlobalHandles::CopyTracedGlobal(from, to);
}
void DisposeTracedGlobal(internal::Address* location) {
GlobalHandles::DestroyTraced(location);
GlobalHandles::CopyTracedReference(from, to);
}
void SetFinalizationCallbackTraced(internal::Address* location, void* parameter,
WeakCallbackInfo<void>::Callback callback) {
GlobalHandles::SetFinalizationCallbackForTraced(location, parameter,
callback);
void DisposeTracedReference(internal::Address* location) {
GlobalHandles::DestroyTracedReference(location);
}
} // namespace internal
......@@ -10277,11 +10269,6 @@ bool EmbedderHeapTracer::IsRootForNonTracingGC(
return true;
}
bool EmbedderHeapTracer::IsRootForNonTracingGC(
const v8::TracedGlobal<v8::Value>& handle) {
return true;
}
void EmbedderHeapTracer::ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) {
UNREACHABLE();
......
......@@ -33,10 +33,6 @@ namespace internal {
namespace {
// Specifies whether V8 expects the holder memory of a global handle to be live
// or dead.
enum class HandleHolder { kLive, kDead };
constexpr size_t kBlockSize = 256;
} // namespace
......@@ -574,8 +570,7 @@ class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
set_state(NEAR_DEATH);
}
void ResetPhantomHandle(HandleHolder handle_holder) {
DCHECK_EQ(HandleHolder::kLive, handle_holder);
void ResetPhantomHandle() {
DCHECK_EQ(PHANTOM_WEAK_RESET_HANDLE, weakness_type());
DCHECK_EQ(PENDING, state());
DCHECK_NULL(weak_callback_);
......@@ -653,7 +648,6 @@ class GlobalHandles::TracedNode final
void MarkAsUsed() { set_state(NORMAL); }
bool IsInUse() const { return state() != FREE; }
bool IsRetainer() const { return state() == NORMAL; }
bool IsPhantomResetHandle() const { return callback_ == nullptr; }
bool is_in_young_list() const { return IsInYoungList::decode(flags_); }
void set_in_young_list(bool v) { flags_ = IsInYoungList::update(flags_, v); }
......@@ -661,9 +655,6 @@ class GlobalHandles::TracedNode final
bool is_root() const { return IsRoot::decode(flags_); }
void set_root(bool v) { flags_ = IsRoot::update(flags_, v); }
bool has_destructor() const { return HasDestructor::decode(flags_); }
void set_has_destructor(bool v) { flags_ = HasDestructor::update(flags_, v); }
bool markbit() const { return Markbit::decode(flags_); }
void clear_markbit() { flags_ = Markbit::update(flags_, false); }
void set_markbit() { flags_ = Markbit::update(flags_, true); }
......@@ -673,44 +664,10 @@ class GlobalHandles::TracedNode final
void clear_object() { object_ = kNullAddress; }
void SetFinalizationCallback(void* parameter,
WeakCallbackInfo<void>::Callback callback) {
set_parameter(parameter);
callback_ = callback;
}
bool HasFinalizationCallback() const { return callback_ != nullptr; }
void CopyObjectReference(const TracedNode& other) { object_ = other.object_; }
void CollectPhantomCallbackData(
std::vector<std::pair<TracedNode*, PendingPhantomCallback>>*
pending_phantom_callbacks) {
void ResetPhantomHandle() {
DCHECK(IsInUse());
DCHECK_NOT_NULL(callback_);
void* embedder_fields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
nullptr};
ExtractInternalFields(JSObject::cast(object()), embedder_fields,
v8::kEmbedderFieldsInWeakCallback);
// Zap with something dangerous.
location().store(Object(0xCA11));
pending_phantom_callbacks->push_back(std::make_pair(
this, PendingPhantomCallback(callback_, parameter(), embedder_fields)));
set_state(NEAR_DEATH);
}
void ResetPhantomHandle(HandleHolder handle_holder) {
DCHECK(IsInUse());
// Even if the handle holder should be alive, the back reference may have
// been cleared which prevents the handle from being reclaimed at this
// point. This can happen for explicitly reset handles during incremental
// marking that then cannot be reclaimed during Scavenge.
if (handle_holder == HandleHolder::kLive && data_.parameter) {
Address** handle = reinterpret_cast<Address**>(data_.parameter);
*handle = nullptr;
}
NodeSpace<TracedNode>::Release(this);
DCHECK(!IsInUse());
}
......@@ -721,27 +678,21 @@ class GlobalHandles::TracedNode final
using NodeState = base::BitField8<State, 0, 2>;
using IsInYoungList = NodeState::Next<bool, 1>;
using IsRoot = IsInYoungList::Next<bool, 1>;
using HasDestructor = IsRoot::Next<bool, 1>;
using Markbit = HasDestructor::Next<bool, 1>;
using Markbit = IsRoot::Next<bool, 1>;
using IsOnStack = Markbit::Next<bool, 1>;
void ClearImplFields() {
set_root(true);
// Nodes are black allocated for simplicity.
set_markbit();
callback_ = nullptr;
set_is_on_stack(false);
set_has_destructor(false);
}
void CheckImplFieldsAreCleared() const {
DCHECK(is_root());
DCHECK(markbit());
DCHECK_NULL(callback_);
}
WeakCallbackInfo<void>::Callback callback_;
friend class NodeBase<GlobalHandles::TracedNode>;
};
......@@ -902,9 +853,6 @@ void GlobalHandles::TracedNode::Verify(GlobalHandles* global_handles,
#ifdef DEBUG
const TracedNode* node = FromLocation(*slot);
DCHECK(node->IsInUse());
DCHECK_IMPLIES(!node->has_destructor(), nullptr == node->parameter());
DCHECK_IMPLIES(node->has_destructor() && !node->HasFinalizationCallback(),
node->parameter());
bool slot_on_stack = global_handles->on_stack_nodes_->IsOnStack(
reinterpret_cast<uintptr_t>(slot));
DCHECK_EQ(slot_on_stack, node->is_on_stack());
......@@ -971,17 +919,16 @@ Handle<Object> GlobalHandles::Create(Address value) {
return Create(Object(value));
}
Handle<Object> GlobalHandles::CreateTraced(
Object value, Address* slot, GlobalHandleDestructionMode destruction_mode,
Handle<Object> GlobalHandles::CreateTraced(Object value, Address* slot,
GlobalHandleStoreMode store_mode) {
return CreateTraced(
value, slot, destruction_mode, store_mode,
value, slot, store_mode,
on_stack_nodes_->IsOnStack(reinterpret_cast<uintptr_t>(slot)));
}
Handle<Object> GlobalHandles::CreateTraced(
Object value, Address* slot, GlobalHandleDestructionMode destruction_mode,
GlobalHandleStoreMode store_mode, bool is_on_stack) {
Handle<Object> GlobalHandles::CreateTraced(Object value, Address* slot,
GlobalHandleStoreMode store_mode,
bool is_on_stack) {
GlobalHandles::TracedNode* result;
if (is_on_stack) {
result = on_stack_nodes_->Acquire(value, reinterpret_cast<uintptr_t>(slot));
......@@ -995,17 +942,13 @@ Handle<Object> GlobalHandles::CreateTraced(
WriteBarrier::MarkingFromGlobalHandle(value);
}
}
const bool has_destructor =
destruction_mode == GlobalHandleDestructionMode::kWithDestructor;
result->set_has_destructor(has_destructor);
result->set_parameter(has_destructor ? slot : nullptr);
result->set_parameter(nullptr);
return result->handle();
}
Handle<Object> GlobalHandles::CreateTraced(
Address value, Address* slot, GlobalHandleDestructionMode destruction_mode,
Handle<Object> GlobalHandles::CreateTraced(Address value, Address* slot,
GlobalHandleStoreMode store_mode) {
return CreateTraced(Object(value), slot, destruction_mode, store_mode);
return CreateTraced(Object(value), slot, store_mode);
}
Handle<Object> GlobalHandles::CopyGlobal(Address* location) {
......@@ -1028,23 +971,15 @@ void SetSlotThreadSafe(Address** slot, Address* val) {
} // namespace
// static
void GlobalHandles::CopyTracedGlobal(const Address* const* from, Address** to) {
void GlobalHandles::CopyTracedReference(const Address* const* from,
Address** to) {
DCHECK_NOT_NULL(*from);
DCHECK_NULL(*to);
const TracedNode* node = TracedNode::FromLocation(*from);
// Copying a traced handle with finalization callback is prohibited because
// the callback may require knowing about multiple copies of the traced
// handle.
CHECK_WITH_MSG(!node->HasFinalizationCallback(),
"Copying of references is not supported when "
"SetFinalizationCallback is set.");
GlobalHandles* global_handles =
GlobalHandles::From(const_cast<TracedNode*>(node));
Handle<Object> o = global_handles->CreateTraced(
node->object(), reinterpret_cast<Address*>(to),
node->has_destructor() ? GlobalHandleDestructionMode::kWithDestructor
: GlobalHandleDestructionMode::kWithoutDestructor,
GlobalHandleStoreMode::kAssigningStore);
SetSlotThreadSafe(to, o.location());
TracedNode::Verify(global_handles, from);
......@@ -1070,10 +1005,10 @@ void GlobalHandles::MoveGlobal(Address** from, Address** to) {
// those the callers need to ensure consistency.
}
void GlobalHandles::MoveTracedGlobal(Address** from, Address** to) {
void GlobalHandles::MoveTracedReference(Address** from, Address** to) {
// Fast path for moving from an empty reference.
if (!*from) {
DestroyTraced(*to);
DestroyTracedReference(*to);
SetSlotThreadSafe(to, nullptr);
return;
}
......@@ -1097,17 +1032,6 @@ void GlobalHandles::MoveTracedGlobal(Address** from, Address** to) {
to_on_stack = to_node->is_on_stack();
}
// Moving a traced handle with finalization callback is prohibited because
// the callback may require knowing about multiple copies of the traced
// handle.
CHECK_WITH_MSG(!from_node->HasFinalizationCallback(),
"Moving of references is not supported when "
"SetFinalizationCallback is set.");
// Types in v8.h ensure that we only copy/move handles that have the same
// destructor behavior.
DCHECK_IMPLIES(to_node,
to_node->has_destructor() == from_node->has_destructor());
// Moving.
if (from_on_stack || to_on_stack) {
// Move involving a stack slot.
......@@ -1115,9 +1039,6 @@ void GlobalHandles::MoveTracedGlobal(Address** from, Address** to) {
DCHECK(global_handles);
Handle<Object> o = global_handles->CreateTraced(
from_node->object(), reinterpret_cast<Address*>(to),
from_node->has_destructor()
? GlobalHandleDestructionMode::kWithDestructor
: GlobalHandleDestructionMode::kWithoutDestructor,
GlobalHandleStoreMode::kAssigningStore, to_on_stack);
SetSlotThreadSafe(to, o.location());
to_node = TracedNode::FromLocation(*to);
......@@ -1135,20 +1056,16 @@ void GlobalHandles::MoveTracedGlobal(Address** from, Address** to) {
WriteBarrier::MarkingFromGlobalHandle(to_node->object());
}
}
DestroyTraced(*from);
DestroyTracedReference(*from);
SetSlotThreadSafe(from, nullptr);
} else {
// Pure heap move.
DestroyTraced(*to);
DestroyTracedReference(*to);
SetSlotThreadSafe(to, *from);
to_node = from_node;
DCHECK_NOT_NULL(*from);
DCHECK_NOT_NULL(*to);
DCHECK_EQ(*from, *to);
// Fixup back reference for destructor.
if (to_node->has_destructor()) {
to_node->set_parameter(to);
}
WriteBarrier::MarkingFromGlobalHandle(to_node->object());
SetSlotThreadSafe(from, nullptr);
}
......@@ -1175,7 +1092,7 @@ void GlobalHandles::Destroy(Address* location) {
}
// static
void GlobalHandles::DestroyTraced(Address* location) {
void GlobalHandles::DestroyTracedReference(Address* location) {
if (location != nullptr) {
TracedNode* node = TracedNode::FromLocation(location);
if (node->is_on_stack()) {
......@@ -1209,20 +1126,9 @@ void GlobalHandles::DestroyTraced(Address* location) {
// next cycle.
node->clear_object();
node->set_parameter(nullptr);
node->SetFinalizationCallback(nullptr, nullptr);
// The destructor setting is left untouched to avoid casting a
// v8::TracedGlobal to a v8::TracedReference for the EmbedderRootsHandler
// which would be UB.
}
}
void GlobalHandles::SetFinalizationCallbackForTraced(
Address* location, void* parameter,
WeakCallbackInfo<void>::Callback callback) {
TracedNode::FromLocation(location)->SetFinalizationCallback(parameter,
callback);
}
using GenericCallback = v8::WeakCallbackInfo<void>::Callback;
void GlobalHandles::MakeWeak(Address* location, void* parameter,
......@@ -1269,7 +1175,7 @@ void GlobalHandles::IterateWeakRootsForPhantomHandles(
should_reset_handle(isolate()->heap(), node->location())) {
if (node->IsPhantomResetHandle()) {
node->MarkPending();
node->ResetPhantomHandle(HandleHolder::kLive);
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPhantomCallback()) {
node->MarkPending();
......@@ -1280,31 +1186,20 @@ void GlobalHandles::IterateWeakRootsForPhantomHandles(
for (TracedNode* node : *traced_nodes_) {
if (!node->IsInUse()) continue;
// Detect unreachable nodes first.
if (!node->markbit() && node->IsPhantomResetHandle() &&
!node->has_destructor()) {
// The handle is unreachable and does not have a callback and a
// destructor associated with it. We can clear it even if the target V8
// object is alive. Note that the desctructor and the callback may
// access the handle, that is why we avoid clearing it.
node->ResetPhantomHandle(HandleHolder::kDead);
if (!node->markbit()) {
// The handle itself is unreachable. We can clear it even if the target V8
// object is alive.
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
continue;
} else if (node->markbit()) {
}
// Clear the markbit for the next GC.
node->clear_markbit();
}
DCHECK(node->IsInUse());
// Detect nodes with unreachable target objects.
if (should_reset_handle(isolate()->heap(), node->location())) {
// If the node allows eager resetting, then reset it here. Otherwise,
// collect its callback that will reset it.
if (node->IsPhantomResetHandle()) {
node->ResetPhantomHandle(node->has_destructor() ? HandleHolder::kLive
: HandleHolder::kDead);
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else {
node->CollectPhantomCallbackData(&traced_pending_phantom_callbacks_);
}
}
}
}
......@@ -1335,18 +1230,11 @@ void GlobalHandles::IdentifyWeakUnmodifiedObjects(
DCHECK(node->is_root());
if (is_unmodified(node->location())) {
v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) {
START_ALLOW_USE_DEPRECATED()
node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value)));
END_ALLOW_USE_DEPRECATED()
} else {
node->set_root(handler->IsRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value)));
}
}
}
}
}
void GlobalHandles::IterateYoungStrongAndDependentRoots(RootVisitor* v) {
......@@ -1397,7 +1285,7 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
DCHECK(node->IsPhantomResetHandle() || node->IsPhantomCallback());
if (node->IsPhantomResetHandle()) {
node->MarkPending();
node->ResetPhantomHandle(HandleHolder::kLive);
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPhantomCallback()) {
node->MarkPending();
......@@ -1422,25 +1310,13 @@ void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
DCHECK_IMPLIES(node->is_root(),
!should_reset_handle(isolate_->heap(), node->location()));
if (should_reset_handle(isolate_->heap(), node->location())) {
if (node->IsPhantomResetHandle()) {
if (node->has_destructor()) {
// For handles with destructor it is guaranteed that the embedder
// memory is still alive as the destructor would have otherwise
// removed the memory.
node->ResetPhantomHandle(HandleHolder::kLive);
} else {
v8::Value* value = ToApi<v8::Value>(node->handle());
handler->ResetRoot(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
// We cannot check whether a node is in use here as the reset behavior
// depends on whether incremental marking is running when reclaiming
// young objects.
}
++number_of_phantom_handle_resets_;
} else {
node->CollectPhantomCallbackData(&traced_pending_phantom_callbacks_);
}
} else {
if (!node->is_root()) {
node->set_root(true);
......@@ -1724,17 +1600,10 @@ void GlobalHandles::IterateTracedNodes(
for (TracedNode* node : *traced_nodes_) {
if (node->IsInUse()) {
v8::Value* value = ToApi<v8::Value>(node->handle());
if (node->has_destructor()) {
START_ALLOW_USE_DEPRECATED()
visitor->VisitTracedGlobalHandle(
*reinterpret_cast<v8::TracedGlobal<v8::Value>*>(&value));
END_ALLOW_USE_DEPRECATED()
} else {
visitor->VisitTracedReference(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
}
}
}
}
DISABLE_CFI_PERF
......
......@@ -90,12 +90,9 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
// API for traced handles.
//
static void MoveTracedGlobal(Address** from, Address** to);
static void CopyTracedGlobal(const Address* const* from, Address** to);
static void DestroyTraced(Address* location);
static void SetFinalizationCallbackForTraced(
Address* location, void* parameter,
WeakCallbackInfo<void>::Callback callback);
static void MoveTracedReference(Address** from, Address** to);
static void CopyTracedReference(const Address* const* from, Address** to);
static void DestroyTracedReference(Address* location);
static void MarkTraced(Address* location);
explicit GlobalHandles(Isolate* isolate);
......@@ -109,14 +106,11 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
inline Handle<T> Create(T value);
Handle<Object> CreateTraced(Object value, Address* slot,
GlobalHandleDestructionMode destruction_mode,
GlobalHandleStoreMode store_mode,
bool is_on_stack);
Handle<Object> CreateTraced(Object value, Address* slot,
GlobalHandleDestructionMode destruction_mode,
GlobalHandleStoreMode store_mode);
Handle<Object> CreateTraced(Address value, Address* slot,
GlobalHandleDestructionMode destruction_mode,
GlobalHandleStoreMode store_mode);
void RecordStats(HeapStats* stats);
......
......@@ -64,10 +64,6 @@ class V8ToCppGCReferencesVisitor final
isolate_(isolate),
wrapper_descriptor_(wrapper_descriptor) {}
void VisitTracedGlobalHandle(const v8::TracedGlobal<v8::Value>&) final {
UNREACHABLE();
}
void VisitTracedReference(const v8::TracedReference<v8::Value>& value) final {
VisitHandle(value, value.WrapperClassId());
}
......
......@@ -6,7 +6,6 @@
#include "include/v8-cppgc.h"
#include "src/base/logging.h"
#include "src/common/allow-deprecated.h"
#include "src/handles/global-handles.h"
#include "src/heap/embedder-tracing-inl.h"
#include "src/heap/gc-tracer.h"
......@@ -211,13 +210,6 @@ bool DefaultEmbedderRootsHandler::IsRoot(
return !tracer_ || tracer_->IsRootForNonTracingGC(handle);
}
START_ALLOW_USE_DEPRECATED()
bool DefaultEmbedderRootsHandler::IsRoot(
const v8::TracedGlobal<v8::Value>& handle) {
return !tracer_ || tracer_->IsRootForNonTracingGC(handle);
}
END_ALLOW_USE_DEPRECATED()
void DefaultEmbedderRootsHandler::ResetRoot(
const v8::TracedReference<v8::Value>& handle) {
// Resetting is only called when IsRoot() returns false which
......
......@@ -27,10 +27,6 @@ class V8_EXPORT_PRIVATE DefaultEmbedderRootsHandler final
public:
bool IsRoot(const v8::TracedReference<v8::Value>& handle) final;
START_ALLOW_USE_DEPRECATED()
bool IsRoot(const v8::TracedGlobal<v8::Value>& handle) final;
END_ALLOW_USE_DEPRECATED()
void ResetRoot(const v8::TracedReference<v8::Value>& handle) final;
void SetTracer(EmbedderHeapTracer* tracer) { tracer_ = tracer; }
......
......@@ -1032,22 +1032,15 @@
'test-debug/TerminateOnResumeFromOtherThread': [SKIP],
'test-debug/TerminateOnResumeRunJavaScriptAtBreakpoint': [SKIP],
'test-debug/TerminateOnResumeRunMicrotaskAtBreakpoint': [SKIP],
'test-embedder-tracing/BasicTracedReference': [SKIP],
'test-embedder-tracing/GarbageCollectionForTesting': [SKIP],
'test-embedder-tracing/NotifyEmptyStack': [SKIP],
'test-embedder-tracing/TracedGlobalCopy': [SKIP],
'test-embedder-tracing/TracedGlobalCopyNoDestructor': [SKIP],
'test-embedder-tracing/TracedGlobalCopyWithDestructor': [SKIP],
'test-embedder-tracing/TracedGlobalDestructor': [SKIP],
'test-embedder-tracing/TracedGlobalInStdUnorderedMap': [SKIP],
'test-embedder-tracing/TracedGlobalInStdVector': [SKIP],
'test-embedder-tracing/TracedGlobalMove': [SKIP],
'test-embedder-tracing/TracedGlobalNoDestructor': [SKIP],
'test-embedder-tracing/TracedGlobalSetFinalizationCallbackMarkSweep': [SKIP],
'test-embedder-tracing/TracedGlobalToUnmodifiedJSObjectDiesOnMarkSweep': [SKIP],
'test-embedder-tracing/TracedReferenceCopyReferences': [SKIP],
'test-embedder-tracing/TracedReferenceCopy': [SKIP],
'test-embedder-tracing/TracedReferenceHandlesDoNotLeak': [SKIP],
'test-embedder-tracing/TracedReferenceHandlesMarking': [SKIP],
'test-embedder-tracing/TracedReferenceMove': [SKIP],
'test-embedder-tracing/TracedReferenceToUnmodifiedJSObjectDiesOnMarkSweep': [SKIP],
'test-embedder-tracing/TracingInEphemerons': [SKIP],
'test-embedder-tracing/TracingInRevivedSubgraph': [SKIP],
'test-embedder-tracing/V8RegisteringEmbedderReference': [SKIP],
......
......@@ -70,19 +70,11 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
embedder_fields.begin(), embedder_fields.end());
}
void AddReferenceForTracing(v8::TracedGlobal<v8::Value>* global) {
to_register_with_v8_.push_back(global);
}
void AddReferenceForTracing(v8::TracedReference<v8::Value>* ref) {
to_register_with_v8_references_.push_back(ref);
}
bool AdvanceTracing(double deadline_in_ms) final {
for (auto global : to_register_with_v8_) {
RegisterEmbedderReference(global->As<v8::Data>());
}
to_register_with_v8_.clear();
for (auto ref : to_register_with_v8_references_) {
RegisterEmbedderReference(ref->As<v8::Data>());
}
......@@ -90,7 +82,7 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
return true;
}
bool IsTracingDone() final { return to_register_with_v8_.empty(); }
bool IsTracingDone() final { return to_register_with_v8_references_.empty(); }
void TracePrologue(EmbedderHeapTracer::TraceFlags) final {
if (prologue_behavior_ == TracePrologueBehavior::kCallV8WriteBarrier) {
......@@ -112,21 +104,31 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
return false;
}
void ConsiderTracedGlobalAsRoot(bool value) {
consider_traced_global_as_root_ = value;
void DoNotConsiderAsRootForScavenge(v8::TracedReference<v8::Value>* handle) {
handle->SetWrapperClassId(17);
non_root_handles_.push_back(handle);
}
bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
return handle.WrapperClassId() != 17;
}
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
return consider_traced_global_as_root_;
void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
for (auto* non_root_handle : non_root_handles_) {
if (*non_root_handle == handle) {
non_root_handle->Reset();
}
}
}
private:
std::vector<std::pair<void*, void*>> registered_from_v8_;
std::vector<v8::TracedGlobal<v8::Value>*> to_register_with_v8_;
std::vector<v8::TracedReference<v8::Value>*> to_register_with_v8_references_;
bool consider_traced_global_as_root_ = true;
TracePrologueBehavior prologue_behavior_ = TracePrologueBehavior::kNoop;
v8::Global<v8::Array> array_;
std::vector<v8::TracedReference<v8::Value>*> non_root_handles_;
};
} // namespace
......@@ -163,16 +165,16 @@ TEST(EmbedderRegisteringV8Reference) {
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::TracedGlobal<v8::Value> g;
auto handle = std::make_unique<v8::TracedReference<v8::Value>>();
{
v8::HandleScope inner_scope(isolate);
v8::Local<v8::Value> o =
v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
g.Reset(isolate, o);
handle->Reset(isolate, o);
}
tracer.AddReferenceForTracing(&g);
tracer.AddReferenceForTracing(handle.get());
CcTest::CollectGarbage(i::OLD_SPACE);
CHECK(!g.IsEmpty());
CHECK(!handle->IsEmpty());
}
namespace {
......@@ -299,12 +301,12 @@ TEST(FinalizeTracingWhenMarking) {
namespace {
void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
v8::TracedGlobal<v8::Object>* global) {
v8::TracedReference<v8::Object>* handle) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(v8::Object::New(isolate));
CHECK(!object.IsEmpty());
*global = v8::TracedGlobal<v8::Object>(isolate, object);
CHECK(!global->IsEmpty());
*handle = v8::TracedReference<v8::Object>(isolate, object);
CHECK(!handle->IsEmpty());
}
template <typename T>
......@@ -320,92 +322,47 @@ void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
enum class SurvivalMode { kSurvives, kDies };
template <typename ModifierFunction, typename ConstructTracedGlobalFunction>
void TracedGlobalTest(v8::Isolate* isolate,
ConstructTracedGlobalFunction construct_function,
ModifierFunction modifier_function, void (*gc_function)(),
SurvivalMode survives) {
template <typename ModifierFunction, typename ConstructTracedReferenceFunction>
void TracedReferenceTest(v8::Isolate* isolate,
ConstructTracedReferenceFunction construct_function,
ModifierFunction modifier_function,
void (*gc_function)(), SurvivalMode survives) {
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
auto* global_handles =
reinterpret_cast<i::Isolate*>(isolate)->global_handles();
auto global = std::make_unique<v8::TracedGlobal<v8::Object>>();
construct_function(isolate, context, global.get());
CHECK(InCorrectGeneration(isolate, *global));
modifier_function(*global);
const size_t initial_count = global_handles->handles_count();
auto handle = std::make_unique<v8::TracedReference<v8::Object>>();
construct_function(isolate, context, handle.get());
CHECK(InCorrectGeneration(isolate, *handle));
modifier_function(*handle);
const size_t after_modification_count = global_handles->handles_count();
gc_function();
CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !global->IsEmpty());
CHECK_IMPLIES(survives == SurvivalMode::kDies, global->IsEmpty());
// Cannot check the handle as it is not explicitly cleared by the GC. Instead
// check the handles count.
CHECK_IMPLIES(survives == SurvivalMode::kSurvives,
after_modification_count == global_handles->handles_count());
CHECK_IMPLIES(survives == SurvivalMode::kDies,
initial_count == global_handles->handles_count());
}
} // namespace
TEST(TracedGlobalReset) {
TEST(TracedReferenceReset) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::TracedGlobal<v8::Object> traced;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
CHECK(!traced.IsEmpty());
traced.Reset();
CHECK(traced.IsEmpty());
}
TEST(TracedGlobalInStdVector) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
std::vector<v8::TracedGlobal<v8::Object>> vec;
{
v8::HandleScope new_scope(isolate);
vec.emplace_back(isolate, v8::Object::New(isolate));
}
CHECK(!vec[0].IsEmpty());
InvokeMarkSweep();
CHECK(vec[0].IsEmpty());
}
TEST(TracedGlobalCopyWithDestructor) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope outer_scope(isolate);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
const size_t initial_count = global_handles->handles_count();
auto global1 = std::make_unique<v8::TracedGlobal<v8::Object>>();
{
v8::HandleScope scope(isolate);
global1->Reset(isolate, v8::Object::New(isolate));
}
auto global2 = std::make_unique<v8::TracedGlobal<v8::Object>>(*global1);
auto global3 = std::make_unique<v8::TracedGlobal<v8::Object>>();
*global3 = *global2;
CHECK_EQ(initial_count + 3, global_handles->handles_count());
CHECK(!global1->IsEmpty());
CHECK_EQ(*global1, *global2);
CHECK_EQ(*global2, *global3);
{
v8::HandleScope scope(isolate);
auto tmp = v8::Local<v8::Object>::New(isolate, *global3);
CHECK(!tmp.IsEmpty());
InvokeMarkSweep();
}
CHECK_EQ(initial_count + 3, global_handles->handles_count());
CHECK(!global1->IsEmpty());
CHECK_EQ(*global1, *global2);
CHECK_EQ(*global2, *global3);
InvokeMarkSweep();
CHECK_EQ(initial_count, global_handles->handles_count());
CHECK(global1->IsEmpty());
CHECK_EQ(*global1, *global2);
CHECK_EQ(*global2, *global3);
v8::TracedReference<v8::Object> handle;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &handle);
CHECK(!handle.IsEmpty());
handle.Reset();
CHECK(handle.IsEmpty());
}
TEST(TracedGlobalCopyNoDestructor) {
TEST(TracedReferenceCopyReferences) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......@@ -413,127 +370,112 @@ TEST(TracedGlobalCopyNoDestructor) {
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
const size_t initial_count = global_handles->handles_count();
auto global1 = std::make_unique<v8::TracedReference<v8::Value>>();
auto handle1 = std::make_unique<v8::TracedReference<v8::Value>>();
{
v8::HandleScope scope(isolate);
global1->Reset(isolate, v8::Object::New(isolate));
handle1->Reset(isolate, v8::Object::New(isolate));
}
auto global2 = std::make_unique<v8::TracedReference<v8::Value>>(*global1);
auto global3 = std::make_unique<v8::TracedReference<v8::Value>>();
*global3 = *global2;
auto handle2 = std::make_unique<v8::TracedReference<v8::Value>>(*handle1);
auto handle3 = std::make_unique<v8::TracedReference<v8::Value>>();
*handle3 = *handle2;
CHECK_EQ(initial_count + 3, global_handles->handles_count());
CHECK(!global1->IsEmpty());
CHECK_EQ(*global1, *global2);
CHECK_EQ(*global2, *global3);
CHECK(!handle1->IsEmpty());
CHECK_EQ(*handle1, *handle2);
CHECK_EQ(*handle2, *handle3);
{
v8::HandleScope scope(isolate);
auto tmp = v8::Local<v8::Value>::New(isolate, *global3);
auto tmp = v8::Local<v8::Value>::New(isolate, *handle3);
CHECK(!tmp.IsEmpty());
InvokeMarkSweep();
}
CHECK_EQ(initial_count + 3, global_handles->handles_count());
CHECK(!global1->IsEmpty());
CHECK_EQ(*global1, *global2);
CHECK_EQ(*global2, *global3);
CHECK(!handle1->IsEmpty());
CHECK_EQ(*handle1, *handle2);
CHECK_EQ(*handle2, *handle3);
InvokeMarkSweep();
CHECK_EQ(initial_count, global_handles->handles_count());
}
TEST(TracedGlobalInStdUnorderedMap) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
std::unordered_map<int, v8::TracedGlobal<v8::Object>> map;
{
v8::HandleScope new_scope(isolate);
map.emplace(std::piecewise_construct, std::forward_as_tuple(1),
std::forward_as_tuple(isolate, v8::Object::New(isolate)));
}
CHECK(!map[1].IsEmpty());
InvokeMarkSweep();
CHECK(map[1].IsEmpty());
}
TEST(TracedGlobalToUnmodifiedJSObjectDiesOnMarkSweep) {
TEST(TracedReferenceToUnmodifiedJSObjectDiesOnMarkSweep) {
// When stressing incremental marking, a write barrier may keep the object
// alive.
if (FLAG_stress_incremental_marking) return;
CcTest::InitializeVM();
TracedGlobalTest(
TracedReferenceTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, [] { InvokeMarkSweep(); },
[](const TracedReference<v8::Object>&) {}, [] { InvokeMarkSweep(); },
SurvivalMode::kDies);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesMarkSweepWhenHeldAliveOtherwise) {
TEST(TracedReferenceToUnmodifiedJSObjectSurvivesMarkSweepWhenHeldAlive) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> strong_global;
TracedGlobalTest(
TracedReferenceTest(
CcTest::isolate(), ConstructJSObject,
[isolate, &strong_global](const TracedGlobal<v8::Object>& global) {
[isolate, &strong_global](const TracedReference<v8::Object>& handle) {
v8::HandleScope scope(isolate);
strong_global = v8::Global<v8::Object>(isolate, global.Get(isolate));
strong_global = v8::Global<v8::Object>(isolate, handle.Get(isolate));
},
[]() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavenge) {
TEST(TracedReferenceToUnmodifiedJSObjectSurvivesScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TracedGlobalTest(
TracedReferenceTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, []() { InvokeScavenge(); },
[](const TracedReference<v8::Object>&) {}, []() { InvokeScavenge(); },
SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavengeWhenExcludedFromRoots) {
TEST(TracedReferenceToUnmodifiedJSObjectSurvivesScavengeWhenExcludedFromRoots) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(false);
TracedGlobalTest(
TracedReferenceTest(
CcTest::isolate(), ConstructJSObject,
[](const TracedGlobal<v8::Object>& global) {}, []() { InvokeScavenge(); },
SurvivalMode::kSurvives);
[&tracer](const TracedReference<v8::Object>& handle) {
tracer.DoNotConsiderAsRootForScavenge(&handle.As<v8::Value>());
},
[]() { InvokeScavenge(); }, SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSApiObjectSurvivesScavengePerDefault) {
TEST(TracedReferenceToUnmodifiedJSApiObjectSurvivesScavengePerDefault) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(true);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject<TracedGlobal<v8::Object>>,
[](const TracedGlobal<v8::Object>& global) {}, []() { InvokeScavenge(); },
TracedReferenceTest(
CcTest::isolate(), ConstructJSApiObject<TracedReference<v8::Object>>,
[](const TracedReference<v8::Object>&) {}, []() { InvokeScavenge(); },
SurvivalMode::kSurvives);
}
TEST(TracedGlobalToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) {
TEST(
TracedReferenceToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
tracer.ConsiderTracedGlobalAsRoot(false);
TracedGlobalTest(
CcTest::isolate(), ConstructJSApiObject<TracedGlobal<v8::Object>>,
[](const TracedGlobal<v8::Object>& global) {}, []() { InvokeScavenge(); },
SurvivalMode::kDies);
TracedReferenceTest(
CcTest::isolate(), ConstructJSApiObject<TracedReference<v8::Object>>,
[&tracer](const TracedReference<v8::Object>& handle) {
tracer.DoNotConsiderAsRootForScavenge(&handle.As<v8::Value>());
},
[]() { InvokeScavenge(); }, SurvivalMode::kDies);
}
TEST(TracedGlobalWrapperClassId) {
TEST(TracedReferenceWrapperClassId) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......@@ -541,7 +483,7 @@ TEST(TracedGlobalWrapperClassId) {
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
v8::TracedGlobal<v8::Object> traced;
v8::TracedReference<v8::Object> traced;
ConstructJSObject(isolate, isolate->GetCurrentContext(), &traced);
CHECK_EQ(0, traced.WrapperClassId());
traced.SetWrapperClassId(17);
......@@ -599,30 +541,14 @@ TEST(TracedReferenceHandlesDoNotLeak) {
CHECK_EQ(initial_count, final_count + 1);
}
TEST(TracedGlobalHandlesAreRetained) {
// TracedGlobal handles are cleared by the destructor of the embedder object.
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::TracedGlobal<v8::Value> global;
global.Reset(isolate, v8::Undefined(isolate));
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
const size_t initial_count = global_handles->handles_count();
// We need two GCs because handles are black allocated.
InvokeMarkSweep();
InvokeMarkSweep();
const size_t final_count = global_handles->handles_count();
CHECK_EQ(initial_count, final_count);
}
namespace {
class TracedGlobalVisitor final
class TracedReferenceVisitor final
: public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor {
public:
~TracedGlobalVisitor() override = default;
void VisitTracedGlobalHandle(const TracedGlobal<Value>& value) final {
~TracedReferenceVisitor() override = default;
void VisitTracedReference(const TracedReference<Value>& value) final {
if (value.WrapperClassId() == 57) {
count_++;
}
......@@ -636,7 +562,7 @@ class TracedGlobalVisitor final
} // namespace
TEST(TracedGlobalIteration) {
TEST(TracedReferenceIteration) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......@@ -644,11 +570,11 @@ TEST(TracedGlobalIteration) {
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
auto traced = std::make_unique<v8::TracedGlobal<v8::Object>>();
ConstructJSObject(isolate, isolate->GetCurrentContext(), traced.get());
CHECK(!traced->IsEmpty());
traced->SetWrapperClassId(57);
TracedGlobalVisitor visitor;
auto handle = std::make_unique<v8::TracedReference<v8::Object>>();
ConstructJSObject(isolate, isolate->GetCurrentContext(), handle.get());
CHECK(!handle->IsEmpty());
handle->SetWrapperClassId(57);
TracedReferenceVisitor visitor;
{
v8::HandleScope new_scope(isolate);
tracer.IterateTracedGlobalHandles(&visitor);
......@@ -656,64 +582,6 @@ TEST(TracedGlobalIteration) {
CHECK_EQ(1, visitor.count());
}
namespace {
void FinalizationCallback(const WeakCallbackInfo<void>& data) {
v8::TracedGlobal<v8::Object>* traced =
reinterpret_cast<v8::TracedGlobal<v8::Object>*>(data.GetParameter());
CHECK_EQ(reinterpret_cast<void*>(0x4), data.GetInternalField(0));
CHECK_EQ(reinterpret_cast<void*>(0x8), data.GetInternalField(1));
traced->Reset();
}
} // namespace
TEST(TracedGlobalSetFinalizationCallbackScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
TestEmbedderHeapTracer tracer;
tracer.ConsiderTracedGlobalAsRoot(false);
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
auto traced = std::make_unique<v8::TracedGlobal<v8::Object>>();
ConstructJSApiObject(isolate, isolate->GetCurrentContext(), traced.get());
CHECK(!traced->IsEmpty());
{
v8::HandleScope new_scope(isolate);
auto local = traced->Get(isolate);
local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
}
traced->SetFinalizationCallback(traced.get(), FinalizationCallback);
heap::InvokeScavenge();
CHECK(traced->IsEmpty());
}
TEST(TracedGlobalSetFinalizationCallbackMarkSweep) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
auto traced = std::make_unique<v8::TracedGlobal<v8::Object>>();
ConstructJSApiObject(isolate, isolate->GetCurrentContext(), traced.get());
CHECK(!traced->IsEmpty());
{
v8::HandleScope new_scope(isolate);
auto local = traced->Get(isolate);
local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
}
traced->SetFinalizationCallback(traced.get(), FinalizationCallback);
heap::InvokeMarkSweep();
CHECK(traced->IsEmpty());
}
TEST(TracePrologueCallingIntoV8WriteBarrier) {
// Regression test: https://crbug.com/940003
if (!FLAG_incremental_marking) return;
......@@ -736,34 +604,7 @@ TEST(TracePrologueCallingIntoV8WriteBarrier) {
heap::InvokeMarkSweep();
}
TEST(TracedGlobalWithDestructor) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
const size_t initial_count = global_handles->handles_count();
auto* traced = new v8::TracedGlobal<v8::Object>();
{
v8::HandleScope new_scope(isolate);
v8::Local<v8::Object> object(ConstructTraceableJSApiObject(
isolate->GetCurrentContext(), nullptr, nullptr));
CHECK(traced->IsEmpty());
*traced = v8::TracedGlobal<v8::Object>(isolate, object);
CHECK(!traced->IsEmpty());
CHECK_EQ(initial_count + 1, global_handles->handles_count());
}
delete traced;
CHECK_EQ(initial_count, global_handles->handles_count());
// GC should not need to clear the handle.
heap::InvokeMarkSweep();
CHECK_EQ(initial_count, global_handles->handles_count());
}
TEST(TracedGlobalNoDestructor) {
TEST(BasicTracedReference) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......@@ -807,24 +648,7 @@ class EmptyEmbedderHeapTracer : public v8::EmbedderHeapTracer {
};
// EmbedderHeapTracer that can optimize Scavenger handling when used with
// TraceGlobal handles that have destructors.
class EmbedderHeapTracerDestructorNonTracingClearing final
: public EmptyEmbedderHeapTracer {
public:
explicit EmbedderHeapTracerDestructorNonTracingClearing(
uint16_t class_id_to_optimize)
: class_id_to_optimize_(class_id_to_optimize) {}
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
return handle.WrapperClassId() != class_id_to_optimize_;
}
private:
uint16_t class_id_to_optimize_;
};
// EmbedderHeapTracer that can optimize Scavenger handling when used with
// TraceGlobal handles without destructors.
// TracedReference.
class EmbedderHeapTracerNoDestructorNonTracingClearing final
: public EmptyEmbedderHeapTracer {
public:
......@@ -877,33 +701,7 @@ void SetupOptimizedAndNonOptimizedHandle(v8::Isolate* isolate,
} // namespace
TEST(TracedGlobalDestructorReclaimedOnScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
constexpr uint16_t kClassIdToOptimize = 17;
EmbedderHeapTracerDestructorNonTracingClearing tracer(kClassIdToOptimize);
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
i::GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
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>();
SetupOptimizedAndNonOptimizedHandle(isolate, kClassIdToOptimize,
optimized_handle, non_optimized_handle);
CHECK_EQ(initial_count + 2, global_handles->handles_count());
heap::InvokeScavenge();
CHECK_EQ(initial_count + 1, global_handles->handles_count());
CHECK(optimized_handle->IsEmpty());
delete optimized_handle;
CHECK(!non_optimized_handle->IsEmpty());
delete non_optimized_handle;
CHECK_EQ(initial_count, global_handles->handles_count());
}
TEST(TracedGlobalNoDestructorReclaimedOnScavenge) {
TEST(TracedReferenceNoDestructorReclaimedOnScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
......@@ -999,13 +797,13 @@ enum class TargetHandling {
kInitializedOldGen
};
template <typename T>
V8_NOINLINE void StackToHeapTest(TestEmbedderHeapTracer* tracer, Operation op,
TargetHandling target_handling) {
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> observer;
T stack_handle;
T* heap_handle = new T();
v8::TracedReference<v8::Value> stack_handle;
v8::TracedReference<v8::Value>* heap_handle =
new v8::TracedReference<v8::Value>();
if (target_handling != TargetHandling::kNonInitialized) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject(
......@@ -1040,13 +838,13 @@ V8_NOINLINE void StackToHeapTest(TestEmbedderHeapTracer* tracer, Operation op,
delete heap_handle;
}
template <typename T>
V8_NOINLINE void HeapToStackTest(TestEmbedderHeapTracer* tracer, Operation op,
TargetHandling target_handling) {
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> observer;
T stack_handle;
T* heap_handle = new T();
v8::TracedReference<v8::Value> stack_handle;
v8::TracedReference<v8::Value>* heap_handle =
new v8::TracedReference<v8::Value>();
if (target_handling != TargetHandling::kNonInitialized) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject(
......@@ -1081,13 +879,12 @@ V8_NOINLINE void HeapToStackTest(TestEmbedderHeapTracer* tracer, Operation op,
delete heap_handle;
}
template <typename T>
V8_NOINLINE void StackToStackTest(TestEmbedderHeapTracer* tracer, Operation op,
TargetHandling target_handling) {
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> observer;
T stack_handle1;
T stack_handle2;
v8::TracedReference<v8::Value> stack_handle1;
v8::TracedReference<v8::Value> stack_handle2;
if (target_handling != TargetHandling::kNonInitialized) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject(
......@@ -1120,7 +917,6 @@ V8_NOINLINE void StackToStackTest(TestEmbedderHeapTracer* tracer, Operation op,
CHECK(observer.IsEmpty());
}
template <typename T>
V8_NOINLINE void TracedReferenceCleanedTest(TestEmbedderHeapTracer* tracer) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
......@@ -1129,7 +925,7 @@ V8_NOINLINE void TracedReferenceCleanedTest(TestEmbedderHeapTracer* tracer) {
const size_t before =
CcTest::i_isolate()->global_handles()->NumberOfOnStackHandlesForTesting();
for (int i = 0; i < 100; i++) {
T stack_handle;
v8::TracedReference<v8::Value> stack_handle;
stack_handle.Reset(isolate, object);
}
CHECK_EQ(before + 1, CcTest::i_isolate()
......@@ -1137,27 +933,6 @@ V8_NOINLINE void TracedReferenceCleanedTest(TestEmbedderHeapTracer* tracer) {
->NumberOfOnStackHandlesForTesting());
}
V8_NOINLINE void TracedGlobalDestructorTest(TestEmbedderHeapTracer* tracer) {
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> observer;
{
v8::TracedGlobal<v8::Value> stack_handle;
{
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(ConstructTraceableJSApiObject(
isolate->GetCurrentContext(), nullptr, nullptr));
stack_handle.Reset(isolate, object);
observer.Reset(isolate, object);
observer.SetWeak();
}
CHECK(!observer.IsEmpty());
heap::InvokeMarkSweep();
CHECK(!observer.IsEmpty());
}
heap::InvokeMarkSweep();
CHECK(observer.IsEmpty());
}
} // namespace
TEST(TracedReferenceOnStack) {
......@@ -1170,16 +945,6 @@ TEST(TracedReferenceOnStack) {
OnStackTest<v8::TracedReference<v8::Value>>(&tracer);
}
TEST(TracedGlobalOnStack) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
OnStackTest<v8::TracedGlobal<v8::Value>>(&tracer);
}
TEST(TracedReferenceCleaned) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
......@@ -1187,141 +952,57 @@ TEST(TracedReferenceCleaned) {
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
TracedReferenceCleanedTest<v8::TracedReference<v8::Value>>(&tracer);
}
TEST(TracedGlobalCleaned) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
TracedReferenceCleanedTest<v8::TracedGlobal<v8::Value>>(&tracer);
TracedReferenceCleanedTest(&tracer);
}
TEST(TracedReferenceMove) {
using ReferenceType = v8::TracedReference<v8::Value>;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
StackToHeapTest(&tracer, Operation::kMove, TargetHandling::kNonInitialized);
StackToHeapTest(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
StackToHeapTest(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
HeapToStackTest(&tracer, Operation::kMove, TargetHandling::kNonInitialized);
HeapToStackTest(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
HeapToStackTest(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
StackToStackTest(&tracer, Operation::kMove, TargetHandling::kNonInitialized);
StackToStackTest(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
StackToStackTest(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
}
TEST(TracedReferenceCopy) {
using ReferenceType = v8::TracedReference<v8::Value>;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
StackToHeapTest(&tracer, Operation::kCopy, TargetHandling::kNonInitialized);
StackToHeapTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
StackToHeapTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
HeapToStackTest(&tracer, Operation::kCopy, TargetHandling::kNonInitialized);
HeapToStackTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
HeapToStackTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
StackToStackTest(&tracer, Operation::kCopy, TargetHandling::kNonInitialized);
StackToStackTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
StackToStackTest(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
}
TEST(TracedGlobalMove) {
using ReferenceType = v8::TracedGlobal<v8::Value>;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
StackToHeapTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kNonInitialized);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedYoungGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kMove,
TargetHandling::kInitializedOldGen);
}
TEST(TracedGlobalCopy) {
using ReferenceType = v8::TracedGlobal<v8::Value>;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
StackToHeapTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
HeapToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kNonInitialized);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedYoungGen);
StackToStackTest<ReferenceType>(&tracer, Operation::kCopy,
TargetHandling::kInitializedOldGen);
}
TEST(TracedGlobalDestructor) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(CcTest::isolate(),
&tracer);
tracer.SetStackStart(&manual_gc);
TracedGlobalDestructorTest(&tracer);
}
TEST(NotifyEmptyStack) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
......
......@@ -44,6 +44,10 @@ namespace internal {
namespace {
struct TracedReferenceWrapper {
v8::TracedReference<v8::Object> handle;
};
// Empty v8::EmbedderHeapTracer that never keeps objects alive on Scavenge. See
// |IsRootForNonTracingGC|.
class NonRootingEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
......@@ -58,9 +62,26 @@ class NonRootingEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
void TraceEpilogue(TraceSummary*) final {}
void EnterFinalPause(EmbedderStackState) final {}
bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) final {
bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
return false;
}
void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
for (auto* wrapper : wrappers_) {
if (wrapper->handle == handle) {
wrapper->handle.Reset();
}
}
}
void Register(TracedReferenceWrapper* wrapper) {
wrappers_.push_back(wrapper);
}
private:
std::vector<TracedReferenceWrapper*> wrappers_;
};
void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); }
......@@ -76,10 +97,6 @@ struct FlagAndGlobal {
v8::Global<v8::Object> handle;
};
struct TracedGlobalWrapper {
v8::TracedGlobal<v8::Object> handle;
};
void ResetHandleAndSetFlag(const v8::WeakCallbackInfo<FlagAndGlobal>& data) {
data.GetParameter()->handle.Reset();
data.GetParameter()->flag = true;
......@@ -104,12 +121,12 @@ void ConstructJSObject(v8::Isolate* isolate, v8::Global<v8::Object>* global) {
}
void ConstructJSObject(v8::Isolate* isolate,
v8::TracedGlobal<v8::Object>* traced) {
v8::TracedReference<v8::Object>* handle) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(v8::Object::New(isolate));
CHECK(!object.IsEmpty());
*traced = v8::TracedGlobal<v8::Object>(isolate, object);
CHECK(!traced->IsEmpty());
*handle = v8::TracedReference<v8::Object>(isolate, object);
CHECK(!handle->IsEmpty());
}
template <typename HandleContainer>
......@@ -150,12 +167,11 @@ void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function,
CHECK_IMPLIES(survives == SurvivalMode::kDies, fp.flag);
}
template <typename ConstructFunction, typename ModifierFunction,
typename GCFunction>
void TracedGlobalTest(v8::Isolate* isolate,
template <typename ConstructFunction, typename ModifierFunction>
void TracedReferenceTestWithScavenge(v8::Isolate* isolate,
ConstructFunction construct_function,
ModifierFunction modifier_function,
GCFunction gc_function, SurvivalMode survives) {
SurvivalMode survives) {
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
......@@ -163,11 +179,14 @@ void TracedGlobalTest(v8::Isolate* isolate,
NonRootingEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
auto fp = std::make_unique<TracedGlobalWrapper>();
auto fp = std::make_unique<TracedReferenceWrapper>();
tracer.Register(fp.get());
construct_function(isolate, context, fp.get());
CHECK(heap::InCorrectGeneration(isolate, fp->handle));
modifier_function(fp.get());
gc_function();
InvokeScavenge();
// Scavenge clear properly resets the original handle, so we can check the
// handle directly here.
CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !fp->handle.IsEmpty());
CHECK_IMPLIES(survives == SurvivalMode::kDies, fp->handle.IsEmpty());
}
......@@ -343,14 +362,13 @@ TEST(WeakHandleToUnmodifiedJSObjectDiesOnScavenge) {
SurvivalMode::kDies);
}
TEST(TracedGlobalToUnmodifiedJSObjectSurvivesScavenge) {
TEST(TracedReferenceToUnmodifiedJSObjectSurvivesScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TracedGlobalTest(
CcTest::isolate(), &ConstructJSObject<TracedGlobalWrapper>,
[](TracedGlobalWrapper* fp) {}, []() { InvokeScavenge(); },
SurvivalMode::kSurvives);
TracedReferenceTestWithScavenge(
CcTest::isolate(), &ConstructJSObject<TracedReferenceWrapper>,
[](TracedReferenceWrapper* fp) {}, SurvivalMode::kSurvives);
}
TEST(WeakHandleToUnmodifiedJSObjectDiesOnMarkCompact) {
......@@ -382,17 +400,16 @@ TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnScavenge) {
SurvivalMode::kDies);
}
TEST(TracedGlobalToUnmodifiedJSApiObjectDiesOnScavenge) {
TEST(TracedReferenceToUnmodifiedJSApiObjectDiesOnScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
CcTest::InitializeVM();
TracedGlobalTest(
CcTest::isolate(), &ConstructJSApiObject<TracedGlobalWrapper>,
[](TracedGlobalWrapper* fp) {}, []() { InvokeScavenge(); },
SurvivalMode::kDies);
TracedReferenceTestWithScavenge(
CcTest::isolate(), &ConstructJSApiObject<TracedReferenceWrapper>,
[](TracedReferenceWrapper* fp) {}, SurvivalMode::kDies);
}
TEST(TracedGlobalToJSApiObjectWithIdentityHashSurvivesScavenge) {
TEST(TracedReferenceToJSApiObjectWithIdentityHashSurvivesScavenge) {
if (FLAG_single_generation) return;
ManualGCScope manual_gc;
......@@ -401,9 +418,9 @@ TEST(TracedGlobalToJSApiObjectWithIdentityHashSurvivesScavenge) {
HandleScope scope(i_isolate);
Handle<JSWeakMap> weakmap = i_isolate->factory()->NewJSWeakMap();
TracedGlobalTest(
CcTest::isolate(), &ConstructJSApiObject<TracedGlobalWrapper>,
[&weakmap, i_isolate](TracedGlobalWrapper* fp) {
TracedReferenceTestWithScavenge(
CcTest::isolate(), &ConstructJSApiObject<TracedReferenceWrapper>,
[&weakmap, i_isolate](TracedReferenceWrapper* fp) {
v8::HandleScope scope(CcTest::isolate());
Handle<JSReceiver> key =
Utils::OpenHandle(*fp->handle.Get(CcTest::isolate()));
......@@ -411,7 +428,7 @@ TEST(TracedGlobalToJSApiObjectWithIdentityHashSurvivesScavenge) {
int32_t hash = key->GetOrCreateHash(i_isolate).value();
JSWeakCollection::Set(weakmap, key, smi, hash);
},
[]() { InvokeScavenge(); }, SurvivalMode::kSurvives);
SurvivalMode::kSurvives);
}
TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesScavengeWhenInHandle) {
......@@ -447,13 +464,13 @@ TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
[]() { InvokeMarkSweep(); }, SurvivalMode::kSurvives);
}
TEST(TracedGlobalToJSApiObjectWithModifiedMapSurvivesScavenge) {
TEST(TracedReferenceToJSApiObjectWithModifiedMapSurvivesScavenge) {
if (FLAG_single_generation) return;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
LocalContext context;
TracedGlobal<v8::Object> handle;
TracedReference<v8::Object> handle;
{
v8::HandleScope scope(isolate);
// Create an API object which does not have the same map as constructor.
......@@ -469,13 +486,13 @@ TEST(TracedGlobalToJSApiObjectWithModifiedMapSurvivesScavenge) {
CHECK(!handle.IsEmpty());
}
TEST(TracedGlobalTOJsApiObjectWithElementsSurvivesScavenge) {
TEST(TracedReferenceTOJsApiObjectWithElementsSurvivesScavenge) {
if (FLAG_single_generation) return;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
LocalContext context;
TracedGlobal<v8::Object> handle;
TracedReference<v8::Object> handle;
{
v8::HandleScope scope(isolate);
......@@ -717,13 +734,14 @@ TEST(TotalSizeTracedNode) {
Isolate* i_isolate = CcTest::i_isolate();
v8::HandleScope scope(isolate);
v8::TracedGlobal<v8::Object>* global = new TracedGlobal<v8::Object>();
v8::TracedReference<v8::Object>* handle = new TracedReference<v8::Object>();
CHECK_EQ(i_isolate->global_handles()->TotalSize(), 0);
CHECK_EQ(i_isolate->global_handles()->UsedSize(), 0);
ConstructJSObject(isolate, global);
ConstructJSObject(isolate, handle);
CHECK_GT(i_isolate->global_handles()->TotalSize(), 0);
CHECK_GT(i_isolate->global_handles()->UsedSize(), 0);
delete global;
delete handle;
InvokeMarkSweep();
CHECK_GT(i_isolate->global_handles()->TotalSize(), 0);
CHECK_EQ(i_isolate->global_handles()->UsedSize(), 0);
}
......
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