Skip to content

[lldb] Add enum support to TypeSystemSwiftTypeRef::DumpTypeValue #2361

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
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
6 changes: 6 additions & 0 deletions lldb/include/lldb/Target/SwiftLanguageRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ class SwiftLanguageRuntime : public LanguageRuntime {
llvm::Optional<unsigned> GetNumChildren(CompilerType type,
ValueObject *valobj);

/// Determine the enum case name for the \p data value of the enum \p type.
/// This is performed using Swift reflection.
llvm::Optional<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx);

llvm::Optional<size_t> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes);
Expand Down
129 changes: 85 additions & 44 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,26 @@ static bool ContainsSugaredParen(swift::Demangle::NodePointer node) {

return false;
}


swift::Demangle::NodePointer
StripPrivateIDs(swift::Demangle::Demangler &dem,
swift::Demangle::NodePointer node) {
using namespace swift::Demangle;
return TypeSystemSwiftTypeRef::Transform(dem, node, [&](NodePointer node) {
if (node->getKind() != Node::Kind::PrivateDeclName ||
node->getNumChildren() != 2)
return node;

assert(node->getFirstChild()->getKind() == Node::Kind::Identifier);
assert(node->getLastChild()->getKind() == Node::Kind::Identifier);
auto *new_node = dem.createNode(Node::Kind::PrivateDeclName);
auto *ident = dem.createNodeWithAllocatedText(
Node::Kind::Identifier, node->getLastChild()->getText());
new_node->addChild(ident, dem);
return new_node;
});
}

/// Compare two swift types from different type systems by comparing their
/// (canonicalized) mangled name.
template <> bool Equivalent<CompilerType>(CompilerType l, CompilerType r) {
Expand All @@ -1443,10 +1462,10 @@ template <> bool Equivalent<CompilerType>(CompilerType l, CompilerType r) {
if (ContainsUnresolvedTypeAlias(r_node) ||
ContainsGenericTypeParameter(r_node) || ContainsSugaredParen(r_node))
return true;
if (swift::Demangle::mangleNode(
TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, l_node)) ==
swift::Demangle::mangleNode(
TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, r_node)))
if (swift::Demangle::mangleNode(StripPrivateIDs(
dem, TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, l_node))) ==
swift::Demangle::mangleNode(StripPrivateIDs(
dem, TypeSystemSwiftTypeRef::CanonicalizeSugar(dem, r_node))))
return true;

