Commit 872e315b authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

cppgc: Add allocation support for managed types

This CL adds basic infrastructure for:
- MakeGarbageCollected
- GarbageCollected and related type traits
- Heap (API / internal)
- Basic allocation based on malloc
- CollectGarbage without marking

This allows for allocation and reclamation through an explicit GC
call. No objects are held alive from any source (stack, globals,
refs), yet.

The exact wiring of platform is future work.

Change-Id: I81b7c0ba7b525188f8c0bf9de3b7af35d34322af
Bug: chromium:1056170
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2120538
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66887}
parent 77b5390b
......@@ -3930,15 +3930,22 @@ v8_source_set("cppgc_base") {
"include/cppgc/allocation.h",
"include/cppgc/api-constants.h",
"include/cppgc/finalizer-trait.h",
"include/cppgc/garbage-collected.h",
"include/cppgc/gc-info.h",
"include/cppgc/heap.h",
"include/cppgc/platform.h",
"include/v8config.h",
"src/heap/cppgc/allocation.cc",
"src/heap/cppgc/gc-info-table.cc",
"src/heap/cppgc/gc-info-table.h",
"src/heap/cppgc/gc-info.cc",
"src/heap/cppgc/heap-inl.h",
"src/heap/cppgc/heap-object-header-inl.h",
"src/heap/cppgc/heap-object-header.cc",
"src/heap/cppgc/heap-object-header.h",
"src/heap/cppgc/heap.cc",
"src/heap/cppgc/heap.h",
"src/heap/cppgc/platform.cc",
]
configs = [ ":internal_config" ]
......
......@@ -8,26 +8,84 @@
#include <stdint.h>
#include <atomic>
#include "include/cppgc/api-constants.h"
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/gc-info.h"
#include "include/cppgc/heap.h"
#include "include/cppgc/internals.h"
namespace cppgc {
template <typename T>
class MakeGarbageCollectedTraitBase;
namespace internal {
// Marks an object as being fully constructed, resulting in precise handling
// by the garbage collector.
inline void MarkObjectAsFullyConstructed(const void* payload) {
// See api_constants for an explanation of the constants.
std::atomic<uint16_t>* atomic_mutable_bitfield =
reinterpret_cast<std::atomic<uint16_t>*>(
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(
reinterpret_cast<const uint8_t*>(payload) -
api_constants::kFullyConstructedBitFieldOffsetFromPayload)));
uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed);
value = value | api_constants::kFullyConstructedBitMask;
atomic_mutable_bitfield->store(value, std::memory_order_release);
}
class V8_EXPORT MakeGarbageCollectedTraitInternal {
protected:
static inline void MarkObjectAsFullyConstructed(const void* payload) {
// See api_constants for an explanation of the constants.
std::atomic<uint16_t>* atomic_mutable_bitfield =
reinterpret_cast<std::atomic<uint16_t>*>(
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(
reinterpret_cast<const uint8_t*>(payload) -
api_constants::kFullyConstructedBitFieldOffsetFromPayload)));
uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed);
value = value | api_constants::kFullyConstructedBitMask;
atomic_mutable_bitfield->store(value, std::memory_order_release);
}
static void* Allocate(cppgc::Heap* heap, size_t size, GCInfoIndex index);
friend class HeapObjectHeader;
};
} // namespace internal
// Users with custom allocation needs (e.g. overriding size) should override
// MakeGarbageCollectedTrait (see below) and inherit their trait from
// MakeGarbageCollectedTraitBase to get access to low-level primitives.
template <typename T>
class MakeGarbageCollectedTraitBase
: private internal::MakeGarbageCollectedTraitInternal {
protected:
// Allocates an object of |size| bytes on |heap|.
//
// TODO(mlippautz): Allow specifying arena for specific embedder uses.
static void* Allocate(Heap* heap, size_t size) {
return internal::MakeGarbageCollectedTraitInternal::Allocate(
heap, size, internal::GCInfoTrait<T>::Index());
}
// Marks an object as being fully constructed, resulting in precise handling
// by the garbage collector.
static void MarkObjectAsFullyConstructed(const void* payload) {
// internal::MarkObjectAsFullyConstructed(payload);
internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed(
payload);
}
};
template <typename T>
class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> {
public:
template <typename... Args>
static T* Call(Heap* heap, Args&&... args) {
static_assert(internal::IsGarbageCollectedType<T>::value,
"T needs to be a garbage collected object");
void* memory = MakeGarbageCollectedTraitBase<T>::Allocate(heap, sizeof(T));
T* object = ::new (memory) T(std::forward<Args>(args)...);
MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
return object;
}
};
// Default MakeGarbageCollected: Constructs an instance of T, which is a garbage
// collected type.
template <typename T, typename... Args>
T* MakeGarbageCollected(Heap* heap, Args&&... args) {
return MakeGarbageCollectedTrait<T>::Call(heap, std::forward<Args>(args)...);
}
} // namespace cppgc
#endif // INCLUDE_CPPGC_ALLOCATION_H_
......@@ -7,19 +7,13 @@
#include <type_traits>
#include "include/cppgc/internals.h"
namespace cppgc {
namespace internal {
using FinalizationCallback = void (*)(void*);
// Pre-C++17 custom implementation of std::void_t.
template <typename... Ts>
struct make_void {
typedef void type;
};
template <typename... Ts>
using void_t = typename make_void<Ts...>::type;
template <typename T, typename = void>
struct HasFinalizeGarbageCollectedObject : std::false_type {};
......
// 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.
#ifndef INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
#define INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
#include <type_traits>
#include "include/cppgc/internals.h"
#include "include/cppgc/platform.h"
namespace cppgc {
namespace internal {
template <typename T, typename = void>
struct IsGarbageCollectedType : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsGarbageCollectedType<
T, void_t<typename std::remove_const_t<T>::IsGarbageCollectedTypeMarker>>
: std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
} // namespace internal
template <typename>
class GarbageCollected {
public:
using IsGarbageCollectedTypeMarker = void;
// Must use MakeGarbageCollected.
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
// The garbage collector is taking care of reclaiming the object. Also,
// virtual destructor requires an unambiguous, accessible 'operator delete'.
void operator delete(void*) {
#ifdef V8_ENABLE_CHECKS
internal::Abort();
#endif // V8_ENABLE_CHECKS
}
void operator delete[](void*) = delete;
protected:
GarbageCollected() = default;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
// 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.
#ifndef INCLUDE_CPPGC_HEAP_H_
#define INCLUDE_CPPGC_HEAP_H_
#include <memory>
#include "include/v8config.h"
namespace cppgc {
namespace internal {
class Heap;
} // namespace internal
class V8_EXPORT Heap {
public:
static std::unique_ptr<Heap> Create();
virtual ~Heap() = default;
private:
Heap() = default;
friend class internal::Heap;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_H_
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_API_CONSTANTS_H_
#define INCLUDE_CPPGC_API_CONSTANTS_H_
#ifndef INCLUDE_CPPGC_INTERNALS_H_
#define INCLUDE_CPPGC_INTERNALS_H_
#include <stddef.h>
#include <stdint.h>
......@@ -13,8 +13,17 @@
namespace cppgc {
namespace internal {
// Pre-C++17 custom implementation of std::void_t.
template <typename... Ts>
struct make_void {
typedef void type;
};
template <typename... Ts>
using void_t = typename make_void<Ts...>::type;
// Embedders should not rely on this code!
// Internal constants to avoid exposing internal types on the API surface.
// DO NOT USE THESE CONSTANTS FROM USER CODE!
namespace api_constants {
// Offset of the uint16_t bitfield from the payload contaning the
// in-construction bit. This is subtracted from the payload pointer to get
......@@ -29,4 +38,4 @@ static constexpr size_t kFullyConstructedBitMask = size_t{1};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_API_CONSTANTS_H_
#endif // INCLUDE_CPPGC_INTERNALS_H_
......@@ -6,6 +6,7 @@
#define INCLUDE_CPPGC_PLATFORM_H_
#include "include/v8-platform.h"
#include "include/v8config.h"
namespace cppgc {
......@@ -13,6 +14,18 @@ namespace cppgc {
// depending on namespace v8.
using PageAllocator = v8::PageAllocator;
// Initializes the garbage collector with the provided platform. Must be called
// before creating a Heap.
V8_EXPORT void InitializePlatform(PageAllocator* page_allocator);
// Must be called after destroying the last used heap.
V8_EXPORT void ShutdownPlatform();
namespace internal {
V8_EXPORT void Abort();
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_PLATFORM_H_
// 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/base/logging.h"
#include "src/base/macros.h"
#include "src/heap/cppgc/heap-inl.h"
namespace cppgc {
namespace internal {
// static
void* MakeGarbageCollectedTraitInternal::Allocate(cppgc::Heap* heap,
size_t size,
GCInfoIndex index) {
DCHECK_NOT_NULL(heap);
return Heap::From(heap)->Allocate(size, index);
}
} // namespace internal
} // namespace cppgc
......@@ -26,6 +26,13 @@ using ConstAddress = const uint8_t*;
constexpr size_t kAllocationGranularity = 8;
constexpr size_t kAllocationMask = kAllocationGranularity - 1;
constexpr size_t kPageSizeLog2 = 17;
constexpr size_t kPageSize = 1 << kPageSizeLog2;
constexpr size_t kPageOffsetMask = kPageSize - 1;
constexpr size_t kPageBaseMask = ~kPageOffsetMask;
constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2;
} // namespace internal
} // namespace cppgc
......
// 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/heap.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-object-header-inl.h"
#ifndef V8_HEAP_CPPGC_HEAP_INL_H_
#define V8_HEAP_CPPGC_HEAP_INL_H_
namespace cppgc {
namespace internal {
void* Heap::Allocate(size_t size, GCInfoIndex index) {
// TODO(chromium:1056170): This is merely a dummy implementation and will be
// replaced with proper allocation code throughout the migration.
size_t allocation_size = size + sizeof(HeapObjectHeader);
// The allocation size calculation can overflow for large sizes.
CHECK_GT(allocation_size, size);
// calloc() provides stricter alignment guarantees than the GC. Allocate
// a multiple of kAllocationGranularity to follow restrictions of
// HeapObjectHeader.
allocation_size = (allocation_size + kAllocationMask) & ~kAllocationMask;
void* memory = calloc(1, allocation_size);
HeapObjectHeader* header =
new (memory) HeapObjectHeader(allocation_size, index);
objects_.push_back(header);
return header->Payload();
}
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_HEAP_INL_H_
......@@ -5,8 +5,8 @@
#ifndef V8_HEAP_CPPGC_HEAP_OBJECT_HEADER_INL_H_
#define V8_HEAP_CPPGC_HEAP_OBJECT_HEADER_INL_H_
#include "include/cppgc/allocation.h"
#include "include/cppgc/gc-info.h"
#include "src/base/atomic-utils.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
......@@ -82,6 +82,10 @@ bool HeapObjectHeader::IsInConstruction() const {
return !FullyConstructedField::decode(encoded);
}
void HeapObjectHeader::MarkAsFullyConstructed() {
MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed(Payload());
}
template <HeapObjectHeader::AccessMode mode>
bool HeapObjectHeader::IsMarked() const {
const uint16_t encoded =
......
......@@ -4,7 +4,10 @@
#include "src/heap/cppgc/heap-object-header.h"
#include "include/cppgc/internals.h"
#include "src/base/macros.h"
#include "src/heap/cppgc/gc-info-table.h"
#include "src/heap/cppgc/heap-object-header-inl.h"
namespace cppgc {
namespace internal {
......@@ -16,5 +19,12 @@ void HeapObjectHeader::CheckApiConstants() {
(sizeof(encoded_high_) + sizeof(encoded_low_)));
}
void HeapObjectHeader::Finalize() {
const GCInfo& gc_info = GlobalGCInfoTable::GCInfoFromIndex(GetGCInfoIndex());
if (gc_info.finalize) {
gc_info.finalize(Payload());
}
}
} // namespace internal
} // namespace cppgc
......@@ -8,7 +8,6 @@
#include <stdint.h>
#include <atomic>
#include "include/cppgc/api-constants.h"
#include "include/cppgc/gc-info.h"
#include "src/base/bit-field.h"
#include "src/heap/cppgc/globals.h"
......@@ -69,6 +68,7 @@ class HeapObjectHeader final {
template <AccessMode = AccessMode::kNonAtomic>
bool IsInConstruction() const;
inline void MarkAsFullyConstructed();
// Use MarkObjectAsFullyConstructed() to mark an object as being constructed.
template <AccessMode = AccessMode::kNonAtomic>
......@@ -77,6 +77,8 @@ class HeapObjectHeader final {
void Unmark();
inline bool TryMarkAtomic();
void Finalize();
private:
enum class EncodedHalf : uint8_t { kLow, kHigh };
......
// 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/heap.h"
#include <memory>
#include "src/heap/cppgc/heap-object-header.h"
namespace cppgc {
std::unique_ptr<Heap> Heap::Create() {
return std::make_unique<internal::Heap>();
}
namespace internal {
void Heap::CollectGarbage() {
for (HeapObjectHeader* header : objects_) {
header->Finalize();
free(header);
}
objects_.clear();
}
} // namespace internal
} // namespace cppgc
// 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.
#ifndef V8_HEAP_CPPGC_HEAP_H_
#define V8_HEAP_CPPGC_HEAP_H_
#include <vector>
#include "include/cppgc/gc-info.h"
#include "include/cppgc/heap.h"
#include "src/heap/cppgc/heap-object-header.h"
namespace cppgc {
namespace internal {
class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
public:
static Heap* From(cppgc::Heap* heap) { return static_cast<Heap*>(heap); }
Heap() = default;
~Heap() final = default;
inline void* Allocate(size_t size, GCInfoIndex index);
void CollectGarbage();
private:
std::vector<HeapObjectHeader*> objects_;
};
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_HEAP_H_
// 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/platform.h"
#include "src/base/platform/platform.h"
#include "src/heap/cppgc/gc-info-table.h"
namespace cppgc {
namespace internal {
static PageAllocator* g_page_allocator;
} // namespace internal
void InitializePlatform(PageAllocator* page_allocator) {
internal::g_page_allocator = page_allocator;
internal::GlobalGCInfoTable::Create(page_allocator);
}
void ShutdownPlatform() { internal::g_page_allocator = nullptr; }
namespace internal {
void Abort() { v8::base::OS::Abort(); }
} // namespace internal
} // namespace cppgc
......@@ -44,9 +44,13 @@ v8_source_set("cppgc_unittests_sources") {
testonly = true
sources = [
"heap/cppgc/allocation_unittest.cc",
"heap/cppgc/finalizer-trait_unittest.cc",
"heap/cppgc/garbage-collected_unittest.cc",
"heap/cppgc/gc-info_unittest.cc",
"heap/cppgc/heap-object-header_unittest.cc",
"heap/cppgc/tests.cc",
"heap/cppgc/tests.h",
]
configs = [
......
// 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 <memory>
#include "src/heap/cppgc/heap.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
TEST(GCBasicHeapTest, CreateAndDestroyHeap) {
std::unique_ptr<Heap> heap{Heap::Create()};
}
namespace {
class Foo : public GarbageCollected<Foo> {
public:
static size_t destructor_callcount;
Foo() { destructor_callcount = 0; }
~Foo() { destructor_callcount++; }
};
size_t Foo::destructor_callcount;
class GCAllocationTest : public testing::TestWithHeap {};
} // namespace
TEST_F(GCAllocationTest, MakeGarbageCollectedAndReclaim) {
MakeGarbageCollected<Foo>(GetHeap());
EXPECT_EQ(0u, Foo::destructor_callcount);
internal::Heap::From(GetHeap())->CollectGarbage();
EXPECT_EQ(1u, Foo::destructor_callcount);
}
} // namespace cppgc
// 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/garbage-collected.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
class GCed : public GarbageCollected<GCed> {};
class NotGCed {};
} // namespace
TEST(GarbageCollectedTest, GarbageCollectedTrait) {
EXPECT_FALSE(IsGarbageCollectedType<int>::value);
EXPECT_FALSE(IsGarbageCollectedType<NotGCed>::value);
EXPECT_TRUE(IsGarbageCollectedType<GCed>::value);
}
} // namespace internal
} // namespace cppgc
......@@ -8,6 +8,7 @@
#include "src/base/page-allocator.h"
#include "src/base/platform/platform.h"
#include "src/heap/cppgc/gc-info-table.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
......@@ -123,30 +124,26 @@ TEST(GCInfoTableTest, MultiThreadedResizeToMaxIndex) {
namespace {
class GCInfoTraitTest : public testing::TestWithPlatform {};
class BasicType final {};
class OtherBasicType final {};
} // namespace
TEST(GCInfoTraitTest, IndexInBounds) {
v8::base::PageAllocator page_allocator;
GlobalGCInfoTable::Create(&page_allocator);
TEST_F(GCInfoTraitTest, IndexInBounds) {
const GCInfoIndex index = GCInfoTrait<BasicType>::Index();
EXPECT_GT(GCInfoTable::kMaxIndex, index);
EXPECT_LE(GCInfoTable::kMinIndex, index);
}
TEST(GCInfoTraitTest, TraitReturnsSameIndexForSameType) {
v8::base::PageAllocator page_allocator;
GlobalGCInfoTable::Create(&page_allocator);
TEST_F(GCInfoTraitTest, TraitReturnsSameIndexForSameType) {
const GCInfoIndex index1 = GCInfoTrait<BasicType>::Index();
const GCInfoIndex index2 = GCInfoTrait<BasicType>::Index();
EXPECT_EQ(index1, index2);
}
TEST(GCInfoTraitTest, TraitReturnsDifferentIndexForDifferentTypes) {
v8::base::PageAllocator page_allocator;
GlobalGCInfoTable::Create(&page_allocator);
TEST_F(GCInfoTraitTest, TraitReturnsDifferentIndexForDifferentTypes) {
const GCInfoIndex index1 = GCInfoTrait<BasicType>::Index();
const GCInfoIndex index2 = GCInfoTrait<OtherBasicType>::Index();
EXPECT_NE(index1, index2);
......
......@@ -72,7 +72,7 @@ TEST(HeapObjectHeaderTest, MarkObjectAsFullyConstructed) {
constexpr size_t kSize = kAllocationGranularity;
HeapObjectHeader header(kSize, kGCInfoIndex);
EXPECT_TRUE(header.IsInConstruction());
MarkObjectAsFullyConstructed(&header + 1);
header.MarkAsFullyConstructed();
EXPECT_FALSE(header.IsInConstruction());
// Size shares the same bitfield and should be unaffected by
// MarkObjectAsFullyConstructed.
......@@ -157,7 +157,7 @@ TEST(HeapObjectHeaderTest, ConstructionBitProtectsNonAtomicWrites) {
reinterpret_cast<Payload*>(header->Payload()));
CHECK(gc_thread.Start());
new (header->Payload()) Payload();
MarkObjectAsFullyConstructed(header->Payload());
header->MarkAsFullyConstructed();
gc_thread.Join();
}
......
// 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 "test/unittests/heap/cppgc/tests.h"
namespace cppgc {
namespace testing {
// static
std::unique_ptr<cppgc::PageAllocator> TestWithPlatform::page_allocator_;
// static
void TestWithPlatform::SetUpTestSuite() {
page_allocator_.reset(new v8::base::PageAllocator());
cppgc::InitializePlatform(page_allocator_.get());
}
// static
void TestWithPlatform::TearDownTestSuite() {
cppgc::ShutdownPlatform();
page_allocator_.reset();
}
void TestWithHeap::SetUp() {
heap_ = Heap::Create();
TestWithPlatform::SetUp();
}
void TestWithHeap::TearDown() {
heap_.reset();
TestWithPlatform::TearDown();
}
} // namespace testing
} // namespace cppgc
// 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.
#ifndef V8_UNITTESTS_HEAP_CPPGC_TESTS_H_
#define V8_UNITTESTS_HEAP_CPPGC_TESTS_H_
#include "include/cppgc/heap.h"
#include "include/cppgc/platform.h"
#include "src/base/page-allocator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace testing {
class TestWithPlatform : public ::testing::Test {
protected:
static void SetUpTestSuite();
static void TearDownTestSuite();
private:
static std::unique_ptr<cppgc::PageAllocator> page_allocator_;
};
class TestWithHeap : public TestWithPlatform {
protected:
void SetUp() override;
void TearDown() override;
Heap* GetHeap() const { return heap_.get(); }
private:
std::unique_ptr<cppgc::Heap> heap_;
};
} // namespace testing
} // namespace cppgc
#endif // V8_UNITTESTS_HEAP_CPPGC_TESTS_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment