| // 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, 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.IsTheHole(isolate)); |
| } else { |
| WeakCell current = WeakCell::cast(Object(va_arg(args, Address))); |
| 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> key1 = CreateKey("key1", isolate); |
| Handle<JSObject> key2 = CreateKey("key2", 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, key1, isolate); |
| |
| { |
| CHECK(finalization_group->key_map().IsObjectHashTable()); |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 0); |
| } |
| |
| // Register another weak reference with a different key and verify internal |
| // data structures. |
| Handle<WeakCell> weak_cell2 = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key2, isolate); |
| |
| { |
| CHECK(finalization_group->key_map().IsObjectHashTable()); |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 1, *weak_cell2); |
| } |
| |
| // Register another weak reference with key1 and verify internal data |
| // structures. |
| Handle<WeakCell> weak_cell3 = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| |
| { |
| CHECK(finalization_group->key_map().IsObjectHashTable()); |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell3, |
| *weak_cell1); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 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> key1 = CreateKey("key1", isolate); |
| |
| Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1"); |
| Handle<WeakCell> weak_cell1 = FinalizationGroupRegister( |
| finalization_group, js_object, holdings1, key1, isolate); |
| Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2"); |
| Handle<WeakCell> weak_cell2 = FinalizationGroupRegister( |
| finalization_group, js_object, holdings2, key1, 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). |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell2, |
| *weak_cell1); |
| } |
| |
| Object cleared1 = |
| JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate); |
| CHECK_EQ(cleared1, *holdings2); |
| |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1); |
| } |
| |
| Object cleared2 = |
| JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate); |
| CHECK_EQ(cleared2, *holdings1); |
| |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 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> key1 = CreateKey("key1", isolate); |
| Handle<JSObject> key2 = CreateKey("key2", isolate); |
| Handle<Object> undefined = |
| handle(ReadOnlyRoots(isolate).undefined_value(), isolate); |
| |
| Handle<WeakCell> weak_cell1a = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| Handle<WeakCell> weak_cell1b = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| |
| Handle<WeakCell> weak_cell2a = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key2, isolate); |
| Handle<WeakCell> weak_cell2b = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key2, isolate); |
| |
| VerifyWeakCellChain(isolate, finalization_group->active_cells(), 4, |
| *weak_cell2b, *weak_cell2a, *weak_cell1b, *weak_cell1a); |
| VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b, |
| *weak_cell1a); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 2, *weak_cell2b, |
| *weak_cell2a); |
| } |
| |
| JSFinalizationGroup::Unregister(finalization_group, key1, isolate); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 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> key1 = CreateKey("key1", isolate); |
| Handle<JSObject> key2 = CreateKey("key2", isolate); |
| Handle<Object> undefined = |
| handle(ReadOnlyRoots(isolate).undefined_value(), isolate); |
| |
| Handle<WeakCell> weak_cell1a = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| Handle<WeakCell> weak_cell1b = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| |
| Handle<WeakCell> weak_cell2a = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key2, isolate); |
| Handle<WeakCell> weak_cell2b = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key2, 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); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b, |
| *weak_cell1a); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 2, *weak_cell2b, |
| *weak_cell2a); |
| } |
| |
| JSFinalizationGroup::Unregister(finalization_group, key2, 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); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b, |
| *weak_cell1a); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 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> key1 = CreateKey("key1", isolate); |
| Handle<Object> undefined = |
| handle(ReadOnlyRoots(isolate).undefined_value(), isolate); |
| |
| Handle<WeakCell> weak_cell1 = FinalizationGroupRegister( |
| finalization_group, js_object, undefined, key1, isolate); |
| |
| VerifyWeakCellChain(isolate, finalization_group->active_cells(), 1, |
| *weak_cell1); |
| VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1); |
| } |
| |
| JSFinalizationGroup::Unregister(finalization_group, key1, isolate); |
| |
| VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0); |
| VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0); |
| } |
| |
| JSFinalizationGroup::Unregister(finalization_group, key1, isolate); |
| |
| VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0); |
| VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 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> key1 = CreateKey("key1", isolate); |
| Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1"); |
| Handle<WeakCell> weak_cell1 = FinalizationGroupRegister( |
| finalization_group, js_object, holdings1, key1, 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); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0); |
| } |
| |
| JSFinalizationGroup::Unregister(finalization_group, key1, isolate); |
| |
| VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0); |
| VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0); |
| { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 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> key1 = CreateKey("key1", isolate); |
| |
| JSFinalizationGroup::Unregister(finalization_group, key1, 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. |
| isolate->default_microtask_queue()->RunMicrotasks(isolate); |
| 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. |
| isolate->default_microtask_queue()->RunMicrotasks(isolate); |
| heap::SimulateIncrementalMarking(heap, true); |
| CcTest::CollectAllGarbage(); |
| |
| CHECK(weak_ref->target().IsUndefined(isolate)); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |