Skip to content

[lldb] Use reflection to get the dynamic type of a class #3204

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
23 changes: 11 additions & 12 deletions lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,18 +471,17 @@ bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager,
}
}

if (m_options.GetGenerateDebugInfo()) {
StreamString jit_module_name;
jit_module_name.Printf("%s%u", FunctionName(),
m_options.GetExpressionNumber());
auto module = m_execution_unit_sp->CreateJITModule(jit_module_name.GetString().data());

auto *swift_runtime = SwiftLanguageRuntime::Get(process);
if (module && swift_runtime) {
ModuleList modules;
modules.Append(module, false);
swift_runtime->ModulesDidLoad(modules);
}
StreamString jit_module_name;
jit_module_name.Printf("%s%u", FunctionName(),
m_options.GetExpressionNumber());
auto module =
m_execution_unit_sp->CreateJITModule(jit_module_name.GetString().data());

auto *swift_runtime = SwiftLanguageRuntime::Get(process);
if (module && swift_runtime) {
ModuleList modules;
modules.Append(module, false);
swift_runtime->ModulesDidLoad(modules);
}

if (jit_error.Success()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1604,23 +1604,6 @@ bool SwiftLanguageRuntimeImpl::ForEachSuperClassType(
return false;

lldb::addr_t pointer = instance.GetPointerValue();
// Maybe this belongs into GetPointerValue, but on the other hand it
// is also nice to not hide the existence of reference storage
// types. Perhaps they should even be modelled in the ValueObject
// hierarchy. This also partially papers over the fact that
// libReflection cannot tell up how many bits to strip from
// multi-payload enum values.
auto addr_deref =
FixupPointerValue(pointer, instance_type);
pointer = addr_deref.first;
if (addr_deref.second) {
// This is a reference storage object.
if (!reflection_ctx->getReader().readInteger(
swift::reflection::RemoteAddress(addr_deref.first),
&pointer))
return false;
}

auto md_ptr = reflection_ctx->readMetadataFromInstance(pointer);
if (!md_ptr)
return false;
Expand Down Expand Up @@ -1699,6 +1682,21 @@ static bool IsScratchContextLocked(TargetSP target) {
return target ? IsScratchContextLocked(*target) : true;
}

static bool IsPrivateNSClass(NodePointer node) {
if (!node || node->getKind() != Node::Kind::Type ||
node->getNumChildren() == 0)
return false;
NodePointer classNode = node->getFirstChild();
if (!classNode || classNode->getKind() != Node::Kind::Class ||
classNode->getNumChildren() < 2)
return false;
for (NodePointer child : *classNode)
if (child->getKind() == Node::Kind::Identifier && child->hasText())
return child->getText().startswith("__NS") ||
child->getText().startswith("NSTaggedPointer");
return false;
}

bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_Class(
ValueObject &in_value, SwiftASTContextForExpressions &scratch_ctx,
lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name,
Expand All @@ -1707,37 +1705,86 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_Class(
lldb::addr_t class_metadata_ptr = in_value.GetPointerValue(&address_type);
if (class_metadata_ptr == LLDB_INVALID_ADDRESS || class_metadata_ptr == 0)
return false;
address.SetRawAddress(class_metadata_ptr);

CompilerType static_type = in_value.GetCompilerType();
auto *tss =
llvm::dyn_cast_or_null<TypeSystemSwift>(static_type.GetTypeSystem());
if (!tss)
return false;
address.SetRawAddress(class_metadata_ptr);
auto &ts = tss->GetTypeSystemSwiftTypeRef();
// Ask the Objective-C runtime about Objective-C types.
if (tss->IsImportedType(static_type.GetOpaqueQualType(), nullptr))
if (auto *objc_runtime = SwiftLanguageRuntime::GetObjCRuntime(m_process)) {
Value::ValueType value_type;
if (objc_runtime->GetDynamicTypeAndAddress(
in_value, use_dynamic, class_type_or_name, address, value_type)) {
bool found = false;
// Return the most specific class which we can get the typeref.
ForEachSuperClassType(in_value, [&](SuperClassType sc) -> bool {

Choose a reason for hiding this comment

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

Can you add a comment here explaining why we are filtering out private Foundation types?

if (auto *tr = sc.get_typeref()) {
swift::Demangle::Demangler dem;
swift::Demangle::NodePointer node = tr->getDemangling(dem);
// Skip private Foundation types since it's unlikely that would be
// useful to users.
if (IsPrivateNSClass(node))
return false;
class_type_or_name.SetCompilerType(ts.RemangleAsType(dem, node));
found = true;
return true;
}
return false;
});
return found;
}
return false;
}
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
auto &remote_ast = GetRemoteASTContext(scratch_ctx);
auto *reflection_ctx = GetReflectionContext();
swift::remote::RemoteAddress instance_address(class_metadata_ptr);
auto metadata_address = remote_ast.getHeapMetadataForObject(instance_address);
auto metadata_address =
reflection_ctx->readMetadataFromInstance(class_metadata_ptr);
if (!metadata_address) {
if (log) {
log->Printf("could not read heap metadata for object at %llu: %s\n",
class_metadata_ptr,
metadata_address.getFailure().render().c_str());
}

if (log)
log->Printf("could not read heap metadata for object at %llu\n",
class_metadata_ptr);
return false;
}

auto instance_type =
remote_ast.getTypeForRemoteTypeMetadata(metadata_address.getValue(),
/*skipArtificial=*/true);
if (!instance_type) {
if (log) {
log->Printf("could not get type metadata from address %" PRIu64 " : %s\n",
metadata_address.getValue().getAddressData(),
instance_type.getFailure().render().c_str());
}
const auto *typeref =
reflection_ctx->readTypeFromMetadata(*metadata_address,
/*skipArtificial=*/false);
if (!typeref)
return false;
swift::Demangle::Demangler dem;
swift::Demangle::NodePointer node = typeref->getDemangling(dem);
class_type_or_name.SetCompilerType(ts.RemangleAsType(dem, node));

#ifndef NDEBUG
auto &remote_ast = GetRemoteASTContext(scratch_ctx);
auto remote_ast_metadata_address =
remote_ast.getHeapMetadataForObject(instance_address);
if (remote_ast_metadata_address) {
auto instance_type = remote_ast.getTypeForRemoteTypeMetadata(
remote_ast_metadata_address.getValue(),
/*skipArtificial=*/true);
if (instance_type) {
auto ref_type = ToCompilerType(instance_type.getValue());
ConstString a = ref_type.GetMangledTypeName();
ConstString b = class_type_or_name.GetCompilerType().GetMangledTypeName();
if (a != b)
llvm::dbgs() << "RemoteAST and runtime diverge " << a << " != " << b
<< "\n";
} else {
if (log) {
log->Printf(
"could not get type metadata from address %" PRIu64 " : %s\n",
*metadata_address, instance_type.getFailure().render().c_str());
}
}
}

// The read lock must have been acquired by the caller.
class_type_or_name.SetCompilerType(
{&scratch_ctx, instance_type.getValue().getPointer()});
#endif
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2566,6 +2566,7 @@ CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex(
if (suffix.consume_front("__ObjC."))
ast_child_name = suffix.str();
assert((llvm::StringRef(child_name).contains('.') ||
llvm::StringRef(ast_child_name).contains('.') ||
Equivalent(child_name, ast_child_name)));
assert(ast_language_flags ||
(Equivalent(llvm::Optional<uint64_t>(child_byte_size),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ def test(self):
elif found == 2 and response == 1 and 'SwiftDWARFImporterDelegate' in line:
self.assertTrue('types collected' in line, line)
response += 1
self.assertEqual(found, 3)
self.assertEqual(found, 1)
self.assertEqual(response, 1)
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def dynamic_val_commands(self):
"z = ",
"Derived<Int>) aBase = 0x",
"Base<Int>)",
".Base = {",
".Base<Swift.Int> = {",
"v = 449493530",
"q = 3735928559"])
self.runCmd("continue")
Expand All @@ -76,6 +76,6 @@ def dynamic_val_commands(self):
"z = ",
"Derived<Int>) aBase = 0x",
"Base<Int>)",
".Base = {",
".Base<Swift.Int> = {",
"v = 449493530",
"q = 3735928559"])
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def genericresolution_commands(self):
substrs=[
"(Int) x = 123",
"(a.OtherClass<Int>) self = 0x",
"a.AClass = {}",
"a.AClass<Swift.Int> = {}",
"v = 1234567"])
self.runCmd("continue")
self.expect(
Expand Down Expand Up @@ -76,7 +76,7 @@ def genericresolution_commands(self):
"(Int) x = 5",
'(String) y = "hello world"',
"(a.OtherClass<Int>) self = 0x",
"a.AClass = {}",
"a.AClass<Swift.Int> = {}",
"v = 1234567"])
self.runCmd("continue")
self.expect(
Expand Down
2 changes: 1 addition & 1 deletion lldb/test/Shell/SwiftREPL/OptionalWithDynamicType.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// REQUIRES: system-darwin
// REQUIRES: swift

// RUN: %lldb --repl < %s 2>&1 | FileCheck %s
// RUN: %lldb --repl < %s | FileCheck %s

Choose a reason for hiding this comment

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

why do we need to filter out stderr output?

Copy link
Author

Choose a reason for hiding this comment

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

Some unrelated warnings were making this test fail, so I thought I'd remove stderr since that's not what the test is checking for.

Choose a reason for hiding this comment

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

Hmm, yeah. It doesn't seem to be necessary given the CHECK lines.


import Foundation

Expand Down