Commit 00a589d9 authored by jochen's avatar jochen Committed by Commit bot

[api] Bring back finalizers on global handles

Seems like node.js depends on it in many places. At least try to get rid
of WeakCallbackData vs WeakCallbackInfo

BUG=
R=hpayer@chromium.org
LOG=y

Review URL: https://codereview.chromium.org/1883173002

Cr-Commit-Position: refs/heads/master@{#35528}
parent 3533c084
......@@ -480,9 +480,12 @@ class WeakCallbackData {
template <class T>
using PhantomCallbackData = WeakCallbackInfo<T>;
enum class WeakCallbackType { kParameter, kInternalFields };
// kParameter will pass a void* parameter back to the callback, kInternalFields
// will pass the first two internal fields back to the callback, kFinalizer
// will pass a void* parameter back, but is invoked before the object is
// actually collected, so it can be resurrected. In the last case, it is not
// possible to request a second pass callback.
enum class WeakCallbackType { kParameter, kInternalFields, kFinalizer };
/**
* An object reference that is independent of any handle scope. Where
......
......@@ -197,14 +197,16 @@ class GlobalHandles::Node {
bool IsRetainer() const {
return state() != FREE &&
!(state() == NEAR_DEATH && weakness_type() != NORMAL_WEAK);
!(state() == NEAR_DEATH && weakness_type() != NORMAL_WEAK &&
weakness_type() != FINALIZER_WEAK);
}
bool IsStrongRetainer() const { return state() == NORMAL; }
bool IsWeakRetainer() const {
return state() == WEAK || state() == PENDING ||
(state() == NEAR_DEATH && weakness_type() == NORMAL_WEAK);
(state() == NEAR_DEATH && weakness_type() == NORMAL_WEAK &&
weakness_type() != FINALIZER_WEAK);
}
void MarkPending() {
......@@ -272,8 +274,11 @@ class GlobalHandles::Node {
set_weakness_type(PHANTOM_WEAK);
break;
case v8::WeakCallbackType::kInternalFields:
set_weakness_type(PHANTOM_WEAK_2_INTERNAL_FIELDS);
break;
set_weakness_type(PHANTOM_WEAK_2_INTERNAL_FIELDS);
break;
case v8::WeakCallbackType::kFinalizer:
set_weakness_type(NORMAL_WEAK);
break;
}
set_parameter(parameter);
weak_callback_ = reinterpret_cast<WeakCallback>(phantom_callback);
......@@ -332,18 +337,30 @@ class GlobalHandles::Node {
ExternalOneByteString::cast(object_)->resource() != NULL);
DCHECK(!object_->IsExternalTwoByteString() ||
ExternalTwoByteString::cast(object_)->resource() != NULL);
if (weakness_type() != NORMAL_WEAK) return false;
if (weakness_type() != NORMAL_WEAK && weakness_type() != FINALIZER_WEAK) {
return false;
}
// Leaving V8.
VMState<EXTERNAL> vmstate(isolate);
HandleScope handle_scope(isolate);
Object** object = location();
Handle<Object> handle(*object, isolate);
v8::WeakCallbackData<v8::Value, void> data(
reinterpret_cast<v8::Isolate*>(isolate), parameter(),
v8::Utils::ToLocal(handle));
set_parameter(NULL);
weak_callback_(data);
if (weakness_type() == NORMAL_WEAK) {
Object** object = location();
Handle<Object> handle(*object, isolate);
v8::WeakCallbackData<v8::Value, void> data(
reinterpret_cast<v8::Isolate*>(isolate), parameter(),
v8::Utils::ToLocal(handle));
set_parameter(NULL);
weak_callback_(data);
} else {
void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr,
nullptr};
v8::WeakCallbackInfo<void> data(reinterpret_cast<v8::Isolate*>(isolate),
parameter(), internal_fields, nullptr);
auto callback = reinterpret_cast<v8::WeakCallbackInfo<void>::Callback>(
weak_callback_);
callback(data);
}
// Absence of explicit cleanup or revival of weak handle
// in most of the cases would lead to memory leak.
......@@ -650,9 +667,10 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
if (node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != NORMAL_WEAK) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
node->weakness_type() != NORMAL_WEAK &&
node->weakness_type() != FINALIZER_WEAK) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
v->VisitPointer(node->location());
}
......@@ -711,7 +729,8 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != NORMAL_WEAK) {
node->weakness_type() != NORMAL_WEAK &&
node->weakness_type() != FINALIZER_WEAK) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
......@@ -754,7 +773,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) {
node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != NORMAL_WEAK) {
node->weakness_type() != NORMAL_WEAK &&
node->weakness_type() != FINALIZER_WEAK) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
......
......@@ -96,7 +96,6 @@ struct ObjectGroupRetainerInfo {
RetainedObjectInfo* info;
};
enum WeaknessType {
NORMAL_WEAK, // Embedder gets a handle to the dying object.
// In the following cases, the embedder gets the parameter they passed in
......@@ -105,10 +104,11 @@ enum WeaknessType {
// objects through this interface would be GC unsafe so in that case the
// embedder gets a null pointer instead.
PHANTOM_WEAK,
PHANTOM_WEAK_2_INTERNAL_FIELDS
PHANTOM_WEAK_2_INTERNAL_FIELDS,
// Like NORMAL_WEAK, but uses WeakCallbackInfo instead of WeakCallbackData.
FINALIZER_WEAK,
};
class GlobalHandles {
public:
~GlobalHandles();
......
......@@ -417,3 +417,36 @@ TEST(WeakPersistentSmi) {
// Should not crash.
g.SetWeak<void>(nullptr, &WeakCallback, v8::WeakCallbackType::kParameter);
}
void finalizer(const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
data.GetParameter()->ClearWeak();
v8::Local<v8::Object> o =
v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
v8_str("was here"))
.FromJust();
}
TEST(FinalizerWeakness) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> g;
int identity;
{
v8::HandleScope scope(isolate);
v8::Local<v8::Object> o = v8::Object::New(isolate);
identity = o->GetIdentityHash();
g.Reset(isolate, o);
g.SetWeak(&g, finalizer, v8::WeakCallbackType::kFinalizer);
}
CcTest::i_isolate()->heap()->CollectAllAvailableGarbage();
CHECK(!g.IsEmpty());
v8::HandleScope scope(isolate);
v8::Local<v8::Object> o = v8::Local<v8::Object>::New(isolate, g);
CHECK_EQ(identity, o->GetIdentityHash());
CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust());
}
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