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 (#28823)
SR-5289: Teach Mirror how to inspect weak, unowned, and unmanaged refs Correctly reflect weak, unowned, and unmanaged references to both Swift and Obj-C types (including existential references to such types) that occur in both Swift class objects and in Swift structs. This includes the specific reported case (unowned reference to an Obj-C object) and several related ones. Related changes in this PR: * Tweak internal bitmap used for tracking ownership modifiers to reject unsupported combinations. * Move FieldType into ReflectionMirror.mm FieldType is really just an internal implementation detail of this one source file, so it does not belong in an ABI header. * Use TypeReferenceOwnership directly to track field ownership This avoids bitwise copying of properties and localizes some of the knowledge about reference ownership * Generate a top-level "copyFieldContents" from ReferenceStorage.def Adding new ownership types to ReferenceStorage.def will now automatically produce calls to `copy*FieldContents` - failure to provide a suitable implementation will fail the build. * Add `deallocateBoxForExistentialIn` to match `allocateBoxForExistentialIn` Caveat: The unit tests are not as strict as I'd like. Attempting to make them so ran afoul of otherwise-unrelated bugs in dynamic casting.
This commit is contained in:
@@ -509,6 +509,234 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
|
||||
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 +1082,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 +1844,7 @@ mirrors.test("Float") {
|
||||
}
|
||||
|
||||
do {
|
||||
var input: Float = 42.125
|
||||
let input: Float = 42.125
|
||||
var output = ""
|
||||
dump(input, to: &output)
|
||||
|
||||
@@ -1649,7 +1877,7 @@ mirrors.test("Double") {
|
||||
}
|
||||
|
||||
do {
|
||||
var input: Double = 42.125
|
||||
let input: Double = 42.125
|
||||
var output = ""
|
||||
dump(input, to: &output)
|
||||
|
||||
@@ -1745,9 +1973,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 +1983,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