diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index 933704ab4b8b4..4fb894cc09843 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -430,6 +430,9 @@ extern "C" void *swift_bridgeObjectRetain(void *value); /// Increment the strong retain count of an object which might not be a native /// Swift object. extern "C" void *swift_unknownRetain(void *value); +/// Increment the strong retain count of an object which might not be a native +/// Swift object by n. +extern "C" void *swift_unknownRetain_n(void *value, int n); #else @@ -437,6 +440,10 @@ static inline void swift_unknownRetain(void *value) { swift_retain(static_cast(value)); } +static inline void swift_unknownRetain_n(void *value, int n) { + swift_retain_n(static_cast(value), n); +} + #endif /* SWIFT_OBJC_INTEROP */ extern "C" void swift_bridgeObjectRelease(void *value); @@ -446,6 +453,9 @@ extern "C" void swift_bridgeObjectRelease(void *value); /// Decrement the strong retain count of an object which might not be a native /// Swift object. extern "C" void swift_unknownRelease(void *value); +/// Decrement the strong retain count of an object which might not be a native +/// Swift object by n. +extern "C" void swift_unknownRelease_n(void *value, int n); #else @@ -453,6 +463,10 @@ static inline void swift_unknownRelease(void *value) { swift_release(static_cast(value)); } +static inline void swift_unknownRelease_n(void *value, int n) { + swift_release_n(static_cast(value), n); +} + #endif /* SWIFT_OBJC_INTEROP */ #if SWIFT_OBJC_INTEROP diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 824a2ac1aee21..38132bfb3e2d0 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -566,18 +566,36 @@ static bool usesNativeSwiftReferenceCounting_unowned(const void *object) { return usesNativeSwiftReferenceCounting_allocated(object); } +void *swift::swift_unknownRetain_n(void *object, int n) { + void *objc_ret = nullptr; + if (isObjCTaggedPointerOrNull(object)) return object; + if (usesNativeSwiftReferenceCounting_allocated(object)) + return swift_retain_n(static_cast(object), n); + for (int i = 0; i < n; ++i) + objc_ret = objc_retain(static_cast(object)); + return objc_ret; +} + +void swift::swift_unknownRelease_n(void *object, int n) { + if (isObjCTaggedPointerOrNull(object)) return; + if (usesNativeSwiftReferenceCounting_allocated(object)) + return swift_release_n(static_cast(object), n); + for (int i = 0; i < n; ++i) + objc_release(static_cast(object)); +} + void *swift::swift_unknownRetain(void *object) { if (isObjCTaggedPointerOrNull(object)) return object; if (usesNativeSwiftReferenceCounting_allocated(object)) - return swift_retain((HeapObject*) object); - return objc_retain((id) object); + return swift_retain(static_cast(object)); + return objc_retain(static_cast(object)); } void swift::swift_unknownRelease(void *object) { if (isObjCTaggedPointerOrNull(object)) return; if (usesNativeSwiftReferenceCounting_allocated(object)) - return swift_release((HeapObject*) object); - return objc_release((id) object); + return swift_release(static_cast(object)); + return objc_release(static_cast(object)); } /// Return true iff the given BridgeObject is not known to use native diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 8efddf1b235a8..f4cbc9942a3dd 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -7,6 +7,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND find_library(FOUNDATION_LIBRARY Foundation) list(APPEND PLATFORM_SOURCES weak.mm + Refcounting.mm ) list(APPEND PLATFORM_TARGET_LINK_LIBRARIES ${FOUNDATION_LIBRARY} @@ -17,7 +18,6 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND add_swift_unittest(SwiftRuntimeTests Metadata.cpp Enum.cpp - Refcounting.cpp ${PLATFORM_SOURCES} ) diff --git a/unittests/runtime/Refcounting.cpp b/unittests/runtime/Refcounting.mm similarity index 60% rename from unittests/runtime/Refcounting.cpp rename to unittests/runtime/Refcounting.mm index d415a82cf1ef8..59a29cef45858 100644 --- a/unittests/runtime/Refcounting.cpp +++ b/unittests/runtime/Refcounting.mm @@ -10,12 +10,27 @@ // //===----------------------------------------------------------------------===// +#include +#include #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "gtest/gtest.h" using namespace swift; +static unsigned DestroyedObjCCount = 0; +/// A trivial class that increments DestroyedObjCCount when deallocated. +@interface ObjCTestClass : NSObject @end +@implementation ObjCTestClass +- (void) dealloc { + DestroyedObjCCount++; + [super dealloc]; +} +@end +static HeapObject *make_objc_object() { + return static_cast([ObjCTestClass new]); +} + struct TestObject : HeapObject { size_t *Addr; size_t Value; @@ -29,18 +44,19 @@ static void destroyTestObject(HeapObject *_object) { swift_deallocObject(object, sizeof(TestObject), alignof(TestObject) - 1); } -static const FullMetadata TestObjectMetadata{ - HeapMetadataHeader{{destroyTestObject}, {nullptr}}, - HeapMetadata{Metadata{MetadataKind::HeapLocalVariable}} +static const FullMetadata TestClassObjectMetadata = { + { { &destroyTestObject }, { &_TWVBo } }, + { { { MetadataKind::Class } }, 0, /*rodata*/ 1, + ClassFlags::UsesSwift1Refcounting, nullptr, 0, 0, 0, 0, 0 } }; /// Create an object that, when deallocated, stores the given value to /// the given pointer. static TestObject *allocTestObject(size_t *addr, size_t value) { auto result = - static_cast(swift_allocObject(&TestObjectMetadata, - sizeof(TestObject), - alignof(TestObject) - 1)); + static_cast(swift_allocObject(&TestClassObjectMetadata, + sizeof(TestObject), + alignof(TestObject) - 1)); result->Addr = addr; result->Value = value; return result; @@ -120,3 +136,40 @@ TEST(RefcountingTest, retain_release_n) { swift_release(object); EXPECT_EQ(1u, value); } + +TEST(RefcountingTest, unknown_retain_release_n) { + size_t value = 0; + auto object = allocTestObject(&value, 1); + EXPECT_EQ(0u, value); + auto retainResult = swift_unknownRetain_n(object, 32); + EXPECT_EQ(object, retainResult); + retainResult = swift_unknownRetain(object); + EXPECT_EQ(object, retainResult); + EXPECT_EQ(0u, value); + EXPECT_EQ(34u, swift_retainCount(object)); + swift_unknownRelease_n(object, 31); + EXPECT_EQ(0u, value); + EXPECT_EQ(3u, swift_retainCount(object)); + swift_unknownRelease(object); + EXPECT_EQ(0u, value); + EXPECT_EQ(2u, swift_retainCount(object)); + swift_unknownRelease_n(object, 1); + EXPECT_EQ(0u, value); + EXPECT_EQ(1u, swift_retainCount(object)); + swift_unknownRelease(object); + EXPECT_EQ(1u, value); +} + +TEST(RefcountingTest, objc_unknown_retain_release_n) { + auto object = make_objc_object(); + auto retainResult = swift_unknownRetain_n(object, 32); + EXPECT_EQ(object, retainResult); + retainResult = swift_unknownRetain(object); + EXPECT_EQ(object, retainResult); + swift_unknownRelease_n(object, 31); + swift_unknownRelease(object); + swift_unknownRelease_n(object, 1); + swift_unknownRelease(object); + // The object should be destroyed by now. + EXPECT_EQ(1u, DestroyedObjCCount); +}