[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

@@ -160,6 +160,7 @@ using TargetRelativeIndirectablePointer
struct HeapObject;
class WeakReference;
struct UnownedReference;
template <typename Runtime> struct TargetMetadata;
using Metadata = TargetMetadata<InProcess>;
@@ -645,6 +646,9 @@ public:
// NOTE: This *is* a box for copy-on-write existentials.
OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const;
// Deallocate an out-of-line buffer box if one is present.
void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const;
/// Get the nominal type descriptor if this metadata describes a nominal type,
/// or return null if it does not.
ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>

View File

@@ -913,55 +913,6 @@ public:
};
using TupleTypeFlags = TargetTupleTypeFlags<size_t>;
/// Field types and flags as represented in a nominal type's field/case type
/// vector.
class FieldType {
typedef uintptr_t int_type;
// Type metadata is always at least pointer-aligned, so we get at least two
// low bits to stash flags. We could use three low bits on 64-bit, and maybe
// some high bits as well.
enum : int_type {
Indirect = 1,
Weak = 2,
TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1),
};
int_type Data;
constexpr FieldType(int_type Data) : Data(Data) {}
public:
constexpr FieldType() : Data(0) {}
FieldType withType(const Metadata *T) const {
return FieldType((Data & ~TypeMask) | (uintptr_t)T);
}
constexpr FieldType withIndirect(bool indirect) const {
return FieldType((Data & ~Indirect)
| (indirect ? Indirect : 0));
}
constexpr FieldType withWeak(bool weak) const {
return FieldType((Data & ~Weak)
| (weak ? Weak : 0));
}
bool isIndirect() const {
return bool(Data & Indirect);
}
bool isWeak() const {
return bool(Data & Weak);
}
const Metadata *getType() const {
return (const Metadata *)(Data & TypeMask);
}
int_type getIntValue() const {
return Data;
}
};
/// Flags for exclusivity-checking operations.
enum class ExclusivityFlags : uintptr_t {
Read = 0x0,

View File

@@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl {
using ClassExistentialContainer = ClassExistentialContainerImpl<void *>;
using WeakClassExistentialContainer =
ClassExistentialContainerImpl<WeakReference>;
using UnownedClassExistentialContainer =
ClassExistentialContainerImpl<UnownedReference>;
} // end swift namespace

View File

@@ -3862,6 +3862,13 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff
return refAndValueAddr.buffer;
}
template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const {
auto *vwt = getValueWitnesses();
if (vwt->isValueInline())
return;
swift_deallocBox(reinterpret_cast<HeapObject *>(buffer->PrivateData[0]));
}
template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const {
auto *vwt = getValueWitnesses();
if (vwt->isValueInline())

View File

@@ -50,8 +50,10 @@ public:
#define REF_STORAGE(Name, ...) \
void set##Name() { Data |= Name; } \
bool is##Name() const { return Data & Name; }
bool is##Name() const { return Data == Name; }
#include "swift/AST/ReferenceStorage.def"
bool isStrong() const { return Data == 0; }
};
/// Type information consists of metadata and its ownership info,
@@ -76,9 +78,11 @@ public:
const Metadata *getMetadata() const { return Response.Value; }
MetadataResponse getResponse() const { return Response; }
bool isWeak() const { return ReferenceOwnership.isWeak(); }
bool isUnowned() const { return ReferenceOwnership.isUnowned(); }
bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); }
#define REF_STORAGE(Name, ...) \
bool is##Name() const { return ReferenceOwnership.is##Name(); }
#include "swift/AST/ReferenceStorage.def"
bool isStrong() const { return ReferenceOwnership.isStrong(); }
TypeReferenceOwnership getReferenceOwnership() const {
return ReferenceOwnership;

View File

@@ -89,6 +89,29 @@ using namespace swift;
namespace {
class FieldType {
const Metadata *type;
bool indirect;
TypeReferenceOwnership referenceOwnership;
public:
constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { }
constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { }
static constexpr FieldType untypedEnumCase(bool indirect) {
FieldType type{};
type.indirect = indirect;
return type;
}
const Metadata *getType() const { return type; }
const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; }
bool isIndirect() const { return indirect; }
void setIndirect(bool value) { indirect = value; }
void setReferenceOwnership(TypeReferenceOwnership newOwnership) {
referenceOwnership = newOwnership;
}
};
/// The layout of Any.
using Any = OpaqueExistentialContainer;
@@ -123,58 +146,63 @@ unwrapExistential(const Metadata *T, OpaqueValue *Value) {
return std::make_tuple(T, Value);
}
static bool loadSpecialReferenceStorage(OpaqueValue *fieldData,
const FieldType fieldType,
Any *outValue) {
// isWeak() implies a reference type via Sema.
if (!fieldType.isWeak())
return false;
auto type = fieldType.getType();
static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
assert(type->getKind() == MetadataKind::Optional);
auto *srcContainer = reinterpret_cast<WeakClassExistentialContainer*>(fieldData);
auto *destClassContainer = reinterpret_cast<ClassExistentialContainer*>(destContainer);
destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value);
auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer);
memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize);
}
auto *weakField = reinterpret_cast<WeakReference *>(fieldData);
auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField);
static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
auto *srcContainer = reinterpret_cast<UnownedClassExistentialContainer*>(fieldData);
auto *destClassContainer = reinterpret_cast<ClassExistentialContainer*>(destContainer);
destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value);
auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer);
memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize);
}
// Now that we have a strong reference, we need to create a temporary buffer
// from which to copy the whole value, which might be a native class-bound
// existential, which means we also need to copy n witness tables, for
// however many protocols are in the protocol composition. For example, if we
// are copying a:
// weak var myWeakProperty : (Protocol1 & Protocol2)?
// then we need to copy three values:
// - the instance
// - the witness table for Protocol1
// - the witness table for Protocol2
static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
// Also known as "unowned(unsafe)".
// This is simpler than the unowned/weak cases because unmanaged
// references are fundamentally the same as strong ones, so we
// can use the regular strong reference support that already
// knows how to handle existentials and Obj-C references.
type->vw_initializeWithCopy(destContainer, fieldData);
}
auto *weakContainer =
reinterpret_cast<WeakClassExistentialContainer *>(fieldData);
static AnyReturn copyFieldContents(OpaqueValue *fieldData,
const FieldType fieldType) {
Any outValue;
auto *type = fieldType.getType();
outValue.Type = type;
auto ownership = fieldType.getReferenceOwnership();
auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer);
// Create a temporary existential where we can put the strong reference.
// The allocateBuffer value witness requires a ValueBuffer to own the
// allocated storage.
ValueBuffer temporaryBuffer;
if (ownership.isStrong()) {
type->vw_initializeWithCopy(destContainer, fieldData);
}
auto *temporaryValue = reinterpret_cast<ClassExistentialContainer *>(
type->allocateBufferIn(&temporaryBuffer));
// Generate a conditional clause for every known ownership type.
// If this causes errors, it's because someone added a new ownership type
// to ReferenceStorage.def and missed some related updates.
#define REF_STORAGE(Name, ...) \
else if (ownership.is##Name()) { \
copy##Name##FieldContents(destContainer, type, fieldData); \
}
#include "swift/AST/ReferenceStorage.def"
// Now copy the entire value out of the parent, which will include the
// witness tables.
temporaryValue->Value = strongValue;
auto valueWitnessesSize = type->getValueWitnesses()->getSize() -
sizeof(WeakClassExistentialContainer);
memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(),
valueWitnessesSize);
else {
// The field was declared with a reference type we don't understand.
warning(0, "Value with unrecognized reference type is reflected as ()");
// Clean up the buffer allocated above
type->deallocateBoxForExistentialIn(&outValue.Buffer);
// Return an existential containing Void
outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING);
}
outValue->Type = type;
auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer);
type->vw_initializeWithCopy(opaqueValueAddr,
reinterpret_cast<OpaqueValue *>(temporaryValue));
type->deallocateBufferIn(&temporaryBuffer);
swift_unknownObjectRelease(strongValue);
return true;
return AnyReturn(outValue);
}
@@ -310,11 +338,7 @@ getFieldAt(const Metadata *base, unsigned index) {
"type '%*s' that claims to be reflectable. Its fields will show up as "
"'unknown' in Mirrors\n",
(int)typeName.length, typeName.data);
return {"unknown",
FieldType()
.withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))
.withIndirect(false)
.withWeak(false)};
return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
};
auto *baseDesc = base->getTypeContextDescriptor();
@@ -325,14 +349,13 @@ getFieldAt(const Metadata *base, unsigned index) {
if (!fields)
return failedToFindMetadata();
const FieldDescriptor &descriptor = *fields;
auto &field = descriptor.getFields()[index];
auto &field = fields->getFields()[index];
// Bounds are always valid as the offset is constant.
auto name = field.getFieldName();
// Enum cases don't always have types.
if (!field.hasMangledTypeName())
return {name, FieldType().withIndirect(field.isIndirectCase())};
return {name, FieldType::untypedEnumCase(field.isIndirectCase())};
auto typeName = field.getMangledTypeName();
@@ -360,10 +383,10 @@ getFieldAt(const Metadata *base, unsigned index) {
(int)typeName.size(), typeName.data());
}
return {name, FieldType()
.withType(typeInfo.getMetadata())
.withIndirect(field.isIndirectCase())
.withWeak(typeInfo.isWeak())};
auto fieldType = FieldType(typeInfo.getMetadata());
fieldType.setIndirect(field.isIndirectCase());
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
return {name, fieldType};
}
// Implementation for structs.
@@ -397,7 +420,6 @@ struct StructImpl : ReflectionMirrorImpl {
// Load the offset from its respective vector.
auto fieldOffset = Struct->getFieldOffsets()[i];
Any result;
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
@@ -409,15 +431,7 @@ struct StructImpl : ReflectionMirrorImpl {
auto *bytes = reinterpret_cast<char*>(value);
auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
if (!didLoad) {
result.Type = fieldInfo.getType();
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
result.Type->vw_initializeWithCopy(opaqueValueAddr,
const_cast<OpaqueValue *>(fieldData));
}
return AnyReturn(result);
return copyFieldContents(fieldData, fieldInfo);
}
};
@@ -559,7 +573,6 @@ struct ClassImpl : ReflectionMirrorImpl {
#endif
}
Any result;
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
@@ -571,15 +584,7 @@ struct ClassImpl : ReflectionMirrorImpl {
*outName = name.data();
*outFreeFunc = nullptr;
bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
if (!didLoad) {
result.Type = fieldInfo.getType();
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
result.Type->vw_initializeWithCopy(opaqueValueAddr,
const_cast<OpaqueValue *>(fieldData));
}
return AnyReturn(result);
return copyFieldContents(fieldData, fieldInfo);
}
#if SWIFT_OBJC_INTEROP

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