[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:
parent
899479913a
commit
bb5b15c1fd
68
include/v8.h
68
include/v8.h
@ -810,9 +810,9 @@ struct TracedGlobalTrait {
|
||||
};
|
||||
|
||||
/**
|
||||
* A traced handle with move semantics, similar to std::unique_ptr. The handle
|
||||
* is to be used together with |v8::EmbedderHeapTracer| and specifies edges from
|
||||
* the embedder into V8's heap.
|
||||
* A traced handle with copy and move semantics. The handle is to be used
|
||||
* together with |v8::EmbedderHeapTracer| and specifies edges from the embedder
|
||||
* into V8's heap.
|
||||
*
|
||||
* The exact semantics are:
|
||||
* - Tracing garbage collections use |v8::EmbedderHeapTracer|.
|
||||
@ -859,6 +859,23 @@ class TracedGlobal {
|
||||
*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.
|
||||
*/
|
||||
@ -871,10 +888,21 @@ class TracedGlobal {
|
||||
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;
|
||||
void operator=(const TracedGlobal&) = delete;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
static void MoveTracedGlobalReference(internal::Address** from,
|
||||
internal::Address** to);
|
||||
static void CopyTracedGlobalReference(const internal::Address* const* from,
|
||||
internal::Address** to);
|
||||
static internal::Address* CopyGlobalReference(internal::Address* from);
|
||||
static void DisposeGlobal(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_);
|
||||
}
|
||||
|
||||
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>
|
||||
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) {
|
||||
if (this != &rhs) {
|
||||
@ -10165,17 +10211,13 @@ TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class S>
|
||||
TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal<S>&& rhs) {
|
||||
TYPE_CHECK(T, S);
|
||||
TracedGlobal<T>& TracedGlobal<T>::operator=(const TracedGlobal& rhs) {
|
||||
if (this != &rhs) {
|
||||
this->Reset();
|
||||
if (rhs.val_ != nullptr) {
|
||||
this->val_ = rhs.val_;
|
||||
V8::MoveTracedGlobalReference(
|
||||
reinterpret_cast<internal::Address**>(&rhs.val_),
|
||||
V8::CopyTracedGlobalReference(
|
||||
reinterpret_cast<const internal::Address* const*>(&rhs.val_),
|
||||
reinterpret_cast<internal::Address**>(&this->val_));
|
||||
rhs.val_ = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
|
@ -1060,6 +1060,11 @@ void V8::MoveTracedGlobalReference(internal::Address** from,
|
||||
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,
|
||||
WeakCallbackInfo<void>::Callback weak_callback,
|
||||
WeakCallbackType type) {
|
||||
|
@ -36,6 +36,7 @@ class GlobalHandles::NodeBlock final {
|
||||
using BlockType = NodeBlock<_NodeType>;
|
||||
using NodeType = _NodeType;
|
||||
|
||||
V8_INLINE static const NodeBlock* From(const NodeType* node);
|
||||
V8_INLINE static NodeBlock* From(NodeType* node);
|
||||
|
||||
NodeBlock(GlobalHandles* global_handles,
|
||||
@ -70,6 +71,16 @@ class GlobalHandles::NodeBlock final {
|
||||
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>
|
||||
GlobalHandles::NodeBlock<NodeType>* GlobalHandles::NodeBlock<NodeType>::From(
|
||||
NodeType* node) {
|
||||
@ -243,6 +254,10 @@ void GlobalHandles::NodeSpace<NodeType>::Free(NodeType* node) {
|
||||
template <class Child>
|
||||
class NodeBase {
|
||||
public:
|
||||
static const Child* FromLocation(const Address* location) {
|
||||
return reinterpret_cast<const Child*>(location);
|
||||
}
|
||||
|
||||
static Child* FromLocation(Address* location) {
|
||||
return reinterpret_cast<Child*>(location);
|
||||
}
|
||||
@ -730,6 +745,27 @@ Handle<Object> GlobalHandles::CopyGlobal(Address* 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) {
|
||||
DCHECK_NOT_NULL(*from);
|
||||
DCHECK_NOT_NULL(*to);
|
||||
|
@ -81,6 +81,7 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
|
||||
//
|
||||
|
||||
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,
|
||||
|
@ -353,6 +353,85 @@ TEST(TracedGlobalInStdVector) {
|
||||
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) {
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
|
Loading…
Reference in New Issue
Block a user