summaryrefslogtreecommitdiff
path: root/deps/v8/test/unittests/heap/cppgc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/test/unittests/heap/cppgc')
-rw-r--r--deps/v8/test/unittests/heap/cppgc/compactor-unittest.cc250
-rw-r--r--deps/v8/test/unittests/heap/cppgc/concurrent-marking-unittest.cc147
-rw-r--r--deps/v8/test/unittests/heap/cppgc/concurrent-sweeper-unittest.cc25
-rw-r--r--deps/v8/test/unittests/heap/cppgc/cross-thread-persistent-unittest.cc101
-rw-r--r--deps/v8/test/unittests/heap/cppgc/custom-spaces-unittest.cc107
-rw-r--r--deps/v8/test/unittests/heap/cppgc/ephemeron-pair-unittest.cc112
-rw-r--r--deps/v8/test/unittests/heap/cppgc/gc-info-unittest.cc16
-rw-r--r--deps/v8/test/unittests/heap/cppgc/gc-invoker-unittest.cc2
-rw-r--r--deps/v8/test/unittests/heap/cppgc/heap-object-header-unittest.cc16
-rw-r--r--deps/v8/test/unittests/heap/cppgc/heap-unittest.cc36
-rw-r--r--deps/v8/test/unittests/heap/cppgc/marker-unittest.cc22
-rw-r--r--deps/v8/test/unittests/heap/cppgc/marking-verifier-unittest.cc45
-rw-r--r--deps/v8/test/unittests/heap/cppgc/marking-visitor-unittest.cc54
-rw-r--r--deps/v8/test/unittests/heap/cppgc/name-trait-unittest.cc133
-rw-r--r--deps/v8/test/unittests/heap/cppgc/page-memory-unittest.cc11
-rw-r--r--deps/v8/test/unittests/heap/cppgc/persistent-family-unittest.cc (renamed from deps/v8/test/unittests/heap/cppgc/persistent-unittest.cc)184
-rw-r--r--deps/v8/test/unittests/heap/cppgc/stack-unittest.cc3
-rw-r--r--deps/v8/test/unittests/heap/cppgc/sweeper-unittest.cc5
-rw-r--r--deps/v8/test/unittests/heap/cppgc/test-platform.cc123
-rw-r--r--deps/v8/test/unittests/heap/cppgc/test-platform.h57
-rw-r--r--deps/v8/test/unittests/heap/cppgc/tests.h5
-rw-r--r--deps/v8/test/unittests/heap/cppgc/weak-container-unittest.cc184
-rw-r--r--deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc10
23 files changed, 1371 insertions, 277 deletions
diff --git a/deps/v8/test/unittests/heap/cppgc/compactor-unittest.cc b/deps/v8/test/unittests/heap/cppgc/compactor-unittest.cc
new file mode 100644
index 0000000000..92ae9dc6b6
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/compactor-unittest.cc
@@ -0,0 +1,250 @@
+// 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 "src/heap/cppgc/compactor.h"
+
+#include "include/cppgc/allocation.h"
+#include "include/cppgc/custom-space.h"
+#include "include/cppgc/persistent.h"
+#include "src/heap/cppgc/heap-object-header.h"
+#include "src/heap/cppgc/heap-page.h"
+#include "src/heap/cppgc/marker.h"
+#include "src/heap/cppgc/stats-collector.h"
+#include "test/unittests/heap/cppgc/tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cppgc {
+
+class CompactableCustomSpace : public CustomSpace<CompactableCustomSpace> {
+ public:
+ static constexpr size_t kSpaceIndex = 0;
+ static constexpr bool kSupportsCompaction = true;
+};
+
+namespace internal {
+
+namespace {
+
+struct CompactableGCed : public GarbageCollected<CompactableGCed> {
+ public:
+ ~CompactableGCed() { ++g_destructor_callcount; }
+ void Trace(Visitor* visitor) const {
+ visitor->Trace(other);
+ visitor->RegisterMovableReference(other.GetSlotForTesting());
+ }
+ static size_t g_destructor_callcount;
+ Member<CompactableGCed> other;
+ size_t id = 0;
+};
+// static
+size_t CompactableGCed::g_destructor_callcount = 0;
+
+template <int kNumObjects>
+struct CompactableHolder
+ : public GarbageCollected<CompactableHolder<kNumObjects>> {
+ public:
+ explicit CompactableHolder(cppgc::AllocationHandle& allocation_handle) {
+ for (int i = 0; i < kNumObjects; ++i)
+ objects[i] = MakeGarbageCollected<CompactableGCed>(allocation_handle);
+ }
+
+ void Trace(Visitor* visitor) const {
+ for (int i = 0; i < kNumObjects; ++i) {
+ visitor->Trace(objects[i]);
+ visitor->RegisterMovableReference(objects[i].GetSlotForTesting());
+ }
+ }
+ Member<CompactableGCed> objects[kNumObjects];
+};
+
+class CompactorTest : public testing::TestWithPlatform {
+ public:
+ CompactorTest() {
+ Heap::HeapOptions options;
+ options.custom_spaces.emplace_back(
+ std::make_unique<CompactableCustomSpace>());
+ heap_ = Heap::Create(platform_, std::move(options));
+ }
+
+ void StartCompaction() {
+ compactor().EnableForNextGCForTesting();
+ compactor().InitializeIfShouldCompact(
+ GarbageCollector::Config::MarkingType::kIncremental,
+ GarbageCollector::Config::StackState::kNoHeapPointers);
+ EXPECT_TRUE(compactor().IsEnabledForTesting());
+ }
+
+ void CancelCompaction() {
+ bool cancelled = compactor().CancelIfShouldNotCompact(
+ GarbageCollector::Config::MarkingType::kAtomic,
+ GarbageCollector::Config::StackState::kMayContainHeapPointers);
+ EXPECT_TRUE(cancelled);
+ }
+
+ void FinishCompaction() { compactor().CompactSpacesIfEnabled(); }
+
+ void StartGC() {
+ CompactableGCed::g_destructor_callcount = 0u;
+ StartCompaction();
+ heap()->StartIncrementalGarbageCollection(
+ GarbageCollector::Config::PreciseIncrementalConfig());
+ }
+
+ void EndGC() {
+ heap()->marker()->FinishMarking(
+ GarbageCollector::Config::StackState::kNoHeapPointers);
+ FinishCompaction();
+ // Sweeping also verifies the object start bitmap.
+ const Sweeper::SweepingConfig sweeping_config{
+ Sweeper::SweepingConfig::SweepingType::kAtomic,
+ Sweeper::SweepingConfig::CompactableSpaceHandling::kIgnore};
+ heap()->sweeper().Start(sweeping_config);
+ }
+
+ Heap* heap() { return Heap::From(heap_.get()); }
+ cppgc::AllocationHandle& GetAllocationHandle() {
+ return heap_->GetAllocationHandle();
+ }
+ Compactor& compactor() { return heap()->compactor(); }
+
+ private:
+ std::unique_ptr<cppgc::Heap> heap_;
+};
+
+} // namespace
+
+} // namespace internal
+
+template <>
+struct SpaceTrait<internal::CompactableGCed> {
+ using Space = CompactableCustomSpace;
+};
+
+namespace internal {
+
+TEST_F(CompactorTest, NothingToCompact) {
+ StartCompaction();
+ FinishCompaction();
+}
+
+TEST_F(CompactorTest, CancelledNothingToCompact) {
+ StartCompaction();
+ CancelCompaction();
+}
+
+TEST_F(CompactorTest, NonEmptySpaceAllLive) {
+ static constexpr int kNumObjects = 10;
+ Persistent<CompactableHolder<kNumObjects>> holder =
+ MakeGarbageCollected<CompactableHolder<kNumObjects>>(
+ GetAllocationHandle(), GetAllocationHandle());
+ CompactableGCed* references[kNumObjects] = {nullptr};
+ for (int i = 0; i < kNumObjects; ++i) {
+ references[i] = holder->objects[i];
+ }
+ StartGC();
+ EndGC();
+ EXPECT_EQ(0u, CompactableGCed::g_destructor_callcount);
+ for (int i = 0; i < kNumObjects; ++i) {
+ EXPECT_EQ(holder->objects[i], references[i]);
+ }
+}
+
+TEST_F(CompactorTest, NonEmptySpaceAllDead) {
+ static constexpr int kNumObjects = 10;
+ Persistent<CompactableHolder<kNumObjects>> holder =
+ MakeGarbageCollected<CompactableHolder<kNumObjects>>(
+ GetAllocationHandle(), GetAllocationHandle());
+ CompactableGCed::g_destructor_callcount = 0u;
+ StartGC();
+ for (int i = 0; i < kNumObjects; ++i) {
+ holder->objects[i] = nullptr;
+ }
+ EndGC();
+ EXPECT_EQ(10u, CompactableGCed::g_destructor_callcount);
+}
+
+TEST_F(CompactorTest, NonEmptySpaceHalfLive) {
+ static constexpr int kNumObjects = 10;
+ Persistent<CompactableHolder<kNumObjects>> holder =
+ MakeGarbageCollected<CompactableHolder<kNumObjects>>(
+ GetAllocationHandle(), GetAllocationHandle());
+ CompactableGCed* references[kNumObjects] = {nullptr};
+ for (int i = 0; i < kNumObjects; ++i) {
+ references[i] = holder->objects[i];
+ }
+ StartGC();
+ for (int i = 0; i < kNumObjects; i += 2) {
+ holder->objects[i] = nullptr;
+ }
+ EndGC();
+ // Half of object were destroyed.
+ EXPECT_EQ(5u, CompactableGCed::g_destructor_callcount);
+ // Remaining objects are compacted.
+ for (int i = 1; i < kNumObjects; i += 2) {
+ EXPECT_EQ(holder->objects[i], references[i / 2]);
+ }
+}
+
+TEST_F(CompactorTest, CompactAcrossPages) {
+ Persistent<CompactableHolder<1>> holder =
+ MakeGarbageCollected<CompactableHolder<1>>(GetAllocationHandle(),
+ GetAllocationHandle());
+ CompactableGCed* reference = holder->objects[0];
+ static constexpr size_t kObjectsPerPage =
+ kPageSize / (sizeof(CompactableGCed) + sizeof(HeapObjectHeader));
+ for (size_t i = 0; i < kObjectsPerPage; ++i) {
+ holder->objects[0] =
+ MakeGarbageCollected<CompactableGCed>(GetAllocationHandle());
+ }
+ // Last allocated object should be on a new page.
+ EXPECT_NE(reference, holder->objects[0]);
+ EXPECT_NE(BasePage::FromInnerAddress(heap(), reference),
+ BasePage::FromInnerAddress(heap(), holder->objects[0].Get()));
+ StartGC();
+ EndGC();
+ // Half of object were destroyed.
+ EXPECT_EQ(kObjectsPerPage, CompactableGCed::g_destructor_callcount);
+ EXPECT_EQ(reference, holder->objects[0]);
+}
+
+TEST_F(CompactorTest, InteriorSlotToPreviousObject) {
+ static constexpr int kNumObjects = 3;
+ Persistent<CompactableHolder<kNumObjects>> holder =
+ MakeGarbageCollected<CompactableHolder<kNumObjects>>(
+ GetAllocationHandle(), GetAllocationHandle());
+ CompactableGCed* references[kNumObjects] = {nullptr};
+ for (int i = 0; i < kNumObjects; ++i) {
+ references[i] = holder->objects[i];
+ }
+ holder->objects[2]->other = holder->objects[1];
+ holder->objects[1] = nullptr;
+ holder->objects[0] = nullptr;
+ StartGC();
+ EndGC();
+ EXPECT_EQ(1u, CompactableGCed::g_destructor_callcount);
+ EXPECT_EQ(references[1], holder->objects[2]);
+ EXPECT_EQ(references[0], holder->objects[2]->other);
+}
+
+TEST_F(CompactorTest, InteriorSlotToNextObject) {
+ static constexpr int kNumObjects = 3;
+ Persistent<CompactableHolder<kNumObjects>> holder =
+ MakeGarbageCollected<CompactableHolder<kNumObjects>>(
+ GetAllocationHandle(), GetAllocationHandle());
+ CompactableGCed* references[kNumObjects] = {nullptr};
+ for (int i = 0; i < kNumObjects; ++i) {
+ references[i] = holder->objects[i];
+ }
+ holder->objects[1]->other = holder->objects[2];
+ holder->objects[2] = nullptr;
+ holder->objects[0] = nullptr;
+ StartGC();
+ EndGC();
+ EXPECT_EQ(1u, CompactableGCed::g_destructor_callcount);
+ EXPECT_EQ(references[0], holder->objects[1]);
+ EXPECT_EQ(references[1], holder->objects[1]->other);
+}
+
+} // namespace internal
+} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/concurrent-marking-unittest.cc b/deps/v8/test/unittests/heap/cppgc/concurrent-marking-unittest.cc
index b39a545b7b..4da9870221 100644
--- a/deps/v8/test/unittests/heap/cppgc/concurrent-marking-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/concurrent-marking-unittest.cc
@@ -16,51 +16,17 @@
namespace cppgc {
namespace internal {
-#if defined(THREAD_SANITIZER)
-
namespace {
-class GCed : public GarbageCollected<GCed> {
- public:
- void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
-
- Member<GCed> child_;
-};
-
-class GCedWithCallback : public GarbageCollected<GCedWithCallback> {
- public:
- template <typename Callback>
- explicit GCedWithCallback(Callback callback) {
- callback(this);
- }
-
- void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
-
- Member<GCedWithCallback> child_;
-};
-
-class Mixin : public GarbageCollectedMixin {
- public:
- void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
-
- Member<Mixin> child_;
-};
-
-class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {
- public:
- void Trace(cppgc::Visitor* visitor) const { Mixin::Trace(visitor); }
-};
-
-template <typename T>
-class GCedHolder : public GarbageCollected<GCedHolder<T>> {
- public:
- void Trace(cppgc::Visitor* visitor) const { visitor->Trace(object_); }
-
- Member<T> object_;
-};
-
class ConcurrentMarkingTest : public testing::TestWithHeap {
public:
+#if defined(THREAD_SANITIZER)
+ // Use more iteration on tsan builds to expose data races.
+ static constexpr int kNumStep = 1000;
+#else
+ static constexpr int kNumStep = 10;
+#endif // defined(THREAD_SANITIZER)
+
using Config = Heap::Config;
static constexpr Config ConcurrentPreciseConfig = {
Config::CollectionType::kMajor, Config::StackState::kNoHeapPointers,
@@ -95,16 +61,52 @@ class ConcurrentMarkingTest : public testing::TestWithHeap {
constexpr ConcurrentMarkingTest::Config
ConcurrentMarkingTest::ConcurrentPreciseConfig;
+template <typename T>
+struct GCedHolder : public GarbageCollected<GCedHolder<T>> {
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(object); }
+ Member<T> object;
+};
+
+class GCed : public GarbageCollected<GCed> {
+ public:
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
+
+ Member<GCed> child_;
+};
+
+class GCedWithCallback : public GarbageCollected<GCedWithCallback> {
+ public:
+ template <typename Callback>
+ explicit GCedWithCallback(Callback callback) {
+ callback(this);
+ }
+
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
+
+ Member<GCedWithCallback> child_;
+};
+
+class Mixin : public GarbageCollectedMixin {
+ public:
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
+
+ Member<Mixin> child_;
+};
+
+class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {
+ public:
+ void Trace(cppgc::Visitor* visitor) const { Mixin::Trace(visitor); }
+};
+
} // namespace
// The following tests below check for data races during concurrent marking.
TEST_F(ConcurrentMarkingTest, MarkingObjects) {
- static constexpr int kNumStep = 1000;
StartConcurrentGC();
Persistent<GCedHolder<GCed>> root =
MakeGarbageCollected<GCedHolder<GCed>>(GetAllocationHandle());
- Member<GCed>* last_object = &root->object_;
+ Member<GCed>* last_object = &root->object;
for (int i = 0; i < kNumStep; ++i) {
for (int j = 0; j < kNumStep; ++j) {
*last_object = MakeGarbageCollected<GCed>(GetAllocationHandle());
@@ -117,11 +119,10 @@ TEST_F(ConcurrentMarkingTest, MarkingObjects) {
}
TEST_F(ConcurrentMarkingTest, MarkingInConstructionObjects) {
- static constexpr int kNumStep = 1000;
StartConcurrentGC();
Persistent<GCedHolder<GCedWithCallback>> root =
MakeGarbageCollected<GCedHolder<GCedWithCallback>>(GetAllocationHandle());
- Member<GCedWithCallback>* last_object = &root->object_;
+ Member<GCedWithCallback>* last_object = &root->object;
for (int i = 0; i < kNumStep; ++i) {
for (int j = 0; j < kNumStep; ++j) {
MakeGarbageCollected<GCedWithCallback>(
@@ -137,11 +138,10 @@ TEST_F(ConcurrentMarkingTest, MarkingInConstructionObjects) {
}
TEST_F(ConcurrentMarkingTest, MarkingMixinObjects) {
- static constexpr int kNumStep = 1000;
StartConcurrentGC();
Persistent<GCedHolder<Mixin>> root =
MakeGarbageCollected<GCedHolder<Mixin>>(GetAllocationHandle());
- Member<Mixin>* last_object = &root->object_;
+ Member<Mixin>* last_object = &root->object;
for (int i = 0; i < kNumStep; ++i) {
for (int j = 0; j < kNumStep; ++j) {
*last_object = MakeGarbageCollected<GCedWithMixin>(GetAllocationHandle());
@@ -153,7 +153,58 @@ TEST_F(ConcurrentMarkingTest, MarkingMixinObjects) {
FinishGC();
}
-#endif // defined(THREAD_SANITIZER)
+namespace {
+
+struct ConcurrentlyTraceable : public GarbageCollected<ConcurrentlyTraceable> {
+ static size_t trace_counter;
+ void Trace(Visitor*) const { ++trace_counter; }
+};
+size_t ConcurrentlyTraceable::trace_counter = 0;
+
+struct NotConcurrentlyTraceable
+ : public GarbageCollected<NotConcurrentlyTraceable> {
+ static size_t trace_counter;
+ void Trace(Visitor* visitor) const {
+ if (visitor->DeferTraceToMutatorThreadIfConcurrent(
+ this,
+ [](Visitor*, const void*) {
+ ++NotConcurrentlyTraceable::trace_counter;
+ },
+ sizeof(NotConcurrentlyTraceable)))
+ return;
+ ++trace_counter;
+ }
+};
+size_t NotConcurrentlyTraceable::trace_counter = 0;
+
+} // namespace
+
+TEST_F(ConcurrentMarkingTest, ConcurrentlyTraceableObjectIsTracedConcurrently) {
+ Persistent<GCedHolder<ConcurrentlyTraceable>> root =
+ MakeGarbageCollected<GCedHolder<ConcurrentlyTraceable>>(
+ GetAllocationHandle());
+ root->object =
+ MakeGarbageCollected<ConcurrentlyTraceable>(GetAllocationHandle());
+ EXPECT_EQ(0u, ConcurrentlyTraceable::trace_counter);
+ StartConcurrentGC();
+ GetMarkerRef()->WaitForConcurrentMarkingForTesting();
+ EXPECT_NE(0u, ConcurrentlyTraceable::trace_counter);
+ FinishGC();
+}
+
+TEST_F(ConcurrentMarkingTest,
+ NotConcurrentlyTraceableObjectIsNotTracedConcurrently) {
+ Persistent<GCedHolder<NotConcurrentlyTraceable>> root =
+ MakeGarbageCollected<GCedHolder<NotConcurrentlyTraceable>>(
+ GetAllocationHandle());
+ root->object =
+ MakeGarbageCollected<NotConcurrentlyTraceable>(GetAllocationHandle());
+ EXPECT_EQ(0u, NotConcurrentlyTraceable::trace_counter);
+ StartConcurrentGC();
+ GetMarkerRef()->WaitForConcurrentMarkingForTesting();
+ EXPECT_EQ(0u, NotConcurrentlyTraceable::trace_counter);
+ FinishGC();
+}
} // namespace internal
} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/concurrent-sweeper-unittest.cc b/deps/v8/test/unittests/heap/cppgc/concurrent-sweeper-unittest.cc
index 3794adce25..b1cdc5d8fc 100644
--- a/deps/v8/test/unittests/heap/cppgc/concurrent-sweeper-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/concurrent-sweeper-unittest.cc
@@ -61,6 +61,8 @@ class NonFinalizable : public GarbageCollected<NonFinalizable<Size>> {
using NormalNonFinalizable = NonFinalizable<32>;
using LargeNonFinalizable = NonFinalizable<kLargeObjectSizeThreshold * 2>;
+} // namespace
+
class ConcurrentSweeperTest : public testing::TestWithHeap {
public:
ConcurrentSweeperTest() { g_destructor_callcount = 0; }
@@ -73,7 +75,16 @@ class ConcurrentSweeperTest : public testing::TestWithHeap {
heap->stats_collector()->NotifyMarkingStarted();
heap->stats_collector()->NotifyMarkingCompleted(0);
Sweeper& sweeper = heap->sweeper();
- sweeper.Start(Sweeper::Config::kIncrementalAndConcurrent);
+ const Sweeper::SweepingConfig sweeping_config{
+ Sweeper::SweepingConfig::SweepingType::kIncrementalAndConcurrent,
+ Sweeper::SweepingConfig::CompactableSpaceHandling::kSweep};
+ sweeper.Start(sweeping_config);
+ }
+
+ void WaitForConcurrentSweeping() {
+ Heap* heap = Heap::From(GetHeap());
+ Sweeper& sweeper = heap->sweeper();
+ sweeper.WaitForConcurrentSweepingForTesting();
}
void FinishSweeping() {
@@ -126,8 +137,6 @@ class ConcurrentSweeperTest : public testing::TestWithHeap {
}
};
-} // namespace
-
TEST_F(ConcurrentSweeperTest, BackgroundSweepOfNormalPage) {
// Non finalizable objects are swept right away.
using GCedType = NormalNonFinalizable;
@@ -145,7 +154,7 @@ TEST_F(ConcurrentSweeperTest, BackgroundSweepOfNormalPage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
- GetPlatform().WaitAllBackgroundTasks();
+ WaitForConcurrentSweeping();
#if !defined(CPPGC_YOUNG_GENERATION)
// Check that the marked object was unmarked.
@@ -184,7 +193,7 @@ TEST_F(ConcurrentSweeperTest, BackgroundSweepOfLargePage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
- GetPlatform().WaitAllBackgroundTasks();
+ WaitForConcurrentSweeping();
#if !defined(CPPGC_YOUNG_GENERATION)
// Check that the marked object was unmarked.
@@ -224,7 +233,7 @@ TEST_F(ConcurrentSweeperTest, DeferredFinalizationOfNormalPage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
- GetPlatform().WaitAllBackgroundTasks();
+ WaitForConcurrentSweeping();
// Check that pages are not returned right away.
for (auto* page : pages) {
@@ -256,7 +265,7 @@ TEST_F(ConcurrentSweeperTest, DeferredFinalizationOfLargePage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
- GetPlatform().WaitAllBackgroundTasks();
+ WaitForConcurrentSweeping();
// Check that the page is not returned to the space.
EXPECT_EQ(space->end(), std::find(space->begin(), space->end(), page));
@@ -302,7 +311,7 @@ TEST_F(ConcurrentSweeperTest, IncrementalSweeping) {
EXPECT_TRUE(marked_large_header.IsMarked());
// Wait for incremental sweeper to finish.
- GetPlatform().WaitAllForegroundTasks();
+ GetPlatform().RunAllForegroundTasks();
EXPECT_EQ(2u, g_destructor_callcount);
#if !defined(CPPGC_YOUNG_GENERATION)
diff --git a/deps/v8/test/unittests/heap/cppgc/cross-thread-persistent-unittest.cc b/deps/v8/test/unittests/heap/cppgc/cross-thread-persistent-unittest.cc
new file mode 100644
index 0000000000..3a9dc91000
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/cross-thread-persistent-unittest.cc
@@ -0,0 +1,101 @@
+// 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/cross-thread-persistent.h"
+
+#include "include/cppgc/allocation.h"
+#include "src/base/platform/condition-variable.h"
+#include "src/base/platform/mutex.h"
+#include "src/base/platform/platform.h"
+#include "test/unittests/heap/cppgc/tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cppgc {
+namespace internal {
+
+namespace {
+
+struct GCed final : GarbageCollected<GCed> {
+ static size_t destructor_call_count;
+ GCed() { destructor_call_count = 0; }
+ ~GCed() { destructor_call_count++; }
+ virtual void Trace(cppgc::Visitor*) const {}
+ int a = 0;
+};
+size_t GCed::destructor_call_count = 0;
+
+class Runner final : public v8::base::Thread {
+ public:
+ template <typename Callback>
+ explicit Runner(Callback callback)
+ : Thread(v8::base::Thread::Options("CrossThreadPersistent Thread")),
+ callback_(callback) {}
+
+ void Run() final { callback_(); }
+
+ private:
+ std::function<void()> callback_;
+};
+
+} // namespace
+
+class CrossThreadPersistentTest : public testing::TestWithHeap {};
+
+TEST_F(CrossThreadPersistentTest, RetainStronglyOnDifferentThread) {
+ subtle::CrossThreadPersistent<GCed> holder =
+ MakeGarbageCollected<GCed>(GetAllocationHandle());
+ {
+ Runner runner([obj = std::move(holder)]() {});
+ EXPECT_FALSE(holder);
+ EXPECT_EQ(0u, GCed::destructor_call_count);
+ PreciseGC();
+ EXPECT_EQ(0u, GCed::destructor_call_count);
+ runner.StartSynchronously();
+ runner.Join();
+ }
+ EXPECT_EQ(0u, GCed::destructor_call_count);
+ PreciseGC();
+ EXPECT_EQ(1u, GCed::destructor_call_count);
+}
+
+TEST_F(CrossThreadPersistentTest, RetainWeaklyOnDifferentThread) {
+ subtle::WeakCrossThreadPersistent<GCed> in =
+ MakeGarbageCollected<GCed>(GetAllocationHandle());
+ // Set up |out| with an object that is always retained to ensure that the
+ // different thread indeed moves back an empty handle.
+ Persistent<GCed> out_holder =
+ MakeGarbageCollected<GCed>(GetAllocationHandle());
+ subtle::WeakCrossThreadPersistent<GCed> out = *out_holder;
+ {
+ Persistent<GCed> temporary_holder = *in;
+ Runner runner([obj = std::move(in), &out]() { out = std::move(obj); });
+ EXPECT_FALSE(in);
+ EXPECT_TRUE(out);
+ EXPECT_EQ(0u, GCed::destructor_call_count);
+ PreciseGC();
+ EXPECT_EQ(0u, GCed::destructor_call_count);
+ temporary_holder.Clear();
+ PreciseGC();
+ EXPECT_EQ(1u, GCed::destructor_call_count);
+ runner.StartSynchronously();
+ runner.Join();
+ }
+ EXPECT_FALSE(out);
+}
+
+TEST_F(CrossThreadPersistentTest, DestroyRacingWithGC) {
+ // Destroy a handle on a different thread while at the same time invoking a
+ // garbage collection on the original thread.
+ subtle::CrossThreadPersistent<GCed> holder =
+ MakeGarbageCollected<GCed>(GetAllocationHandle());
+ Runner runner([&obj = holder]() { obj.Clear(); });
+ EXPECT_TRUE(holder);
+ runner.StartSynchronously();
+ PreciseGC();
+ runner.Join();
+ EXPECT_FALSE(holder);
+}
+
+} // namespace internal
+} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/custom-spaces-unittest.cc b/deps/v8/test/unittests/heap/cppgc/custom-spaces-unittest.cc
index 24e7367f67..7e73a73178 100644
--- a/deps/v8/test/unittests/heap/cppgc/custom-spaces-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/custom-spaces-unittest.cc
@@ -37,8 +37,9 @@ class TestWithHeapWithCustomSpaces : public testing::TestWithPlatform {
}
void PreciseGC() {
- heap_->ForceGarbageCollectionSlow("TestWithHeapWithCustomSpaces", "Testing",
- cppgc::Heap::StackState::kNoHeapPointers);
+ heap_->ForceGarbageCollectionSlow(
+ ::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ "Testing", cppgc::Heap::StackState::kNoHeapPointers);
}
cppgc::Heap* GetHeap() const { return heap_.get(); }
@@ -140,4 +141,106 @@ TEST_F(TestWithHeapWithCustomSpaces, SweepCustomSpace) {
}
} // namespace internal
+
+// Test custom space compactability.
+
+class CompactableCustomSpace : public CustomSpace<CompactableCustomSpace> {
+ public:
+ static constexpr size_t kSpaceIndex = 0;
+ static constexpr bool kSupportsCompaction = true;
+};
+
+class NotCompactableCustomSpace
+ : public CustomSpace<NotCompactableCustomSpace> {
+ public:
+ static constexpr size_t kSpaceIndex = 1;
+ static constexpr bool kSupportsCompaction = false;
+};
+
+class DefaultCompactableCustomSpace
+ : public CustomSpace<DefaultCompactableCustomSpace> {
+ public:
+ static constexpr size_t kSpaceIndex = 2;
+ // By default space are not compactable.
+};
+
+namespace internal {
+namespace {
+
+class TestWithHeapWithCompactableCustomSpaces
+ : public testing::TestWithPlatform {
+ protected:
+ TestWithHeapWithCompactableCustomSpaces() {
+ Heap::HeapOptions options;
+ options.custom_spaces.emplace_back(
+ std::make_unique<CompactableCustomSpace>());
+ options.custom_spaces.emplace_back(
+ std::make_unique<NotCompactableCustomSpace>());
+ options.custom_spaces.emplace_back(
+ std::make_unique<DefaultCompactableCustomSpace>());
+ heap_ = Heap::Create(platform_, std::move(options));
+ g_destructor_callcount = 0;
+ }
+
+ void PreciseGC() {
+ heap_->ForceGarbageCollectionSlow("TestWithHeapWithCompactableCustomSpaces",
+ "Testing",
+ cppgc::Heap::StackState::kNoHeapPointers);
+ }
+
+ cppgc::Heap* GetHeap() const { return heap_.get(); }
+
+ private:
+ std::unique_ptr<cppgc::Heap> heap_;
+};
+
+class CompactableGCed final : public GarbageCollected<CompactableGCed> {
+ public:
+ void Trace(Visitor*) const {}
+};
+class NotCompactableGCed final : public GarbageCollected<NotCompactableGCed> {
+ public:
+ void Trace(Visitor*) const {}
+};
+class DefaultCompactableGCed final
+ : public GarbageCollected<DefaultCompactableGCed> {
+ public:
+ void Trace(Visitor*) const {}
+};
+
+} // namespace
+} // namespace internal
+
+template <>
+struct SpaceTrait<internal::CompactableGCed> {
+ using Space = CompactableCustomSpace;
+};
+template <>
+struct SpaceTrait<internal::NotCompactableGCed> {
+ using Space = NotCompactableCustomSpace;
+};
+template <>
+struct SpaceTrait<internal::DefaultCompactableGCed> {
+ using Space = DefaultCompactableCustomSpace;
+};
+
+namespace internal {
+
+TEST_F(TestWithHeapWithCompactableCustomSpaces,
+ AllocateOnCompactableCustomSpaces) {
+ auto* compactable =
+ MakeGarbageCollected<CompactableGCed>(GetHeap()->GetAllocationHandle());
+ auto* not_compactable = MakeGarbageCollected<NotCompactableGCed>(
+ GetHeap()->GetAllocationHandle());
+ auto* default_compactable = MakeGarbageCollected<DefaultCompactableGCed>(
+ GetHeap()->GetAllocationHandle());
+ EXPECT_TRUE(NormalPage::FromPayload(compactable)->space()->is_compactable());
+ EXPECT_FALSE(
+ NormalPage::FromPayload(not_compactable)->space()->is_compactable());
+ EXPECT_FALSE(
+ NormalPage::FromPayload(default_compactable)->space()->is_compactable());
+}
+
+} // namespace internal
+
} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/ephemeron-pair-unittest.cc b/deps/v8/test/unittests/heap/cppgc/ephemeron-pair-unittest.cc
new file mode 100644
index 0000000000..1172eedb86
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/ephemeron-pair-unittest.cc
@@ -0,0 +1,112 @@
+// 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/ephemeron-pair.h"
+
+#include "include/cppgc/allocation.h"
+#include "include/cppgc/persistent.h"
+#include "src/heap/cppgc/heap-object-header.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 GCed : public GarbageCollected<GCed> {
+ public:
+ void Trace(cppgc::Visitor*) const {}
+};
+
+class EphemeronHolder : public GarbageCollected<GCed> {
+ public:
+ EphemeronHolder(GCed* key, GCed* value) : ephemeron_pair_(key, value) {}
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(ephemeron_pair_); }
+
+ private:
+ EphemeronPair<GCed, GCed> ephemeron_pair_;
+};
+
+class EhpemeronPairTest : public testing::TestWithHeap {
+ using MarkingConfig = Marker::MarkingConfig;
+
+ static constexpr Marker::MarkingConfig IncrementalPreciseMarkingConfig = {
+ MarkingConfig::CollectionType::kMajor,
+ MarkingConfig::StackState::kNoHeapPointers,
+ MarkingConfig::MarkingType::kIncremental};
+
+ public:
+ void FinishSteps() {
+ while (!SingleStep()) {
+ }
+ }
+
+ void FinishMarking() {
+ marker_->FinishMarking(MarkingConfig::StackState::kNoHeapPointers);
+ // Pretend do finish sweeping as StatsCollector verifies that Notify*
+ // methods are called in the right order.
+ Heap::From(GetHeap())->stats_collector()->NotifySweepingCompleted();
+ }
+
+ void InitializeMarker(HeapBase& heap, cppgc::Platform* platform) {
+ marker_ = MarkerFactory::CreateAndStartMarking<Marker>(
+ heap, platform, IncrementalPreciseMarkingConfig);
+ }
+
+ Marker* marker() const { return marker_.get(); }
+
+ private:
+ bool SingleStep() {
+ return marker_->IncrementalMarkingStepForTesting(
+ MarkingConfig::StackState::kNoHeapPointers);
+ }
+
+ std::unique_ptr<Marker> marker_;
+};
+
+// static
+constexpr Marker::MarkingConfig
+ EhpemeronPairTest::IncrementalPreciseMarkingConfig;
+
+} // namespace
+
+TEST_F(EhpemeronPairTest, ValueMarkedWhenKeyIsMarked) {
+ GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ Persistent<EphemeronHolder> holder =
+ MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
+ HeapObjectHeader::FromPayload(key).TryMarkAtomic();
+ InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
+ FinishMarking();
+ EXPECT_TRUE(HeapObjectHeader::FromPayload(value).IsMarked());
+}
+
+TEST_F(EhpemeronPairTest, ValueNotMarkedWhenKeyIsNotMarked) {
+ GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ Persistent<EphemeronHolder> holder =
+ MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
+ InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
+ FinishMarking();
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(key).IsMarked());
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(value).IsMarked());
+}
+
+TEST_F(EhpemeronPairTest, ValueNotMarkedBeforeKey) {
+ GCed* key = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ GCed* value = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ Persistent<EphemeronHolder> holder =
+ MakeGarbageCollected<EphemeronHolder>(GetAllocationHandle(), key, value);
+ InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get());
+ FinishSteps();
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(value).IsMarked());
+ HeapObjectHeader::FromPayload(key).TryMarkAtomic();
+ FinishMarking();
+ EXPECT_TRUE(HeapObjectHeader::FromPayload(value).IsMarked());
+}
+
+} // namespace internal
+} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/gc-info-unittest.cc b/deps/v8/test/unittests/heap/cppgc/gc-info-unittest.cc
index b45de5cd06..9c48621e10 100644
--- a/deps/v8/test/unittests/heap/cppgc/gc-info-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/gc-info-unittest.cc
@@ -14,6 +14,12 @@
namespace cppgc {
namespace internal {
+namespace {
+
+constexpr GCInfo GetEmptyGCInfo() { return {nullptr, nullptr, nullptr, false}; }
+
+} // namespace
+
TEST(GCInfoTableTest, InitialEmpty) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
@@ -23,7 +29,7 @@ TEST(GCInfoTableTest, InitialEmpty) {
TEST(GCInfoTableTest, ResizeToMaxIndex) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
- GCInfo info = {nullptr, nullptr, false};
+ GCInfo info = GetEmptyGCInfo();
for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex;
i++) {
GCInfoIndex index = table.RegisterNewGCInfo(info);
@@ -34,7 +40,7 @@ TEST(GCInfoTableTest, ResizeToMaxIndex) {
TEST(GCInfoTableDeathTest, MoreThanMaxIndexInfos) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
- GCInfo info = {nullptr, nullptr, false};
+ GCInfo info = GetEmptyGCInfo();
// Create GCInfoTable::kMaxIndex entries.
for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex;
i++) {
@@ -46,7 +52,7 @@ TEST(GCInfoTableDeathTest, MoreThanMaxIndexInfos) {
TEST(GCInfoTableDeathTest, OldTableAreaIsReadOnly) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
- GCInfo info = {nullptr, nullptr, false};
+ GCInfo info = GetEmptyGCInfo();
// Use up all slots until limit.
GCInfoIndex limit = table.LimitForTesting();
// Bail out if initial limit is already the maximum because of large committed
@@ -76,7 +82,7 @@ class ThreadRegisteringGCInfoObjects final : public v8::base::Thread {
num_registrations_(num_registrations) {}
void Run() final {
- GCInfo info = {nullptr, nullptr, false};
+ GCInfo info = GetEmptyGCInfo();
for (GCInfoIndex i = 0; i < num_registrations_; i++) {
table_->RegisterNewGCInfo(info);
}
@@ -101,7 +107,7 @@ TEST(GCInfoTableTest, MultiThreadedResizeToMaxIndex) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
- GCInfo info = {nullptr, nullptr, false};
+ GCInfo info = GetEmptyGCInfo();
for (size_t i = 0; i < main_thread_initialized; i++) {
table.RegisterNewGCInfo(info);
}
diff --git a/deps/v8/test/unittests/heap/cppgc/gc-invoker-unittest.cc b/deps/v8/test/unittests/heap/cppgc/gc-invoker-unittest.cc
index 9dc1b8d426..319f6e433d 100644
--- a/deps/v8/test/unittests/heap/cppgc/gc-invoker-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/gc-invoker-unittest.cc
@@ -105,7 +105,7 @@ TEST(GCInvokerTest, ConservativeGCIsInvokedAsPreciseGCViaPlatform) {
EXPECT_CALL(gc, epoch).WillRepeatedly(::testing::Return(0));
EXPECT_CALL(gc, CollectGarbage);
invoker.CollectGarbage(GarbageCollector::Config::ConservativeAtomicConfig());
- platform.WaitAllForegroundTasks();
+ platform.RunAllForegroundTasks();
}
TEST(GCInvokerTest, IncrementalGCIsStarted) {
diff --git a/deps/v8/test/unittests/heap/cppgc/heap-object-header-unittest.cc b/deps/v8/test/unittests/heap/cppgc/heap-object-header-unittest.cc
index 71df62a72b..2621af2891 100644
--- a/deps/v8/test/unittests/heap/cppgc/heap-object-header-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/heap-object-header-unittest.cc
@@ -40,8 +40,7 @@ TEST(HeapObjectHeaderTest, GetGCInfoIndex) {
constexpr size_t kSize = kAllocationGranularity;
HeapObjectHeader header(kSize, kGCInfoIndex);
EXPECT_EQ(kGCInfoIndex, header.GetGCInfoIndex());
- EXPECT_EQ(kGCInfoIndex,
- header.GetGCInfoIndex<HeapObjectHeader::AccessMode::kAtomic>());
+ EXPECT_EQ(kGCInfoIndex, header.GetGCInfoIndex<AccessMode::kAtomic>());
}
TEST(HeapObjectHeaderTest, GetSize) {
@@ -49,7 +48,7 @@ TEST(HeapObjectHeaderTest, GetSize) {
constexpr size_t kSize = kAllocationGranularity * 23;
HeapObjectHeader header(kSize, kGCInfoIndex);
EXPECT_EQ(kSize, header.GetSize());
- EXPECT_EQ(kSize, header.GetSize<HeapObjectHeader::AccessMode::kAtomic>());
+ EXPECT_EQ(kSize, header.GetSize<AccessMode::kAtomic>());
}
TEST(HeapObjectHeaderTest, IsLargeObject) {
@@ -57,13 +56,10 @@ TEST(HeapObjectHeaderTest, IsLargeObject) {
constexpr size_t kSize = kAllocationGranularity * 23;
HeapObjectHeader header(kSize, kGCInfoIndex);
EXPECT_EQ(false, header.IsLargeObject());
- EXPECT_EQ(false,
- header.IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>());
+ EXPECT_EQ(false, header.IsLargeObject<AccessMode::kAtomic>());
HeapObjectHeader large_header(0, kGCInfoIndex + 1);
EXPECT_EQ(true, large_header.IsLargeObject());
- EXPECT_EQ(
- true,
- large_header.IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>());
+ EXPECT_EQ(true, large_header.IsLargeObject<AccessMode::kAtomic>());
}
TEST(HeapObjectHeaderTest, MarkObjectAsFullyConstructed) {
@@ -110,7 +106,7 @@ TEST(HeapObjectHeaderTest, Unmark) {
EXPECT_FALSE(header2.IsMarked());
EXPECT_TRUE(header2.TryMarkAtomic());
EXPECT_TRUE(header2.IsMarked());
- header2.Unmark<HeapObjectHeader::AccessMode::kAtomic>();
+ header2.Unmark<AccessMode::kAtomic>();
// GCInfoIndex shares the same bitfield and should be unaffected by Unmark.
EXPECT_EQ(kGCInfoIndex, header2.GetGCInfoIndex());
EXPECT_FALSE(header2.IsMarked());
@@ -130,7 +126,7 @@ class ConcurrentGCThread final : public v8::base::Thread {
payload_(payload) {}
void Run() final {
- while (header_->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>()) {
+ while (header_->IsInConstruction<AccessMode::kAtomic>()) {
}
USE(v8::base::AsAtomicPtr(const_cast<size_t*>(&payload_->value))
->load(std::memory_order_relaxed));
diff --git a/deps/v8/test/unittests/heap/cppgc/heap-unittest.cc b/deps/v8/test/unittests/heap/cppgc/heap-unittest.cc
index 0357b24ec0..694d031dda 100644
--- a/deps/v8/test/unittests/heap/cppgc/heap-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/heap-unittest.cc
@@ -117,5 +117,41 @@ TEST_F(GCHeapTest, ObjectPayloadSize) {
EXPECT_LE(expected_size, Heap::From(GetHeap())->ObjectPayloadSize());
}
+TEST_F(GCHeapTest, AllocateWithAdditionalBytes) {
+ static constexpr size_t kBaseSize = sizeof(HeapObjectHeader) + sizeof(Foo);
+ static constexpr size_t kAdditionalBytes = 10u * kAllocationGranularity;
+ {
+ Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle());
+ EXPECT_LE(kBaseSize, HeapObjectHeader::FromPayload(object).GetSize());
+ }
+ {
+ Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle(),
+ AdditionalBytes(kAdditionalBytes));
+ EXPECT_LE(kBaseSize + kAdditionalBytes,
+ HeapObjectHeader::FromPayload(object).GetSize());
+ }
+ {
+ Foo* object = MakeGarbageCollected<Foo>(
+ GetAllocationHandle(),
+ AdditionalBytes(kAdditionalBytes * kAdditionalBytes));
+ EXPECT_LE(kBaseSize + kAdditionalBytes * kAdditionalBytes,
+ HeapObjectHeader::FromPayload(object).GetSize());
+ }
+}
+
+TEST_F(GCHeapTest, AllocatedSizeDependOnAdditionalBytes) {
+ static constexpr size_t kAdditionalBytes = 10u * kAllocationGranularity;
+ Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle());
+ Foo* object_with_bytes = MakeGarbageCollected<Foo>(
+ GetAllocationHandle(), AdditionalBytes(kAdditionalBytes));
+ Foo* object_with_more_bytes = MakeGarbageCollected<Foo>(
+ GetAllocationHandle(),
+ AdditionalBytes(kAdditionalBytes * kAdditionalBytes));
+ EXPECT_LT(HeapObjectHeader::FromPayload(object).GetSize(),
+ HeapObjectHeader::FromPayload(object_with_bytes).GetSize());
+ EXPECT_LT(HeapObjectHeader::FromPayload(object_with_bytes).GetSize(),
+ HeapObjectHeader::FromPayload(object_with_more_bytes).GetSize());
+}
+
} // namespace internal
} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/marker-unittest.cc b/deps/v8/test/unittests/heap/cppgc/marker-unittest.cc
index b879d9b989..2574db151f 100644
--- a/deps/v8/test/unittests/heap/cppgc/marker-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/marker-unittest.cc
@@ -28,7 +28,6 @@ class MarkerTest : public testing::TestWithHeap {
auto* heap = Heap::From(GetHeap());
InitializeMarker(*heap, GetPlatformHandle().get(), config);
marker_->FinishMarking(stack_state);
- marker_->ProcessWeakness();
// Pretend do finish sweeping as StatsCollector verifies that Notify*
// methods are called in the right order.
heap->stats_collector()->NotifySweepingCompleted();
@@ -234,7 +233,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedEmptyStack) {
Member<GCedWithCallback> member(obj);
marker->VisitorForTesting().Trace(member);
});
- EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(object).IsMarked());
marker()->FinishMarking(MarkingConfig::StackState::kMayContainHeapPointers);
EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
}
@@ -248,7 +247,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) {
GetAllocationHandle(), [marker = marker()](GCedWithCallback* obj) {
Member<GCedWithCallback> member(obj);
marker->VisitorForTesting().Trace(member);
- EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(obj).IsMarked());
marker->FinishMarking(
MarkingConfig::StackState::kMayContainHeapPointers);
EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
@@ -258,14 +257,20 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) {
TEST_F(MarkerTest, SentinelNotClearedOnWeakPersistentHandling) {
static const Marker::MarkingConfig config = {
MarkingConfig::CollectionType::kMajor,
- MarkingConfig::StackState::kNoHeapPointers};
- InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get(), config);
+ MarkingConfig::StackState::kNoHeapPointers,
+ MarkingConfig::MarkingType::kIncremental};
Persistent<GCed> root = MakeGarbageCollected<GCed>(GetAllocationHandle());
auto* tmp = MakeGarbageCollected<GCed>(GetAllocationHandle());
root->SetWeakChild(tmp);
- marker()->FinishMarking(MarkingConfig::StackState::kNoHeapPointers);
+ InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get(), config);
+ while (!marker()->IncrementalMarkingStepForTesting(
+ MarkingConfig::StackState::kNoHeapPointers)) {
+ }
+ // {root} object must be marked at this point because we do not allow
+ // encountering kSentinelPointer in WeakMember on regular Trace() calls.
+ ASSERT_TRUE(HeapObjectHeader::FromPayload(root.Get()).IsMarked());
root->SetWeakChild(kSentinelPointer);
- marker()->ProcessWeakness();
+ marker()->FinishMarking(MarkingConfig::StackState::kNoHeapPointers);
EXPECT_EQ(kSentinelPointer, root->weak_child());
}
@@ -290,7 +295,6 @@ class IncrementalMarkingTest : public testing::TestWithHeap {
void FinishMarking() {
marker_->FinishMarking(MarkingConfig::StackState::kMayContainHeapPointers);
- marker_->ProcessWeakness();
// Pretend do finish sweeping as StatsCollector verifies that Notify*
// methods are called in the right order.
Heap::From(GetHeap())->stats_collector()->NotifySweepingCompleted();
@@ -372,7 +376,7 @@ TEST_F(IncrementalMarkingTest, IncrementalStepDuringAllocation) {
holder->member_ = obj;
EXPECT_FALSE(header->IsMarked());
FinishSteps(MarkingConfig::StackState::kMayContainHeapPointers);
- EXPECT_TRUE(header->IsMarked());
+ EXPECT_FALSE(header->IsMarked());
});
FinishSteps(MarkingConfig::StackState::kNoHeapPointers);
EXPECT_TRUE(header->IsMarked());
diff --git a/deps/v8/test/unittests/heap/cppgc/marking-verifier-unittest.cc b/deps/v8/test/unittests/heap/cppgc/marking-verifier-unittest.cc
index fb5ba772da..603a47399b 100644
--- a/deps/v8/test/unittests/heap/cppgc/marking-verifier-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/marking-verifier-unittest.cc
@@ -23,7 +23,8 @@ class MarkingVerifierTest : public testing::TestWithHeap {
void VerifyMarking(HeapBase& heap, StackState stack_state) {
Heap::From(GetHeap())->object_allocator().ResetLinearAllocationBuffers();
- MarkingVerifier verifier(heap, stack_state);
+ MarkingVerifier verifier(heap);
+ verifier.Run(stack_state);
}
};
@@ -98,6 +99,48 @@ TEST_F(MarkingVerifierTest, DoesntDieOnInConstructionOnObject) {
});
}
+namespace {
+class GCedWithCallbackAndChild final
+ : public GarbageCollected<GCedWithCallbackAndChild> {
+ public:
+ template <typename Callback>
+ GCedWithCallbackAndChild(GCed* gced, Callback callback) : child_(gced) {
+ callback(this);
+ }
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }
+
+ private:
+ Member<GCed> child_;
+};
+
+template <typename T>
+struct Holder : public GarbageCollected<Holder<T>> {
+ public:
+ void Trace(cppgc::Visitor* visitor) const { visitor->Trace(object); }
+ Member<T> object = nullptr;
+};
+} // namespace
+
+TEST_F(MarkingVerifierTest, DoesntDieOnInConstructionObjectWithWriteBarrier) {
+ // Regression test: https://crbug.com/v8/10989.
+ // GCedWithCallbackAndChild is marked by write barrier and then discarded by
+ // FlushNotFullyConstructedObjects because it is already marked.
+ Persistent<Holder<GCedWithCallbackAndChild>> persistent =
+ MakeGarbageCollected<Holder<GCedWithCallbackAndChild>>(
+ GetAllocationHandle());
+ GarbageCollector::Config config =
+ GarbageCollector::Config::PreciseIncrementalConfig();
+ Heap::From(GetHeap())->StartIncrementalGarbageCollection(config);
+ MakeGarbageCollected<GCedWithCallbackAndChild>(
+ GetAllocationHandle(), MakeGarbageCollected<GCed>(GetAllocationHandle()),
+ [&persistent](GCedWithCallbackAndChild* obj) {
+ persistent->object = obj;
+ });
+ GetMarkerRef()->IncrementalMarkingStepForTesting(
+ GarbageCollector::Config::StackState::kNoHeapPointers);
+ Heap::From(GetHeap())->FinalizeIncrementalGarbageCollectionIfRunning(config);
+}
+
// Death tests.
namespace {
diff --git a/deps/v8/test/unittests/heap/cppgc/marking-visitor-unittest.cc b/deps/v8/test/unittests/heap/cppgc/marking-visitor-unittest.cc
index 51387712c7..a411c7e63a 100644
--- a/deps/v8/test/unittests/heap/cppgc/marking-visitor-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/marking-visitor-unittest.cc
@@ -44,17 +44,20 @@ class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {
void Trace(cppgc::Visitor*) const override {}
};
-class TestMarkingVisitor : public MarkingVisitor {
+class TestMarkingVisitor : public MutatorMarkingVisitor {
public:
explicit TestMarkingVisitor(Marker* marker)
- : MarkingVisitor(marker->heap(), marker->MarkingStateForTesting()) {}
+ : MutatorMarkingVisitor(marker->heap(),
+ marker->MutatorMarkingStateForTesting()) {}
~TestMarkingVisitor() { marking_state_.Publish(); }
+
+ MarkingStateBase& marking_state() { return marking_state_; }
};
} // namespace
TEST_F(MarkingVisitorTest, MarkedBytesAreInitiallyZero) {
- EXPECT_EQ(0u, GetMarker()->MarkingStateForTesting().marked_bytes());
+ EXPECT_EQ(0u, GetMarker()->MutatorMarkingStateForTesting().marked_bytes());
}
// Strong references are marked.
@@ -216,7 +219,10 @@ TEST_F(MarkingVisitorTest, MarkMemberInConstruction) {
Member<GCedWithInConstructionCallback> object(obj);
visitor.Trace(object);
});
- EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_TRUE(visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkingVisitorTest, MarkMemberMixinInConstruction) {
@@ -228,7 +234,10 @@ TEST_F(MarkingVisitorTest, MarkMemberMixinInConstruction) {
Member<MixinWithInConstructionCallback> mixin(obj);
visitor.Trace(mixin);
});
- EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_TRUE(visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) {
@@ -240,7 +249,11 @@ TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) {
WeakMember<GCedWithInConstructionCallback> object(obj);
visitor.Trace(object);
});
- EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_FALSE(
+ visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixinInConstruction) {
@@ -252,7 +265,11 @@ TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixinInConstruction) {
WeakMember<MixinWithInConstructionCallback> mixin(obj);
visitor.Trace(mixin);
});
- EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_FALSE(
+ visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkingVisitorTest, MarkPersistentInConstruction) {
@@ -264,7 +281,10 @@ TEST_F(MarkingVisitorTest, MarkPersistentInConstruction) {
Persistent<GCedWithInConstructionCallback> object(obj);
visitor.TraceRootForTesting(object, SourceLocation::Current());
});
- EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_TRUE(visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkingVisitorTest, MarkPersistentMixinInConstruction) {
@@ -276,7 +296,23 @@ TEST_F(MarkingVisitorTest, MarkPersistentMixinInConstruction) {
Persistent<MixinWithInConstructionCallback> mixin(obj);
visitor.TraceRootForTesting(mixin, SourceLocation::Current());
});
- EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(gced);
+ EXPECT_TRUE(visitor.marking_state().not_fully_constructed_worklist().Contains(
+ &header));
+ EXPECT_FALSE(header.IsMarked());
+}
+
+TEST_F(MarkingVisitorTest, StrongTracingMarksWeakMember) {
+ WeakMember<GCed> object(MakeGarbageCollected<GCed>(GetAllocationHandle()));
+ HeapObjectHeader& header = HeapObjectHeader::FromPayload(object);
+
+ TestMarkingVisitor visitor(GetMarker());
+
+ EXPECT_FALSE(header.IsMarked());
+
+ visitor.TraceStrongly(object);
+
+ EXPECT_TRUE(header.IsMarked());
}
} // namespace internal
diff --git a/deps/v8/test/unittests/heap/cppgc/name-trait-unittest.cc b/deps/v8/test/unittests/heap/cppgc/name-trait-unittest.cc
new file mode 100644
index 0000000000..57f100379d
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/name-trait-unittest.cc
@@ -0,0 +1,133 @@
+// 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/internal/name-trait.h"
+
+#include "include/cppgc/allocation.h"
+#include "include/cppgc/garbage-collected.h"
+#include "src/base/build_config.h"
+#include "test/unittests/heap/cppgc/tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cppgc {
+namespace internal {
+
+namespace {
+
+struct NoName : public GarbageCollected<NoName> {
+ virtual void Trace(Visitor*) const {}
+};
+
+struct OtherNoName : public GarbageCollected<OtherNoName> {
+ virtual void Trace(Visitor*) const {}
+};
+
+class ClassWithName final : public GarbageCollected<OtherNoName>,
+ public NameProvider {
+ public:
+ explicit ClassWithName(const char* name) : name_(name) {}
+ virtual void Trace(Visitor*) const {}
+ const char* GetName() const final { return name_; }
+
+ private:
+ const char* name_;
+};
+
+} // namespace
+
+TEST(NameTraitTest, InternalNamesHiddenInOfficialBuild) {
+ // Use a runtime test instead of static_assert to allow local builds but block
+ // enabling the feature accidentally through the waterfall.
+ //
+ // Do not include such type information in official builds to
+ // (a) save binary size on string literals, and
+ // (b) avoid exposing internal types until it has been clarified whether
+ // exposing internals in DevTools is fine.
+#if defined(OFFICIAL_BUILD)
+ EXPECT_TRUE(NameProvider::HideInternalNames());
+#endif
+}
+
+TEST(NameTraitTest, DefaultName) {
+ EXPECT_STREQ(NameProvider::HideInternalNames()
+ ? "InternalNode"
+ : "cppgc::internal::(anonymous namespace)::NoName",
+ NameTrait<NoName>::GetName(nullptr).value);
+ EXPECT_STREQ(NameProvider::HideInternalNames()
+ ? "InternalNode"
+ : "cppgc::internal::(anonymous namespace)::OtherNoName",
+ NameTrait<OtherNoName>::GetName(nullptr).value);
+}
+
+TEST(NameTraitTest, CustomName) {
+ ClassWithName with_name("CustomName");
+ const char* name = NameTrait<ClassWithName>::GetName(&with_name).value;
+ EXPECT_STREQ("CustomName", name);
+}
+
+namespace {
+
+class TraitTester : public NameTraitBase {
+ public:
+ // Expose type signature parser to allow testing various inputs.
+ using NameTraitBase::GetNameFromTypeSignature;
+};
+
+} // namespace
+
+TEST(NameTraitTest, NoTypeAvailable) {
+ HeapObjectName name = TraitTester::GetNameFromTypeSignature(nullptr);
+ EXPECT_STREQ(NameProvider::kNoNameDeducible, name.value);
+ EXPECT_TRUE(name.name_was_hidden);
+}
+
+TEST(NameTraitTest, ParsingPrettyFunction) {
+ // Test assumes that __PRETTY_FUNCTION__ and friends return a string
+ // containing the the type as [T = <type>].
+ HeapObjectName name = TraitTester::GetNameFromTypeSignature(
+ "Some signature of a method [T = ClassNameInSignature]");
+ EXPECT_STREQ("ClassNameInSignature", name.value);
+ EXPECT_FALSE(name.name_was_hidden);
+ // While object names are generally leaky, the test needs to be cleaned up
+ // gracefully.
+ delete[] name.value;
+}
+
+class HeapObjectHeaderNameTest : public testing::TestWithHeap {};
+
+TEST_F(HeapObjectHeaderNameTest, LookupNameThroughGCInfo) {
+ auto* no_name = MakeGarbageCollected<NoName>(GetAllocationHandle());
+ auto no_name_tuple = HeapObjectHeader::FromPayload(no_name).GetName();
+ if (NameProvider::HideInternalNames()) {
+ EXPECT_STREQ(NameProvider::kHiddenName, no_name_tuple.value);
+ EXPECT_TRUE(no_name_tuple.name_was_hidden);
+ } else {
+ EXPECT_STREQ("cppgc::internal::(anonymous namespace)::NoName",
+ no_name_tuple.value);
+ EXPECT_FALSE(no_name_tuple.name_was_hidden);
+ }
+
+ auto* other_no_name =
+ MakeGarbageCollected<OtherNoName>(GetAllocationHandle());
+ auto other_no_name_tuple =
+ HeapObjectHeader::FromPayload(other_no_name).GetName();
+ if (NameProvider::HideInternalNames()) {
+ EXPECT_STREQ(NameProvider::kHiddenName, no_name_tuple.value);
+ EXPECT_TRUE(no_name_tuple.name_was_hidden);
+ } else {
+ EXPECT_STREQ("cppgc::internal::(anonymous namespace)::OtherNoName",
+ other_no_name_tuple.value);
+ EXPECT_FALSE(other_no_name_tuple.name_was_hidden);
+ }
+
+ auto* class_with_name =
+ MakeGarbageCollected<ClassWithName>(GetAllocationHandle(), "CustomName");
+ auto class_with_name_tuple =
+ HeapObjectHeader::FromPayload(class_with_name).GetName();
+ EXPECT_STREQ("CustomName", class_with_name_tuple.value);
+ EXPECT_FALSE(class_with_name_tuple.name_was_hidden);
+}
+
+} // namespace internal
+} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/page-memory-unittest.cc b/deps/v8/test/unittests/heap/cppgc/page-memory-unittest.cc
index 6781dacd3d..6c8533c2f0 100644
--- a/deps/v8/test/unittests/heap/cppgc/page-memory-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/page-memory-unittest.cc
@@ -117,9 +117,16 @@ TEST(PageMemoryRegionTest, PlatformUsesGuardPages) {
v8::base::PageAllocator allocator;
#if defined(V8_HOST_ARCH_PPC64) && !defined(_AIX)
EXPECT_FALSE(SupportsCommittingGuardPages(&allocator));
-#else // !V8_HOST_ARCH_PPC64
+#elif defined(V8_HOST_ARCH_ARM64)
+ if (allocator.CommitPageSize() == 4096) {
+ EXPECT_TRUE(SupportsCommittingGuardPages(&allocator));
+ } else {
+ // Arm64 supports both 16k and 64k OS pages.
+ EXPECT_FALSE(SupportsCommittingGuardPages(&allocator));
+ }
+#else // Regular case.
EXPECT_TRUE(SupportsCommittingGuardPages(&allocator));
-#endif // !V8_HOST_ARCH_PPC64
+#endif
}
namespace {
diff --git a/deps/v8/test/unittests/heap/cppgc/persistent-unittest.cc b/deps/v8/test/unittests/heap/cppgc/persistent-family-unittest.cc
index b832bc9d23..ae6ee23625 100644
--- a/deps/v8/test/unittests/heap/cppgc/persistent-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/persistent-family-unittest.cc
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "include/cppgc/persistent.h"
-
#include <vector>
#include "include/cppgc/allocation.h"
+#include "include/cppgc/cross-thread-persistent.h"
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/internal/pointer-policies.h"
#include "include/cppgc/member.h"
+#include "include/cppgc/persistent.h"
+#include "include/cppgc/source-location.h"
#include "include/cppgc/type-traits.h"
#include "src/heap/cppgc/heap.h"
#include "src/heap/cppgc/liveness-broker.h"
@@ -33,11 +34,39 @@ struct DerivedGCed : GCed {
};
template <template <typename> class PersistentType>
+struct PersistentRegionTrait;
+
+template <>
+struct PersistentRegionTrait<Persistent> {
+ static PersistentRegion& Get(cppgc::Heap* heap) {
+ return internal::Heap::From(heap)->GetStrongPersistentRegion();
+ }
+};
+
+template <>
+struct PersistentRegionTrait<WeakPersistent> {
+ static PersistentRegion& Get(cppgc::Heap* heap) {
+ return internal::Heap::From(heap)->GetWeakPersistentRegion();
+ }
+};
+
+template <>
+struct PersistentRegionTrait<subtle::CrossThreadPersistent> {
+ static PersistentRegion& Get(cppgc::Heap* heap) {
+ return internal::Heap::From(heap)->GetStrongCrossThreadPersistentRegion();
+ }
+};
+
+template <>
+struct PersistentRegionTrait<subtle::WeakCrossThreadPersistent> {
+ static PersistentRegion& Get(cppgc::Heap* heap) {
+ return internal::Heap::From(heap)->GetWeakCrossThreadPersistentRegion();
+ }
+};
+
+template <template <typename> class PersistentType>
PersistentRegion& GetRegion(cppgc::Heap* heap) {
- auto* heap_impl = internal::Heap::From(heap);
- return IsWeak<PersistentType<GCed>>::value
- ? heap_impl->GetWeakPersistentRegion()
- : heap_impl->GetStrongPersistentRegion();
+ return PersistentRegionTrait<PersistentType>::Get(heap);
}
template <typename T>
@@ -46,6 +75,11 @@ using LocalizedPersistent =
internal::KeepLocationPolicy,
internal::DefaultCheckingPolicy>;
+template <typename T>
+using LocalizedCrossThreadPersistent = internal::BasicCrossThreadPersistent<
+ T, internal::StrongCrossThreadPersistentPolicy,
+ internal::KeepLocationPolicy, internal::DefaultCheckingPolicy>;
+
class RootVisitor final : public VisitorBase {
public:
RootVisitor() = default;
@@ -61,11 +95,12 @@ class RootVisitor final : public VisitorBase {
}
protected:
- void VisitRoot(const void* t, TraceDescriptor desc) final {
+ void VisitRoot(const void* t, TraceDescriptor desc,
+ const SourceLocation&) final {
desc.callback(this, desc.base_object_payload);
}
void VisitWeakRoot(const void*, TraceDescriptor, WeakCallback callback,
- const void* object) final {
+ const void* object, const SourceLocation&) final {
weak_callbacks_.emplace_back(callback, object);
}
@@ -112,6 +147,8 @@ TEST_F(PersistentTest, NullStateCtor) {
auto* heap = GetHeap();
NullStateCtor<Persistent>(heap);
NullStateCtor<WeakPersistent>(heap);
+ NullStateCtor<subtle::CrossThreadPersistent>(heap);
+ NullStateCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -136,6 +173,8 @@ TEST_F(PersistentTest, RawCtor) {
auto* heap = GetHeap();
RawCtor<Persistent>(heap);
RawCtor<WeakPersistent>(heap);
+ RawCtor<subtle::CrossThreadPersistent>(heap);
+ RawCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -191,6 +230,8 @@ TEST_F(PersistentTest, CopyCtor) {
auto* heap = GetHeap();
CopyCtor<Persistent>(heap);
CopyCtor<WeakPersistent>(heap);
+ CopyCtor<subtle::CrossThreadPersistent>(heap);
+ CopyCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -233,6 +274,8 @@ TEST_F(PersistentTest, MoveCtor) {
auto* heap = GetHeap();
MoveCtor<Persistent>(heap);
MoveCtor<WeakPersistent>(heap);
+ MoveCtor<subtle::CrossThreadPersistent>(heap);
+ MoveCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType,
@@ -258,10 +301,16 @@ TEST_F(PersistentTest, MemberCtor) {
MemberCtor<WeakPersistent, Member>(heap);
MemberCtor<WeakPersistent, WeakMember>(heap);
MemberCtor<WeakPersistent, UntracedMember>(heap);
+ MemberCtor<subtle::CrossThreadPersistent, Member>(heap);
+ MemberCtor<subtle::CrossThreadPersistent, WeakMember>(heap);
+ MemberCtor<subtle::CrossThreadPersistent, UntracedMember>(heap);
+ MemberCtor<subtle::WeakCrossThreadPersistent, Member>(heap);
+ MemberCtor<subtle::WeakCrossThreadPersistent, WeakMember>(heap);
+ MemberCtor<subtle::WeakCrossThreadPersistent, UntracedMember>(heap);
}
template <template <typename> class PersistentType>
-void NullStateAssignemnt(cppgc::Heap* heap) {
+void NullStateAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p =
@@ -292,8 +341,10 @@ void NullStateAssignemnt(cppgc::Heap* heap) {
TEST_F(PersistentTest, NullStateAssignemnt) {
auto* heap = GetHeap();
- NullStateAssignemnt<Persistent>(heap);
- NullStateAssignemnt<WeakPersistent>(heap);
+ NullStateAssignment<Persistent>(heap);
+ NullStateAssignment<WeakPersistent>(heap);
+ NullStateAssignment<subtle::CrossThreadPersistent>(heap);
+ NullStateAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -322,6 +373,8 @@ TEST_F(PersistentTest, RawAssignment) {
auto* heap = GetHeap();
RawAssignment<Persistent>(heap);
RawAssignment<WeakPersistent>(heap);
+ RawAssignment<subtle::CrossThreadPersistent>(heap);
+ RawAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -384,6 +437,8 @@ TEST_F(PersistentTest, CopyAssignment) {
auto* heap = GetHeap();
CopyAssignment<Persistent>(heap);
CopyAssignment<WeakPersistent>(heap);
+ CopyAssignment<subtle::CrossThreadPersistent>(heap);
+ CopyAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -444,6 +499,8 @@ TEST_F(PersistentTest, MoveAssignment) {
auto* heap = GetHeap();
MoveAssignment<Persistent>(heap);
MoveAssignment<WeakPersistent>(heap);
+ MoveAssignment<subtle::CrossThreadPersistent>(heap);
+ MoveAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType,
@@ -470,6 +527,12 @@ TEST_F(PersistentTest, MemberAssignment) {
MemberAssignment<WeakPersistent, Member>(heap);
MemberAssignment<WeakPersistent, WeakMember>(heap);
MemberAssignment<WeakPersistent, UntracedMember>(heap);
+ MemberAssignment<subtle::CrossThreadPersistent, Member>(heap);
+ MemberAssignment<subtle::CrossThreadPersistent, WeakMember>(heap);
+ MemberAssignment<subtle::CrossThreadPersistent, UntracedMember>(heap);
+ MemberAssignment<subtle::WeakCrossThreadPersistent, Member>(heap);
+ MemberAssignment<subtle::WeakCrossThreadPersistent, WeakMember>(heap);
+ MemberAssignment<subtle::WeakCrossThreadPersistent, UntracedMember>(heap);
}
template <template <typename> class PersistentType>
@@ -488,6 +551,8 @@ TEST_F(PersistentTest, Clear) {
auto* heap = GetHeap();
ClearTest<Persistent>(heap);
ClearTest<WeakPersistent>(heap);
+ ClearTest<subtle::CrossThreadPersistent>(heap);
+ ClearTest<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
@@ -507,6 +572,8 @@ TEST_F(PersistentTest, Release) {
auto* heap = GetHeap();
ReleaseTest<Persistent>(heap);
ReleaseTest<WeakPersistent>(heap);
+ ReleaseTest<subtle::CrossThreadPersistent>(heap);
+ ReleaseTest<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType1,
@@ -630,6 +697,10 @@ TEST_F(PersistentTest, ClearOnHeapDestruction) {
weak_persistent = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
const Persistent<GCed> persistent_sentinel(kSentinelPointer);
const WeakPersistent<GCed> weak_persistent_sentinel(kSentinelPointer);
+ const subtle::CrossThreadPersistent<GCed> cross_thread_persistent_sentinel(
+ kSentinelPointer);
+ const subtle::WeakCrossThreadPersistent<GCed>
+ cross_thread_weak_persistent_sentinel(kSentinelPointer);
heap.reset();
EXPECT_EQ(nullptr, persistent);
@@ -651,6 +722,14 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_EQ(expected_loc.Line() + 1, actual_loc.Line());
}
{
+ const auto expected_loc = SourceLocation::Current();
+ LocalizedCrossThreadPersistent<GCed> p = gced;
+ const auto actual_loc = p.Location();
+ EXPECT_STREQ(expected_loc.Function(), actual_loc.Function());
+ EXPECT_STREQ(expected_loc.FileName(), actual_loc.FileName());
+ EXPECT_EQ(expected_loc.Line() + 1, actual_loc.Line());
+ }
+ {
// Copy ctor doesn't copy source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2 = p1;
@@ -659,6 +738,14 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
+ // Copy ctor doesn't copy source location.
+ LocalizedCrossThreadPersistent<GCed> p1 = gced;
+ LocalizedCrossThreadPersistent<GCed> p2 = p1;
+ EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
+ EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
+ EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
+ }
+ {
// Copy assignment doesn't copy source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2;
@@ -668,6 +755,15 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
+ // Copy assignment doesn't copy source location.
+ LocalizedCrossThreadPersistent<GCed> p1 = gced;
+ LocalizedCrossThreadPersistent<GCed> p2;
+ p2 = p1;
+ EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
+ EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
+ EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
+ }
+ {
// Clearing doesn't clear source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2 = gced;
@@ -677,6 +773,15 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
+ // Clearing doesn't clear source location.
+ LocalizedCrossThreadPersistent<GCed> p1 = gced;
+ LocalizedCrossThreadPersistent<GCed> p2 = gced;
+ p2.Clear();
+ EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
+ EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
+ EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
+ }
+ {
LocalizedPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedPersistent<GCed> p2 = std::move(p1);
@@ -685,6 +790,14 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
{
+ LocalizedCrossThreadPersistent<GCed> p1 = gced;
+ const auto expected_loc = p1.Location();
+ LocalizedCrossThreadPersistent<GCed> p2 = std::move(p1);
+ EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
+ EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
+ EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
+ }
+ {
LocalizedPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedPersistent<GCed> p2;
@@ -693,8 +806,57 @@ TEST_F(PersistentTest, LocalizedPersistent) {
EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
+ {
+ LocalizedCrossThreadPersistent<GCed> p1 = gced;
+ const auto expected_loc = p1.Location();
+ LocalizedCrossThreadPersistent<GCed> p2;
+ p2 = std::move(p1);
+ EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
+ EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
+ EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
+ }
}
+
#endif
+namespace {
+
+class ExpectingLocationVisitor final : public VisitorBase {
+ public:
+ explicit ExpectingLocationVisitor(const SourceLocation& expected_location)
+ : expected_loc_(expected_location) {}
+
+ protected:
+ void VisitRoot(const void* t, TraceDescriptor desc,
+ const SourceLocation& loc) final {
+ EXPECT_STREQ(expected_loc_.Function(), loc.Function());
+ EXPECT_STREQ(expected_loc_.FileName(), loc.FileName());
+ EXPECT_EQ(expected_loc_.Line(), loc.Line());
+ }
+
+ private:
+ const SourceLocation& expected_loc_;
+};
+
+} // namespace
+
+TEST_F(PersistentTest, PersistentTraceLocation) {
+ GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
+ {
+#if CPPGC_SUPPORTS_SOURCE_LOCATION
+ // Baseline for creating expected location which has a different line
+ // number.
+ const auto loc = SourceLocation::Current();
+ const auto expected_loc =
+ SourceLocation::Current(loc.Function(), loc.FileName(), loc.Line() + 6);
+#else // !CCPPGC_SUPPORTS_SOURCE_LOCATION
+ const SourceLocation expected_loc;
+#endif // !CCPPGC_SUPPORTS_SOURCE_LOCATION
+ LocalizedPersistent<GCed> p = gced;
+ ExpectingLocationVisitor visitor(expected_loc);
+ visitor.TraceRootForTesting(p, p.Location());
+ }
+}
+
} // namespace internal
} // namespace cppgc
diff --git a/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc b/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc
index a4b50f8d30..0fff908b36 100644
--- a/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/stack-unittest.cc
@@ -261,6 +261,8 @@ TEST_F(GCStackTest, IteratePointersFindsParameterNesting7) {
EXPECT_TRUE(scanner->found());
}
+// Disabled on msvc, due to miscompilation, see https://crbug.com/v8/10658.
+#if !defined(_MSC_VER) || defined(__clang__)
TEST_F(GCStackTest, IteratePointersFindsParameterNesting8) {
auto scanner = std::make_unique<StackScanner>();
void* needle = RecursivelyPassOnParameter(8, scanner->needle(), GetStack(),
@@ -268,6 +270,7 @@ TEST_F(GCStackTest, IteratePointersFindsParameterNesting8) {
EXPECT_EQ(scanner->needle(), needle);
EXPECT_TRUE(scanner->found());
}
+#endif // !_MSC_VER || __clang__
// The following test uses inline assembly and has been checked to work on clang
// to verify that the stack-scanning trampoline pushes callee-saved registers.
diff --git a/deps/v8/test/unittests/heap/cppgc/sweeper-unittest.cc b/deps/v8/test/unittests/heap/cppgc/sweeper-unittest.cc
index 3591af29a4..8031deac41 100644
--- a/deps/v8/test/unittests/heap/cppgc/sweeper-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/sweeper-unittest.cc
@@ -48,7 +48,10 @@ class SweeperTest : public testing::TestWithHeap {
// methods are called in the right order.
heap->stats_collector()->NotifyMarkingStarted();
heap->stats_collector()->NotifyMarkingCompleted(0);
- sweeper.Start(Sweeper::Config::kAtomic);
+ const Sweeper::SweepingConfig sweeping_config{
+ Sweeper::SweepingConfig::SweepingType::kAtomic,
+ Sweeper::SweepingConfig::CompactableSpaceHandling::kSweep};
+ sweeper.Start(sweeping_config);
sweeper.FinishIfRunning();
}
diff --git a/deps/v8/test/unittests/heap/cppgc/test-platform.cc b/deps/v8/test/unittests/heap/cppgc/test-platform.cc
index c649b1e89d..2268d546b3 100644
--- a/deps/v8/test/unittests/heap/cppgc/test-platform.cc
+++ b/deps/v8/test/unittests/heap/cppgc/test-platform.cc
@@ -4,132 +4,29 @@
#include "test/unittests/heap/cppgc/test-platform.h"
+#include "include/libplatform/libplatform.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
-#include "src/heap/cppgc/default-job.h"
namespace cppgc {
namespace internal {
namespace testing {
-namespace {
-class TestJobThread final : public v8::base::Thread {
- public:
- using id = uint8_t;
-
- explicit TestJobThread(TestJob* job) : Thread(Options("job")), job_(job) {}
-
- void Run() final;
-
- static size_t GetMaxSupportedConcurrency() { return 4u; }
-
- private:
- TestJob* const job_;
-};
-} // namespace
-
-// Default implementation of Jobs based on std::thread.
-class TestJob final : public DefaultJobImpl<TestJobThread> {
- public:
- explicit TestJob(Key key, std::unique_ptr<cppgc::JobTask> job_task)
- : DefaultJobImpl(key, std::move(job_task)) {}
-
- std::shared_ptr<TestJobThread> CreateThread(DefaultJobImpl* job) final {
- std::shared_ptr<TestJobThread> thread =
- std::make_shared<TestJobThread>(this);
- const bool thread_started = thread->Start();
- USE(thread_started);
- DCHECK(thread_started);
- return thread;
- }
-};
-
-void TestJobThread::Run() {
- DCHECK_NOT_NULL(job_);
- job_->RunJobTask();
-}
-
-void TestTaskRunner::PostTask(std::unique_ptr<cppgc::Task> task) {
- tasks_.push_back(std::move(task));
-}
-
-void TestTaskRunner::PostNonNestableTask(std::unique_ptr<cppgc::Task> task) {
- PostTask(std::move(task));
-}
-
-void TestTaskRunner::PostDelayedTask(std::unique_ptr<cppgc::Task> task,
- double) {
- PostTask(std::move(task));
-}
-
-void TestTaskRunner::PostNonNestableDelayedTask(
- std::unique_ptr<cppgc::Task> task, double) {
- PostTask(std::move(task));
-}
-
-void TestTaskRunner::PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) {
- idle_tasks_.push_back(std::move(task));
-}
-
-bool TestTaskRunner::RunSingleTask() {
- if (!tasks_.size()) return false;
-
- tasks_.back()->Run();
- tasks_.pop_back();
-
- return true;
-}
-
-bool TestTaskRunner::RunSingleIdleTask(double deadline_in_seconds) {
- if (!idle_tasks_.size()) return false;
-
- idle_tasks_.back()->Run(deadline_in_seconds);
- idle_tasks_.pop_back();
-
- return true;
-}
-
-void TestTaskRunner::RunUntilIdle() {
- for (auto& task : tasks_) {
- task->Run();
- }
- tasks_.clear();
-
- for (auto& task : idle_tasks_) {
- task->Run(std::numeric_limits<double>::infinity());
- }
- idle_tasks_.clear();
-}
-
TestPlatform::TestPlatform()
- : foreground_task_runner_(std::make_unique<TestTaskRunner>()) {}
-
-TestPlatform::~TestPlatform() V8_NOEXCEPT { WaitAllBackgroundTasks(); }
+ : DefaultPlatform(0, DefaultPlatform::IdleTaskSupport::kEnabled) {}
std::unique_ptr<cppgc::JobHandle> TestPlatform::PostJob(
- cppgc::TaskPriority, std::unique_ptr<cppgc::JobTask> job_task) {
- if (AreBackgroundTasksDisabled()) return {};
-
- std::shared_ptr<TestJob> job =
- DefaultJobFactory<TestJob>::Create(std::move(job_task));
- jobs_.push_back(job);
- return std::make_unique<TestJob::JobHandle>(std::move(job));
-}
-
-double TestPlatform::MonotonicallyIncreasingTime() {
- return v8::base::TimeTicks::HighResolutionNow().ToInternalValue() /
- static_cast<double>(v8::base::Time::kMicrosecondsPerSecond);
-}
-
-void TestPlatform::WaitAllForegroundTasks() {
- foreground_task_runner_->RunUntilIdle();
+ cppgc::TaskPriority priority, std::unique_ptr<cppgc::JobTask> job_task) {
+ if (AreBackgroundTasksDisabled()) return nullptr;
+ return v8_platform_->PostJob(priority, std::move(job_task));
}
-void TestPlatform::WaitAllBackgroundTasks() {
- for (auto& job : jobs_) {
- job->Join();
+void TestPlatform::RunAllForegroundTasks() {
+ v8::platform::PumpMessageLoop(v8_platform_.get(), kNoIsolate);
+ if (GetForegroundTaskRunner()->IdleTasksEnabled()) {
+ v8::platform::RunIdleTasks(v8_platform_.get(), kNoIsolate,
+ std::numeric_limits<double>::max());
}
- jobs_.clear();
}
TestPlatform::DisableBackgroundTasksScope::DisableBackgroundTasksScope(
diff --git a/deps/v8/test/unittests/heap/cppgc/test-platform.h b/deps/v8/test/unittests/heap/cppgc/test-platform.h
index 1faa6efb40..d6a93f45c9 100644
--- a/deps/v8/test/unittests/heap/cppgc/test-platform.h
+++ b/deps/v8/test/unittests/heap/cppgc/test-platform.h
@@ -5,45 +5,14 @@
#ifndef V8_UNITTESTS_HEAP_CPPGC_TEST_PLATFORM_H_
#define V8_UNITTESTS_HEAP_CPPGC_TEST_PLATFORM_H_
-#include <memory>
-#include <vector>
-
-#include "include/cppgc/platform.h"
-#include "src/base/page-allocator.h"
-#include "src/base/platform/platform.h"
+#include "include/cppgc/default-platform.h"
+#include "src/base/compiler-specific.h"
namespace cppgc {
namespace internal {
namespace testing {
-class TestJob;
-
-class TestTaskRunner : public cppgc::TaskRunner {
- public:
- void PostTask(std::unique_ptr<cppgc::Task> task) override;
- void PostDelayedTask(std::unique_ptr<cppgc::Task> task, double) override;
-
- bool NonNestableTasksEnabled() const override { return true; }
- void PostNonNestableTask(std::unique_ptr<cppgc::Task> task) override;
-
- bool NonNestableDelayedTasksEnabled() const override { return true; }
- void PostNonNestableDelayedTask(std::unique_ptr<cppgc::Task> task,
- double) override;
-
- bool IdleTasksEnabled() override { return true; }
- void PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) override;
-
- bool RunSingleTask();
- bool RunSingleIdleTask(double duration_in_seconds);
-
- void RunUntilIdle();
-
- private:
- std::vector<std::unique_ptr<cppgc::Task>> tasks_;
- std::vector<std::unique_ptr<cppgc::IdleTask>> idle_tasks_;
-};
-
-class TestPlatform : public Platform {
+class TestPlatform : public DefaultPlatform {
public:
class DisableBackgroundTasksScope {
public:
@@ -55,32 +24,18 @@ class TestPlatform : public Platform {
};
TestPlatform();
- ~TestPlatform() V8_NOEXCEPT override;
- PageAllocator* GetPageAllocator() override { return &page_allocator_; }
-
- std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
- return foreground_task_runner_;
- }
-
- // TestPlatform does not support job priorities. All jobs would be assigned
- // the same priority regardless of the cppgc::TaskPriority parameter.
std::unique_ptr<cppgc::JobHandle> PostJob(
- cppgc::TaskPriority, std::unique_ptr<cppgc::JobTask> job_task) override;
-
- double MonotonicallyIncreasingTime() override;
+ cppgc::TaskPriority priority,
+ std::unique_ptr<cppgc::JobTask> job_task) final;
- void WaitAllForegroundTasks();
- void WaitAllBackgroundTasks();
+ void RunAllForegroundTasks();
private:
bool AreBackgroundTasksDisabled() const {
return disabled_background_tasks_ > 0;
}
- v8::base::PageAllocator page_allocator_;
- std::shared_ptr<TestTaskRunner> foreground_task_runner_;
- std::vector<std::shared_ptr<TestJob>> jobs_;
size_t disabled_background_tasks_ = 0;
};
diff --git a/deps/v8/test/unittests/heap/cppgc/tests.h b/deps/v8/test/unittests/heap/cppgc/tests.h
index 175116d985..ac445c3370 100644
--- a/deps/v8/test/unittests/heap/cppgc/tests.h
+++ b/deps/v8/test/unittests/heap/cppgc/tests.h
@@ -33,8 +33,9 @@ class TestWithHeap : public TestWithPlatform {
TestWithHeap();
void PreciseGC() {
- heap_->ForceGarbageCollectionSlow("TestWithHeap", "Testing",
- cppgc::Heap::StackState::kNoHeapPointers);
+ heap_->ForceGarbageCollectionSlow(
+ ::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ "Testing", cppgc::Heap::StackState::kNoHeapPointers);
}
cppgc::Heap* GetHeap() const { return heap_.get(); }
diff --git a/deps/v8/test/unittests/heap/cppgc/weak-container-unittest.cc b/deps/v8/test/unittests/heap/cppgc/weak-container-unittest.cc
new file mode 100644
index 0000000000..d21f4249b3
--- /dev/null
+++ b/deps/v8/test/unittests/heap/cppgc/weak-container-unittest.cc
@@ -0,0 +1,184 @@
+// 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
diff --git a/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc b/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
index b06083d1ef..5673d47c8c 100644
--- a/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
@@ -43,9 +43,10 @@ class ExpectWriteBarrierFires final : private IncrementalMarkingScope {
ExpectWriteBarrierFires(MarkerBase* marker,
std::initializer_list<void*> objects)
: IncrementalMarkingScope(marker),
- marking_worklist_(marker->MarkingStateForTesting().marking_worklist()),
+ marking_worklist_(
+ marker->MutatorMarkingStateForTesting().marking_worklist()),
write_barrier_worklist_(
- marker->MarkingStateForTesting().write_barrier_worklist()),
+ marker->MutatorMarkingStateForTesting().write_barrier_worklist()),
objects_(objects) {
EXPECT_TRUE(marking_worklist_.IsGlobalEmpty());
EXPECT_TRUE(write_barrier_worklist_.IsGlobalEmpty());
@@ -92,9 +93,10 @@ class ExpectNoWriteBarrierFires final : private IncrementalMarkingScope {
ExpectNoWriteBarrierFires(MarkerBase* marker,
std::initializer_list<void*> objects)
: IncrementalMarkingScope(marker),
- marking_worklist_(marker->MarkingStateForTesting().marking_worklist()),
+ marking_worklist_(
+ marker->MutatorMarkingStateForTesting().marking_worklist()),
write_barrier_worklist_(
- marker->MarkingStateForTesting().write_barrier_worklist()) {
+ marker->MutatorMarkingStateForTesting().write_barrier_worklist()) {
EXPECT_TRUE(marking_worklist_.IsGlobalEmpty());
EXPECT_TRUE(write_barrier_worklist_.IsGlobalEmpty());
for (void* object : objects) {