Skip to content

Commit 92011f2

Browse files
committed
Gather opaque type conformance requirements when scanning associated type infos from a binary
When detecting that an associated type's substituted type is an opaque type, read out its opaque type descriptor to collect the names of protocols it must conform to.
1 parent 2f4049a commit 92011f2

File tree

9 files changed

+301
-84
lines changed

9 files changed

+301
-84
lines changed

include/swift/ABI/GenericContext.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ class TargetGenericRequirementDescriptor {
145145
return Protocol;
146146
}
147147

148+
/// Retreive the raw value of the Protocol requirement pointer.
149+
int32_t getUnresolvedProtocolAddress() const {
150+
assert(getKind() == GenericRequirementKind::Protocol);
151+
return Protocol.getUnresolvedProtocolAddress();
152+
}
153+
154+
/// Retreive the offset to the Protocol field
155+
constexpr inline auto
156+
getProtocolOffset() const -> typename Runtime::StoredSize {
157+
return offsetof(typename std::remove_reference<decltype(*this)>::type, Protocol);
158+
}
159+
148160
/// Retrieve the right-hand type for a SameType or BaseClass requirement.
149161
llvm::StringRef getMangledTypeName() const {
150162
assert(getKind() == GenericRequirementKind::SameType ||
@@ -301,7 +313,8 @@ class TargetGenericEnvironment
301313
}
302314
};
303315

304-
using GenericEnvironmentDescriptor = TargetGenericEnvironment<InProcess>;
316+
using GenericEnvironmentDescriptor =
317+
TargetGenericEnvironment<InProcess>;
305318

306319
/// CRTP class for a context descriptor that includes trailing generic
307320
/// context description.

include/swift/ABI/Metadata.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -2996,7 +2996,11 @@ struct TargetOpaqueTypeDescriptor final
29962996
return cd->getKind() == ContextDescriptorKind::OpaqueType;
29972997
}
29982998
};
2999-
2999+
3000+
template <template <typename Runtime> class ObjCInteropKind,
3001+
unsigned PointerSize>
3002+
using ExternalOpaqueTypeDescriptor = TargetOpaqueTypeDescriptor<
3003+
External<ObjCInteropKind<RuntimeTarget<PointerSize>>>>;
30003004
using OpaqueTypeDescriptor = TargetOpaqueTypeDescriptor<InProcess>;
30013005

30023006
/// The instantiation cache for generic metadata. This must be guaranteed

include/swift/ABI/MetadataRef.h

+10
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,16 @@ class RelativeTargetProtocolDescriptorPointer {
308308
swiftPointer.getPointer()));
309309
}
310310

311+
/// Retrieve a reference to the protocol.
312+
int32_t getUnresolvedProtocolAddress() const {
313+
#if SWIFT_OBJC_INTEROP
314+
if (isObjC()) {
315+
return objcPointer.getUnresolvedOffset();
316+
}
317+
#endif
318+
return swiftPointer.getUnresolvedOffset();
319+
}
320+
311321
operator TargetProtocolDescriptorRef<Runtime>() const {
312322
return getProtocol();
313323
}

include/swift/Basic/RelativePointer.h

+10-7
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,7 @@ class RelativeIndirectablePointerIntPair {
347347

348348
public:
349349
const ValueTy *getPointer() const & {
350-
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
351-
"alignment of value and offset must be at least 2 to "
352-
"make room for indirectable flag");
353-
354-
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
350+
Offset offset = getUnresolvedOffset();
355351

356352
// Check for null.
357353
if (Nullable && offset == 0)
@@ -370,10 +366,17 @@ class RelativeIndirectablePointerIntPair {
370366
}
371367
}
372368

369+
Offset getUnresolvedOffset() const & {
370+
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
371+
"alignment of value and offset must be at least 2 to "
372+
"make room for indirectable flag");
373+
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
374+
return offset;
375+
}
376+
373377
/// A zero relative offset encodes a null reference.
374378
bool isNull() const & {
375-
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
376-
return offset == 0;
379+
return getUnresolvedOffset() == 0;
377380
}
378381

