Skip to content

Implement projectExistentialAndUnwrapClass #37673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 122 additions & 1 deletion include/swift/Reflection/ReflectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class ReflectionContext
using super = remote::MetadataReader<Runtime, TypeRefBuilder>;
using super::readMetadata;
using super::readObjCClassName;

using super::readResolvedPointerValue;
std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache;

/// All buffers we need to keep around long term. This will automatically free them
Expand Down Expand Up @@ -789,6 +789,52 @@ class ReflectionContext
}
}

llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
getDynamicTypeAndAddressClassExistential(RemoteAddress ExistentialAddress) {
auto PointerValue =
readResolvedPointerValue(ExistentialAddress.getAddressData());
if (!PointerValue)
return {};
auto Result = readMetadataFromInstance(*PointerValue);
if (!Result)
return {};
auto TypeResult = readTypeFromMetadata(Result.getValue());
if (!TypeResult)
return {};
return {{std::move(TypeResult), RemoteAddress(*PointerValue)}};
}

llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
getDynamicTypeAndAddressErrorExistential(RemoteAddress ExistentialAddress,
bool *IsBridgedError = nullptr) {
auto Result = readMetadataAndValueErrorExistential(ExistentialAddress);
if (!Result)
return {};

auto TypeResult =
readTypeFromMetadata(Result->MetadataAddress.getAddressData());
if (!TypeResult)
return {};

if (IsBridgedError)
*IsBridgedError = Result->IsBridgedError;

return {{TypeResult, Result->PayloadAddress}};
}

llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
getDynamicTypeAndAddressOpaqueExistential(RemoteAddress ExistentialAddress) {
auto Result = readMetadataAndValueOpaqueExistential(ExistentialAddress);
if (!Result)
return {};

auto TypeResult =
readTypeFromMetadata(Result->MetadataAddress.getAddressData());
if (!TypeResult)
return {};
return {{std::move(TypeResult), Result->PayloadAddress}};
}

bool projectExistential(RemoteAddress ExistentialAddress,
const TypeRef *ExistentialTR,
const TypeRef **OutInstanceTR,
Expand Down Expand Up @@ -850,6 +896,75 @@ class ReflectionContext
return false;
}
}
/// A version of `projectExistential` tailored for LLDB.
/// This version dereferences the resulting TypeRef if it wraps
/// a class type, it also dereferences the input `ExistentialAddress` before
/// attempting to find its dynamic type and address when dealing with error
/// existentials.
llvm::Optional<std::pair<const TypeRef *, RemoteAddress>>
projectExistentialAndUnwrapClass(RemoteAddress ExistentialAddress,
const TypeRef &ExistentialTR) {
auto IsClass = [](const TypeRef *TypeResult) {
// When the existential wraps a class type, LLDB expects that the
// address returned is the class instance itself and not the address
// of the reference.
bool IsClass = TypeResult->getKind() == TypeRefKind::ForeignClass ||
TypeResult->getKind() == TypeRefKind::ObjCClass;
if (auto *nominal = llvm::dyn_cast<NominalTypeRef>(TypeResult))
IsClass = nominal->isClass();
else if (auto *boundGeneric =
llvm::dyn_cast<BoundGenericTypeRef>(TypeResult))
IsClass = boundGeneric->isClass();
return IsClass;
};

auto DereferenceAndSet = [&](RemoteAddress &Address) {
auto PointerValue = readResolvedPointerValue(Address.getAddressData());
if (!PointerValue)
return false;
Address = RemoteAddress(*PointerValue);
return true;
};

auto ExistentialRecordTI = getRecordTypeInfo(&ExistentialTR, nullptr);
if (!ExistentialRecordTI)
return {};

switch (ExistentialRecordTI->getRecordKind()) {
case RecordKind::ClassExistential:
return getDynamicTypeAndAddressClassExistential(ExistentialAddress);
case RecordKind::ErrorExistential: {
// LLDB stores the address of the error pointer.
if (!DereferenceAndSet(ExistentialAddress))
return {};

bool IsBridgedError = false;
auto Pair = getDynamicTypeAndAddressErrorExistential(ExistentialAddress,
&IsBridgedError);
if (!Pair)
return {};

if (!IsBridgedError && IsClass(std::get<const TypeRef *>(*Pair)))
if (!DereferenceAndSet(std::get<RemoteAddress>(*Pair)))
return {};

return Pair;
}
case RecordKind::OpaqueExistential: {
auto Pair = getDynamicTypeAndAddressOpaqueExistential(ExistentialAddress);
if (!Pair)
return {};

if (IsClass(std::get<const TypeRef *>(*Pair)))
if (!DereferenceAndSet(std::get<RemoteAddress>(*Pair)))
return {};

return Pair;
}
default:
return {};
}
}

/// Projects the value of an enum.
///
Expand Down Expand Up @@ -889,6 +1004,12 @@ class ReflectionContext
}
}

const RecordTypeInfo *getRecordTypeInfo(const TypeRef *TR,
remote::TypeInfoProvider *ExternalTypeInfo) {
auto *TypeInfo = getTypeInfo(TR, ExternalTypeInfo);
return dyn_cast_or_null<const RecordTypeInfo>(TypeInfo);
}

/// Iterate the protocol conformance cache tree rooted at NodePtr, calling
/// Call with the type and protocol in each node.
void iterateConformanceTree(StoredPointer NodePtr,
Expand Down
13 changes: 13 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef,
swift_typeref_t *OutInstanceTypeRef,
swift_addr_t *OutStartOfInstanceData);

