Skip to content

Commit be3a3f9

Browse files
committed
SR-5289: Implement reflection for unowned and unmanaged fields
This involves refactoring how we handle reference ownership when reflecting fields of struct and class objects: * 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. * Implement `copyUnownedFieldContents` and `copyUnmanagedFieldContents`
1 parent 36972a1 commit be3a3f9

File tree

2 files changed

+84
-131
lines changed

2 files changed

+84
-131
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -913,55 +913,6 @@ class TargetTupleTypeFlags {
913913
};
914914
using TupleTypeFlags = TargetTupleTypeFlags<size_t>;
915915

916-
/// Field types and flags as represented in a nominal type's field/case type
917-
/// vector.
918-
class FieldType {
919-
typedef uintptr_t int_type;
920-
// Type metadata is always at least pointer-aligned, so we get at least two
921-
// low bits to stash flags. We could use three low bits on 64-bit, and maybe
922-
// some high bits as well.
923-
enum : int_type {
924-
Indirect = 1,
925-
Weak = 2,
926-
927-
TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1),
928-
};
929-
int_type Data;
930-
931-
constexpr FieldType(int_type Data) : Data(Data) {}
932-
public:
933-
constexpr FieldType() : Data(0) {}
934-
FieldType withType(const Metadata *T) const {
935-
return FieldType((Data & ~TypeMask) | (uintptr_t)T);
936-
}
937-
938-
constexpr FieldType withIndirect(bool indirect) const {
939-
return FieldType((Data & ~Indirect)
940-
| (indirect ? Indirect : 0));
941-
}
942-
943-
constexpr FieldType withWeak(bool weak) const {
944-
return FieldType((Data & ~Weak)
945-
| (weak ? Weak : 0));
946-
}
947-
948-
bool isIndirect() const {
949-
return bool(Data & Indirect);
950-
}
951-
952-
bool isWeak() const {
953-
return bool(Data & Weak);
954-
}
955-
956-
const Metadata *getType() const {
957-
return (const Metadata *)(Data & TypeMask);
958-
}
959-
960-
int_type getIntValue() const {
961-
return Data;
962-
}
963-
};
964-
965916
/// Flags for exclusivity-checking operations.
966917
enum class ExclusivityFlags : uintptr_t {
967918
Read = 0x0,

stdlib/public/runtime/ReflectionMirror.mm

Lines changed: 84 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ int asprintf(char **strp, const char *fmt, ...) {
8080

8181
using namespace swift;
8282

83+
class FieldType {
84+
const Metadata *type;
85+
bool indirect;
86+
TypeReferenceOwnership referenceOwnership;
87+
public:
88+
89+
constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { }
90+
constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { }
91+
92+
static constexpr FieldType untypedEnumCase(bool indirect) {
93+
FieldType type{};
94+
type.indirect = indirect;
95+
return type;
96+
}
97+
const Metadata *getType() const { return type; }
98+
const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; }
99+
bool isIndirect() const { return indirect; }
100+
void setIndirect(bool value) { indirect = value; }
101+
void setReferenceOwnership(TypeReferenceOwnership newOwnership) {
102+
referenceOwnership = newOwnership;
103+
}
104+
};
105+
83106
#if SWIFT_OBJC_INTEROP
84107
// Declare the debugQuickLookObject selector.
85108
@interface DeclareSelectors
@@ -123,58 +146,60 @@ - (id)debugQuickLookObject;
123146
return std::make_tuple(T, Value);
124147
}
125148

126-
static bool loadSpecialReferenceStorage(OpaqueValue *fieldData,
127-
const FieldType fieldType,
128-
Any *outValue) {
129-
// isWeak() implies a reference type via Sema.
130-
if (!fieldType.isWeak())
131-
return false;
132-
133-
auto type = fieldType.getType();
149+
static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
134150
assert(type->getKind() == MetadataKind::Optional);
151+
auto *srcContainer = reinterpret_cast<WeakClassExistentialContainer*>(fieldData);
152+
auto *destClassContainer = reinterpret_cast<ClassExistentialContainer*>(destContainer);
153+
destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value);
154+
auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer);
155+
memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize);
156+
}
135157

