1 // Copyright 2020 the V8 project 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 "include/cppgc/member.h"
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "include/cppgc/allocation.h"
11 #include "include/cppgc/garbage-collected.h"
12 #include "include/cppgc/persistent.h"
13 #include "include/cppgc/sentinel-pointer.h"
14 #include "include/cppgc/type-traits.h"
15 #include "test/unittests/heap/cppgc/tests.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace cppgc {
19 namespace internal {
20 
21 namespace {
22 
23 struct GCed : GarbageCollected<GCed> {
Tracecppgc::internal::__anonc9d65bca0111::GCed24   virtual void Trace(cppgc::Visitor*) const {}
25 };
26 struct DerivedGCed : GCed {
Tracecppgc::internal::__anonc9d65bca0111::DerivedGCed27   void Trace(cppgc::Visitor* v) const override { GCed::Trace(v); }
28 };
29 
30 // Compile tests.
31 static_assert(!IsWeakV<Member<GCed>>, "Member is always strong.");
32 static_assert(IsWeakV<WeakMember<GCed>>, "WeakMember is always weak.");
33 
34 static_assert(IsMemberTypeV<Member<GCed>>, "Member must be Member.");
35 static_assert(!IsMemberTypeV<WeakMember<GCed>>,
36               "WeakMember must not be Member.");
37 static_assert(!IsMemberTypeV<UntracedMember<GCed>>,
38               "UntracedMember must not be Member.");
39 static_assert(!IsMemberTypeV<int>, "int must not be Member.");
40 static_assert(!IsWeakMemberTypeV<Member<GCed>>,
41               "Member must not be WeakMember.");
42 static_assert(IsWeakMemberTypeV<WeakMember<GCed>>,
43               "WeakMember must be WeakMember.");
44 static_assert(!IsWeakMemberTypeV<UntracedMember<GCed>>,
45               "UntracedMember must not be WeakMember.");
46 static_assert(!IsWeakMemberTypeV<int>, "int must not be WeakMember.");
47 static_assert(!IsUntracedMemberTypeV<Member<GCed>>,
48               "Member must not be UntracedMember.");
49 static_assert(!IsUntracedMemberTypeV<WeakMember<GCed>>,
50               "WeakMember must not be UntracedMember.");
51 static_assert(IsUntracedMemberTypeV<UntracedMember<GCed>>,
52               "UntracedMember must be UntracedMember.");
53 static_assert(!IsUntracedMemberTypeV<int>, "int must not be UntracedMember.");
54 
55 struct CustomWriteBarrierPolicy {
56   static size_t InitializingWriteBarriersTriggered;
57   static size_t AssigningWriteBarriersTriggered;
InitializingBarriercppgc::internal::__anonc9d65bca0111::CustomWriteBarrierPolicy58   static void InitializingBarrier(const void* slot, const void* value) {
59     ++InitializingWriteBarriersTriggered;
60   }
AssigningBarriercppgc::internal::__anonc9d65bca0111::CustomWriteBarrierPolicy61   static void AssigningBarrier(const void* slot, const void* value) {
62     ++AssigningWriteBarriersTriggered;
63   }
64 };
65 size_t CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0;
66 size_t CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0;
67 
68 using MemberWithCustomBarrier =
69     BasicMember<GCed, StrongMemberTag, CustomWriteBarrierPolicy>;
70 
71 struct CustomCheckingPolicy {
72   static std::vector<GCed*> Cached;
73   static size_t ChecksTriggered;
CheckPointercppgc::internal::__anonc9d65bca0111::CustomCheckingPolicy74   void CheckPointer(const void* ptr) {
75     EXPECT_NE(Cached.cend(), std::find(Cached.cbegin(), Cached.cend(), ptr));
76     ++ChecksTriggered;
77   }
78 };
79 std::vector<GCed*> CustomCheckingPolicy::Cached;
80 size_t CustomCheckingPolicy::ChecksTriggered = 0;
81 
82 using MemberWithCustomChecking =
83     BasicMember<GCed, StrongMemberTag, DijkstraWriteBarrierPolicy,
84                 CustomCheckingPolicy>;
85 
86 class MemberTest : public testing::TestSupportingAllocationOnly {};
87 
88 }  // namespace
89 
90 template <template <typename> class MemberType>
EmptyTest()91 void EmptyTest() {
92   {
93     MemberType<GCed> empty;
94     EXPECT_EQ(nullptr, empty.Get());
95     EXPECT_EQ(nullptr, empty.Release());
96   }
97   {
98     MemberType<GCed> empty = nullptr;
99     EXPECT_EQ(nullptr, empty.Get());
100     EXPECT_EQ(nullptr, empty.Release());
101   }
102   {
103     // Move-constructs empty from another Member that is created from nullptr.
104     MemberType<const GCed> empty = nullptr;
105     EXPECT_EQ(nullptr, empty.Get());
106     EXPECT_EQ(nullptr, empty.Release());
107   }
108 }
109 
TEST_F(MemberTest,Empty)110 TEST_F(MemberTest, Empty) {
111   EmptyTest<Member>();
112   EmptyTest<WeakMember>();
113   EmptyTest<UntracedMember>();
114 }
115 
116 template <template <typename> class MemberType>
AtomicCtorTest(cppgc::Heap * heap)117 void AtomicCtorTest(cppgc::Heap* heap) {
118   {
119     GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
120     MemberType<GCed> member(gced,
121                             typename MemberType<GCed>::AtomicInitializerTag());
122     EXPECT_EQ(gced, member.Get());
123   }
124   {
125     GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
126     MemberType<GCed> member(*gced,
127                             typename MemberType<GCed>::AtomicInitializerTag());
128     EXPECT_EQ(gced, member.Get());
129   }
130   {
131     MemberType<GCed> member(nullptr,
132                             typename MemberType<GCed>::AtomicInitializerTag());
133     EXPECT_FALSE(member.Get());
134   }
135   {
136     SentinelPointer s;
137     MemberType<GCed> member(s,
138                             typename MemberType<GCed>::AtomicInitializerTag());
139     EXPECT_EQ(s, member.Get());
140   }
141 }
142 
TEST_F(MemberTest,AtomicCtor)143 TEST_F(MemberTest, AtomicCtor) {
144   cppgc::Heap* heap = GetHeap();
145   AtomicCtorTest<Member>(heap);
146   AtomicCtorTest<WeakMember>(heap);
147   AtomicCtorTest<UntracedMember>(heap);
148 }
149 
150 template <template <typename> class MemberType>
ClearTest(cppgc::Heap * heap)151 void ClearTest(cppgc::Heap* heap) {
152   MemberType<GCed> member =
153       MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
154   EXPECT_NE(nullptr, member.Get());
155   member.Clear();
156   EXPECT_EQ(nullptr, member.Get());
157 }
158 
TEST_F(MemberTest,Clear)159 TEST_F(MemberTest, Clear) {
160   cppgc::Heap* heap = GetHeap();
161   ClearTest<Member>(heap);
162   ClearTest<WeakMember>(heap);
163   ClearTest<UntracedMember>(heap);
164 }
165 
166 template <template <typename> class MemberType>
ReleaseTest(cppgc::Heap * heap)167 void ReleaseTest(cppgc::Heap* heap) {
168   GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
169   MemberType<GCed> member = gced;
170   EXPECT_NE(nullptr, member.Get());
171   GCed* raw = member.Release();
172   EXPECT_EQ(gced, raw);
173   EXPECT_EQ(nullptr, member.Get());
174 }
175 
TEST_F(MemberTest,Release)176 TEST_F(MemberTest, Release) {
177   cppgc::Heap* heap = GetHeap();
178   ReleaseTest<Member>(heap);
179   ReleaseTest<WeakMember>(heap);
180   ReleaseTest<UntracedMember>(heap);
181 }
182 
183 template <template <typename> class MemberType1,
184           template <typename> class MemberType2>
SwapTest(cppgc::Heap * heap)185 void SwapTest(cppgc::Heap* heap) {
186   GCed* gced1 = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
187   GCed* gced2 = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
188   MemberType1<GCed> member1 = gced1;
189   MemberType2<GCed> member2 = gced2;
190   EXPECT_EQ(gced1, member1.Get());
191   EXPECT_EQ(gced2, member2.Get());
192   member1.Swap(member2);
193   EXPECT_EQ(gced2, member1.Get());
194   EXPECT_EQ(gced1, member2.Get());
195 }
196 
TEST_F(MemberTest,Swap)197 TEST_F(MemberTest, Swap) {
198   cppgc::Heap* heap = GetHeap();
199   SwapTest<Member, Member>(heap);
200   SwapTest<Member, WeakMember>(heap);
201   SwapTest<Member, UntracedMember>(heap);
202   SwapTest<WeakMember, Member>(heap);
203   SwapTest<WeakMember, WeakMember>(heap);
204   SwapTest<WeakMember, UntracedMember>(heap);
205   SwapTest<UntracedMember, Member>(heap);
206   SwapTest<UntracedMember, WeakMember>(heap);
207   SwapTest<UntracedMember, UntracedMember>(heap);
208 }
209 
210 template <template <typename> class MemberType1,
211           template <typename> class MemberType2>
MoveTest(cppgc::Heap * heap)212 void MoveTest(cppgc::Heap* heap) {
213   {
214     GCed* gced1 = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
215     MemberType1<GCed> member1 = gced1;
216     MemberType2<GCed> member2(std::move(member1));
217     // Move-from member must be in empty state.
218     EXPECT_FALSE(member1);
219     EXPECT_EQ(gced1, member2.Get());
220   }
221   {
222     GCed* gced1 = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
223     MemberType1<GCed> member1 = gced1;
224     MemberType2<GCed> member2;
225     member2 = std::move(member1);
226     // Move-from member must be in empty state.
227     EXPECT_FALSE(member1);
228     EXPECT_EQ(gced1, member2.Get());
229   }
230 }
231 
TEST_F(MemberTest,Move)232 TEST_F(MemberTest, Move) {
233   cppgc::Heap* heap = GetHeap();
234   MoveTest<Member, Member>(heap);
235   MoveTest<Member, WeakMember>(heap);
236   MoveTest<Member, UntracedMember>(heap);
237   MoveTest<WeakMember, Member>(heap);
238   MoveTest<WeakMember, WeakMember>(heap);
239   MoveTest<WeakMember, UntracedMember>(heap);
240   MoveTest<UntracedMember, Member>(heap);
241   MoveTest<UntracedMember, WeakMember>(heap);
242   MoveTest<UntracedMember, UntracedMember>(heap);
243 }
244 
245 template <template <typename> class MemberType1,
246           template <typename> class MemberType2>
HeterogeneousConversionTest(cppgc::Heap * heap)247 void HeterogeneousConversionTest(cppgc::Heap* heap) {
248   {
249     MemberType1<GCed> member1 =
250         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
251     MemberType2<GCed> member2 = member1;
252     EXPECT_EQ(member1.Get(), member2.Get());
253   }
254   {
255     MemberType1<DerivedGCed> member1 =
256         MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
257     MemberType2<GCed> member2 = member1;
258     EXPECT_EQ(member1.Get(), member2.Get());
259   }
260   {
261     MemberType1<GCed> member1 =
262         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
263     MemberType2<GCed> member2;
264     member2 = member1;
265     EXPECT_EQ(member1.Get(), member2.Get());
266   }
267   {
268     MemberType1<DerivedGCed> member1 =
269         MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
270     MemberType2<GCed> member2;
271     member2 = member1;
272     EXPECT_EQ(member1.Get(), member2.Get());
273   }
274 }
275 
TEST_F(MemberTest,HeterogeneousInterface)276 TEST_F(MemberTest, HeterogeneousInterface) {
277   cppgc::Heap* heap = GetHeap();
278   HeterogeneousConversionTest<Member, Member>(heap);
279   HeterogeneousConversionTest<Member, WeakMember>(heap);
280   HeterogeneousConversionTest<Member, UntracedMember>(heap);
281   HeterogeneousConversionTest<WeakMember, Member>(heap);
282   HeterogeneousConversionTest<WeakMember, WeakMember>(heap);
283   HeterogeneousConversionTest<WeakMember, UntracedMember>(heap);
284   HeterogeneousConversionTest<UntracedMember, Member>(heap);
285   HeterogeneousConversionTest<UntracedMember, WeakMember>(heap);
286   HeterogeneousConversionTest<UntracedMember, UntracedMember>(heap);
287 }
288 
289 template <template <typename> class MemberType,
290           template <typename> class PersistentType>
PersistentConversionTest(cppgc::Heap * heap)291 void PersistentConversionTest(cppgc::Heap* heap) {
292   {
293     PersistentType<GCed> persistent =
294         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
295     MemberType<GCed> member = persistent;
296     EXPECT_EQ(persistent.Get(), member.Get());
297   }
298   {
299     PersistentType<DerivedGCed> persistent =
300         MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
301     MemberType<GCed> member = persistent;
302     EXPECT_EQ(persistent.Get(), member.Get());
303   }
304   {
305     PersistentType<GCed> persistent =
306         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
307     MemberType<GCed> member;
308     member = persistent;
309     EXPECT_EQ(persistent.Get(), member.Get());
310   }
311   {
312     PersistentType<DerivedGCed> persistent =
313         MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
314     MemberType<GCed> member;
315     member = persistent;
316     EXPECT_EQ(persistent.Get(), member.Get());
317   }
318 }
319 
TEST_F(MemberTest,PersistentConversion)320 TEST_F(MemberTest, PersistentConversion) {
321   cppgc::Heap* heap = GetHeap();
322   PersistentConversionTest<Member, Persistent>(heap);
323   PersistentConversionTest<Member, WeakPersistent>(heap);
324   PersistentConversionTest<WeakMember, Persistent>(heap);
325   PersistentConversionTest<WeakMember, WeakPersistent>(heap);
326   PersistentConversionTest<UntracedMember, Persistent>(heap);
327   PersistentConversionTest<UntracedMember, WeakPersistent>(heap);
328 }
329 
330 template <template <typename> class MemberType1,
331           template <typename> class MemberType2>
EqualityTest(cppgc::Heap * heap)332 void EqualityTest(cppgc::Heap* heap) {
333   {
334     GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
335     MemberType1<GCed> member1 = gced;
336     MemberType2<GCed> member2 = gced;
337     EXPECT_TRUE(member1 == member2);
338     EXPECT_FALSE(member1 != member2);
339     member2 = member1;
340     EXPECT_TRUE(member1 == member2);
341     EXPECT_FALSE(member1 != member2);
342   }
343   {
344     MemberType1<GCed> member1 =
345         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
346     MemberType2<GCed> member2 =
347         MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
348     EXPECT_TRUE(member1 != member2);
349     EXPECT_FALSE(member1 == member2);
350   }
351 }
352 
TEST_F(MemberTest,EqualityTest)353 TEST_F(MemberTest, EqualityTest) {
354   cppgc::Heap* heap = GetHeap();
355   EqualityTest<Member, Member>(heap);
356   EqualityTest<Member, WeakMember>(heap);
357   EqualityTest<Member, UntracedMember>(heap);
358   EqualityTest<WeakMember, Member>(heap);
359   EqualityTest<WeakMember, WeakMember>(heap);
360   EqualityTest<WeakMember, UntracedMember>(heap);
361   EqualityTest<UntracedMember, Member>(heap);
362   EqualityTest<UntracedMember, WeakMember>(heap);
363   EqualityTest<UntracedMember, UntracedMember>(heap);
364 }
365 
TEST_F(MemberTest,WriteBarrierTriggered)366 TEST_F(MemberTest, WriteBarrierTriggered) {
367   CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0;
368   CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0;
369   GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
370   MemberWithCustomBarrier member1 = gced;
371   EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
372   EXPECT_EQ(0u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
373   member1 = gced;
374   EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
375   EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
376   member1 = nullptr;
377   EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
378   EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
379   MemberWithCustomBarrier member2 = nullptr;
380   // No initializing barriers for std::nullptr_t.
381   EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
382   EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
383   member2 = kSentinelPointer;
384   EXPECT_EQ(kSentinelPointer, member2.Get());
385   EXPECT_EQ(kSentinelPointer, member2);
386   // No initializing barriers for pointer sentinel.
387   EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
388   EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
389   member2.Swap(member1);
390   EXPECT_EQ(3u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
391 }
392 
TEST_F(MemberTest,CheckingPolicy)393 TEST_F(MemberTest, CheckingPolicy) {
394   static constexpr size_t kElements = 64u;
395   CustomCheckingPolicy::ChecksTriggered = 0u;
396 
397   for (std::size_t i = 0; i < kElements; ++i) {
398     CustomCheckingPolicy::Cached.push_back(
399         MakeGarbageCollected<GCed>(GetAllocationHandle()));
400   }
401 
402   MemberWithCustomChecking member;
403   for (GCed* item : CustomCheckingPolicy::Cached) {
404     member = item;
405   }
406   EXPECT_EQ(CustomCheckingPolicy::Cached.size(),
407             CustomCheckingPolicy::ChecksTriggered);
408 }
409 
410 namespace {
411 
412 class MemberHeapTest : public testing::TestWithHeap {};
413 
414 class GCedWithMembers final : public GarbageCollected<GCedWithMembers> {
415  public:
416   static size_t live_count_;
417 
GCedWithMembers()418   GCedWithMembers() : GCedWithMembers(nullptr, nullptr) {}
GCedWithMembers(GCedWithMembers * strong,GCedWithMembers * weak)419   explicit GCedWithMembers(GCedWithMembers* strong, GCedWithMembers* weak)
420       : strong_nested_(strong), weak_nested_(weak) {
421     ++live_count_;
422   }
423 
~GCedWithMembers()424   ~GCedWithMembers() { --live_count_; }
425 
Trace(cppgc::Visitor * visitor) const426   void Trace(cppgc::Visitor* visitor) const {
427     visitor->Trace(strong_nested_);
428     visitor->Trace(weak_nested_);
429   }
430 
WasNestedCleared() const431   bool WasNestedCleared() const { return !weak_nested_; }
432 
433  private:
434   Member<GCedWithMembers> strong_nested_;
435   WeakMember<GCedWithMembers> weak_nested_;
436 };
437 size_t GCedWithMembers::live_count_ = 0;
438 
439 }  // namespace
440 
TEST_F(MemberHeapTest,MemberRetainsObject)441 TEST_F(MemberHeapTest, MemberRetainsObject) {
442   EXPECT_EQ(0u, GCedWithMembers::live_count_);
443   {
444     GCedWithMembers* nested_object =
445         MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle());
446     Persistent<GCedWithMembers> gced_with_members =
447         MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle(),
448                                               nested_object, nested_object);
449     EXPECT_EQ(2u, GCedWithMembers::live_count_);
450     PreciseGC();
451     EXPECT_EQ(2u, GCedWithMembers::live_count_);
452     EXPECT_FALSE(gced_with_members->WasNestedCleared());
453   }
454   PreciseGC();
455   EXPECT_EQ(0u, GCedWithMembers::live_count_);
456   {
457     GCedWithMembers* nested_object =
458         MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle());
459     GCedWithMembers* gced_with_members = MakeGarbageCollected<GCedWithMembers>(
460         GetAllocationHandle(), nested_object, nested_object);
461     EXPECT_EQ(2u, GCedWithMembers::live_count_);
462     ConservativeGC();
463     EXPECT_EQ(2u, GCedWithMembers::live_count_);
464     EXPECT_FALSE(gced_with_members->WasNestedCleared());
465   }
466   PreciseGC();
467   EXPECT_EQ(0u, GCedWithMembers::live_count_);
468 }
469 
TEST_F(MemberHeapTest,WeakMemberDoesNotRetainObject)470 TEST_F(MemberHeapTest, WeakMemberDoesNotRetainObject) {
471   EXPECT_EQ(0u, GCedWithMembers::live_count_);
472   auto* weak_nested =
473       MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle());
474   Persistent<GCedWithMembers> gced_with_members(
475       MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle(), nullptr,
476                                             weak_nested));
477   PreciseGC();
478   EXPECT_EQ(1u, GCedWithMembers::live_count_);
479   EXPECT_TRUE(gced_with_members->WasNestedCleared());
480 }
481 
482 namespace {
483 class GCedWithConstWeakMember
484     : public GarbageCollected<GCedWithConstWeakMember> {
485  public:
GCedWithConstWeakMember(const GCedWithMembers * weak)486   explicit GCedWithConstWeakMember(const GCedWithMembers* weak)
487       : weak_member_(weak) {}
488 
Trace(Visitor * visitor) const489   void Trace(Visitor* visitor) const { visitor->Trace(weak_member_); }
490 
weak_member() const491   const GCedWithMembers* weak_member() const { return weak_member_; }
492 
493  private:
494   const WeakMember<const GCedWithMembers> weak_member_;
495 };
496 }  // namespace
497 
TEST_F(MemberHeapTest,ConstWeakRefIsClearedOnGC)498 TEST_F(MemberHeapTest, ConstWeakRefIsClearedOnGC) {
499   const WeakPersistent<const GCedWithMembers> weak_persistent =
500       MakeGarbageCollected<GCedWithMembers>(GetAllocationHandle());
501   Persistent<GCedWithConstWeakMember> persistent =
502       MakeGarbageCollected<GCedWithConstWeakMember>(GetAllocationHandle(),
503                                                     weak_persistent);
504   PreciseGC();
505   EXPECT_FALSE(weak_persistent);
506   EXPECT_FALSE(persistent->weak_member());
507 }
508 
509 #if V8_ENABLE_CHECKS
510 
511 namespace {
512 class MemberHeapDeathTest : public testing::TestWithHeap {};
513 
514 class LinkedNode final : public GarbageCollected<LinkedNode> {
515  public:
LinkedNode(LinkedNode * next)516   explicit LinkedNode(LinkedNode* next) : next_(next) {}
Trace(Visitor * v) const517   void Trace(Visitor* v) const { v->Trace(next_); }
518 
SetNext(LinkedNode * next)519   void SetNext(LinkedNode* next) { next_ = next; }
520 
521  private:
522   Member<LinkedNode> next_;
523 };
524 
525 }  // namespace
526 
TEST_F(MemberHeapDeathTest,CheckForOffHeapMemberCrashesOnReassignment)527 TEST_F(MemberHeapDeathTest, CheckForOffHeapMemberCrashesOnReassignment) {
528   std::vector<Member<LinkedNode>> off_heap_member;
529   // Verification state is constructed on first assignment.
530   off_heap_member.emplace_back(
531       MakeGarbageCollected<LinkedNode>(GetAllocationHandle(), nullptr));
532   {
533     auto tmp_heap = cppgc::Heap::Create(platform_);
534     auto* tmp_obj = MakeGarbageCollected<LinkedNode>(
535         tmp_heap->GetAllocationHandle(), nullptr);
536     EXPECT_DEATH_IF_SUPPORTED(off_heap_member[0] = tmp_obj, "");
537   }
538 }
539 
TEST_F(MemberHeapDeathTest,CheckForOnStackMemberCrashesOnReassignment)540 TEST_F(MemberHeapDeathTest, CheckForOnStackMemberCrashesOnReassignment) {
541   Member<LinkedNode> stack_member;
542   // Verification state is constructed on first assignment.
543   stack_member =
544       MakeGarbageCollected<LinkedNode>(GetAllocationHandle(), nullptr);
545   {
546     auto tmp_heap = cppgc::Heap::Create(platform_);
547     auto* tmp_obj = MakeGarbageCollected<LinkedNode>(
548         tmp_heap->GetAllocationHandle(), nullptr);
549     EXPECT_DEATH_IF_SUPPORTED(stack_member = tmp_obj, "");
550   }
551 }
552 
TEST_F(MemberHeapDeathTest,CheckForOnHeapMemberCrashesOnInitialAssignment)553 TEST_F(MemberHeapDeathTest, CheckForOnHeapMemberCrashesOnInitialAssignment) {
554   auto* obj = MakeGarbageCollected<LinkedNode>(GetAllocationHandle(), nullptr);
555   {
556     auto tmp_heap = cppgc::Heap::Create(platform_);
557     EXPECT_DEATH_IF_SUPPORTED(
558         // For regular on-heap Member references the verification state is
559         // constructed eagerly on creating the reference.
560         MakeGarbageCollected<LinkedNode>(tmp_heap->GetAllocationHandle(), obj),
561         "");
562   }
563 }
564 
565 #endif  // V8_ENABLE_CHECKS
566 
567 }  // namespace internal
568 }  // namespace cppgc
569