diff --git a/kotlin-native/codestyle/cpp/README.md b/kotlin-native/codestyle/cpp/README.md index 8deda2e0f3b..c855ad8a30a 100644 --- a/kotlin-native/codestyle/cpp/README.md +++ b/kotlin-native/codestyle/cpp/README.md @@ -6,6 +6,8 @@ * Headers should live in the same folder with it's implementation counterpart (if there's one) **TODO**: This does not work with multiple implementations of a single header. * Headers should use header guards +* Headers that are designed to be included into both `.cpp`/`.mm` and `.c`/`.m` should have extension `.h` +* Headers that are designed to be included into `.cpp`/`.mm` only should have extension `.hpp` ## Namespace usage diff --git a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp index 01e3b2dbade..a812fc6931b 100644 --- a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp @@ -41,10 +41,11 @@ #include "KString.h" #include "Memory.h" #include "MemoryPrivate.hpp" +#include "Mutex.hpp" #include "Natives.h" #include "Porting.h" #include "Runtime.h" -#include "Utils.h" +#include "Utils.hpp" #include "WorkerBoundReference.h" #include "Weak.h" @@ -150,20 +151,16 @@ KBoolean g_hasCyclicCollector = true; #endif // USE_CYCLIC_GC // TODO: Consider using ObjHolder. -class ScopedRefHolder { +class ScopedRefHolder : private kotlin::MoveOnly { public: ScopedRefHolder() = default; explicit ScopedRefHolder(KRef obj); - ScopedRefHolder(const ScopedRefHolder&) = delete; - ScopedRefHolder(ScopedRefHolder&& other) noexcept: obj_(other.obj_) { other.obj_ = nullptr; } - ScopedRefHolder& operator=(const ScopedRefHolder&) = delete; - ScopedRefHolder& operator=(ScopedRefHolder&& other) noexcept { ScopedRefHolder tmp(std::move(other)); swap(tmp); @@ -191,7 +188,7 @@ struct CycleDetectorRootset { KStdVector heldRefs; }; -class CycleDetector { +class CycleDetector : private kotlin::Pinned { public: static void insertCandidateIfNeeded(KRef object) { if (canBeACandidate(object)) @@ -208,10 +205,6 @@ class CycleDetector { private: CycleDetector() = default; ~CycleDetector() = default; - CycleDetector(const CycleDetector&) = delete; - CycleDetector(CycleDetector&&) = delete; - CycleDetector& operator=(const CycleDetector&) = delete; - CycleDetector& operator=(CycleDetector&&) = delete; static CycleDetector& instance() { // Only store a pointer to CycleDetector in .bss diff --git a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp index 29501c5d49c..a4c8625fcb9 100644 --- a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp +++ b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp @@ -38,11 +38,12 @@ #include "Exceptions.h" #include "ExecFormat.h" #include "Memory.h" +#include "Mutex.hpp" #include "Natives.h" #include "KString.h" #include "SourceInfo.h" #include "Types.h" -#include "Utils.h" +#include "Utils.hpp" #include "ObjCExceptions.h" namespace { @@ -279,7 +280,8 @@ RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) { #if KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS namespace { -class TerminateHandler { +// Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5) +class TerminateHandler : private kotlin::Pinned { // In fact, it's safe to call my_handler directly from outside: it will do the job and then invoke original handler, // even if it has not been initialized yet. So one may want to make it public and/or not the class member @@ -313,11 +315,6 @@ class TerminateHandler { return singleton; } - // Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5) - TerminateHandler(const TerminateHandler&) = delete; - TerminateHandler(TerminateHandler&&) = delete; - TerminateHandler& operator=(const TerminateHandler&) = delete; - TerminateHandler& operator=(TerminateHandler&&) = delete; // Dtor might be in use to restore original handler. However, consequent install // will not reconstruct handler anyway, so let's keep dtor deleted to avoid confusion. ~TerminateHandler() = delete; diff --git a/kotlin-native/runtime/src/main/cpp/Utils.h b/kotlin-native/runtime/src/main/cpp/Mutex.hpp similarity index 89% rename from kotlin-native/runtime/src/main/cpp/Utils.h rename to kotlin-native/runtime/src/main/cpp/Mutex.hpp index ae3e4952c7e..87778b15ce4 100644 --- a/kotlin-native/runtime/src/main/cpp/Utils.h +++ b/kotlin-native/runtime/src/main/cpp/Mutex.hpp @@ -14,8 +14,12 @@ * limitations under the License. */ +#ifndef RUNTIME_MUTEX_H +#define RUNTIME_MUTEX_H + #include #include "KAssert.h" +#include "Utils.hpp" class SimpleMutex { private: @@ -37,7 +41,7 @@ class SimpleMutex { // TODO: use std::lock_guard instead? template -class LockGuard { +class LockGuard : private kotlin::Pinned { public: explicit LockGuard(Mutex& mutex_) : mutex(mutex_) { mutex.lock(); @@ -49,7 +53,6 @@ class LockGuard { private: Mutex& mutex; - - LockGuard(const LockGuard&) = delete; - LockGuard& operator=(const LockGuard&) = delete; }; + +#endif // RUNTIME_MUTEX_H diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm index fde6e386b7b..e98988da83c 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExport.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExport.mm @@ -39,7 +39,7 @@ #import "ObjCExportPrivate.h" #import "ObjCMMAPI.h" #import "Runtime.h" -#import "Utils.h" +#import "Mutex.hpp" #import "Exceptions.h" struct ObjCToKotlinMethodAdapter { diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm index d7c0a273500..b41d4453c57 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm @@ -24,7 +24,7 @@ #import "ObjCExport.h" #import "Porting.h" #import "Runtime.h" -#import "Utils.h" +#import "Mutex.hpp" #import "ObjCExportErrors.h" diff --git a/kotlin-native/runtime/src/main/cpp/ObjCInterop.mm b/kotlin-native/runtime/src/main/cpp/ObjCInterop.mm index bff9950996a..d2da6eb3743 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCInterop.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCInterop.mm @@ -33,7 +33,7 @@ #include "ObjCExportPrivate.h" #include "ObjCMMAPI.h" #include "Types.h" -#include "Utils.h" +#include "Mutex.hpp" // Replaced in ObjCExportCodeGenerator. __attribute__((weak)) const char* Kotlin_ObjCInterop_uniquePrefix = nullptr; diff --git a/kotlin-native/runtime/src/main/cpp/ObjCMMAPI.h b/kotlin-native/runtime/src/main/cpp/ObjCMMAPI.h index de90e53140e..3478e0b0e68 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCMMAPI.h +++ b/kotlin-native/runtime/src/main/cpp/ObjCMMAPI.h @@ -7,22 +7,18 @@ #define RUNTIME_OBJCMMAPI_H #include "Common.h" +#include "Utils.hpp" #if KONAN_OBJC_INTEROP extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject); namespace konan { -class AutoreleasePool { +class AutoreleasePool : private kotlin::Pinned { public: AutoreleasePool(); ~AutoreleasePool(); - AutoreleasePool(const AutoreleasePool&) = delete; - AutoreleasePool(AutoreleasePool&&) = delete; - AutoreleasePool& operator=(const AutoreleasePool&) = delete; - AutoreleasePool& operator=(AutoreleasePool&&) = delete; - private: void* handle; }; diff --git a/kotlin-native/runtime/src/main/cpp/TestSupportCompilerGenerated.hpp b/kotlin-native/runtime/src/main/cpp/TestSupportCompilerGenerated.hpp index d685f32c16e..cf0bb7d1f7c 100644 --- a/kotlin-native/runtime/src/main/cpp/TestSupportCompilerGenerated.hpp +++ b/kotlin-native/runtime/src/main/cpp/TestSupportCompilerGenerated.hpp @@ -10,9 +10,10 @@ #include "gtest/gtest.h" #include "Types.h" +#include "Utils.hpp" template -class ScopedStrictMockFunction { +class ScopedStrictMockFunction : private kotlin::MoveOnly { public: using Mock = testing::StrictMock>; @@ -24,9 +25,6 @@ public: *globalMockLocation_ = mock_.get(); } - ScopedStrictMockFunction(const ScopedStrictMockFunction&) = delete; - ScopedStrictMockFunction& operator=(const ScopedStrictMockFunction&) = delete; - ScopedStrictMockFunction(ScopedStrictMockFunction&& rhs) : globalMockLocation_(rhs.globalMockLocation_), mock_(std::move(rhs.mock_)) { rhs.globalMockLocation_ = nullptr; } diff --git a/kotlin-native/runtime/src/main/cpp/Utils.hpp b/kotlin-native/runtime/src/main/cpp/Utils.hpp new file mode 100644 index 00000000000..1f10f53082a --- /dev/null +++ b/kotlin-native/runtime/src/main/cpp/Utils.hpp @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_UTILS_H +#define RUNTIME_UTILS_H + +namespace kotlin { + +// A helper for implementing classes with disabled copy constructor and copy assignment. +// Usage: +// class A: private MoveOnly { +// ... +// }; +// Prefer private inheritance to discourage casting instances of `A` to instances +// of `MoveOnly`. +class MoveOnly { + // Hide constructors, assignments and destructor, to discourage operating on an instance of `MoveOnly`. +protected: + MoveOnly() noexcept = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) noexcept = default; + + MoveOnly& operator=(const MoveOnly&) = delete; + MoveOnly& operator=(MoveOnly&&) noexcept = default; + + // Not virtual by design. Since this class hides this destructor, no one can destroy an + // instance of `MoveOnly` directly, so this destructor is never called in a virtual manner. + ~MoveOnly() = default; +}; + +// A helper for implementing classes with disabled copy and move constructors, and copy and move assignments. +// Usage: +// class A: private Pinned { +// ... +// }; +// Prefer private inheritance to discourage casting instances of `A` to instances +// of `Pinned`. +class Pinned { + // Hide constructors, assignments and destructor, to discourage operating on an instance of `Pinned`. +protected: + Pinned() noexcept = default; + Pinned(const Pinned&) = delete; + Pinned(Pinned&&) = delete; + + Pinned& operator=(const Pinned&) = delete; + Pinned& operator=(Pinned&&) = delete; + + // Not virtual by design. Since this class hides this destructor, no one can destroy an + // instance of `Pinned` directly, so this destructor is never called in a virtual manner. + ~Pinned() = default; +}; + +} // namespace kotlin + +#endif // RUNTIME_UTILS_H diff --git a/kotlin-native/runtime/src/main/cpp/UtilsTest.cpp b/kotlin-native/runtime/src/main/cpp/UtilsTest.cpp new file mode 100644 index 00000000000..b6403b45766 --- /dev/null +++ b/kotlin-native/runtime/src/main/cpp/UtilsTest.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include "Utils.hpp" + +#include + +#include "gtest/gtest.h" + +namespace { + +// TODO: use std variants when we move to C++17 +template +constexpr bool is_nothrow_default_constructible_v = std::is_nothrow_default_constructible::value; +template +constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible::value; +template +constexpr bool is_copy_constructible_v = std::is_copy_constructible::value; +template +constexpr bool is_copy_assignable_v = std::is_copy_assignable::value; +template +constexpr bool is_move_constructible_v = std::is_move_constructible::value; +template +constexpr bool is_move_assignable_v = std::is_move_assignable::value; +template +constexpr bool is_nothrow_move_constructible_v = std::is_nothrow_move_constructible::value; +template +constexpr bool is_nothrow_move_assignable_v = std::is_nothrow_move_assignable::value; + +struct A { + int field; +}; + +class MoveOnlyImpl : private kotlin::MoveOnly { +public: + A a; +}; + +class PinnedImpl : private kotlin::Pinned { +public: + A a; +}; + +} // namespace + +TEST(UtilsTest, MoveOnlyImpl) { + static_assert(is_nothrow_default_constructible_v, "Must be nothrow default constructible"); + static_assert(is_nothrow_destructible_v, "Must be nothrow destructible"); + static_assert(!is_copy_constructible_v, "Must not be copy constructible"); + static_assert(!is_copy_assignable_v, "Must not be copy assignable"); + static_assert(is_nothrow_move_constructible_v, "Must be nothrow move constructible"); + static_assert(is_nothrow_move_assignable_v, "Must be nothrow move assignable"); + static_assert(sizeof(MoveOnlyImpl) == sizeof(A), "Must not increase size"); +} + +TEST(UtilsTest, PinnedImpl) { + static_assert(is_nothrow_default_constructible_v, "Must be nothrow default constructible"); + static_assert(is_nothrow_destructible_v, "Must be nothrow destructible"); + static_assert(!is_copy_constructible_v, "Must not be copy constructible"); + static_assert(!is_copy_assignable_v, "Must not be copy assignable"); + static_assert(!is_move_constructible_v, "Must not be move constructible"); + static_assert(!is_move_assignable_v, "Must not be move assignable"); + static_assert(sizeof(PinnedImpl) == sizeof(A), "Must not increase size"); +} diff --git a/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm b/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm index bbe2641770a..911008d2596 100644 --- a/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm +++ b/kotlin-native/runtime/src/objc/cpp/ObjCExportClasses.mm @@ -22,7 +22,7 @@ #import "ObjCExportInit.h" #import "ObjCExportPrivate.h" #import "Runtime.h" -#import "Utils.h" +#import "Mutex.hpp" #import "Exceptions.h" extern "C" id objc_retainAutoreleaseReturnValue(id self); diff --git a/kotlin-native/runtime/src/objc/cpp/ObjCExportNumbers.mm b/kotlin-native/runtime/src/objc/cpp/ObjCExportNumbers.mm index e721dab6991..4ca3850029a 100644 --- a/kotlin-native/runtime/src/objc/cpp/ObjCExportNumbers.mm +++ b/kotlin-native/runtime/src/objc/cpp/ObjCExportNumbers.mm @@ -23,7 +23,7 @@ #import "ObjCExport.h" #import "Runtime.h" -#import "Utils.h" +#import "Mutex.hpp" extern "C" {