https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/136766
>From daf394bf76b5fd627f77aee6e451e7d706d26916 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee....@gmail.com> Date: Tue, 22 Apr 2025 13:58:25 -0700 Subject: [PATCH 1/5] [lldb] Expose language plugin commands based based on language of current frame --- .../lldb/Interpreter/CommandInterpreter.h | 6 ++ .../source/Interpreter/CommandInterpreter.cpp | 55 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index 724d88d65f6ac..26e0767951e7f 100644 --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -730,6 +730,12 @@ class CommandInterpreter : public Broadcaster, bool EchoCommandNonInteractive(llvm::StringRef line, const Flags &io_handler_flags) const; + /// Return the language specific command object for the current frame. + /// + /// For example, when stopped on a C++ frame, this returns the command object + /// for "language cplusplus" (`CommandObjectMultiwordItaniumABI`). + lldb::CommandObjectSP GetFrameLanguageCommand() const; + // A very simple state machine which models the command handling transitions enum class CommandHandlingState { eIdle, diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index eb4741feb0aa5..2ff02ae5086b4 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -1018,6 +1018,26 @@ CommandInterpreter::VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command, return cur_as_multi; } +CommandObjectSP CommandInterpreter::GetFrameLanguageCommand() const { + if (auto frame_sp = GetExecutionContext().GetFrameSP()) { + auto frame_language = Language::GetPrimaryLanguage( + frame_sp->GuessLanguage().AsLanguageType()); + + auto it = m_command_dict.find("language"); + if (it != m_command_dict.end()) { + // The root "language" command. + CommandObjectSP language_cmd_sp = it->second; + + if (auto *plugin = Language::FindPlugin(frame_language)) { + // "cplusplus", "objc", etc. + auto lang_name = plugin->GetPluginName(); + return language_cmd_sp->GetSubcommandSPExact(lang_name); + } + } + } + return {}; +} + CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, bool exact, StringList *matches, @@ -1050,11 +1070,20 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, command_sp = pos->second; } + // The `language` subcommand ("language objc", "language cplusplus", etc). + CommandObjectMultiword *lang_subcmd = nullptr; + if (!command_sp) { + if (auto subcmd_sp = GetFrameLanguageCommand()) { + lang_subcmd = subcmd_sp->GetAsMultiwordCommand(); + command_sp = subcmd_sp->GetSubcommandSPExact(cmd_str); + } + } + if (!exact && !command_sp) { // We will only get into here if we didn't find any exact matches. CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp, - real_match_sp; + real_match_sp, lang_match_sp; StringList local_matches; if (matches == nullptr) @@ -1064,6 +1093,7 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, unsigned int num_alias_matches = 0; unsigned int num_user_matches = 0; unsigned int num_user_mw_matches = 0; + unsigned int num_lang_matches = 0; // Look through the command dictionaries one by one, and if we get only one // match from any of them in toto, then return that, otherwise return an @@ -1121,11 +1151,28 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, user_mw_match_sp = pos->second; } + if (lang_subcmd) { + num_lang_matches = + AddNamesMatchingPartialString(lang_subcmd->GetSubcommandDictionary(), + cmd_str, *matches, descriptions); + } + + if (num_lang_matches == 1) { + cmd.assign(matches->GetStringAtIndex(num_cmd_matches + num_alias_matches + + num_user_matches + + num_user_mw_matches)); + + auto &lang_dict = lang_subcmd->GetSubcommandDictionary(); + auto pos = lang_dict.find(cmd); + if (pos != lang_dict.end()) + lang_match_sp = pos->second; + } + // If we got exactly one match, return that, otherwise return the match // list. if (num_user_matches + num_user_mw_matches + num_cmd_matches + - num_alias_matches == + num_alias_matches + num_lang_matches == 1) { if (num_cmd_matches) return real_match_sp; @@ -1133,8 +1180,10 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, return alias_match_sp; else if (num_user_mw_matches) return user_mw_match_sp; - else + else if (num_user_matches) return user_match_sp; + else + return lang_match_sp; } } else if (matches && command_sp) { matches->AppendString(cmd_str); >From 1bbfd1276f9617f6df451700e0ed601c2bd0f341 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee....@gmail.com> Date: Wed, 23 Apr 2025 13:04:27 -0700 Subject: [PATCH 2/5] Add test --- .../API/commands/command/language/Makefile | 3 ++ .../language/TestFrameLanguageCommands.py | 43 +++++++++++++++++++ .../API/commands/command/language/lib.cpp | 3 ++ .../API/commands/command/language/main.mm | 6 +++ 4 files changed, 55 insertions(+) create mode 100644 lldb/test/API/commands/command/language/Makefile create mode 100644 lldb/test/API/commands/command/language/TestFrameLanguageCommands.py create mode 100644 lldb/test/API/commands/command/language/lib.cpp create mode 100644 lldb/test/API/commands/command/language/main.mm diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile new file mode 100644 index 0000000000000..ce845d59ac035 --- /dev/null +++ b/lldb/test/API/commands/command/language/Makefile @@ -0,0 +1,3 @@ +OBJCXX_SOURCES := main.mm +CXX_SOURCES := lib.cpp +include Makefile.rules diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py new file mode 100644 index 0000000000000..89439856470a7 --- /dev/null +++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py @@ -0,0 +1,43 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + _, _, thread, _ = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("lib.cpp") + ) + + frame = thread.selected_frame + self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeC_plus_plus_11) + self.assertEqual(frame.name, "f()") + self.expect( + "help demangle", + substrs=[ + "Demangle a C++ mangled name.", + "Syntax: language cplusplus demangle [<mangled-name> ...]", + ], + ) + self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()") + + # Switch the objc caller. + self.runCmd("up") + frame = thread.selected_frame + self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeObjC_plus_plus) + self.assertEqual(frame.name, "main") + self.expect("help demangle", error=True) + self.expect( + "help tagged-pointer", + substrs=[ + "Commands for operating on Objective-C tagged pointers.", + "Syntax: class-table <subcommand> [<subcommand-options>]", + ], + ) + self.expect( + "tagged-pointer info 0", + error=True, + startstr="error: could not convert '0' to a valid address", + ) diff --git a/lldb/test/API/commands/command/language/lib.cpp b/lldb/test/API/commands/command/language/lib.cpp new file mode 100644 index 0000000000000..225d2992d36d2 --- /dev/null +++ b/lldb/test/API/commands/command/language/lib.cpp @@ -0,0 +1,3 @@ +#include <stdio.h> +extern void f(); +void f() { puts("break here"); } diff --git a/lldb/test/API/commands/command/language/main.mm b/lldb/test/API/commands/command/language/main.mm new file mode 100644 index 0000000000000..93b87eb4d3176 --- /dev/null +++ b/lldb/test/API/commands/command/language/main.mm @@ -0,0 +1,6 @@ +extern void f(); + +int main() { + f(); + return 0; +} >From 57173b13cacd2dc0c593420748731b47ebee30eb Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee....@gmail.com> Date: Wed, 23 Apr 2025 13:33:16 -0700 Subject: [PATCH 3/5] Check prefix matching in the test --- .../language/TestFrameLanguageCommands.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py index 89439856470a7..5a4c62c5721bb 100644 --- a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py +++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py @@ -14,6 +14,8 @@ def test(self): frame = thread.selected_frame self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeC_plus_plus_11) self.assertEqual(frame.name, "f()") + + # Test `help`. self.expect( "help demangle", substrs=[ @@ -21,21 +23,21 @@ def test(self): "Syntax: language cplusplus demangle [<mangled-name> ...]", ], ) - self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()") - # Switch the objc caller. + # Run a `language cplusplus` command. + self.expect(f"demangle _Z1fv", startstr="_Z1fv ---> f()") + # Test prefix matching. + self.expect("dem _Z1fv", startstr="_Z1fv ---> f()") + + # Select the objc caller. self.runCmd("up") frame = thread.selected_frame self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeObjC_plus_plus) self.assertEqual(frame.name, "main") + + # Ensure `demangle` doesn't resolve from the objc frame. self.expect("help demangle", error=True) - self.expect( - "help tagged-pointer", - substrs=[ - "Commands for operating on Objective-C tagged pointers.", - "Syntax: class-table <subcommand> [<subcommand-options>]", - ], - ) + # Run a `language objc` command. self.expect( "tagged-pointer info 0", error=True, >From e49a373d678d697b532fb3fbb76dda912e53ad46 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee....@gmail.com> Date: Wed, 23 Apr 2025 16:10:35 -0700 Subject: [PATCH 4/5] Change Makefile to (hopefully) load the objc runtime --- lldb/test/API/commands/command/language/Makefile | 2 ++ .../API/commands/command/language/TestFrameLanguageCommands.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile index ce845d59ac035..48d511771b0a6 100644 --- a/lldb/test/API/commands/command/language/Makefile +++ b/lldb/test/API/commands/command/language/Makefile @@ -1,3 +1,5 @@ OBJCXX_SOURCES := main.mm +CFLAGS_EXTRAS := -fobjc-arc CXX_SOURCES := lib.cpp +LD_EXTRAS := -lobjc include Makefile.rules diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py index 5a4c62c5721bb..3936f06abeb80 100644 --- a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py +++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py @@ -25,7 +25,7 @@ def test(self): ) # Run a `language cplusplus` command. - self.expect(f"demangle _Z1fv", startstr="_Z1fv ---> f()") + self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()") # Test prefix matching. self.expect("dem _Z1fv", startstr="_Z1fv ---> f()") >From 0d267ec6eae56a5745d52338e0febbc33f32be9b Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee....@gmail.com> Date: Thu, 24 Apr 2025 10:11:24 -0700 Subject: [PATCH 5/5] Remove -fobjc-arc (not supported on linux) --- lldb/test/API/commands/command/language/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile index 48d511771b0a6..2d5049417ee70 100644 --- a/lldb/test/API/commands/command/language/Makefile +++ b/lldb/test/API/commands/command/language/Makefile @@ -1,5 +1,4 @@ OBJCXX_SOURCES := main.mm -CFLAGS_EXTRAS := -fobjc-arc CXX_SOURCES := lib.cpp LD_EXTRAS := -lobjc include Makefile.rules _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits