v8/test/cctest/test-js-weak-refs.cc
Shu-yu Guo 45bb44985e [weakrefs] Hold unregister tokens weakly
Change unregister tokens to be held weakly instead of strongly. This
enables the use case for an object to be used as its own unregister
token.

To avoid using an ephemeron table, FinalizationGroup's key_map is
changed to key off unregister tokens' identity hashes. Because hashes
may collide, a single key list may rarely contain multiple tokens. When
a FinalizationGroup WeakCell's token becomes unreachable, during GC, it
is removed from the the doubly linked key list and removed from the key
map if it had a unique key.

Bug: v8:8179
Change-Id: If88fd2ab196e3f9a287990ae345117a0abb2f04d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1970493
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65532}
2019-12-19 22:42:09 +00:00

850 lines
31 KiB
C++

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/execution/isolate.h"
#include "src/execution/microtask-queue.h"
#include "src/handles/handles-inl.h"
#include "src/heap/factory-inl.h"
#include "src/objects/js-objects.h"
#include "src/objects/js-weak-refs-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 {
namespace internal {
namespace {
Handle<JSFinalizationGroup> ConstructJSFinalizationGroup(Isolate* isolate) {
Factory* factory = isolate->factory();
Handle<String> finalization_group_name =
factory->NewStringFromStaticChars("FinalizationGroup");
Handle<Object> global =
handle(isolate->native_context()->global_object(), isolate);
Handle<JSFunction> finalization_group_fun = Handle<JSFunction>::cast(
Object::GetProperty(isolate, global, finalization_group_name)
.ToHandleChecked());
auto finalization_group = Handle<JSFinalizationGroup>::cast(
JSObject::New(finalization_group_fun, finalization_group_fun,
Handle<AllocationSite>::null())
.ToHandleChecked());
#ifdef VERIFY_HEAP
finalization_group->JSFinalizationGroupVerify(isolate);
#endif // VERIFY_HEAP
return finalization_group;
}
Handle<JSWeakRef> ConstructJSWeakRef(Handle<JSReceiver> target,
Isolate* isolate) {
Factory* factory = isolate->factory();
Handle<String> weak_ref_name = factory->WeakRef_string();
Handle<Object> global =
handle(isolate->native_context()->global_object(), isolate);
Handle<JSFunction> weak_ref_fun = Handle<JSFunction>::cast(
Object::GetProperty(isolate, global, weak_ref_name).ToHandleChecked());
auto weak_ref = Handle<JSWeakRef>::cast(
JSObject::New(weak_ref_fun, weak_ref_fun, Handle<AllocationSite>::null())
.ToHandleChecked());
weak_ref->set_target(*target);
#ifdef VERIFY_HEAP
weak_ref->JSWeakRefVerify(isolate);
#endif // VERIFY_HEAP
return weak_ref;
}
Handle<JSObject> CreateKey(const char* key_prop_value, Isolate* isolate) {
Factory* factory = isolate->factory();
Handle<String> key_string = factory->NewStringFromStaticChars("key_string");
Handle<JSObject> key =
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::AddProperty(isolate, key, key_string,
factory->NewStringFromAsciiChecked(key_prop_value),
NONE);
return key;
}
Handle<WeakCell> FinalizationGroupRegister(
Handle<JSFinalizationGroup> finalization_group, Handle<JSObject> target,
Handle<Object> holdings, Handle<Object> key, Isolate* isolate) {
JSFinalizationGroup::Register(finalization_group, target, holdings, key,
isolate);
CHECK(finalization_group->active_cells().IsWeakCell());
Handle<WeakCell> weak_cell =
handle(WeakCell::cast(finalization_group->active_cells()), isolate);
#ifdef VERIFY_HEAP
weak_cell->WeakCellVerify(isolate);
#endif // VERIFY_HEAP
return weak_cell;
}
Handle<WeakCell> FinalizationGroupRegister(
Handle<JSFinalizationGroup> finalization_group, Handle<JSObject> target,
Isolate* isolate) {
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
return FinalizationGroupRegister(finalization_group, target, undefined,
undefined, isolate);
}
void NullifyWeakCell(Handle<WeakCell> weak_cell, Isolate* isolate) {
auto empty_func = [](HeapObject object, ObjectSlot slot, Object target) {};
weak_cell->Nullify(isolate, empty_func);
#ifdef VERIFY_HEAP
weak_cell->WeakCellVerify(isolate);
#endif // VERIFY_HEAP
}
// Usage: VerifyWeakCellChain(isolate, list_head, n, cell1, cell2, ..., celln);
// verifies that list_head == cell1 and cell1, cell2, ..., celln. form a list.
void VerifyWeakCellChain(Isolate* isolate, Object list_head, int n_args, ...) {
CHECK_GE(n_args, 0);
va_list args;
va_start(args, n_args);
if (n_args == 0) {
// Verify empty list
CHECK(list_head.IsUndefined(isolate));
} else {
WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
CHECK_EQ(current, list_head);
CHECK(current.prev().IsUndefined(isolate));
for (int i = 1; i < n_args; i++) {
WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
CHECK_EQ(current.next(), next);
CHECK_EQ(next.prev(), current);
current = next;
}
CHECK(current.next().IsUndefined(isolate));
}
va_end(args);
}
// Like VerifyWeakCellChain but verifies the chain created with key_list_prev
// and key_list_next instead of prev and next.
void VerifyWeakCellKeyChain(Isolate* isolate, SimpleNumberDictionary key_map,
Object unregister_token, int n_args, ...) {
CHECK_GE(n_args, 0);
va_list args;
va_start(args, n_args);
Object hash = unregister_token.GetHash();
InternalIndex entry = InternalIndex::NotFound();
if (!hash.IsUndefined(isolate)) {
uint32_t key = Smi::ToInt(hash);
entry = key_map.FindEntry(isolate, key);
}
if (n_args == 0) {
// Verify empty list
CHECK(entry.is_not_found());
} else {
CHECK(entry.is_found());
WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
Object list_head = key_map.ValueAt(entry);
CHECK_EQ(current, list_head);
CHECK(current.key_list_prev().IsUndefined(isolate));
for (int i = 1; i < n_args; i++) {
WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
CHECK_EQ(current.key_list_next(), next);
CHECK_EQ(next.key_list_prev(), current);
current = next;
}
CHECK(current.key_list_next().IsUndefined(isolate));
}
va_end(args);
}
} // namespace
TEST(TestRegister) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
// Register a weak reference and verify internal data structures.
Handle<WeakCell> weak_cell1 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 1,
*weak_cell1);
CHECK(weak_cell1->key_list_prev().IsUndefined(isolate));
CHECK(weak_cell1->key_list_next().IsUndefined(isolate));
CHECK(finalization_group->cleared_cells().IsUndefined(isolate));
// No key was used during registration, key-based map stays uninitialized.
CHECK(finalization_group->key_map().IsUndefined(isolate));
// Register another weak reference and verify internal data structures.
Handle<WeakCell> weak_cell2 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
*weak_cell2, *weak_cell1);
CHECK(weak_cell2->key_list_prev().IsUndefined(isolate));
CHECK(weak_cell2->key_list_next().IsUndefined(isolate));
CHECK(finalization_group->cleared_cells().IsUndefined(isolate));
CHECK(finalization_group->key_map().IsUndefined(isolate));
}
TEST(TestRegisterWithKey) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<JSObject> token2 = CreateKey("token2", isolate);
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
// Register a weak reference with a key and verify internal data structures.
Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
}
// Register another weak reference with a different key and verify internal
// data structures.
Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 1, *weak_cell2);
}
// Register another weak reference with token1 and verify internal data
// structures.
Handle<WeakCell> weak_cell3 = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell3,
*weak_cell1);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 1, *weak_cell2);
}
}
TEST(TestWeakCellNullify1) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<WeakCell> weak_cell1 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
Handle<WeakCell> weak_cell2 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
// Nullify the first WeakCell and verify internal data structures.
NullifyWeakCell(weak_cell1, isolate);
CHECK_EQ(finalization_group->active_cells(), *weak_cell2);
CHECK(weak_cell2->prev().IsUndefined(isolate));
CHECK(weak_cell2->next().IsUndefined(isolate));
CHECK_EQ(finalization_group->cleared_cells(), *weak_cell1);
CHECK(weak_cell1->prev().IsUndefined(isolate));
CHECK(weak_cell1->next().IsUndefined(isolate));
// Nullify the second WeakCell and verify internal data structures.
NullifyWeakCell(weak_cell2, isolate);
CHECK(finalization_group->active_cells().IsUndefined(isolate));
CHECK_EQ(finalization_group->cleared_cells(), *weak_cell2);
CHECK_EQ(weak_cell2->next(), *weak_cell1);
CHECK(weak_cell2->prev().IsUndefined(isolate));
CHECK_EQ(weak_cell1->prev(), *weak_cell2);
CHECK(weak_cell1->next().IsUndefined(isolate));
}
TEST(TestWeakCellNullify2) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<WeakCell> weak_cell1 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
Handle<WeakCell> weak_cell2 =
FinalizationGroupRegister(finalization_group, js_object, isolate);
// Like TestWeakCellNullify1 but nullify the WeakCells in opposite order.
NullifyWeakCell(weak_cell2, isolate);
CHECK_EQ(finalization_group->active_cells(), *weak_cell1);
CHECK(weak_cell1->prev().IsUndefined(isolate));
CHECK(weak_cell1->next().IsUndefined(isolate));
CHECK_EQ(finalization_group->cleared_cells(), *weak_cell2);
CHECK(weak_cell2->prev().IsUndefined(isolate));
CHECK(weak_cell2->next().IsUndefined(isolate));
NullifyWeakCell(weak_cell1, isolate);
CHECK(finalization_group->active_cells().IsUndefined(isolate));
CHECK_EQ(finalization_group->cleared_cells(), *weak_cell1);
CHECK_EQ(weak_cell1->next(), *weak_cell2);
CHECK(weak_cell1->prev().IsUndefined(isolate));
CHECK_EQ(weak_cell2->prev(), *weak_cell1);
CHECK(weak_cell2->next().IsUndefined(isolate));
}
TEST(TestJSFinalizationGroupPopClearedCellHoldings1) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
finalization_group, js_object, holdings1, undefined, isolate);
Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
finalization_group, js_object, holdings2, undefined, isolate);
Handle<Object> holdings3 = factory->NewStringFromAsciiChecked("holdings3");
Handle<WeakCell> weak_cell3 = FinalizationGroupRegister(
finalization_group, js_object, holdings3, undefined, isolate);
NullifyWeakCell(weak_cell2, isolate);
NullifyWeakCell(weak_cell3, isolate);
CHECK(finalization_group->NeedsCleanup());
Object cleared1 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared1, *holdings3);
CHECK(weak_cell3->prev().IsUndefined(isolate));
CHECK(weak_cell3->next().IsUndefined(isolate));
CHECK(finalization_group->NeedsCleanup());
Object cleared2 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared2, *holdings2);
CHECK(weak_cell2->prev().IsUndefined(isolate));
CHECK(weak_cell2->next().IsUndefined(isolate));
CHECK(!finalization_group->NeedsCleanup());
NullifyWeakCell(weak_cell1, isolate);
CHECK(finalization_group->NeedsCleanup());
Object cleared3 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared3, *holdings1);
CHECK(weak_cell1->prev().IsUndefined(isolate));
CHECK(weak_cell1->next().IsUndefined(isolate));
CHECK(!finalization_group->NeedsCleanup());
CHECK(finalization_group->active_cells().IsUndefined(isolate));
CHECK(finalization_group->cleared_cells().IsUndefined(isolate));
}
TEST(TestJSFinalizationGroupPopClearedCellHoldings2) {
// Test that when all WeakCells for a key are popped, the key is removed from
// the key map.
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
finalization_group, js_object, holdings1, token1, isolate);
Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
finalization_group, js_object, holdings2, token1, isolate);
NullifyWeakCell(weak_cell1, isolate);
NullifyWeakCell(weak_cell2, isolate);
// Nullifying doesn't affect the key chains (just moves WeakCells from
// active_cells to cleared_cells).
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell2,
*weak_cell1);
}
Object cleared1 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared1, *holdings2);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
}
Object cleared2 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared2, *holdings1);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
}
}
TEST(TestUnregisterActiveCells) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<JSObject> token2 = CreateKey("token2", isolate);
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
Handle<WeakCell> weak_cell1a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell1b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell2a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
Handle<WeakCell> weak_cell2b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 4,
*weak_cell2b, *weak_cell2a, *weak_cell1b, *weak_cell1a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
*weak_cell1a);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
*weak_cell2a);
}
JSFinalizationGroup::Unregister(finalization_group, token1, isolate);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
*weak_cell2a);
}
// Both weak_cell1a and weak_cell1b removed from active_cells.
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
*weak_cell2b, *weak_cell2a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
}
TEST(TestUnregisterActiveAndClearedCells) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<JSObject> token2 = CreateKey("token2", isolate);
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
Handle<WeakCell> weak_cell1a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell1b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell2a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
Handle<WeakCell> weak_cell2b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
NullifyWeakCell(weak_cell2a, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 3,
*weak_cell2b, *weak_cell1b, *weak_cell1a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 1,
*weak_cell2a);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
*weak_cell1a);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
*weak_cell2a);
}
JSFinalizationGroup::Unregister(finalization_group, token2, isolate);
// Both weak_cell2a and weak_cell2b removed.
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
*weak_cell1b, *weak_cell1a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
*weak_cell1a);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
}
}
TEST(TestWeakCellUnregisterTwice) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 1,
*weak_cell1);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
}
JSFinalizationGroup::Unregister(finalization_group, token1, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
}
JSFinalizationGroup::Unregister(finalization_group, token1, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
}
}
TEST(TestWeakCellUnregisterPopped) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
finalization_group, js_object, holdings1, token1, isolate);
NullifyWeakCell(weak_cell1, isolate);
CHECK(finalization_group->NeedsCleanup());
Object cleared1 =
JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
CHECK_EQ(cleared1, *holdings1);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
}
JSFinalizationGroup::Unregister(finalization_group, token1, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 0);
}
}
TEST(TestWeakCellUnregisterNonexistentKey) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> token1 = CreateKey("token1", isolate);
JSFinalizationGroup::Unregister(finalization_group, token1, isolate);
}
TEST(TestJSWeakRef) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSWeakRef> weak_ref;
{
HandleScope inner_scope(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
// This doesn't add the target into the KeepDuringJob set.
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
CcTest::CollectAllGarbage();
CHECK(!inner_weak_ref->target().IsUndefined(isolate));
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
}
CHECK(!weak_ref->target().IsUndefined(isolate));
CcTest::CollectAllGarbage();
CHECK(weak_ref->target().IsUndefined(isolate));
}
TEST(TestJSWeakRefIncrementalMarking) {
FLAG_harmony_weak_refs = true;
if (!FLAG_incremental_marking) {
return;
}
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
HandleScope outer_scope(isolate);
Handle<JSWeakRef> weak_ref;
{
HandleScope inner_scope(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
// This doesn't add the target into the KeepDuringJob set.
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
heap::SimulateIncrementalMarking(heap, true);
CcTest::CollectAllGarbage();
CHECK(!inner_weak_ref->target().IsUndefined(isolate));
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
}
CHECK(!weak_ref->target().IsUndefined(isolate));
heap::SimulateIncrementalMarking(heap, true);
CcTest::CollectAllGarbage();
CHECK(weak_ref->target().IsUndefined(isolate));
}
TEST(TestJSWeakRefKeepDuringJob) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
HandleScope outer_scope(isolate);
Handle<JSWeakRef> weak_ref;
{
HandleScope inner_scope(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
heap->KeepDuringJob(js_object);
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
}
CHECK(!weak_ref->target().IsUndefined(isolate));
CcTest::CollectAllGarbage();
CHECK(!weak_ref->target().IsUndefined(isolate));
// Clears the KeepDuringJob set.
context->GetIsolate()->ClearKeptObjects();
CcTest::CollectAllGarbage();
CHECK(weak_ref->target().IsUndefined(isolate));
}
TEST(TestJSWeakRefKeepDuringJobIncrementalMarking) {
FLAG_harmony_weak_refs = true;
if (!FLAG_incremental_marking) {
return;
}
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
HandleScope outer_scope(isolate);
Handle<JSWeakRef> weak_ref;
{
HandleScope inner_scope(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
heap->KeepDuringJob(js_object);
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
}
CHECK(!weak_ref->target().IsUndefined(isolate));
heap::SimulateIncrementalMarking(heap, true);
CcTest::CollectAllGarbage();
CHECK(!weak_ref->target().IsUndefined(isolate));
// Clears the KeepDuringJob set.
context->GetIsolate()->ClearKeptObjects();
heap::SimulateIncrementalMarking(heap, true);
CcTest::CollectAllGarbage();
CHECK(weak_ref->target().IsUndefined(isolate));
}
TEST(TestRemoveUnregisterToken) {
FLAG_harmony_weak_refs = true;
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope outer_scope(isolate);
Handle<JSFinalizationGroup> finalization_group =
ConstructJSFinalizationGroup(isolate);
Handle<JSObject> js_object =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<JSObject> token1 = CreateKey("token1", isolate);
Handle<JSObject> token2 = CreateKey("token2", isolate);
Handle<Object> undefined =
handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
Handle<WeakCell> weak_cell1a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell1b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token1, isolate);
Handle<WeakCell> weak_cell2a = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
Handle<WeakCell> weak_cell2b = FinalizationGroupRegister(
finalization_group, js_object, undefined, token2, isolate);
NullifyWeakCell(weak_cell2a, isolate);
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 3,
*weak_cell2b, *weak_cell1b, *weak_cell1a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 1,
*weak_cell2a);
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
*weak_cell1a);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 2, *weak_cell2b,
*weak_cell2a);
}
finalization_group->RemoveUnregisterToken(
JSReceiver::cast(*token2), isolate,
[undefined](WeakCell matched_cell) {
matched_cell.set_unregister_token(*undefined);
},
[](HeapObject, ObjectSlot, Object) {});
// Both weak_cell2a and weak_cell2b remain on the weak cell chains.
VerifyWeakCellChain(isolate, finalization_group->active_cells(), 3,
*weak_cell2b, *weak_cell1b, *weak_cell1a);
VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 1,
*weak_cell2a);
// But both weak_cell2a and weak_cell2b are removed from the key chain.
{
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_group->key_map());
VerifyWeakCellKeyChain(isolate, key_map, *token1, 2, *weak_cell1b,
*weak_cell1a);
VerifyWeakCellKeyChain(isolate, key_map, *token2, 0);
}
}
} // namespace internal
} // namespace v8