[api,heap] TracedGlobal: Provide copy operators

Provide copy ctor and assignment operators as it turned out that they are
useful for embedders in certain scenarios when dealing with TracedGlobal
handles without finalization callbacks.

Bug: v8:9660
Change-Id: I2b04f540baeef61a0bc8329ca06b999571cbfe66
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1773250
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63439}
This commit is contained in:
Michael Lippautz 2019-08-28 16:56:24 +02:00 committed by Commit Bot
parent 899479913a
commit bb5b15c1fd
5 changed files with 176 additions and 13 deletions

View File

@ -810,9 +810,9 @@ struct TracedGlobalTrait {
}; };
/** /**
* A traced handle with move semantics, similar to std::unique_ptr. The handle * A traced handle with copy and move semantics. The handle is to be used
* is to be used together with |v8::EmbedderHeapTracer| and specifies edges from * together with |v8::EmbedderHeapTracer| and specifies edges from the embedder
* the embedder into V8's heap. * into V8's heap.
* *
* The exact semantics are: * The exact semantics are:
* - Tracing garbage collections use |v8::EmbedderHeapTracer|. * - Tracing garbage collections use |v8::EmbedderHeapTracer|.
@ -859,6 +859,23 @@ class TracedGlobal {
*this = std::move(other); *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. * Move assignment operator initializing TracedGlobal from an existing one.
*/ */
@ -871,10 +888,21 @@ class TracedGlobal {
V8_INLINE TracedGlobal& operator=(TracedGlobal<S>&& rhs); V8_INLINE TracedGlobal& operator=(TracedGlobal<S>&& rhs);
/** /**
* TracedGlobal only supports move semantics and forbids copying. * Copy assignment operator initializing TracedGlobal from an existing one.
*
* Note: Prohibited when |other| has a finalization callback set through
* |SetFinalizationCallback|.
*/ */
TracedGlobal(const TracedGlobal&) = delete; V8_INLINE TracedGlobal& operator=(const TracedGlobal& rhs);
void operator=(const TracedGlobal&) = delete;
/**
* 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);
/** /**
* Returns true if this TracedGlobal is empty, i.e., has not been assigned an * Returns true if this TracedGlobal is empty, i.e., has not been assigned an
@ -9035,6 +9063,8 @@ class V8_EXPORT V8 {
internal::Address** to); internal::Address** to);
static void MoveTracedGlobalReference(internal::Address** from, static void MoveTracedGlobalReference(internal::Address** from,
internal::Address** to); internal::Address** to);
static void CopyTracedGlobalReference(const internal::Address* const* from,
internal::Address** to);
static internal::Address* CopyGlobalReference(internal::Address* from); static internal::Address* CopyGlobalReference(internal::Address* from);
static void DisposeGlobal(internal::Address* global_handle); static void DisposeGlobal(internal::Address* global_handle);
static void DisposeTracedGlobal(internal::Address* global_handle); static void DisposeTracedGlobal(internal::Address* global_handle);
@ -10149,6 +10179,22 @@ void TracedGlobal<T>::Reset(Isolate* isolate, const Local<S>& other) {
this->val_ = New(isolate, other.val_, &val_); this->val_ = New(isolate, other.val_, &val_);
} }
template <class T>
template <class S>
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal<S>&& rhs) {
TYPE_CHECK(T, S);
*this = std::move(rhs.template As<T>());
return *this;
}
template <class T>
template <class S>
TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal<S>& rhs) {
TYPE_CHECK(T, S);
*this = rhs.template As<T>();
return *this;
}
template <class T> template <class T>
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) { TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) {
if (this != &rhs) { if (this != &rhs) {
@ -10165,17 +10211,13 @@ TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) {
} }
template <class T> template <class T>
template <class S> TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal& rhs) {
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal<S>&& rhs) {
TYPE_CHECK(T, S);
if (this != &rhs) { if (this != &rhs) {
this->Reset(); this->Reset();
if (rhs.val_ != nullptr) { if (rhs.val_ != nullptr) {
this->val_ = rhs.val_; V8::CopyTracedGlobalReference(
V8::MoveTracedGlobalReference( reinterpret_cast<const internal::Address* const*>(&rhs.val_),
reinterpret_cast<internal::Address**>(&rhs.val_),
reinterpret_cast<internal::Address**>(&this->val_)); reinterpret_cast<internal::Address**>(&this->val_));
rhs.val_ = nullptr;
} }
} }
return *this; return *this;

View File

@ -1060,6 +1060,11 @@ void V8::MoveTracedGlobalReference(internal::Address** from,
i::GlobalHandles::MoveTracedGlobal(from, to); i::GlobalHandles::MoveTracedGlobal(from, to);
} }
void V8::CopyTracedGlobalReference(const internal::Address* const* from,
internal::Address** to) {
i::GlobalHandles::CopyTracedGlobal(from, to);
}
void V8::MakeWeak(i::Address* location, void* parameter, void V8::MakeWeak(i::Address* location, void* parameter,
WeakCallbackInfo<void>::Callback weak_callback, WeakCallbackInfo<void>::Callback weak_callback,
WeakCallbackType type) { WeakCallbackType type) {

View File

@ -36,6 +36,7 @@ class GlobalHandles::NodeBlock final {
using BlockType = NodeBlock<_NodeType>; using BlockType = NodeBlock<_NodeType>;
using NodeType = _NodeType; using NodeType = _NodeType;
V8_INLINE static const NodeBlock* From(const NodeType* node);
V8_INLINE static NodeBlock* From(NodeType* node); V8_INLINE static NodeBlock* From(NodeType* node);
NodeBlock(GlobalHandles* global_handles, NodeBlock(GlobalHandles* global_handles,
@ -70,6 +71,16 @@ class GlobalHandles::NodeBlock final {
DISALLOW_COPY_AND_ASSIGN(NodeBlock); DISALLOW_COPY_AND_ASSIGN(NodeBlock);
}; };
template <class NodeType>
const GlobalHandles::NodeBlock<NodeType>*
GlobalHandles::NodeBlock<NodeType>::From(const NodeType* node) {
uintptr_t ptr = reinterpret_cast<const uintptr_t>(node) -
sizeof(NodeType) * node->index();
const BlockType* block = reinterpret_cast<const BlockType*>(ptr);
DCHECK_EQ(node, block->at(node->index()));
return block;
}
template <class NodeType> template <class NodeType>
GlobalHandles::NodeBlock<NodeType>* GlobalHandles::NodeBlock<NodeType>::From( GlobalHandles::NodeBlock<NodeType>* GlobalHandles::NodeBlock<NodeType>::From(
NodeType* node) { NodeType* node) {
@ -243,6 +254,10 @@ void GlobalHandles::NodeSpace<NodeType>::Free(NodeType* node) {
template <class Child> template <class Child>
class NodeBase { class NodeBase {
public: public:
static const Child* FromLocation(const Address* location) {
return reinterpret_cast<const Child*>(location);
}
static Child* FromLocation(Address* location) { static Child* FromLocation(Address* location) {
return reinterpret_cast<Child*>(location); return reinterpret_cast<Child*>(location);
} }
@ -730,6 +745,27 @@ Handle<Object> GlobalHandles::CopyGlobal(Address* location) {
return global_handles->Create(*location); return global_handles->Create(*location);
} }
// static
void GlobalHandles::CopyTracedGlobal(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(!node->HasFinalizationCallback());
GlobalHandles* global_handles =
NodeBlock<TracedNode>::From(node)->global_handles();
Handle<Object> o = global_handles->CreateTraced(
node->object(), reinterpret_cast<Address*>(to), node->has_destructor());
*to = o.location();
#ifdef VERIFY_HEAP
if (i::FLAG_verify_heap) {
Object(**to).ObjectVerify(global_handles->isolate());
}
#endif // VERIFY_HEAP
}
void GlobalHandles::MoveGlobal(Address** from, Address** to) { void GlobalHandles::MoveGlobal(Address** from, Address** to) {
DCHECK_NOT_NULL(*from); DCHECK_NOT_NULL(*from);
DCHECK_NOT_NULL(*to); DCHECK_NOT_NULL(*to);

View File

@ -81,6 +81,7 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
// //
static void MoveTracedGlobal(Address** from, Address** to); static void MoveTracedGlobal(Address** from, Address** to);
static void CopyTracedGlobal(const Address* const* from, Address** to);
static void DestroyTraced(Address* location); static void DestroyTraced(Address* location);
static void SetFinalizationCallbackForTraced( static void SetFinalizationCallbackForTraced(
Address* location, void* parameter, Address* location, void* parameter,

View File

@ -353,6 +353,85 @@ TEST(TracedGlobalInStdVector) {
CHECK(vec[0].IsEmpty()); CHECK(vec[0].IsEmpty());
} }
TEST(TracedGlobalCopyWithDestructor) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
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;
{
v8::HandleScope scope(isolate);
global1.Reset(isolate, v8::Object::New(isolate));
}
v8::TracedGlobal<v8::Object> global2(global1);
v8::TracedGlobal<v8::Object> global3;
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);
}
TEST(TracedGlobalCopyNoDestructor) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
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::HandleScope scope(isolate);
global1.Reset(isolate, v8::Object::New(isolate));
}
v8::TracedGlobal<v8::Value> global2(global1);
v8::TracedGlobal<v8::Value> global3;
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::Value>::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());
}
TEST(TracedGlobalInStdUnorderedMap) { TEST(TracedGlobalInStdUnorderedMap) {
ManualGCScope manual_gc; ManualGCScope manual_gc;
CcTest::InitializeVM(); CcTest::InitializeVM();