Skip to content

Commit 3d9ac50

Browse files
committed
Teach MetadataReader how to skip artificial subclasses,
and improve RemoteAST to provide corresponding APIs.
1 parent 63d2ac9 commit 3d9ac50

File tree

5 files changed

+152
-13
lines changed

5 files changed

+152
-13
lines changed

include/swift/Remote/MetadataReader.h

+70-7
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,8 @@ class MetadataReader {
626626
}
627627

628628
/// Given a remote pointer to metadata, attempt to turn it into a type.
629-
BuiltType readTypeFromMetadata(StoredPointer MetadataAddress) {
629+
BuiltType readTypeFromMetadata(StoredPointer MetadataAddress,
630+
bool skipArtificialSubclasses = false) {
630631
auto Cached = TypeCache.find(MetadataAddress);
631632
if (Cached != TypeCache.end())
632633
return Cached->second;
@@ -643,7 +644,7 @@ class MetadataReader {
643644

644645
switch (Meta->getKind()) {
645646
case MetadataKind::Class:
646-
return readNominalTypeFromMetadata(Meta);
647+
return readNominalTypeFromMetadata(Meta, skipArtificialSubclasses);
647648
case MetadataKind::Struct:
648649
return readNominalTypeFromMetadata(Meta);
649650
case MetadataKind::Enum:
@@ -997,6 +998,20 @@ class MetadataReader {
997998
return targetAddress + signext;
998999
}
9991000

1001+
template<typename Offset>
1002+
llvm::Optional<StoredPointer>
1003+
resolveNullableRelativeOffset(StoredPointer targetAddress) {
1004+
Offset relative;
1005+
if (!Reader->readInteger(RemoteAddress(targetAddress), &relative))
1006+
return llvm::None;
1007+
if (relative == 0)
1008+
return 0;
1009+
using SignedOffset = typename std::make_signed<Offset>::type;
1010+
using SignedPointer = typename std::make_signed<StoredPointer>::type;
1011+
auto signext = (SignedPointer)(SignedOffset)relative;
1012+
return targetAddress + signext;
1013+
}
1014+
10001015
/// Given a pointer to an Objective-C class, try to read its class name.
10011016
bool readObjCClassName(StoredPointer classAddress, std::string &className) {
10021017
// The following algorithm only works on the non-fragile Apple runtime.
@@ -1118,12 +1133,39 @@ class MetadataReader {
11181133
return MetadataRef(address, metadata);
11191134
}
11201135

1121-
StoredPointer readAddressOfNominalTypeDescriptor(MetadataRef metadata) {
1136+
StoredPointer readAddressOfNominalTypeDescriptor(MetadataRef &metadata,
1137+
bool skipArtificialSubclasses = false) {
11221138
switch (metadata->getKind()) {
11231139
case MetadataKind::Class: {
11241140
auto classMeta = cast<TargetClassMetadata<Runtime>>(metadata);
1125-
return resolveRelativeOffset<StoredPointer>(metadata.getAddress() +
1126-
classMeta->offsetToDescriptorOffset());
1141+
while (true) {
1142+
auto descriptorAddress =
1143+
resolveNullableRelativeOffset<StoredPointer>(metadata.getAddress() +
1144+
classMeta->offsetToDescriptorOffset());
1145+
1146+
// Propagate errors reading the offset.
1147+
if (!descriptorAddress) return 0;
1148+
1149+
// If this class has a null descriptor, it's artificial,
1150+
// and we need to skip it upon request. Otherwise, we're done.
1151+
if (*descriptorAddress || !skipArtificialSubclasses)
1152+
return *descriptorAddress;
1153+
1154+
auto superclassMetadataAddress = classMeta->SuperClass;
1155+
if (!superclassMetadataAddress)
1156+
return 0;
1157+
1158+
auto superMeta = readMetadata(superclassMetadataAddress);
1159+
if (!superMeta)
1160+
return 0;
1161+
auto superclassMeta =
1162+
dyn_cast<TargetClassMetadata<Runtime>>(superMeta);
1163+
if (!superclassMeta)
1164+
return 0;
1165+
1166+
classMeta = superclassMeta;
1167+
metadata = superMeta;
1168+
}
11271169
}
11281170

11291171
case MetadataKind::Struct:
@@ -1241,11 +1283,24 @@ class MetadataReader {
12411283
return substitutions;
12421284
}
12431285

1244-
BuiltType readNominalTypeFromMetadata(MetadataRef metadata) {
1245-
auto descriptorAddress = readAddressOfNominalTypeDescriptor(metadata);
1286+
BuiltType readNominalTypeFromMetadata(MetadataRef origMetadata,
1287+
bool skipArtificialSubclasses = false) {
1288+
auto metadata = origMetadata;
1289+
auto descriptorAddress =
1290+
readAddressOfNominalTypeDescriptor(metadata,
1291+
skipArtificialSubclasses);
12461292
if (!descriptorAddress)
12471293
return BuiltType();
12481294

1295+
// If we've skipped an artificial subclasses, check the cache at
1296+
// the superclass. (This also protects against recursion.)
1297+
if (skipArtificialSubclasses &&
1298+
metadata.getAddress() != origMetadata.getAddress()) {
1299+
auto it = TypeCache.find(metadata.getAddress());
1300+
if (it != TypeCache.end())
1301+
return it->second;
1302+
}
1303+
12491304
// Read the nominal type descriptor.
12501305
auto descriptor = readNominalTypeDescriptor(descriptorAddress);
12511306
if (!descriptor)
@@ -1277,6 +1332,14 @@ class MetadataReader {
12771332
if (!nominal) return BuiltType();
12781333

12791334
TypeCache[metadata.getAddress()] = nominal;
1335+
1336+
// If we've skipped an artificial subclass, remove the
1337+
// recursion-protection entry we made for it.
1338+
if (skipArtificialSubclasses &&
1339+
metadata.getAddress() != origMetadata.getAddress()) {
1340+
TypeCache.erase(origMetadata.getAddress());
1341+
}
1342+
12801343
return nominal;
12811344
}
12821345

include/swift/RemoteAST/RemoteAST.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,16 @@ class RemoteASTContext {
168168

169169
/// Given an address which is supposedly of type metadata, try to
170170
/// resolve it to a specific type in the local AST.
171-
Result<Type> getTypeForRemoteTypeMetadata(remote::RemoteAddress address);
171+
///
172+
/// \param skipArtificial If true, the address may be an artificial type
173+
/// wrapper that should be ignored. For example, it could be a
174+
/// dynamic subclass created by (e.g.) CoreData or KVO; if so, and this
175+
/// flag is set, this method will implicitly ignore the subclass
176+
/// and instead attempt to resolve a type for the first non-artificial
177+
/// superclass.
178+
Result<Type>
179+
getTypeForRemoteTypeMetadata(remote::RemoteAddress address,
180+
bool skipArtificial = false);
172181

173182
/// Given an address which is supposedly of type metadata, try to
174183
/// resolve it to a specific MetadataKind value for its backing type.
@@ -198,6 +207,10 @@ class RemoteASTContext {
198207
Result<uint64_t> getOffsetOfMember(Type type,
199208
remote::RemoteAddress optMetadataAddress,
200209
StringRef memberName);
210+
211+
/// Given a heap object, resolve its heap metadata.
212+
Result<remote::RemoteAddress>
213+
getHeapMetadataForObject(remote::RemoteAddress address);
201214
};
202215

203216
} // end namespace remoteAST

lib/RemoteAST/RemoteAST.cpp

+22-5
Original file line numberDiff line numberDiff line change
@@ -666,11 +666,13 @@ class RemoteASTContextImpl {
666666
virtual ~RemoteASTContextImpl() = default;
667667

668668
virtual Result<Type>
669-
getTypeForRemoteTypeMetadata(RemoteAddress metadata) = 0;
669+
getTypeForRemoteTypeMetadata(RemoteAddress metadata, bool skipArtificial) = 0;
670670
virtual Result<MetadataKind>
671671
getKindForRemoteTypeMetadata(RemoteAddress metadata) = 0;
672672
virtual Result<NominalTypeDecl*>
673673
getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) = 0;
674+
virtual Result<RemoteAddress>
675+
getHeapMetadataForObject(RemoteAddress object) = 0;
674676

675677
Result<uint64_t>
676678
getOffsetOfMember(Type type, RemoteAddress optMetadata, StringRef memberName){
@@ -971,8 +973,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl {
971973
ASTContext &ctx)
972974
: Reader(std::move(reader), ctx) {}
973975

974-
Result<Type> getTypeForRemoteTypeMetadata(RemoteAddress metadata) override {
975-
if (auto result = Reader.readTypeFromMetadata(metadata.getAddressData()))
976+
Result<Type> getTypeForRemoteTypeMetadata(RemoteAddress metadata,
977+
bool skipArtificial) override {
978+
if (auto result = Reader.readTypeFromMetadata(metadata.getAddressData(),
979+
skipArtificial))
976980
return result;
977981
return getFailure<Type>();
978982
}
@@ -1014,6 +1018,13 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl {
10141018
return fail<uint64_t>(Failure::Unimplemented,
10151019
"look up field offset by name");
10161020
}
1021+
1022+
Result<RemoteAddress>
1023+
getHeapMetadataForObject(RemoteAddress object) override {
1024+
auto result = Reader.readMetadataFromInstance(object.getAddressData());
1025+
if (result.first) return RemoteAddress(result.second);
1026+
return getFailure<RemoteAddress>();
1027+
}
10171028
};
10181029

10191030
} // end anonymous namespace
@@ -1046,8 +1057,9 @@ RemoteASTContext::~RemoteASTContext() {
10461057
}
10471058

10481059
Result<Type>
1049-
RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address) {
1050-
return asImpl(Impl)->getTypeForRemoteTypeMetadata(address);
1060+
RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address,
1061+
bool skipArtificial) {
1062+
return asImpl(Impl)->getTypeForRemoteTypeMetadata(address, skipArtificial);
10511063
}
10521064

10531065
Result<MetadataKind>
@@ -1065,3 +1077,8 @@ RemoteASTContext::getOffsetOfMember(Type type, RemoteAddress optMetadata,
10651077
StringRef memberName) {
10661078
return asImpl(Impl)->getOffsetOfMember(type, optMetadata, memberName);
10671079
}
1080+
1081+
Result<remote::RemoteAddress>
1082+
RemoteASTContext::getHeapMetadataForObject(remote::RemoteAddress address) {
1083+
return asImpl(Impl)->getHeapMetadataForObject(address);
1084+
}

test/RemoteAST/objc_classes.swift

+16
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,21 @@ import Foundation
88
@_silgen_name("printMetadataType")
99
func printType(_: Any.Type)
1010

11+
@_silgen_name("printHeapMetadataType")
12+
func printDynamicType(_: AnyObject)
13+
1114
printType(NSString.self)
1215
// CHECK: NSString
16+
17+
class A<T> : NSObject {
18+
@objc var property: Int
19+
override init() { property = 0 }
20+
}
21+
let a = A<Int>()
22+
printDynamicType(a)
23+
// CHECK: A<Int>
24+
25+
let observer = NSObject()
26+
a.addObserver(observer, forKeyPath: "property", options: [], context: nil)
27+
printDynamicType(a)
28+
// CHECK: A<Int>

tools/swift-remoteast-test/swift-remoteast-test.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,36 @@ extern "C" void printMetadataType(const Metadata *typeMetadata) {
5252
}
5353
}
5454

55+
// FIXME: swiftcall
56+
/// func printDynamicType(_: AnyObject)
57+
LLVM_ATTRIBUTE_USED
58+
extern "C" void printHeapMetadataType(void *object) {
59+
assert(Context && "context was not set");
60+
61+
std::shared_ptr<MemoryReader> reader(new InProcessMemoryReader());
62+
RemoteASTContext remoteAST(*Context, std::move(reader));
63+
64+
auto &out = llvm::outs();
65+
66+
auto metadataResult =
67+
remoteAST.getHeapMetadataForObject(RemoteAddress(object));
68+
if (!metadataResult) {
69+
out << metadataResult.getFailure().render() << '\n';
70+
return;
71+
}
72+
auto metadata = metadataResult.getValue();
73+
74+
auto result =
75+
remoteAST.getTypeForRemoteTypeMetadata(metadata, /*skipArtificial*/ true);
76+
if (result) {
77+
out << "found type: ";
78+
result.getValue().print(out);
79+
out << '\n';
80+
} else {
81+
out << result.getFailure().render() << '\n';
82+
}
83+
}
84+
5585
static void printMemberOffset(const Metadata *typeMetadata,
5686
StringRef memberName, bool passMetadata) {
5787
assert(Context && "context was not set");

0 commit comments

Comments
 (0)