Skip to content

[lldb] Replace usage of remote AST with reflection on GetDynamicTypeAndAddress_Protocol. #3024

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
Original file line number Diff line number Diff line change
Expand Up @@ -1808,18 +1808,128 @@ bool SwiftLanguageRuntimeImpl::IsValidErrorValue(ValueObject &in_value) {

return true;
}
/// Digs into the protocols inside a protocol composition and checks if
/// any of them belong to the LLDB module.
static bool
IsDeclaredInLLDBExpr(const swift::reflection::TypeRef *typeref) {
using swift::Demangle::Node;

const auto *protocol_typeref = llvm::dyn_cast_or_null<
const swift::reflection::ProtocolCompositionTypeRef>(typeref);

if (!protocol_typeref)
return false;

swift::Demangle::Demangler dem;

NodePointer type = protocol_typeref->getDemangling(dem);

Choose a reason for hiding this comment

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

For a followup-commit: We should factor this out into a static helper and unit-test it.

Copy link
Author

Choose a reason for hiding this comment

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

You mean the whole method?

Choose a reason for hiding this comment

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

The remainder of the method that operates on the demangle tree. It doesn't need a live process or reflection metadata and can be easily unit-tested, so we should do that :-)

Copy link
Author

Choose a reason for hiding this comment

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

Aah ok!

if (!type || type->getKind() != Node::Kind::Type ||
type->getNumChildren() == 0)
return false;

NodePointer maybe_protocol_list = type->getFirstChild();
NodePointer protocol_list;

if (!maybe_protocol_list)
return false;

switch (maybe_protocol_list->getKind()) {
case Node::Kind::ProtocolList:
protocol_list = maybe_protocol_list;
break;
case Node::Kind::ProtocolListWithAnyObject:
case Node::Kind::ProtocolListWithClass:
if (maybe_protocol_list->getNumChildren() == 0)
return false;
protocol_list = maybe_protocol_list->getFirstChild();
break;
default:
return false;
}

if (!protocol_list || protocol_list->getKind() != Node::Kind::ProtocolList ||
protocol_list->getNumChildren() == 0)
return false;

NodePointer type_list = protocol_list->getFirstChild();
if (!type_list || type_list->getKind() != Node::Kind::TypeList)
return false;

for (NodePointer internal_type : *type_list) {
if (!internal_type || internal_type->getKind() != Node::Kind::Type ||
internal_type->getNumChildren() == 0)
continue;

NodePointer protocol = internal_type->getFirstChild();
if (!protocol || protocol->getKind() != Node::Kind::Protocol ||
protocol->getNumChildren() == 0)
continue;

NodePointer module = protocol->getFirstChild();
if (!module || module->getKind() != Node::Kind::Module ||
!module->hasText())
continue;

if (module->getText().startswith("__lldb_expr_"))
return true;
}
return false;
}

bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_Protocol(
ValueObject &in_value, CompilerType protocol_type,
SwiftASTContextForExpressions &scratch_ctx,
lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name,
Address &address) {
auto remote_ast_impl = [&](bool use_local_buffer,
lldb::addr_t existential_address)
-> llvm::Optional<std::pair<CompilerType, Address>> {
swift::remote::RemoteAddress remote_existential(existential_address);
auto &remote_ast = GetRemoteASTContext(scratch_ctx);
auto swift_type = GetSwiftType(protocol_type);
if (!swift_type)
return {};
if (use_local_buffer)
PushLocalBuffer(existential_address,
in_value.GetByteSize().getValueOr(0));

auto result = remote_ast.getDynamicTypeAndAddressForExistential(
remote_existential, swift_type);
if (use_local_buffer)
PopLocalBuffer();

if (!result.isSuccess())
return {};

auto type_and_address = result.getValue();

CompilerType type = ToCompilerType(type_and_address.InstanceType);
Address address;
address.SetRawAddress(type_and_address.PayloadAddress.getAddressData());
return {{type, address}};
};

Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));

auto &target = m_process.GetTarget();
assert(IsScratchContextLocked(target) &&
"Swift scratch context not locked ahead");
auto &remote_ast = GetRemoteASTContext(scratch_ctx);

auto *tss =
llvm::dyn_cast_or_null<TypeSystemSwift>(protocol_type.GetTypeSystem());
if (!tss) {
if (log)
log->Printf("Could not get type system swift");
return false;
}

const swift::reflection::TypeRef *protocol_typeref =
GetTypeRef(protocol_type, tss->GetSwiftASTContext());
if (!protocol_typeref) {
if (log)
log->Printf("Could not get protocol typeref");
return false;
}

