https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/94823
>From c2fea75364a0017be5e59020467d661bd00122ba Mon Sep 17 00:00:00 2001 From: Jim Ingham <jing...@apple.com> Date: Fri, 7 Jun 2024 17:36:34 -0700 Subject: [PATCH 1/4] Add the ability for Script based commands to specify their "repeat command". Among other things, returning an empty string as the repeat command disables auto-repeat, which can be useful for state-changing commands. --- lldb/bindings/python/python-wrapper.swig | 23 ++++++++ lldb/examples/python/cmdtemplate.py | 6 +- lldb/include/lldb/Interpreter/CommandObject.h | 4 ++ .../lldb/Interpreter/ScriptInterpreter.h | 5 ++ .../source/Commands/CommandObjectCommands.cpp | 26 ++++++++- lldb/source/Commands/CommandObjectThread.cpp | 2 +- .../Python/SWIGPythonBridge.h | 4 ++ .../Python/ScriptInterpreterPython.cpp | 27 +++++++++ .../Python/ScriptInterpreterPythonImpl.h | 14 +++-- .../script/add/TestAddParsedCommand.py | 55 +++++++++++++++++++ .../command/script/add/test_commands.py | 25 ++++++++- .../Python/PythonTestSuite.cpp | 6 ++ 12 files changed, 187 insertions(+), 10 deletions(-) diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 1370afc885d43..bb3c9433e2032 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -831,6 +831,29 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject( return true; } +std::optional<std::string> +lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, + std::string &command) { + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName<PythonCallable>("get_repeat_command"); + // If not implemented, repeat the exact command. + if (!pfunc.IsAllocated()) + return std::nullopt; + + PythonObject result; + PythonString command_str(command); + result = pfunc(command_str); + + // A return of None is the equivalent of nullopt - means repeat + // the command as is: + if (result.IsNone()) + return std::nullopt; + + return result.Str().GetString().str(); +} + #include "lldb/Interpreter/CommandReturnObject.h" bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( diff --git a/lldb/examples/python/cmdtemplate.py b/lldb/examples/python/cmdtemplate.py index 49a08365268f8..9a96888508b6f 100644 --- a/lldb/examples/python/cmdtemplate.py +++ b/lldb/examples/python/cmdtemplate.py @@ -19,7 +19,7 @@ class FrameStatCommand(ParsedCommand): @classmethod def register_lldb_command(cls, debugger, module_name): - ParsedCommandBase.do_register_cmd(cls, debugger, module_name) + ParsedCommand.do_register_cmd(cls, debugger, module_name) print( 'The "{0}" command has been installed, type "help {0}" or "{0} ' '--help" for detailed help.'.format(cls.program) @@ -72,6 +72,10 @@ def setup_command_definition(self): default = True, ) + def get_repeat_command(self, args): + """As an example, make the command not auto-repeat:""" + return "" + def get_short_help(self): return "Example command for use in debugging" diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h index a641a468b49d2..727ea0d963734 100644 --- a/lldb/include/lldb/Interpreter/CommandObject.h +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -296,6 +296,10 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> { /// /// \param[in] current_command_args /// The command arguments. + /// + /// \param[in] index + /// This is for internal use - it is how the completion request is tracked + /// in CommandObjectMultiword, and should otherwise be ignored. /// /// \return /// std::nullopt if there is no special repeat command - it will use the diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 932eaa8b8a4a2..934fd1837fcc0 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -480,6 +480,11 @@ class ScriptInterpreter : public PluginInterface { const lldb_private::ExecutionContext &exe_ctx) { return false; } + + virtual std::optional<std::string> GetRepeatCommandForScriptedCommand( + StructuredData::GenericSP impl_obj_sp, Args &args) { + return std::nullopt; + } virtual bool RunScriptFormatKeyword(const char *impl_function, Process *process, std::string &output, diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index f4903e373b086..4144876c0751f 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -1142,6 +1142,14 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } + std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { + ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); + if (!scripter) + return std::nullopt; + + return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); + } + llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectRaw::GetHelp(); @@ -1589,6 +1597,8 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { return error; } + size_t GetNumOptions() { return m_num_options; } + private: struct EnumValueStorage { EnumValueStorage() { @@ -1827,6 +1837,14 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } + std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { + ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); + if (!scripter) + return std::nullopt; + + return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); + } + llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectParsed::GetHelp(); @@ -1858,7 +1876,13 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { return CommandObjectParsed::GetHelpLong(); } - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { + // CommandObjectParsed requires that a command with no options return + // nullptr. + if (m_options.GetNumOptions() == 0) + return nullptr; + return &m_options; + } protected: diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index db96ee2cec383..772d76e8b148b 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -132,7 +132,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads { Options *GetOptions() override { return &m_options; } std::optional<std::string> GetRepeatCommand(Args ¤t_args, - uint32_t idx) override { + uint32_t index) override { llvm::StringRef count_opt("--count"); llvm::StringRef start_opt("--start"); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index c1a11b9134d62..c94fa7d82ab53 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -219,6 +219,10 @@ class SWIGBridge { StructuredDataImpl &args_impl, lldb_private::CommandReturnObject &cmd_retobj, lldb::ExecutionContextRefSP exe_ctx_ref_sp); + + static std::optional<std::string> + LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, + std::string &command); static bool LLDBSwigPythonCallModuleInit(const char *python_module_name, const char *session_dictionary_name, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 6e676de146b3d..c621ee8381ea9 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -2818,6 +2818,33 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand( return ret_val; } +std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand( + StructuredData::GenericSP impl_obj_sp, Args &args) { + if (!impl_obj_sp || !impl_obj_sp->IsValid()) + return std::nullopt; + + lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); + + if (!debugger_sp.get()) + return std::nullopt; + + std::optional<std::string> ret_val; + + { + Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + StructuredData::ArraySP args_arr_sp(new StructuredData::Array()); + + // For scripting commands, we send the command string: + std::string command; + args.GetQuotedCommandString(command); + ret_val = SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedCommand( + static_cast<PyObject *>(impl_obj_sp->GetValue()), command); + } + return ret_val; + +} /// In Python, a special attribute __doc__ contains the docstring for an object /// (function, method, class, ...) if any is defined Otherwise, the attribute's diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index fcd21dff612b1..f8c4873fafb41 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -182,12 +182,14 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb_private::CommandReturnObject &cmd_retobj, Status &error, const lldb_private::ExecutionContext &exe_ctx) override; - virtual bool RunScriptBasedParsedCommand( - StructuredData::GenericSP impl_obj_sp, Args& args, - ScriptedCommandSynchronicity synchronicity, - lldb_private::CommandReturnObject &cmd_retobj, Status &error, - const lldb_private::ExecutionContext &exe_ctx) override; - + bool RunScriptBasedParsedCommand( + StructuredData::GenericSP impl_obj_sp, Args& args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject &cmd_retobj, Status &error, + const lldb_private::ExecutionContext &exe_ctx) override; + + std::optional<std::string> GetRepeatCommandForScriptedCommand( + StructuredData::GenericSP impl_obj_sp, Args &args) override; Status GenerateFunction(const char *signature, const StringList &input, bool is_callback) override; diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py index d30b0b67124ed..10a54e9fed35f 100644 --- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py +++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py @@ -16,6 +16,11 @@ class ParsedCommandTestCase(TestBase): def test(self): self.pycmd_tests() + def setUp(self): + TestBase.setUp(self) + self.stdin_path = self.getBuildArtifact("stdin.txt") + self.stdout_path = self.getBuildArtifact("stdout.txt") + def check_help_options(self, cmd_name, opt_list, substrs=[]): """ Pass the command name in cmd_name and a vector of the short option, type & long option. @@ -32,6 +37,38 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]): print(f"Opt Vec\n{substrs}") self.expect("help " + cmd_name, substrs=substrs) + def run_one_repeat(self, commands, expected_num_errors): + with open(self.stdin_path, "w") as input_handle: + input_handle.write(commands) + + in_fileH = open(self.stdin_path, "r") + self.dbg.SetInputFileHandle(in_fileH, False) + + out_fileH = open(self.stdout_path, "w") + self.dbg.SetOutputFileHandle(out_fileH, False) + self.dbg.SetErrorFileHandle(out_fileH, False) + + options = lldb.SBCommandInterpreterRunOptions() + options.SetEchoCommands(False) + options.SetPrintResults(True) + options.SetPrintErrors(True) + options.SetAllowRepeats(True) + + n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter( + True, False, options, 0, False, False) + + in_fileH.close() + out_fileH.close() + + results = None + with open(self.stdout_path, "r") as out_fileH: + results = out_fileH.read() + + print(f"RESULTS:\n{results}\nDONE") + self.assertEqual(n_errors, expected_num_errors) + + return results + def pycmd_tests(self): source_dir = self.getSourceDir() test_file_path = os.path.join(source_dir, "test_commands.py") @@ -197,3 +234,21 @@ def cleanup(): "two-args 'First Argument' 'Second Argument'", substrs=["0: First Argument", "1: Second Argument"], ) + + # Now make sure get_repeat_command works properly: + + # no-args turns off auto-repeat + results = self.run_one_repeat("no-args\n\n", 1) + self.assertIn("No auto repeat", results, "Got auto-repeat error") + + # one-args does the normal repeat + results = self.run_one_repeat("one-arg-no-opt ONE_ARG\n\n", 0) + self.assertEqual(results.count("ONE_ARG"), 2, "We did a normal repeat") + + # two-args adds an argument: + results = self.run_one_repeat("two-args FIRST_ARG SECOND_ARG\n\n", 0) + self.assertEqual(results.count("FIRST_ARG"), 2, "Passed first arg to both commands") + self.assertEqual(results.count("SECOND_ARG"), 2, "Passed second arg to both commands") + self.assertEqual(results.count("THIRD_ARG"), 1, "Passed third arg in repeat") + + diff --git a/lldb/test/API/commands/command/script/add/test_commands.py b/lldb/test/API/commands/command/script/add/test_commands.py index 68f5a44556366..94d8bb241b2da 100644 --- a/lldb/test/API/commands/command/script/add/test_commands.py +++ b/lldb/test/API/commands/command/script/add/test_commands.py @@ -32,6 +32,12 @@ def __call__(self, debugger, args_array, exe_ctx, result): ) +# Use these to make sure that get_repeat_command sends the right +# command. +no_args_repeat = None +one_arg_repeat = None +two_arg_repeat = None + class NoArgsCommand(ReportingCmd): program = "no-args" @@ -96,6 +102,12 @@ def setup_command_definition(self): default="foo", ) + def get_repeat_command(self, command): + # No auto-repeat + global no_args_repeat + no_args_repeat = command + return "" + def get_short_help(self): return "Example command for use in debugging" @@ -118,6 +130,12 @@ def setup_command_definition(self): [self.ov_parser.make_argument_element(lldb.eArgTypeSourceFile, "plain")] ) + def get_repeat_command(self, command): + # Repeat the current command + global one_arg_repeat + one_arg_repeat = command + return None + def get_short_help(self): return "Example command for use in debugging" @@ -187,8 +205,13 @@ def setup_command_definition(self): ] ) + def get_repeat_command(self, command): + global two_arg_repeat + two_arg_repeat = command + return command + " THIRD_ARG" + def get_short_help(self): - return "Example command for use in debugging" + return "This is my short help string" def get_long_help(self): return self.help_string diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 23162436d42c9..03eb08af8182f 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -210,6 +210,12 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( return false; } +std::optional<std::string> +LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, + std::string &command) { + return std::nullopt; +} + bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit( const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP debugger) { >From e3f6b8ca2f05b0721b213f5a2af9e0b96d1229bf Mon Sep 17 00:00:00 2001 From: Jim Ingham <jing...@apple.com> Date: Fri, 7 Jun 2024 17:54:28 -0700 Subject: [PATCH 2/4] formatters --- lldb/include/lldb/Interpreter/CommandObject.h | 2 +- .../lldb/Interpreter/ScriptInterpreter.h | 7 ++--- .../source/Commands/CommandObjectCommands.cpp | 21 ++++++++------- .../Python/SWIGPythonBridge.h | 4 +-- .../Python/ScriptInterpreterPython.cpp | 8 +++--- .../Python/ScriptInterpreterPythonImpl.h | 17 ++++++------ .../script/add/TestAddParsedCommand.py | 27 ++++++++++--------- .../command/script/add/test_commands.py | 2 +- .../Python/PythonTestSuite.cpp | 2 +- 9 files changed, 48 insertions(+), 42 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h index 727ea0d963734..d48dbcdd5a5da 100644 --- a/lldb/include/lldb/Interpreter/CommandObject.h +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -296,7 +296,7 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> { /// /// \param[in] current_command_args /// The command arguments. - /// + /// /// \param[in] index /// This is for internal use - it is how the completion request is tracked /// in CommandObjectMultiword, and should otherwise be ignored. diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 934fd1837fcc0..ff3306b853a14 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -480,9 +480,10 @@ class ScriptInterpreter : public PluginInterface { const lldb_private::ExecutionContext &exe_ctx) { return false; } - - virtual std::optional<std::string> GetRepeatCommandForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, Args &args) { + + virtual std::optional<std::string> + GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp, + Args &args) { return std::nullopt; } diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index 4144876c0751f..c63445b7c8c86 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -1142,11 +1142,12 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } - std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { + std::optional<std::string> GetRepeatCommand(Args &args, + uint32_t index) override { ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); if (!scripter) return std::nullopt; - + return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); } @@ -1596,9 +1597,9 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { options.ForEach(add_element); return error; } - + size_t GetNumOptions() { return m_num_options; } - + private: struct EnumValueStorage { EnumValueStorage() { @@ -1837,14 +1838,15 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } - std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { + std::optional<std::string> GetRepeatCommand(Args &args, + uint32_t index) override { ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); if (!scripter) return std::nullopt; - + return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); } - + llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectParsed::GetHelp(); @@ -1875,16 +1877,15 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { SetHelpLong(docstring); return CommandObjectParsed::GetHelpLong(); } - + Options *GetOptions() override { // CommandObjectParsed requires that a command with no options return // nullptr. if (m_options.GetNumOptions() == 0) return nullptr; - return &m_options; + return &m_options; } - protected: void DoExecute(Args &args, CommandReturnObject &result) override { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index c94fa7d82ab53..7ee5dc5b0469e 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -219,10 +219,10 @@ class SWIGBridge { StructuredDataImpl &args_impl, lldb_private::CommandReturnObject &cmd_retobj, lldb::ExecutionContextRefSP exe_ctx_ref_sp); - + static std::optional<std::string> LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, - std::string &command); + std::string &command); static bool LLDBSwigPythonCallModuleInit(const char *python_module_name, const char *session_dictionary_name, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index c621ee8381ea9..bedb7c4189f2b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -2818,8 +2818,9 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand( return ret_val; } -std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, Args &args) { +std::optional<std::string> +ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand( + StructuredData::GenericSP impl_obj_sp, Args &args) { if (!impl_obj_sp || !impl_obj_sp->IsValid()) return std::nullopt; @@ -2829,7 +2830,7 @@ std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScrip return std::nullopt; std::optional<std::string> ret_val; - + { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); @@ -2843,7 +2844,6 @@ std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScrip static_cast<PyObject *>(impl_obj_sp->GetValue()), command); } return ret_val; - } /// In Python, a special attribute __doc__ contains the docstring for an object diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index f8c4873fafb41..0185a1a0422f6 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -183,14 +183,15 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { const lldb_private::ExecutionContext &exe_ctx) override; bool RunScriptBasedParsedCommand( - StructuredData::GenericSP impl_obj_sp, Args& args, - ScriptedCommandSynchronicity synchronicity, - lldb_private::CommandReturnObject &cmd_retobj, Status &error, - const lldb_private::ExecutionContext &exe_ctx) override; - - std::optional<std::string> GetRepeatCommandForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, Args &args) override; - + StructuredData::GenericSP impl_obj_sp, Args &args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject &cmd_retobj, Status &error, + const lldb_private::ExecutionContext &exe_ctx) override; + + std::optional<std::string> + GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp, + Args &args) override; + Status GenerateFunction(const char *signature, const StringList &input, bool is_callback) override; diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py index 10a54e9fed35f..381cdf2022f48 100644 --- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py +++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py @@ -40,33 +40,34 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]): def run_one_repeat(self, commands, expected_num_errors): with open(self.stdin_path, "w") as input_handle: input_handle.write(commands) - + in_fileH = open(self.stdin_path, "r") self.dbg.SetInputFileHandle(in_fileH, False) - + out_fileH = open(self.stdout_path, "w") self.dbg.SetOutputFileHandle(out_fileH, False) self.dbg.SetErrorFileHandle(out_fileH, False) - + options = lldb.SBCommandInterpreterRunOptions() options.SetEchoCommands(False) options.SetPrintResults(True) options.SetPrintErrors(True) options.SetAllowRepeats(True) - + n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter( - True, False, options, 0, False, False) - + True, False, options, 0, False, False + ) + in_fileH.close() out_fileH.close() - + results = None with open(self.stdout_path, "r") as out_fileH: results = out_fileH.read() print(f"RESULTS:\n{results}\nDONE") self.assertEqual(n_errors, expected_num_errors) - + return results def pycmd_tests(self): @@ -247,8 +248,10 @@ def cleanup(): # two-args adds an argument: results = self.run_one_repeat("two-args FIRST_ARG SECOND_ARG\n\n", 0) - self.assertEqual(results.count("FIRST_ARG"), 2, "Passed first arg to both commands") - self.assertEqual(results.count("SECOND_ARG"), 2, "Passed second arg to both commands") + self.assertEqual( + results.count("FIRST_ARG"), 2, "Passed first arg to both commands" + ) + self.assertEqual( + results.count("SECOND_ARG"), 2, "Passed second arg to both commands" + ) self.assertEqual(results.count("THIRD_ARG"), 1, "Passed third arg in repeat") - - diff --git a/lldb/test/API/commands/command/script/add/test_commands.py b/lldb/test/API/commands/command/script/add/test_commands.py index 94d8bb241b2da..fcde6cd3ef6dc 100644 --- a/lldb/test/API/commands/command/script/add/test_commands.py +++ b/lldb/test/API/commands/command/script/add/test_commands.py @@ -209,7 +209,7 @@ def get_repeat_command(self, command): global two_arg_repeat two_arg_repeat = command return command + " THIRD_ARG" - + def get_short_help(self): return "This is my short help string" diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 03eb08af8182f..dce86accf9691 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -212,7 +212,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( std::optional<std::string> LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, - std::string &command) { + std::string &command) { return std::nullopt; } >From dda7d78c51a64e2bf39c082cd2fb414a30be35ba Mon Sep 17 00:00:00 2001 From: Jim Ingham <jing...@apple.com> Date: Tue, 2 Jul 2024 14:05:28 -0700 Subject: [PATCH 3/4] Add docs for `get_repeat_command`. --- lldb/docs/use/python-reference.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst index 795e38fab3794..041e541a96f08 100644 --- a/lldb/docs/use/python-reference.rst +++ b/lldb/docs/use/python-reference.rst @@ -562,6 +562,18 @@ which should implement the following interface: this call should return the short help text for this command[1] def get_long_help(self): this call should return the long help text for this command[1] + def get_repeat_command(self, command): + The auto-repeat command is what will get executed when the user types just + a return at the next prompt after this command is run. Even if your command + was run because it was specified as a repeat command, that invocation will still + get asked for IT'S repeat command, so you can chain a series of repeats, for instance + to implement a pager. + + The command argument is the command that is about to be executed. + + If this call returns None, then the ordinary repeat mechanism will be used + If this call returns an empty string, then auto-repeat is disabled + If this call returns any other string, that will be the repeat command [1] [1] This method is optional. >From 461007259ecf8850f23aa23b6c9bd505df72e7f5 Mon Sep 17 00:00:00 2001 From: Jim Ingham <jing...@apple.com> Date: Tue, 2 Jul 2024 14:10:31 -0700 Subject: [PATCH 4/4] Remove some debugging print's, responding to review comments. --- lldb/bindings/python/python-wrapper.swig | 3 +-- .../API/commands/command/script/add/TestAddParsedCommand.py | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index bb3c9433e2032..86f8de0b4a8b9 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -842,9 +842,8 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedComma if (!pfunc.IsAllocated()) return std::nullopt; - PythonObject result; PythonString command_str(command); - result = pfunc(command_str); + PythonObject result = pfunc(command_str); // A return of None is the equivalent of nullopt - means repeat // the command as is: diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py index 381cdf2022f48..c7680e9bb7f41 100644 --- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py +++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py @@ -34,7 +34,6 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]): else: (short_opt, type, long_opt) = elem substrs.append(f"-{short_opt} <{type}> ( --{long_opt} <{type}> )") - print(f"Opt Vec\n{substrs}") self.expect("help " + cmd_name, substrs=substrs) def run_one_repeat(self, commands, expected_num_errors): @@ -65,7 +64,6 @@ def run_one_repeat(self, commands, expected_num_errors): with open(self.stdout_path, "r") as out_fileH: results = out_fileH.read() - print(f"RESULTS:\n{results}\nDONE") self.assertEqual(n_errors, expected_num_errors) return results @@ -206,9 +204,6 @@ def cleanup(): num_completions = interp.HandleCompletionWithDescriptions( cmd_str, len(cmd_str) - 1, 0, 1000, matches, descriptions ) - print( - f"First: {matches.GetStringAtIndex(0)}\nSecond: {matches.GetStringAtIndex(1)}\nThird: {matches.GetStringAtIndex(2)}" - ) self.assertEqual(num_completions, 1, "Only one completion for source file") self.assertEqual(matches.GetSize(), 2, "The first element is the complete line") self.assertEqual( _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits