Skip to content

Commit 3a09d63

Browse files
Merge pull request #37673 from augusto2112/project-existential-lldb
Implement projectExistentialAndUnwrapClass
2 parents d13c81d + e36e8ee commit 3a09d63

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)