Skip to content

Commit 9dddc64

Browse files
committed
SwiftRemoteMirror: Project error existentials
Error existentials have a kind of special heap layout and can also be compatible as NSError instances, too.
1 parent 063e755 commit 9dddc64

File tree

11 files changed

+458
-136
lines changed

11 files changed

+458
-136
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ template <typename Runtime>
4040
class ReflectionContext
4141
: public remote::MetadataReader<Runtime, TypeRefBuilder> {
4242
using super = remote::MetadataReader<Runtime, TypeRefBuilder>;
43+
using super::readMetadata;
44+
using super::readObjCClassName;
4345

4446
std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache;
4547

@@ -229,6 +231,77 @@ class ReflectionContext
229231
}
230232
return true;
231233
}
234+
case RecordKind::ErrorExistential: {
235+
// We have a pointer to an error existential, which is always heap object.
236+
237+
bool successfullyGotIsa = false;
238+
StoredPointer MetadataAddress = 0;
239+
std::tie(successfullyGotIsa, MetadataAddress)
240+
= readMetadataFromInstance(ExistentialAddress.getAddressData());
241+
242+
if (!successfullyGotIsa)
243+
return false;
244+
245+
bool isObjC = false;
246+
247+
// If we can determine the Objective-C class name, this is probably an
248+
// error existential with NSError-compatible layout.
249+
std::string ObjCClassName;
250+
if (readObjCClassName(MetadataAddress, ObjCClassName)) {
251+
if (ObjCClassName == "_SwiftNativeNSError")
252+
isObjC = true;
253+
} else {
254+
// Otherwise, we can check to see if this is a class metadata with the
255+
// kind value's least significant bit set, which indicates a pure
256+
// Swift class.
257+
auto Meta = readMetadata(MetadataAddress);
258+
auto ClassMeta = dyn_cast<TargetClassMetadata<Runtime>>(Meta);
259+
if (!ClassMeta)
260+
return false;
261+
262+
isObjC = ClassMeta->isPureObjC();
263+
}
264+
265+
// In addition to the isa pointer and two 32-bit reference counts, if the
266+
// error existential is layout-compatible with NSError, we also need to
267+
// skip over its three word-sized fields: the error code, the domain,
268+
// and userInfo.
269+
StoredPointer InstanceMetadataAddressAddress
270+
= ExistentialAddress.getAddressData() +
271+
(isObjC ? 5 : 2) * sizeof(StoredPointer);
272+
273+
// We need to get the instance's alignment info so we can get the exact
274+
// offset of the start of its data in the class.
275+
StoredPointer InstanceMetadataAddress = 0;
276+
std::tie(successfullyGotIsa, InstanceMetadataAddress) =
277+
readMetadataFromInstance(InstanceMetadataAddressAddress);
278+
if (!successfullyGotIsa)
279+
return false;
280+
281+
auto InstanceTR = readTypeFromMetadata(InstanceMetadataAddress);
282+
if (!InstanceTR)
283+
return false;
284+
285+
auto InstanceTI = getTypeInfo(InstanceTR);
286+
if (!InstanceTI)
287+
return false;
288+
289+
// Now we need to skip over the instance metadata pointer and instance's
290+
// conformance pointer for Swift.ErrorProtocol.
291+
StoredPointer InstanceAddress = InstanceMetadataAddressAddress +
292+
2 * sizeof(StoredPointer);
293+
294+
// Round up to alignment, and we have the start address of the
295+
// instance payload.
296+
auto Alignment = InstanceTI->getAlignment();
297+
InstanceAddress += Alignment - InstanceAddress % Alignment;
298+
299+
*OutInstanceTR = InstanceTR;
300+
*OutInstanceAddress = RemoteAddress(InstanceAddress);
301+
302+
return true;
303+
break;
304+
}
232305
default:
233306
return false;
234307
}

