text: // Copyright 2020 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "include/cppgc/allocation.h"
  #include "src/heap/cppgc/marker.h"
  #include "src/heap/cppgc/marking-visitor.h"
  #include "src/heap/cppgc/stats-collector.h"
  #include "test/unittests/heap/cppgc/tests.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace cppgc {
  namespace internal {
  
  namespace {
  class WeakContainerTest : public testing::TestWithHeap {
   public:
    using Config = Marker::MarkingConfig;
  
    void StartMarking() {
      Config config = {Config::CollectionType::kMajor,
                       Config::StackState::kNoHeapPointers,
                       Config::MarkingType::kIncremental};
      GetMarkerRef() = MarkerFactory::CreateAndStartMarking<Marker>(
          Heap::From(GetHeap())->AsBase(), GetPlatformHandle().get(), config);
    }
  
    void FinishMarking(Config::StackState stack_state) {
      GetMarkerRef()->FinishMarking(stack_state);
      Heap::From(GetHeap())->stats_collector()->NotifySweepingCompleted();
    }
  };
  
  class TraceableGCed : public GarbageCollected<TraceableGCed> {
   public:
    void Trace(cppgc::Visitor*) const { n_trace_calls++; }
    static size_t n_trace_calls;
  };
  size_t TraceableGCed::n_trace_calls = 0u;
  
  class NonTraceableGCed : public GarbageCollected<NonTraceableGCed> {
   public:
    void Trace(cppgc::Visitor*) const { n_trace_calls++; }
    static size_t n_trace_calls;
  };
  size_t NonTraceableGCed::n_trace_calls = 0u;
  
  void EmptyWeakCallback(const LivenessBroker&, const void*) {}
  
  template <typename T>
  V8_NOINLINE T access(volatile const T& t) {
    return t;
  }
  
  }  // namespace
  
  }  // namespace internal
  
  template <>
  struct TraceTrait<internal::TraceableGCed>
      : public internal::TraceTraitBase<internal::TraceableGCed> {
    static TraceDescriptor GetWeakTraceDescriptor(const void* self) {
      return {self, Trace};
    }
  };
  
  template <>
  struct TraceTrait<internal::NonTraceableGCed>
      : public internal::TraceTraitBase<internal::NonTraceableGCed> {
    static TraceDescriptor GetWeakTraceDescriptor(const void* self) {
      return {self, nullptr};
    }
  };
  
  namespace internal {
  
  TEST_F(WeakContainerTest, TraceableGCedTraced) {
    TraceableGCed* obj =
        MakeGarbageCollected<TraceableGCed>(GetAllocationHandle());
    TraceableGCed::n_trace_calls = 0u;
    StartMarking();
    GetMarkerRef()->VisitorForTesting().TraceWeakContainer(obj, EmptyWeakCallback,
                                                           nullptr);
    FinishMarking(Config::StackState::kNoHeapPointers);
    EXPECT_NE(0u, TraceableGCed::n_trace_calls);
    access(obj);
  }
  
  TEST_F(WeakContainerTest, NonTraceableGCedNotTraced) {
    NonTraceableGCed* obj =
        MakeGarbageCollected<NonTraceableGCed>(GetAllocationHandle());
    NonTraceableGCed::n_trace_calls = 0u;
    StartMarking();
    GetMarkerRef()->VisitorForTesting().TraceWeakContainer(obj, EmptyWeakCallback,
                                                           nullptr);
    FinishMarking(Config::StackState::kNoHeapPointers);
    EXPECT_EQ(0u, NonTraceableGCed::n_trace_calls);
    access(obj);
  }
  
  TEST_F(WeakContainerTest, NonTraceableGCedNotTracedConservatively) {
    NonTraceableGCed* obj =
        MakeGarbageCollected<NonTraceableGCed>(GetAllocationHandle());
    NonTraceableGCed::n_trace_calls = 0u;
    StartMarking();
    GetMarkerRef()->VisitorForTesting().TraceWeakContainer(obj, EmptyWeakCallback,
                                                           nullptr);
    FinishMarking(Config::StackState::kMayContainHeapPointers);
    EXPECT_NE(0u, NonTraceableGCed::n_trace_calls);
    access(obj);
  }
  
  TEST_F(WeakContainerTest, ConservativeGCTracesWeakContainer) {
    size_t trace_count_without_conservative;
    {
      TraceableGCed* obj =
          MakeGarbageCollected<TraceableGCed>(GetAllocationHandle());
      TraceableGCed::n_trace_calls = 0u;
      StartMarking();
      GetMarkerRef()->VisitorForTesting().TraceWeakContainer(
          obj, EmptyWeakCallback, nullptr);
      FinishMarking(Config::StackState::kNoHeapPointers);
      trace_count_without_conservative = TraceableGCed::n_trace_calls;
      access(obj);
    }
    {
      TraceableGCed* obj =
          MakeGarbageCollected<TraceableGCed>(GetAllocationHandle());
      TraceableGCed::n_trace_calls = 0u;
      StartMarking();
      GetMarkerRef()->VisitorForTesting().TraceWeakContainer(
          obj, EmptyWeakCallback, nullptr);
      FinishMarking(Config::StackState::kMayContainHeapPointers);
      EXPECT_LT(trace_count_without_conservative, TraceableGCed::n_trace_calls);
      access(obj);
    }
  }
  
  TEST_F(WeakContainerTest, ConservativeGCTracesWeakContainerOnce) {
    NonTraceableGCed* obj =
        MakeGarbageCollected<NonTraceableGCed>(GetAllocationHandle());
    NonTraceableGCed* copy_obj = obj;
    USE(copy_obj);
    NonTraceableGCed* another_copy_obj = obj;
    USE(another_copy_obj);
    NonTraceableGCed::n_trace_calls = 0u;
    StartMarking();
    GetMarkerRef()->VisitorForTesting().TraceWeakContainer(obj, EmptyWeakCallback,
                                                           nullptr);
    FinishMarking(Config::StackState::kMayContainHeapPointers);
    EXPECT_EQ(1u, NonTraceableGCed::n_trace_calls);
    access(obj);
  }
  
  namespace {
  
  struct WeakCallback {
    static void callback(const LivenessBroker&, const void* data) {
      n_callback_called++;
      obj = data;
    }
    static size_t n_callback_called;
    static const void* obj;
  };
  size_t WeakCallback::n_callback_called = 0u;
  const void* WeakCallback::obj = nullptr;
  
  }  // namespace
  
  TEST_F(WeakContainerTest, WeakContainerWeakCallbackCalled) {
    TraceableGCed* obj =
        MakeGarbageCollected<TraceableGCed>(GetAllocationHandle());
    WeakCallback::n_callback_called = 0u;
    WeakCallback::obj = nullptr;
    StartMarking();
    GetMarkerRef()->VisitorForTesting().TraceWeakContainer(
        obj, WeakCallback::callback, obj);
    FinishMarking(Config::StackState::kMayContainHeapPointers);
    EXPECT_NE(0u, WeakCallback::n_callback_called);
    EXPECT_EQ(obj, WeakCallback::obj);
  }
  
  }  // namespace internal
  }  // namespace cppgc
  