Skip to content

Commit e36e8ee

Browse files
committed
Implement projectExistentialAndUnwrapClass
Implement a version of projectExistential tailored for LLDB. There are 2 differences when projecting existentials for LLDB: 1 - When it comes to existentials, LLDB stores the address of the error pointer, which must be dereferenced. 2 - When the existential wraps a class type, LLDB expects the address returned is the class instance itself and not the address of the reference. This patch also adapts the swift reflection test machinery to test projectExistentialAndUnwrapClass as well. This is done by exposing the new functionality from swift reflection test. It is tested in existentials.swift, and ensures that the typeref information is exactly the same as what is expected from projectExistential, except the out address.
1 parent 959c47b commit e36e8ee

File tree

7 files changed

+613
-27
lines changed

7 files changed

+613
-27
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class ReflectionContext
9292
using super = remote::MetadataReader<Runtime, TypeRefBuilder>;
9393
using super::readMetadata;
9494
using super::readObjCClassName;
95-
95+
using super::readResolvedPointerValue;
9696
std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache;
9797

9898
/// All buffers we need to keep around long term. This will automatically free them
@@ -789,6 +789,52 @@ class ReflectionContext
789789
}
790790
}
791791

792+
llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
793+
getDynamicTypeAndAddressClassExistential(RemoteAddress ExistentialAddress) {
794+
auto PointerValue =
795+
readResolvedPointerValue(ExistentialAddress.getAddressData());
796+
if (!PointerValue)
797+
return {};
798+
auto Result = readMetadataFromInstance(*PointerValue);
799+
if (!Result)
800+
return {};
801+
auto TypeResult = readTypeFromMetadata(Result.getValue());
802+
if (!TypeResult)
803+
return {};
804+
return {{std::move(TypeResult), RemoteAddress(*PointerValue)}};
805+
}
806+
807+
llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
808+
getDynamicTypeAndAddressErrorExistential(RemoteAddress ExistentialAddress,
809+
bool *IsBridgedError = nullptr) {
810+
auto Result = readMetadataAndValueErrorExistential(ExistentialAddress);
811+
if (!Result)
812+
return {};
813+
814+
auto TypeResult =
815+
readTypeFromMetadata(Result->MetadataAddress.getAddressData());
816+
if (!TypeResult)
817+
return {};
818+
819+
if (IsBridgedError)
820+
*IsBridgedError = Result->IsBridgedError;
821+
822+
return {{TypeResult, Result->PayloadAddress}};
823+
}
824+
825+
llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
826+
getDynamicTypeAndAddressOpaqueExistential(RemoteAddress ExistentialAddress) {
827+
auto Result = readMetadataAndValueOpaqueExistential(ExistentialAddress);
828+
if (!Result)
829+
return {};
830+
831+
auto TypeResult =
832+
readTypeFromMetadata(Result->MetadataAddress.getAddressData());
833+
if (!TypeResult)
834+
return {};
835+
return {{std::move(TypeResult), Result->PayloadAddress}};
836+
}
837+
792838
bool projectExistential(RemoteAddress ExistentialAddress,
793839
const TypeRef *ExistentialTR,
794840
const TypeRef **OutInstanceTR,
@@ -850,6 +896,75 @@ class ReflectionContext
850896
return false;
851897
}
852898
}
899+
/// A version of `projectExistential` tailored for LLDB.
900+
/// This version dereferences the resulting TypeRef if it wraps
901+
/// a class type, it also dereferences the input `ExistentialAddress` before
902+
/// attempting to find its dynamic type and address when dealing with error
903+
/// existentials.
904+
llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
905+
projectExistentialAndUnwrapClass(RemoteAddress ExistentialAddress,
906+
const TypeRef &ExistentialTR) {
907+
auto IsClass = [](const TypeRef *TypeResult) {
908+
// When the existential wraps a class type, LLDB expects that the
909+
// address returned is the class instance itself and not the address
910+
// of the reference.
911+
bool IsClass = TypeResult->getKind() == TypeRefKind::ForeignClass ||
912+
TypeResult->getKind() == TypeRefKind::ObjCClass;
913+
if (auto *nominal = llvm::dyn_cast<NominalTypeRef>(TypeResult))
914+
IsClass = nominal->isClass();
915+
else if (auto *boundGeneric =
916+
llvm::dyn_cast<BoundGenericTypeRef>(TypeResult))
917+
IsClass = boundGeneric->isClass();
918+
return IsClass;
919+
};
920+
921+
auto DereferenceAndSet = [&](RemoteAddress &Address) {
922+
auto PointerValue = readResolvedPointerValue(Address.getAddressData());
923+
if (!PointerValue)
924+
return false;
925+
Address = RemoteAddress(*PointerValue);
926+
return true;
927+
};
928+
929+
auto ExistentialRecordTI = getRecordTypeInfo(&ExistentialTR, nullptr);
930+
if (!ExistentialRecordTI)
931+
return {};
932+
933+
switch (ExistentialRecordTI->getRecordKind()) {
934+
case RecordKind::ClassExistential:
935+
return getDynamicTypeAndAddressClassExistential(ExistentialAddress);
936+
case RecordKind::ErrorExistential: {
937+
// LLDB stores the address of the error pointer.
938+
if (!DereferenceAndSet(ExistentialAddress))
939+
return {};
940+
941+
bool IsBridgedError = false;
942+
auto Pair = getDynamicTypeAndAddressErrorExistential(ExistentialAddress,
943+
&IsBridgedError);
944+
if (!Pair)
945+
return {};
946+
947+
if (!IsBridgedError && IsClass(std::get<const TypeRef *>(*Pair)))
948+
if (!DereferenceAndSet(std::get<RemoteAddress>(*Pair)))
949+
return {};
950+
951+
return Pair;
952+
}
953+
case RecordKind::OpaqueExistential: {
954+
auto Pair = getDynamicTypeAndAddressOpaqueExistential(ExistentialAddress);
955+
if (!Pair)
956+
return {};
957+
958+
if (IsClass(std::get<const TypeRef *>(*Pair)))
959+
if (!DereferenceAndSet(std::get<RemoteAddress>(*Pair)))
960+
return {};
961+
962+
return Pair;
963+
}
964+
default:
965+
return {};
966+
}
967+
}
853968