379382
IntTy getInt() const & {

include/swift/Reflection/TypeRefBuilder.h

+186-6
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ struct AssociatedType {
315315
std::string SubstitutedTypeMangledName;
316316
std::string SubstitutedTypeFullyQualifiedName;
317317
std::string SubstitutedTypeDiagnosticPrintName;
318+
std::vector<std::string> OpaqueTypeProtocolConformanceRequirements;
318319
};
319320

320321
/// Info about all of a given type's associated types, as read out from an Image
@@ -523,10 +524,10 @@ class TypeRefBuilder {
523524
Node::Kind::OpaqueTypeDescriptorSymbolicReference) {
524525
auto underlyingTy = OpaqueUnderlyingTypeReader(
525526
opaqueDescriptor->getIndex(), ordinal);
526-
527+
527528
if (!underlyingTy)
528529
return nullptr;
529-
530+
530531
GenericArgumentMap subs;
531532
for (unsigned d = 0, de = genericArgs.size(); d < de; ++d) {
532533
auto argsForDepth = genericArgs[d];
@@ -1005,12 +1006,115 @@ class TypeRefBuilder {
10051006
bool printTypeName = false);
10061007
FieldTypeCollectionResult collectFieldTypes(llvm::Optional<std::string> forMangledTypeName);
10071008
void dumpFieldSection(std::ostream &stream);
1008-
AssociatedTypeCollectionResult collectAssociatedTypes(llvm::Optional<std::string> forMangledTypeName);
1009-
void dumpAssociatedTypeSection(std::ostream &stream);
10101009
void dumpBuiltinTypeSection(std::ostream &stream);
10111010
void dumpCaptureSection(std::ostream &stream);
10121011
void dumpMultiPayloadEnumSection(std::ostream &stream);
10131012

1013+
///
1014+
/// Extraction of associated types
1015+
///
1016+
public:
1017+
template <template <typename Runtime> class ObjCInteropKind,
1018+
unsigned PointerSize>
1019+
AssociatedTypeCollectionResult
1020+
collectAssociatedTypes(llvm::Optional<std::string> forMangledTypeName) {
1021+
AssociatedTypeCollectionResult result;
1022+
for (const auto &sections : ReflectionInfos) {
1023+
for (auto descriptor : sections.AssociatedType) {
1024+
// Read out the relevant info from the associated type descriptor:
1025+
// The type's name and which protocol conformance it corresponds to
1026+
auto typeRef = readTypeRef(descriptor, descriptor->ConformingTypeName);
1027+
auto typeName = nodeToString(demangleTypeRef(typeRef));
1028+
auto optionalMangledTypeName = normalizeReflectionName(typeRef);
1029+
auto protocolNode = demangleTypeRef(
1030+
readTypeRef(descriptor, descriptor->ProtocolTypeName));
1031+
auto protocolName = nodeToString(protocolNode);
1032+
clearNodeFactory();
1033+
if (optionalMangledTypeName.hasValue()) {
1034+
auto mangledTypeName = optionalMangledTypeName.getValue();
1035+
if (forMangledTypeName.hasValue()) {
1036+
if (mangledTypeName != forMangledTypeName.getValue())
1037+
continue;
1038+
}
1039+
1040+
// For each associated type, gather its typealias name,
1041+
// the substituted type info, and if the substituted type is opaque -
1042+
// gather its protocol conformance requirements
1043+
std::vector<AssociatedType> associatedTypes;
1044+
for (const auto &associatedTypeRef : *descriptor.getLocalBuffer()) {
1045+
auto associatedType = descriptor.getField(associatedTypeRef);
1046+
std::string typealiasTypeName =
1047+
getTypeRefString(
1048+
readTypeRef(associatedType, associatedType->Name))
1049+
.str();
1050+
1051+
std::string mangledSubstitutedTypeName =
1052+
std::string(associatedType->SubstitutedTypeName);
1053+
auto substitutedTypeRef = readTypeRef(
1054+
associatedType, associatedType->SubstitutedTypeName);
1055+
auto optionalMangledSubstitutedTypeName =
1056+
normalizeReflectionName(substitutedTypeRef);
1057+
if (optionalMangledSubstitutedTypeName.hasValue()) {
1058+
mangledSubstitutedTypeName =
1059+
optionalMangledSubstitutedTypeName.getValue();
1060+
}
1061+
1062+
// We intentionally do not want to resolve opaque type
1063+
// references, because if the substituted type is opaque, we
1064+
// would like to get at its OpaqueTypeDescriptor address, which
1065+
// is stored on the OpaqueTypeDescriptorSymbolicReference typeRef.
1066+
auto substitutedDemangleTree =
1067+
demangleTypeRef(substitutedTypeRef,
1068+
/* useOpaqueTypeSymbolicReferences */ true);
1069+
1070+
// If the substituted type is an opaque type, also gather info
1071+
// about which protocols it is required to conform to
1072+
std::vector<std::string> OpaqueTypeConformanceRequirements;
1073+
gatherConformanceRequirementsIfOpaque<ObjCInteropKind, PointerSize>(
1074+
substitutedDemangleTree, OpaqueTypeConformanceRequirements);
1075+
1076+
auto substitutedTypeName = nodeToString(substitutedDemangleTree);
1077+
std::stringstream OS;
1078+
dumpTypeRef(substitutedTypeRef, OS);
1079+
associatedTypes.emplace_back(
1080+
AssociatedType{typealiasTypeName, mangledSubstitutedTypeName,
1081+
substitutedTypeName, OS.str(),
1082+
OpaqueTypeConformanceRequirements});
1083+
}
1084+
result.AssociatedTypeInfos.emplace_back(AssociatedTypeInfo{
1085+
mangledTypeName, typeName, protocolName, associatedTypes});
1086+
}
1087+
}
1088+
}
1089+
return result;
1090+
}
1091+
1092+
template <template <typename Runtime> class ObjCInteropKind,
1093+
unsigned PointerSize>
1094+
void gatherConformanceRequirementsIfOpaque(
1095+
Demangle::Node *substitutedTypeDemangleTree,
1096+
std::vector<std::string> &OpaqueTypeConformanceRequirements) {
1097+
// With unresolved opaque symbolic references, the demangle tree we
1098+
// extract the opaque type descriptor's address from is of the form:
1099+
// kind=Type
1100+
// kind=OpaqueType
1101+
// kind=OpaqueTypeDescriptorSymbolicReference, index={{1-9+}}
1102+
// Where the `index` value is the descriptor's address
1103+
//
1104+
if (substitutedTypeDemangleTree->getKind() == Node::Kind::Type) {
1105+
auto childDemangleTree = substitutedTypeDemangleTree->getFirstChild();
1106+
if (childDemangleTree->getKind() == Node::Kind::OpaqueType) {
1107+
auto opaqueTypeChildDemangleTree = childDemangleTree->getFirstChild();
1108+
if (opaqueTypeChildDemangleTree->getKind() ==
1109+
Node::Kind::OpaqueTypeDescriptorSymbolicReference) {
1110+
OpaqueTypeConformanceRequirements =
1111+
collectOpaqueTypeConformanceNames<ObjCInteropKind, PointerSize>(
1112+
opaqueTypeChildDemangleTree->getIndex());
1113+
}
1114+
}
1115+
}
1116+
}
1117+
10141118
private:
10151119
struct ContextNameInfo {
10161120
std::string name;
@@ -1142,7 +1246,6 @@ class TypeRefBuilder {
11421246
remote::RemoteAddress(protocolDescriptorAddress),
11431247
sizeof(ExternalContextDescriptor<ObjCInteropKind, PointerSize>));
11441248
if (!protocolContextDescriptorBytes.get()) {
1145-
// Error = "Failed to read context (protocol) descriptor.";
11461249
return llvm::None;
11471250
}
11481251
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
@@ -1332,6 +1435,83 @@ class TypeRefBuilder {
13321435
}
13331436
};
13341437

1438+
template <template <typename Runtime> class ObjCInteropKind,
1439+
unsigned PointerSize>
1440+
void dumpAssociatedTypeSection(std::ostream &stream) {
1441+
auto associatedTypeCollectionResult =
1442+
collectAssociatedTypes<ObjCInteropKind, PointerSize>(
1443+
llvm::Optional<std::string>());
1444+
for (const auto &info :
1445+
associatedTypeCollectionResult.AssociatedTypeInfos) {
1446+
stream << "- " << info.FullyQualifiedName << " : "
1447+
<< info.ProtocolFullyQualifiedName << "\n";
1448+
for (const auto &typeAlias : info.AssociatedTypes) {
1449+
stream << "typealias " << typeAlias.TypeAliasName << " = "
1450+
<< typeAlias.SubstitutedTypeFullyQualifiedName << "\n";
1451+
stream << typeAlias.SubstitutedTypeDiagnosticPrintName;
1452+
if (!typeAlias.OpaqueTypeProtocolConformanceRequirements.empty()) {
1453+
stream << "opaque type conformance requirements: \n";
1454+
for (const auto &protocolName :
1455+
typeAlias.OpaqueTypeProtocolConformanceRequirements) {
1456+
stream << protocolName << "\n";
1457+
}
1458+
}
1459+
}
1460+
stream << "\n";
1461+
}
1462+
}
1463+
1464+
template <template <typename Runtime> class ObjCInteropKind,
1465+
unsigned PointerSize>
1466+
std::vector<std::string>
1467+
collectOpaqueTypeConformanceNames(uintptr_t opaqueTypeDescriptorAddress) {
1468+
std::vector<std::string> result;
1469+
auto opaqueTypeDescriptorBytes = OpaqueByteReader(
1470+
remote::RemoteAddress(opaqueTypeDescriptorAddress),
1471+
sizeof(ExternalOpaqueTypeDescriptor<ObjCInteropKind, PointerSize>));
1472+
if (!opaqueTypeDescriptorBytes.get()) {
1473+
return result;
1474+
}
1475+
const ExternalOpaqueTypeDescriptor<ObjCInteropKind, PointerSize>
1476+
*opaqueTypeDescriptor =
1477+
(const ExternalOpaqueTypeDescriptor<ObjCInteropKind, PointerSize> *)
1478+
opaqueTypeDescriptorBytes.get();
1479+
1480+
if (!opaqueTypeDescriptor) {
1481+
return result;
1482+
}
1483+
1484+
for (const auto &req : opaqueTypeDescriptor->getGenericRequirements()) {
1485+
if (req.getKind() == GenericRequirementKind::Protocol) {
1486+
// Compute the address of the protocol descriptor offset as:
1487+
// opaqueTypeDescriptorAddress + offset of the protocol descriptor
1488+
// offset in the descriptor
1489+
auto protocolDescriptorOffsetOffset = (uintptr_t)(&req) +
1490+
req.getProtocolOffset() -
1491+
(uintptr_t)opaqueTypeDescriptor;
1492+
auto protocolDescriptorOffsetAddress =
1493+
opaqueTypeDescriptorAddress + protocolDescriptorOffsetOffset;
1494+
auto protocolDescriptorOffsetValue = req.getUnresolvedProtocolAddress();
1495+
1496+
// Compute the address of the protocol descriptor by following the
1497+
// offset
1498+
auto protocolDescriptorAddress = detail::applyRelativeOffset(
1499+
(const char *)protocolDescriptorOffsetAddress,
1500+
protocolDescriptorOffsetValue);
1501+
1502+
auto nameReader =
1503+
QualifiedContextNameReader<ObjCInteropKind, PointerSize>(
1504+
OpaqueByteReader, OpaqueStringReader, OpaquePointerReader,
1505+
OpaqueDynamicSymbolResolver);
1506+
auto conformanceRequirementProtocolName =
1507+
nameReader.readFullyQualifiedProtocolNameFromProtocolDescriptor(
1508+
protocolDescriptorAddress);
1509+
result.push_back(*conformanceRequirementProtocolName);
1510+
}
1511+
}
1512+
return result;
1513+
}
1514+
13351515
///
13361516
/// Extraction of protocol conformances
13371517
///
@@ -1687,7 +1867,7 @@ class TypeRefBuilder {
16871867
stream << "\n";
16881868
stream << "ASSOCIATED TYPES:\n";
16891869
stream << "=================\n";
1690-
dumpAssociatedTypeSection(stream);
1870+
dumpAssociatedTypeSection<ObjCInteropKind, PointerSize>(stream);
16911871
stream << "\n";
16921872
stream << "BUILTIN TYPES:\n";
16931873
stream << "==============\n";

lib/StaticMirror/BinaryScanningTool.cpp

+36-2
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,46 @@ BinaryScanningTool::collectConformances(const std::vector<std::string> &protocol
8080

8181
AssociatedTypeCollectionResult
8282
BinaryScanningTool::collectAssociatedTypes(const std::string &mangledTypeName) {
83-
return Context->Builder.collectAssociatedTypes(mangledTypeName);
83+
switch (PointerSize) {
84+
case 4:
85+
// FIXME: This could/should be configurable.
86+
#if SWIFT_OBJC_INTEROP
87+
return Context->Builder.collectAssociatedTypes<WithObjCInterop, 4>(mangledTypeName);
88+
#else
89+
return Context->Builder.collectAssociatedTypes<NoObjCInterop, 4>(mangledTypeName);
90+
#endif
91+
case 8:
92+
#if SWIFT_OBJC_INTEROP
93+
return Context->Builder.collectAssociatedTypes<WithObjCInterop, 8>(mangledTypeName);
94+
#else
95+
return Context->Builder.collectAssociatedTypes<NoObjCInterop, 8>(mangledTypeName);
96+
#endif
97+
default:
98+
fputs("unsupported word size in object file\n", stderr);
99+
abort();
100+
}
84101
}
85102

86103
AssociatedTypeCollectionResult
87104
BinaryScanningTool::collectAllAssociatedTypes() {
88-
return Context->Builder.collectAssociatedTypes(llvm::Optional<std::string>());
105+
switch (PointerSize) {
106+
case 4:
107+
// FIXME: This could/should be configurable.
108+
#if SWIFT_OBJC_INTEROP
109+
return Context->Builder.collectAssociatedTypes<WithObjCInterop, 4>(llvm::Optional<std::string>());
110+
#else
111+
return Context->Builder.collectAssociatedTypes<NoObjCInterop, 4>(llvm::Optional<std::string>());
112+
#endif
113+
case 8:
114+
#if SWIFT_OBJC_INTEROP
115+
return Context->Builder.collectAssociatedTypes<WithObjCInterop, 8>(llvm::Optional<std::string>());
116+
#else
117+
return Context->Builder.collectAssociatedTypes<NoObjCInterop, 8>(llvm::Optional<std::string>());
118+
#endif
119+
default:
120+
fputs("unsupported word size in object file\n", stderr);
121+
abort();
122+
}
89123
}
90124

91125
FieldTypeCollectionResult

0 commit comments

Comments
 (0)