[K/N] Add tracking of application state to the GC scheduler.
Merge-request: KT-MR-6435 Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
committed by
Space
parent
fcfc79aa35
commit
d47193d36f
@@ -25,6 +25,8 @@ RUNTIME_WEAK Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = n
|
||||
#ifdef KONAN_ANDROID
|
||||
RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1;
|
||||
#endif
|
||||
// Keep it 0 even when the compiler defaults to 1: if the overriding mechanism breaks, keeping it disabled is safer.
|
||||
RUNTIME_WEAK int32_t Kotlin_appStateTracking = 0;
|
||||
|
||||
ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept {
|
||||
return static_cast<compiler::DestroyRuntimeMode>(Kotlin_destroyRuntimeMode);
|
||||
@@ -39,6 +41,9 @@ ALWAYS_INLINE bool compiler::suspendFunctionsFromAnyThreadFromObjCEnabled() noex
|
||||
return Kotlin_suspendFunctionsFromAnyThreadFromObjC != 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE compiler::AppStateTracking compiler::appStateTracking() noexcept {
|
||||
return static_cast<compiler::AppStateTracking>(Kotlin_appStateTracking);
|
||||
}
|
||||
|
||||
#ifdef KONAN_ANDROID
|
||||
ALWAYS_INLINE bool compiler::printToAndroidLogcat() noexcept {
|
||||
@@ -52,4 +57,4 @@ ALWAYS_INLINE int compiler::getSourceInfo(void* addr, SourceInfo *result, int re
|
||||
} else {
|
||||
return Kotlin_getSourceInfo_Function(addr, result, result_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,11 @@ enum class GCSchedulerType {
|
||||
kAggressive = 3,
|
||||
};
|
||||
|
||||
// Must match AppStateTracking in AppStateTracking.kt
|
||||
enum class AppStateTracking {
|
||||
kDisabled = 0,
|
||||
kEnabled = 1,
|
||||
};
|
||||
|
||||
ALWAYS_INLINE inline bool shouldContainDebugInfo() noexcept {
|
||||
return Kotlin_needDebugInfo != 0;
|
||||
@@ -101,6 +106,7 @@ ALWAYS_INLINE inline GCSchedulerType getGCSchedulerType() noexcept {
|
||||
WorkerExceptionHandling workerExceptionHandling() noexcept;
|
||||
DestroyRuntimeMode destroyRuntimeMode() noexcept;
|
||||
bool suspendFunctionsFromAnyThreadFromObjCEnabled() noexcept;
|
||||
AppStateTracking appStateTracking() noexcept;
|
||||
int getSourceInfo(void* addr, SourceInfo *result, int result_size) noexcept;
|
||||
|
||||
#ifdef KONAN_ANDROID
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#include <functional>
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include "ObjCForward.hpp"
|
||||
#include "ObjectPtr.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
OBJC_FORWARD_DECLARE(NSNotificationCenter);
|
||||
OBJC_FORWARD_DECLARE(NSString);
|
||||
|
||||
namespace kotlin::objc_support {
|
||||
|
||||
class NSNotificationSubscription : private MoveOnly {
|
||||
public:
|
||||
NSNotificationSubscription(NSNotificationCenter* center, NSString* name, std::function<void()> handler) noexcept;
|
||||
NSNotificationSubscription(NSString* name, std::function<void()> handler) noexcept;
|
||||
|
||||
NSNotificationSubscription(NSNotificationSubscription&&) = default;
|
||||
NSNotificationSubscription& operator=(NSNotificationSubscription&&) = default;
|
||||
|
||||
~NSNotificationSubscription() { reset(); }
|
||||
|
||||
void reset() noexcept;
|
||||
bool subscribed() const noexcept;
|
||||
explicit operator bool() const noexcept { return subscribed(); }
|
||||
|
||||
private:
|
||||
object_ptr<NSNotificationCenter> center_;
|
||||
// center_ will hold the strong reference to token_ anyway.
|
||||
id token_;
|
||||
};
|
||||
|
||||
} // namespace kotlin::objc_support
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#include "NSNotificationSubscription.hpp"
|
||||
|
||||
#import <Foundation/NSNotification.h>
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
objc_support::NSNotificationSubscription::NSNotificationSubscription(
|
||||
NSNotificationCenter* center, NSString* name, std::function<void()> handler) noexcept :
|
||||
center_([center retain]),
|
||||
token_([center addObserverForName:name
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification* notification) {
|
||||
handler();
|
||||
}]) {}
|
||||
|
||||
objc_support::NSNotificationSubscription::NSNotificationSubscription(NSString* name, std::function<void()> handler) noexcept :
|
||||
NSNotificationSubscription([NSNotificationCenter defaultCenter], name, std::move(handler)) {}
|
||||
|
||||
bool objc_support::NSNotificationSubscription::subscribed() const noexcept {
|
||||
return token_ != nil;
|
||||
}
|
||||
|
||||
void objc_support::NSNotificationSubscription::reset() noexcept {
|
||||
@autoreleasepool {
|
||||
[*center_ removeObserver:token_];
|
||||
center_.reset();
|
||||
token_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#include "NSNotificationSubscription.hpp"
|
||||
|
||||
#import <Foundation/NSNotification.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "std_support/Memory.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
class WithDestructorHook;
|
||||
|
||||
using DestructorHook = void(WithDestructorHook*);
|
||||
|
||||
class WithDestructorHook : private Pinned {
|
||||
public:
|
||||
explicit WithDestructorHook(std::function<DestructorHook> hook) : hook_(std::move(hook)) {}
|
||||
|
||||
~WithDestructorHook() { hook_(this); }
|
||||
|
||||
private:
|
||||
std::function<DestructorHook> hook_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface Kotlin_objc_support_NSNotificationSubscriptionTest : NSObject {
|
||||
NSNotificationCenter* center_;
|
||||
std::function<void()> handler_;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNotificationCenter:(NSNotificationCenter*)center
|
||||
name:(NSNotificationName)name
|
||||
handler:(std::function<void()>)handler;
|
||||
|
||||
- (void)reset;
|
||||
|
||||
- (void)onNotification:(NSNotification*)notification;
|
||||
|
||||
@end
|
||||
|
||||
@implementation Kotlin_objc_support_NSNotificationSubscriptionTest
|
||||
|
||||
- (instancetype)initWithNotificationCenter:(NSNotificationCenter*)center
|
||||
name:(NSNotificationName)name
|
||||
handler:(std::function<void()>)handler {
|
||||
if ((self = [super init])) {
|
||||
center_ = center;
|
||||
handler_ = std::move(handler);
|
||||
|
||||
[center_ addObserver:self selector:@selector(onNotification:) name:name object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
[center_ removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)onNotification:(NSNotification*)notification {
|
||||
handler_();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
class NSNotificationSubscriptionTest : public testing::Test {
|
||||
public:
|
||||
objc_support::NSNotificationSubscription subscribe(const char* name, std::function<void()> handler) noexcept {
|
||||
return objc_support::NSNotificationSubscription(center_, [NSString stringWithUTF8String:name], std::move(handler));
|
||||
}
|
||||
|
||||
objc_support::object_ptr<Kotlin_objc_support_NSNotificationSubscriptionTest> subscribeOther(
|
||||
const char* name, std::function<void()> handler) noexcept {
|
||||
return objc_support::object_ptr<Kotlin_objc_support_NSNotificationSubscriptionTest>(
|
||||
[[Kotlin_objc_support_NSNotificationSubscriptionTest alloc] initWithNotificationCenter:center_
|
||||
name:[NSString stringWithUTF8String:name]
|
||||
handler:std::move(handler)]);
|
||||
}
|
||||
|
||||
void post(const char* name) noexcept { [center_ postNotificationName:[NSString stringWithUTF8String:name] object:nil]; }
|
||||
|
||||
private:
|
||||
NSNotificationCenter* center_ = [[NSNotificationCenter alloc] init];
|
||||
};
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, Subscribed) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler;
|
||||
|
||||
auto subscription = subscribe(name, handler.AsStdFunction());
|
||||
EXPECT_TRUE(subscription.subscribed());
|
||||
EXPECT_TRUE(subscription);
|
||||
|
||||
subscription.reset();
|
||||
EXPECT_FALSE(subscription.subscribed());
|
||||
EXPECT_FALSE(subscription);
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, Post) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler;
|
||||
|
||||
auto subscription = subscribe(name, handler.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(handler, Call());
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler);
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, PostWrongName) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
constexpr const char* wrongName = "NOTIFICATION_NAME_WRONG";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler;
|
||||
|
||||
auto subscription = subscribe(name, handler.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(handler, Call()).Times(0);
|
||||
post(wrongName);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler);
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, PostAfterReset) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler;
|
||||
|
||||
auto subscription = subscribe(name, handler.AsStdFunction());
|
||||
subscription.reset();
|
||||
|
||||
EXPECT_CALL(handler, Call()).Times(0);
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler);
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, PostAfterDtor) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler;
|
||||
|
||||
{
|
||||
// Create and destroy subscription object.
|
||||
subscribe(name, handler.AsStdFunction());
|
||||
}
|
||||
|
||||
EXPECT_CALL(handler, Call()).Times(0);
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler);
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, MultipleSubscribers) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler1;
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler2;
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler3;
|
||||
testing::StrictMock<testing::MockFunction<void()>> handler4;
|
||||
|
||||
auto subscription1 = subscribeOther(name, handler1.AsStdFunction());
|
||||
auto subscription2 = subscribe(name, handler2.AsStdFunction());
|
||||
auto subscription3 = subscribe(name, handler3.AsStdFunction());
|
||||
auto subscription4 = subscribeOther(name, handler4.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(handler1, Call());
|
||||
EXPECT_CALL(handler2, Call());
|
||||
EXPECT_CALL(handler3, Call());
|
||||
EXPECT_CALL(handler4, Call());
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler1);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler2);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler3);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler4);
|
||||
|
||||
subscription3.reset();
|
||||
|
||||
EXPECT_CALL(handler1, Call());
|
||||
EXPECT_CALL(handler2, Call());
|
||||
EXPECT_CALL(handler4, Call());
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler1);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler2);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler4);
|
||||
|
||||
[*subscription4 reset];
|
||||
subscription4.reset();
|
||||
|
||||
EXPECT_CALL(handler1, Call());
|
||||
EXPECT_CALL(handler2, Call());
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler1);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler2);
|
||||
|
||||
subscription2.reset();
|
||||
|
||||
EXPECT_CALL(handler1, Call());
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&handler1);
|
||||
|
||||
// Make sure to unsubscribe.
|
||||
[*subscription1 reset];
|
||||
}
|
||||
|
||||
TEST_F(NSNotificationSubscriptionTest, DestroysHandler) {
|
||||
constexpr const char* name = "NOTIFICATION_NAME";
|
||||
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(_)).Times(0);
|
||||
auto subscription =
|
||||
subscribe(name, [withDestructorHook = std_support::make_shared<WithDestructorHook>(destructorHook.AsStdFunction())] {});
|
||||
post(name);
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(_));
|
||||
subscription.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __OBJC__
|
||||
#define OBJC_FORWARD_DECLARE(clazz) @class clazz
|
||||
#else
|
||||
#define OBJC_FORWARD_DECLARE(clazz) class clazz
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#if defined(__has_feature) && __has_feature(objc_arc)
|
||||
#error "Assumes that ARC is not used"
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "ObjCForward.hpp"
|
||||
|
||||
OBJC_FORWARD_DECLARE(NSObject);
|
||||
|
||||
namespace kotlin::objc_support {
|
||||
|
||||
namespace internal {
|
||||
|
||||
class ObjectPtrImpl {
|
||||
public:
|
||||
ObjectPtrImpl() noexcept;
|
||||
explicit ObjectPtrImpl(NSObject* object) noexcept;
|
||||
|
||||
ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept;
|
||||
ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept;
|
||||
|
||||
~ObjectPtrImpl();
|
||||
|
||||
ObjectPtrImpl& operator=(const ObjectPtrImpl& rhs) noexcept {
|
||||
ObjectPtrImpl tmp(rhs);
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ObjectPtrImpl& operator=(ObjectPtrImpl&& rhs) noexcept {
|
||||
ObjectPtrImpl tmp(std::move(rhs));
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(ObjectPtrImpl& rhs) noexcept;
|
||||
|
||||
NSObject* get() const noexcept;
|
||||
bool valid() const noexcept;
|
||||
|
||||
void reset() noexcept;
|
||||
void reset(NSObject* object) noexcept;
|
||||
|
||||
bool operator==(const ObjectPtrImpl& rhs) const noexcept;
|
||||
bool operator<(const ObjectPtrImpl& rhs) const noexcept;
|
||||
|
||||
std::size_t computeHash() const noexcept;
|
||||
|
||||
private:
|
||||
NSObject* object_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// `std::shared_ptr`-like smart pointer for ObjC objects.
|
||||
//
|
||||
// IMPORTANT: Must be constructed from a retained ObjC object.
|
||||
// This optimizes for the common case of constructing directly from `[[T alloc] init...]`.
|
||||
//
|
||||
// Unlike a regular C++ smart pointer `operator*` does not return a reference but return a pointer
|
||||
// for convenient syntax of calling ObjC methods: `[*smartPtr methodName]`.
|
||||
// Use `(*smartPtr).propertyName` to access properties.
|
||||
// Use `smartPtr->fieldName` to access fields.
|
||||
//
|
||||
// Implements hashes and comparisons making it suitable to store in associative containers.
|
||||
// Equality comparison is shallow (does not use `[NSObject isEqual:]`) mirroring regular C++ smart pointers.
|
||||
//
|
||||
// When transfering ownership, prefer to move because it avoids calling `retain` and `release`.
|
||||
template <typename T>
|
||||
class object_ptr {
|
||||
public:
|
||||
// Construct empty `object_ptr`.
|
||||
object_ptr() noexcept = default;
|
||||
|
||||
// Construct from ObjC object.
|
||||
//
|
||||
// IMPORTANT: this does not `retain` `ptr`.
|
||||
explicit object_ptr(T* ptr) noexcept : impl_(ptr) {}
|
||||
|
||||
void swap(object_ptr<T>& rhs) noexcept { impl_.swap(rhs.impl_); }
|
||||
|
||||
T* get() const noexcept { return (T*)impl_.get(); }
|
||||
T* operator*() const noexcept { return (T*)impl_.get(); }
|
||||
T* operator->() const noexcept { return (T*)impl_.get(); }
|
||||
|
||||
explicit operator bool() const noexcept { return impl_.valid(); }
|
||||
|
||||
// Release stored pointer and become empty.
|
||||
void reset() noexcept { impl_.reset(); }
|
||||
|
||||
// Release stored pointer and store `ptr` instead.
|
||||
//
|
||||
// IMPORTANT: this does not `retain` `ptr`.
|
||||
void reset(T* ptr) noexcept { impl_.reset(ptr); }
|
||||
|
||||
template <typename U>
|
||||
bool operator==(const object_ptr<U>& rhs) const noexcept {
|
||||
return impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool operator<(const object_ptr<U>& rhs) const noexcept {
|
||||
return impl_ < rhs.impl_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct std::hash<object_ptr>;
|
||||
|
||||
template <typename U>
|
||||
friend class object_ptr;
|
||||
|
||||
internal::ObjectPtrImpl impl_;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const object_ptr<T>& lhs, const object_ptr<U>& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>(const object_ptr<T>& lhs, const object_ptr<U>& rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator<=(const object_ptr<T>& lhs, const object_ptr<U>& rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>=(const object_ptr<T>& lhs, const object_ptr<U>& rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace kotlin::objc_support
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename T>
|
||||
void swap(kotlin::objc_support::object_ptr<T>& lhs, kotlin::objc_support::object_ptr<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct hash<kotlin::objc_support::object_ptr<T>> {
|
||||
std::size_t operator()(const kotlin::objc_support::object_ptr<T>& value) { return std::hash<NSObject*>()(value.impl_.get()); }
|
||||
};
|
||||
|
||||
// TODO: std::atomic specialization?
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#include "ObjectPtr.hpp"
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl() noexcept : object_(nil) {}
|
||||
|
||||
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(NSObject* object) noexcept : object_(object) {}
|
||||
|
||||
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept : object_([rhs.object_ retain]) {}
|
||||
|
||||
objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept : object_(rhs.object_) {
|
||||
rhs.object_ = nil;
|
||||
}
|
||||
|
||||
objc_support::internal::ObjectPtrImpl::~ObjectPtrImpl() {
|
||||
@autoreleasepool {
|
||||
[object_ release];
|
||||
}
|
||||
}
|
||||
|
||||
void objc_support::internal::ObjectPtrImpl::swap(ObjectPtrImpl& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(object_, rhs.object_);
|
||||
}
|
||||
|
||||
NSObject* objc_support::internal::ObjectPtrImpl::get() const noexcept {
|
||||
return object_;
|
||||
}
|
||||
|
||||
bool objc_support::internal::ObjectPtrImpl::valid() const noexcept {
|
||||
return object_ != nil;
|
||||
}
|
||||
|
||||
void objc_support::internal::ObjectPtrImpl::reset() noexcept {
|
||||
reset(nil);
|
||||
}
|
||||
|
||||
void objc_support::internal::ObjectPtrImpl::reset(NSObject* object) noexcept {
|
||||
@autoreleasepool {
|
||||
[object_ release];
|
||||
object_ = object;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t objc_support::internal::ObjectPtrImpl::computeHash() const noexcept {
|
||||
return object_.hash;
|
||||
}
|
||||
|
||||
bool objc_support::internal::ObjectPtrImpl::operator==(const ObjectPtrImpl& rhs) const noexcept {
|
||||
return object_ == rhs.object_;
|
||||
}
|
||||
|
||||
bool objc_support::internal::ObjectPtrImpl::operator<(const ObjectPtrImpl& rhs) const noexcept {
|
||||
return std::less<>()(object_, rhs.object_);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#if KONAN_HAS_FOUNDATION_FRAMEWORK
|
||||
|
||||
#include "ObjectPtr.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "std_support/Memory.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
class WithDestructorHook;
|
||||
|
||||
using DestructorHook = void(WithDestructorHook*);
|
||||
|
||||
class WithDestructorHook : private Pinned {
|
||||
public:
|
||||
explicit WithDestructorHook(std::function<DestructorHook> hook) : hook_(std::move(hook)) {}
|
||||
|
||||
~WithDestructorHook() { hook_(this); }
|
||||
|
||||
private:
|
||||
std::function<DestructorHook> hook_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface WithDestructorHookObjC : NSObject {
|
||||
std_support::unique_ptr<WithDestructorHook> impl_;
|
||||
}
|
||||
|
||||
@property(readonly) WithDestructorHook* impl;
|
||||
|
||||
- (instancetype)initWithDestructorHook:(std::function<DestructorHook>)hook;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WithDestructorHookObjC
|
||||
|
||||
- (instancetype)initWithDestructorHook:(std::function<DestructorHook>)hook {
|
||||
if ((self = [super init])) {
|
||||
impl_ = std_support::make_unique<WithDestructorHook>(hook);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (WithDestructorHook*)impl {
|
||||
return impl_.get();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#define EXPECT_CONTAINS(smartPtr, ptr) \
|
||||
do { \
|
||||
EXPECT_THAT(static_cast<bool>(smartPtr), (ptr) != nil); \
|
||||
EXPECT_THAT(*(smartPtr), (ptr)); \
|
||||
EXPECT_THAT((smartPtr).get(), (ptr)); \
|
||||
} while (false)
|
||||
|
||||
TEST(ObjectPtrTest, DefaultCtor) {
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj;
|
||||
EXPECT_CONTAINS(obj, nil);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ObjectCtor) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr);
|
||||
EXPECT_CONTAINS(obj, ptr);
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, CopyCtor) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(obj1);
|
||||
EXPECT_CONTAINS(obj1, ptr);
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
|
||||
obj1.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, MoveCtor) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(std::move(obj1));
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, CopyAssignmentIntoEmpty) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2;
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr);
|
||||
obj2 = obj1;
|
||||
EXPECT_CONTAINS(obj1, ptr);
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, CopyAssignmentIntoSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(ptr2);
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
obj2 = obj1;
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj1, ptr1);
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl)).Times(0);
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, MoveAssignmentIntoEmpty) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2;
|
||||
{
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0);
|
||||
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr);
|
||||
obj2 = std::move(obj1);
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj2, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, MoveAssignmentIntoSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(ptr2);
|
||||
{
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl)).Times(0);
|
||||
obj2 = std::move(obj1);
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, SwapEmptyEmpty) {
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2;
|
||||
obj1.swap(obj2);
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, nil);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, SwapEmptySet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(ptr2);
|
||||
obj1.swap(obj2);
|
||||
EXPECT_CONTAINS(obj1, ptr2);
|
||||
EXPECT_CONTAINS(obj2, nil);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, SwapSetEmpty) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr1);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2;
|
||||
obj1.swap(obj2);
|
||||
EXPECT_CONTAINS(obj1, nil);
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, SwapSetSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr1);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(ptr2);
|
||||
obj1.swap(obj2);
|
||||
EXPECT_CONTAINS(obj1, ptr2);
|
||||
EXPECT_CONTAINS(obj2, ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ResetEmptyFromEmpty) {
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj;
|
||||
obj.reset();
|
||||
EXPECT_CONTAINS(obj, nil);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ResetEmptyFromSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
obj.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
EXPECT_CONTAINS(obj, nil);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ResetObjectFromEmpty) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj;
|
||||
|
||||
obj.reset(ptr);
|
||||
EXPECT_CONTAINS(obj, ptr);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ResetObjectFromSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr1);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
obj.reset(ptr2);
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
EXPECT_CONTAINS(obj, ptr2);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
}
|
||||
|
||||
#define EXPECT_EQUAL(expr1, expr2) \
|
||||
do { \
|
||||
EXPECT_TRUE((expr1) == (expr2)); \
|
||||
EXPECT_FALSE((expr1) != (expr2)); \
|
||||
EXPECT_FALSE((expr1) < (expr2)); \
|
||||
EXPECT_TRUE((expr1) <= (expr2)); \
|
||||
EXPECT_FALSE((expr1) > (expr2)); \
|
||||
EXPECT_TRUE((expr1) >= (expr2)); \
|
||||
} while (false)
|
||||
|
||||
#define EXPECT_LESS(expr1, expr2) \
|
||||
do { \
|
||||
EXPECT_FALSE((expr1) == (expr2)); \
|
||||
EXPECT_TRUE((expr1) != (expr2)); \
|
||||
EXPECT_TRUE((expr1) < (expr2)); \
|
||||
EXPECT_TRUE((expr1) <= (expr2)); \
|
||||
EXPECT_FALSE((expr1) > (expr2)); \
|
||||
EXPECT_FALSE((expr1) >= (expr2)); \
|
||||
} while (false)
|
||||
|
||||
#define EXPECT_GREATER(expr1, expr2) \
|
||||
do { \
|
||||
EXPECT_FALSE((expr1) == (expr2)); \
|
||||
EXPECT_TRUE((expr1) != (expr2)); \
|
||||
EXPECT_FALSE((expr1) < (expr2)); \
|
||||
EXPECT_FALSE((expr1) <= (expr2)); \
|
||||
EXPECT_TRUE((expr1) > (expr2)); \
|
||||
EXPECT_TRUE((expr1) >= (expr2)); \
|
||||
} while (false)
|
||||
|
||||
TEST(ObjectPtrTest, ComparisonsEmpty) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> empty;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> reset(ptr);
|
||||
EXPECT_CALL(destructorHook, Call(ptr.impl));
|
||||
reset.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
objc_support::object_ptr<NSObject> emptyOtherType;
|
||||
|
||||
EXPECT_EQUAL(empty, empty);
|
||||
EXPECT_EQUAL(empty, objc_support::object_ptr<WithDestructorHookObjC>());
|
||||
EXPECT_EQUAL(empty, reset);
|
||||
EXPECT_EQUAL(empty, emptyOtherType);
|
||||
EXPECT_EQUAL(reset, empty);
|
||||
EXPECT_EQUAL(reset, reset);
|
||||
EXPECT_EQUAL(reset, emptyOtherType);
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ComparisonsEmptySet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr3 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr1);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> empty;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> reset(ptr2);
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
reset.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
objc_support::object_ptr<NSObject> emptyOtherType;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> objOtherType(ptr3);
|
||||
|
||||
EXPECT_GREATER(obj, empty);
|
||||
EXPECT_GREATER(obj, reset);
|
||||
EXPECT_GREATER(obj, emptyOtherType);
|
||||
EXPECT_LESS(empty, obj);
|
||||
EXPECT_LESS(empty, objOtherType);
|
||||
EXPECT_LESS(reset, obj);
|
||||
EXPECT_LESS(reset, objOtherType);
|
||||
EXPECT_GREATER(objOtherType, empty);
|
||||
EXPECT_GREATER(objOtherType, reset);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
EXPECT_CALL(destructorHook, Call(ptr3.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, ComparisonsSetSet) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
if (ptr2 < ptr1) {
|
||||
std::swap(ptr1, ptr2);
|
||||
}
|
||||
ASSERT_LT(ptr1, ptr2);
|
||||
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj1(ptr1);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj2(ptr2);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj3(obj1);
|
||||
objc_support::object_ptr<NSObject> objOtherType([ptr1 retain]);
|
||||
|
||||
EXPECT_EQUAL(obj1, obj1);
|
||||
EXPECT_LESS(obj1, obj2);
|
||||
EXPECT_EQUAL(obj1, obj3);
|
||||
EXPECT_EQUAL(obj1, objOtherType);
|
||||
EXPECT_GREATER(obj2, obj1);
|
||||
EXPECT_EQUAL(obj2, obj2);
|
||||
EXPECT_GREATER(obj2, obj3);
|
||||
EXPECT_GREATER(obj2, objOtherType);
|
||||
EXPECT_EQUAL(obj3, obj1);
|
||||
EXPECT_LESS(obj3, obj2);
|
||||
EXPECT_EQUAL(obj3, obj3);
|
||||
EXPECT_EQUAL(obj3, objOtherType);
|
||||
EXPECT_EQUAL(objOtherType, obj1);
|
||||
EXPECT_LESS(objOtherType, obj2);
|
||||
EXPECT_EQUAL(objOtherType, obj3);
|
||||
EXPECT_EQUAL(objOtherType, objOtherType);
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
}
|
||||
|
||||
TEST(ObjectPtrTest, Hashing) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> destructorHook;
|
||||
|
||||
auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()];
|
||||
objc_support::object_ptr<WithDestructorHookObjC> obj(ptr1);
|
||||
objc_support::object_ptr<WithDestructorHookObjC> empty;
|
||||
objc_support::object_ptr<WithDestructorHookObjC> reset(ptr2);
|
||||
EXPECT_CALL(destructorHook, Call(ptr2.impl));
|
||||
reset.reset();
|
||||
testing::Mock::VerifyAndClearExpectations(&destructorHook);
|
||||
|
||||
using Hash = std::hash<objc_support::object_ptr<WithDestructorHookObjC>>;
|
||||
using HashImpl = std::hash<NSObject*>;
|
||||
|
||||
EXPECT_THAT(Hash()(obj), HashImpl()(ptr1));
|
||||
EXPECT_THAT(Hash()(empty), HashImpl()(nullptr));
|
||||
EXPECT_THAT(Hash()(reset), HashImpl()(nullptr));
|
||||
|
||||
EXPECT_CALL(destructorHook, Call(ptr1.impl));
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user