854969
/// Projects the value of an enum.
855970
///
@@ -889,6 +1004,12 @@ class ReflectionContext
8891004
}
8901005
}
8911006

1007+
const RecordTypeInfo *getRecordTypeInfo(const TypeRef *TR,
1008+
remote::TypeInfoProvider *ExternalTypeInfo) {
1009+
auto *TypeInfo = getTypeInfo(TR, ExternalTypeInfo);
1010+
return dyn_cast_or_null<const RecordTypeInfo>(TypeInfo);
1011+
}
1012+
8921013
/// Iterate the protocol conformance cache tree rooted at NodePtr, calling
8931014
/// Call with the type and protocol in each node.
8941015
void iterateConformanceTree(StoredPointer NodePtr,

include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,19 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef,
247247
swift_typeref_t *OutInstanceTypeRef,
248248
swift_addr_t *OutStartOfInstanceData);
249249

250+
/// Like swift_reflection_projectExistential, with 2 differences:
251+
///
252+
/// - When dealing with an error existential, this version will dereference
253+
/// the ExistentialAddress before proceeding.
254+
/// - After setting OutInstanceTypeRef and OutStartOfInstanceData this version
255+
/// may derefence and set OutStartOfInstanceData if OutInstanceTypeRef is a
256+
/// class TypeRef.
257+
SWIFT_REMOTE_MIRROR_LINKAGE
258+
int swift_reflection_projectExistentialAndUnwrapClass(
259+
SwiftReflectionContextRef ContextRef, swift_addr_t ExistentialAddress,
260+
swift_typeref_t ExistentialTypeRef, swift_typeref_t *OutInstanceTypeRef,
261+
swift_addr_t *OutStartOfInstanceData);
262+
250263
/// Projects the value of an enum.
251264
///
252265
/// Takes the address and typeref for an enum and determines the

stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//===----------------------------------------------------------------------===//
1818

