Skip to content

Commit bc79518

Browse files
committed
[Static Mirror] Handle anonymous contexts when re-constructing fully-qualified type names
Anonymous contexts (e.g. types nested inside functons) require special handling when we are constructing a fully-qualified name. We construct the name by walking from a type's descriptor to its parent contexts. Previously, we would give up upon encountering an anonymous contexts. This change refactors fully-qualified name construction to happen in two phases: 1. Collect a full context ancestor chain 2. Walk the chain backwards to reconstruct the fully-qualified name As opposed to the previous approach which always constructed the name while recursively walking to the parent context. This is required because types nested inside anonymous contexts are represented in the fully-qualified type name as `(type_name in $XXXXXXXX)` where XXXXXXXX is the address of the context descriptor of the parent anonymous context. Resolves rdar://91073103
1 parent 7a4c36e commit bc79518

File tree

4 files changed

+210
-96
lines changed

4 files changed

+210
-96
lines changed

include/swift/ABI/Metadata.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,6 +2839,9 @@ struct TargetAnonymousContextDescriptor final
28392839
};
28402840
using AnonymousContextDescriptor = TargetAnonymousContextDescriptor<InProcess>;
28412841

2842+
template<template <typename Runtime> class ObjCInteropKind, unsigned PointerSize>
2843+
using ExternalAnonymousContextDescriptor = TargetAnonymousContextDescriptor<External<ObjCInteropKind<RuntimeTarget<PointerSize>>>>;
2844+
28422845
/// A protocol descriptor.
28432846
///
28442847
/// Protocol descriptors contain information about the contents of a protocol:

include/swift/Reflection/TypeRefBuilder.h

Lines changed: 198 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <iomanip>
2828
#include <iostream>
2929
#include <ostream>
30+
#include <sstream>
3031
#include <unordered_map>
3132
#include <vector>
3233

