[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:
tbkka
2019-12-17 09:42:52 -08:00
committed by GitHub
parent d595213f36
commit fdb1926421
7 changed files with 337 additions and 136 deletions

View File

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