include/swift/Reflection/TypeLowering.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ enum class RecordKind : unsigned {
5252
// An existential metatype.
5353
ExistentialMetatype,
5454

55+
// An error existential is a special kind of heap object, so is a retainable
56+
// pointer, with no witness tables.
57+
ErrorExistential,
58+
5559
// A class instance layout, consisting of the stored properties of
5660
// one class, excluding superclasses.
5761
ClassInstance,

include/swift/Reflection/TypeRef.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,14 @@ class ProtocolTypeRef final : public TypeRef {
386386
FIND_OR_CREATE_TYPEREF(A, ProtocolTypeRef, MangledName);
387387
}
388388

389+
bool isAnyObject() const {
390+
return MangledName == "Ps9AnyObject_";
391+
}
392+
393+
bool isErrorProtocol() const {
394+
return MangledName == "Ps13ErrorProtocol_";
395+
}
396+
389397
const std::string &getMangledName() const {
390398
return MangledName;
391399
}

include/swift/Remote/MetadataReader.h

Lines changed: 87 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -895,23 +895,29 @@ class MetadataReader {
895895
return targetAddress + signext;
896896
}
897897

898-
private:
899-
template <template <class R> class M>
900-
MetadataRef _readMetadata(StoredPointer address) {
901-
return _readMetadata(address, sizeof(M<Runtime>));
902-
}
898+
/// Given a pointer to an Objective-C class, try to read its class name.
899+
bool readObjCClassName(StoredPointer classAddress, std::string &className) {
900+
// The following algorithm only works on the non-fragile Apple runtime.
903901

904-
MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) {
905-
auto size = sizeAfter;
906-
uint8_t *buffer = (uint8_t *) malloc(size);
907-
if (!Reader->readBytes(RemoteAddress(address), buffer, size)) {
908-
free(buffer);
909-
return nullptr;
910-
}
902+
// Grab the RO-data pointer. This part is not ABI.
903+
StoredPointer roDataPtr = readObjCRODataPtr(classAddress);
904+
if (!roDataPtr) return false;
911905

912-
auto metadata = reinterpret_cast<TargetMetadata<Runtime>*>(buffer);
913-
MetadataCache.insert(std::make_pair(address, OwnedMetadataRef(metadata)));
914-
return MetadataRef(address, metadata);
906+
// This is ABI.
907+
static constexpr auto OffsetToName =
908+
roundUpToAlignment(size_t(12), sizeof(StoredPointer))
909+
+ sizeof(StoredPointer);;
910+
911+
// Read the name pointer.
912+
StoredPointer namePtr;
913+
if (!Reader->readInteger(RemoteAddress(roDataPtr + OffsetToName), &namePtr))
914+
return false;
915+
916+
// If the name pointer is null, treat that as an error.
917+
if (!namePtr)
918+
return false;
919+
920+
return Reader->readString(RemoteAddress(namePtr), className);
915921
}
916922

917923
MetadataRef readMetadata(StoredPointer address) {
@@ -924,64 +930,83 @@ class MetadataReader {
924930
return nullptr;
925931

926932
switch (getEnumeratedMetadataKind(KindValue)) {
927-
case MetadataKind::Class:
928-
return _readMetadata<TargetClassMetadata>(address);
929-
case MetadataKind::Enum:
930-
return _readMetadata<TargetEnumMetadata>(address);
931-
case MetadataKind::ErrorObject:
932-
return _readMetadata<TargetEnumMetadata>(address);
933-
case MetadataKind::Existential: {
934-
StoredPointer numProtocolsAddress = address +
933+
case MetadataKind::Class:
934+
return _readMetadata<TargetClassMetadata>(address);
935+
case MetadataKind::Enum:
936+
return _readMetadata<TargetEnumMetadata>(address);
937+
case MetadataKind::ErrorObject:
938+
return _readMetadata<TargetEnumMetadata>(address);
939+
case MetadataKind::Existential: {
940+
StoredPointer numProtocolsAddress = address +
935941
TargetExistentialTypeMetadata<Runtime>::OffsetToNumProtocols;
936-
StoredPointer numProtocols;
937-
if (!Reader->readInteger(RemoteAddress(numProtocolsAddress),
938-
&numProtocols))
939-
return nullptr;
942+
StoredPointer numProtocols;
943+
if (!Reader->readInteger(RemoteAddress(numProtocolsAddress),
944+
&numProtocols))
945+
return nullptr;
940946

941-
auto totalSize = sizeof(TargetExistentialTypeMetadata<Runtime>)
942-
+ numProtocols *
943-
sizeof(ConstTargetMetadataPointer<Runtime, TargetProtocolDescriptor>);
947+
auto totalSize = sizeof(TargetExistentialTypeMetadata<Runtime>)
948+
+ numProtocols *
949+
sizeof(ConstTargetMetadataPointer<Runtime, TargetProtocolDescriptor>);
944950

945-
return _readMetadata(address, totalSize);
946-
}
947-
case MetadataKind::ExistentialMetatype:
948-
return _readMetadata<TargetExistentialMetatypeMetadata>(address);
949-
case MetadataKind::ForeignClass:
950-
return _readMetadata<TargetForeignClassMetadata>(address);
951-
case MetadataKind::Function:
952-
return _readMetadata<TargetFunctionTypeMetadata>(address);
953-
case MetadataKind::HeapGenericLocalVariable:
954-
return _readMetadata<TargetHeapLocalVariableMetadata>(address);
955-
case MetadataKind::HeapLocalVariable:
956-
return _readMetadata<TargetHeapLocalVariableMetadata>(address);
957-
case MetadataKind::Metatype:
958-
return _readMetadata<TargetMetatypeMetadata>(address);
959-
case MetadataKind::ObjCClassWrapper:
960-
return _readMetadata<TargetObjCClassWrapperMetadata>(address);
961-
case MetadataKind::Opaque:
962-
return _readMetadata<TargetOpaqueMetadata>(address);
963-
case MetadataKind::Optional:
964-
return _readMetadata<TargetEnumMetadata>(address);
965-
case MetadataKind::Struct:
966-
return _readMetadata<TargetStructMetadata>(address);
967-
case MetadataKind::Tuple: {
968-
auto numElementsAddress = address +
951+
return _readMetadata(address, totalSize);
952+
}
953+
case MetadataKind::ExistentialMetatype:
954+
return _readMetadata<TargetExistentialMetatypeMetadata>(address);
955+
case MetadataKind::ForeignClass:
956+
return _readMetadata<TargetForeignClassMetadata>(address);
957+
case MetadataKind::Function:
958+
return _readMetadata<TargetFunctionTypeMetadata>(address);
959+
case MetadataKind::HeapGenericLocalVariable:
960+
return _readMetadata<TargetHeapLocalVariableMetadata>(address);
961+
case MetadataKind::HeapLocalVariable:
962+
return _readMetadata<TargetHeapLocalVariableMetadata>(address);
963+
case MetadataKind::Metatype:
964+
return _readMetadata<TargetMetatypeMetadata>(address);
965+
case MetadataKind::ObjCClassWrapper:
966+
return _readMetadata<TargetObjCClassWrapperMetadata>(address);
967+
case MetadataKind::Opaque:
968+
return _readMetadata<TargetOpaqueMetadata>(address);
969+
case MetadataKind::Optional:
970+
return _readMetadata<TargetEnumMetadata>(address);
971+
case MetadataKind::Struct:
972+
return _readMetadata<TargetStructMetadata>(address);
973+
case MetadataKind::Tuple: {
974+
auto numElementsAddress = address +
969975
TargetTupleTypeMetadata<Runtime>::OffsetToNumElements;
970-
StoredSize numElements;
971-
if (!Reader->readInteger(RemoteAddress(numElementsAddress),
972-
&numElements))
973-
return nullptr;
974-
auto totalSize = sizeof(TargetTupleTypeMetadata<Runtime>)
975-
+ numElements * sizeof(StoredPointer);
976-
return _readMetadata(address, totalSize);
977-
}
976+
StoredSize numElements;
977+
if (!Reader->readInteger(RemoteAddress(numElementsAddress),
978+
&numElements))
979+
return nullptr;
980+
auto totalSize = sizeof(TargetTupleTypeMetadata<Runtime>)
981+
+ numElements * sizeof(StoredPointer);
982+
return _readMetadata(address, totalSize);
983+
}
978984
}
979985

980986
// We can fall out here if the value wasn't actually a valid
981987
// MetadataKind.
982988
return nullptr;
983989
}
984990

991+
private:
992+
template <template <class R> class M>
993+
MetadataRef _readMetadata(StoredPointer address) {
994+
return _readMetadata(address, sizeof(M<Runtime>));
995+
}
996+
997+
MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) {
998+
auto size = sizeAfter;
999+
uint8_t *buffer = (uint8_t *) malloc(size);
1000+
if (!Reader->readBytes(RemoteAddress(address), buffer, size)) {
1001+
free(buffer);
1002+
return nullptr;
1003+
}
1004+
1005+
auto metadata = reinterpret_cast<TargetMetadata<Runtime>*>(buffer);
1006+
MetadataCache.insert(std::make_pair(address, OwnedMetadataRef(metadata)));
1007+
return MetadataRef(address, metadata);
1008+
}
1009+
9851010
StoredPointer readAddressOfNominalTypeDescriptor(MetadataRef metadata) {
9861011
switch (metadata->getKind()) {
9871012
case MetadataKind::Class: {
@@ -1144,31 +1169,6 @@ class MetadataReader {
11441169
return nominal;
11451170
}
11461171

1147-
/// Given a pointer to an Objective-C class, try to read its class name.
1148-
bool readObjCClassName(StoredPointer classAddress, std::string &className) {
1149-
// The following algorithm only works on the non-fragile Apple runtime.
1150-
1151-
// Grab the RO-data pointer. This part is not ABI.
1152-
StoredPointer roDataPtr = readObjCRODataPtr(classAddress);
1153-
if (!roDataPtr) return false;
1154-
1155-
// This is ABI.
1156-
static constexpr auto OffsetToName =
1157-
roundUpToAlignment(size_t(12), sizeof(StoredPointer))
1158-
+ sizeof(StoredPointer);;
1159-
1160-
// Read the name pointer.
1161-
StoredPointer namePtr;
1162-
if (!Reader->readInteger(RemoteAddress(roDataPtr + OffsetToName), &namePtr))
1163-
return false;
1164-
1165-
// If the name pointer is null, treat that as an error.
1166-
if (!namePtr)
1167-
return false;
1168-
1169-
return Reader->readString(RemoteAddress(namePtr), className);
1170-
}
1171-
11721172
/// Given that the remote process is running the non-fragile Apple runtime,
11731173
/// grab the ro-data from a class pointer.
11741174
StoredPointer readObjCRODataPtr(StoredPointer classAddress) {

include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ typedef enum swift_layout_kind {
6262
SWIFT_THICK_FUNCTION,
6363
SWIFT_EXISTENTIAL,
6464
SWIFT_CLASS_EXISTENTIAL,
65+
SWIFT_ERROR_EXISTENTIAL,
6566
SWIFT_EXISTENTIAL_METATYPE,
6667
SWIFT_CLASS_INSTANCE,
6768
SWIFT_CLOSURE_CONTEXT,

stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public enum InstanceKind : UInt8 {
4141
case None
4242
case Object
4343
case Existential
44+
case ErrorExistential
4445
case Closure
4546
}
4647

@@ -362,6 +363,34 @@ public func reflect<T>(any: T) {
362363
anyPointer.deallocateCapacity(sizeof(Any.self))
363364
}
364365

366+
// Reflect an `ErrorProtocol`, a.k.a. an "error existential".
367+
//
368+
// These are always boxed on the heap, with the following layout:
369+
//
370+
// - Word 0: Metadata Pointer
371+
// - Word 1: 2x 32-bit reference counts
372+
//
373+
// If Objective-C interop is available, an ErrorProtocol is also an
374+
// `NSError`, and so has:
375+
//
376+
// - Word 2: code (NSInteger)
377+
// - Word 3: domain (NSString *)
378+
// - Word 4: userInfo (NSDictionary *)
379+
//
380+
// Then, always follow:
381+
//
382+
// - Word 2 or 5: Instance type metadata pointer
383+
// - Word 3 or 6: Instance witness table for conforming
384+
// to `Swift.ErrorProtocol`.
385+
//
386+
// Following that is the instance that conforms to `ErrorProtocol`,
387+
// rounding up to its alignment.
388+
public func reflect<T: ErrorProtocol>(error: T) {
389+
let error: ErrorProtocol = error
390+
let errorPointerValue = unsafeBitCast(error, to: UInt.self)
391+
reflect(instanceAddress: errorPointerValue, kind: .ErrorExistential)
392+
}
393+
365394
/// Reflect a closure context. The given function must be a Swift-native
366395
/// @convention(thick) function value.
367396
public func reflect(function: () -> ()) {

0 commit comments

Comments
 (0)