@@ -1035,88 +1036,111 @@ class TypeRefBuilder {
10351036
OpaquePointerReader(pointerReader),
10361037
OpaqueDynamicSymbolResolver(dynamicSymbolResolver) {}
10371038

1038-
llvm::Optional<std::string>
1039-
getParentContextName(uintptr_t contextDescriptorAddress) {
1040-
llvm::Optional<std::string> optionalParentContextName;
1041-
auto contextTypeDescriptorBytes = OpaqueByteReader(
1042-
remote::RemoteAddress(contextDescriptorAddress),
1043-
sizeof(ExternalContextDescriptor<ObjCInteropKind, PointerSize>));
1044-
if (!contextTypeDescriptorBytes.get()) {
1045-
Error = "Failed to read context descriptor.";
1046-
return llvm::None;
1047-
}
1048-
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
1049-
*contextDescriptor =
1050-
(const ExternalContextDescriptor<ObjCInteropKind, PointerSize> *)
1051-
contextTypeDescriptorBytes.get();
1039+
struct ContextNameInfo {
1040+
std::string name;
1041+
uintptr_t descriptorAddress;
1042+
bool isAnonymous;
1043+
1044+
~ContextNameInfo() {}
1045+
};
1046+
1047+
bool isModuleDescriptor(
1048+
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
1049+
*contextDescriptor) {
1050+
return isa<ExternalModuleContextDescriptor<ObjCInteropKind, PointerSize>>(
1051+
contextDescriptor);
1052+
}
10521053

1054+
uintptr_t getParentDescriptorAddress(
1055+
uintptr_t contextDescriptorAddress,
1056+
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
1057+
*contextDescriptor) {
10531058
auto parentOffsetAddress = detail::applyRelativeOffset(
10541059
(const char *)contextDescriptorAddress,
10551060
(int32_t)contextDescriptor->getParentOffset());
1056-
auto parentOffsetBytes = OpaqueByteReader(
1061+
auto parentOfsetBytes = OpaqueByteReader(
10571062
remote::RemoteAddress(parentOffsetAddress), sizeof(uint32_t));
1058-
if (!parentOffsetBytes.get()) {
1059-
Error = "Failed to parent offset in a type descriptor.";
1060-
return llvm::None;
1061-
}
1062-
auto parentFieldOffset = (const int32_t *)parentOffsetBytes.get();
1063+
auto parentFieldOffset = (const int32_t *)parentOfsetBytes.get();
10631064
auto parentTargetAddress = detail::applyRelativeOffset(
10641065
(const char *)parentOffsetAddress, *parentFieldOffset);
1066+
return parentTargetAddress;
1067+
}
10651068

1066-
//
1067-
auto readContextParentName =
1068-
[&](uintptr_t descriptorAddress) -> llvm::Optional<std::string> {
1069-
llvm::Optional<std::string> optionalParentName;
1069+
llvm::Optional<ContextNameInfo>
1070+
getContextName(uintptr_t contextDescriptorAddress,
1071+
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
1072+
*contextDescriptor) {
1073+
if (auto moduleDescriptor = dyn_cast<
1074+
ExternalModuleContextDescriptor<ObjCInteropKind, PointerSize>>(
1075+
contextDescriptor)) {
1076+
auto moduleDescriptorName = readModuleNameFromModuleDescriptor(
1077+
moduleDescriptor, contextDescriptorAddress);
1078+
if (!moduleDescriptorName.hasValue())
1079+
return llvm::None;
1080+
else
1081+
return ContextNameInfo{moduleDescriptorName.getValue(),
1082+
contextDescriptorAddress, false};
1083+
} else if (auto typeDescriptor = dyn_cast<ExternalTypeContextDescriptor<
1084+
ObjCInteropKind, PointerSize>>(contextDescriptor)) {
1085+
auto typeDescriptorName = readTypeNameFromTypeDescriptor(
1086+
typeDescriptor, contextDescriptorAddress);
1087+
if (!typeDescriptorName.hasValue())
1088+
return llvm::None;
1089+
else
1090+
return ContextNameInfo{typeDescriptorName.getValue(),
1091+
contextDescriptorAddress, false};
1092+
} else if (auto anonymousDescriptor =
1093+
dyn_cast<ExternalAnonymousContextDescriptor<
1094+
ObjCInteropKind, PointerSize>>(contextDescriptor)) {
1095+
auto anonymousDescriptorName = readAnonymousNameFromAnonymousDescriptor(
1096+
anonymousDescriptor, contextDescriptorAddress);
1097+
if (!anonymousDescriptorName.hasValue())
1098+
return llvm::None;
1099+
else
1100+
return ContextNameInfo{anonymousDescriptorName.getValue(),
1101+
contextDescriptorAddress, true};
1102+
} else {
1103+
Error = "Unexpected type of context descriptor.";
1104+
return llvm::None;
1105+
}
1106+
}
1107+
1108+
void getParentContextChain(
1109+
uintptr_t contextDescriptorAddress,
1110+
const ExternalContextDescriptor<ObjCInteropKind, PointerSize>
1111+
*contextDescriptor,
1112+
std::vector<ContextNameInfo> &chain) {
1113+
const auto parentDescriptorAddress = getParentDescriptorAddress(
1114+
contextDescriptorAddress, contextDescriptor);
1115+
1116+
auto addParentNameAndRecurse =
1117+
[&](uintptr_t parentContextDescriptorAddress,
1118+
std::vector<ContextNameInfo> &chain) -> void {
10701119
auto parentContextDescriptorBytes = OpaqueByteReader(
1071-
remote::RemoteAddress(descriptorAddress),
1120+
remote::RemoteAddress(parentContextDescriptorAddress),
10721121
sizeof(ExternalContextDescriptor<ObjCInteropKind, PointerSize>));
10731122
if (!parentContextDescriptorBytes.get()) {
10741123
Error = "Failed to read context descriptor.";
1075-
return llvm::None;
1124+
return;
10761125
}
1077-
const ExternalContextDescriptor<ObjCInteropKind,
1078-
PointerSize> *parentContextDescriptor =
1126+
const auto parentDescriptor =
10791127
(const ExternalContextDescriptor<ObjCInteropKind, PointerSize> *)
10801128
parentContextDescriptorBytes.get();
1081-
1082-
if (auto moduleDescriptor = dyn_cast<
1083-
ExternalModuleContextDescriptor<ObjCInteropKind, PointerSize>>(
1084-
parentContextDescriptor)) {
1085-
auto moduleDescriptorName = readModuleNameFromModuleDescriptor(
1086-
moduleDescriptor, parentTargetAddress);
1087-
if (!moduleDescriptorName.hasValue())
1088-
return llvm::None;
1089-
else
1090-
optionalParentName = moduleDescriptorName;
1091-
} else if (auto typeDescriptor =
1092-
dyn_cast<ExternalTypeContextDescriptor<ObjCInteropKind,
1093-
PointerSize>>(
1094-
parentContextDescriptor)) {
1095-
auto typeDescriptorName = readTypeNameFromTypeDescriptor(
1096-
typeDescriptor, parentTargetAddress);
1097-
if (!typeDescriptorName.hasValue())
1098-
return llvm::None;
1099-
else
1100-
optionalParentName = typeDescriptorName;
1101-
// Recurse to get this type's parent.
1102-
auto optionalParentParentName =
1103-
getParentContextName(descriptorAddress);
1104-
if (optionalParentParentName.hasValue()) {
1105-
optionalParentName = optionalParentParentName.getValue() + "." +
1106-
optionalParentName.getValue();
1107-
}
1108-
} else {
1109-
Error = "Unexpected type of parent context descriptor.";
1110-
return llvm::None;
1129+
const auto parentNameInfo =
1130+
getContextName(parentContextDescriptorAddress, parentDescriptor);
1131+
if (!parentNameInfo.hasValue()) {
1132+
return;
1133+
}
1134+
chain.push_back(parentNameInfo.getValue());
1135+
if (!isModuleDescriptor(parentDescriptor)) {
1136+
getParentContextChain(parentContextDescriptorAddress,
1137+
parentDescriptor, chain);
11111138
}
1112-
1113-
return optionalParentName;
11141139
};
11151140

1116-
// Set low bit indicates that this is an indirect
1117-
// reference
1118-
if (parentTargetAddress & 0x1) {
1119-
auto adjustedParentTargetAddress = parentTargetAddress & ~0x1;
1141+
// Set low bit indicates that this is an indirect reference
1142+
if (parentDescriptorAddress & 0x1) {
1143+
auto adjustedParentTargetAddress = parentDescriptorAddress & ~0x1;
11201144
if (auto symbol = OpaquePointerReader(
11211145
remote::RemoteAddress(adjustedParentTargetAddress),
11221146
PointerSize)) {
@@ -1125,20 +1149,21 @@ class TypeRefBuilder {
11251149
auto demangledRoot =
11261150
Ctx.demangleSymbolAsNode(symbol->getSymbol().str());
11271151
assert(demangledRoot->getKind() == Node::Kind::Global);
1128-
optionalParentContextName =
1152+
std::string nodeName =
11291153
nodeToString(demangledRoot->getChild(0)->getChild(0));
1154+
chain.push_back(
1155+
ContextNameInfo{nodeName, adjustedParentTargetAddress, false});
11301156
} else {
1131-
optionalParentContextName =
1132-
readContextParentName(adjustedParentTargetAddress);
1157+
addParentNameAndRecurse(adjustedParentTargetAddress, chain);
11331158
}
11341159
} else {
11351160
Error = "Error reading external symbol address.";
1136-
return llvm::None;
1161+
return;
11371162
}
11381163
} else {
1139-
optionalParentContextName = readContextParentName(parentTargetAddress);
1164+
addParentNameAndRecurse(parentDescriptorAddress, chain);
11401165
}
1141-
return optionalParentContextName;
1166+
return;
11421167
}
11431168

11441169
llvm::Optional<std::string> readTypeNameFromTypeDescriptor(
@@ -1183,6 +1208,70 @@ class TypeRefBuilder {
11831208
return parentName;
11841209
}
11851210

1211+
llvm::Optional<std::string> readAnonymousNameFromAnonymousDescriptor(
1212+
const ExternalAnonymousContextDescriptor<ObjCInteropKind, PointerSize>
1213+
*anonymousDescriptor,
1214+
uintptr_t anonymousDescriptorAddress) {
1215+
if (!anonymousDescriptor->hasMangledName()) {
1216+
std::stringstream stream;
1217+
stream << "(unknown context at $" << std::hex
1218+
<< anonymousDescriptorAddress << ")";
1219+
return stream.str();
1220+
}
1221+
return llvm::None;
1222+
}
1223+
1224+
std::string constructFullyQualifiedNameFromContextChain(
1225+
const std::vector<ContextNameInfo> &contextNameChain) {
1226+
std::string newQualifiedTypeName = "";
1227+
std::vector<std::string> reversedQualifiedTypeNameMembers;
1228+
1229+
// Traverse the context chain, adding up context names.
1230+
// Anonymous contexts require special handling: when a type is nested in
1231+
// an anonymous context, its qualified name is printed as `(type_name in
1232+
// $hex_val)` where hex_val is the address of the descriptor of the
1233+
// anonymous parent context.
1234+
bool skipNext = false;
1235+
for (size_t i = 0; i < contextNameChain.size(); ++i) {
1236+
if (skipNext) {
1237+
skipNext = false;
1238+
continue;
1239+
}
1240+
const auto &contextNameInfo = contextNameChain[i];
1241+
bool lastContext = (i == contextNameChain.size() - 1);
1242+
bool currentContextIsAnonymous = contextNameInfo.isAnonymous;
1243+
bool nextContextIsAnonymous =
1244+
lastContext ? false : contextNameChain[i + 1].isAnonymous;
1245+
if (nextContextIsAnonymous && !currentContextIsAnonymous) {
1246+
std::stringstream stream;
1247+
stream << "(" << contextNameInfo.name << " in $" << std::hex
1248+
<< contextNameChain[i + 1].descriptorAddress << ")";
1249+
reversedQualifiedTypeNameMembers.push_back(stream.str());
1250+
skipNext = true;
1251+
} else if (nextContextIsAnonymous && currentContextIsAnonymous) {
1252+
1253+
} else if (!nextContextIsAnonymous && !currentContextIsAnonymous) {
1254+
reversedQualifiedTypeNameMembers.push_back(contextNameInfo.name);
1255+
} else if (!nextContextIsAnonymous && currentContextIsAnonymous) {
1256+
reversedQualifiedTypeNameMembers.push_back(contextNameInfo.name);
1257+
} else {
1258+
llvm_unreachable("Exhausted possibilities.");
1259+
}
1260+
}
1261+
1262+
// Combine the individual context name reps into a single fully-qualified
1263+
// name string
1264+
for (auto it = reversedQualifiedTypeNameMembers.rbegin();
1265+
it != reversedQualifiedTypeNameMembers.rend(); ++it) {
1266+
newQualifiedTypeName.append(*it);
1267+
if (std::next(it) != reversedQualifiedTypeNameMembers.rend()) {
1268+
newQualifiedTypeName.append(".");
1269+
}
1270+
}
1271+
1272+
return newQualifiedTypeName;
1273+
}
1274+
11861275
llvm::Optional<std::string> readProtocolNameFromProtocolDescriptor(
11871276
uintptr_t protocolDescriptorAddress) {
11881277
std::string protocolName;
@@ -1307,14 +1396,14 @@ class TypeRefBuilder {
13071396
else
13081397
typeName = optionalTypeName.getValue();
13091398

1310-
// Prepend the parent context name
1311-
auto optionalParentName =
1312-
getParentContextName(contextTypeDescriptorAddress);
1313-
if (optionalParentName.hasValue()) {
1314-
typeName = optionalParentName.getValue() + "." + typeName;
1315-
}
1316-
1317-
return std::make_pair(mangledTypeName, typeName);
1399+
std::vector<ContextNameInfo> contextNameChain;
1400+
contextNameChain.push_back(
1401+
ContextNameInfo{typeName, contextTypeDescriptorAddress, false});
1402+
getParentContextChain(contextTypeDescriptorAddress, contextDescriptor,
1403+
contextNameChain);
1404+
std::string fullyQualifiedName =
1405+
constructFullyQualifiedNameFromContextChain(contextNameChain);
1406+
return std::make_pair(mangledTypeName, fullyQualifiedName);
13181407
}
13191408

13201409
/// Extract protocol name from a Conformance Descriptor
@@ -1342,6 +1431,33 @@ class TypeRefBuilder {
13421431
(const char *)protocolDescriptorFieldAddress,
13431432
(int32_t)*protocolDescriptorOffset);
13441433

1434+
auto constructFullyQualifiedProtocolName =
1435+
[&](uintptr_t protocolDescriptorAddress)
1436+
-> llvm::Optional<std::string> {
1437+
auto protocolName =
1438+
readProtocolNameFromProtocolDescriptor(protocolDescriptorAddress);
1439+
1440+
// Read the protocol conformance descriptor itself
1441+
auto protocolContextDescriptorBytes = OpaqueByteReader(
1442+
remote::RemoteAddress(protocolDescriptorAddress),
1443+
sizeof(ExternalContextDescriptor<ObjCInteropKind, PointerSize>));
1444+
if (!protocolContextDescriptorBytes.get()) {
1445+
Error = "Failed to read context (protocol) descriptor.";
1446+
return llvm::None;
1447+
}
1448+
const ExternalContextDescriptor<ObjCInteropKind,
1449+
PointerSize> *protocolDescriptor =
1450+
(const ExternalContextDescriptor<ObjCInteropKind, PointerSize> *)
1451+
protocolContextDescriptorBytes.get();
1452+
1453+
std::vector<ContextNameInfo> contextNameChain;
1454+
contextNameChain.push_back(ContextNameInfo{
1455+
protocolName.getValue(), protocolDescriptorAddress, false});
1456+
getParentContextChain(protocolDescriptorAddress, protocolDescriptor,
1457+
contextNameChain);
1458+
return constructFullyQualifiedNameFromContextChain(contextNameChain);
1459+
};
1460+
13451461
// Set low bit indicates that this is an indirect
13461462
// reference
13471463
if (protocolDescriptorTarget & 0x1) {
@@ -1360,16 +1476,9 @@ class TypeRefBuilder {
13601476
nodeToString(demangledRoot->getChild(0)->getChild(0));
13611477
} else {
13621478
// This is an absolute address of a protocol descriptor
1363-
auto protocolDescriptorAddress = symbol->getOffset();
1364-
protocolName = readProtocolNameFromProtocolDescriptor(
1365-
protocolDescriptorAddress);
1366-
// Prepend the parent context name
1367-
auto optionalParentName =
1368-
getParentContextName(protocolDescriptorAddress);
1369-
if (optionalParentName.hasValue()) {
1370-
protocolName =
1371-
optionalParentName.getValue() + "." + *protocolName;
1372-
}
1479+
auto protocolDescriptorAddress = (uintptr_t)symbol->getOffset();
1480+
protocolName =
1481+
constructFullyQualifiedProtocolName(protocolDescriptorAddress);
13731482
}
13741483
} else {
13751484
Error = "Error reading external protocol address.";
@@ -1379,13 +1488,7 @@ class TypeRefBuilder {
13791488
// If this is a direct reference, get symbol name from the protocol
13801489
// descriptor.
13811490
protocolName =
1382-
readProtocolNameFromProtocolDescriptor(protocolDescriptorTarget);
1383-
// Prepend the parent context name
1384-
auto optionalParentName =
1385-
getParentContextName(protocolDescriptorTarget);
1386-
if (optionalParentName.hasValue()) {
1387-
protocolName = optionalParentName.getValue() + "." + *protocolName;
1388-
}
1491+
constructFullyQualifiedProtocolName(protocolDescriptorTarget);
13891492
}
13901493

13911494
return protocolName;

test/Reflection/Inputs/Conformances.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ struct StructA : MyProto, Hashable {
33
var x: Int
44
}
55

6+
struct SomeStruct {
7+
public func someFunc() -> Int {
8+
struct SomeNestedStruct : MyProto {}
9+
return 42
10+
}
11+
}
12+
613
struct foo {
714
struct bar {
815
struct baz {

0 commit comments

Comments
 (0)