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:
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl {
|
||||
using ClassExistentialContainer = ClassExistentialContainerImpl<void *>;
|
||||
using WeakClassExistentialContainer =
|
||||
ClassExistentialContainerImpl<WeakReference>;
|
||||
using UnownedClassExistentialContainer =
|
||||
ClassExistentialContainerImpl<UnownedReference>;
|
||||
|
||||
} // end swift namespace
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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