Skip to content

[lldb] Resolve typealias information for assiciated types via reflection #9768

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 2 commits into from
Dec 19, 2024
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
45 changes: 31 additions & 14 deletions lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct DescriptorFinderForwarder : public swift::reflection::DescriptorFinder {
/// An implementation of the generic ReflectionContextInterface that
/// is templatized on target pointer width and specialized to either
/// 32-bit or 64-bit pointers, with and without ObjC interoperability.
template <typename ReflectionContext>
template <typename ReflectionContext, bool ObjCEnabled, unsigned PointerSize>
class TargetReflectionContext : public ReflectionContextInterface {
DescriptorFinderForwarder m_forwader;
ReflectionContext m_reflection_ctx;
Expand Down Expand Up @@ -313,7 +313,7 @@ class TargetReflectionContext : public ReflectionContextInterface {
}

std::optional<std::pair<const swift::reflection::TypeRef *,
swift::reflection::RemoteAddress>>
swift::reflection::RemoteAddress>>
ProjectExistentialAndUnwrapClass(
swift::reflection::RemoteAddress existential_address,
const swift::reflection::TypeRef &existential_tr,
Expand All @@ -323,6 +323,19 @@ class TargetReflectionContext : public ReflectionContextInterface {
existential_address, existential_tr);
}

const swift::reflection::TypeRef *
LookupTypeWitness(const std::string &MangledTypeName,
const std::string &Member, StringRef Protocol) override {
return m_type_converter.getBuilder().lookupTypeWitness(MangledTypeName,
Member, Protocol);
}
swift::reflection::ConformanceCollectionResult GetAllConformances() override {
swift::reflection::TypeRefBuilder &b = m_type_converter.getBuilder();
if (ObjCEnabled)
return b.collectAllConformances<swift::WithObjCInterop, PointerSize>();
return b.collectAllConformances<swift::NoObjCInterop, PointerSize>();
}

const swift::reflection::TypeRef *
ReadTypeFromMetadata(lldb::addr_t metadata_address,
swift::reflection::DescriptorFinder *descriptor_finder,
Expand Down Expand Up @@ -420,18 +433,22 @@ std::unique_ptr<ReflectionContextInterface>
ReflectionContextInterface::CreateReflectionContext(
uint8_t ptr_size, std::shared_ptr<swift::remote::MemoryReader> reader,
bool ObjCInterop, SwiftMetadataCache *swift_metadata_cache) {
using ReflectionContext32ObjCInterop =
TargetReflectionContext<swift::reflection::ReflectionContext<
swift::External<swift::WithObjCInterop<swift::RuntimeTarget<4>>>>>;
using ReflectionContext32NoObjCInterop =
TargetReflectionContext<swift::reflection::ReflectionContext<
swift::External<swift::NoObjCInterop<swift::RuntimeTarget<4>>>>>;
using ReflectionContext64ObjCInterop =
TargetReflectionContext<swift::reflection::ReflectionContext<
swift::External<swift::WithObjCInterop<swift::RuntimeTarget<8>>>>>;
using ReflectionContext64NoObjCInterop =
TargetReflectionContext<swift::reflection::ReflectionContext<
swift::External<swift::NoObjCInterop<swift::RuntimeTarget<8>>>>>;
using ReflectionContext32ObjCInterop = TargetReflectionContext<
swift::reflection::ReflectionContext<
swift::External<swift::WithObjCInterop<swift::RuntimeTarget<4>>>>,
true, 4>;
using ReflectionContext32NoObjCInterop = TargetReflectionContext<
swift::reflection::ReflectionContext<
swift::External<swift::NoObjCInterop<swift::RuntimeTarget<4>>>>,
false, 4>;
using ReflectionContext64ObjCInterop = TargetReflectionContext<
swift::reflection::ReflectionContext<
swift::External<swift::WithObjCInterop<swift::RuntimeTarget<8>>>>,
true, 8>;
using ReflectionContext64NoObjCInterop = TargetReflectionContext<
swift::reflection::ReflectionContext<
swift::External<swift::NoObjCInterop<swift::RuntimeTarget<8>>>>,
false, 8>;
if (ptr_size == 4) {
if (ObjCInterop)
return std::make_unique<ReflectionContext32ObjCInterop>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ class ReflectionContextInterface {
const swift::reflection::TypeRef *enum_type_ref,
swift::remote::TypeInfoProvider *provider,
swift::reflection::DescriptorFinder *descriptor_finder) = 0;
virtual const swift::reflection::TypeRef *
LookupTypeWitness(const std::string &MangledTypeName,
const std::string &Member, StringRef Protocol) = 0;
virtual swift::reflection::ConformanceCollectionResult
GetAllConformances() = 0;
virtual const swift::reflection::TypeRef *ReadTypeFromMetadata(
lldb::addr_t metadata_address,
swift::reflection::DescriptorFinder *descriptor_finder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.h"
#include "Plugins/Process/Utility/RegisterContext_x86.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "Plugins/TypeSystem/Swift/SwiftDemangle.h"
#include "Utility/ARM64_DWARF_Registers.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
Expand Down Expand Up @@ -282,6 +283,11 @@ class SwiftLanguageRuntimeStub {
return false;
}

CompilerType ResolveTypeAlias(CompilerType alias) {
STUB_LOG();
return {};
}

void DumpTyperef(CompilerType type, TypeSystemSwiftTypeRef *module_holder,
Stream *s) {
STUB_LOG();
Expand Down Expand Up @@ -523,6 +529,36 @@ SwiftLanguageRuntimeImpl::GetSwiftMetadataCache() {
return &m_swift_metadata_cache;
}

std::vector<std::string>
SwiftLanguageRuntimeImpl::GetConformances(llvm::StringRef mangled_name) {
if (m_conformances.empty()) {
using namespace swift::Demangle;
Demangler dem;

ThreadSafeReflectionContext reflection_ctx = GetReflectionContext();
if (!reflection_ctx)
return {};

Progress progress("Parsing Swift conformances");
swift::reflection::ConformanceCollectionResult conformances =
reflection_ctx->GetAllConformances();

for (auto &conformance : conformances.Conformances) {
auto [mod, proto] = StringRef(conformance.ProtocolName).split('.');
NodePointer n =
swift_demangle::CreateNominal(dem, Node::Kind::Protocol, mod, proto);
auto mangling = mangleNode(n);
if (!mangling.isSuccess())
return {};
llvm::StringRef protocol =
swift::Demangle::dropSwiftManglingPrefix(mangling.result());

m_conformances[mangled_name].push_back(protocol.str());
}
}
return m_conformances.lookup(mangled_name);
}

void SwiftLanguageRuntimeImpl::SetupReflection() {
LLDB_SCOPED_TIMER();

Expand Down Expand Up @@ -943,6 +979,9 @@ void SwiftLanguageRuntimeImpl::ModulesDidLoad(const ModuleList &module_list) {
// The modules will be lazily processed on the next call to
// GetReflectionContext.
m_modules_to_add.AppendIfNeeded(module_list);

// This could be done more efficiently with a better reflection API.
m_conformances.clear();
}

std::string
Expand Down Expand Up @@ -2399,6 +2438,11 @@ bool SwiftLanguageRuntime::IsStoredInlineInBuffer(CompilerType type) {
FORWARD(IsStoredInlineInBuffer, type);
}

llvm::Expected<CompilerType>
SwiftLanguageRuntime::ResolveTypeAlias(CompilerType alias) {
FORWARD(ResolveTypeAlias, alias);
}

std::optional<uint64_t> SwiftLanguageRuntime::GetMemberVariableOffset(
CompilerType instance_type, ValueObject *instance,
llvm::StringRef member_name, Status *error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ class SwiftLanguageRuntime : public LanguageRuntime {

bool IsStoredInlineInBuffer(CompilerType type) override;

/// Check if this type alias is listed in any witness tables and resolve it.
llvm::Expected<CompilerType> ResolveTypeAlias(CompilerType alias);

/// Retrieve the offset of the named member variable within an instance
/// of the given type.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ class LLDBTypeInfoProvider : public swift::remote::TypeInfoProvider {
auto *node = dem.demangleSymbol(wrapped);
if (!node) {
// Try `mangledName` as plain ObjC class name. Ex: NSObject, NSView, etc.
auto maybeMangled = swift_demangle::mangleClass(
auto maybeMangled = swift_demangle::MangleClass(
dem, swift::MANGLING_MODULE_OBJC, mangledName);
if (!maybeMangled.isSuccess()) {
LLDB_LOG(GetLog(LLDBLog::Types),
Expand Down Expand Up @@ -3034,6 +3034,117 @@ bool SwiftLanguageRuntimeImpl::IsStoredInlineInBuffer(CompilerType type) {
return true;
}

llvm::Expected<CompilerType>
SwiftLanguageRuntimeImpl::ResolveTypeAlias(CompilerType alias) {
using namespace swift::Demangle;
Demangler dem;

auto tss = alias.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>();
if (!tss)
return llvm::createStringError("not a Swift type");
auto tr_ts = tss->GetTypeSystemSwiftTypeRef();
if (!tr_ts)
return llvm::createStringError("could not get typesystem");

// Extract the mangling of the alias type's parent type and its
// generic substitutions if any.
auto mangled = alias.GetMangledTypeName().GetStringRef();
NodePointer type = swift_demangle::GetDemangledType(dem, mangled);
if (!type || type->getKind() != Node::Kind::TypeAlias ||
type->getNumChildren() != 2)
return llvm::createStringError("not a type alias");

NodePointer alias_node = type->getChild(1);
if (!alias_node || alias_node->getKind() != Node::Kind::Identifier ||
!alias_node->hasText())
return llvm::createStringError("type alias has no name");
std::string member = alias_node->getText().str();

NodePointer parent_node = type->getChild(0);
if (!parent_node)
return llvm::createStringError("top-level type alias");
NodePointer subst_node = nullptr;

switch (parent_node->getKind()) {
case Node::Kind::Class:
case Node::Kind::Structure:
case Node::Kind::Enum:
break;
// For the lookup, get the unbound type.
case Node::Kind::BoundGenericClass:
subst_node =
swift_demangle::ChildAtPath(parent_node, {Node::Kind::TypeList});
parent_node = swift_demangle::ChildAtPath(
parent_node, {Node::Kind::Type, Node::Kind::Class});
break;
case Node::Kind::BoundGenericStructure:
subst_node =
swift_demangle::ChildAtPath(parent_node, {Node::Kind::TypeList});
parent_node = swift_demangle::ChildAtPath(
parent_node, {Node::Kind::Type, Node::Kind::Structure});
break;
case Node::Kind::BoundGenericEnum:
subst_node =
swift_demangle::ChildAtPath(parent_node, {Node::Kind::TypeList});
parent_node = swift_demangle::ChildAtPath(
parent_node, {Node::Kind::Type, Node::Kind::Enum});
break;
default:
return llvm::createStringError("unsupported parent kind");
}
if (!parent_node)
return llvm::createStringError("unsupported generic parent kind");

NodePointer global = dem.createNode(Node::Kind::Global);
global->addChild(parent_node, dem);
auto mangling = swift::Demangle::mangleNode(global);
if (!mangling.isSuccess())
return llvm::createStringError("mangling error");
std::string in_type =
swift::Demangle::dropSwiftManglingPrefix(mangling.result()).str();

// `in_type` now holds the type alias' parent type.
// `member` is the name of the type alias.
// Scan through the witness tables of all of the parent's conformances.
ThreadSafeReflectionContext reflection_ctx = GetReflectionContext();
if (!reflection_ctx)
return llvm::createStringError("no reflection context");
for (const std::string &protocol : GetConformances(in_type)) {
auto *type_ref =
reflection_ctx->LookupTypeWitness(in_type, member, protocol);
if (!type_ref)
continue;

// Success, we found an associated type in the conformance. If
// the parent type was generic, the type alias could point to a
// type parameter. Reapply any substitutions from the parent type.
if (subst_node) {
swift::reflection::GenericArgumentMap substitutions;
unsigned idx = 0;
for (auto &child : *subst_node) {
auto mangling = swift_demangle::GetMangledName(dem, child);
if (!mangling.isSuccess())
continue;
const auto *type_ref = reflection_ctx->GetTypeRefOrNull(
mangling.result(), tr_ts->GetDescriptorFinder());
if (!type_ref)
continue;

substitutions.insert({{0, idx++}, type_ref});
}
type_ref = reflection_ctx->ApplySubstitutions(
type_ref, substitutions, tr_ts->GetDescriptorFinder());
}

CompilerType resolved = GetTypeFromTypeRef(*tr_ts, type_ref);
LLDB_LOG(GetLog(LLDBLog::Types),
"Resolved type alias {0} = {1} using reflection metadata.",
alias.GetMangledTypeName(), resolved.GetMangledTypeName());
return resolved;
}
return llvm::createStringError("cannot resolve type alias via reflection");
}

std::optional<uint64_t>
SwiftLanguageRuntimeImpl::GetBitSize(CompilerType type,
ExecutionContextScope *exe_scope) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class SwiftLanguageRuntimeImpl {
llvm::ArrayRef<swift::reflection::FieldInfo> fields);

bool IsStoredInlineInBuffer(CompilerType type);
llvm::Expected<CompilerType> ResolveTypeAlias(CompilerType alias);

/// Ask Remote Mirrors for the size of a Swift type.
std::optional<uint64_t> GetBitSize(CompilerType type,
Expand Down Expand Up @@ -357,6 +358,8 @@ class SwiftLanguageRuntimeImpl {

CompilerType m_box_metadata_type;

llvm::StringMap<std::vector<std::string>> m_conformances;

private:
/// Don't call these directly.
/// \{
Expand Down Expand Up @@ -385,6 +388,9 @@ class SwiftLanguageRuntimeImpl {

SwiftMetadataCache *GetSwiftMetadataCache();

/// Find all conformances for a nominal type in the reflection metadata.
std::vector<std::string> GetConformances(llvm::StringRef mangled_name);

/// These members are used to track and toggle the state of the "dynamic
/// exclusivity enforcement flag" in the swift runtime. This flag is set to
/// true when an LLDB expression starts running, and reset to its original
Expand Down
54 changes: 36 additions & 18 deletions lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,30 +81,48 @@ GetDemangledType(swift::Demangle::Demangler &dem, llvm::StringRef name) {
}

/// Wrap node in Global/TypeMangling/Type.
static swift::Demangle::NodePointer
mangleType(swift::Demangle::Demangler &dem,
swift::Demangle::NodePointer typeNode) {
inline swift::Demangle::NodePointer
MangleType(swift::Demangle::Demangler &dem,
swift::Demangle::NodePointer type_node) {
auto *global = dem.createNode(Node::Kind::Global);
auto *typeMangling = dem.createNode(Node::Kind::TypeMangling);
global->addChild(typeMangling, dem);
auto *type_mangling = dem.createNode(Node::Kind::TypeMangling);
global->addChild(type_mangling, dem);
auto *type = dem.createNode(Node::Kind::Type);
typeMangling->addChild(type, dem);
type->addChild(typeNode, dem);
type_mangling->addChild(type, dem);
type->addChild(type_node, dem);
return global;
}

/// Produce a type mangle tree for a nominal type.
inline swift::Demangle::NodePointer
CreateNominal(swift::Demangle::Demangler &dem, swift::Demangle::Node::Kind kind,
llvm::StringRef module_name, llvm::StringRef type_name) {
auto *nominal = dem.createNode(kind);
auto *m = dem.createNodeWithAllocatedText(Node::Kind::Module, module_name);
auto *id = dem.createNodeWithAllocatedText(Node::Kind::Identifier, type_name);
nominal->addChild(m, dem);
nominal->addChild(id, dem);
return nominal;
}

/// Produce a type mangling for a class.
inline ManglingErrorOr<std::string> mangleClass(swift::Demangle::Demangler &dem,
llvm::StringRef moduleName,
llvm::StringRef className) {
auto *classNode = dem.createNode(Node::Kind::Class);
auto *module =
dem.createNodeWithAllocatedText(Node::Kind::Module, moduleName);
auto *identifier =
dem.createNodeWithAllocatedText(Node::Kind::Identifier, className);
classNode->addChild(module, dem);
classNode->addChild(identifier, dem);
return mangleNode(mangleType(dem, classNode));
inline ManglingErrorOr<std::string> MangleClass(swift::Demangle::Demangler &dem,
llvm::StringRef module_name,
llvm::StringRef class_name) {
auto *node = CreateNominal(dem, Node::Kind::Class, module_name, class_name);
return mangleNode(MangleType(dem, node));
}

/// Create a mangled name for a type node.
inline swift::Demangle::ManglingErrorOr<std::string>
GetMangledName(swift::Demangle::Demangler &dem,
swift::Demangle::NodePointer node) {
using namespace swift::Demangle;
auto global = dem.createNode(Node::Kind::Global);
auto type_mangling = dem.createNode(Node::Kind::TypeMangling);
global->addChild(type_mangling, dem);
type_mangling->addChild(node, dem);
return mangleNode(global);
}

} // namespace swift_demangle
Expand Down
Loading