/// Like swift_reflection_projectExistential, with 2 differences:
///
/// - When dealing with an error existential, this version will dereference
/// the ExistentialAddress before proceeding.
/// - After setting OutInstanceTypeRef and OutStartOfInstanceData this version
/// may derefence and set OutStartOfInstanceData if OutInstanceTypeRef is a
/// class TypeRef.
SWIFT_REMOTE_MIRROR_LINKAGE
int swift_reflection_projectExistentialAndUnwrapClass(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used in swift-reflection-test.c so we can access it from the existentials.swift test.

SwiftReflectionContextRef ContextRef, swift_addr_t ExistentialAddress,
swift_typeref_t ExistentialTypeRef, swift_typeref_t *OutInstanceTypeRef,
swift_addr_t *OutStartOfInstanceData);

/// Projects the value of an enum.
///
/// Takes the address and typeref for an enum and determines the
Expand Down
32 changes: 29 additions & 3 deletions stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//===----------------------------------------------------------------------===//

let RequestInstanceKind = "k"
let RequestShouldUnwrapClassExistential = "u"
let RequestInstanceAddress = "i"
let RequestReflectionInfos = "r"
let RequestImages = "m"
Expand Down Expand Up @@ -346,11 +347,15 @@ internal func sendPointerSize() {
/// The parent sends a Done message to indicate that it's done
/// looking at this instance. It will continue to ask for instances,
/// so call doneReflecting() when you don't have any more instances.
internal func reflect(instanceAddress: UInt, kind: InstanceKind) {
internal func reflect(instanceAddress: UInt,
kind: InstanceKind,
shouldUnwrapClassExistential: Bool = false) {
while let command = readLine(strippingNewline: true) {
switch command {
case String(validatingUTF8: RequestInstanceKind)!:
sendValue(kind.rawValue)
case String(validatingUTF8: RequestShouldUnwrapClassExistential)!:
sendValue(shouldUnwrapClassExistential)
case String(validatingUTF8: RequestInstanceAddress)!:
sendValue(instanceAddress)
case String(validatingUTF8: RequestReflectionInfos)!:
Expand Down Expand Up @@ -437,12 +442,18 @@ public func reflect(object: AnyObject) {
/// The test doesn't care about the witness tables - we only care
/// about what's in the buffer, so we always put these values into
/// an Any existential.
public func reflect<T>(any: T, kind: InstanceKind = .Existential) {
///
/// If shouldUnwrapClassExistential is set to true, this exercises
/// projectExistentialAndUnwrapClass instead of projectExistential.
public func reflect<T>(any: T, kind: InstanceKind = .Existential,
shouldUnwrapClassExistential: Bool = false) {
let any: Any = any
let anyPointer = UnsafeMutablePointer<Any>.allocate(capacity: MemoryLayout<Any>.size)
anyPointer.initialize(to: any)
let anyPointerValue = UInt(bitPattern: anyPointer)
reflect(instanceAddress: anyPointerValue, kind: kind)
reflect(instanceAddress: anyPointerValue,
kind: kind,
shouldUnwrapClassExistential: shouldUnwrapClassExistential)
anyPointer.deallocate()
}

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

// Like reflect<T: Error>(error: T), but calls projectExistentialAndUnwrapClass
// instead of projectExistential and adds an extra level of indirection, which is
// what projectExistentialAndUnwrapClass expects.
public func reflectUnwrappingClassExistential<T: Error>(error: T) {
let error: Error = error
let errorPointerValue = unsafeBitCast(error, to: UInt.self)
let anyPointer = UnsafeMutablePointer<Any>.allocate(capacity: MemoryLayout<Any>.size)
anyPointer.initialize(to: errorPointerValue)
let anyPointerValue = UInt(bitPattern: anyPointer)
reflect(instanceAddress: anyPointerValue,
kind: .ErrorExistential,
shouldUnwrapClassExistential: true)
anyPointer.deallocate()
}

// Reflect an `Enum`
//
// These are handled like existentials, but
Expand Down
19 changes: 18 additions & 1 deletion stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,23 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef,
return Success;
}

int swift_reflection_projectExistentialAndUnwrapClass(SwiftReflectionContextRef ContextRef,
swift_addr_t ExistentialAddress,
swift_typeref_t ExistentialTypeRef,
swift_typeref_t *InstanceTypeRef,
swift_addr_t *StartOfInstanceData) {
auto Context = ContextRef->nativeContext;
auto ExistentialTR = reinterpret_cast<const TypeRef *>(ExistentialTypeRef);
auto RemoteExistentialAddress = RemoteAddress(ExistentialAddress);
auto Pair = Context->projectExistentialAndUnwrapClass(
RemoteExistentialAddress, *ExistentialTR);
if (!Pair.hasValue())
return false;
*InstanceTypeRef = reinterpret_cast<swift_typeref_t>(std::get<const TypeRef *>(*Pair));
*StartOfInstanceData = std::get<RemoteAddress>(*Pair).getAddressData();

return true;
}
int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef,
swift_addr_t EnumAddress,
swift_typeref_t EnumTypeRef,
Expand Down Expand Up @@ -761,4 +778,4 @@ const char *swift_reflection_iterateAsyncTaskAllocations(
Call(AllocationPtr, Count, ConvertedChunks.data(), ContextPtr);
});
return returnableCString(ContextRef, Error);
}
}
1 change: 1 addition & 0 deletions stdlib/tools/swift-reflection-test/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

static const char *REQUEST_INSTANCE_KIND = "k\n";
static const char *REQUEST_SHOULD_UNWRAP_CLASS_EXISTENTIAL = "u\n";
static const char *REQUEST_INSTANCE_ADDRESS = "i\n";
static const char *REQUEST_REFLECTION_INFO = "r\n";
static const char *REQUEST_IMAGES = "m\n";
Expand Down
Loading