Skip to content

Commit beb8ca9

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 b680591 commit beb8ca9

File tree

9 files changed

+301
-84
lines changed

9 files changed

+301
-84
lines changed

include/swift/ABI/GenericContext.h

Lines changed: 14 additions & 1 deletion
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

Lines changed: 5 additions & 1 deletion
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 10 additions & 7 deletions
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

Lines changed: 186 additions & 6 deletions
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];
@@ -1011,12 +1012,115 @@ class TypeRefBuilder {
10111012
bool printTypeName = false);
10121013
FieldTypeCollectionResult collectFieldTypes(llvm::Optional<std::string> forMangledTypeName);
10131014
void dumpFieldSection(std::ostream &stream);
1014-
AssociatedTypeCollectionResult collectAssociatedTypes(llvm::Optional<std::string> forMangledTypeName);
1015-
void dumpAssociatedTypeSection(std::ostream &stream);
10161015
void dumpBuiltinTypeSection(std::ostream &stream);
10171016
void dumpCaptureSection(std::ostream &stream);
10181017
void dumpMultiPayloadEnumSection(std::ostream &stream);
10191018

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

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

lib/StaticMirror/BinaryScanningTool.cpp

Lines changed: 36 additions & 2 deletions
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)