lldb::addr_t existential_address;
bool use_local_buffer = false;
Expand All @@ -1844,29 +1954,65 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_Protocol(
if (log)
log->Printf("existential address is 0x%llx", existential_address);

if (!existential_address || existential_address == LLDB_INVALID_ADDRESS)
if (!existential_address || existential_address == LLDB_INVALID_ADDRESS) {
if (log)
log->Printf("Existential address is invalid");
return false;
}

// If the protocol list contains any types declared in LLDB,
// we use the remote AST implementation since we currently
// don't register the metadata generated by LLDB.
if (IsDeclaredInLLDBExpr(protocol_typeref)) {
auto pair = remote_ast_impl(use_local_buffer, existential_address);
if (!pair)
return false;
class_type_or_name.SetCompilerType(std::get<CompilerType>(*pair));
address = std::get<Address>(*pair);
return true;
}

if (use_local_buffer)
PushLocalBuffer(existential_address, in_value.GetByteSize().getValueOr(0));

swift::remote::RemoteAddress remote_existential(existential_address);
auto result = remote_ast.getDynamicTypeAndAddressForExistential(
remote_existential, GetSwiftType(protocol_type));

auto *reflection_ctx = GetReflectionContext();
auto pair = reflection_ctx->projectExistentialAndUnwrapClass(
remote_existential, *protocol_typeref);
if (use_local_buffer)
PopLocalBuffer();

if (!result.isSuccess()) {
if (!pair) {
if (log)
log->Printf("RemoteAST failed to get dynamic type of existential");
log->Printf("Runtime failed to get dynamic type of existential");
return false;
}

auto type_and_address = result.getValue();
class_type_or_name.SetCompilerType(
ToCompilerType(type_and_address.InstanceType));
address.SetRawAddress(type_and_address.PayloadAddress.getAddressData());
const swift::reflection::TypeRef *typeref;
swift::remote::RemoteAddress out_address(nullptr);
std::tie(typeref, out_address) = *pair;
auto &ts = tss->GetTypeSystemSwiftTypeRef();
swift::Demangle::Demangler dem;
swift::Demangle::NodePointer node = typeref->getDemangling(dem);
class_type_or_name.SetCompilerType(ts.RemangleAsType(dem, node));
address.SetRawAddress(out_address.getAddressData());

#ifndef NDEBUG
auto reference_pair = remote_ast_impl(use_local_buffer, existential_address);
assert(pair.hasValue() >= reference_pair.hasValue() &&
"RemoteAST and runtime diverge");

if (reference_pair) {
CompilerType ref_type = std::get<CompilerType>(*reference_pair);
Address ref_address = std::get<Address>(*reference_pair);
ConstString a = class_type_or_name.GetCompilerType().GetMangledTypeName();
ConstString b = ref_type.GetMangledTypeName();
if (a != b)
llvm::dbgs() << "RemoteAST and runtime diverge " << a << " != " << b
<< "\n";
}
#endif
return true;
}

Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ class SwiftASTContext : public TypeSystemSwift {
Target *target = nullptr);

SwiftASTContext(const SwiftASTContext &rhs) = delete;

TypeSystemSwiftTypeRef &GetTypeSystemSwiftTypeRef() override {
return m_typeref_typesystem;
}

~SwiftASTContext();

Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Decl;
namespace lldb_private {
class TypeSystemClang;
class SwiftASTContext;

class TypeSystemSwiftTypeRef;
/// The implementation of lldb::Type's m_payload field for TypeSystemSwift.
class TypePayloadSwift {
/// Layout: bit 1 ... IsFixedValueBuffer.
Expand Down Expand Up @@ -109,6 +109,7 @@ class TypeSystemSwift : public TypeSystem {

static LanguageSet GetSupportedLanguagesForTypes();
virtual SwiftASTContext *GetSwiftASTContext() = 0;
virtual TypeSystemSwiftTypeRef &GetTypeSystemSwiftTypeRef() = 0;
virtual Module *GetModule() const = 0;
virtual lldb::TypeSP GetCachedType(ConstString mangled) = 0;
virtual void SetCachedType(ConstString mangled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift {

TypeSystemSwiftTypeRef(SwiftASTContext *swift_ast_context);
SwiftASTContext *GetSwiftASTContext() override { return m_swift_ast_context; }
TypeSystemSwiftTypeRef &GetTypeSystemSwiftTypeRef() override { return *this; }

Module *GetModule() const override;
swift::CanType GetCanonicalSwiftType(CompilerType compiler_type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ def test(self):
self.expect("fr v -d run-target -- input",
substrs=['(Dylib.LibraryProtocol) input'])
self.expect("expr -d run-target -- input",
substrs=['(Dylib.LibraryProtocol)'])
substrs=['(a.FromMainModule)'])
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def test_private_import(self):
lldbutil.run_to_source_breakpoint(
self, 'break here', lldb.SBFileSpec('main.swift'),
extra_images=['Library'])
# We should not be able to resolve the types.
self.expect("fr var -d run -- x", substrs=["(Any)"])
self.expect("fr var -d run -- x", substrs=["(Invisible.InvisibleStruct)"])
# FIXME: This crashes LLDB with a Swift DESERIALIZATION FAILURE.
# self.expect("fr var -d run -- y", substrs=["(Any)"])