// SwiftASTContext hardcodes some less-precise types.
Expand Down Expand Up @@ -2293,6 +2312,8 @@ CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex(
child_bitfield_bit_offset, child_is_base_class,
child_is_deref_of_parent, valobj, language_flags);
};
auto ast_num_children = m_swift_ast_context->GetNumChildren(
ReconstructType(type), omit_empty_base_classes, exe_ctx);
auto impl = [&]() -> CompilerType {
ExecutionContextScope *exe_scope = nullptr;
if (exe_ctx)
Expand All @@ -2312,9 +2333,15 @@ CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex(
if (llvm::StringRef(AsMangledName(type))
.endswith("sSo18NSNotificationNameaD"))
return GetTypeFromMangledTypename(ConstString("$sSo8NSStringCD"));
// FIXME: Private discriminators come out in a different format.
if (result.GetMangledTypeName().GetStringRef().count('$') > 1)
return fallback();
if (result.GetMangledTypeName().GetStringRef().count('$') > 1 &&
ast_num_children == runtime->GetNumChildren({this, type}, valobj))
// If available, prefer the AST for private types. Private
// identifiers are not ABI; the runtime returns anonymous private
// identifiers (using a '$' prefix) which cannot match identifiers
// in the AST. Because these private types can't be used in an AST
// context, prefer the AST type if available.
if (auto ast_type = fallback())
return ast_type;
return result;
}
}
Expand Down Expand Up @@ -2413,8 +2440,7 @@ CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex(
// Because the API deals out an index into a list of children we
// can't mix&match between the two typesystems if there is such a
// divergence. We'll need to replace all calls at once.
if (m_swift_ast_context->GetNumChildren(ReconstructType(type),
omit_empty_base_classes, exe_ctx) <
if (ast_num_children <
runtime->GetNumChildren({this, type}, valobj).getValueOr(0))
return impl();

Expand All @@ -2433,9 +2459,9 @@ CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex(
ast_child_name = suffix.str();
assert((llvm::StringRef(child_name).contains('.') ||
Equivalent(child_name, ast_child_name)));
assert((Equivalent(llvm::Optional<uint64_t>(child_byte_size),
llvm::Optional<uint64_t>(ast_child_byte_size)) ||
ast_language_flags));
assert(ast_language_flags ||
(Equivalent(llvm::Optional<uint64_t>(child_byte_size),
llvm::Optional<uint64_t>(ast_child_byte_size))));
assert(Equivalent(llvm::Optional<uint64_t>(child_byte_offset),
llvm::Optional<uint64_t>(ast_child_byte_offset)));
assert(
Expand Down Expand Up @@ -2787,37 +2813,14 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue(
size_t data_byte_size, uint32_t bitfield_bit_size,
uint32_t bitfield_bit_offset, ExecutionContextScope *exe_scope,
bool is_base_class) {
if (!type)
return false;

using namespace swift::Demangle;
Demangler dem;
auto *node = DemangleCanonicalType(dem, type);
auto kind = node->getKind();

switch (kind) {
case Node::Kind::Structure: {
// TODO: Handle ObjC enums masquerading as structs.
// In rare instances, a Swift `Structure` wraps an ObjC enum. An example is
// `$sSo16ComparisonResultVD`. For now, use `SwiftASTContext` to handle
// these enum structs.
auto resolved = ResolveTypeAlias(m_swift_ast_context, dem, node, true);
auto clang_type = std::get<CompilerType>(resolved);
bool is_signed;
if (!clang_type.IsEnumerationType(is_signed))
break;
LLVM_FALLTHROUGH;
}
case Node::Kind::Enum:
case Node::Kind::BoundGenericEnum:
// TODO: Add support for Enums.
return m_swift_ast_context->DumpTypeValue(
ReconstructType(type), s, format, data, data_offset, data_byte_size,
bitfield_bit_size, bitfield_bit_offset, exe_scope, is_base_class);
}

auto impl = [&]() -> bool {
switch (kind) {
if (!type)
return false;

using namespace swift::Demangle;
Demangler dem;
auto *node = DemangleCanonicalType(dem, type);
switch (node->getKind()) {
case Node::Kind::Class:
case Node::Kind::BoundGenericClass:
if (is_base_class)
Expand Down Expand Up @@ -2877,9 +2880,47 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue(
s, format, data, data_offset, data_byte_size, bitfield_bit_size,
bitfield_bit_offset, exe_scope, is_base_class);
}
case Node::Kind::Structure:
case Node::Kind::BoundGenericStructure:
return false;
case Node::Kind::Structure: {
// In some instances, a swift `structure` wraps an objc enum. The enum
// case needs to be handled, but structs are no-ops.
auto resolved = ResolveTypeAlias(m_swift_ast_context, dem, node, true);
auto clang_type = std::get<CompilerType>(resolved);
if (!clang_type)
return false;

bool is_signed;
if (!clang_type.IsEnumerationType(is_signed))
// The type is a clang struct, not an enum.
return false;

// The type is an enum imported from clang. Try Swift type metadata first,
// and failing that fallback to the AST.
LLVM_FALLTHROUGH;
}
case Node::Kind::Enum:
case Node::Kind::BoundGenericEnum: {
if (exe_scope)
if (auto runtime =
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess())) {
ExecutionContext exe_ctx;
exe_scope->CalculateExecutionContext(exe_ctx);
if (auto case_name =
runtime->GetEnumCaseName({this, type}, data, &exe_ctx)) {
s->PutCString(*case_name);
return true;
}
}

// No result available from the runtime, fallback to the AST.
// This can happen in two cases:
// 1. MultiPayloadEnums not currently supported by Swift reflection
// 2. Some clang imported enums
return m_swift_ast_context->DumpTypeValue(
ReconstructType(type), s, format, data, data_offset, data_byte_size,
bitfield_bit_size, bitfield_bit_offset, exe_scope, is_base_class);
}
default:
assert(false && "Unhandled node kind");
LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES),
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/Target/SwiftLanguageRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ class SwiftLanguageRuntimeStub {
return {};
}

llvm::Optional<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx) {
STUB_LOG();
return {};
}

llvm::Optional<size_t> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
Expand Down Expand Up @@ -2168,6 +2175,11 @@ SwiftLanguageRuntime::GetNumChildren(CompilerType type, ValueObject *valobj) {
FORWARD(GetNumChildren, type, valobj);
}

llvm::Optional<std::string> SwiftLanguageRuntime::GetEnumCaseName(
CompilerType type, const DataExtractor &data, ExecutionContext *exe_ctx) {
FORWARD(GetEnumCaseName, type, data, exe_ctx);
}

llvm::Optional<size_t> SwiftLanguageRuntime::GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
Expand Down
58 changes: 53 additions & 5 deletions lldb/source/Target/SwiftLanguageRuntimeDynamicTypeResolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ class LLDBMemoryReader : public swift::remote::MemoryReader {
} else {
*result = 0;
}
break;
return true;
}
case DLQ_GetPointerSize: {
auto result = static_cast<uint8_t *>(outBuffer);
Expand All @@ -304,6 +304,15 @@ class LLDBMemoryReader : public swift::remote::MemoryReader {
*result = m_process.GetAddressByteSize(); // FIXME: sizeof(size_t)
return true;
}
case DLQ_GetLeastValidPointerValue: {
auto *result = (uint64_t *)outBuffer;
auto &triple = m_process.GetTarget().GetArchitecture().GetTriple();
if (triple.isOSDarwin() && triple.isArch64Bit())
*result = 0x100000000;
else
*result = 0x1000;
return true;
}
}

return false;
Expand Down Expand Up @@ -381,8 +390,8 @@ class LLDBMemoryReader : public swift::remote::MemoryReader {
uint64_t size) override {
if (m_local_buffer) {
auto addr = address.getAddressData();
if (addr >= m_local_buffer &&
addr + size <= m_local_buffer + m_local_buffer_size) {
if (addr >= *m_local_buffer &&
addr + size <= *m_local_buffer + m_local_buffer_size) {
// If this crashes, the assumptions stated in
// GetDynamicTypeAndAddress_Protocol() most likely no longer
// hold.
Expand Down Expand Up @@ -472,15 +481,15 @@ class LLDBMemoryReader : public swift::remote::MemoryReader {

void popLocalBuffer() {
lldbassert(m_local_buffer);
m_local_buffer = 0;
m_local_buffer.reset();
m_local_buffer_size = 0;
}

private:
Process &m_process;
size_t m_max_read_amount;

uint64_t m_local_buffer = 0;
llvm::Optional<uint64_t> m_local_buffer;
uint64_t m_local_buffer_size = 0;
};

Expand Down Expand Up @@ -1217,6 +1226,45 @@ findFieldWithName(const std::vector<swift::reflection::FieldInfo> &fields,
return child_indexes.size();
}

static llvm::Optional<std::string>
GetMultiPayloadEnumCaseName(const swift::reflection::EnumTypeInfo *eti,
const DataExtractor &data) {
assert(eti->getEnumKind() == swift::reflection::EnumKind::MultiPayloadEnum);
auto payload_capacity = eti->getPayloadSize();
if (data.GetByteSize() == payload_capacity + 1) {
auto tag = data.GetDataStart()[payload_capacity];
const auto &cases = eti->getCases();
if (tag >= 0 && tag < cases.size())
return cases[tag].Name;
}
return {};
}

llvm::Optional<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(
CompilerType type, const DataExtractor &data, ExecutionContext *exe_ctx) {
using namespace swift::reflection;
using namespace swift::remote;
auto *ti = GetTypeInfo(type, exe_ctx->GetFramePtr());
if (ti->getKind() != TypeInfoKind::Enum)
return {};

auto *eti = llvm::cast<EnumTypeInfo>(ti);
PushLocalBuffer((int64_t)data.GetDataStart(), data.GetByteSize());
auto defer = llvm::make_scope_exit([&] { PopLocalBuffer(); });
RemoteAddress addr(data.GetDataStart());
int case_index;
if (eti->projectEnumValue(*GetMemoryReader(), addr, &case_index))
return eti->getCases()[case_index].Name;

// Temporary workaround.
if (eti->getEnumKind() == EnumKind::MultiPayloadEnum &&
type.GetMangledTypeName().GetStringRef().startswith(
"$s10Foundation9IndexPathV7Storage10"))
return GetMultiPayloadEnumCaseName(eti, data);

return {};
}

llvm::Optional<size_t> SwiftLanguageRuntimeImpl::GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Target/SwiftLanguageRuntimeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class SwiftLanguageRuntimeImpl {
llvm::Optional<unsigned> GetNumFields(CompilerType type,
ExecutionContext *exe_ctx);

llvm::Optional<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx);

llvm::Optional<size_t> GetIndexOfChildMemberWithName(
CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx,
bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
from lldbsuite.test.decorators import *

lldbinline.MakeInlineTest(__file__, globals(),
decorators=[swiftTest,skipIf(oslist=['windows'])])
decorators=[swiftTest,skipIf(oslist=['windows', 'linux'])])