[api,global-handle] Introduce TracedGlobal::SetFinalizationCallback
Introduce a way to set a custom finalization callback that can be used to signal and set up destruction of embedder memory. Bug: chromium:923361 Change-Id: Ifc62ebd534aba3b02511c74b59161ec3edc0ee0d Reviewed-on: https://chromium-review.googlesource.com/c/1452447 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#59381}
This commit is contained in:
parent
8408dbafc5
commit
edef7f1341
23
include/v8.h
23
include/v8.h
@ -941,6 +941,19 @@ class V8_EXPORT TracedGlobal {
|
||||
*/
|
||||
V8_INLINE uint16_t WrapperClassId() const;
|
||||
|
||||
/**
|
||||
* Adds a finalization callback to the handle. The type of this callback is
|
||||
* similar to WeakCallbackType::kInternalFields, i.e., it will pass the
|
||||
* parameter and the first two internal fields of the object.
|
||||
*
|
||||
* The callback is then supposed to reset the handle in the callback. No
|
||||
* further V8 API may be called in this callback. In case additional work
|
||||
* involving V8 needs to be done, a second callback can be scheduled using
|
||||
* WeakCallbackInfo<void>::SetSecondPassCallback.
|
||||
*/
|
||||
V8_INLINE void SetFinalizationCallback(
|
||||
void* parameter, WeakCallbackInfo<void>::Callback callback);
|
||||
|
||||
private:
|
||||
V8_INLINE static T* New(Isolate* isolate, T* that, T** slot);
|
||||
|
||||
@ -8681,6 +8694,9 @@ class V8_EXPORT V8 {
|
||||
WeakCallbackType type);
|
||||
static void MakeWeak(internal::Address** location_addr);
|
||||
static void* ClearWeak(internal::Address* location);
|
||||
static void SetFinalizationCallbackTraced(
|
||||
internal::Address* location, void* parameter,
|
||||
WeakCallbackInfo<void>::Callback callback);
|
||||
static void AnnotateStrongRetainer(internal::Address* location,
|
||||
const char* label);
|
||||
static Value* Eternalize(Isolate* isolate, Value* handle);
|
||||
@ -9858,6 +9874,13 @@ uint16_t TracedGlobal<T>::WrapperClassId() const {
|
||||
return *reinterpret_cast<uint16_t*>(addr);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void TracedGlobal<T>::SetFinalizationCallback(
|
||||
void* parameter, typename WeakCallbackInfo<void>::Callback callback) {
|
||||
V8::SetFinalizationCallbackTraced(
|
||||
reinterpret_cast<internal::Address*>(this->val_), parameter, callback);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReturnValue<T>::ReturnValue(internal::Address* slot) : value_(slot) {}
|
||||
|
||||
|
@ -1074,6 +1074,13 @@ void V8::DisposeTracedGlobal(internal::Address* location) {
|
||||
i::GlobalHandles::DestroyTraced(location);
|
||||
}
|
||||
|
||||
void V8::SetFinalizationCallbackTraced(
|
||||
internal::Address* location, void* parameter,
|
||||
WeakCallbackInfo<void>::Callback callback) {
|
||||
i::GlobalHandles::SetFinalizationCallbackForTraced(location, parameter,
|
||||
callback);
|
||||
}
|
||||
|
||||
Value* V8::Eternalize(Isolate* v8_isolate, Value* value) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
i::Object object = *Utils::OpenHandle(value);
|
||||
|
@ -345,6 +345,21 @@ class NodeBase {
|
||||
} data_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
void ExtractInternalFields(JSObject jsobject, void** embedder_fields, int len) {
|
||||
int field_count = jsobject->GetEmbedderFieldCount();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (field_count == i) break;
|
||||
void* pointer;
|
||||
if (EmbedderDataSlot(jsobject, i).ToAlignedPointer(&pointer)) {
|
||||
embedder_fields[i] = pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
|
||||
public:
|
||||
// State transition diagram:
|
||||
@ -519,7 +534,8 @@ class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
|
||||
}
|
||||
|
||||
void CollectPhantomCallbackData(
|
||||
std::vector<PendingPhantomCallback>* pending_phantom_callbacks) {
|
||||
std::vector<std::pair<Node*, PendingPhantomCallback>>*
|
||||
pending_phantom_callbacks) {
|
||||
DCHECK(weakness_type() == PHANTOM_WEAK ||
|
||||
weakness_type() == PHANTOM_WEAK_2_EMBEDDER_FIELDS);
|
||||
DCHECK(state() == PENDING);
|
||||
@ -528,29 +544,23 @@ class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
|
||||
void* embedder_fields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
|
||||
nullptr};
|
||||
if (weakness_type() != PHANTOM_WEAK && object()->IsJSObject()) {
|
||||
JSObject jsobject = JSObject::cast(object());
|
||||
int field_count = jsobject->GetEmbedderFieldCount();
|
||||
for (int i = 0; i < v8::kEmbedderFieldsInWeakCallback; ++i) {
|
||||
if (field_count == i) break;
|
||||
void* pointer;
|
||||
if (EmbedderDataSlot(jsobject, i).ToAlignedPointer(&pointer)) {
|
||||
embedder_fields[i] = pointer;
|
||||
}
|
||||
}
|
||||
ExtractInternalFields(JSObject::cast(object()), embedder_fields,
|
||||
v8::kEmbedderFieldsInWeakCallback);
|
||||
}
|
||||
|
||||
// Zap with something dangerous.
|
||||
location().store(Object(0x6057CA11));
|
||||
location().store(Object(0xCA11));
|
||||
|
||||
pending_phantom_callbacks->push_back(PendingPhantomCallback(
|
||||
this, weak_callback_, parameter(), embedder_fields));
|
||||
pending_phantom_callbacks->push_back(std::make_pair(
|
||||
this,
|
||||
PendingPhantomCallback(weak_callback_, parameter(), embedder_fields)));
|
||||
DCHECK(IsInUse());
|
||||
set_state(NEAR_DEATH);
|
||||
}
|
||||
|
||||
void ResetPhantomHandle() {
|
||||
DCHECK(weakness_type() == PHANTOM_WEAK_RESET_HANDLE);
|
||||
DCHECK(state() == PENDING);
|
||||
DCHECK_EQ(PHANTOM_WEAK_RESET_HANDLE, weakness_type());
|
||||
DCHECK_EQ(PENDING, state());
|
||||
DCHECK_NULL(weak_callback_);
|
||||
Address** handle = reinterpret_cast<Address**>(parameter());
|
||||
*handle = nullptr;
|
||||
@ -626,7 +636,7 @@ class GlobalHandles::TracedNode final
|
||||
public:
|
||||
TracedNode() { set_in_new_space_list(false); }
|
||||
|
||||
enum State { FREE = 0, NORMAL };
|
||||
enum State { FREE = 0, NORMAL, NEAR_DEATH };
|
||||
|
||||
State state() const { return NodeState::decode(flags_); }
|
||||
void set_state(State state) { flags_ = NodeState::update(flags_, state); }
|
||||
@ -634,6 +644,8 @@ class GlobalHandles::TracedNode final
|
||||
void MarkAsFree() { set_state(FREE); }
|
||||
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_new_space_list() const { return IsInNewSpaceList::decode(flags_); }
|
||||
void set_in_new_space_list(bool v) {
|
||||
@ -643,6 +655,31 @@ class GlobalHandles::TracedNode final
|
||||
bool is_root() const { return IsRoot::decode(flags_); }
|
||||
void set_root(bool v) { flags_ = IsRoot::update(flags_, v); }
|
||||
|
||||
void SetFinalizationCallback(void* parameter,
|
||||
WeakCallbackInfo<void>::Callback callback) {
|
||||
set_parameter(parameter);
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void CollectPhantomCallbackData(
|
||||
std::vector<std::pair<TracedNode*, PendingPhantomCallback>>*
|
||||
pending_phantom_callbacks) {
|
||||
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() {
|
||||
DCHECK(IsInUse());
|
||||
Address** handle = reinterpret_cast<Address**>(data_.parameter);
|
||||
@ -652,12 +689,21 @@ class GlobalHandles::TracedNode final
|
||||
}
|
||||
|
||||
protected:
|
||||
class NodeState : public BitField8<State, 0, 1> {};
|
||||
class NodeState : public BitField8<State, 0, 2> {};
|
||||
class IsInNewSpaceList : public BitField8<bool, NodeState::kNext, 1> {};
|
||||
class IsRoot : public BitField8<bool, IsInNewSpaceList::kNext, 1> {};
|
||||
|
||||
void ClearImplFields() { set_root(true); }
|
||||
void CheckImplFieldsAreCleared() const { DCHECK(is_root()); }
|
||||
void ClearImplFields() {
|
||||
set_root(true);
|
||||
callback_ = nullptr;
|
||||
}
|
||||
|
||||
void CheckImplFieldsAreCleared() const {
|
||||
DCHECK(is_root());
|
||||
DCHECK_NULL(callback_);
|
||||
}
|
||||
|
||||
WeakCallbackInfo<void>::Callback callback_;
|
||||
|
||||
friend class NodeBase<GlobalHandles::TracedNode>;
|
||||
|
||||
@ -744,6 +790,13 @@ void GlobalHandles::DestroyTraced(Address* location) {
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalHandles::SetFinalizationCallbackForTraced(
|
||||
Address* location, void* parameter,
|
||||
WeakCallbackInfo<void>::Callback callback) {
|
||||
TracedNode::FromLocation(location)->SetFinalizationCallback(parameter,
|
||||
callback);
|
||||
}
|
||||
|
||||
typedef v8::WeakCallbackInfo<void>::Callback GenericCallback;
|
||||
|
||||
void GlobalHandles::MakeWeak(Address* location, void* parameter,
|
||||
@ -798,15 +851,19 @@ void GlobalHandles::IterateWeakRootsForPhantomHandles(
|
||||
++number_of_phantom_handle_resets_;
|
||||
} else if (node->IsPhantomCallback()) {
|
||||
node->MarkPending();
|
||||
node->CollectPhantomCallbackData(&pending_phantom_callbacks_);
|
||||
node->CollectPhantomCallbackData(®ular_pending_phantom_callbacks_);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (TracedNode* node : *traced_nodes_) {
|
||||
if (node->IsInUse() &&
|
||||
should_reset_handle(isolate()->heap(), node->location())) {
|
||||
if (node->IsPhantomResetHandle()) {
|
||||
node->ResetPhantomHandle();
|
||||
++number_of_phantom_handle_resets_;
|
||||
} else {
|
||||
node->CollectPhantomCallbackData(&traced_pending_phantom_callbacks_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -903,7 +960,7 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles(
|
||||
++number_of_phantom_handle_resets_;
|
||||
} else if (node->IsPhantomCallback()) {
|
||||
node->MarkPending();
|
||||
node->CollectPhantomCallbackData(&pending_phantom_callbacks_);
|
||||
node->CollectPhantomCallbackData(®ular_pending_phantom_callbacks_);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -920,8 +977,12 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles(
|
||||
DCHECK_IMPLIES(node->is_root(),
|
||||
!should_reset_handle(isolate_->heap(), node->location()));
|
||||
if (should_reset_handle(isolate_->heap(), node->location())) {
|
||||
if (node->IsPhantomResetHandle()) {
|
||||
node->ResetPhantomHandle();
|
||||
++number_of_phantom_handle_resets_;
|
||||
} else {
|
||||
node->CollectPhantomCallbackData(&traced_pending_phantom_callbacks_);
|
||||
}
|
||||
} else {
|
||||
if (!node->is_root()) {
|
||||
node->set_root(true);
|
||||
@ -946,9 +1007,7 @@ void GlobalHandles::InvokeSecondPassPhantomCallbacks() {
|
||||
while (!second_pass_callbacks_.empty()) {
|
||||
auto callback = second_pass_callbacks_.back();
|
||||
second_pass_callbacks_.pop_back();
|
||||
DCHECK_NULL(callback.node());
|
||||
// Fire second pass callback
|
||||
callback.Invoke(isolate());
|
||||
callback.Invoke(isolate(), PendingPhantomCallback::kSecondPass);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1023,23 +1082,38 @@ void GlobalHandles::UpdateListOfNewSpaceNodes() {
|
||||
UpdateAndCompactListOfNewSpaceNode(&traced_new_space_nodes_);
|
||||
}
|
||||
|
||||
size_t GlobalHandles::InvokeFirstPassWeakCallbacks() {
|
||||
template <typename T>
|
||||
size_t GlobalHandles::InvokeFirstPassWeakCallbacks(
|
||||
std::vector<std::pair<T*, PendingPhantomCallback>>* pending) {
|
||||
size_t freed_nodes = 0;
|
||||
std::vector<PendingPhantomCallback> pending_phantom_callbacks;
|
||||
pending_phantom_callbacks.swap(pending_phantom_callbacks_);
|
||||
std::vector<std::pair<T*, PendingPhantomCallback>> pending_phantom_callbacks;
|
||||
pending_phantom_callbacks.swap(*pending);
|
||||
{
|
||||
// The initial pass callbacks must simply clear the nodes.
|
||||
for (auto callback : pending_phantom_callbacks) {
|
||||
// Skip callbacks that have already been processed once.
|
||||
if (callback.node() == nullptr) continue;
|
||||
callback.Invoke(isolate());
|
||||
if (callback.callback()) second_pass_callbacks_.push_back(callback);
|
||||
for (auto& pair : pending_phantom_callbacks) {
|
||||
T* node = pair.first;
|
||||
DCHECK_EQ(T::NEAR_DEATH, node->state());
|
||||
pair.second.Invoke(isolate(), PendingPhantomCallback::kFirstPass);
|
||||
|
||||
// Transition to second pass. It is required that the first pass callback
|
||||
// resets the handle using |v8::PersistentBase::Reset|. Also see comments
|
||||
// on |v8::WeakCallbackInfo|.
|
||||
CHECK_WITH_MSG(T::FREE == node->state(),
|
||||
"Handle not reset in first callback. See comments on "
|
||||
"|v8::WeakCallbackInfo|.");
|
||||
|
||||
if (pair.second.callback()) second_pass_callbacks_.push_back(pair.second);
|
||||
freed_nodes++;
|
||||
}
|
||||
}
|
||||
return freed_nodes;
|
||||
}
|
||||
|
||||
size_t GlobalHandles::InvokeFirstPassWeakCallbacks() {
|
||||
return InvokeFirstPassWeakCallbacks(®ular_pending_phantom_callbacks_) +
|
||||
InvokeFirstPassWeakCallbacks(&traced_pending_phantom_callbacks_);
|
||||
}
|
||||
|
||||
void GlobalHandles::InvokeOrScheduleSecondPassPhantomCallbacks(
|
||||
bool synchronous_second_pass) {
|
||||
if (!second_pass_callbacks_.empty()) {
|
||||
@ -1059,11 +1133,10 @@ void GlobalHandles::InvokeOrScheduleSecondPassPhantomCallbacks(
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) {
|
||||
void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate,
|
||||
InvocationType type) {
|
||||
Data::Callback* callback_addr = nullptr;
|
||||
if (node_ != nullptr) {
|
||||
// Initialize for first pass callback.
|
||||
DCHECK(node_->state() == Node::NEAR_DEATH);
|
||||
if (type == kFirstPass) {
|
||||
callback_addr = &callback_;
|
||||
}
|
||||
Data data(reinterpret_cast<v8::Isolate*>(isolate), parameter_,
|
||||
@ -1071,15 +1144,6 @@ void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) {
|
||||
Data::Callback callback = callback_;
|
||||
callback_ = nullptr;
|
||||
callback(data);
|
||||
if (node_ != nullptr) {
|
||||
// Transition to second pass. It is required that the first pass callback
|
||||
// resets the handle using |v8::PersistentBase::Reset|. Also see comments on
|
||||
// |v8::WeakCallbackInfo|.
|
||||
CHECK_WITH_MSG(Node::FREE == node_->state(),
|
||||
"Handle not reset in first callback. See comments on "
|
||||
"|v8::WeakCallbackInfo|.");
|
||||
node_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalHandles::InRecursiveGC(unsigned gc_processing_counter) {
|
||||
@ -1143,7 +1207,7 @@ void GlobalHandles::IterateAllRoots(RootVisitor* v) {
|
||||
}
|
||||
}
|
||||
for (TracedNode* node : *traced_nodes_) {
|
||||
if (node->IsInUse()) {
|
||||
if (node->IsRetainer()) {
|
||||
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location());
|
||||
}
|
||||
}
|
||||
@ -1158,7 +1222,7 @@ void GlobalHandles::IterateAllNewSpaceRoots(RootVisitor* v) {
|
||||
}
|
||||
}
|
||||
for (TracedNode* node : traced_new_space_nodes_) {
|
||||
if (node->IsInUse()) {
|
||||
if (node->IsRetainer()) {
|
||||
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location());
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define V8_GLOBAL_HANDLES_H_
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "include/v8.h"
|
||||
@ -43,17 +44,16 @@ class GlobalHandles final {
|
||||
template <class NodeType>
|
||||
class NodeBlock;
|
||||
|
||||
// Move a global handle.
|
||||
//
|
||||
// API for regular handles.
|
||||
//
|
||||
|
||||
static void MoveGlobal(Address** from, Address** to);
|
||||
static void MoveTracedGlobal(Address** from, Address** to);
|
||||
|
||||
// Destroy a global handle.
|
||||
static void Destroy(Address* location);
|
||||
static void DestroyTraced(Address* location);
|
||||
|
||||
// Copy a global handle.
|
||||
static Handle<Object> CopyGlobal(Address* location);
|
||||
|
||||
static void Destroy(Address* location);
|
||||
|
||||
// Make the global handle weak and set the callback parameter for the
|
||||
// handle. When the garbage collector recognizes that only weak global
|
||||
// handles point to an object the callback function is invoked (for each
|
||||
@ -66,7 +66,6 @@ class GlobalHandles final {
|
||||
static void MakeWeak(Address* location, void* parameter,
|
||||
WeakCallbackInfo<void>::Callback weak_callback,
|
||||
v8::WeakCallbackType type);
|
||||
|
||||
static void MakeWeak(Address** location_addr);
|
||||
|
||||
static void AnnotateStrongRetainer(Address* location, const char* label);
|
||||
@ -80,6 +79,16 @@ class GlobalHandles final {
|
||||
// Tells whether global handle is weak.
|
||||
static bool IsWeak(Address* location);
|
||||
|
||||
//
|
||||
// API for traced handles.
|
||||
//
|
||||
|
||||
static void MoveTracedGlobal(Address** from, Address** to);
|
||||
static void DestroyTraced(Address* location);
|
||||
static void SetFinalizationCallbackForTraced(
|
||||
Address* location, void* parameter,
|
||||
WeakCallbackInfo<void>::Callback callback);
|
||||
|
||||
explicit GlobalHandles(Isolate* isolate);
|
||||
~GlobalHandles();
|
||||
|
||||
@ -195,6 +204,10 @@ class GlobalHandles final {
|
||||
size_t PostScavengeProcessing(unsigned post_processing_count);
|
||||
size_t PostMarkSweepProcessing(unsigned post_processing_count);
|
||||
|
||||
template <typename T>
|
||||
size_t InvokeFirstPassWeakCallbacks(
|
||||
std::vector<std::pair<T*, PendingPhantomCallback>>* pending);
|
||||
|
||||
template <typename T>
|
||||
void UpdateAndCompactListOfNewSpaceNode(std::vector<T*>* node_list);
|
||||
void UpdateListOfNewSpaceNodes();
|
||||
@ -216,7 +229,10 @@ class GlobalHandles final {
|
||||
size_t handles_count_ = 0;
|
||||
size_t number_of_phantom_handle_resets_ = 0;
|
||||
|
||||
std::vector<PendingPhantomCallback> pending_phantom_callbacks_;
|
||||
std::vector<std::pair<Node*, PendingPhantomCallback>>
|
||||
regular_pending_phantom_callbacks_;
|
||||
std::vector<std::pair<TracedNode*, PendingPhantomCallback>>
|
||||
traced_pending_phantom_callbacks_;
|
||||
std::vector<PendingPhantomCallback> second_pass_callbacks_;
|
||||
bool second_pass_callbacks_task_posted_ = false;
|
||||
|
||||
@ -229,22 +245,23 @@ class GlobalHandles final {
|
||||
class GlobalHandles::PendingPhantomCallback final {
|
||||
public:
|
||||
typedef v8::WeakCallbackInfo<void> Data;
|
||||
|
||||
enum InvocationType { kFirstPass, kSecondPass };
|
||||
|
||||
PendingPhantomCallback(
|
||||
Node* node, Data::Callback callback, void* parameter,
|
||||
Data::Callback callback, void* parameter,
|
||||
void* embedder_fields[v8::kEmbedderFieldsInWeakCallback])
|
||||
: node_(node), callback_(callback), parameter_(parameter) {
|
||||
: callback_(callback), parameter_(parameter) {
|
||||
for (int i = 0; i < v8::kEmbedderFieldsInWeakCallback; ++i) {
|
||||
embedder_fields_[i] = embedder_fields[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Invoke(Isolate* isolate);
|
||||
void Invoke(Isolate* isolate, InvocationType type);
|
||||
|
||||
Node* node() const { return node_; }
|
||||
Data::Callback callback() const { return callback_; }
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
Data::Callback callback_;
|
||||
void* parameter_;
|
||||
void* embedder_fields_[v8::kEmbedderFieldsInWeakCallback];
|
||||
|
@ -488,6 +488,63 @@ 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) {
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
TestEmbedderHeapTracer tracer;
|
||||
tracer.ConsiderTracedGlobalAsRoot(false);
|
||||
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
||||
|
||||
v8::TracedGlobal<v8::Object> traced;
|
||||
ConstructJSApiObject(isolate, isolate->GetCurrentContext(), &traced);
|
||||
CHECK(!traced.IsEmpty());
|
||||
{
|
||||
v8::HandleScope scope(isolate);
|
||||
auto local = traced.Get(isolate);
|
||||
local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
|
||||
local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
|
||||
}
|
||||
traced.SetFinalizationCallback(&traced, FinalizationCallback);
|
||||
heap::InvokeScavenge();
|
||||
CHECK(traced.IsEmpty());
|
||||
}
|
||||
|
||||
TEST(TracedGlobalSetFinalizationCallbackMarkSweep) {
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
TestEmbedderHeapTracer tracer;
|
||||
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
|
||||
|
||||
v8::TracedGlobal<v8::Object> traced;
|
||||
ConstructJSApiObject(isolate, isolate->GetCurrentContext(), &traced);
|
||||
CHECK(!traced.IsEmpty());
|
||||
{
|
||||
v8::HandleScope scope(isolate);
|
||||
auto local = traced.Get(isolate);
|
||||
local->SetAlignedPointerInInternalField(0, reinterpret_cast<void*>(0x4));
|
||||
local->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0x8));
|
||||
}
|
||||
traced.SetFinalizationCallback(&traced, FinalizationCallback);
|
||||
heap::InvokeMarkSweep();
|
||||
CHECK(traced.IsEmpty());
|
||||
}
|
||||
|
||||
} // namespace heap
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user