blob: 008fecf1f27758811a9d82ff3348923034c0baa4 [file] [log] [blame]
// Copyright 2018 The Chromium 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 <atomic>
#include <iostream>
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
namespace blink {
namespace {
class WeaknessMarkingTest : public TestSupportingGC {};
} // namespace
enum class ObjectLiveness { Alive = 0, Dead };
template <typename Map,
template <typename T>
class KeyHolder,
template <typename T>
class ValueHolder>
void TestMapImpl(ObjectLiveness expected_key_liveness,
ObjectLiveness expected_value_liveness) {
Persistent<Map> map = MakeGarbageCollected<Map>();
KeyHolder<IntegerObject> int_key = MakeGarbageCollected<IntegerObject>(1);
ValueHolder<IntegerObject> int_value = MakeGarbageCollected<IntegerObject>(2);
map->insert(int_key.Get(), int_value.Get());
TestSupportingGC::PreciselyCollectGarbage();
if (expected_key_liveness == ObjectLiveness::Alive) {
EXPECT_TRUE(int_key.Get());
} else {
EXPECT_FALSE(int_key.Get());
}
if (expected_value_liveness == ObjectLiveness::Alive) {
EXPECT_TRUE(int_value.Get());
} else {
EXPECT_FALSE(int_value.Get());
}
EXPECT_EQ(((expected_key_liveness == ObjectLiveness::Alive) &&
(expected_value_liveness == ObjectLiveness::Alive))
? 1u
: 0u,
map->size());
}
TEST_F(WeaknessMarkingTest, WeakToWeakMap) {
using Map = HeapHashMap<WeakMember<IntegerObject>, WeakMember<IntegerObject>>;
TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
ObjectLiveness::Alive);
TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
ObjectLiveness::Dead);
TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
ObjectLiveness::Dead);
}
TEST_F(WeaknessMarkingTest, WeakToStrongMap) {
using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
ObjectLiveness::Alive);
TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
ObjectLiveness::Dead);
}
TEST_F(WeaknessMarkingTest, StrongToWeakMap) {
using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
ObjectLiveness::Dead);
TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
ObjectLiveness::Dead);
}
TEST_F(WeaknessMarkingTest, StrongToStrongMap) {
using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>;
TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Alive,
ObjectLiveness::Alive);
}
template <typename Set, template <typename T> class Type>
void TestSetImpl(ObjectLiveness object_liveness) {
Persistent<Set> set = MakeGarbageCollected<Set>();
Type<IntegerObject> object = MakeGarbageCollected<IntegerObject>(1);
set->insert(object.Get());
TestSupportingGC::PreciselyCollectGarbage();
if (object_liveness == ObjectLiveness::Alive) {
EXPECT_TRUE(object.Get());
} else {
EXPECT_FALSE(object.Get());
}
EXPECT_EQ((object_liveness == ObjectLiveness::Alive) ? 1u : 0u, set->size());
}
TEST_F(WeaknessMarkingTest, WeakSet) {
using Set = HeapHashSet<WeakMember<IntegerObject>>;
TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Dead);
}
TEST_F(WeaknessMarkingTest, StrongSet) {
using Set = HeapHashSet<Member<IntegerObject>>;
TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Alive);
}
TEST_F(WeaknessMarkingTest, DeadValueInReverseEphemeron) {
using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
Persistent<Map> map = MakeGarbageCollected<Map>();
Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
map->insert(key.Get(), MakeGarbageCollected<IntegerObject>(2));
EXPECT_EQ(1u, map->size());
TestSupportingGC::PreciselyCollectGarbage();
// Entries with dead values are removed.
EXPECT_EQ(0u, map->size());
}
TEST_F(WeaknessMarkingTest, NullValueInReverseEphemeron) {
using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
Persistent<Map> map = MakeGarbageCollected<Map>();
Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
map->insert(key.Get(), nullptr);
EXPECT_EQ(1u, map->size());
TestSupportingGC::PreciselyCollectGarbage();
// Entries with null values are kept.
EXPECT_EQ(1u, map->size());
}
namespace weakness_marking_test {
class EphemeronCallbacksCounter
: public GarbageCollected<EphemeronCallbacksCounter> {
public:
EphemeronCallbacksCounter(size_t* count_holder)
: count_holder_(count_holder) {}
void Trace(Visitor* visitor) const {
visitor->RegisterWeakCallbackMethod<EphemeronCallbacksCounter,
&EphemeronCallbacksCounter::Callback>(
this);
}
void Callback(const LivenessBroker& info) {
*count_holder_ = ThreadState::Current()
->Heap()
.GetDiscoveredEphemeronPairsWorklist()
->SizeForTesting();
}
private:
size_t* count_holder_;
};
TEST_F(WeaknessMarkingTest, UntracableEphemeronIsNotRegsitered) {
size_t ephemeron_count;
Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
TestSupportingGC::PreciselyCollectGarbage();
size_t old_ephemeron_count = ephemeron_count;
using Map = HeapHashMap<WeakMember<IntegerObject>, int>;
Persistent<Map> map = MakeGarbageCollected<Map>();
map->insert(MakeGarbageCollected<IntegerObject>(1), 2);
TestSupportingGC::PreciselyCollectGarbage();
// Ephemeron value is not traceable, thus the map shouldn't be treated as an
// ephemeron.
EXPECT_EQ(old_ephemeron_count, ephemeron_count);
}
TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) {
size_t ephemeron_count;
Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
TestSupportingGC::PreciselyCollectGarbage();
size_t old_ephemeron_count = ephemeron_count;
using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
Persistent<Map> map = MakeGarbageCollected<Map>();
map->insert(MakeGarbageCollected<IntegerObject>(1),
MakeGarbageCollected<IntegerObject>(2));
TestSupportingGC::PreciselyCollectGarbage();
EXPECT_NE(old_ephemeron_count, ephemeron_count);
}
TEST_F(WeaknessMarkingTest, SwapIntoAlreadyProcessedWeakSet) {
// Regression test: https://crbug.com/1038623
//
// Test ensures that an empty weak set that has already been marked sets up
// weakness callbacks. This is important as another backing may be swapped in
// at some point after marking it initially.
using WeakLinkedSet = HeapLinkedHashSet<WeakMember<IntegerObject>>;
Persistent<WeakLinkedSet> holder3(MakeGarbageCollected<WeakLinkedSet>());
Persistent<WeakLinkedSet> holder4(MakeGarbageCollected<WeakLinkedSet>());
holder3->insert(MakeGarbageCollected<IntegerObject>(1));
IncrementalMarkingTestDriver driver2(ThreadState::Current());
driver2.Start();
driver2.FinishSteps();
holder3->Swap(*holder4.Get());
driver2.FinishGC();
}
TEST_F(WeaknessMarkingTest, EmptyEphemeronCollection) {
// Tests that an empty ephemeron collection does not crash in the GC when
// processing a non-existent backing store.
using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
Persistent<Map> map = MakeGarbageCollected<Map>();
TestSupportingGC::PreciselyCollectGarbage();
}
TEST_F(WeaknessMarkingTest, ClearWeakHashTableAfterMarking) {
// Regression test: https://crbug.com/1054363
//
// Test ensures that no marked backing with weak pointers to dead object is
// left behind after marking. The test creates a backing that is floating
// garbage. The marking verifier ensures that all buckets are properly
// deleted.
using Set = HeapHashSet<WeakMember<IntegerObject>>;
Persistent<Set> holder(MakeGarbageCollected<Set>());
holder->insert(MakeGarbageCollected<IntegerObject>(1));
IncrementalMarkingTestDriver driver(ThreadState::Current());
driver.Start();
driver.FinishSteps();
holder->clear();
driver.FinishGC();
}
TEST_F(WeaknessMarkingTest, StrongifyBackingOnStack) {
// Test eunsures that conservative GC strongifies the backing store of
// on-stack HeapLinkedHashSet.
using WeakSet = HeapLinkedHashSet<WeakMember<IntegerObject>>;
using StrongSet = HeapLinkedHashSet<Member<IntegerObject>>;
WeakSet weak_set_on_stack;
weak_set_on_stack.insert(MakeGarbageCollected<IntegerObject>(1));
StrongSet strong_set_on_stack;
strong_set_on_stack.insert(MakeGarbageCollected<IntegerObject>(1));
TestSupportingGC::ConservativelyCollectGarbage();
EXPECT_EQ(1u, weak_set_on_stack.size());
EXPECT_EQ(1u, strong_set_on_stack.size());
EXPECT_EQ(1, weak_set_on_stack.begin()->Get()->Value());
EXPECT_EQ(1, strong_set_on_stack.begin()->Get()->Value());
}
} // namespace weakness_marking_test
} // namespace blink