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:
tbkka
2019-12-16 12:26:22 -08:00
committed by GitHub
parent 8a3e4a2fe8
commit 9c638ae60d
7 changed files with 329 additions and 136 deletions

View File

@@ -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)
}