1 // Copyright 2017 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 <initializer_list>
6
7 #include "base/bind.h"
8 #include "base/test/scoped_feature_list.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/blink/public/common/features.h"
11 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
12 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
13 #include "third_party/blink/renderer/platform/heap/heap.h"
14 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
15 #include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
16 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
17 #include "third_party/blink/renderer/platform/heap/impl/heap_compact.h"
18 #include "third_party/blink/renderer/platform/heap/impl/trace_traits.h"
19 #include "third_party/blink/renderer/platform/heap/member.h"
20 #include "third_party/blink/renderer/platform/heap/persistent.h"
21 #include "third_party/blink/renderer/platform/heap/thread_state.h"
22 #include "third_party/blink/renderer/platform/heap/visitor.h"
23 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
24
25 namespace blink {
26
27 class IncrementalMarkingTest : public TestSupportingGC {};
28
29 namespace incremental_marking_test {
30
31 // Visitor that expects every directly reachable object from a given backing
32 // store to be in the set of provided objects.
33 class BackingVisitor : public Visitor {
34 public:
BackingVisitor(ThreadState * state,Vector<void * > * objects)35 BackingVisitor(ThreadState* state, Vector<void*>* objects)
36 : Visitor(state), objects_(objects) {}
~BackingVisitor()37 ~BackingVisitor() final {}
38
ProcessBackingStore(HeapObjectHeader * header)39 void ProcessBackingStore(HeapObjectHeader* header) {
40 EXPECT_TRUE(header->IsMarked());
41 header->Unmark();
42
43 GCInfo::From(header->GcInfoIndex()).trace(this, header->Payload());
44 }
45
Visit(const void * obj,TraceDescriptor desc)46 void Visit(const void* obj, TraceDescriptor desc) final {
47 EXPECT_TRUE(obj);
48 auto** pos = std::find(objects_->begin(), objects_->end(), obj);
49 if (objects_->end() != pos)
50 objects_->erase(pos);
51 // The garbage collector will find those objects so we can mark them.
52 HeapObjectHeader* const header =
53 HeapObjectHeader::FromPayload(desc.base_object_payload);
54 if (!header->IsMarked())
55 EXPECT_TRUE(header->TryMark());
56 }
57
VisitEphemeron(const void * key,TraceDescriptor value_desc)58 void VisitEphemeron(const void* key, TraceDescriptor value_desc) final {
59 if (!HeapObjectHeader::FromPayload(key)->IsMarked())
60 return;
61 value_desc.callback(this, value_desc.base_object_payload);
62 }
63
64 private:
65 Vector<void*>* objects_;
66 };
67
68 // Base class for initializing worklists.
69 class IncrementalMarkingScopeBase {
70 DISALLOW_NEW();
71
72 public:
IncrementalMarkingScopeBase(ThreadState * thread_state)73 explicit IncrementalMarkingScopeBase(ThreadState* thread_state)
74 : thread_state_(thread_state), heap_(thread_state_->Heap()) {
75 if (thread_state_->IsMarkingInProgress() ||
76 thread_state_->IsSweepingInProgress()) {
77 TestSupportingGC::PreciselyCollectGarbage();
78 }
79 heap_.SetupWorklists(false);
80 }
81
~IncrementalMarkingScopeBase()82 ~IncrementalMarkingScopeBase() {
83 heap_.DestroyMarkingWorklists(BlinkGC::StackState::kNoHeapPointersOnStack);
84 heap_.DestroyCompactionWorklists();
85 }
86
heap() const87 ThreadHeap& heap() const { return heap_; }
88
89 protected:
90 ThreadState* const thread_state_;
91 ThreadHeap& heap_;
92 };
93
94 class IncrementalMarkingScope : public IncrementalMarkingScopeBase {
95 STACK_ALLOCATED();
96
97 public:
IncrementalMarkingScope(ThreadState * thread_state)98 explicit IncrementalMarkingScope(ThreadState* thread_state)
99 : IncrementalMarkingScopeBase(thread_state),
100 gc_forbidden_scope_(thread_state),
101 marking_worklist_(heap_.GetMarkingWorklist()),
102 write_barrier_worklist_(heap_.GetWriteBarrierWorklist()),
103 not_fully_constructed_worklist_(
104 heap_.GetNotFullyConstructedWorklist()) {
105 thread_state_->SetGCPhase(ThreadState::GCPhase::kMarking);
106 ThreadState::AtomicPauseScope atomic_pause_scope_(thread_state_);
107 ScriptForbiddenScope script_forbidden_scope;
108 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
109 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
110 EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
111 thread_state->EnableIncrementalMarkingBarrier();
112 thread_state->current_gc_data_.visitor = std::make_unique<MarkingVisitor>(
113 thread_state, MarkingVisitor::kGlobalMarking);
114 }
115
~IncrementalMarkingScope()116 ~IncrementalMarkingScope() {
117 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
118 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
119 EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
120 thread_state_->DisableIncrementalMarkingBarrier();
121 // Need to clear out unused worklists that might have been polluted during
122 // test.
123 heap_.GetWeakCallbackWorklist()->Clear();
124 thread_state_->SetGCPhase(ThreadState::GCPhase::kSweeping);
125 thread_state_->SetGCPhase(ThreadState::GCPhase::kNone);
126 }
127
marking_worklist() const128 MarkingWorklist* marking_worklist() const { return marking_worklist_; }
write_barrier_worklist() const129 WriteBarrierWorklist* write_barrier_worklist() const {
130 return write_barrier_worklist_;
131 }
not_fully_constructed_worklist() const132 NotFullyConstructedWorklist* not_fully_constructed_worklist() const {
133 return not_fully_constructed_worklist_;
134 }
135
136 protected:
137 ThreadState::GCForbiddenScope gc_forbidden_scope_;
138 MarkingWorklist* const marking_worklist_;
139 WriteBarrierWorklist* const write_barrier_worklist_;
140 NotFullyConstructedWorklist* const not_fully_constructed_worklist_;
141 };
142
143 // Expects that the write barrier fires for the objects passed to the
144 // constructor. This requires that the objects are added to the marking stack
145 // as well as headers being marked.
146 class ExpectWriteBarrierFires : public IncrementalMarkingScope {
147 public:
ExpectWriteBarrierFires(ThreadState * thread_state,std::initializer_list<void * > objects)148 ExpectWriteBarrierFires(ThreadState* thread_state,
149 std::initializer_list<void*> objects)
150 : IncrementalMarkingScope(thread_state),
151 objects_(objects),
152 backing_visitor_(thread_state_, &objects_) {
153 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
154 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
155 for (void* object : objects_) {
156 // Ensure that the object is in the normal arena so we can ignore backing
157 // objects on the marking stack.
158 CHECK(ThreadHeap::IsNormalArenaIndex(
159 PageFromObject(object)->Arena()->ArenaIndex()));
160 headers_.push_back(HeapObjectHeader::FromPayload(object));
161 EXPECT_FALSE(headers_.back()->IsMarked());
162 }
163 EXPECT_FALSE(objects_.IsEmpty());
164 }
165
~ExpectWriteBarrierFires()166 ~ExpectWriteBarrierFires() {
167 // All objects watched should be on the marking or write barrier worklist.
168 MarkingItem item;
169 while (marking_worklist_->Pop(WorklistTaskId::MutatorThread, &item)) {
170 // Inspect backing stores to allow specifying objects that are only
171 // reachable through a backing store.
172 if (!ThreadHeap::IsNormalArenaIndex(
173 PageFromObject(item.base_object_payload)
174 ->Arena()
175 ->ArenaIndex())) {
176 backing_visitor_.ProcessBackingStore(
177 HeapObjectHeader::FromPayload(item.base_object_payload));
178 continue;
179 }
180 auto** pos =
181 std::find(objects_.begin(), objects_.end(), item.base_object_payload);
182 if (objects_.end() != pos)
183 objects_.erase(pos);
184 }
185 HeapObjectHeader* header;
186 while (
187 write_barrier_worklist_->Pop(WorklistTaskId::MutatorThread, &header)) {
188 // Inspect backing stores to allow specifying objects that are only
189 // reachable through a backing store.
190 if (!ThreadHeap::IsNormalArenaIndex(
191 PageFromObject(header->Payload())->Arena()->ArenaIndex())) {
192 backing_visitor_.ProcessBackingStore(header);
193 continue;
194 }
195 auto** pos =
196 std::find(objects_.begin(), objects_.end(), header->Payload());
197 if (objects_.end() != pos)
198 objects_.erase(pos);
199 }
200 EXPECT_TRUE(objects_.IsEmpty());
201 // All headers of objects watched should be marked at this point.
202 for (HeapObjectHeader* header : headers_) {
203 EXPECT_TRUE(header->IsMarked());
204 header->Unmark();
205 }
206 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
207 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
208 }
209
210 private:
211 Vector<void*> objects_;
212 Vector<HeapObjectHeader*> headers_;
213 BackingVisitor backing_visitor_;
214 };
215
216 // Expects that no write barrier fires for the objects passed to the
217 // constructor. This requires that the marking stack stays empty and the marking
218 // state of the object stays the same across the lifetime of the scope.
219 class ExpectNoWriteBarrierFires : public IncrementalMarkingScope {
220 public:
ExpectNoWriteBarrierFires(ThreadState * thread_state,std::initializer_list<void * > objects)221 ExpectNoWriteBarrierFires(ThreadState* thread_state,
222 std::initializer_list<void*> objects)
223 : IncrementalMarkingScope(thread_state) {
224 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
225 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
226 for (void* object : objects) {
227 HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
228 headers_.push_back(std::make_pair(header, header->IsMarked()));
229 }
230 }
231
~ExpectNoWriteBarrierFires()232 ~ExpectNoWriteBarrierFires() {
233 EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
234 EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
235 for (const auto& pair : headers_) {
236 EXPECT_EQ(pair.second, pair.first->IsMarked());
237 pair.first->Unmark();
238 }
239 }
240
241 private:
242 Vector<std::pair<HeapObjectHeader*, bool /* was marked */>> headers_;
243 };
244
245 class Object : public LinkedObject {
246 public:
247 Object() = default;
Object(Object * next)248 explicit Object(Object* next) : LinkedObject(next) {}
249
IsMarked() const250 bool IsMarked() const {
251 return HeapObjectHeader::FromPayload(this)->IsMarked();
252 }
253
Trace(Visitor * visitor) const254 void Trace(Visitor* visitor) const override { LinkedObject::Trace(visitor); }
255 };
256
257 class ObjectWithWriteBarrier : public GarbageCollected<ObjectWithWriteBarrier> {
258 public:
Trace(Visitor * v) const259 void Trace(Visitor* v) const { v->Trace(object_); }
260
Set(Object * object)261 void Set(Object* object) { object_ = object; }
262
263 private:
264 Member<Object> object_;
265 };
266
267 // =============================================================================
268 // Basic infrastructure support. ===============================================
269 // =============================================================================
270
TEST_F(IncrementalMarkingTest,EnableDisableBarrier)271 TEST_F(IncrementalMarkingTest, EnableDisableBarrier) {
272 EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
273 ThreadState::Current()->EnableIncrementalMarkingBarrier();
274 EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
275 EXPECT_TRUE(ThreadState::IsAnyIncrementalMarking());
276 ThreadState::Current()->DisableIncrementalMarkingBarrier();
277 EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
278 }
279
TEST_F(IncrementalMarkingTest,WriteBarrierTriggersWhenMarkingIsOn)280 TEST_F(IncrementalMarkingTest, WriteBarrierTriggersWhenMarkingIsOn) {
281 auto* object1 = MakeGarbageCollected<Object>();
282 auto* object2 = MakeGarbageCollected<ObjectWithWriteBarrier>();
283 {
284 ExpectWriteBarrierFires scope(ThreadState::Current(), {object1});
285 EXPECT_FALSE(object1->IsMarked());
286 object2->Set(object1);
287 EXPECT_TRUE(object1->IsMarked());
288 }
289 }
290
TEST_F(IncrementalMarkingTest,WriteBarrierBailoutWhenMarkingIsOff)291 TEST_F(IncrementalMarkingTest, WriteBarrierBailoutWhenMarkingIsOff) {
292 auto* object1 = MakeGarbageCollected<Object>();
293 auto* object2 = MakeGarbageCollected<ObjectWithWriteBarrier>();
294 EXPECT_FALSE(object1->IsMarked());
295 object2->Set(object1);
296 EXPECT_FALSE(object1->IsMarked());
297 }
298
299 // =============================================================================
300 // Member<T> support. ==========================================================
301 // =============================================================================
302
TEST_F(IncrementalMarkingTest,MemberSetUnmarkedObject)303 TEST_F(IncrementalMarkingTest, MemberSetUnmarkedObject) {
304 auto* parent = MakeGarbageCollected<Object>();
305 auto* child = MakeGarbageCollected<Object>();
306 {
307 ExpectWriteBarrierFires scope(ThreadState::Current(), {child});
308 EXPECT_FALSE(child->IsMarked());
309 parent->set_next(child);
310 EXPECT_TRUE(child->IsMarked());
311 }
312 }
313
TEST_F(IncrementalMarkingTest,MemberSetMarkedObjectNoBarrier)314 TEST_F(IncrementalMarkingTest, MemberSetMarkedObjectNoBarrier) {
315 auto* parent = MakeGarbageCollected<Object>();
316 auto* child = MakeGarbageCollected<Object>();
317 EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark());
318 {
319 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {child});
320 parent->set_next(child);
321 }
322 }
323
TEST_F(IncrementalMarkingTest,MemberInitializingStoreNoBarrier)324 TEST_F(IncrementalMarkingTest, MemberInitializingStoreNoBarrier) {
325 auto* object1 = MakeGarbageCollected<Object>();
326 HeapObjectHeader* object1_header = HeapObjectHeader::FromPayload(object1);
327 {
328 IncrementalMarkingScope scope(ThreadState::Current());
329 EXPECT_FALSE(object1_header->IsMarked());
330 auto* object2 = MakeGarbageCollected<Object>(object1);
331 HeapObjectHeader* object2_header = HeapObjectHeader::FromPayload(object2);
332 EXPECT_FALSE(object1_header->IsMarked());
333 EXPECT_FALSE(object2_header->IsMarked());
334 }
335 }
336
TEST_F(IncrementalMarkingTest,MemberReferenceAssignMember)337 TEST_F(IncrementalMarkingTest, MemberReferenceAssignMember) {
338 auto* obj = MakeGarbageCollected<LinkedObject>();
339 auto* ref_obj = MakeGarbageCollected<LinkedObject>();
340 Member<LinkedObject>& m2 = ref_obj->next_ref();
341 Member<LinkedObject> m3(obj);
342 {
343 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
344 m2 = m3;
345 }
346 }
347
TEST_F(IncrementalMarkingTest,MemberSetDeletedValueNoBarrier)348 TEST_F(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) {
349 auto* obj = MakeGarbageCollected<LinkedObject>();
350 Member<LinkedObject>& m = obj->next_ref();
351 {
352 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
353 m = WTF::kHashTableDeletedValue;
354 }
355 }
356
TEST_F(IncrementalMarkingTest,MemberCopyDeletedValueNoBarrier)357 TEST_F(IncrementalMarkingTest, MemberCopyDeletedValueNoBarrier) {
358 auto* obj1 = MakeGarbageCollected<LinkedObject>();
359 Member<LinkedObject>& m1 = obj1->next_ref();
360 m1 = WTF::kHashTableDeletedValue;
361 {
362 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
363 auto* obj2 = MakeGarbageCollected<LinkedObject>();
364 obj2->next_ref() = m1;
365 }
366 }
367
TEST_F(IncrementalMarkingTest,MemberHashTraitConstructDeletedValueNoBarrier)368 TEST_F(IncrementalMarkingTest, MemberHashTraitConstructDeletedValueNoBarrier) {
369 auto* obj = MakeGarbageCollected<LinkedObject>();
370 Member<LinkedObject>& m = obj->next_ref();
371 {
372 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
373 HashTraits<Member<LinkedObject>>::ConstructDeletedValue(m, false);
374 }
375 }
376
TEST_F(IncrementalMarkingTest,MemberHashTraitIsDeletedValueNoBarrier)377 TEST_F(IncrementalMarkingTest, MemberHashTraitIsDeletedValueNoBarrier) {
378 auto* obj =
379 MakeGarbageCollected<LinkedObject>(MakeGarbageCollected<LinkedObject>());
380 Member<LinkedObject>& m = obj->next_ref();
381 {
382 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
383 EXPECT_FALSE(HashTraits<Member<LinkedObject>>::IsDeletedValue(m));
384 }
385 }
386
387 // =============================================================================
388 // Mixin support. ==============================================================
389 // =============================================================================
390
391 namespace {
392
393 class Mixin : public GarbageCollectedMixin {
394 public:
Mixin()395 Mixin() : next_(nullptr) {}
~Mixin()396 virtual ~Mixin() {}
397
Trace(Visitor * visitor) const398 void Trace(Visitor* visitor) const override { visitor->Trace(next_); }
399
Bar()400 virtual void Bar() {}
401
402 protected:
403 Member<Object> next_;
404 };
405
406 class ClassWithVirtual {
407 protected:
Foo()408 virtual void Foo() {}
409 };
410
411 class Child : public GarbageCollected<Child>,
412 public ClassWithVirtual,
413 public Mixin {
414 public:
Child()415 Child() : ClassWithVirtual(), Mixin() {}
~Child()416 ~Child() override {}
417
Trace(Visitor * visitor) const418 void Trace(Visitor* visitor) const override { Mixin::Trace(visitor); }
419
Foo()420 void Foo() override {}
Bar()421 void Bar() override {}
422 };
423
424 class ParentWithMixinPointer : public GarbageCollected<ParentWithMixinPointer> {
425 public:
ParentWithMixinPointer()426 ParentWithMixinPointer() : mixin_(nullptr) {}
427
set_mixin(Mixin * mixin)428 void set_mixin(Mixin* mixin) { mixin_ = mixin; }
429
Trace(Visitor * visitor) const430 virtual void Trace(Visitor* visitor) const { visitor->Trace(mixin_); }
431
432 protected:
433 Member<Mixin> mixin_;
434 };
435
436 } // namespace
437
TEST_F(IncrementalMarkingTest,WriteBarrierOnUnmarkedMixinApplication)438 TEST_F(IncrementalMarkingTest, WriteBarrierOnUnmarkedMixinApplication) {
439 ParentWithMixinPointer* parent =
440 MakeGarbageCollected<ParentWithMixinPointer>();
441 auto* child = MakeGarbageCollected<Child>();
442 Mixin* mixin = static_cast<Mixin*>(child);
443 EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
444 {
445 ExpectWriteBarrierFires scope(ThreadState::Current(), {child});
446 parent->set_mixin(mixin);
447 }
448 }
449
TEST_F(IncrementalMarkingTest,NoWriteBarrierOnMarkedMixinApplication)450 TEST_F(IncrementalMarkingTest, NoWriteBarrierOnMarkedMixinApplication) {
451 ParentWithMixinPointer* parent =
452 MakeGarbageCollected<ParentWithMixinPointer>();
453 auto* child = MakeGarbageCollected<Child>();
454 EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark());
455 Mixin* mixin = static_cast<Mixin*>(child);
456 EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
457 {
458 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {child});
459 parent->set_mixin(mixin);
460 }
461 }
462
463 // =============================================================================
464 // HeapVector support. =========================================================
465 // =============================================================================
466
467 namespace {
468
469 // HeapVector allows for insertion of container objects that can be traced but
470 // are themselves non-garbage collected.
471 class NonGarbageCollectedContainer {
472 DISALLOW_NEW();
473
474 public:
NonGarbageCollectedContainer(Object * obj,int y)475 NonGarbageCollectedContainer(Object* obj, int y) : obj_(obj), y_(y) {}
476
~NonGarbageCollectedContainer()477 virtual ~NonGarbageCollectedContainer() {}
Trace(Visitor * visitor) const478 virtual void Trace(Visitor* visitor) const { visitor->Trace(obj_); }
479
480 private:
481 Member<Object> obj_;
482 int y_;
483 };
484
485 class NonGarbageCollectedContainerRoot {
486 DISALLOW_NEW();
487
488 public:
NonGarbageCollectedContainerRoot(Object * obj1,Object * obj2,int y)489 NonGarbageCollectedContainerRoot(Object* obj1, Object* obj2, int y)
490 : next_(obj1, y), obj_(obj2) {}
~NonGarbageCollectedContainerRoot()491 virtual ~NonGarbageCollectedContainerRoot() {}
492
Trace(Visitor * visitor) const493 virtual void Trace(Visitor* visitor) const {
494 visitor->Trace(next_);
495 visitor->Trace(obj_);
496 }
497
498 private:
499 NonGarbageCollectedContainer next_;
500 Member<Object> obj_;
501 };
502
503 } // namespace
504
TEST_F(IncrementalMarkingTest,HeapVectorPushBackMember)505 TEST_F(IncrementalMarkingTest, HeapVectorPushBackMember) {
506 auto* obj = MakeGarbageCollected<Object>();
507 auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>();
508 {
509 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
510 vec->push_back(obj);
511 }
512 }
513
TEST_F(IncrementalMarkingTest,HeapVectorPushBackNonGCedContainer)514 TEST_F(IncrementalMarkingTest, HeapVectorPushBackNonGCedContainer) {
515 auto* obj = MakeGarbageCollected<Object>();
516 auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
517 {
518 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
519 vec->push_back(NonGarbageCollectedContainer(obj, 1));
520 }
521 }
522
TEST_F(IncrementalMarkingTest,HeapVectorPushBackStdPair)523 TEST_F(IncrementalMarkingTest, HeapVectorPushBackStdPair) {
524 auto* obj1 = MakeGarbageCollected<Object>();
525 auto* obj2 = MakeGarbageCollected<Object>();
526 auto* vec = MakeGarbageCollected<
527 HeapVector<std::pair<Member<Object>, Member<Object>>>>();
528 {
529 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
530 vec->push_back(std::make_pair(Member<Object>(obj1), Member<Object>(obj2)));
531 }
532 }
533
TEST_F(IncrementalMarkingTest,HeapVectorEmplaceBackMember)534 TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackMember) {
535 auto* obj = MakeGarbageCollected<Object>();
536 auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>();
537 {
538 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
539 vec->emplace_back(obj);
540 }
541 }
542
TEST_F(IncrementalMarkingTest,HeapVectorEmplaceBackNonGCedContainer)543 TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackNonGCedContainer) {
544 auto* obj = MakeGarbageCollected<Object>();
545 auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
546 {
547 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
548 vec->emplace_back(obj, 1);
549 }
550 }
551
TEST_F(IncrementalMarkingTest,HeapVectorEmplaceBackStdPair)552 TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackStdPair) {
553 auto* obj1 = MakeGarbageCollected<Object>();
554 auto* obj2 = MakeGarbageCollected<Object>();
555 auto* vec = MakeGarbageCollected<
556 HeapVector<std::pair<Member<Object>, Member<Object>>>>();
557 {
558 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
559 vec->emplace_back(obj1, obj2);
560 }
561 }
562
TEST_F(IncrementalMarkingTest,HeapVectorCopyMember)563 TEST_F(IncrementalMarkingTest, HeapVectorCopyMember) {
564 auto* object = MakeGarbageCollected<Object>();
565 auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>();
566 vec1->push_back(object);
567 {
568 ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
569 MakeGarbageCollected<HeapVector<Member<Object>>>(*vec1);
570 }
571 }
572
TEST_F(IncrementalMarkingTest,HeapVectorCopyNonGCedContainer)573 TEST_F(IncrementalMarkingTest, HeapVectorCopyNonGCedContainer) {
574 auto* obj = MakeGarbageCollected<Object>();
575 auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
576 vec1->emplace_back(obj, 1);
577 {
578 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
579 MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(*vec1);
580 }
581 }
582
TEST_F(IncrementalMarkingTest,HeapVectorCopyStdPair)583 TEST_F(IncrementalMarkingTest, HeapVectorCopyStdPair) {
584 using ValueType = std::pair<Member<Object>, Member<Object>>;
585 auto* obj1 = MakeGarbageCollected<Object>();
586 auto* obj2 = MakeGarbageCollected<Object>();
587 auto* vec1 = MakeGarbageCollected<HeapVector<ValueType>>();
588 vec1->emplace_back(obj1, obj2);
589 {
590 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
591 MakeGarbageCollected<HeapVector<ValueType>>(*vec1);
592 }
593 }
594
TEST_F(IncrementalMarkingTest,HeapVectorMoveMember)595 TEST_F(IncrementalMarkingTest, HeapVectorMoveMember) {
596 auto* obj = MakeGarbageCollected<Object>();
597 auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>();
598 vec1->push_back(obj);
599 {
600 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
601 MakeGarbageCollected<HeapVector<Member<Object>>>(std::move(*vec1));
602 }
603 }
604
TEST_F(IncrementalMarkingTest,HeapVectorMoveNonGCedContainer)605 TEST_F(IncrementalMarkingTest, HeapVectorMoveNonGCedContainer) {
606 auto* obj = MakeGarbageCollected<Object>();
607 auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
608 vec1->emplace_back(obj, 1);
609 {
610 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
611 MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(
612 std::move(*vec1));
613 }
614 }
615
TEST_F(IncrementalMarkingTest,HeapVectorMoveStdPair)616 TEST_F(IncrementalMarkingTest, HeapVectorMoveStdPair) {
617 using ValueType = std::pair<Member<Object>, Member<Object>>;
618 using VectorType = HeapVector<ValueType>;
619 auto* obj1 = MakeGarbageCollected<Object>();
620 auto* obj2 = MakeGarbageCollected<Object>();
621 auto* vec1 = MakeGarbageCollected<VectorType>();
622 vec1->emplace_back(obj1, obj2);
623 {
624 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
625 MakeGarbageCollected<VectorType>(std::move(*vec1));
626 }
627 }
628
TEST_F(IncrementalMarkingTest,HeapVectorSwapMember)629 TEST_F(IncrementalMarkingTest, HeapVectorSwapMember) {
630 using VectorType = HeapVector<Member<Object>>;
631 auto* obj1 = MakeGarbageCollected<Object>();
632 auto* obj2 = MakeGarbageCollected<Object>();
633 auto* vec1 = MakeGarbageCollected<VectorType>();
634 vec1->push_back(obj1);
635 auto* vec2 = MakeGarbageCollected<VectorType>();
636 vec2->push_back(obj2);
637 {
638 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
639 std::swap(*vec1, *vec2);
640 }
641 }
642
TEST_F(IncrementalMarkingTest,HeapVectorSwapNonGCedContainer)643 TEST_F(IncrementalMarkingTest, HeapVectorSwapNonGCedContainer) {
644 using VectorType = HeapVector<NonGarbageCollectedContainer>;
645 auto* obj1 = MakeGarbageCollected<Object>();
646 auto* obj2 = MakeGarbageCollected<Object>();
647 auto* vec1 = MakeGarbageCollected<VectorType>();
648 vec1->emplace_back(obj1, 1);
649 auto* vec2 = MakeGarbageCollected<VectorType>();
650 vec2->emplace_back(obj2, 2);
651 {
652 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
653 std::swap(*vec1, *vec2);
654 }
655 }
656
TEST_F(IncrementalMarkingTest,HeapVectorSwapStdPair)657 TEST_F(IncrementalMarkingTest, HeapVectorSwapStdPair) {
658 using ValueType = std::pair<Member<Object>, Member<Object>>;
659 using VectorType = HeapVector<ValueType>;
660 auto* obj1 = MakeGarbageCollected<Object>();
661 auto* obj2 = MakeGarbageCollected<Object>();
662 auto* vec1 = MakeGarbageCollected<VectorType>();
663 vec1->emplace_back(obj1, nullptr);
664 auto* vec2 = MakeGarbageCollected<VectorType>();
665 vec2->emplace_back(nullptr, obj2);
666 {
667 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
668 std::swap(*vec1, *vec2);
669 }
670 }
671
TEST_F(IncrementalMarkingTest,HeapVectorSubscriptOperator)672 TEST_F(IncrementalMarkingTest, HeapVectorSubscriptOperator) {
673 auto* obj1 = MakeGarbageCollected<Object>();
674 auto* obj2 = MakeGarbageCollected<Object>();
675 HeapVector<Member<Object>> vec;
676 vec.push_back(obj1);
677 {
678 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj2});
679 EXPECT_EQ(1u, vec.size());
680 EXPECT_EQ(obj1, vec[0]);
681 vec[0] = obj2;
682 EXPECT_EQ(obj2, vec[0]);
683 EXPECT_FALSE(obj1->IsMarked());
684 }
685 }
686
TEST_F(IncrementalMarkingTest,HeapVectorEagerTracingStopsAtMember)687 TEST_F(IncrementalMarkingTest, HeapVectorEagerTracingStopsAtMember) {
688 auto* obj1 = MakeGarbageCollected<Object>();
689 auto* obj2 = MakeGarbageCollected<Object>();
690 auto* obj3 = MakeGarbageCollected<Object>();
691 obj1->set_next(obj3);
692 HeapVector<NonGarbageCollectedContainerRoot> vec;
693 {
694 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
695 vec.emplace_back(obj1, obj2, 3);
696 // |obj3| is only reachable from |obj1| which is not eagerly traced. Only
697 // objects without object headers are eagerly traced.
698 EXPECT_FALSE(obj3->IsMarked());
699 }
700 }
701
702 // =============================================================================
703 // HeapDeque support. ==========================================================
704 // =============================================================================
705
TEST_F(IncrementalMarkingTest,HeapDequePushBackMember)706 TEST_F(IncrementalMarkingTest, HeapDequePushBackMember) {
707 auto* obj = MakeGarbageCollected<Object>();
708 HeapDeque<Member<Object>> deq;
709 {
710 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
711 deq.push_back(obj);
712 }
713 }
714
TEST_F(IncrementalMarkingTest,HeapDequePushFrontMember)715 TEST_F(IncrementalMarkingTest, HeapDequePushFrontMember) {
716 auto* obj = MakeGarbageCollected<Object>();
717 HeapDeque<Member<Object>> deq;
718 {
719 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
720 deq.push_front(obj);
721 }
722 }
723
TEST_F(IncrementalMarkingTest,HeapDequeEmplaceBackMember)724 TEST_F(IncrementalMarkingTest, HeapDequeEmplaceBackMember) {
725 auto* obj = MakeGarbageCollected<Object>();
726 HeapDeque<Member<Object>> deq;
727 {
728 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
729 deq.emplace_back(obj);
730 }
731 }
732
TEST_F(IncrementalMarkingTest,HeapDequeEmplaceFrontMember)733 TEST_F(IncrementalMarkingTest, HeapDequeEmplaceFrontMember) {
734 auto* obj = MakeGarbageCollected<Object>();
735 HeapDeque<Member<Object>> deq;
736 {
737 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
738 deq.emplace_front(obj);
739 }
740 }
741
TEST_F(IncrementalMarkingTest,HeapDequeCopyMember)742 TEST_F(IncrementalMarkingTest, HeapDequeCopyMember) {
743 auto* object = MakeGarbageCollected<Object>();
744 HeapDeque<Member<Object>> deq1;
745 deq1.push_back(object);
746 {
747 ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
748 HeapDeque<Member<Object>> deq2(deq1);
749 }
750 }
751
TEST_F(IncrementalMarkingTest,HeapDequeMoveMember)752 TEST_F(IncrementalMarkingTest, HeapDequeMoveMember) {
753 auto* object = MakeGarbageCollected<Object>();
754 HeapDeque<Member<Object>> deq1;
755 deq1.push_back(object);
756 {
757 ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
758 HeapDeque<Member<Object>> deq2(std::move(deq1));
759 }
760 }
761
TEST_F(IncrementalMarkingTest,HeapDequeSwapMember)762 TEST_F(IncrementalMarkingTest, HeapDequeSwapMember) {
763 auto* obj1 = MakeGarbageCollected<Object>();
764 auto* obj2 = MakeGarbageCollected<Object>();
765 HeapDeque<Member<Object>> deq1;
766 deq1.push_back(obj1);
767 HeapDeque<Member<Object>> deq2;
768 deq2.push_back(obj2);
769 {
770 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
771 std::swap(deq1, deq2);
772 }
773 }
774
775 // =============================================================================
776 // HeapHashSet support. ========================================================
777 // =============================================================================
778
779 namespace {
780
781 template <typename Container>
Insert()782 void Insert() {
783 auto* obj = MakeGarbageCollected<Object>();
784 Container container;
785 {
786 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
787 container.insert(obj);
788 }
789 }
790
791 template <typename Container>
InsertNoBarrier()792 void InsertNoBarrier() {
793 auto* obj = MakeGarbageCollected<Object>();
794 Container container;
795 {
796 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
797 container.insert(obj);
798 }
799 }
800
801 template <typename Container>
Copy()802 void Copy() {
803 auto* obj = MakeGarbageCollected<Object>();
804 Container container1;
805 container1.insert(obj);
806 {
807 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
808 Container container2(container1);
809 EXPECT_TRUE(container1.Contains(obj));
810 EXPECT_TRUE(container2.Contains(obj));
811 }
812 }
813
814 template <typename Container>
CopyNoBarrier()815 void CopyNoBarrier() {
816 auto* obj = MakeGarbageCollected<Object>();
817 Container container1;
818 container1.insert(obj);
819 {
820 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
821 Container container2(container1);
822 EXPECT_TRUE(container1.Contains(obj));
823 EXPECT_TRUE(container2.Contains(obj));
824 }
825 }
826
827 template <typename Container>
Move()828 void Move() {
829 auto* obj = MakeGarbageCollected<Object>();
830 auto* container1 = MakeGarbageCollected<Container>();
831 auto* container2 = MakeGarbageCollected<Container>();
832 container1->insert(obj);
833 {
834 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
835 *container2 = std::move(*container1);
836 }
837 }
838
839 template <typename Container>
MoveNoBarrier()840 void MoveNoBarrier() {
841 auto* obj = MakeGarbageCollected<Object>();
842 auto* container1 = MakeGarbageCollected<Container>();
843 container1->insert(obj);
844 {
845 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
846 auto* container2 = MakeGarbageCollected<Container>(std::move(*container1));
847 }
848 }
849
850 template <typename Container>
Swap()851 void Swap() {
852 auto* obj1 = MakeGarbageCollected<Object>();
853 auto* obj2 = MakeGarbageCollected<Object>();
854 auto* container1 = MakeGarbageCollected<Container>();
855 container1->insert(obj1);
856 auto* container2 = MakeGarbageCollected<Container>();
857 container2->insert(obj2);
858 {
859 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
860 std::swap(*container1, *container2);
861 }
862 }
863
864 template <typename Container>
SwapNoBarrier()865 void SwapNoBarrier() {
866 auto* obj1 = MakeGarbageCollected<Object>();
867 auto* obj2 = MakeGarbageCollected<Object>();
868 auto* container1 = MakeGarbageCollected<Container>();
869 container1->insert(obj1);
870 auto* container2 = MakeGarbageCollected<Container>();
871 container2->insert(obj2);
872 {
873 ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
874 std::swap(*container1, *container2);
875 }
876 }
877
878 } // namespace
879
TEST_F(IncrementalMarkingTest,HeapHashSetInsert)880 TEST_F(IncrementalMarkingTest, HeapHashSetInsert) {
881 Insert<HeapHashSet<Member<Object>>>();
882 // Weak references are strongified for the current cycle.
883 Insert<HeapHashSet<WeakMember<Object>>>();
884 }
885
TEST_F(IncrementalMarkingTest,HeapHashSetCopy)886 TEST_F(IncrementalMarkingTest, HeapHashSetCopy) {
887 Copy<HeapHashSet<Member<Object>>>();
888 // Weak references are strongified for the current cycle.
889 Copy<HeapHashSet<WeakMember<Object>>>();
890 }
891
TEST_F(IncrementalMarkingTest,HeapHashSetMove)892 TEST_F(IncrementalMarkingTest, HeapHashSetMove) {
893 Move<HeapHashSet<Member<Object>>>();
894 // Weak references are strongified for the current cycle.
895 Move<HeapHashSet<WeakMember<Object>>>();
896 }
897
TEST_F(IncrementalMarkingTest,HeapHashSetSwap)898 TEST_F(IncrementalMarkingTest, HeapHashSetSwap) {
899 Swap<HeapHashSet<Member<Object>>>();
900 // Weak references are strongified for the current cycle.
901 Swap<HeapHashSet<WeakMember<Object>>>();
902 }
903
904 // =============================================================================
905 // HeapLinkedHashSet support. ==================================================
906 // =============================================================================
907
TEST_F(IncrementalMarkingTest,HeapLinkedHashSetInsert)908 TEST_F(IncrementalMarkingTest, HeapLinkedHashSetInsert) {
909 Insert<HeapLinkedHashSet<Member<Object>>>();
910 // Weak references are strongified for the current cycle.
911 Insert<HeapLinkedHashSet<WeakMember<Object>>>();
912 }
913
TEST_F(IncrementalMarkingTest,HeapLinkedHashSetCopy)914 TEST_F(IncrementalMarkingTest, HeapLinkedHashSetCopy) {
915 Copy<HeapLinkedHashSet<Member<Object>>>();
916 // Weak references are strongified for the current cycle.
917 Copy<HeapLinkedHashSet<WeakMember<Object>>>();
918 }
919
TEST_F(IncrementalMarkingTest,HeapLinkedHashSetMove)920 TEST_F(IncrementalMarkingTest, HeapLinkedHashSetMove) {
921 Move<HeapLinkedHashSet<Member<Object>>>();
922 // Weak references are strongified for the current cycle.
923 Move<HeapLinkedHashSet<WeakMember<Object>>>();
924 }
925
TEST_F(IncrementalMarkingTest,HeapLinkedHashSetSwap)926 TEST_F(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
927 Swap<HeapLinkedHashSet<Member<Object>>>();
928 // Weak references are strongified for the current cycle.
929 Swap<HeapLinkedHashSet<WeakMember<Object>>>();
930 }
931
932 // =============================================================================
933 // HeapHashCountedSet support. =================================================
934 // =============================================================================
935
936 // HeapHashCountedSet does not support copy or move.
937
TEST_F(IncrementalMarkingTest,HeapHashCountedSetInsert)938 TEST_F(IncrementalMarkingTest, HeapHashCountedSetInsert) {
939 Insert<HeapHashCountedSet<Member<Object>>>();
940 // Weak references are strongified for the current cycle.
941 Insert<HeapHashCountedSet<WeakMember<Object>>>();
942 }
943
TEST_F(IncrementalMarkingTest,HeapHashCountedSetSwap)944 TEST_F(IncrementalMarkingTest, HeapHashCountedSetSwap) {
945 // HeapHashCountedSet is not move constructible so we cannot use std::swap.
946 {
947 auto* obj1 = MakeGarbageCollected<Object>();
948 auto* obj2 = MakeGarbageCollected<Object>();
949 auto* container1 =
950 MakeGarbageCollected<HeapHashCountedSet<Member<Object>>>();
951 container1->insert(obj1);
952 auto* container2 =
953 MakeGarbageCollected<HeapHashCountedSet<Member<Object>>>();
954 container2->insert(obj2);
955 {
956 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
957 container1->swap(*container2);
958 }
959 }
960 {
961 auto* obj1 = MakeGarbageCollected<Object>();
962 auto* obj2 = MakeGarbageCollected<Object>();
963 auto* container1 =
964 MakeGarbageCollected<HeapHashCountedSet<WeakMember<Object>>>();
965 container1->insert(obj1);
966 auto* container2 =
967 MakeGarbageCollected<HeapHashCountedSet<WeakMember<Object>>>();
968 container2->insert(obj2);
969 {
970 // Weak references are strongified for the current cycle.
971 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
972 container1->swap(*container2);
973 }
974 }
975 }
976
977 // =============================================================================
978 // HeapHashMap support. ========================================================
979 // =============================================================================
980
TEST_F(IncrementalMarkingTest,HeapHashMapInsertMember)981 TEST_F(IncrementalMarkingTest, HeapHashMapInsertMember) {
982 auto* obj1 = MakeGarbageCollected<Object>();
983 auto* obj2 = MakeGarbageCollected<Object>();
984 HeapHashMap<Member<Object>, Member<Object>> map;
985 {
986 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
987 map.insert(obj1, obj2);
988 }
989 }
990
TEST_F(IncrementalMarkingTest,HeapHashMapInsertWeakMember)991 TEST_F(IncrementalMarkingTest, HeapHashMapInsertWeakMember) {
992 auto* obj1 = MakeGarbageCollected<Object>();
993 auto* obj2 = MakeGarbageCollected<Object>();
994 HeapHashMap<WeakMember<Object>, WeakMember<Object>> map;
995 {
996 // Weak references are strongified for the current cycle.
997 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
998 map.insert(obj1, obj2);
999 }
1000 }
1001
TEST_F(IncrementalMarkingTest,HeapHashMapInsertMemberWeakMember)1002 TEST_F(IncrementalMarkingTest, HeapHashMapInsertMemberWeakMember) {
1003 auto* obj1 = MakeGarbageCollected<Object>();
1004 auto* obj2 = MakeGarbageCollected<Object>();
1005 HeapHashMap<Member<Object>, WeakMember<Object>> map;
1006 {
1007 // Weak references are strongified for the current cycle.
1008 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1009 map.insert(obj1, obj2);
1010 }
1011 }
1012
TEST_F(IncrementalMarkingTest,HeapHashMapInsertWeakMemberMember)1013 TEST_F(IncrementalMarkingTest, HeapHashMapInsertWeakMemberMember) {
1014 auto* obj1 = MakeGarbageCollected<Object>();
1015 auto* obj2 = MakeGarbageCollected<Object>();
1016 HeapHashMap<WeakMember<Object>, Member<Object>> map;
1017 {
1018 // Weak references are strongified for the current cycle.
1019 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1020 map.insert(obj1, obj2);
1021 }
1022 }
1023
TEST_F(IncrementalMarkingTest,HeapHashMapSetMember)1024 TEST_F(IncrementalMarkingTest, HeapHashMapSetMember) {
1025 auto* obj1 = MakeGarbageCollected<Object>();
1026 auto* obj2 = MakeGarbageCollected<Object>();
1027 HeapHashMap<Member<Object>, Member<Object>> map;
1028 {
1029 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1030 map.Set(obj1, obj2);
1031 }
1032 }
1033
TEST_F(IncrementalMarkingTest,HeapHashMapSetMemberUpdateValue)1034 TEST_F(IncrementalMarkingTest, HeapHashMapSetMemberUpdateValue) {
1035 auto* obj1 = MakeGarbageCollected<Object>();
1036 auto* obj2 = MakeGarbageCollected<Object>();
1037 auto* obj3 = MakeGarbageCollected<Object>();
1038 HeapHashMap<Member<Object>, Member<Object>> map;
1039 map.insert(obj1, obj2);
1040 {
1041 // Only |obj3| is newly added to |map|, so we only expect the barrier to
1042 // fire on this one.
1043 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
1044 map.Set(obj1, obj3);
1045 EXPECT_FALSE(HeapObjectHeader::FromPayload(obj1)->IsMarked());
1046 EXPECT_FALSE(HeapObjectHeader::FromPayload(obj2)->IsMarked());
1047 }
1048 }
1049
TEST_F(IncrementalMarkingTest,HeapHashMapIteratorChangeKey)1050 TEST_F(IncrementalMarkingTest, HeapHashMapIteratorChangeKey) {
1051 auto* obj1 = MakeGarbageCollected<Object>();
1052 auto* obj2 = MakeGarbageCollected<Object>();
1053 auto* obj3 = MakeGarbageCollected<Object>();
1054 HeapHashMap<Member<Object>, Member<Object>> map;
1055 map.insert(obj1, obj2);
1056 {
1057 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
1058 auto it = map.find(obj1);
1059 EXPECT_NE(map.end(), it);
1060 it->key = obj3;
1061 }
1062 }
1063
TEST_F(IncrementalMarkingTest,HeapHashMapIteratorChangeValue)1064 TEST_F(IncrementalMarkingTest, HeapHashMapIteratorChangeValue) {
1065 auto* obj1 = MakeGarbageCollected<Object>();
1066 auto* obj2 = MakeGarbageCollected<Object>();
1067 auto* obj3 = MakeGarbageCollected<Object>();
1068 HeapHashMap<Member<Object>, Member<Object>> map;
1069 map.insert(obj1, obj2);
1070 {
1071 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
1072 auto it = map.find(obj1);
1073 EXPECT_NE(map.end(), it);
1074 it->value = obj3;
1075 }
1076 }
1077
TEST_F(IncrementalMarkingTest,HeapHashMapCopyMemberMember)1078 TEST_F(IncrementalMarkingTest, HeapHashMapCopyMemberMember) {
1079 auto* obj1 = MakeGarbageCollected<Object>();
1080 auto* obj2 = MakeGarbageCollected<Object>();
1081 HeapHashMap<Member<Object>, Member<Object>> map1;
1082 map1.insert(obj1, obj2);
1083 {
1084 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1085 EXPECT_TRUE(map1.Contains(obj1));
1086 HeapHashMap<Member<Object>, Member<Object>> map2(map1);
1087 EXPECT_TRUE(map1.Contains(obj1));
1088 EXPECT_TRUE(map2.Contains(obj1));
1089 }
1090 }
1091
TEST_F(IncrementalMarkingTest,HeapHashMapCopyWeakMemberWeakMember)1092 TEST_F(IncrementalMarkingTest, HeapHashMapCopyWeakMemberWeakMember) {
1093 auto* obj1 = MakeGarbageCollected<Object>();
1094 auto* obj2 = MakeGarbageCollected<Object>();
1095 HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
1096 map1.insert(obj1, obj2);
1097 {
1098 // Weak references are strongified for the current cycle.
1099 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1100 EXPECT_TRUE(map1.Contains(obj1));
1101 HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(map1);
1102 EXPECT_TRUE(map1.Contains(obj1));
1103 EXPECT_TRUE(map2.Contains(obj1));
1104 }
1105 }
1106
TEST_F(IncrementalMarkingTest,HeapHashMapCopyMemberWeakMember)1107 TEST_F(IncrementalMarkingTest, HeapHashMapCopyMemberWeakMember) {
1108 auto* obj1 = MakeGarbageCollected<Object>();
1109 auto* obj2 = MakeGarbageCollected<Object>();
1110 HeapHashMap<Member<Object>, WeakMember<Object>> map1;
1111 map1.insert(obj1, obj2);
1112 {
1113 // Weak references are strongified for the current cycle.
1114 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1115 EXPECT_TRUE(map1.Contains(obj1));
1116 HeapHashMap<Member<Object>, WeakMember<Object>> map2(map1);
1117 EXPECT_TRUE(map1.Contains(obj1));
1118 EXPECT_TRUE(map2.Contains(obj1));
1119 }
1120 }
1121
TEST_F(IncrementalMarkingTest,HeapHashMapCopyWeakMemberMember)1122 TEST_F(IncrementalMarkingTest, HeapHashMapCopyWeakMemberMember) {
1123 auto* obj1 = MakeGarbageCollected<Object>();
1124 auto* obj2 = MakeGarbageCollected<Object>();
1125 HeapHashMap<WeakMember<Object>, Member<Object>> map1;
1126 map1.insert(obj1, obj2);
1127 {
1128 // Weak references are strongified for the current cycle.
1129 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1130 EXPECT_TRUE(map1.Contains(obj1));
1131 HeapHashMap<WeakMember<Object>, Member<Object>> map2(map1);
1132 EXPECT_TRUE(map1.Contains(obj1));
1133 EXPECT_TRUE(map2.Contains(obj1));
1134 }
1135 }
1136
TEST_F(IncrementalMarkingTest,HeapHashMapMoveMember)1137 TEST_F(IncrementalMarkingTest, HeapHashMapMoveMember) {
1138 auto* obj1 = MakeGarbageCollected<Object>();
1139 auto* obj2 = MakeGarbageCollected<Object>();
1140 auto* map1 =
1141 MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>();
1142 map1->insert(obj1, obj2);
1143 {
1144 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1145 MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>(
1146 std::move(*map1));
1147 }
1148 }
1149
TEST_F(IncrementalMarkingTest,HeapHashMapMoveWeakMember)1150 TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMember) {
1151 auto* obj1 = MakeGarbageCollected<Object>();
1152 auto* obj2 = MakeGarbageCollected<Object>();
1153 auto* map1 = MakeGarbageCollected<
1154 HeapHashMap<WeakMember<Object>, WeakMember<Object>>>();
1155 map1->insert(obj1, obj2);
1156 {
1157 // Weak references are strongified for the current cycle.
1158 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1159 MakeGarbageCollected<HeapHashMap<WeakMember<Object>, WeakMember<Object>>>(
1160 std::move(*map1));
1161 }
1162 }
1163
TEST_F(IncrementalMarkingTest,HeapHashMapMoveMemberWeakMember)1164 TEST_F(IncrementalMarkingTest, HeapHashMapMoveMemberWeakMember) {
1165 auto* obj1 = MakeGarbageCollected<Object>();
1166 auto* obj2 = MakeGarbageCollected<Object>();
1167 auto* map1 =
1168 MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>();
1169 map1->insert(obj1, obj2);
1170 {
1171 // Weak references are strongified for the current cycle.
1172 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1173 MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>(
1174 std::move(*map1));
1175 }
1176 }
1177
TEST_F(IncrementalMarkingTest,HeapHashMapMoveWeakMemberMember)1178 TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMemberMember) {
1179 auto* obj1 = MakeGarbageCollected<Object>();
1180 auto* obj2 = MakeGarbageCollected<Object>();
1181 auto* map1 =
1182 MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>();
1183 map1->insert(obj1, obj2);
1184 {
1185 // Weak references are strongified for the current cycle.
1186 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
1187 MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>(
1188 std::move(*map1));
1189 }
1190 }
1191
TEST_F(IncrementalMarkingTest,HeapHashMapSwapMemberMember)1192 TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberMember) {
1193 auto* obj1 = MakeGarbageCollected<Object>();
1194 auto* obj2 = MakeGarbageCollected<Object>();
1195 auto* obj3 = MakeGarbageCollected<Object>();
1196 auto* obj4 = MakeGarbageCollected<Object>();
1197 auto* map1 =
1198 MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>();
1199 map1->insert(obj1, obj2);
1200 auto* map2 =
1201 MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>();
1202 map2->insert(obj3, obj4);
1203 {
1204 ExpectWriteBarrierFires scope(ThreadState::Current(),
1205 {obj1, obj2, obj3, obj4});
1206 std::swap(*map1, *map2);
1207 }
1208 }
1209
TEST_F(IncrementalMarkingTest,HeapHashMapSwapWeakMemberWeakMember)1210 TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberWeakMember) {
1211 auto* obj1 = MakeGarbageCollected<Object>();
1212 auto* obj2 = MakeGarbageCollected<Object>();
1213 auto* obj3 = MakeGarbageCollected<Object>();
1214 auto* obj4 = MakeGarbageCollected<Object>();
1215 auto* map1 = MakeGarbageCollected<
1216 HeapHashMap<WeakMember<Object>, WeakMember<Object>>>();
1217 map1->insert(obj1, obj2);
1218 auto* map2 = MakeGarbageCollected<
1219 HeapHashMap<WeakMember<Object>, WeakMember<Object>>>();
1220 map2->insert(obj3, obj4);
1221 {
1222 // Weak references are strongified for the current cycle.
1223 ExpectWriteBarrierFires scope(ThreadState::Current(),
1224 {obj1, obj2, obj3, obj4});
1225 std::swap(*map1, *map2);
1226 }
1227 }
1228
TEST_F(IncrementalMarkingTest,HeapHashMapSwapMemberWeakMember)1229 TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberWeakMember) {
1230 auto* obj1 = MakeGarbageCollected<Object>();
1231 auto* obj2 = MakeGarbageCollected<Object>();
1232 auto* obj3 = MakeGarbageCollected<Object>();
1233 auto* obj4 = MakeGarbageCollected<Object>();
1234 auto* map1 =
1235 MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>();
1236 map1->insert(obj1, obj2);
1237 auto* map2 =
1238 MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>();
1239 map2->insert(obj3, obj4);
1240 {
1241 // Weak references are strongified for the current cycle.
1242 ExpectWriteBarrierFires scope(ThreadState::Current(),
1243 {obj1, obj2, obj3, obj4});
1244 std::swap(*map1, *map2);
1245 }
1246 }
1247
TEST_F(IncrementalMarkingTest,HeapHashMapSwapWeakMemberMember)1248 TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberMember) {
1249 auto* obj1 = MakeGarbageCollected<Object>();
1250 auto* obj2 = MakeGarbageCollected<Object>();
1251 auto* obj3 = MakeGarbageCollected<Object>();
1252 auto* obj4 = MakeGarbageCollected<Object>();
1253 auto* map1 =
1254 MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>();
1255 map1->insert(obj1, obj2);
1256 auto* map2 =
1257 MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>();
1258 map2->insert(obj3, obj4);
1259 {
1260 // Weak references are strongified for the current cycle.
1261 ExpectWriteBarrierFires scope(ThreadState::Current(),
1262 {obj1, obj2, obj3, obj4});
1263 std::swap(*map1, *map2);
1264 }
1265 }
1266
TEST_F(IncrementalMarkingTest,HeapHashMapCopyKeysToVectorMember)1267 TEST_F(IncrementalMarkingTest, HeapHashMapCopyKeysToVectorMember) {
1268 auto* obj1 = MakeGarbageCollected<Object>();
1269 auto* obj2 = MakeGarbageCollected<Object>();
1270 HeapHashMap<Member<Object>, Member<Object>> map;
1271 map.insert(obj1, obj2);
1272 HeapVector<Member<Object>> vec;
1273 {
1274 // Only key should have its write barrier fired. A write barrier call for
1275 // value hints to an inefficient implementation.
1276 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1});
1277 CopyKeysToVector(map, vec);
1278 }
1279 }
1280
TEST_F(IncrementalMarkingTest,HeapHashMapCopyValuesToVectorMember)1281 TEST_F(IncrementalMarkingTest, HeapHashMapCopyValuesToVectorMember) {
1282 auto* obj1 = MakeGarbageCollected<Object>();
1283 auto* obj2 = MakeGarbageCollected<Object>();
1284 HeapHashMap<Member<Object>, Member<Object>> map;
1285 map.insert(obj1, obj2);
1286 HeapVector<Member<Object>> vec;
1287 {
1288 // Only value should have its write barrier fired. A write barrier call for
1289 // key hints to an inefficient implementation.
1290 ExpectWriteBarrierFires scope(ThreadState::Current(), {obj2});
1291 CopyValuesToVector(map, vec);
1292 }
1293 }
1294
1295 // TODO(keishi) Non-weak hash table backings should be promptly freed but they
1296 // are currently not because we emit write barriers for the backings, and we
1297 // don't free marked backings.
TEST_F(IncrementalMarkingTest,DISABLED_WeakHashMapPromptlyFreeDisabled)1298 TEST_F(IncrementalMarkingTest, DISABLED_WeakHashMapPromptlyFreeDisabled) {
1299 ThreadState* state = ThreadState::Current();
1300 state->SetGCState(ThreadState::kIncrementalMarkingStepScheduled);
1301 Persistent<Object> obj1 = MakeGarbageCollected<Object>();
1302 NormalPageArena* arena = static_cast<NormalPageArena*>(
1303 ThreadState::Current()->Heap().Arena(BlinkGC::kHashTableArenaIndex));
1304 CHECK(arena);
1305 {
1306 size_t before = arena->promptly_freed_size();
1307 // Create two maps so we don't promptly free at the allocation point.
1308 HeapHashMap<WeakMember<Object>, Member<Object>> weak_map1;
1309 HeapHashMap<WeakMember<Object>, Member<Object>> weak_map2;
1310 weak_map1.insert(obj1, obj1);
1311 weak_map2.insert(obj1, obj1);
1312 weak_map1.clear();
1313 size_t after = arena->promptly_freed_size();
1314 // Weak hash table backings should not be promptly freed.
1315 EXPECT_EQ(after, before);
1316 }
1317 {
1318 size_t before = arena->promptly_freed_size();
1319 // Create two maps so we don't promptly free at the allocation point.
1320 HeapHashMap<Member<Object>, Member<Object>> map1;
1321 HeapHashMap<Member<Object>, Member<Object>> map2;
1322 map1.insert(obj1, obj1);
1323 map2.insert(obj1, obj1);
1324 map1.clear();
1325 size_t after = arena->promptly_freed_size();
1326 // Non-weak hash table backings should be promptly freed.
1327 EXPECT_GT(after, before);
1328 }
1329 state->SetGCState(ThreadState::kIncrementalMarkingFinalizeScheduled);
1330 state->SetGCState(ThreadState::kNoGCScheduled);
1331 }
1332
1333 namespace {
1334
1335 class RegisteringMixin;
1336 using ObjectRegistry = HeapHashMap<void*, Member<RegisteringMixin>>;
1337
1338 class RegisteringMixin : public GarbageCollectedMixin {
1339 public:
RegisteringMixin(ObjectRegistry * registry)1340 explicit RegisteringMixin(ObjectRegistry* registry) {
1341 HeapObjectHeader* header = HeapObjectHeader::FromPayload(
1342 TraceTrait<RegisteringMixin>::GetTraceDescriptor(this)
1343 .base_object_payload);
1344 EXPECT_TRUE(header->IsInConstruction());
1345 registry->insert(reinterpret_cast<void*>(this), this);
1346 }
1347 };
1348
1349 class RegisteringObject : public GarbageCollected<RegisteringObject>,
1350 public RegisteringMixin {
1351 public:
RegisteringObject(ObjectRegistry * registry)1352 explicit RegisteringObject(ObjectRegistry* registry)
1353 : RegisteringMixin(registry) {}
1354 };
1355
1356 } // namespace
1357
TEST_F(IncrementalMarkingTest,WriteBarrierDuringMixinConstruction)1358 TEST_F(IncrementalMarkingTest, WriteBarrierDuringMixinConstruction) {
1359 IncrementalMarkingScope scope(ThreadState::Current());
1360 ObjectRegistry registry;
1361 RegisteringObject* object =
1362 MakeGarbageCollected<RegisteringObject>(®istry);
1363
1364 // Clear any objects that have been added to the regular marking worklist in
1365 // the process of calling the constructor.
1366 MarkingItem marking_item;
1367 while (scope.marking_worklist()->Pop(WorklistTaskId::MutatorThread,
1368 &marking_item)) {
1369 HeapObjectHeader* header =
1370 HeapObjectHeader::FromPayload(marking_item.base_object_payload);
1371 if (header->IsMarked())
1372 header->Unmark();
1373 }
1374 EXPECT_TRUE(scope.marking_worklist()->IsGlobalEmpty());
1375 // Clear any write barriers so far.
1376 HeapObjectHeader* header;
1377 while (scope.write_barrier_worklist()->Pop(WorklistTaskId::MutatorThread,
1378 &header)) {
1379 if (header->IsMarked())
1380 header->Unmark();
1381 }
1382 EXPECT_TRUE(scope.write_barrier_worklist()->IsGlobalEmpty());
1383
1384 EXPECT_FALSE(scope.not_fully_constructed_worklist()->IsGlobalEmpty());
1385 NotFullyConstructedItem partial_item;
1386 bool found_mixin_object = false;
1387 // The same object may be on the marking work list because of expanding
1388 // and rehashing of the backing store in the registry.
1389 while (scope.not_fully_constructed_worklist()->Pop(
1390 WorklistTaskId::MutatorThread, &partial_item)) {
1391 if (object == partial_item)
1392 found_mixin_object = true;
1393 HeapObjectHeader* header = HeapObjectHeader::FromPayload(partial_item);
1394 if (header->IsMarked())
1395 header->Unmark();
1396 }
1397 EXPECT_TRUE(found_mixin_object);
1398 EXPECT_TRUE(scope.not_fully_constructed_worklist()->IsGlobalEmpty());
1399 }
1400
TEST_F(IncrementalMarkingTest,OverrideAfterMixinConstruction)1401 TEST_F(IncrementalMarkingTest, OverrideAfterMixinConstruction) {
1402 ObjectRegistry registry;
1403 RegisteringMixin* mixin = MakeGarbageCollected<RegisteringObject>(®istry);
1404 HeapObjectHeader* header = HeapObjectHeader::FromPayload(
1405 TraceTrait<RegisteringMixin>::GetTraceDescriptor(mixin)
1406 .base_object_payload);
1407
1408 EXPECT_FALSE(header->IsInConstruction());
1409 }
1410
1411 // =============================================================================
1412 // Tests that execute complete incremental garbage collections. ================
1413 // =============================================================================
1414
TEST_F(IncrementalMarkingTest,TestDriver)1415 TEST_F(IncrementalMarkingTest, TestDriver) {
1416 IncrementalMarkingTestDriver driver(ThreadState::Current());
1417 driver.Start();
1418 EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
1419 driver.SingleStep();
1420 EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
1421 driver.FinishGC();
1422 EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
1423 }
1424
TEST_F(IncrementalMarkingTest,DropBackingStore)1425 TEST_F(IncrementalMarkingTest, DropBackingStore) {
1426 // Regression test: https://crbug.com/828537
1427 using WeakStore = HeapHashCountedSet<WeakMember<Object>>;
1428
1429 Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>());
1430 persistent->insert(MakeGarbageCollected<Object>());
1431 IncrementalMarkingTestDriver driver(ThreadState::Current());
1432 driver.Start();
1433 driver.FinishSteps();
1434 persistent->clear();
1435 // Marking verifier should not crash on a black backing store with all
1436 // black->white edges.
1437 driver.FinishGC();
1438 }
1439
TEST_F(IncrementalMarkingTest,NoBackingFreeDuringIncrementalMarking)1440 TEST_F(IncrementalMarkingTest, NoBackingFreeDuringIncrementalMarking) {
1441 // Regression test: https://crbug.com/870306
1442 // Only reproduces in ASAN configurations.
1443 using WeakStore = HeapHashCountedSet<WeakMember<Object>>;
1444
1445 Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>());
1446 // Prefill the collection to grow backing store. A new backing store
1447 // allocationwould trigger the write barrier, mitigating the bug where
1448 // a backing store is promptly freed.
1449 for (size_t i = 0; i < 8; i++) {
1450 persistent->insert(MakeGarbageCollected<Object>());
1451 }
1452 IncrementalMarkingTestDriver driver(ThreadState::Current());
1453 driver.Start();
1454 persistent->insert(MakeGarbageCollected<Object>());
1455 // Is not allowed to free the backing store as the previous insert may have
1456 // registered a slot.
1457 persistent->clear();
1458 driver.FinishSteps();
1459 driver.FinishGC();
1460 }
1461
TEST_F(IncrementalMarkingTest,DropReferenceWithHeapCompaction)1462 TEST_F(IncrementalMarkingTest, DropReferenceWithHeapCompaction) {
1463 using Store = HeapHashCountedSet<Member<Object>>;
1464
1465 Persistent<Store> persistent(MakeGarbageCollected<Store>());
1466 persistent->insert(MakeGarbageCollected<Object>());
1467 IncrementalMarkingTestDriver driver(ThreadState::Current());
1468 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1469 driver.Start();
1470 driver.FinishSteps();
1471 persistent->clear();
1472 // Registration of movable and updatable references should not crash because
1473 // if a slot have nullptr reference, it doesn't call registeration method.
1474 driver.FinishGC();
1475 }
1476
TEST_F(IncrementalMarkingTest,HasInlineCapacityCollectionWithHeapCompaction)1477 TEST_F(IncrementalMarkingTest, HasInlineCapacityCollectionWithHeapCompaction) {
1478 using Store = HeapVector<Member<Object>, 2>;
1479
1480 Persistent<Store> persistent(MakeGarbageCollected<Store>());
1481 Persistent<Store> persistent2(MakeGarbageCollected<Store>());
1482
1483 IncrementalMarkingTestDriver driver(ThreadState::Current());
1484 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1485 persistent->push_back(MakeGarbageCollected<Object>());
1486 driver.Start();
1487 driver.FinishGC();
1488
1489 // Should collect also slots that has only inline buffer and nullptr
1490 // references.
1491 #if defined(ANNOTATE_CONTIGUOUS_CONTAINER)
1492 // When ANNOTATE_CONTIGUOUS_CONTAINER is defined, inline capacity is ignored.
1493 EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 1u);
1494 #else
1495 EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 2u);
1496 #endif
1497 }
1498
TEST_F(IncrementalMarkingTest,WeakHashMapHeapCompaction)1499 TEST_F(IncrementalMarkingTest, WeakHashMapHeapCompaction) {
1500 using Store = HeapHashCountedSet<WeakMember<Object>>;
1501
1502 Persistent<Store> persistent(MakeGarbageCollected<Store>());
1503
1504 IncrementalMarkingTestDriver driver(ThreadState::Current());
1505 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1506 driver.Start();
1507 driver.FinishSteps();
1508 persistent->insert(MakeGarbageCollected<Object>());
1509 driver.FinishGC();
1510
1511 // Weak callback should register the slot.
1512 EXPECT_EQ(1u, driver.GetHeapCompactLastFixupCount());
1513 }
1514
TEST_F(IncrementalMarkingTest,ConservativeGCWhileCompactionScheduled)1515 TEST_F(IncrementalMarkingTest, ConservativeGCWhileCompactionScheduled) {
1516 using Store = HeapVector<Member<Object>>;
1517 Persistent<Store> persistent(MakeGarbageCollected<Store>());
1518 persistent->push_back(MakeGarbageCollected<Object>());
1519
1520 IncrementalMarkingTestDriver driver(ThreadState::Current());
1521 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1522 driver.Start();
1523 driver.FinishSteps();
1524 ThreadState::Current()->CollectGarbageForTesting(
1525 BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack,
1526 BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping,
1527 BlinkGC::GCReason::kForcedGCForTesting);
1528
1529 // Heap compaction should be canceled if incremental marking finishes with a
1530 // conservative GC.
1531 EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 0u);
1532 }
1533
1534 namespace {
1535
1536 class ObjectWithWeakMember : public GarbageCollected<ObjectWithWeakMember> {
1537 public:
1538 ObjectWithWeakMember() = default;
1539
set_object(Object * object)1540 void set_object(Object* object) { object_ = object; }
1541
Trace(Visitor * visitor) const1542 void Trace(Visitor* visitor) const { visitor->Trace(object_); }
1543
1544 private:
1545 WeakMember<Object> object_ = nullptr;
1546 };
1547
1548 } // namespace
1549
TEST_F(IncrementalMarkingTest,WeakMember)1550 TEST_F(IncrementalMarkingTest, WeakMember) {
1551 // Regression test: https://crbug.com/913431
1552
1553 Persistent<ObjectWithWeakMember> persistent(
1554 MakeGarbageCollected<ObjectWithWeakMember>());
1555 IncrementalMarkingTestDriver driver(ThreadState::Current());
1556 driver.Start();
1557 driver.FinishSteps();
1558 persistent->set_object(MakeGarbageCollected<Object>());
1559 driver.FinishGC();
1560 ConservativelyCollectGarbage();
1561 }
1562
TEST_F(IncrementalMarkingTest,MemberSwap)1563 TEST_F(IncrementalMarkingTest, MemberSwap) {
1564 // Regression test: https://crbug.com/913431
1565 //
1566 // MemberBase::Swap may be used to swap in a not-yet-processed member into an
1567 // already-processed member. This leads to a stale pointer that is not marked.
1568
1569 Persistent<Object> object1(MakeGarbageCollected<Object>());
1570 IncrementalMarkingTestDriver driver(ThreadState::Current());
1571 driver.Start();
1572 // The repro leverages the fact that initializing stores do not emit a barrier
1573 // (because they are still reachable from stack) to simulate the problematic
1574 // interleaving.
1575 driver.FinishSteps();
1576 Object* object2 =
1577 MakeGarbageCollected<Object>(MakeGarbageCollected<Object>());
1578 object2->next_ref().Swap(object1->next_ref());
1579 driver.FinishGC();
1580 ConservativelyCollectGarbage();
1581 }
1582
1583 namespace {
1584
1585 template <typename T>
1586 class ObjectHolder : public GarbageCollected<ObjectHolder<T>> {
1587 public:
1588 ObjectHolder() = default;
1589
Trace(Visitor * visitor) const1590 virtual void Trace(Visitor* visitor) const { visitor->Trace(holder_); }
1591
set_value(T * value)1592 void set_value(T* value) { holder_ = value; }
value() const1593 T* value() const { return holder_.Get(); }
1594
1595 private:
1596 Member<T> holder_;
1597 };
1598
1599 } // namespace
1600
TEST_F(IncrementalMarkingTest,StepDuringObjectConstruction)1601 TEST_F(IncrementalMarkingTest, StepDuringObjectConstruction) {
1602 // Test ensures that objects in construction are delayed for processing to
1603 // allow omitting write barriers on initializing stores.
1604
1605 using O = ObjectWithCallbackBeforeInitializer<Object>;
1606 using Holder = ObjectHolder<O>;
1607 Persistent<Holder> holder(MakeGarbageCollected<Holder>());
1608 IncrementalMarkingTestDriver driver(ThreadState::Current());
1609 driver.Start();
1610 MakeGarbageCollected<O>(
1611 base::BindOnce(
1612 [](IncrementalMarkingTestDriver* driver, Holder* holder, O* thiz) {
1613 // Publish not-fully-constructed object |thiz| by triggering write
1614 // barrier for the object.
1615 holder->set_value(thiz);
1616 // Finish call incremental steps.
1617 driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
1618 },
1619 &driver, holder.Get()),
1620 MakeGarbageCollected<Object>());
1621 driver.FinishGC();
1622 PreciselyCollectGarbage();
1623 }
1624
TEST_F(IncrementalMarkingTest,StepDuringMixinObjectConstruction)1625 TEST_F(IncrementalMarkingTest, StepDuringMixinObjectConstruction) {
1626 // Test ensures that mixin objects in construction are delayed for processing
1627 // to allow omitting write barriers on initializing stores.
1628
1629 using Parent = ObjectWithMixinWithCallbackBeforeInitializer<Object>;
1630 using Mixin = MixinWithCallbackBeforeInitializer<Object>;
1631 using Holder = ObjectHolder<Mixin>;
1632 Persistent<Holder> holder(MakeGarbageCollected<Holder>());
1633 IncrementalMarkingTestDriver driver(ThreadState::Current());
1634 driver.Start();
1635 MakeGarbageCollected<Parent>(
1636 base::BindOnce(
1637 [](IncrementalMarkingTestDriver* driver, Holder* holder,
1638 Mixin* thiz) {
1639 // Publish not-fully-constructed object
1640 // |thiz| by triggering write barrier for
1641 // the object.
1642 holder->set_value(thiz);
1643 // Finish call incremental steps.
1644 driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
1645 },
1646 &driver, holder.Get()),
1647 MakeGarbageCollected<Object>());
1648 driver.FinishGC();
1649 PreciselyCollectGarbage();
1650 }
1651
TEST_F(IncrementalMarkingTest,IncrementalMarkingShrinkingBackingCompaction)1652 TEST_F(IncrementalMarkingTest, IncrementalMarkingShrinkingBackingCompaction) {
1653 // Regression test: https://crbug.com/918064
1654
1655 using Nested = HeapVector<HeapVector<Member<Object>>>;
1656 // The following setup will ensure that the outer HeapVector's backing store
1657 // contains slots to other to-be-compacted backings.
1658 Persistent<Nested> holder(MakeGarbageCollected<Nested>());
1659 for (int i = 0; i < 32; i++) {
1660 holder->emplace_back();
1661 holder->at(i).emplace_back(MakeGarbageCollected<Object>());
1662 }
1663 IncrementalMarkingTestDriver driver(ThreadState::Current());
1664 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1665 driver.Start();
1666 driver.FinishSteps();
1667 // Reduce size of the outer backing store.
1668 for (int i = 0; i < 16; i++) {
1669 holder->pop_back();
1670 }
1671 // Ensure that shrinking the backing does not crash in compaction as there may
1672 // be registered slots left in the area that is already freed.
1673 holder->ShrinkToFit();
1674 driver.FinishGC();
1675 }
1676
TEST_F(IncrementalMarkingTest,InPayloadWriteBarrierRegistersInvalidSlotForCompaction)1677 TEST_F(IncrementalMarkingTest,
1678 InPayloadWriteBarrierRegistersInvalidSlotForCompaction) {
1679 // Regression test: https://crbug.com/918064
1680
1681 using Nested = HeapVector<HeapVector<Member<Object>>>;
1682 IncrementalMarkingTestDriver driver(ThreadState::Current());
1683 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1684 // Allocate a vector and reserve a buffer to avoid triggering the write
1685 // barrier during incremental marking.
1686 Nested* nested = MakeGarbageCollected<Nested>();
1687 nested->ReserveCapacity(32);
1688 driver.Start();
1689 // Initialize the inner vector, triggering tracing and slots registration.
1690 // This could be an object using DISALLOW_NEW() but HeapVector is easier to
1691 // test.
1692 nested->emplace_back(1);
1693 // Use the inner vector as otherwise the slot would not be registered due to
1694 // not having a backing store itself.
1695 nested->at(0).emplace_back(MakeGarbageCollected<Object>());
1696 driver.FinishSteps();
1697 // GCs here are without stack. This is just to show that we don't want this
1698 // object marked.
1699 CHECK(!HeapObjectHeader::FromPayload(nested)
1700 ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>());
1701 nested = nullptr;
1702 driver.FinishGC();
1703 }
1704
TEST_F(IncrementalMarkingTest,AdjustMarkedBytesOnMarkedBackingStore)1705 TEST_F(IncrementalMarkingTest, AdjustMarkedBytesOnMarkedBackingStore) {
1706 // Regression test: https://crbug.com/966456
1707 //
1708 // Test ensures that backing expansion does not crash in trying to adjust
1709 // marked bytes when the page is actually about to be swept and marking is not
1710 // in progress.
1711
1712 // Disable concurrent sweeping to check that sweeping is not in progress after
1713 // the FinishGC call.
1714 base::test::ScopedFeatureList scoped_feature_list;
1715 scoped_feature_list.InitAndDisableFeature(
1716 blink::features::kBlinkHeapConcurrentSweeping);
1717 using Container = HeapVector<Member<Object>>;
1718 Persistent<Container> holder(MakeGarbageCollected<Container>());
1719 holder->push_back(MakeGarbageCollected<Object>());
1720 holder->Grow(16);
1721 ThreadState::Current()->Heap().ResetAllocationPointForTesting();
1722 // Slowly shrink down the backing, only adjusting capacity without performing
1723 // free as the resulting memory block is too small for a free list entry.
1724 for (int i = 15; i > 0; i--) {
1725 holder->Shrink(i);
1726 holder->ShrinkToFit();
1727 }
1728 IncrementalMarkingTestDriver driver(ThreadState::Current());
1729 driver.Start();
1730 driver.FinishSteps();
1731 // The object is marked at this point.
1732 CHECK(HeapObjectHeader::FromPayload(holder.Get())
1733 ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>());
1734 driver.FinishGC(false);
1735 // The object is still marked as sweeping did not make any progress.
1736 CHECK(HeapObjectHeader::FromPayload(holder.Get())->IsMarked());
1737 // Re-grow to some size within the initial payload size (capacity=16).
1738 holder->Grow(8);
1739 }
1740
TEST_F(IncrementalMarkingTest,HeapCompactWithStaleSlotInNestedContainer)1741 TEST_F(IncrementalMarkingTest, HeapCompactWithStaleSlotInNestedContainer) {
1742 // Regression test: https://crbug.com/980962
1743 //
1744 // Test ensures that interior pointers are updated even if the backing store
1745 // itself is not referenced anymore. Consider the case where a |B| is
1746 // references a value |V| through slot |B.x|. Even if |B| is not referred to
1747 // from an actual object any more, the slot |B.x| needs to be in valid state
1748 // when |V| is moved.
1749
1750 using Nested = HeapVector<HeapVector<Member<Object>>>;
1751
1752 // Allocate dummy storage so that other vector backings are actually moved.
1753 MakeGarbageCollected<HeapVector<Member<Object>>>()->push_back(
1754 MakeGarbageCollected<Object>());
1755
1756 IncrementalMarkingTestDriver driver(ThreadState::Current());
1757 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1758 driver.Start();
1759 Nested* outer = MakeGarbageCollected<Nested>();
1760 outer->push_back(HeapVector<Member<Object>>());
1761 outer->at(0).push_back(MakeGarbageCollected<Object>());
1762 // The outer HeapVector object is not marked, which leaves the backing store
1763 // as marked with a valid slot inside. Now, if the outer backing store moves
1764 // first and its page is freed, then referring to the slot when the inner
1765 // backing store is moved may crash.
1766 outer = nullptr;
1767 driver.FinishSteps();
1768 driver.FinishGC();
1769 }
1770
1771 class Destructed final : public GarbageCollected<Destructed> {
1772 public:
~Destructed()1773 ~Destructed() { n_destructed++; }
1774
Trace(Visitor *) const1775 void Trace(Visitor*) const {}
1776
1777 static size_t n_destructed;
1778 };
1779
1780 size_t Destructed::n_destructed = 0;
1781
1782 class LinkedHashSetWrapper final
1783 : public GarbageCollected<LinkedHashSetWrapper> {
1784 public:
1785 using HashType = HeapLinkedHashSet<Member<Destructed>>;
1786
LinkedHashSetWrapper()1787 LinkedHashSetWrapper() {
1788 for (size_t i = 0; i < 10; ++i) {
1789 hash_set_.insert(MakeGarbageCollected<Destructed>());
1790 }
1791 }
1792
Trace(Visitor * v) const1793 void Trace(Visitor* v) const { v->Trace(hash_set_); }
1794
Swap()1795 void Swap() {
1796 HashType hash_set;
1797 hash_set_.Swap(hash_set);
1798 }
1799
1800 HashType hash_set_;
1801 };
1802
TEST_F(IncrementalMarkingTest,LinkedHashSetMovingCallback)1803 TEST_F(IncrementalMarkingTest, LinkedHashSetMovingCallback) {
1804 ClearOutOldGarbage();
1805
1806 Destructed::n_destructed = 0;
1807 {
1808 HeapHashSet<Member<Destructed>> to_be_destroyed;
1809 to_be_destroyed.ReserveCapacityForSize(100);
1810 }
1811 Persistent<LinkedHashSetWrapper> wrapper =
1812 MakeGarbageCollected<LinkedHashSetWrapper>();
1813
1814 IncrementalMarkingTestDriver driver(ThreadState::Current());
1815 ThreadState::Current()->EnableCompactionForNextGCForTesting();
1816 driver.Start();
1817 driver.FinishSteps();
1818
1819 // Destroy the link between original HeapLinkedHashSet object and its backing
1820 // store.
1821 wrapper->Swap();
1822 DCHECK(wrapper->hash_set_.IsEmpty());
1823
1824 PreciselyCollectGarbage();
1825
1826 EXPECT_EQ(10u, Destructed::n_destructed);
1827 }
1828
1829 class DestructedAndTraced final : public GarbageCollected<DestructedAndTraced> {
1830 public:
~DestructedAndTraced()1831 ~DestructedAndTraced() { n_destructed++; }
1832
Trace(Visitor *) const1833 void Trace(Visitor*) const { n_traced++; }
1834
1835 static size_t n_destructed;
1836 static size_t n_traced;
1837 };
1838
1839 size_t DestructedAndTraced::n_destructed = 0;
1840 size_t DestructedAndTraced::n_traced = 0;
1841
TEST_F(IncrementalMarkingTest,ConservativeGCOfWeakContainer)1842 TEST_F(IncrementalMarkingTest, ConservativeGCOfWeakContainer) {
1843 // Regression test: https://crbug.com/1108676
1844 //
1845 // Test ensures that on-stack references to weak containers (e.g. iterators)
1846 // force re-tracing of the entire container. Otherwise, if the container was
1847 // previously traced and is not re-traced, some bucket might be deleted which
1848 // will make existing iterators invalid.
1849
1850 using WeakContainer = HeapHashMap<WeakMember<DestructedAndTraced>, size_t>;
1851 Persistent<WeakContainer> map = MakeGarbageCollected<WeakContainer>();
1852 static constexpr size_t kNumObjects = 10u;
1853 for (size_t i = 0; i < kNumObjects; ++i) {
1854 map->insert(MakeGarbageCollected<DestructedAndTraced>(), i);
1855 }
1856 DestructedAndTraced::n_destructed = 0;
1857
1858 for (auto it = map->begin(); it != map->end(); ++it) {
1859 size_t value = it->value;
1860 DestructedAndTraced::n_traced = 0;
1861 IncrementalMarkingTestDriver driver(ThreadState::Current());
1862 driver.Start();
1863 driver.FinishSteps();
1864 // map should now be marked, but has not been traced since it's weak.
1865 EXPECT_EQ(0u, DestructedAndTraced::n_traced);
1866 ConservativelyCollectGarbage();
1867 // map buckets were traced (at least once).
1868 EXPECT_NE(kNumObjects, DestructedAndTraced::n_traced);
1869 // Check that iterator is still valid.
1870 EXPECT_EQ(value, it->value);
1871 }
1872
1873 // All buckets were kept alive.
1874 EXPECT_EQ(0u, DestructedAndTraced::n_destructed);
1875 }
1876
1877 } // namespace incremental_marking_test
1878 } // namespace blink
1879