1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <atomic>
6 #include <iostream>
7 #include <memory>
8 
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
11 #include "third_party/blink/renderer/platform/heap/persistent.h"
12 #include "third_party/blink/renderer/platform/heap/visitor.h"
13 #include "third_party/blink/renderer/platform/wtf/hash_traits.h"
14 
15 namespace blink {
16 
17 namespace {
18 
19 class WeaknessMarkingTest : public TestSupportingGC {};
20 
21 }  // namespace
22 
23 enum class ObjectLiveness { Alive = 0, Dead };
24 
25 template <typename Map,
26           template <typename T>
27           class KeyHolder,
28           template <typename T>
29           class ValueHolder>
TestMapImpl(ObjectLiveness expected_key_liveness,ObjectLiveness expected_value_liveness)30 void TestMapImpl(ObjectLiveness expected_key_liveness,
31                  ObjectLiveness expected_value_liveness) {
32   Persistent<Map> map = MakeGarbageCollected<Map>();
33   KeyHolder<IntegerObject> int_key = MakeGarbageCollected<IntegerObject>(1);
34   ValueHolder<IntegerObject> int_value = MakeGarbageCollected<IntegerObject>(2);
35   map->insert(int_key.Get(), int_value.Get());
36   TestSupportingGC::PreciselyCollectGarbage();
37   if (expected_key_liveness == ObjectLiveness::Alive) {
38     EXPECT_TRUE(int_key.Get());
39   } else {
40     EXPECT_FALSE(int_key.Get());
41   }
42   if (expected_value_liveness == ObjectLiveness::Alive) {
43     EXPECT_TRUE(int_value.Get());
44   } else {
45     EXPECT_FALSE(int_value.Get());
46   }
47   EXPECT_EQ(((expected_key_liveness == ObjectLiveness::Alive) &&
48              (expected_value_liveness == ObjectLiveness::Alive))
49                 ? 1u
50                 : 0u,
51             map->size());
52 }
53 
TEST_F(WeaknessMarkingTest,WeakToWeakMap)54 TEST_F(WeaknessMarkingTest, WeakToWeakMap) {
55   using Map = HeapHashMap<WeakMember<IntegerObject>, WeakMember<IntegerObject>>;
56   TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
57                                            ObjectLiveness::Alive);
58   TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
59                                                ObjectLiveness::Alive);
60   TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
61                                                ObjectLiveness::Dead);
62   TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
63                                                    ObjectLiveness::Dead);
64 }
65 
TEST_F(WeaknessMarkingTest,WeakToStrongMap)66 TEST_F(WeaknessMarkingTest, WeakToStrongMap) {
67   using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
68   TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
69                                            ObjectLiveness::Alive);
70   TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
71                                                ObjectLiveness::Alive);
72   TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
73                                                ObjectLiveness::Alive);
74   TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
75                                                    ObjectLiveness::Dead);
76 }
77 
TEST_F(WeaknessMarkingTest,StrongToWeakMap)78 TEST_F(WeaknessMarkingTest, StrongToWeakMap) {
79   using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
80   TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
81                                            ObjectLiveness::Alive);
82   TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
83                                                ObjectLiveness::Alive);
84   TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
85                                                ObjectLiveness::Dead);
86   TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
87                                                    ObjectLiveness::Dead);
88 }
89 
TEST_F(WeaknessMarkingTest,StrongToStrongMap)90 TEST_F(WeaknessMarkingTest, StrongToStrongMap) {
91   using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>;
92   TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
93                                            ObjectLiveness::Alive);
94   TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
95                                                ObjectLiveness::Alive);
96   TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
97                                                ObjectLiveness::Alive);
98   TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Alive,
99                                                    ObjectLiveness::Alive);
100 }
101 
102 template <typename Set, template <typename T> class Type>
TestSetImpl(ObjectLiveness object_liveness)103 void TestSetImpl(ObjectLiveness object_liveness) {
104   Persistent<Set> set = MakeGarbageCollected<Set>();
105   Type<IntegerObject> object = MakeGarbageCollected<IntegerObject>(1);
106   set->insert(object.Get());
107   TestSupportingGC::PreciselyCollectGarbage();
108   if (object_liveness == ObjectLiveness::Alive) {
109     EXPECT_TRUE(object.Get());
110   } else {
111     EXPECT_FALSE(object.Get());
112   }
113   EXPECT_EQ((object_liveness == ObjectLiveness::Alive) ? 1u : 0u, set->size());
114 }
115 
TEST_F(WeaknessMarkingTest,WeakSet)116 TEST_F(WeaknessMarkingTest, WeakSet) {
117   using Set = HeapHashSet<WeakMember<IntegerObject>>;
118   TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
119   TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Dead);
120 }
121 
TEST_F(WeaknessMarkingTest,StrongSet)122 TEST_F(WeaknessMarkingTest, StrongSet) {
123   using Set = HeapHashSet<Member<IntegerObject>>;
124   TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
125   TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Alive);
126 }
127 
TEST_F(WeaknessMarkingTest,DeadValueInReverseEphemeron)128 TEST_F(WeaknessMarkingTest, DeadValueInReverseEphemeron) {
129   using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
130   Persistent<Map> map = MakeGarbageCollected<Map>();
131   Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
132   map->insert(key.Get(), MakeGarbageCollected<IntegerObject>(2));
133   EXPECT_EQ(1u, map->size());
134   TestSupportingGC::PreciselyCollectGarbage();
135   // Entries with dead values are removed.
136   EXPECT_EQ(0u, map->size());
137 }
138 
TEST_F(WeaknessMarkingTest,NullValueInReverseEphemeron)139 TEST_F(WeaknessMarkingTest, NullValueInReverseEphemeron) {
140   using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
141   Persistent<Map> map = MakeGarbageCollected<Map>();
142   Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
143   map->insert(key.Get(), nullptr);
144   EXPECT_EQ(1u, map->size());
145   TestSupportingGC::PreciselyCollectGarbage();
146   // Entries with null values are kept.
147   EXPECT_EQ(1u, map->size());
148 }
149 
150 namespace weakness_marking_test {
151 
152 class EphemeronCallbacksCounter
153     : public GarbageCollected<EphemeronCallbacksCounter> {
154  public:
EphemeronCallbacksCounter(size_t * count_holder)155   EphemeronCallbacksCounter(size_t* count_holder)
156       : count_holder_(count_holder) {}
157 
Trace(Visitor * visitor)158   void Trace(Visitor* visitor) {
159     visitor->RegisterWeakCallbackMethod<EphemeronCallbacksCounter,
160                                         &EphemeronCallbacksCounter::Callback>(
161         this);
162   }
163 
Callback(const WeakCallbackInfo & info)164   void Callback(const WeakCallbackInfo& info) {
165     *count_holder_ = ThreadState::Current()->Heap().ephemeron_callbacks_.size();
166   }
167 
168  private:
169   size_t* count_holder_;
170 };
171 
TEST_F(WeaknessMarkingTest,UntracableEphemeronIsNotRegsitered)172 TEST_F(WeaknessMarkingTest, UntracableEphemeronIsNotRegsitered) {
173   size_t ephemeron_count;
174   Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
175       MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
176   TestSupportingGC::PreciselyCollectGarbage();
177   size_t old_ephemeron_count = ephemeron_count;
178   using Map = HeapHashMap<WeakMember<IntegerObject>, int>;
179   Persistent<Map> map = MakeGarbageCollected<Map>();
180   map->insert(MakeGarbageCollected<IntegerObject>(1), 2);
181   TestSupportingGC::PreciselyCollectGarbage();
182   // Ephemeron value is not traceable, thus the map shouldn't be treated as an
183   // ephemeron.
184   EXPECT_EQ(old_ephemeron_count, ephemeron_count);
185 }
186 
TEST_F(WeaknessMarkingTest,TracableEphemeronIsRegsitered)187 TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) {
188   size_t ephemeron_count;
189   Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
190       MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
191   TestSupportingGC::PreciselyCollectGarbage();
192   size_t old_ephemeron_count = ephemeron_count;
193   using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
194   Persistent<Map> map = MakeGarbageCollected<Map>();
195   map->insert(MakeGarbageCollected<IntegerObject>(1),
196               MakeGarbageCollected<IntegerObject>(2));
197   TestSupportingGC::PreciselyCollectGarbage();
198   EXPECT_NE(old_ephemeron_count, ephemeron_count);
199 }
200 
201 // TODO(keinakashima): add tests for NewLinkedHashSet after supporting
202 // WeakMember
TEST_F(WeaknessMarkingTest,SwapIntoAlreadyProcessedWeakSet)203 TEST_F(WeaknessMarkingTest, SwapIntoAlreadyProcessedWeakSet) {
204   // Regression test: https://crbug.com/1038623
205   //
206   // Test ensures that an empty weak set that has already been marked sets up
207   // weakness callbacks. This is important as another backing may be swapped in
208   // at some point after marking it initially.
209   using WeakLinkedSet = HeapLinkedHashSet<WeakMember<IntegerObject>>;
210   Persistent<WeakLinkedSet> holder1(MakeGarbageCollected<WeakLinkedSet>());
211   Persistent<WeakLinkedSet> holder2(MakeGarbageCollected<WeakLinkedSet>());
212   holder1->insert(MakeGarbageCollected<IntegerObject>(1));
213   IncrementalMarkingTestDriver driver(ThreadState::Current());
214   driver.Start();
215   driver.FinishSteps();
216   holder1->Swap(*holder2.Get());
217   driver.FinishGC();
218 }
219 
TEST_F(WeaknessMarkingTest,EmptyEphemeronCollection)220 TEST_F(WeaknessMarkingTest, EmptyEphemeronCollection) {
221   // Tests that an empty ephemeron collection does not crash in the GC when
222   // processing a non-existent backing store.
223   using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
224   Persistent<Map> map = MakeGarbageCollected<Map>();
225   TestSupportingGC::PreciselyCollectGarbage();
226 }
227 
TEST_F(WeaknessMarkingTest,ClearWeakHashTableAfterMarking)228 TEST_F(WeaknessMarkingTest, ClearWeakHashTableAfterMarking) {
229   // Regression test: https://crbug.com/1054363
230   //
231   // Test ensures that no marked backing with weak pointers to dead object is
232   // left behind after marking. The test creates a backing that is floating
233   // garbage. The marking verifier ensures that all buckets are properly
234   // deleted.
235   using Set = HeapHashSet<WeakMember<IntegerObject>>;
236   Persistent<Set> holder(MakeGarbageCollected<Set>());
237   holder->insert(MakeGarbageCollected<IntegerObject>(1));
238   IncrementalMarkingTestDriver driver(ThreadState::Current());
239   driver.Start();
240   driver.FinishSteps();
241   holder->clear();
242   driver.FinishGC();
243 }
244 
245 }  // namespace weakness_marking_test
246 
247 }  // namespace blink
248