524f0d335e
^KT-61259
162 lines
4.7 KiB
Kotlin
Vendored
162 lines
4.7 KiB
Kotlin
Vendored
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, kotlin.native.runtime.NativeRuntimeApi::class)
|
|
|
|
import kotlin.native.ref.WeakReference
|
|
import kotlinx.cinterop.*
|
|
import kotlin.test.*
|
|
import objcTests.*
|
|
|
|
// Note: these tests rely on GC assertions: without the fix and the assertions it won't actually crash.
|
|
// GC should fire an assertion if it obtains a reference to Kotlin object that is being (or has been) deallocated.
|
|
|
|
@Test
|
|
fun testKT41811() {
|
|
// Attempt to make the state predictable:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
deallocRetainReleaseDeallocated = false
|
|
assertFalse(deallocRetainReleaseDeallocated)
|
|
|
|
createGarbageDeallocRetainRelease()
|
|
|
|
// Runs [DeallocRetainRelease dealloc]:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
assertTrue(deallocRetainReleaseDeallocated)
|
|
|
|
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
|
kotlin.native.runtime.GC.collect()
|
|
}
|
|
|
|
private fun createGarbageDeallocRetainRelease() {
|
|
autoreleasepool {
|
|
object : DeallocRetainRelease() {}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testKT41811LoadWeak() {
|
|
testKT41811LoadWeak(ObjCWeakReference())
|
|
}
|
|
|
|
@Test
|
|
fun testKT41811LoadKotlinWeak() {
|
|
val kotlinWeak = object : NSObject(), WeakReferenceProtocol {
|
|
lateinit var weakReference: WeakReference<Any>
|
|
|
|
override fun referent(): Any? {
|
|
return weakReference.value
|
|
}
|
|
|
|
override fun setReferent(referent: Any?) {
|
|
weakReference = WeakReference(referent!!)
|
|
}
|
|
}
|
|
|
|
testKT41811LoadWeak(kotlinWeak)
|
|
}
|
|
|
|
private fun testKT41811LoadWeak(weakRef: WeakReferenceProtocol) {
|
|
// Attempt to make the state predictable:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
deallocLoadWeakDeallocated = false
|
|
assertFalse(deallocLoadWeakDeallocated)
|
|
|
|
createGarbageDeallocLoadWeak(weakRef)
|
|
|
|
// Runs [DeallocLoadWeak dealloc]:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
assertTrue(deallocLoadWeakDeallocated)
|
|
|
|
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
weakDeallocLoadWeak = null
|
|
}
|
|
|
|
private fun createGarbageDeallocLoadWeak(weakRef: WeakReferenceProtocol) {
|
|
autoreleasepool {
|
|
val obj = object : DeallocLoadWeak() {}
|
|
weakDeallocLoadWeak = weakRef.apply { referent = obj }
|
|
obj.checkWeak()
|
|
assertSame(obj, weakDeallocLoadWeak!!.referent)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testKT41811WithGlobal() {
|
|
// Attempt to make the state predictable:
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
deallocRetainReleaseDeallocated = false
|
|
assertFalse(deallocRetainReleaseDeallocated)
|
|
|
|
autoreleasepool {
|
|
{
|
|
globalDeallocRetainRelease = object: DeallocRetainRelease() {}
|
|
}()
|
|
}
|
|
|
|
assertFalse(deallocRetainReleaseDeallocated)
|
|
|
|
// Clean up local DeallocRetainRelease on Kotlin side
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
assertFalse(deallocRetainReleaseDeallocated)
|
|
|
|
// And drop the last reference to DeallocRetainRelease from ObjC global scope.
|
|
globalDeallocRetainRelease = null
|
|
|
|
assertFalse(deallocRetainReleaseDeallocated)
|
|
|
|
// This will dispose `DeallocRetainRelease` on Kotlin side, which will cause `dealloc`
|
|
// on ObjC side, which triggers `retain` and `release` of `self`. If these messages
|
|
// were to reach Kotlin side, the `release` would have immediately scheduled the
|
|
// second disposal of Kotlin object.
|
|
kotlin.native.runtime.GC.collect()
|
|
|
|
assertTrue(deallocRetainReleaseDeallocated)
|
|
}
|
|
|
|
var localDeallocRetainAndAccessDeallocated = false
|
|
|
|
@Test
|
|
fun testKT41811WithAccess() {
|
|
// Legacy MM crashes with an assertion failure.
|
|
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
|
if (!isExperimentalMM())
|
|
return
|
|
|
|
// Attempt to make the state predictable:
|
|
kotlin.native.internal.GC.collect()
|
|
|
|
deallocRetainAndAccessDeallocated = false
|
|
localDeallocRetainAndAccessDeallocated = false
|
|
|
|
createGarbageDeallocRetainAndAccess()
|
|
|
|
// Runs [DeallocRetainAndAccess dealloc]:
|
|
kotlin.native.internal.GC.collect()
|
|
|
|
assertTrue(deallocRetainAndAccessDeallocated)
|
|
assertTrue(localDeallocRetainAndAccessDeallocated)
|
|
|
|
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
|
kotlin.native.internal.GC.collect()
|
|
}
|
|
|
|
private fun createGarbageDeallocRetainAndAccess() {
|
|
autoreleasepool {
|
|
object : DeallocRetainAndAccess() {
|
|
init {
|
|
onDealloc = {
|
|
assertNull(it)
|
|
assertFalse(localDeallocRetainAndAccessDeallocated)
|
|
localDeallocRetainAndAccessDeallocated = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|