https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/203128
>From e44cadf82148e1953097516747c85d30a042f4ef Mon Sep 17 00:00:00 2001 From: Jim Ingham <[email protected]> Date: Wed, 10 Jun 2026 16:47:05 -0700 Subject: [PATCH 1/5] Add the ability to look up persistent types and variables defined in the expression parser through the SB API's. --- lldb/bindings/interface/SBTargetDocstrings.i | 30 +++++ lldb/include/lldb/API/SBTarget.h | 6 + lldb/source/API/SBTarget.cpp | 85 ++++++++++++++ .../API/python_api/persistent_decls/Makefile | 4 + .../persistent_decls/TestPersistentDecls.py | 111 ++++++++++++++++++ .../API/python_api/persistent_decls/main.c | 6 + 6 files changed, 242 insertions(+) create mode 100644 lldb/test/API/python_api/persistent_decls/Makefile create mode 100644 lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py create mode 100644 lldb/test/API/python_api/persistent_decls/main.c diff --git a/lldb/bindings/interface/SBTargetDocstrings.i b/lldb/bindings/interface/SBTargetDocstrings.i index b486a8f189f10..3cfb18f766cd2 100644 --- a/lldb/bindings/interface/SBTargetDocstrings.i +++ b/lldb/bindings/interface/SBTargetDocstrings.i @@ -307,6 +307,36 @@ produces: :: An SBAddress which will be valid if..." ) lldb::SBTarget::ResolveFileAddress; +%feature("docstring", " + Look up a persistent type defined using the expression parser. + + @param[in] type_name + The base name of the persistent type you defined. + + @param[in] language + A member of the enum lldb::LanguageType giving the + language of the Expression parser you used to define + the persistent type. + + @return + An SBType representing the persistent type you defined." +) lldb::SBTarget::FindExpressionTypeForLanguage; + +%feature("docstring", " + Look up a persistent variable defined using the expression parser. + + @param[in] variable_name + The name of the persistent variable you defined. + + @param[in] language + A member of the enum lldb::LanguageType giving the + language of the Expression parser you used to define + the persistent type. + + @return + An SBValue representing the persistent variable you defined." +) lldb::SBTarget::FindExpressionVariableForLanguage; + %feature("docstring", " Read target memory. If a target process is running then memory is read from here. Otherwise the memory is read from the object diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index d598c44dd7332..113c5db0331df 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -923,6 +923,12 @@ class LLDB_API SBTarget { lldb::SBType GetBasicType(lldb::BasicType type); + lldb::SBType FindExpressionTypeForLanguage(const char *typename_cstr, + lldb::LanguageType lang); + + lldb::SBValue FindExpressionVariableForLanguage(const char *varname_cstr, + lldb::LanguageType lang); + lldb::SBValue CreateValueFromAddress(const char *name, lldb::SBAddress addr, lldb::SBType type); diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 72286336102d2..9ac6f06c9b840 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -40,6 +40,7 @@ #include "lldb/Core/SearchFilter.h" #include "lldb/Core/Section.h" #include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Expression/ExpressionVariable.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" @@ -1878,6 +1879,90 @@ lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name, return sb_sc_list; } +lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, + lldb::LanguageType language) { + TargetSP target_sp = GetSP(); + if (!target_sp) + return {}; + + if (!typename_cstr || !typename_cstr[0]) + return {}; + + ConstString const_typename(typename_cstr); + + // If the language type was "unknown" search all languages: + if (language == lldb::eLanguageTypeUnknown) { + LanguageSet expr_languages = + Language::GetLanguagesSupportingTypeSystemsForExpressions(); + for (int bit : expr_languages.bitvector.set_bits()) { + auto cur_lang = (LanguageType)bit; + PersistentExpressionState *persistent = + target_sp->GetPersistentExpressionStateForLanguage(cur_lang); + if (persistent) { + std::optional<CompilerType> type_op + = persistent->GetCompilerTypeFromPersistentDecl(const_typename); + if (type_op) { + return SBType(*type_op); + } + } + } + } else { + PersistentExpressionState *persistent = + target_sp->GetPersistentExpressionStateForLanguage(language); + if (persistent) { + std::optional<CompilerType> type_op + = persistent->GetCompilerTypeFromPersistentDecl(const_typename); + if (type_op && (*type_op)) { + return SBType(*type_op); + } + } + } + return {}; +} + +lldb::SBValue SBTarget::FindExpressionVariableForLanguage( + const char *varname_cstr, lldb::LanguageType language) { + TargetSP target_sp = GetSP(); + if (!target_sp) + return {}; + + if (!varname_cstr || !varname_cstr[0]) + return {}; + + ConstString const_varname(varname_cstr); + + // If the language type was "unknown" search all languages: + if (language == lldb::eLanguageTypeUnknown) { + LanguageSet expr_languages = + Language::GetLanguagesSupportingTypeSystemsForExpressions(); + for (int bit : expr_languages.bitvector.set_bits()) { + auto cur_lang = (LanguageType)bit; + PersistentExpressionState *persistent = + target_sp->GetPersistentExpressionStateForLanguage(cur_lang); + if (persistent) { + lldb::ExpressionVariableSP expr_var_sp = + persistent->GetVariable(const_varname); + if (expr_var_sp) { + return expr_var_sp->GetValueObject(); + } + } + } + } else { + PersistentExpressionState *persistent = + target_sp->GetPersistentExpressionStateForLanguage(language); + if (persistent) { + lldb::ExpressionVariableSP expr_var_sp = + persistent->GetVariable(const_varname); + if (expr_var_sp) { + return expr_var_sp->GetValueObject(); + } + } + } + return {}; +} + + + lldb::SBType SBTarget::FindFirstType(const char *typename_cstr) { LLDB_INSTRUMENT_VA(this, typename_cstr); diff --git a/lldb/test/API/python_api/persistent_decls/Makefile b/lldb/test/API/python_api/persistent_decls/Makefile new file mode 100644 index 0000000000000..695335e068c0c --- /dev/null +++ b/lldb/test/API/python_api/persistent_decls/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules diff --git a/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py new file mode 100644 index 0000000000000..1381a251bb9a8 --- /dev/null +++ b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py @@ -0,0 +1,111 @@ +""" +Test the SBTarget API's to retrieve persistent types +and values. +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + + +class TestPersistentDecls(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_persistent_types(self): + """Define some types in the expression evaluator and find them.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.types_test(False) + + def test_persistent_types_unknown(self): + """Define some types in the expression evaluator and find them.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.types_test(True) + + def test_persistent_values(self): + """Define some values in the expression evaluator and find them.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.values_test(False) + + def test_persistent_values_unknown(self): + """Define some values in the expression evaluator and find them.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.values_test(True) + + def types_test(self, use_unknown): + (target, _, _, _) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make some types so we aren't succeeding in gettting the only one. + + typename = "$firstStruct" + + self.expect(f"expr struct $SomeType {{ int b; int a; int c;}}") + self.expect(f"expr struct {typename} {{ int a; int b; char *c;}}") + self.expect(f"expr struct $AnotherType {{ int a; char *b; int c;}}") + self.expect(f"expr struct $YetAnotherType {{ char *a; int b; int c;}}") + + if use_unknown: + language = lldb.eLanguageTypeUnknown + else: + language = lldb.eLanguageTypeC_plus_plus + + type = target.FindExpressionTypeForLanguage(typename, language) + self.assertTrue(type.IsValid(), "Got a valid type") + + # Make sure we got the type we expected: + self.assertEqual(type.name, typename, "Got the right type.") + # Now check that the fields are right: + self.assertEqual(type.num_fields, 3, "Right number of fields") + + ivar_a = type.fields[0] + self.assertEqual(ivar_a.name, "a", "a name is right") + self.assertEqual(ivar_a.type.name, "int", "Right type") + + ivar_b = type.fields[1] + self.assertEqual(ivar_b.name, "b", "b name is right") + self.assertEqual(ivar_b.type.name, "int", "Right type") + + ivar_c = type.fields[2] + self.assertEqual(ivar_c.name, "c", "c name is right") + self.assertEqual(ivar_c.type.name, "char *", "Right type") + + def values_test(self, use_unknown): + (target, _, _, _) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + type_name = "struct $MyStruct" + value_name = "$my_struct" + self.expect("expr int $my_int = 100") + self.expect('expr char *$my_char_ptr = "Some char pointer."') + self.expect(f"expr {type_name} {{int a; int b;}}; {type_name} {value_name} = {{10, 20}}") + + # Now try to find one: + if use_unknown: + language = lldb.eLanguageTypeUnknown + else: + language = lldb.eLanguageTypeC_plus_plus + + value = target.FindExpressionVariableForLanguage( + value_name, + language + ) + + self.assertTrue(value.IsValid(), "Got a valid SBValue") + self.assertTrue(value.GetError().Success(), "Got a value") + + value_checker = ValueCheck( + name=value_name, + type=type_name, + children=[ + ValueCheck(name="a", type="int", value='10'), + ValueCheck(name="b", type="int", value='20') + ] + ) + value_checker.check_value(self, value, "Found the right value") diff --git a/lldb/test/API/python_api/persistent_decls/main.c b/lldb/test/API/python_api/persistent_decls/main.c new file mode 100644 index 0000000000000..31280778072e3 --- /dev/null +++ b/lldb/test/API/python_api/persistent_decls/main.c @@ -0,0 +1,6 @@ +int +main() +{ + int test_var = 10; // Set a breakpoint here + return test_var; +} >From 5796a6c8a8abc5bbcaa9187da3b0a73ffeac15ba Mon Sep 17 00:00:00 2001 From: Jim Ingham <[email protected]> Date: Thu, 11 Jun 2026 14:58:17 -0700 Subject: [PATCH 2/5] Formatting --- lldb/include/lldb/API/SBTarget.h | 4 +-- lldb/source/API/SBTarget.cpp | 20 +++++++-------- .../persistent_decls/TestPersistentDecls.py | 25 +++++++++---------- .../API/python_api/persistent_decls/main.c | 4 +-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index 113c5db0331df..c9e04a5a2ec57 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -924,10 +924,10 @@ class LLDB_API SBTarget { lldb::SBType GetBasicType(lldb::BasicType type); lldb::SBType FindExpressionTypeForLanguage(const char *typename_cstr, - lldb::LanguageType lang); + lldb::LanguageType lang); lldb::SBValue FindExpressionVariableForLanguage(const char *varname_cstr, - lldb::LanguageType lang); + lldb::LanguageType lang); lldb::SBValue CreateValueFromAddress(const char *name, lldb::SBAddress addr, lldb::SBType type); diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 9ac6f06c9b840..cb2c0fc482a34 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1879,8 +1879,9 @@ lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name, return sb_sc_list; } -lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, - lldb::LanguageType language) { +lldb::SBType +SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, + lldb::LanguageType language) { TargetSP target_sp = GetSP(); if (!target_sp) return {}; @@ -1899,8 +1900,8 @@ lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, PersistentExpressionState *persistent = target_sp->GetPersistentExpressionStateForLanguage(cur_lang); if (persistent) { - std::optional<CompilerType> type_op - = persistent->GetCompilerTypeFromPersistentDecl(const_typename); + std::optional<CompilerType> type_op = + persistent->GetCompilerTypeFromPersistentDecl(const_typename); if (type_op) { return SBType(*type_op); } @@ -1910,8 +1911,8 @@ lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, PersistentExpressionState *persistent = target_sp->GetPersistentExpressionStateForLanguage(language); if (persistent) { - std::optional<CompilerType> type_op - = persistent->GetCompilerTypeFromPersistentDecl(const_typename); + std::optional<CompilerType> type_op = + persistent->GetCompilerTypeFromPersistentDecl(const_typename); if (type_op && (*type_op)) { return SBType(*type_op); } @@ -1920,8 +1921,9 @@ lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, return {}; } -lldb::SBValue SBTarget::FindExpressionVariableForLanguage( - const char *varname_cstr, lldb::LanguageType language) { +lldb::SBValue +SBTarget::FindExpressionVariableForLanguage(const char *varname_cstr, + lldb::LanguageType language) { TargetSP target_sp = GetSP(); if (!target_sp) return {}; @@ -1961,8 +1963,6 @@ lldb::SBValue SBTarget::FindExpressionVariableForLanguage( return {}; } - - lldb::SBType SBTarget::FindFirstType(const char *typename_cstr) { LLDB_INSTRUMENT_VA(this, typename_cstr); diff --git a/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py index 1381a251bb9a8..e41b314a2bf94 100644 --- a/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py +++ b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py @@ -29,20 +29,20 @@ def test_persistent_values(self): self.build() self.main_source_file = lldb.SBFileSpec("main.c") self.values_test(False) - + def test_persistent_values_unknown(self): """Define some values in the expression evaluator and find them.""" self.build() self.main_source_file = lldb.SBFileSpec("main.c") self.values_test(True) - + def types_test(self, use_unknown): (target, _, _, _) = lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", self.main_source_file ) # Make some types so we aren't succeeding in gettting the only one. - + typename = "$firstStruct" self.expect(f"expr struct $SomeType {{ int b; int a; int c;}}") @@ -54,7 +54,7 @@ def types_test(self, use_unknown): language = lldb.eLanguageTypeUnknown else: language = lldb.eLanguageTypeC_plus_plus - + type = target.FindExpressionTypeForLanguage(typename, language) self.assertTrue(type.IsValid(), "Got a valid type") @@ -84,7 +84,9 @@ def values_test(self, use_unknown): value_name = "$my_struct" self.expect("expr int $my_int = 100") self.expect('expr char *$my_char_ptr = "Some char pointer."') - self.expect(f"expr {type_name} {{int a; int b;}}; {type_name} {value_name} = {{10, 20}}") + self.expect( + f"expr {type_name} {{int a; int b;}}; {type_name} {value_name} = {{10, 20}}" + ) # Now try to find one: if use_unknown: @@ -92,11 +94,8 @@ def values_test(self, use_unknown): else: language = lldb.eLanguageTypeC_plus_plus - value = target.FindExpressionVariableForLanguage( - value_name, - language - ) - + value = target.FindExpressionVariableForLanguage(value_name, language) + self.assertTrue(value.IsValid(), "Got a valid SBValue") self.assertTrue(value.GetError().Success(), "Got a value") @@ -104,8 +103,8 @@ def values_test(self, use_unknown): name=value_name, type=type_name, children=[ - ValueCheck(name="a", type="int", value='10'), - ValueCheck(name="b", type="int", value='20') - ] + ValueCheck(name="a", type="int", value="10"), + ValueCheck(name="b", type="int", value="20"), + ], ) value_checker.check_value(self, value, "Found the right value") diff --git a/lldb/test/API/python_api/persistent_decls/main.c b/lldb/test/API/python_api/persistent_decls/main.c index 31280778072e3..001333e92d9bf 100644 --- a/lldb/test/API/python_api/persistent_decls/main.c +++ b/lldb/test/API/python_api/persistent_decls/main.c @@ -1,6 +1,4 @@ -int -main() -{ +int main() { int test_var = 10; // Set a breakpoint here return test_var; } >From 58a542486507882b4614fd883969d68597380c09 Mon Sep 17 00:00:00 2001 From: Jim Ingham <[email protected]> Date: Mon, 15 Jun 2026 16:18:52 -0700 Subject: [PATCH 3/5] Don't use eLanguageTypeUnknown to search all languages. There is only one possible variable/type per language, so I wanted to preserve the convenience of not having to go through a list to get the one thing it can hold. And if you want to iterate over languages, that's easy to do from the outside. I was logging LanguageTypes a bunch and that got annoying so I also added a formatv adaptor for LanguageType. --- lldb/include/lldb/API/SBTarget.h | 3 +- lldb/include/lldb/Target/Language.h | 9 ++ lldb/source/API/SBTarget.cpp | 118 +++++++++--------- lldb/source/Target/Language.cpp | 6 + .../persistent_decls/TestPersistentDecls.py | 57 +++++---- 5 files changed, 108 insertions(+), 85 deletions(-) diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index c9e04a5a2ec57..1c1bb1b501a0f 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -924,7 +924,8 @@ class LLDB_API SBTarget { lldb::SBType GetBasicType(lldb::BasicType type); lldb::SBType FindExpressionTypeForLanguage(const char *typename_cstr, - lldb::LanguageType lang); + lldb::LanguageType lang, + SBError &error); lldb::SBValue FindExpressionVariableForLanguage(const char *varname_cstr, lldb::LanguageType lang); diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 9205b5e339977..5987ae373936f 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -24,6 +24,8 @@ #include "lldb/Symbol/TypeSystem.h" #include "lldb/lldb-private.h" #include "lldb/lldb-public.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" namespace lldb_private { @@ -519,4 +521,11 @@ class Language : public PluginInterface { } // namespace lldb_private +namespace llvm { + template <> struct format_provider<lldb::LanguageType> { + static void format(const lldb::LanguageType &language, llvm::raw_ostream &OS, + llvm::StringRef Options); +}; +} // namespace llvm + #endif // LLDB_TARGET_LANGUAGE_H diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index cb2c0fc482a34..98525fd684eed 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1881,43 +1881,47 @@ lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name, lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, - lldb::LanguageType language) { + lldb::LanguageType language, + SBError &sb_error) { + sb_error.Clear(); + TargetSP target_sp = GetSP(); - if (!target_sp) + if (!target_sp) { + sb_error.SetErrorString("no target."); return {}; + } - if (!typename_cstr || !typename_cstr[0]) + if (!typename_cstr || !typename_cstr[0]) { + sb_error.SetErrorString("empty type name for search."); return {}; + } + + if (language == eLanguageTypeUnknown) { + sb_error.SetErrorString("eLanguageTypeUnknown can't define expression " + "types."); + return {}; + } ConstString const_typename(typename_cstr); - // If the language type was "unknown" search all languages: - if (language == lldb::eLanguageTypeUnknown) { - LanguageSet expr_languages = - Language::GetLanguagesSupportingTypeSystemsForExpressions(); - for (int bit : expr_languages.bitvector.set_bits()) { - auto cur_lang = (LanguageType)bit; - PersistentExpressionState *persistent = - target_sp->GetPersistentExpressionStateForLanguage(cur_lang); - if (persistent) { - std::optional<CompilerType> type_op = - persistent->GetCompilerTypeFromPersistentDecl(const_typename); - if (type_op) { - return SBType(*type_op); - } - } - } - } else { - PersistentExpressionState *persistent = - target_sp->GetPersistentExpressionStateForLanguage(language); - if (persistent) { - std::optional<CompilerType> type_op = - persistent->GetCompilerTypeFromPersistentDecl(const_typename); - if (type_op && (*type_op)) { - return SBType(*type_op); - } - } + PersistentExpressionState *persistent = + target_sp->GetPersistentExpressionStateForLanguage(language); + + if (!persistent) { + sb_error.SetErrorString(llvm::formatv( + "language {0} does not support expression defined types", + language).str().c_str()); + return {}; } + + std::optional<CompilerType> type_op = + persistent->GetCompilerTypeFromPersistentDecl(const_typename); + if (type_op && (*type_op)) { + return SBType(*type_op); + } + sb_error.SetErrorString(llvm::formatv( + "no type {0} found in expression types for language {1}", + typename_cstr, language).str().c_str()); return {}; } @@ -1926,41 +1930,41 @@ SBTarget::FindExpressionVariableForLanguage(const char *varname_cstr, lldb::LanguageType language) { TargetSP target_sp = GetSP(); if (!target_sp) - return {}; + return ValueObjectConstResult::Create(nullptr, + Status::FromErrorStringWithFormatv( + "no variable {0} found for language {1}", + varname_cstr, language)); + if (!varname_cstr || !varname_cstr[0]) - return {}; + return ValueObjectConstResult::Create(target_sp.get(), + Status::FromErrorString("empty variable name for search.")); + + if (language == eLanguageTypeUnknown) + return ValueObjectConstResult::Create(nullptr, + Status::FromErrorStringWithFormatv( + "eLanguageTypeUnknown doesn't support expression variables.", + varname_cstr, language)); ConstString const_varname(varname_cstr); - // If the language type was "unknown" search all languages: - if (language == lldb::eLanguageTypeUnknown) { - LanguageSet expr_languages = - Language::GetLanguagesSupportingTypeSystemsForExpressions(); - for (int bit : expr_languages.bitvector.set_bits()) { - auto cur_lang = (LanguageType)bit; - PersistentExpressionState *persistent = - target_sp->GetPersistentExpressionStateForLanguage(cur_lang); - if (persistent) { - lldb::ExpressionVariableSP expr_var_sp = - persistent->GetVariable(const_varname); - if (expr_var_sp) { - return expr_var_sp->GetValueObject(); - } - } - } - } else { - PersistentExpressionState *persistent = + PersistentExpressionState *persistent = target_sp->GetPersistentExpressionStateForLanguage(language); - if (persistent) { - lldb::ExpressionVariableSP expr_var_sp = - persistent->GetVariable(const_varname); - if (expr_var_sp) { - return expr_var_sp->GetValueObject(); - } - } + if (!persistent) { + return ValueObjectConstResult::Create(target_sp.get(), + Status::FromErrorStringWithFormatv( + "language: {0} doesn't support expression variables.", language)); } - return {}; + + lldb::ExpressionVariableSP expr_var_sp = + persistent->GetVariable(const_varname); + if (expr_var_sp) + return expr_var_sp->GetValueObject(); + + return ValueObjectConstResult::Create(target_sp.get(), + Status::FromErrorStringWithFormatv( + "no variable {0} found for language {1}", varname_cstr, language)); + } lldb::SBType SBTarget::FindFirstType(const char *typename_cstr) { diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 03754e988faa4..077c403f4ea58 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -631,3 +631,9 @@ bool SourceLanguage::IsObjC() const { bool SourceLanguage::IsCPlusPlus() const { return name == llvm::dwarf::DW_LNAME_C_plus_plus; } + +void llvm::format_provider<lldb::LanguageType>::format( + const lldb::LanguageType &language, llvm::raw_ostream &OS, + llvm::StringRef Options) { + OS << Language::GetNameForLanguageType(language); +} diff --git a/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py index e41b314a2bf94..531366a3e96be 100644 --- a/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py +++ b/lldb/test/API/python_api/persistent_decls/TestPersistentDecls.py @@ -16,27 +16,15 @@ def test_persistent_types(self): """Define some types in the expression evaluator and find them.""" self.build() self.main_source_file = lldb.SBFileSpec("main.c") - self.types_test(False) - - def test_persistent_types_unknown(self): - """Define some types in the expression evaluator and find them.""" - self.build() - self.main_source_file = lldb.SBFileSpec("main.c") - self.types_test(True) + self.types_test() def test_persistent_values(self): """Define some values in the expression evaluator and find them.""" self.build() self.main_source_file = lldb.SBFileSpec("main.c") - self.values_test(False) - - def test_persistent_values_unknown(self): - """Define some values in the expression evaluator and find them.""" - self.build() - self.main_source_file = lldb.SBFileSpec("main.c") - self.values_test(True) + self.values_test() - def types_test(self, use_unknown): + def types_test(self): (target, _, _, _) = lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", self.main_source_file ) @@ -50,12 +38,10 @@ def types_test(self, use_unknown): self.expect(f"expr struct $AnotherType {{ int a; char *b; int c;}}") self.expect(f"expr struct $YetAnotherType {{ char *a; int b; int c;}}") - if use_unknown: - language = lldb.eLanguageTypeUnknown - else: - language = lldb.eLanguageTypeC_plus_plus - - type = target.FindExpressionTypeForLanguage(typename, language) + language = lldb.eLanguageTypeC_plus_plus + error = lldb.SBError() + type = target.FindExpressionTypeForLanguage(typename, language, error) + self.assertSuccess(error, "Got the right error") self.assertTrue(type.IsValid(), "Got a valid type") # Make sure we got the type we expected: @@ -75,7 +61,18 @@ def types_test(self, use_unknown): self.assertEqual(ivar_c.name, "c", "c name is right") self.assertEqual(ivar_c.type.name, "char *", "Right type") - def values_test(self, use_unknown): + # Also test the error returns: + #type = target.FindExpressionTypeForLanguage(typename, lldb.eLanguageTypeUnknown, error) + #self.assertFalse(error, "unknown doesn't support expression types") + #self.assertFalse(type.IsValid(), "Type is also invalid.") + type = target.FindExpressionTypeForLanguage("", language, error) + self.assertFalse(error.Success(), "empty type name for search.") + self.assertFalse(type.IsValid(), "Type is also invalid.") + type = target.FindExpressionTypeForLanguage("doesnt_start_with_dollar", language, error) + self.assertFalse(error.Success(), "Can't find names that don't exist.") + self.assertFalse(type.IsValid(), "Type is also invalid.") + + def values_test(self): (target, _, _, _) = lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", self.main_source_file ) @@ -89,15 +86,12 @@ def values_test(self, use_unknown): ) # Now try to find one: - if use_unknown: - language = lldb.eLanguageTypeUnknown - else: - language = lldb.eLanguageTypeC_plus_plus + language = lldb.eLanguageTypeC_plus_plus value = target.FindExpressionVariableForLanguage(value_name, language) self.assertTrue(value.IsValid(), "Got a valid SBValue") - self.assertTrue(value.GetError().Success(), "Got a value") + self.assertSuccess(value.GetError(), "Got no errors") value_checker = ValueCheck( name=value_name, @@ -108,3 +102,12 @@ def values_test(self, use_unknown): ], ) value_checker.check_value(self, value, "Found the right value") + + # Also test that errors are set correctly: + value = target.FindExpressionVariableForLanguage(value_name, lldb.eLanguageTypeUnknown) + self.assertFalse(value.error.Success(), "unknown can't compile expressions") + value = target.FindExpressionVariableForLanguage(None, language) + self.assertFalse(value.error.Success(), "reject empty expression variable name") + value = target.FindExpressionVariableForLanguage("doesnt_start_with_dollar", language) + self.assertFalse(value.error.Success(), "error on unknown variable name") + >From d4f944aacde4d2947b3eb651341f5880d6597a44 Mon Sep 17 00:00:00 2001 From: Jim Ingham <[email protected]> Date: Mon, 15 Jun 2026 16:45:42 -0700 Subject: [PATCH 4/5] Add LLDB_INSTRUMENT_VA macros to SBTarget additions. --- lldb/source/API/SBTarget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 98525fd684eed..356279513ac7a 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1883,6 +1883,7 @@ lldb::SBType SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, lldb::LanguageType language, SBError &sb_error) { + LLDB_INSTRUMENT_VA(this, typename_cstr, language, sb_error); sb_error.Clear(); TargetSP target_sp = GetSP(); @@ -1928,6 +1929,7 @@ SBTarget::FindExpressionTypeForLanguage(const char *typename_cstr, lldb::SBValue SBTarget::FindExpressionVariableForLanguage(const char *varname_cstr, lldb::LanguageType language) { + LLDB_INSTRUMENT_VA(this, varname_cstr, language); TargetSP target_sp = GetSP(); if (!target_sp) return ValueObjectConstResult::Create(nullptr, >From da648c7a941ccefecb59136080f51f68ad5bc9a1 Mon Sep 17 00:00:00 2001 From: Jim Ingham <[email protected]> Date: Mon, 15 Jun 2026 16:50:44 -0700 Subject: [PATCH 5/5] Copy-paste error. Apparently on macOS we don't assert for too many arguments to a formatv statement so I didn't see the error locally. --- lldb/source/API/SBTarget.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 356279513ac7a..4ac5bfcb9209e 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1944,9 +1944,8 @@ SBTarget::FindExpressionVariableForLanguage(const char *varname_cstr, if (language == eLanguageTypeUnknown) return ValueObjectConstResult::Create(nullptr, - Status::FromErrorStringWithFormatv( - "eLanguageTypeUnknown doesn't support expression variables.", - varname_cstr, language)); + Status::FromErrorString("eLanguageTypeUnknown doesn't support " + "expression variables.")); ConstString const_varname(varname_cstr); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