1919
let RequestInstanceKind = "k"
20+
let RequestShouldUnwrapClassExistential = "u"
2021
let RequestInstanceAddress = "i"
2122
let RequestReflectionInfos = "r"
2223
let RequestImages = "m"
@@ -346,11 +347,15 @@ internal func sendPointerSize() {
346347
/// The parent sends a Done message to indicate that it's done
347348
/// looking at this instance. It will continue to ask for instances,
348349
/// so call doneReflecting() when you don't have any more instances.
349-
internal func reflect(instanceAddress: UInt, kind: InstanceKind) {
350+
internal func reflect(instanceAddress: UInt,
351+
kind: InstanceKind,
352+
shouldUnwrapClassExistential: Bool = false) {
350353
while let command = readLine(strippingNewline: true) {
351354
switch command {
352355
case String(validatingUTF8: RequestInstanceKind)!:
353356
sendValue(kind.rawValue)
357+
case String(validatingUTF8: RequestShouldUnwrapClassExistential)!:
358+
sendValue(shouldUnwrapClassExistential)
354359
case String(validatingUTF8: RequestInstanceAddress)!:
355360
sendValue(instanceAddress)
356361
case String(validatingUTF8: RequestReflectionInfos)!:
@@ -437,12 +442,18 @@ public func reflect(object: AnyObject) {
437442
/// The test doesn't care about the witness tables - we only care
438443
/// about what's in the buffer, so we always put these values into
439444
/// an Any existential.
440-
public func reflect<T>(any: T, kind: InstanceKind = .Existential) {
445+
///
446+
/// If shouldUnwrapClassExistential is set to true, this exercises
447+
/// projectExistentialAndUnwrapClass instead of projectExistential.
448+
public func reflect<T>(any: T, kind: InstanceKind = .Existential,
449+
shouldUnwrapClassExistential: Bool = false) {
441450
let any: Any = any
442451
let anyPointer = UnsafeMutablePointer<Any>.allocate(capacity: MemoryLayout<Any>.size)
443452
anyPointer.initialize(to: any)
444453
let anyPointerValue = UInt(bitPattern: anyPointer)
445-
reflect(instanceAddress: anyPointerValue, kind: kind)
454+
reflect(instanceAddress: anyPointerValue,
455+
kind: kind,
456+
shouldUnwrapClassExistential: shouldUnwrapClassExistential)
446457
anyPointer.deallocate()
447458
}
448459

@@ -474,6 +485,21 @@ public func reflect<T: Error>(error: T) {
474485
reflect(instanceAddress: errorPointerValue, kind: .ErrorExistential)
475486
}
476487

488+
// Like reflect<T: Error>(error: T), but calls projectExistentialAndUnwrapClass
489+
// instead of projectExistential and adds an extra level of indirection, which is
490+
// what projectExistentialAndUnwrapClass expects.
491+
public func reflectUnwrappingClassExistential<T: Error>(error: T) {
492+
let error: Error = error
493+
let errorPointerValue = unsafeBitCast(error, to: UInt.self)
494+
let anyPointer = UnsafeMutablePointer<Any>.allocate(capacity: MemoryLayout<Any>.size)
495+
anyPointer.initialize(to: errorPointerValue)
496+
let anyPointerValue = UInt(bitPattern: anyPointer)
497+
reflect(instanceAddress: anyPointerValue,
498+
kind: .ErrorExistential,
499+
shouldUnwrapClassExistential: true)
500+
anyPointer.deallocate()
501+
}
502+
477503
// Reflect an `Enum`
478504
//
479505
// These are handled like existentials, but

stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,23 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef,
571571
return Success;
572572
}
573573

574+
int swift_reflection_projectExistentialAndUnwrapClass(SwiftReflectionContextRef ContextRef,
575+
swift_addr_t ExistentialAddress,
576+
swift_typeref_t ExistentialTypeRef,
577+
swift_typeref_t *InstanceTypeRef,
578+
swift_addr_t *StartOfInstanceData) {
579+
auto Context = ContextRef->nativeContext;
580+
auto ExistentialTR = reinterpret_cast<const TypeRef *>(ExistentialTypeRef);
581+
auto RemoteExistentialAddress = RemoteAddress(ExistentialAddress);
582+
auto Pair = Context->projectExistentialAndUnwrapClass(
583+
RemoteExistentialAddress, *ExistentialTR);
584+
if (!Pair.hasValue())
585+
return false;
586+
*InstanceTypeRef = reinterpret_cast<swift_typeref_t>(std::get<const TypeRef *>(*Pair));
587+
*StartOfInstanceData = std::get<RemoteAddress>(*Pair).getAddressData();
588+
589+
return true;
590+
}
574591
int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef,
575592
swift_addr_t EnumAddress,
576593
swift_typeref_t EnumTypeRef,
@@ -761,4 +778,4 @@ const char *swift_reflection_iterateAsyncTaskAllocations(
761778
Call(AllocationPtr, Count, ConvertedChunks.data(), ContextPtr);
762779
});
763780
return returnableCString(ContextRef, Error);
764-
}
781+
}

stdlib/tools/swift-reflection-test/messages.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
static const char *REQUEST_INSTANCE_KIND = "k\n";
14+
static const char *REQUEST_SHOULD_UNWRAP_CLASS_EXISTENTIAL = "u\n";
1415
static const char *REQUEST_INSTANCE_ADDRESS = "i\n";
1516
static const char *REQUEST_REFLECTION_INFO = "r\n";
1617
static const char *REQUEST_IMAGES = "m\n";

0 commit comments

Comments
 (0)