Add missing test for Objective-C weak refs to Kotlin objects

This commit is contained in:
Svyatoslav Scherbina
2019-08-09 11:26:20 +03:00
committed by SvyatoslavScherbina
parent e240621550
commit 33ab792465
3 changed files with 180 additions and 0 deletions
@@ -951,6 +951,15 @@ __attribute__((swift_name("TopLevelHidden.InnerInner")))
@interface ValuesTopLevelHiddenInnerInner : NSObject
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("TestWeakRefs")))
@interface ValuesTestWeakRefs : KotlinBase
- (instancetype)initWithFrozen:(BOOL)frozen __attribute__((swift_name("init(frozen:)"))) __attribute__((objc_designated_initializer));
- (id)getObj __attribute__((swift_name("getObj()")));
- (void)clearObj __attribute__((swift_name("clearObj()")));
- (NSArray<id> *)createCycle __attribute__((swift_name("createCycle()")));
@end;
@interface ValuesEnumeration (ValuesKt)
- (ValuesEnumeration *)getAnswer __attribute__((swift_name("getAnswer()")));
@end;
@@ -1029,6 +1038,7 @@ __attribute__((swift_name("ValuesKt")))
+ (void)takeForwardDeclaredProtocolObj:(id<ForwardDeclared>)obj __attribute__((swift_name("takeForwardDeclaredProtocol(obj:)")));
+ (void)error __attribute__((swift_name("error()"))) __attribute__((unavailable("error")));
+ (void)warning __attribute__((swift_name("warning()"))) __attribute__((deprecated("warning")));
+ (void)gc __attribute__((swift_name("gc()")));
@property (class, readonly) double dbl __attribute__((swift_name("dbl")));
@property (class, readonly) float flt __attribute__((swift_name("flt")));
@property (class, readonly) int32_t integer __attribute__((swift_name("integer")));
@@ -710,3 +710,31 @@ open class TestDeprecation() {
@Deprecated("warning", level = DeprecationLevel.WARNING) fun warning() {}
@Deprecated("warning", level = DeprecationLevel.WARNING) val warningVal: Any? = null
@Deprecated("warning", level = DeprecationLevel.WARNING) var warningVar: Any? = null
fun gc() {
kotlin.native.internal.GC.collect()
}
class TestWeakRefs(private val frozen: Boolean) {
private var obj: Any? = Any().also {
if (frozen) it.freeze()
}
fun getObj() = obj!!
fun clearObj() {
obj = null
}
fun createCycle(): List<Any> {
val node1 = Node(null)
val node2 = Node(node1)
node1.next = node2
if (frozen) node1.freeze()
return listOf(node1, node2)
}
private class Node(var next: Node?)
}
@@ -667,6 +667,147 @@ func testDeprecation() throws {
try assertEquals(actual: test.callEffectivelyHidden(obj: ImplementingHiddenSubclass()), expected: -2)
}
func setAssociatedObject(object: AnyObject, value: AnyObject) {
objc_setAssociatedObject(
object,
UnsafeRawPointer(bitPattern: 1)!,
value,
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN
)
}
func testWeakRefs() throws {
try testWeakRefs0(frozen: false)
try testWeakRefs0(frozen: true)
}
func testWeakRefs0(frozen: Bool) throws {
func getObj(test: TestWeakRefs) -> AnyObject {
return autoreleasepool { test.getObj() as AnyObject }
}
func test1() throws {
var test = TestWeakRefs(frozen: frozen)
var obj: AnyObject? = getObj(test: test)
weak var ref = getObj(test: test)
ValuesKt.gc()
try assertTrue(ref === getObj(test: test)) // There are both Kotlin and Swift references to the object.
obj = nil
ValuesKt.gc()
try assertTrue(ref === getObj(test: test)) // There are only Kotlin references to the object.
test.clearObj()
ValuesKt.gc()
try assertTrue(ref === nil)
}
func test2() throws {
var test = TestWeakRefs(frozen: frozen)
var obj: AnyObject? = getObj(test: test)
weak var ref = getObj(test: test)
ValuesKt.gc()
try assertTrue(ref === obj!) // There are both Kotlin and Swift references to the object.
test.clearObj()
ValuesKt.gc()
try assertTrue(ref === obj!) // There are only Swift references to the object.
obj = nil
ValuesKt.gc()
try assertTrue(ref === nil)
}
func test3() throws {
class Holder {
static weak var ref: AnyObject? = nil
static var deinitialized = false
deinit {
// Access weak ref to Kotlin object during its counterpart dealloc:
try! assertTrue(Holder.ref === nil)
Holder.deinitialized = true
}
}
Holder.deinitialized = false
Holder.ref = nil
var test = TestWeakRefs(frozen: frozen)
Holder.ref = getObj(test: test)
// Prepare Holder() to get deinitialized along with getObj(test: test):
setAssociatedObject(
object: getObj(test: test),
value: Holder()
)
try assertFalse(Holder.ref === nil)
try assertFalse(Holder.deinitialized)
test.clearObj()
ValuesKt.gc()
try assertTrue(Holder.ref === nil)
try assertTrue(Holder.deinitialized)
}
func test4() throws {
class Holder {
static weak var ref1: AnyObject? = nil
static weak var ref2: AnyObject? = nil
static var deinitialized: Int = 0
deinit {
// Access weak ref to Kotlin object during its counterpart dealloc:
try! assertTrue(Holder.ref1 === nil)
try! assertTrue(Holder.ref2 === nil)
Holder.deinitialized += 1
}
}
Holder.deinitialized = 0
Holder.ref1 = nil
Holder.ref2 = nil
var test = TestWeakRefs(frozen: frozen)
autoreleasepool {
let cycle = test.createCycle()
let obj1 = cycle[0] as AnyObject
let obj2 = cycle[1] as AnyObject
// Prepare Holders to get deinitialized along with obj1 and obj2:
setAssociatedObject(object: obj1, value: Holder())
setAssociatedObject(object: obj2, value: Holder())
Holder.ref1 = obj1
Holder.ref2 = obj2
}
try assertFalse(Holder.ref1 === nil)
try assertFalse(Holder.ref2 === nil)
try assertEquals(actual: Holder.deinitialized, expected: 0)
ValuesKt.gc()
try assertTrue(Holder.ref1 === nil)
try assertTrue(Holder.ref2 === nil)
try assertEquals(actual: Holder.deinitialized, expected: 2)
}
try test1()
try test2()
try test3()
// try test4()
}
// See https://github.com/JetBrains/kotlin-native/issues/2931
func testGH2931() throws {
for i in 0..<50000 {
@@ -735,6 +876,7 @@ class ValuesTests : TestProvider {
TestCase(name: "TestClashes", method: withAutorelease(testClashes)),
TestCase(name: "TestInvalidIdentifiers", method: withAutorelease(testInvalidIdentifiers)),
TestCase(name: "TestDeprecation", method: withAutorelease(testDeprecation)),
TestCase(name: "TestWeakRefs", method: withAutorelease(testWeakRefs)),
TestCase(name: "TestGH2931", method: withAutorelease(testGH2931)),
]
}