cppgc: Hello world
"By my deeds I honor him. V8." - Add basic build files for library and unittests. - Integrate unittests also in existing V8 unittests for simplicity. The CL also adds FinalizerTrait and unittests to allow building a testing target that executes code. FinalizerTrait is used to determine how managed C++ types are finalized. The trait should not be overridable by users but needs to be exposed on API-level to avoid including library-internal headers. Bug: chromium:1056170 Change-Id: I64d91053410a17a7835e50547f58990625d2da28 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2108549 Reviewed-by: Hannes Payer <hpayer@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Reviewed-by: Michael Achenbach <machenbach@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#66834}
This commit is contained in:
parent
92bd7818b7
commit
9d75253764
37
BUILD.gn
37
BUILD.gn
@ -3920,6 +3920,20 @@ v8_source_set("fuzzer_support") {
|
||||
]
|
||||
}
|
||||
|
||||
v8_source_set("cppgc_base") {
|
||||
visibility = [ ":*" ]
|
||||
|
||||
sources = [
|
||||
"include/cppgc/finalizer-trait.h",
|
||||
"include/v8config.h",
|
||||
"src/heap/cppgc/cppgc.cc",
|
||||
]
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
|
||||
public_deps = [ ":v8_libbase" ]
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Produce a single static library for embedders
|
||||
#
|
||||
@ -3966,6 +3980,12 @@ v8_static_library("wee8") {
|
||||
]
|
||||
}
|
||||
|
||||
v8_static_library("cppgc") {
|
||||
deps = [ ":cppgc_base" ]
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Executables
|
||||
#
|
||||
@ -4257,6 +4277,15 @@ if (is_component_build) {
|
||||
|
||||
public_configs = [ ":external_config" ]
|
||||
}
|
||||
|
||||
v8_component("cppgc_for_testing") {
|
||||
testonly = true
|
||||
|
||||
public_deps = [ ":cppgc_base" ]
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
public_configs = [ ":external_config" ]
|
||||
}
|
||||
} else {
|
||||
group("v8") {
|
||||
public_deps = [
|
||||
@ -4280,6 +4309,14 @@ if (is_component_build) {
|
||||
|
||||
public_configs = [ ":external_config" ]
|
||||
}
|
||||
|
||||
group("cppgc_for_testing") {
|
||||
testonly = true
|
||||
|
||||
public_deps = [ ":cppgc_base" ]
|
||||
|
||||
public_configs = [ ":external_config" ]
|
||||
}
|
||||
}
|
||||
|
||||
v8_executable("d8") {
|
||||
|
5
include/cppgc/README.md
Normal file
5
include/cppgc/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# C++ Garbage Collection
|
||||
|
||||
This directory provides an open-source garbage collection library for C++.
|
||||
|
||||
The library is under construction, meaning that *all APIs in this directory are incomplete and considered unstable and should not be used*.
|
96
include/cppgc/finalizer-trait.h
Normal file
96
include/cppgc/finalizer-trait.h
Normal file
@ -0,0 +1,96 @@
|
||||
// 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_FINALIZER_TRAIT_H_
|
||||
#define INCLUDE_CPPGC_FINALIZER_TRAIT_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
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 {};
|
||||
|
||||
template <typename T>
|
||||
struct HasFinalizeGarbageCollectedObject<
|
||||
T, void_t<decltype(std::declval<T>().FinalizeGarbageCollectedObject())>>
|
||||
: std::true_type {};
|
||||
|
||||
// The FinalizerTraitImpl specifies how to finalize objects.
|
||||
template <typename T, bool isFinalized>
|
||||
struct FinalizerTraitImpl;
|
||||
|
||||
template <typename T>
|
||||
struct FinalizerTraitImpl<T, true> {
|
||||
private:
|
||||
// Dispatch to custom FinalizeGarbageCollectedObject().
|
||||
struct Custom {
|
||||
static void Call(void* obj) {
|
||||
static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch to regular destructor.
|
||||
struct Destructor {
|
||||
static void Call(void* obj) { static_cast<T*>(obj)->~T(); }
|
||||
};
|
||||
|
||||
using FinalizeImpl =
|
||||
std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value, Custom,
|
||||
Destructor>;
|
||||
|
||||
public:
|
||||
static void Finalize(void* obj) {
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
FinalizeImpl::Call(obj);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FinalizerTraitImpl<T, false> {
|
||||
static void Finalize(void* obj) {
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
}
|
||||
};
|
||||
|
||||
// The FinalizerTrait is used to determine if a type requires finalization and
|
||||
// what finalization means.
|
||||
template <typename T>
|
||||
struct FinalizerTrait {
|
||||
private:
|
||||
// Object has a finalizer if it has
|
||||
// - a custom FinalizeGarbageCollectedObject method, or
|
||||
// - a destructor.
|
||||
static constexpr bool kNonTrivialFinalizer =
|
||||
internal::HasFinalizeGarbageCollectedObject<T>::value ||
|
||||
!std::is_trivially_destructible<typename std::remove_cv<T>::type>::value;
|
||||
|
||||
static void Finalize(void* obj) {
|
||||
internal::FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
|
||||
}
|
||||
|
||||
public:
|
||||
// The callback used to finalize an object of type T.
|
||||
static constexpr FinalizationCallback kCallback =
|
||||
kNonTrivialFinalizer ? Finalize : nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr FinalizationCallback FinalizerTrait<T>::kCallback;
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_FINALIZER_TRAIT_H_
|
16
src/heap/cppgc/cppgc.cc
Normal file
16
src/heap/cppgc/cppgc.cc
Normal file
@ -0,0 +1,16 @@
|
||||
// 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/v8config.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
V8_EXPORT void Dummy() {
|
||||
// TODO(mlippautz): Placeholder to force building a library. Remove as soon as
|
||||
// actual code is available.
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
@ -20,6 +20,43 @@ if (is_fuchsia) {
|
||||
}
|
||||
}
|
||||
|
||||
# Stand-alone target for C++ GC unittests. This is used to ensure that it
|
||||
# builds without V8 as well. They are also included in the regular unittests
|
||||
# target for simplicity.
|
||||
v8_executable("cppgc_unittests") {
|
||||
testonly = true
|
||||
|
||||
configs = [
|
||||
"../..:external_config",
|
||||
"../..:internal_config_base",
|
||||
]
|
||||
|
||||
sources = [ "heap/cppgc/run-all-unittests.cc" ]
|
||||
|
||||
deps = [
|
||||
":cppgc_unittests_sources",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
]
|
||||
}
|
||||
|
||||
v8_source_set("cppgc_unittests_sources") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "heap/cppgc/finalizer-trait_unittest.cc" ]
|
||||
|
||||
configs = [
|
||||
"../..:external_config",
|
||||
"../..:internal_config_base",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../..:cppgc_for_testing",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
]
|
||||
}
|
||||
|
||||
v8_executable("unittests") {
|
||||
testonly = true
|
||||
|
||||
@ -29,6 +66,7 @@ v8_executable("unittests") {
|
||||
#}],
|
||||
|
||||
deps = [
|
||||
":cppgc_unittests_sources",
|
||||
":unittests_sources",
|
||||
"../..:v8_for_testing",
|
||||
"../..:v8_libbase",
|
||||
|
118
test/unittests/heap/cppgc/finalizer-trait_unittest.cc
Normal file
118
test/unittests/heap/cppgc/finalizer-trait_unittest.cc
Normal file
@ -0,0 +1,118 @@
|
||||
// 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/finalizer-trait.h"
|
||||
#include <type_traits>
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// Trivially destructible types.
|
||||
class TypeWithoutDestructor final {};
|
||||
class TypeWithPrimitive final {
|
||||
public:
|
||||
int foo = 0;
|
||||
};
|
||||
|
||||
class InvokeCounter {
|
||||
public:
|
||||
static size_t kCallcount;
|
||||
static void Reset() { kCallcount = 0; }
|
||||
static void Invoke() { kCallcount++; }
|
||||
};
|
||||
|
||||
size_t InvokeCounter::kCallcount = 0;
|
||||
|
||||
// Regular C++ use cases.
|
||||
|
||||
class TypeWithDestructor final : public InvokeCounter {
|
||||
public:
|
||||
~TypeWithDestructor() { Invoke(); }
|
||||
};
|
||||
|
||||
class TypeWithVirtualDestructorBase {
|
||||
public:
|
||||
virtual ~TypeWithVirtualDestructorBase() = default;
|
||||
};
|
||||
|
||||
class TypeWithVirtualDestructorChild final
|
||||
: public TypeWithVirtualDestructorBase,
|
||||
public InvokeCounter {
|
||||
public:
|
||||
~TypeWithVirtualDestructorChild() final { Invoke(); }
|
||||
};
|
||||
|
||||
// Manual dispatch to avoid vtables.
|
||||
|
||||
class TypeWithCustomFinalizationMethod final : public InvokeCounter {
|
||||
public:
|
||||
void FinalizeGarbageCollectedObject() { Invoke(); }
|
||||
};
|
||||
|
||||
class TypeWithCustomFinalizationMethodAtBase {
|
||||
public:
|
||||
void FinalizeGarbageCollectedObject();
|
||||
};
|
||||
|
||||
class TypeWithCustomFinalizationMethodAtBaseChild
|
||||
: public TypeWithCustomFinalizationMethodAtBase,
|
||||
public InvokeCounter {
|
||||
public:
|
||||
~TypeWithCustomFinalizationMethodAtBaseChild() { Invoke(); }
|
||||
};
|
||||
|
||||
void TypeWithCustomFinalizationMethodAtBase::FinalizeGarbageCollectedObject() {
|
||||
// The test knows that base is only inherited by a single child. In practice
|
||||
// users can maintain a map of valid types in already existing storage.
|
||||
static_cast<TypeWithCustomFinalizationMethodAtBaseChild*>(this)
|
||||
->~TypeWithCustomFinalizationMethodAtBaseChild();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void ExpectFinalizerIsInvoked(Type* object) {
|
||||
InvokeCounter::Reset();
|
||||
EXPECT_NE(nullptr, FinalizerTrait<Type>::kCallback);
|
||||
FinalizerTrait<Type>::kCallback(object);
|
||||
EXPECT_EQ(1u, InvokeCounter::kCallcount);
|
||||
operator delete(object);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(FinalizerTrait, TypeWithoutDestructorHasNoFinalizer) {
|
||||
static_assert(std::is_trivially_destructible<TypeWithoutDestructor>::value,
|
||||
"trivially destructible");
|
||||
EXPECT_EQ(nullptr, FinalizerTrait<TypeWithoutDestructor>::kCallback);
|
||||
}
|
||||
|
||||
TEST(FinalizerTrait, TypeWithPrimitiveHasNoFinalizer) {
|
||||
static_assert(std::is_trivially_destructible<TypeWithPrimitive>::value,
|
||||
"trivially destructible");
|
||||
EXPECT_EQ(nullptr, FinalizerTrait<TypeWithPrimitive>::kCallback);
|
||||
}
|
||||
|
||||
TEST(FinalizerTrait, FinalizerForTypeWithDestructor) {
|
||||
ExpectFinalizerIsInvoked(new TypeWithDestructor());
|
||||
}
|
||||
|
||||
TEST(FinalizerTrait, FinalizerForTypeWithVirtualBaseDtor) {
|
||||
TypeWithVirtualDestructorBase* base = new TypeWithVirtualDestructorChild();
|
||||
ExpectFinalizerIsInvoked(base);
|
||||
}
|
||||
|
||||
TEST(FinalizerTrait, FinalizerForCustomFinalizationMethod) {
|
||||
ExpectFinalizerIsInvoked(new TypeWithCustomFinalizationMethod());
|
||||
}
|
||||
|
||||
TEST(FinalizerTrait, FinalizerForCustomFinalizationMethodInBase) {
|
||||
TypeWithCustomFinalizationMethodAtBase* base =
|
||||
new TypeWithCustomFinalizationMethodAtBaseChild();
|
||||
ExpectFinalizerIsInvoked(base);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
17
test/unittests/heap/cppgc/run-all-unittests.cc
Normal file
17
test/unittests/heap/cppgc/run-all-unittests.cc
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 "testing/gmock/include/gmock/gmock.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Don't catch SEH exceptions and continue as the following tests might hang
|
||||
// in an broken environment on windows.
|
||||
testing::GTEST_FLAG(catch_exceptions) = false;
|
||||
|
||||
// Most unit-tests are multi-threaded, so enable thread-safe death-tests.
|
||||
testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
|
||||
testing::InitGoogleMock(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Reference in New Issue
Block a user