mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
SR-5289: Teach Mirror how to handle unowned/unmanaged references (#28368)
* SR-5289: Support reflecting weak, unowned, and unmanaged refs This refactors how we handle reference ownership when reflecting fields of struct and class objects. There are now explicit paths for each type of reference and some simple exhaustiveness checks to fail the build if a new reference type is added in the future without updating this logic.
This commit is contained in:
@@ -509,6 +509,226 @@ mirrors.test("struct/WrapNSArray") {
|
||||
|
||||
#endif // _runtime(_ObjC)
|
||||
|
||||
//===--- Weak and Unowned References --------------------------------------===//
|
||||
|
||||
// Check that Mirror correctly reflects weak/unowned refs to both
|
||||
// Swift and ObjC objects from Swift structs and classes.
|
||||
|
||||
protocol WeakUnownedTestsP1: class {
|
||||
func f1() -> Int
|
||||
}
|
||||
|
||||
protocol WeakUnownedTestsP2 {
|
||||
func f2() -> String
|
||||
}
|
||||
|
||||
class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 {
|
||||
let tracker = LifetimeTracked(0)
|
||||
func f1() -> Int { return 2 }
|
||||
func f2() -> String { return "b" }
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 {
|
||||
let tracker = LifetimeTracked(0)
|
||||
func f1() -> Int { return 2 }
|
||||
func f2() -> String { return "b" }
|
||||
}
|
||||
#endif
|
||||
|
||||
// The four tests below populate objects with different types
|
||||
// but identical overall structure.
|
||||
// This function is used by all four to verify that the resulting
|
||||
// Mirror objects have the expected entries.
|
||||
func verifyWeakUnownedReflection
|
||||
<ExpectedClass: WeakUnownedTestsP1 & WeakUnownedTestsP2>
|
||||
(_ m: Mirror, expectedClass: ExpectedClass.Type )
|
||||
{
|
||||
let i = m.children.makeIterator()
|
||||
|
||||
func verifyClassField(child: (label: String?, value: Any), name: String) {
|
||||
expectEqual(child.label, name)
|
||||
let v = child.value as? ExpectedClass
|
||||
expectNotNil(v)
|
||||
expectEqual(v!.f1(), 2)
|
||||
}
|
||||
|
||||
func verifyExistentialField(child: (label: String?, value: Any), name: String) {
|
||||
expectEqual(child.label, name)
|
||||
expectNotNil(child.value)
|
||||
|
||||
// FIXME: These casts are currently broken (Dec 2019)
|
||||
// Once they are fixed, enable additional checks:
|
||||
//let vp1 = child.value as? WeakUnownedTestsP1
|
||||
//expectNotNil(vp1)
|
||||
//expectEqual(vp1!.f1(), 2)
|
||||
//let vp2 = child.value as? WeakUnownedTestsP2
|
||||
//expectNotNil(vp2)
|
||||
//expectEqual(vp2!.f2(), "b")
|
||||
|
||||
let v = child.value as? ExpectedClass
|
||||
expectNotNil(v)
|
||||
expectEqual(v!.f1(), 2)
|
||||
let m = Mirror(reflecting: v!)
|
||||
expectEqual(m.displayStyle, .`class`)
|
||||
// TODO: Find a way to verify that the existential wrapper carries
|
||||
// the expected protocol witnesses. The current Swift runtime does
|
||||
// a very good job of hiding this from users.
|
||||
}
|
||||
|
||||
verifyClassField(child: i.next()!, name: "strong_class")
|
||||
verifyExistentialField(child: i.next()!, name: "strong_existential")
|
||||
verifyClassField(child: i.next()!, name: "weak_class")
|
||||
verifyExistentialField(child: i.next()!, name: "weak_existential")
|
||||
verifyClassField(child: i.next()!, name: "unowned_safe_class")
|
||||
verifyExistentialField(child: i.next()!, name: "unowned_safe_existential")
|
||||
|
||||
verifyClassField(child: i.next()!, name: "unowned_unsafe_class")
|
||||
verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential")
|
||||
expectNil(i.next())
|
||||
|
||||
// The original bug report from SR-5289 crashed when the print() code
|
||||
// attempted to reflect the contents of an unowned field.
|
||||
// The tests above _should_ suffice to check this, but let's print everything
|
||||
// anyway just to be sure.
|
||||
for c in m.children {
|
||||
print(c.label ?? "?", c.value)
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// Related: SR-5289 reported a crash when using Mirror to inspect Swift
|
||||
// class objects containing unowned pointers to Obj-C class objects.
|
||||
mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") {
|
||||
class SwiftClassWithWeakAndUnowned {
|
||||
var strong_class: WeakUnownedObjCClass
|
||||
var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
weak var weak_class: WeakUnownedObjCClass?
|
||||
weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)?
|
||||
unowned(safe) let unowned_safe_class: WeakUnownedObjCClass
|
||||
unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass
|
||||
unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
|
||||
init(_ objc: WeakUnownedObjCClass) {
|
||||
self.strong_class = objc
|
||||
self.strong_existential = objc
|
||||
self.weak_class = objc
|
||||
self.weak_existential = objc
|
||||
self.unowned_safe_class = objc
|
||||
self.unowned_safe_existential = objc
|
||||
self.unowned_unsafe_class = objc
|
||||
self.unowned_unsafe_existential = objc
|
||||
}
|
||||
}
|
||||
|
||||
let objc = WeakUnownedObjCClass()
|
||||
let classWithReferences = SwiftClassWithWeakAndUnowned(objc)
|
||||
let m = Mirror(reflecting: classWithReferences)
|
||||
expectEqual(m.displayStyle, .`class`)
|
||||
expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned")
|
||||
expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self)
|
||||
verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self)
|
||||
}
|
||||
|
||||
mirrors.test("Weak and Unowned Obj-C refs in struct") {
|
||||
struct SwiftStructWithWeakAndUnowned {
|
||||
var strong_class: WeakUnownedObjCClass
|
||||
var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
weak var weak_class: WeakUnownedObjCClass?
|
||||
weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)?
|
||||
unowned(safe) let unowned_safe_class: WeakUnownedObjCClass
|
||||
unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass
|
||||
unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
|
||||
init(_ objc: WeakUnownedObjCClass) {
|
||||
self.strong_class = objc
|
||||
self.strong_existential = objc
|
||||
self.weak_class = objc
|
||||
self.weak_existential = objc
|
||||
self.unowned_safe_class = objc
|
||||
self.unowned_safe_existential = objc
|
||||
self.unowned_unsafe_class = objc
|
||||
self.unowned_unsafe_existential = objc
|
||||
}
|
||||
}
|
||||
|
||||
let objc = WeakUnownedObjCClass()
|
||||
let structWithReferences = SwiftStructWithWeakAndUnowned(objc)
|
||||
let m = Mirror(reflecting: structWithReferences)
|
||||
expectEqual(m.displayStyle, .`struct`)
|
||||
expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned")
|
||||
expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self)
|
||||
verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
mirrors.test("Weak and Unowned Swift refs in class") {
|
||||
class SwiftClassWithWeakAndUnowned {
|
||||
var strong_class: WeakUnownedSwiftClass
|
||||
var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
weak var weak_class: WeakUnownedSwiftClass?
|
||||
weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)?
|
||||
unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass
|
||||
unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)
|
||||
unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass
|
||||
unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)
|
||||
|
||||
init(_ swift: WeakUnownedSwiftClass) {
|
||||
self.strong_class = swift
|
||||
self.strong_existential = swift
|
||||
self.weak_class = swift
|
||||
self.weak_existential = swift
|
||||
self.unowned_safe_class = swift
|
||||
self.unowned_safe_existential = swift
|
||||
self.unowned_unsafe_class = swift
|
||||
self.unowned_unsafe_existential = swift
|
||||
}
|
||||
}
|
||||
|
||||
let swift = WeakUnownedSwiftClass()
|
||||
let classWithReferences = SwiftClassWithWeakAndUnowned(swift)
|
||||
let m = Mirror(reflecting: classWithReferences)
|
||||
expectEqual(m.displayStyle, .`class`)
|
||||
expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned")
|
||||
expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self)
|
||||
verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self)
|
||||
}
|
||||
|
||||
mirrors.test("Weak and Unowned Swift refs in struct") {
|
||||
struct SwiftStructWithWeakAndUnowned {
|
||||
var strong_class: WeakUnownedSwiftClass
|
||||
var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2
|
||||
weak var weak_class: WeakUnownedSwiftClass?
|
||||
weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)?
|
||||
unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass
|
||||
unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)
|
||||
unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass
|
||||
unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)
|
||||
|
||||
init(_ swift: WeakUnownedSwiftClass) {
|
||||
self.strong_class = swift
|
||||
self.strong_existential = swift
|
||||
self.weak_class = swift
|
||||
self.weak_existential = swift
|
||||
self.unowned_safe_class = swift
|
||||
self.unowned_safe_existential = swift
|
||||
self.unowned_unsafe_class = swift
|
||||
self.unowned_unsafe_existential = swift
|
||||
}
|
||||
}
|
||||
|
||||
let swift = WeakUnownedSwiftClass()
|
||||
let structWithReferences = SwiftStructWithWeakAndUnowned(swift)
|
||||
let m = Mirror(reflecting: structWithReferences)
|
||||
expectEqual(m.displayStyle, .`struct`)
|
||||
expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned")
|
||||
expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self)
|
||||
verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self)
|
||||
}
|
||||
|
||||
//===--- Suppressed Superclass Mirrors ------------------------------------===//
|
||||
mirrors.test("Class/Root/NoSuperclassMirror") {
|
||||
class B : CustomReflectable {
|
||||
@@ -854,7 +1074,7 @@ struct GenericStructWithDefaultMirror<T, U> {
|
||||
|
||||
mirrors.test("Struct/Generic/DefaultMirror") {
|
||||
do {
|
||||
var value = GenericStructWithDefaultMirror<Int, [Any?]>(
|
||||
let value = GenericStructWithDefaultMirror<Int, [Any?]>(
|
||||
first: 123,
|
||||
second: ["abc", 456, 789.25])
|
||||
var output = ""
|
||||
@@ -1616,7 +1836,7 @@ mirrors.test("Float") {
|
||||
}
|
||||
|
||||
do {
|
||||
var input: Float = 42.125
|
||||
let input: Float = 42.125
|
||||
var output = ""
|
||||
dump(input, to: &output)
|
||||
|
||||
@@ -1649,7 +1869,7 @@ mirrors.test("Double") {
|
||||
}
|
||||
|
||||
do {
|
||||
var input: Double = 42.125
|
||||
let input: Double = 42.125
|
||||
var output = ""
|
||||
dump(input, to: &output)
|
||||
|
||||
@@ -1745,9 +1965,9 @@ mirrors.test("FieldNamesBug") {
|
||||
}
|
||||
|
||||
mirrors.test("MirrorMirror") {
|
||||
var object = 1
|
||||
var mirror = Mirror(reflecting: object)
|
||||
var mirrorMirror = Mirror(reflecting: mirror)
|
||||
let object = 1
|
||||
let mirror = Mirror(reflecting: object)
|
||||
let mirrorMirror = Mirror(reflecting: mirror)
|
||||
|
||||
expectEqual(0, mirrorMirror.children.count)
|
||||
}
|
||||
@@ -1755,7 +1975,7 @@ mirrors.test("MirrorMirror") {
|
||||
mirrors.test("OpaquePointer/null") {
|
||||
// Don't crash on null pointers. rdar://problem/19708338
|
||||
let pointer: OpaquePointer? = nil
|
||||
let mirror = Mirror(reflecting: pointer)
|
||||
let mirror = Mirror(reflecting: pointer as Any)
|
||||
expectEqual(0, mirror.children.count)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user