From 39dcacad33f31803827c8d9aabec5c1ac56116df Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 17 Oct 2025 13:49:16 +0100 Subject: [PATCH 1/2] [lldb][ValueObject][NFC] Move Objective-C lookup code into helpers In preparation to moving them into the Objective-C LanguageRuntime plugin. --- lldb/include/lldb/ValueObject/ValueObject.h | 3 + .../ObjC/ObjCLanguageRuntime.cpp | 12 +-- lldb/source/ValueObject/ValueObject.cpp | 93 +++++++++++-------- 3 files changed, 60 insertions(+), 48 deletions(-) diff --git a/lldb/include/lldb/ValueObject/ValueObject.h b/lldb/include/lldb/ValueObject/ValueObject.h index ed86f9bd0ea3d..d13664f122ad4 100644 --- a/lldb/include/lldb/ValueObject/ValueObject.h +++ b/lldb/include/lldb/ValueObject/ValueObject.h @@ -1104,6 +1104,9 @@ class ValueObject { private: virtual CompilerType MaybeCalculateCompleteType(); + CompilerType LookupInRuntime(ConstString class_name, Process &process); + CompilerType LookupInModulesVendor(ConstString class_name, Target &process); + void UpdateChildrenAddressType() { GetRoot()->DoUpdateChildrenAddressType(*this); } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp index c33760eccd127..bb28bbe37b17b 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp @@ -446,14 +446,10 @@ ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) { if (!complete_objc_class_type_sp) return std::nullopt; - CompilerType complete_class( - complete_objc_class_type_sp->GetFullCompilerType()); - if (complete_class.GetCompleteType()) { - if (is_pointer_type) - return complete_class.GetPointerType(); - else - return complete_class; - } + if (CompilerType complete_class = + complete_objc_class_type_sp->GetFullCompilerType(); + complete_class.GetCompleteType()) + return is_pointer_type ? complete_class.GetPointerType() : complete_class; return std::nullopt; } diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index ca4ddc6e8f4cb..cf44541156713 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -249,6 +249,51 @@ void ValueObject::ClearDynamicTypeInformation() { SetSyntheticChildren(lldb::SyntheticChildrenSP()); } +CompilerType ValueObject::LookupInModulesVendor(ConstString class_name, + Target &target) { + assert(class_name); + + auto *persistent_state = llvm::cast( + target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); + if (!persistent_state) + return {}; + + auto clang_modules_decl_vendor_sp = + persistent_state->GetClangModulesDeclVendor(); + if (!clang_modules_decl_vendor_sp) + return {}; + + auto types = clang_modules_decl_vendor_sp->FindTypes( + class_name, /*max_matches*/ UINT32_MAX); + if (types.empty()) + return {}; + + return types.front(); +} + +CompilerType ValueObject::LookupInRuntime(ConstString class_name, + Process &process) { + auto *objc_language_runtime = ObjCLanguageRuntime::Get(process); + if (!objc_language_runtime) + return {}; + + auto *runtime_vendor = objc_language_runtime->GetDeclVendor(); + if (!runtime_vendor) + return {}; + + std::vector compiler_decls; + runtime_vendor->FindDecls(class_name, false, UINT32_MAX, compiler_decls); + if (compiler_decls.empty()) + return {}; + + auto *ctx = + llvm::dyn_cast(compiler_decls[0].GetTypeSystem()); + if (!ctx) + return {}; + + return ctx->GetTypeForDecl(compiler_decls[0].GetOpaqueDecl()); +} + CompilerType ValueObject::MaybeCalculateCompleteType() { CompilerType compiler_type(GetCompilerTypeImpl()); @@ -277,7 +322,6 @@ CompilerType ValueObject::MaybeCalculateCompleteType() { } } - std::vector decls; CompilerType class_type; bool is_pointer_type = false; if (TypeSystemClang::IsObjCObjectPointerType(compiler_type, &class_type)) @@ -291,49 +335,18 @@ CompilerType ValueObject::MaybeCalculateCompleteType() { if (!class_name) return compiler_type; - // try the modules - if (TargetSP target_sp = GetTargetSP()) { - auto *persistent_state = llvm::cast( - target_sp->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); - if (!persistent_state) - return compiler_type; - - if (auto clang_modules_decl_vendor = - persistent_state->GetClangModulesDeclVendor()) { - ConstString key_cs(class_name); - auto types = clang_modules_decl_vendor->FindTypes( - key_cs, /*max_matches*/ UINT32_MAX); - if (!types.empty()) { - auto module_type = types.front(); - m_override_type = - is_pointer_type ? module_type.GetPointerType() : module_type; - } - - if (m_override_type.IsValid()) - return m_override_type; + if (auto target_sp = GetTargetSP()) { + if (CompilerType found = LookupInModulesVendor(class_name, *target_sp)) { + m_override_type = is_pointer_type ? found.GetPointerType() : found; + return m_override_type; } } - // then try the runtime - if (auto *objc_language_runtime = ObjCLanguageRuntime::Get(*process_sp)) { - if (auto *runtime_vendor = objc_language_runtime->GetDeclVendor()) { - std::vector compiler_decls; - runtime_vendor->FindDecls(class_name, false, UINT32_MAX, compiler_decls); - if (!compiler_decls.empty()) { - auto *ctx = - llvm::dyn_cast(compiler_decls[0].GetTypeSystem()); - if (ctx) { - CompilerType runtime_type = - ctx->GetTypeForDecl(compiler_decls[0].GetOpaqueDecl()); - m_override_type = - is_pointer_type ? runtime_type.GetPointerType() : runtime_type; - } - } - - if (m_override_type.IsValid()) - return m_override_type; - } + if (CompilerType found = LookupInRuntime(class_name, *process_sp)) { + m_override_type = is_pointer_type ? found.GetPointerType() : found; + return m_override_type; } + return compiler_type; } From 1152cf11f825074fd7d5d699ce5ae6ec7fd8b849 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 17 Oct 2025 13:54:48 +0100 Subject: [PATCH 2/2] [lldb][ValueObject] Move Objective-C specific lookups into ObjCLanguageRuntime I'd like to upstream these lookups. It'd be more acceptable if this lived in the LanguageRuntime. --- lldb/include/lldb/ValueObject/ValueObject.h | 3 - .../ObjC/ObjCLanguageRuntime.cpp | 61 ++++++++++++++-- .../ObjC/ObjCLanguageRuntime.h | 4 ++ lldb/source/ValueObject/ValueObject.cpp | 70 ------------------- .../lang/objc/ivar-in-framework-base/Makefile | 6 ++ .../TestIvarInFrameworkBase.py | 39 +++++++++++ .../lang/objc/ivar-in-framework-base/lib.h | 6 ++ .../lang/objc/ivar-in-framework-base/lib.m | 8 +++ .../lang/objc/ivar-in-framework-base/main.m | 22 ++++++ 9 files changed, 139 insertions(+), 80 deletions(-) create mode 100644 lldb/test/API/lang/objc/ivar-in-framework-base/Makefile create mode 100644 lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py create mode 100644 lldb/test/API/lang/objc/ivar-in-framework-base/lib.h create mode 100644 lldb/test/API/lang/objc/ivar-in-framework-base/lib.m create mode 100644 lldb/test/API/lang/objc/ivar-in-framework-base/main.m diff --git a/lldb/include/lldb/ValueObject/ValueObject.h b/lldb/include/lldb/ValueObject/ValueObject.h index d13664f122ad4..ed86f9bd0ea3d 100644 --- a/lldb/include/lldb/ValueObject/ValueObject.h +++ b/lldb/include/lldb/ValueObject/ValueObject.h @@ -1104,9 +1104,6 @@ class ValueObject { private: virtual CompilerType MaybeCalculateCompleteType(); - CompilerType LookupInRuntime(ConstString class_name, Process &process); - CompilerType LookupInModulesVendor(ConstString class_name, Target &process); - void UpdateChildrenAddressType() { GetRoot()->DoUpdateChildrenAddressType(*this); } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp index bb28bbe37b17b..2b2ca080c7f55 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp @@ -423,6 +423,46 @@ Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition( return error; } +CompilerType ObjCLanguageRuntime::LookupInModulesVendor(ConstString class_name, + Target &target) { + assert(class_name); + + auto *persistent_state = llvm::cast( + target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); + if (!persistent_state) + return {}; + + auto clang_modules_decl_vendor_sp = + persistent_state->GetClangModulesDeclVendor(); + if (!clang_modules_decl_vendor_sp) + return {}; + + auto types = clang_modules_decl_vendor_sp->FindTypes( + class_name, /*max_matches*/ UINT32_MAX); + if (types.empty()) + return {}; + + return types.front(); +} + +CompilerType ObjCLanguageRuntime::LookupInRuntime(ConstString class_name) { + auto *runtime_vendor = GetDeclVendor(); + if (!runtime_vendor) + return {}; + + std::vector compiler_decls; + runtime_vendor->FindDecls(class_name, false, UINT32_MAX, compiler_decls); + if (compiler_decls.empty()) + return {}; + + auto *ctx = + llvm::dyn_cast(compiler_decls[0].GetTypeSystem()); + if (!ctx) + return {}; + + return ctx->GetTypeForDecl(compiler_decls[0].GetOpaqueDecl()); +} + std::optional ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) { CompilerType class_type; @@ -442,14 +482,21 @@ ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) { if (!class_name) return std::nullopt; - TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name); - if (!complete_objc_class_type_sp) - return std::nullopt; + if (TypeSP complete_objc_class_type_sp = + LookupInCompleteClassCache(class_name)) { + if (CompilerType complete_class = + complete_objc_class_type_sp->GetFullCompilerType(); + complete_class.GetCompleteType()) + return is_pointer_type ? complete_class.GetPointerType() : complete_class; + } + + assert(m_process); + if (CompilerType found = + LookupInModulesVendor(class_name, m_process->GetTarget())) + return is_pointer_type ? found.GetPointerType() : found; - if (CompilerType complete_class = - complete_objc_class_type_sp->GetFullCompilerType(); - complete_class.GetCompleteType()) - return is_pointer_type ? complete_class.GetPointerType() : complete_class; + if (CompilerType found = LookupInRuntime(class_name)) + return is_pointer_type ? found.GetPointerType() : found; return std::nullopt; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h index 45de098c15f51..cc8281e9d1a02 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h @@ -465,6 +465,10 @@ class ObjCLanguageRuntime : public LanguageRuntime { ObjCLanguageRuntime(const ObjCLanguageRuntime &) = delete; const ObjCLanguageRuntime &operator=(const ObjCLanguageRuntime &) = delete; + +private: + CompilerType LookupInRuntime(ConstString class_name); + CompilerType LookupInModulesVendor(ConstString class_name, Target &process); }; } // namespace lldb_private diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index cf44541156713..f5d2a8ff1144a 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -249,51 +249,6 @@ void ValueObject::ClearDynamicTypeInformation() { SetSyntheticChildren(lldb::SyntheticChildrenSP()); } -CompilerType ValueObject::LookupInModulesVendor(ConstString class_name, - Target &target) { - assert(class_name); - - auto *persistent_state = llvm::cast( - target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); - if (!persistent_state) - return {}; - - auto clang_modules_decl_vendor_sp = - persistent_state->GetClangModulesDeclVendor(); - if (!clang_modules_decl_vendor_sp) - return {}; - - auto types = clang_modules_decl_vendor_sp->FindTypes( - class_name, /*max_matches*/ UINT32_MAX); - if (types.empty()) - return {}; - - return types.front(); -} - -CompilerType ValueObject::LookupInRuntime(ConstString class_name, - Process &process) { - auto *objc_language_runtime = ObjCLanguageRuntime::Get(process); - if (!objc_language_runtime) - return {}; - - auto *runtime_vendor = objc_language_runtime->GetDeclVendor(); - if (!runtime_vendor) - return {}; - - std::vector compiler_decls; - runtime_vendor->FindDecls(class_name, false, UINT32_MAX, compiler_decls); - if (compiler_decls.empty()) - return {}; - - auto *ctx = - llvm::dyn_cast(compiler_decls[0].GetTypeSystem()); - if (!ctx) - return {}; - - return ctx->GetTypeForDecl(compiler_decls[0].GetOpaqueDecl()); -} - CompilerType ValueObject::MaybeCalculateCompleteType() { CompilerType compiler_type(GetCompilerTypeImpl()); @@ -322,31 +277,6 @@ CompilerType ValueObject::MaybeCalculateCompleteType() { } } - CompilerType class_type; - bool is_pointer_type = false; - if (TypeSystemClang::IsObjCObjectPointerType(compiler_type, &class_type)) - is_pointer_type = true; - else if (TypeSystemClang::IsObjCObjectOrInterfaceType(compiler_type)) - class_type = compiler_type; - else - return compiler_type; - - ConstString class_name(class_type.GetTypeName()); - if (!class_name) - return compiler_type; - - if (auto target_sp = GetTargetSP()) { - if (CompilerType found = LookupInModulesVendor(class_name, *target_sp)) { - m_override_type = is_pointer_type ? found.GetPointerType() : found; - return m_override_type; - } - } - - if (CompilerType found = LookupInRuntime(class_name, *process_sp)) { - m_override_type = is_pointer_type ? found.GetPointerType() : found; - return m_override_type; - } - return compiler_type; } diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile b/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile new file mode 100644 index 0000000000000..c7947fc138c18 --- /dev/null +++ b/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile @@ -0,0 +1,6 @@ +OBJC_SOURCES := main.m lib.m +LD_EXTRAS = -framework Foundation + +include Makefile.rules + +lib.o: CFLAGS = $(CFLAGS_NO_DEBUG) diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py b/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py new file mode 100644 index 0000000000000..40fc6b7a68995 --- /dev/null +++ b/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py @@ -0,0 +1,39 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestIvarInFrameworkBase(TestBase): + """ + Tests whether LLDB's data inspection commands can correctly retrieve + information about ivars from the Objective-C runtime. + In this test-case we have a base class type for which we don't have access + to the debug-info of the implementation (mimicking the scenario of subclassing + a type from a system framework). LLDB won't be able to see the backing ivar for + 'fooProp' from just debug-info, but it will fall back on the runtime to get the + necessary information. + """ + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m")) + self.expect("frame variable *bar", substrs=["_fooProp = 10", "_barProp = 15"]) + + def test_expr(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m")) + self.expect_expr( + "*bar", + result_type="Bar", + result_children=[ + ValueCheck( + name="Foo", + children=[ + ValueCheck(name="NSObject"), + ValueCheck(name="_fooProp", value="10"), + ], + ), + ValueCheck(name="_barProp", value="15"), + ], + ) diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h new file mode 100644 index 0000000000000..31ceb53dc6885 --- /dev/null +++ b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h @@ -0,0 +1,6 @@ +#import + +@interface Foo : NSObject +@property int fooProp; +- (id)init; +@end diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m new file mode 100644 index 0000000000000..e1bf80ac4bd4c --- /dev/null +++ b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m @@ -0,0 +1,8 @@ +#import "lib.h" + +@implementation Foo +- (id)init { + self.fooProp = 10; + return self; +} +@end diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/main.m b/lldb/test/API/lang/objc/ivar-in-framework-base/main.m new file mode 100644 index 0000000000000..1fd352ec92c54 --- /dev/null +++ b/lldb/test/API/lang/objc/ivar-in-framework-base/main.m @@ -0,0 +1,22 @@ +#import "lib.h" +#include + +@interface Bar : Foo +@property int barProp; +- (id)init; +@end + +@implementation Bar + +- (id)init { + self = [super init]; + self.barProp = 15; + return self; +} +@end + +int main() { + Bar *bar = [Bar new]; + puts("break here"); + return 0; +}