diff --git a/backend.native/tests/objcexport/expectedLazy.h b/backend.native/tests/objcexport/expectedLazy.h index 9d5ed562745..b0607115696 100644 --- a/backend.native/tests/objcexport/expectedLazy.h +++ b/backend.native/tests/objcexport/expectedLazy.h @@ -1109,6 +1109,13 @@ __attribute__((swift_name("SharedRefs.MutableData"))) @property int32_t x __attribute__((swift_name("x"))); @end; +__attribute__((swift_name("TestRememberNewObject"))) +@protocol KtTestRememberNewObject +@required +- (id)getObject __attribute__((swift_name("getObject()"))); +- (void)waitForCleanup __attribute__((swift_name("waitForCleanup()"))); +@end; + __attribute__((swift_name("ClassForTypeCheck"))) @interface KtClassForTypeCheck : KtBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); @@ -1283,6 +1290,7 @@ __attribute__((swift_name("ValuesKt"))) + (void)error __attribute__((swift_name("error()"))) __attribute__((unavailable("error"))); + (void)warning __attribute__((swift_name("warning()"))) __attribute__((deprecated("warning"))); + (void)gc __attribute__((swift_name("gc()"))); ++ (void)testRememberNewObjectTest:(id)test __attribute__((swift_name("testRememberNewObject(test:)"))); + (BOOL)testClassTypeCheckX:(id)x __attribute__((swift_name("testClassTypeCheck(x:)"))); + (BOOL)testInterfaceTypeCheckX:(id)x __attribute__((swift_name("testInterfaceTypeCheck(x:)"))); + (int32_t)testAbstractInterfaceCallX:(id)x __attribute__((swift_name("testAbstractInterfaceCall(x:)"))); diff --git a/backend.native/tests/objcexport/values.kt b/backend.native/tests/objcexport/values.kt index f70e39f201d..01753e1c599 100644 --- a/backend.native/tests/objcexport/values.kt +++ b/backend.native/tests/objcexport/values.kt @@ -885,6 +885,17 @@ class SharedRefs { private val mustBeRemoved = mutableListOf>() } +interface TestRememberNewObject { + fun getObject(): Any + fun waitForCleanup() +} + +fun testRememberNewObject(test: TestRememberNewObject) { + val obj = autoreleasepool { test.getObject() } + test.waitForCleanup() + assertNotEquals("", obj.toString()) // Likely crashes if object is removed. +} + open class ClassForTypeCheck fun testClassTypeCheck(x: Any) = x is ClassForTypeCheck diff --git a/backend.native/tests/objcexport/values.swift b/backend.native/tests/objcexport/values.swift index 094c45e3e19..8abf98741ff 100644 --- a/backend.native/tests/objcexport/values.swift +++ b/backend.native/tests/objcexport/values.swift @@ -913,7 +913,7 @@ class TestSharedRefs { DispatchQueue.global().async(execute: getClosure()) } - private static func runInNewThread(initializeKotlinRuntime: Bool, block: @escaping () -> Void) { + private static func launchInNewThread(initializeKotlinRuntime: Bool, block: @escaping () -> Void) -> pthread_t { class Closure { static var currentBlock: (() -> Void)? = nil static var initializeKotlinRuntime: Bool = false @@ -934,11 +934,19 @@ class TestSharedRefs { return nil }, nil) try! assertEquals(actual: createCode, expected: 0) + return thread! + } - let joinCode = pthread_join(thread!, nil) + private static func joinThread(thread: pthread_t) { + let joinCode = pthread_join(thread, nil) try! assertEquals(actual: joinCode, expected: 0) } + private static func runInNewThread(initializeKotlinRuntime: Bool, block: @escaping () -> Void) { + let thread = launchInNewThread(initializeKotlinRuntime: initializeKotlinRuntime, block: block) + joinThread(thread: thread) + } + private func runInNewThread(initializeKotlinRuntime: Bool, block: @escaping () -> Void) { return TestSharedRefs.runInNewThread(initializeKotlinRuntime: initializeKotlinRuntime, block: block) } @@ -1048,6 +1056,51 @@ class TestSharedRefs { try assertTrue(Deinit.weakVar2 === nil) } + func testRememberNewObject(createObject: @escaping (SharedRefs) -> AnyObject) throws { + + class TestImpl : TestRememberNewObject { + let cleanupFinishedSemaphore = DispatchSemaphore(value: 0) + let threadWaitingForCleanupSemaphore = DispatchSemaphore(value: 0) + + var obj: AnyObject? = nil + + func getObject() -> Any { + return obj! + } + + func waitForCleanup() { + threadWaitingForCleanupSemaphore.signal() + cleanupFinishedSemaphore.wait() + } + } + + let test = TestImpl() + + let refs = SharedRefs() + try assertFalse(refs.hasAliveObjects()) + + autoreleasepool { + test.obj = createObject(refs) + } + + try assertTrue(refs.hasAliveObjects()) + + let thread = TestSharedRefs.launchInNewThread(initializeKotlinRuntime: false) { + ValuesKt.testRememberNewObject(test: test) + } + + test.threadWaitingForCleanupSemaphore.wait() + test.obj = nil + ValuesKt.gc() + + try assertTrue(refs.hasAliveObjects()) + + test.cleanupFinishedSemaphore.signal() + + TestSharedRefs.joinThread(thread: thread) + try assertFalse(refs.hasAliveObjects()) + } + func test() throws { try testLambdaSimple() try testObjectPartialRelease() @@ -1064,6 +1117,9 @@ class TestSharedRefs { try testReferenceOutlivesThread(releaseWithKotlinRuntime: true) try testMoreWorkBeforeThreadExit() + try testRememberNewObject(createObject: { $0.createFrozenRegularObject() }) + try testRememberNewObject(createObject: { $0.createFrozenCollection() }) + usleep(300 * 1000) } }