136-
auto *weakField = reinterpret_cast<WeakReference *>(fieldData);
137-
auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField);
138-
139-
// Now that we have a strong reference, we need to create a temporary buffer
140-
// from which to copy the whole value, which might be a native class-bound
141-
// existential, which means we also need to copy n witness tables, for
142-
// however many protocols are in the protocol composition. For example, if we
143-
// are copying a:
144-
// weak var myWeakProperty : (Protocol1 & Protocol2)?
145-
// then we need to copy three values:
146-
// - the instance
147-
// - the witness table for Protocol1
148-
// - the witness table for Protocol2
149-
150-
auto *weakContainer =
151-
reinterpret_cast<WeakClassExistentialContainer *>(fieldData);
152-
153-
// Create a temporary existential where we can put the strong reference.
154-
// The allocateBuffer value witness requires a ValueBuffer to own the
155-
// allocated storage.
156-
ValueBuffer temporaryBuffer;
157-
158-
auto *temporaryValue = reinterpret_cast<ClassExistentialContainer *>(
159-
type->allocateBufferIn(&temporaryBuffer));
160-
161-
// Now copy the entire value out of the parent, which will include the
162-
// witness tables.
163-
temporaryValue->Value = strongValue;
164-
auto valueWitnessesSize = type->getValueWitnesses()->getSize() -
165-
sizeof(WeakClassExistentialContainer);
166-
memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(),
167-
valueWitnessesSize);
168-
169-
outValue->Type = type;
170-
auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer);
171-
type->vw_initializeWithCopy(opaqueValueAddr,
172-
reinterpret_cast<OpaqueValue *>(temporaryValue));
173-
174-
type->deallocateBufferIn(&temporaryBuffer);
175-
swift_unknownObjectRelease(strongValue);
176-
177-
return true;
158+
static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
159+
auto *srcContainer = reinterpret_cast<UnownedClassExistentialContainer*>(fieldData);
160+
auto *destClassContainer = reinterpret_cast<ClassExistentialContainer*>(destContainer);
161+
destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value);
162+
auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer);
163+
memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize);
164+
}
165+
166+
static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) {
167+
type->vw_initializeWithCopy(destContainer, fieldData);
168+
}
169+
170+
static AnyReturn copyFieldContents(OpaqueValue *fieldData,
171+
const FieldType fieldType) {
172+
Any outValue;
173+
auto *type = fieldType.getType();
174+
outValue.Type = type;
175+
auto ownership = fieldType.getReferenceOwnership();
176+
auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer);
177+
178+
if (ownership.isStrong()) {
179+
type->vw_initializeWithCopy(destContainer, fieldData);
180+
}
181+
182+
// Generate a conditional clause for every known ownership type
183+
// If this causes errors, it's because someone added a new ownership type
184+
// to ReferenceStorage.def and missed some related updates.
185+
#define REF_STORAGE(Name, ...) \
186+
else if (ownership.is##Name()) { \
187+
copy##Name##FieldContents(destContainer, type, fieldData); \
188+
}
189+
#include "swift/AST/ReferenceStorage.def"
190+
191+
else {
192+
// The field was declared with a reference type we don't understand.
193+
warning(0, "Value with unrecognized reference type is reflected as ()");
194+
// Clean up the buffer allocated above
195+
type->deallocateBufferIn(&outValue.Buffer);
196+
// Return an existential containing Void
197+
Any emptyOutValue;
198+
emptyOutValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING);
199+
return AnyReturn(emptyOutValue);
200+
}
201+
202+
return AnyReturn(outValue);
178203
}
179204

180205

@@ -310,11 +335,7 @@ static bool _shouldReportMissingReflectionMetadataWarnings() {
310335
"type '%*s' that claims to be reflectable. Its fields will show up as "
311336
"'unknown' in Mirrors\n",
312337
(int)typeName.length, typeName.data);
313-
return {"unknown",
314-
FieldType()
315-
.withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))
316-
.withIndirect(false)
317-
.withWeak(false)};
338+
return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
318339
};
319340

320341
auto *baseDesc = base->getTypeContextDescriptor();
@@ -325,14 +346,13 @@ static bool _shouldReportMissingReflectionMetadataWarnings() {
325346
if (!fields)
326347
return failedToFindMetadata();
327348

328-
const FieldDescriptor &descriptor = *fields;
329-
auto &field = descriptor.getFields()[index];
349+
auto &field = fields->getFields()[index];
330350
// Bounds are always valid as the offset is constant.
331351
auto name = field.getFieldName();
332352

333353
// Enum cases don't always have types.
334354
if (!field.hasMangledTypeName())
335-
return {name, FieldType().withIndirect(field.isIndirectCase())};
355+
return {name, FieldType::untypedEnumCase(field.isIndirectCase())};
336356

337357
auto typeName = field.getMangledTypeName();
338358

@@ -360,10 +380,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() {
360380
(int)typeName.size(), typeName.data());
361381
}
362382

363-
return {name, FieldType()
364-
.withType(typeInfo.getMetadata())
365-
.withIndirect(field.isIndirectCase())
366-
.withWeak(typeInfo.isWeak())};
383+
auto fieldType = FieldType(typeInfo.getMetadata());
384+
fieldType.setIndirect(field.isIndirectCase());
385+
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
386+
return {name, fieldType};
367387
}
368388

369389
// Implementation for structs.
@@ -397,7 +417,6 @@ AnyReturn subscript(intptr_t i, const char **outName,
397417
// Load the offset from its respective vector.
398418
auto fieldOffset = Struct->getFieldOffsets()[i];
399419

400-
Any result;
401420
StringRef name;
402421
FieldType fieldInfo;
403422
std::tie(name, fieldInfo) = getFieldAt(type, i);
@@ -409,15 +428,7 @@ AnyReturn subscript(intptr_t i, const char **outName,
409428
auto *bytes = reinterpret_cast<char*>(value);
410429
auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
411430

412-
bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
413-
if (!didLoad) {
414-
result.Type = fieldInfo.getType();
415-
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
416-
result.Type->vw_initializeWithCopy(opaqueValueAddr,
417-
const_cast<OpaqueValue *>(fieldData));
418-
}
419-
420-
return AnyReturn(result);
431+
return copyFieldContents(fieldData, fieldInfo);
421432
}
422433
};
423434

@@ -559,7 +570,6 @@ AnyReturn subscript(intptr_t i, const char **outName,
559570
#endif
560571
}
561572

562-
Any result;
563573
StringRef name;
564574
FieldType fieldInfo;
565575
std::tie(name, fieldInfo) = getFieldAt(type, i);
@@ -571,15 +581,7 @@ AnyReturn subscript(intptr_t i, const char **outName,
571581
*outName = name.data();
572582
*outFreeFunc = nullptr;
573583

574-
bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
575-
if (!didLoad) {
576-
result.Type = fieldInfo.getType();
577-
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
578-
result.Type->vw_initializeWithCopy(opaqueValueAddr,
579-
const_cast<OpaqueValue *>(fieldData));
580-
}
581-
582-
return AnyReturn(result);
584+
return copyFieldContents(fieldData, fieldInfo);
583585
}
584586

585587
#if SWIFT_OBJC_INTEROP

0 commit comments

Comments
 (0)