https://github.com/medismailben created https://github.com/llvm/llvm-project/pull/210430
Give both `command script add -c` (raw class) and `-P` (ParsedCommand) a single, shared `ScriptedCommandInterface`/`ScriptedCommandPythonInterface`, since they are instantiated through the same `CreateScriptCommandObject`-style call path and already shared several ad-hoc methods verbatim (help text, repeat-command, flags, option/argument definitions). Add a lighter `ScriptedCommand` ABC template (`scripted_command.py`) for raw mode, keeping the existing `ParsedCommand`/`LLDBOptionValueParser` classes as-is. The interface plugs into the existing `ScriptedPythonInterface` machinery: `CreatePluginObject` delegates to the base template so class resolution, argument-count relaxation, and abstract-method validation all go through the shared code path. `RunRawCommand`, `RunParsedCommand`, `GetRepeatCommand`, `HandleArgumentCompletion`, and `HandleOptionArgumentCompletion` resolve `__call__` and method by name on the implementor and dispatch inline using `SWIGBridge::ToSWIGWrapper` helpers, since their locker configuration (`InitSession`/`TearDownSession` and interactive-mode-conditional `NoSTDIN`) does not fit the base's generic `Dispatch<T>()`. This lets us delete the standalone SWIG bridge functions this plugin was the sole caller of. Add a `Transform(lldb::DebuggerSP)` overload in `ScriptedPythonInterface` so that a `DebuggerSP` argument to `CreatePluginObject` gets wrapped in `SBDebugger` via `SWIGBridge::ToSWIGWrapper` on its way to the Python `__init__`. `ScriptedCommandPythonInterface` caches the `DebuggerSP` passed into `CreatePluginObject`, since `ScriptInterpreterPythonImpl` has no accessor for the debugger that owns it. `CommandObjectScriptingObjectRaw` and `CommandObjectScriptingObjectParsed` now hold a `ScriptedCommandInterfaceSP` instead of a raw `StructuredData::GenericSP`, and `command script add`'s `DoExecute` constructs the interface once via `CreateScriptedCommandInterface()->CreatePluginObject(...)` regardless of `-P` vs `-c`, handing it to whichever `CommandObject` subclass is chosen. `command script add -f` (function-based raw commands) is unchanged: it already has a class-based alternative in `-c`, so there is no user-facing gap to migrate. >From 6bdce549a6a71f761398a343538b77f9d4073b21 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Fri, 17 Jul 2026 11:05:57 -0700 Subject: [PATCH] [lldb/script] Migrate ParsedCommand and raw class commands onto ScriptedPythonInterface Give both `command script add -c` (raw class) and `-P` (ParsedCommand) a single, shared `ScriptedCommandInterface`/`ScriptedCommandPythonInterface`, since they are instantiated through the same `CreateScriptCommandObject`-style call path and already shared several ad-hoc methods verbatim (help text, repeat-command, flags, option/argument definitions). Add a lighter `ScriptedCommand` ABC template (`scripted_command.py`) for raw mode, keeping the existing `ParsedCommand`/`LLDBOptionValueParser` classes as-is. The interface plugs into the existing `ScriptedPythonInterface` machinery: `CreatePluginObject` delegates to the base template so class resolution, argument-count relaxation, and abstract-method validation all go through the shared code path. `RunRawCommand`, `RunParsedCommand`, `GetRepeatCommand`, `HandleArgumentCompletion`, and `HandleOptionArgumentCompletion` resolve `__call__` and method by name on the implementor and dispatch inline using `SWIGBridge::ToSWIGWrapper` helpers, since their locker configuration (`InitSession`/`TearDownSession` and interactive-mode-conditional `NoSTDIN`) does not fit the base's generic `Dispatch<T>()`. This lets us delete the standalone SWIG bridge functions this plugin was the sole caller of. Add a `Transform(lldb::DebuggerSP)` overload in `ScriptedPythonInterface` so that a `DebuggerSP` argument to `CreatePluginObject` gets wrapped in `SBDebugger` via `SWIGBridge::ToSWIGWrapper` on its way to the Python `__init__`. `ScriptedCommandPythonInterface` caches the `DebuggerSP` passed into `CreatePluginObject`, since `ScriptInterpreterPythonImpl` has no accessor for the debugger that owns it. `CommandObjectScriptingObjectRaw` and `CommandObjectScriptingObjectParsed` now hold a `ScriptedCommandInterfaceSP` instead of a raw `StructuredData::GenericSP`, and `command script add`'s `DoExecute` constructs the interface once via `CreateScriptedCommandInterface()->CreatePluginObject(...)` regardless of `-P` vs `-c`, handing it to whichever `CommandObject` subclass is chosen. `command script add -f` (function-based raw commands) is unchanged: it already has a class-based alternative in `-c`, so there is no user-facing gap to migrate. Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/bindings/python/CMakeLists.txt | 1 + lldb/bindings/python/python-wrapper.swig | 158 ------ lldb/docs/CMakeLists.txt | 1 + .../python/templates/scripted_command.py | 86 +++ .../Interfaces/ScriptedCommandInterface.h | 83 +++ .../lldb/Interpreter/ScriptInterpreter.h | 82 +-- lldb/include/lldb/lldb-enumerations.h | 4 +- lldb/include/lldb/lldb-forward.h | 3 + .../source/Commands/CommandObjectCommands.cpp | 198 +++---- lldb/source/Interpreter/ScriptInterpreter.cpp | 6 + .../ScriptInterpreter/Python/CMakeLists.txt | 1 + .../ScriptInterpreterPythonInterfaces.cpp | 2 + .../ScriptInterpreterPythonInterfaces.h | 1 + .../ScriptedCommandPythonInterface.cpp | 233 ++++++++ .../ScriptedCommandPythonInterface.h | 81 +++ .../Interfaces/ScriptedPythonInterface.cpp | 54 ++ .../Interfaces/ScriptedPythonInterface.h | 19 + .../Python/SWIGPythonBridge.h | 29 - .../Python/ScriptInterpreterPython.cpp | 503 +----------------- .../Python/ScriptInterpreterPythonImpl.h | 52 +- .../Python/PythonTestSuite.cpp | 41 -- 21 files changed, 673 insertions(+), 965 deletions(-) create mode 100644 lldb/examples/python/templates/scripted_command.py create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedCommandInterface.h create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.h diff --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt index d29b143c1408c..6ffdf9ccafabc 100644 --- a/lldb/bindings/python/CMakeLists.txt +++ b/lldb/bindings/python/CMakeLists.txt @@ -120,6 +120,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_breakpoint.py" "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_hook.py" "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_stackframe_recognizer.py" + "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_command.py" ) if(APPLE) diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 2392737402e20..971298d8e4c78 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -213,25 +213,6 @@ PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProv return PythonObject(); } -PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject( - const char *python_class_name, const char *session_dictionary_name, - lldb::DebuggerSP debugger_sp) { - if (python_class_name == NULL || python_class_name[0] == '\0' || - !session_dictionary_name) - return PythonObject(); - - PyErr_Cleaner py_err_cleaner(true); - auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>( - session_dictionary_name); - auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>( - python_class_name, dict); - - if (!pfunc.IsAllocated()) - return PythonObject(); - - return pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger_sp)), dict); -} - // wrapper that calls an optional instance member of an object taking no // arguments static PyObject *LLDBSwigPython_CallOptionalMember( @@ -639,145 +620,6 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand( return true; } -bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject( - PyObject *implementor, lldb::DebuggerSP debugger, const char *args, - lldb_private::CommandReturnObject &cmd_retobj, - lldb::ExecutionContextRefSP exe_ctx_ref_sp) { - - PyErr_Cleaner py_err_cleaner(true); - - PythonObject self(PyRefType::Borrowed, implementor); - auto pfunc = self.ResolveName<PythonCallable>("__call__"); - - if (!pfunc.IsAllocated()) - return false; - - auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj); - - pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger)), PythonString(args), - SWIGBridge::ToSWIGWrapper(exe_ctx_ref_sp), cmd_retobj_arg.obj()); - - 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; - - PythonString command_str(command); - PythonObject 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(); -} - -StructuredData::DictionarySP -lldb_private::python::SWIGBridge::LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(PyObject *implementor, - std::vector<llvm::StringRef> &args_vec, size_t args_pos, size_t pos_in_arg) { - - PyErr_Cleaner py_err_cleaner(true); - - PythonObject self(PyRefType::Borrowed, implementor); - auto pfunc = self.ResolveName<PythonCallable>("handle_argument_completion"); - // If this isn't implemented, return an empty dict to signal falling back to default completion: - if (!pfunc.IsAllocated()) - return {}; - - PythonList args_list(PyInitialValue::Empty); - for (auto elem : args_vec) - args_list.AppendItem(PythonString(elem)); - - PythonObject result = pfunc(args_list, PythonInteger(args_pos), PythonInteger(pos_in_arg)); - // Returning None means do the ordinary completion - if (result.IsNone()) - return {}; - - // Convert the return dictionary to a DictionarySP. - StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject(); - if (!result_obj_sp) - return {}; - - StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp)); - if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid) - return {}; - return dict_sp; -} - -StructuredData::DictionarySP -lldb_private::python::SWIGBridge::LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(PyObject *implementor, - llvm::StringRef &long_option, size_t pos_in_arg) { - - PyErr_Cleaner py_err_cleaner(true); - - PythonObject self(PyRefType::Borrowed, implementor); - auto pfunc = self.ResolveName<PythonCallable>("handle_option_argument_completion"); - // If this isn't implemented, return an empty dict to signal falling back to default completion: - if (!pfunc.IsAllocated()) - return {}; - - PythonObject result = pfunc(PythonString(long_option), PythonInteger(pos_in_arg)); - // Returning None means do the ordinary completion - if (result.IsNone()) - return {}; - - // Returning a boolean: - // True means the completion was handled, but there were no completions - // False means that the completion was not handled, again, do the ordinary completion: - if (result.GetObjectType() == PyObjectType::Boolean) { - if (!result.IsTrue()) - return {}; - // Make up a completion dictionary with the right element: - StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary()); - dict_sp->AddBooleanItem("no-completion", true); - return dict_sp; - } - - - // Convert the return dictionary to a DictionarySP. - StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject(); - if (!result_obj_sp) - return {}; - - StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp)); - if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid) - return {}; - return dict_sp; -} - -#include "lldb/Interpreter/CommandReturnObject.h" - -bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( - PyObject *implementor, lldb::DebuggerSP debugger, lldb_private::StructuredDataImpl &args_impl, - lldb_private::CommandReturnObject &cmd_retobj, - lldb::ExecutionContextRefSP exe_ctx_ref_sp) { - - PyErr_Cleaner py_err_cleaner(true); - - PythonObject self(PyRefType::Borrowed, implementor); - auto pfunc = self.ResolveName<PythonCallable>("__call__"); - - if (!pfunc.IsAllocated()) { - cmd_retobj.AppendError("Could not find '__call__' method in implementation class"); - return false; - } - - pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger)), SWIGBridge::ToSWIGWrapper(args_impl), - SWIGBridge::ToSWIGWrapper(exe_ctx_ref_sp), SWIGBridge::ToSWIGWrapper(cmd_retobj).obj()); - - return true; -} - PythonObject lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin( const char *python_class_name, const char *session_dictionary_name, const lldb::ProcessSP &process_sp) { diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt index dd091836dc1aa..53647990f7842 100644 --- a/lldb/docs/CMakeLists.txt +++ b/lldb/docs/CMakeLists.txt @@ -33,6 +33,7 @@ if (LLDB_ENABLE_PYTHON AND SPHINX_FOUND) COMMAND "${CMAKE_COMMAND}" -E copy "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_breakpoint.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/" COMMAND "${CMAKE_COMMAND}" -E copy "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_hook.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/" COMMAND "${CMAKE_COMMAND}" -E copy "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_stackframe_recognizer.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/" + COMMAND "${CMAKE_COMMAND}" -E copy "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_command.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/" COMMENT "Copying lldb.py to pretend its a Python package.") add_dependencies(lldb-python-doc-package swig_wrapper_python) diff --git a/lldb/examples/python/templates/scripted_command.py b/lldb/examples/python/templates/scripted_command.py new file mode 100644 index 0000000000000..d9b18ea4376aa --- /dev/null +++ b/lldb/examples/python/templates/scripted_command.py @@ -0,0 +1,86 @@ +from abc import ABCMeta, abstractmethod +from typing import Optional + +import lldb + + +class ScriptedCommand(metaclass=ABCMeta): + """ + The base class for a scripted (raw) command. + + A raw command receives the unparsed argument string exactly as the user + typed it, and is responsible for any parsing it needs. For a command + with a table-driven option/argument parser, see `ParsedCommand` instead. + Register it with `command script add -c <ClassName> ...`. + + Most of the base class methods are `@abstractmethod` that need to be + overwritten by the inheriting class. + """ + + def __init__(self, debugger: lldb.SBDebugger): + """Construct a scripted command. + + Args: + debugger (lldb.SBDebugger): The debugger this command is being + added to. + """ + pass + + @abstractmethod + def __call__( + self, + debugger: lldb.SBDebugger, + args: str, + exe_ctx: lldb.SBExecutionContext, + result: lldb.SBCommandReturnObject, + ) -> None: + """Execute the command. + + Args: + debugger (lldb.SBDebugger): The debugger the command runs + against. + args (str): The raw, unparsed argument string. + exe_ctx (lldb.SBExecutionContext): The execution context. + result (lldb.SBCommandReturnObject): Write command output/errors + here. + """ + pass + + def get_short_help(self) -> Optional[str]: + """A one-line description shown by `help`. + + Returns: + str: The short help string. + """ + pass + + def get_long_help(self) -> Optional[str]: + """The full help text shown by `help <command>`. + + Returns: + str: The long help string. + """ + pass + + def get_flags(self) -> int: + """Command flags (a bitmask of `lldb.eCommandRequires*`/ + `lldb.eCommandProcessMustBe*` etc.) controlling when this command is + available. + + Returns: + int: The flags bitmask. Defaults to 0 (no restrictions). + """ + return 0 + + def get_repeat_command(self, command: str) -> Optional[str]: + """Customize what runs when the user presses Enter to repeat this + command. + + Args: + command (str): The command line that was run. + + Returns: + str: The command line to run on repeat. Defaults to `None`, + meaning repeat the original command unmodified. + """ + pass diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedCommandInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedCommandInterface.h new file mode 100644 index 0000000000000..a302c8d766eb9 --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedCommandInterface.h @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDCOMMANDINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDCOMMANDINTERFACE_H + +#include "ScriptedInterface.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { +/// Interface for a scripted command, covering both the raw (`command script +/// add -c`, `__call__(debugger, args, exe_ctx, result)`) and parsed +/// (`command script add -P`, option/argument table driven) calling +/// conventions. Both are instantiated through the same Python class lookup, +/// and differ only in which of RunRawCommand/RunParsedCommand gets called. +class ScriptedCommandInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, + lldb::DebuggerSP debugger_sp) = 0; + + virtual bool RunRawCommand(llvm::StringRef args, + ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) { + return false; + } + + virtual bool RunParsedCommand(Args &args, + ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) { + return false; + } + + virtual std::optional<std::string> GetRepeatCommand(Args &args) { + return std::nullopt; + } + + virtual StructuredData::DictionarySP + HandleArgumentCompletion(std::vector<llvm::StringRef> &args, size_t args_pos, + size_t char_in_arg) { + return {}; + } + + virtual StructuredData::DictionarySP + HandleOptionArgumentCompletion(llvm::StringRef &long_option, + size_t char_in_arg) { + return {}; + } + + virtual bool GetShortHelp(std::string &dest) { + dest.clear(); + return false; + } + + virtual bool GetLongHelp(std::string &dest) { + dest.clear(); + return false; + } + + virtual uint32_t GetFlags() { return 0; } + + virtual StructuredData::ObjectSP GetOptionsDefinition() { return {}; } + + virtual StructuredData::ObjectSP GetArgumentsDefinition() { return {}; } + + virtual void OptionParsingStarted() {} + + virtual bool SetOptionValue(ExecutionContext *exe_ctx, + llvm::StringRef long_option, + llvm::StringRef value) { + return false; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDCOMMANDINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 122e779bf0279..ebb0e8b310388 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -243,11 +243,6 @@ class ScriptInterpreter : public PluginInterface { return StructuredData::ObjectSP(); } - virtual StructuredData::GenericSP - CreateScriptCommandObject(const char *class_name) { - return StructuredData::GenericSP(); - } - virtual StructuredData::ObjectSP LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) { return StructuredData::ObjectSP(); @@ -374,42 +369,6 @@ class ScriptInterpreter : public PluginInterface { return false; } - virtual bool RunScriptBasedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef args, - ScriptedCommandSynchronicity synchronicity, - lldb_private::CommandReturnObject &cmd_retobj, Status &error, - const lldb_private::ExecutionContext &exe_ctx) { - return false; - } - - 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) { - return false; - } - - virtual std::optional<std::string> - GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp, - Args &args) { - return std::nullopt; - } - - virtual StructuredData::DictionarySP - HandleArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args, - size_t args_pos, size_t char_in_arg) { - return {}; - } - - virtual StructuredData::DictionarySP - HandleOptionArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_name, - size_t char_in_arg) { - return {}; - } - virtual bool RunScriptFormatKeyword(const char *impl_function, Process *process, std::string &output, Status &error) { @@ -448,43 +407,6 @@ class ScriptInterpreter : public PluginInterface { return false; } - virtual bool - GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, - std::string &dest) { - dest.clear(); - return false; - } - - virtual StructuredData::ObjectSP - GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) { - return {}; - } - - virtual StructuredData::ObjectSP - GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) { - return {}; - } - - virtual bool SetOptionValueForCommandObject( - StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx, - llvm::StringRef long_option, llvm::StringRef value) { - return false; - } - - virtual void - OptionParsingStartedForCommandObject(StructuredData::GenericSP cmd_obj_sp) {} - - virtual uint32_t - GetFlagsForCommandObject(StructuredData::GenericSP cmd_obj_sp) { - return 0; - } - - virtual bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, - std::string &dest) { - dest.clear(); - return false; - } - virtual bool CheckObjectExists(const char *name) { return false; } virtual bool @@ -561,6 +483,10 @@ class ScriptInterpreter : public PluginInterface { return {}; } + virtual lldb::ScriptedCommandInterfaceSP CreateScriptedCommandInterface() { + return {}; + } + virtual StructuredData::ObjectSP CreateStructuredDataFromScriptObject(ScriptObject obj) { return {}; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 93c252b55de99..f73d0085f2320 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -268,7 +268,9 @@ enum ScriptedExtension { eScriptedExtensionScriptedThread, eScriptedExtensionScriptedFrame, eScriptedExtensionScriptedStackFrameRecognizer, - kLastScriptedExtension = eScriptedExtensionScriptedStackFrameRecognizer + eScriptedExtensionScriptedCommand, + eScriptedExtensionParsedCommand, + kLastScriptedExtension = eScriptedExtensionParsedCommand }; /// Register numbering types. diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 157aa5743f016..2572aa0dc344b 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -193,6 +193,7 @@ class ScriptedFrameInterface; class ScriptedFrameProviderInterface; class ScriptedMetadata; class ScriptedBreakpointInterface; +class ScriptedCommandInterface; class ScriptedHookInterface; class ScriptedPlatformInterface; class ScriptedProcessInterface; @@ -438,6 +439,8 @@ typedef std::shared_ptr<lldb_private::ScriptedBreakpointInterface> ScriptedBreakpointInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedStackFrameRecognizerInterface> ScriptedStackFrameRecognizerInterfaceSP; +typedef std::shared_ptr<lldb_private::ScriptedCommandInterface> + ScriptedCommandInterfaceSP; typedef std::shared_ptr<lldb_private::Section> SectionSP; typedef std::unique_ptr<lldb_private::SectionList> SectionListUP; typedef std::weak_ptr<lldb_private::Section> SectionWP; diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index 8f006768ecc9a..d59de4d980803 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -16,6 +16,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Interfaces/ScriptedCommandInterface.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueString.h" @@ -1119,19 +1120,19 @@ class CommandObjectPythonFunction : public CommandObjectRaw { /// substitution). class CommandObjectScriptingObjectRaw : public CommandObjectRaw { public: - CommandObjectScriptingObjectRaw(CommandInterpreter &interpreter, - std::string name, - StructuredData::GenericSP cmd_obj_sp, - ScriptedCommandSynchronicity synch, - CompletionType completion_type) - : CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp), - m_synchro(synch), m_fetched_help_short(false), - m_fetched_help_long(false), m_completion_type(completion_type) { + CommandObjectScriptingObjectRaw( + CommandInterpreter &interpreter, std::string name, + lldb::ScriptedCommandInterfaceSP cmd_interface_sp, + ScriptedCommandSynchronicity synch, CompletionType completion_type) + : CommandObjectRaw(interpreter, name), + m_cmd_interface_sp(cmd_interface_sp), m_synchro(synch), + m_fetched_help_short(false), m_fetched_help_long(false), + m_completion_type(completion_type) { StreamString stream; stream.Printf("For more information run 'help %s'", name.c_str()); SetHelp(stream.GetString()); - if (ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter()) - GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); + if (m_cmd_interface_sp) + GetFlags().Set(m_cmd_interface_sp->GetFlags()); } ~CommandObjectScriptingObjectRaw() override = default; @@ -1151,22 +1152,19 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return std::nullopt; - return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); + return m_cmd_interface_sp->GetRepeatCommand(args); } llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectRaw::GetHelp(); - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return CommandObjectRaw::GetHelp(); std::string docstring; - m_fetched_help_short = - scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring); + m_fetched_help_short = m_cmd_interface_sp->GetShortHelp(docstring); if (!docstring.empty()) SetHelp(docstring); @@ -1177,13 +1175,11 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { if (m_fetched_help_long) return CommandObjectRaw::GetHelpLong(); - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return CommandObjectRaw::GetHelpLong(); std::string docstring; - m_fetched_help_long = - scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring); + m_fetched_help_long = m_cmd_interface_sp->GetLongHelp(docstring); if (!docstring.empty()) SetHelpLong(docstring); return CommandObjectRaw::GetHelpLong(); @@ -1192,15 +1188,13 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { protected: void DoExecute(llvm::StringRef raw_command_line, CommandReturnObject &result) override { - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - Status error; result.SetStatus(eReturnStatusInvalid); - if (!scripter || - !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line, - m_synchro, result, error, m_exe_ctx)) { + if (!m_cmd_interface_sp || + !m_cmd_interface_sp->RunRawCommand(raw_command_line, m_synchro, result, + error, m_exe_ctx)) { result.AppendError(error.AsCString()); } else { // Don't change the status if the command already set it... @@ -1214,7 +1208,7 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw { } private: - StructuredData::GenericSP m_cmd_obj_sp; + lldb::ScriptedCommandInterfaceSP m_cmd_interface_sp; ScriptedCommandSynchronicity m_synchro; bool m_fetched_help_short : 1; bool m_fetched_help_long : 1; @@ -1238,23 +1232,16 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { private: class CommandOptions : public Options { public: - CommandOptions(CommandInterpreter &interpreter, - StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), - m_cmd_obj_sp(cmd_obj_sp) {} + CommandOptions(CommandInterpreter &interpreter, + lldb::ScriptedCommandInterfaceSP cmd_interface_sp) + : m_interpreter(interpreter), m_cmd_interface_sp(cmd_interface_sp) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; - ScriptInterpreter *scripter = - m_interpreter.GetDebugger().GetScriptInterpreter(); - if (!scripter) { - return Status::FromErrorString( - "No script interpreter for SetOptionValue."); - return error; - } - if (!m_cmd_obj_sp) { + if (!m_cmd_interface_sp) { return Status::FromErrorString( "SetOptionValue called with empty cmd_obj."); return error; @@ -1268,10 +1255,10 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { // Pass the long option, since you aren't actually required to have a // short_option, and for those options the index or short option character // aren't meaningful on the python side. - const char * long_option = - m_options_definition_up.get()[option_idx].long_option; - bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, - execution_context, long_option, option_arg); + const char *long_option = + m_options_definition_up.get()[option_idx].long_option; + bool success = m_cmd_interface_sp->SetOptionValue( + execution_context, long_option, option_arg); if (!success) return Status::FromErrorStringWithFormatv( "Error setting option: {0} to {1}", long_option, option_arg); @@ -1279,12 +1266,10 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { } void OptionParsingStarting(ExecutionContext *execution_context) override { - ScriptInterpreter *scripter = - m_interpreter.GetDebugger().GetScriptInterpreter(); - if (!scripter || !m_cmd_obj_sp) + if (!m_cmd_interface_sp) return; - scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp); + m_cmd_interface_sp->OptionParsingStarted(); } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -1292,7 +1277,7 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { return {}; return llvm::ArrayRef(m_options_definition_up.get(), m_num_options); } - + static Status ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, size_t counter, uint32_t &usage_mask) { // If the usage entry is not provided, we use LLDB_OPT_SET_ALL. @@ -1726,10 +1711,7 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { OptionElementVector &option_vec, int opt_element_index, CommandInterpreter &interpreter) override { - ScriptInterpreter *scripter = - interpreter.GetDebugger().GetScriptInterpreter(); - - if (!scripter) + if (!m_cmd_interface_sp) return; ExecutionContext exe_ctx = interpreter.GetExecutionContext(); @@ -1746,9 +1728,8 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { // regular option completer handle that: StructuredData::DictionarySP completion_dict_sp; if (!is_enum) - completion_dict_sp = - scripter->HandleOptionArgumentCompletionForScriptedCommand( - m_cmd_obj_sp, option_name, request.GetCursorCharPos()); + completion_dict_sp = m_cmd_interface_sp->HandleOptionArgumentCompletion( + option_name, request.GetCursorCharPos()); if (!completion_dict_sp) { Options::HandleOptionArgumentCompletion(request, option_vec, @@ -1810,18 +1791,17 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { std::vector<std::vector<OptionEnumValueElement>> m_enum_vector; std::vector<std::string> m_usage_container; CommandInterpreter &m_interpreter; - StructuredData::GenericSP m_cmd_obj_sp; + lldb::ScriptedCommandInterfaceSP m_cmd_interface_sp; static std::unordered_set<std::string> g_string_storer; }; public: - static CommandObjectSP Create(CommandInterpreter &interpreter, - std::string name, - StructuredData::GenericSP cmd_obj_sp, - ScriptedCommandSynchronicity synch, - CommandReturnObject &result) { + static CommandObjectSP + Create(CommandInterpreter &interpreter, std::string name, + lldb::ScriptedCommandInterfaceSP cmd_interface_sp, + ScriptedCommandSynchronicity synch, CommandReturnObject &result) { CommandObjectSP new_cmd_sp(new CommandObjectScriptingObjectParsed( - interpreter, name, cmd_obj_sp, synch)); + interpreter, name, cmd_interface_sp, synch)); CommandObjectScriptingObjectParsed *parsed_cmd = static_cast<CommandObjectScriptingObjectParsed *>(new_cmd_sp.get()); @@ -1843,34 +1823,33 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { return new_cmd_sp; } - CommandObjectScriptingObjectParsed(CommandInterpreter &interpreter, - std::string name, - StructuredData::GenericSP cmd_obj_sp, - ScriptedCommandSynchronicity synch) - : CommandObjectParsed(interpreter, name.c_str()), - m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch), - m_options(interpreter, cmd_obj_sp), m_fetched_help_short(false), + CommandObjectScriptingObjectParsed( + CommandInterpreter &interpreter, std::string name, + lldb::ScriptedCommandInterfaceSP cmd_interface_sp, + ScriptedCommandSynchronicity synch) + : CommandObjectParsed(interpreter, name.c_str()), + m_cmd_interface_sp(cmd_interface_sp), m_synchro(synch), + m_options(interpreter, cmd_interface_sp), m_fetched_help_short(false), m_fetched_help_long(false) { StreamString stream; - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) { + if (!m_cmd_interface_sp) { m_options_error = Status::FromErrorString("No script interpreter"); return; } // Set the flags: - GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); + GetFlags().Set(m_cmd_interface_sp->GetFlags()); // Now set up the options definitions from the options: - StructuredData::ObjectSP options_object_sp - = scripter->GetOptionsForCommandObject(cmd_obj_sp); + StructuredData::ObjectSP options_object_sp = + m_cmd_interface_sp->GetOptionsDefinition(); // It's okay not to have an options dict. if (options_object_sp) { // The options come as a dictionary of dictionaries. The key of the // outer dict is the long option name (since that's required). The // value holds all the other option specification bits. - StructuredData::Dictionary *options_dict - = options_object_sp->GetAsDictionary(); + StructuredData::Dictionary *options_dict = + options_object_sp->GetAsDictionary(); // but if it exists, it has to be an array. if (options_dict) { m_options_error = m_options.SetOptionsFromArray(*(options_dict)); @@ -1884,8 +1863,8 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { } // Then fetch the args. Since the arguments can have usage masks you need // an array of arrays. - StructuredData::ObjectSP args_object_sp - = scripter->GetArgumentsForCommandObject(cmd_obj_sp); + StructuredData::ObjectSP args_object_sp = + m_cmd_interface_sp->GetArgumentsDefinition(); if (args_object_sp) { StructuredData::Array *args_array = args_object_sp->GetAsArray(); if (!args_array) { @@ -2017,9 +1996,7 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { public: void HandleArgumentCompletion(CompletionRequest &request, OptionElementVector &option_vec) override { - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - - if (!scripter) + if (!m_cmd_interface_sp) return; // Set up the options values on the scripted side: @@ -2057,8 +2034,8 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { args_elem_pos--; } StructuredData::DictionarySP completion_dict_sp = - scripter->HandleArgumentCompletionForScriptedCommand( - m_cmd_obj_sp, args_vec, args_elem_pos, request.GetCursorCharPos()); + m_cmd_interface_sp->HandleArgumentCompletion( + args_vec, args_elem_pos, request.GetCursorCharPos()); if (!completion_dict_sp) { CommandObject::HandleArgumentCompletion(request, option_vec); @@ -2074,22 +2051,19 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override { - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return std::nullopt; - return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args); + return m_cmd_interface_sp->GetRepeatCommand(args); } llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectParsed::GetHelp(); - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return CommandObjectParsed::GetHelp(); std::string docstring; - m_fetched_help_short = - scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring); + m_fetched_help_short = m_cmd_interface_sp->GetShortHelp(docstring); if (!docstring.empty()) SetHelp(docstring); @@ -2100,13 +2074,11 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { if (m_fetched_help_long) return CommandObjectParsed::GetHelpLong(); - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - if (!scripter) + if (!m_cmd_interface_sp) return CommandObjectParsed::GetHelpLong(); std::string docstring; - m_fetched_help_long = - scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring); + m_fetched_help_long = m_cmd_interface_sp->GetLongHelp(docstring); if (!docstring.empty()) SetHelpLong(docstring); return CommandObjectParsed::GetHelpLong(); @@ -2121,17 +2093,13 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { } protected: - void DoExecute(Args &args, - CommandReturnObject &result) override { - ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter(); - + void DoExecute(Args &args, CommandReturnObject &result) override { Status error; result.SetStatus(eReturnStatusInvalid); - - if (!scripter || - !scripter->RunScriptBasedParsedCommand(m_cmd_obj_sp, args, - m_synchro, result, error, m_exe_ctx)) { + + if (!m_cmd_interface_sp || !m_cmd_interface_sp->RunParsedCommand( + args, m_synchro, result, error, m_exe_ctx)) { result.AppendError(error.AsCString()); } else { // Don't change the status if the command already set it... @@ -2145,7 +2113,7 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed { } private: - StructuredData::GenericSP m_cmd_obj_sp; + lldb::ScriptedCommandInterfaceSP m_cmd_interface_sp; ScriptedCommandSynchronicity m_synchro; CommandOptions m_options; Status m_options_error; @@ -2513,22 +2481,32 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed, return; } - auto cmd_obj_sp = interpreter->CreateScriptCommandObject( - m_options.m_class_name.c_str()); - if (!cmd_obj_sp) { + lldb::ScriptedCommandInterfaceSP cmd_interface_sp = + interpreter->CreateScriptedCommandInterface(); + if (!cmd_interface_sp) { + result.AppendError("cannot create ScriptedCommandInterface"); + return; + } + + auto obj_or_err = cmd_interface_sp->CreatePluginObject( + m_options.m_class_name, GetDebugger().shared_from_this()); + if (!obj_or_err) { result.AppendErrorWithFormatv("cannot create helper object for: " - "'{0}'", m_options.m_class_name); + "'{0}': {1}", + m_options.m_class_name, + llvm::toString(obj_or_err.takeError())); return; } - + if (m_options.m_parsed_command) { - new_cmd_sp = CommandObjectScriptingObjectParsed::Create(m_interpreter, - m_cmd_name, cmd_obj_sp, m_synchronicity, result); + new_cmd_sp = CommandObjectScriptingObjectParsed::Create( + m_interpreter, m_cmd_name, cmd_interface_sp, m_synchronicity, + result); if (!result.Succeeded()) return; } else new_cmd_sp = std::make_shared<CommandObjectScriptingObjectRaw>( - m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity, + m_interpreter, m_cmd_name, cmd_interface_sp, m_synchronicity, m_completion_type); } diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 6436d8161583d..68bcdd0b039f4 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -214,6 +214,10 @@ ScriptInterpreter::ExtensionToString(lldb::ScriptedExtension extension) { return "ScriptedFrame"; case eScriptedExtensionScriptedStackFrameRecognizer: return "ScriptedStackFrameRecognizer"; + case eScriptedExtensionScriptedCommand: + return "ScriptedCommand"; + case eScriptedExtensionParsedCommand: + return "ParsedCommand"; } llvm_unreachable("unhandled ScriptedExtension"); } @@ -234,6 +238,8 @@ ScriptInterpreter::StringToExtension(llvm::StringRef string) { .CaseLower("ScriptedFrame", eScriptedExtensionScriptedFrame) .CaseLower("ScriptedStackFrameRecognizer", eScriptedExtensionScriptedStackFrameRecognizer) + .CaseLower("ScriptedCommand", eScriptedExtensionScriptedCommand) + .CaseLower("ParsedCommand", eScriptedExtensionParsedCommand) .Default(eScriptedExtensionInvalid); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index 201574da72a19..d3dc5773eb0af 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -32,6 +32,7 @@ set(python_plugin_sources Interfaces/ScriptedPythonInterface.cpp Interfaces/ScriptedHookPythonInterface.cpp Interfaces/ScriptedBreakpointPythonInterface.cpp + Interfaces/ScriptedCommandPythonInterface.cpp Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp Interfaces/ScriptedThreadPlanPythonInterface.cpp Interfaces/ScriptedThreadPythonInterface.cpp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp index 8914f6b239023..2c8e0ef8bbeb0 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp @@ -30,6 +30,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() { ScriptedThreadPythonInterface::Initialize(); ScriptedFramePythonInterface::Initialize(); ScriptedStackFrameRecognizerPythonInterface::Initialize(); + ScriptedCommandPythonInterface::Initialize(); } void ScriptInterpreterPythonInterfaces::Terminate() { @@ -43,4 +44,5 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedThreadPythonInterface::Terminate(); ScriptedFramePythonInterface::Terminate(); ScriptedStackFrameRecognizerPythonInterface::Terminate(); + ScriptedCommandPythonInterface::Terminate(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 03d747e63a592..7ddf7ec0e5ae5 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -14,6 +14,7 @@ #include "OperatingSystemPythonInterface.h" #include "ScriptedBreakpointPythonInterface.h" +#include "ScriptedCommandPythonInterface.h" #include "ScriptedFrameProviderPythonInterface.h" #include "ScriptedFramePythonInterface.h" #include "ScriptedHookPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.cpp new file mode 100644 index 0000000000000..da2b869ccf934 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.cpp @@ -0,0 +1,233 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../lldb-python.h" + +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/lldb-enumerations.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedCommandPythonInterface.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedCommandPythonInterface::ScriptedCommandPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedCommandInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedCommandPythonInterface::CreatePluginObject( + llvm::StringRef class_name, lldb::DebuggerSP debugger_sp) { + if (class_name.empty()) + return llvm::createStringError("empty class name"); + + if (!debugger_sp) + return llvm::createStringError("invalid Debugger pointer"); + + m_debugger_sp = debugger_sp; + ScriptedMetadata scripted_metadata(class_name, + StructuredData::DictionarySP()); + return ScriptedPythonInterface::CreatePluginObject( + scripted_metadata, /*script_obj=*/nullptr, debugger_sp); +} + +bool ScriptedCommandPythonInterface::RunRawCommand( + llvm::StringRef args, ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) { + if (!m_object_instance_sp || !m_object_instance_sp->IsValid()) { + error = Status::FromErrorString("no function to execute"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_debugger_sp; + lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); + + if (!debugger_sp) { + error = Status::FromErrorString("invalid Debugger pointer"); + return false; + } + + // Outer Locker sets up the session (with conditional NoSTDIN for + // interactive commands and TearDownSession on exit) that Dispatch's inner + // Locker doesn't provide. Nested locks are safe. + Locker py_lock(&m_interpreter, + Locker::AcquireLock | Locker::InitSession | + (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), + Locker::FreeLock | Locker::TearDownSession); + ScriptInterpreterPythonImpl::SynchronicityHandler synch_handler( + debugger_sp, synchronicity); + + auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj); + Dispatch<StructuredData::ObjectSP>("__call__", error, debugger_sp, args, + exe_ctx_ref_sp, cmd_retobj_arg.obj()); + + if (cmd_retobj.GetStatus() == eReturnStatusFailed) + return false; + + error.Clear(); + return true; +} + +bool ScriptedCommandPythonInterface::RunParsedCommand( + Args &args, ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) { + if (!m_object_instance_sp || !m_object_instance_sp->IsValid()) { + error = Status::FromErrorString("no function to execute"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_debugger_sp; + lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); + + if (!debugger_sp) { + error = Status::FromErrorString("invalid Debugger pointer"); + return false; + } + + Locker py_lock(&m_interpreter, + Locker::AcquireLock | Locker::InitSession | + (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), + Locker::FreeLock | Locker::TearDownSession); + ScriptInterpreterPythonImpl::SynchronicityHandler synch_handler( + debugger_sp, synchronicity); + + StructuredData::ArraySP args_arr_sp(new StructuredData::Array()); + for (const Args::ArgEntry &entry : args) + args_arr_sp->AddStringItem(entry.ref()); + StructuredDataImpl args_impl(args_arr_sp); + + auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj); + Dispatch<StructuredData::ObjectSP>("__call__", error, debugger_sp, args_impl, + exe_ctx_ref_sp, cmd_retobj_arg.obj()); + + if (cmd_retobj.GetStatus() == eReturnStatusFailed) + return false; + + error.Clear(); + return true; +} + +std::optional<std::string> +ScriptedCommandPythonInterface::GetRepeatCommand(Args &args) { + std::string command; + args.GetQuotedCommandString(command); + Status error; + return Dispatch<std::optional<std::string>>("get_repeat_command", error, + llvm::StringRef(command)); +} + +StructuredData::DictionarySP +ScriptedCommandPythonInterface::HandleArgumentCompletion( + std::vector<llvm::StringRef> &args, size_t args_pos, size_t char_in_arg) { + Status error; + return Dispatch<StructuredData::DictionarySP>( + "handle_argument_completion", error, args, args_pos, char_in_arg); +} + +StructuredData::DictionarySP +ScriptedCommandPythonInterface::HandleOptionArgumentCompletion( + llvm::StringRef &long_option, size_t char_in_arg) { + Status error; + StructuredData::ObjectSP result_sp = Dispatch<StructuredData::ObjectSP>( + "handle_option_argument_completion", error, long_option, char_in_arg); + if (!result_sp) + return {}; + + // A boolean return means: True → completion handled but no completions; + // False → completion not handled (fall back to default). + if (result_sp->GetType() == lldb::eStructuredDataTypeBoolean) { + if (!result_sp->GetBooleanValue()) + return {}; + StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary()); + dict_sp->AddBooleanItem("no-completion", true); + return dict_sp; + } + + StructuredData::DictionarySP dict_sp( + new StructuredData::Dictionary(result_sp)); + if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid) + return {}; + return dict_sp; +} + +bool ScriptedCommandPythonInterface::GetShortHelp(std::string &dest) { + dest.clear(); + Status error; + std::string result = Dispatch<std::string>("get_short_help", error); + if (!error.Success() || result.empty()) + return false; + dest = std::move(result); + return true; +} + +bool ScriptedCommandPythonInterface::GetLongHelp(std::string &dest) { + dest.clear(); + Status error; + std::string result = Dispatch<std::string>("get_long_help", error); + if (!error.Success() || result.empty()) + return false; + dest = std::move(result); + return true; +} + +uint32_t ScriptedCommandPythonInterface::GetFlags() { + Status error; + return Dispatch<uint32_t>("get_flags", error); +} + +StructuredData::ObjectSP +ScriptedCommandPythonInterface::GetOptionsDefinition() { + Status error; + return Dispatch<StructuredData::DictionarySP>("get_options_definition", + error); +} + +StructuredData::ObjectSP +ScriptedCommandPythonInterface::GetArgumentsDefinition() { + Status error; + return Dispatch<StructuredData::ArraySP>("get_args_definition", error); +} + +void ScriptedCommandPythonInterface::OptionParsingStarted() { + Status error; + Dispatch<StructuredData::ObjectSP>("option_parsing_started", error); +} + +bool ScriptedCommandPythonInterface::SetOptionValue(ExecutionContext *exe_ctx, + llvm::StringRef long_option, + llvm::StringRef value) { + lldb::ExecutionContextRefSP exe_ctx_ref_sp; + if (exe_ctx) + exe_ctx_ref_sp = std::make_shared<ExecutionContextRef>(exe_ctx); + Status error; + return Dispatch<bool>("set_option_value", error, exe_ctx_ref_sp, long_option, + value); +} + +void ScriptedCommandPythonInterface::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + "Implement a custom command, used by 'command script add -c'/'-P'", + CreateInstance, eScriptedExtensionScriptedCommand, eScriptLanguagePython, + {}); +} + +void ScriptedCommandPythonInterface::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.h new file mode 100644 index 0000000000000..d2bb5386a69ab --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.h @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDCOMMANDPYTHONINTERFACE_H +#define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDCOMMANDPYTHONINTERFACE_H + +#include "lldb/Interpreter/Interfaces/ScriptedCommandInterface.h" + +#include "ScriptedPythonInterface.h" +namespace lldb_private { + +class ScriptedCommandPythonInterface : public ScriptedCommandInterface, + public ScriptedPythonInterface, + public PluginInterface { +public: + ScriptedCommandPythonInterface(ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, + lldb::DebuggerSP debugger_sp) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>(); + } + + bool RunRawCommand(llvm::StringRef args, + ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) override; + + bool RunParsedCommand(Args &args, ScriptedCommandSynchronicity synchronicity, + CommandReturnObject &cmd_retobj, Status &error, + const ExecutionContext &exe_ctx) override; + + std::optional<std::string> GetRepeatCommand(Args &args) override; + + StructuredData::DictionarySP + HandleArgumentCompletion(std::vector<llvm::StringRef> &args, size_t args_pos, + size_t char_in_arg) override; + + StructuredData::DictionarySP + HandleOptionArgumentCompletion(llvm::StringRef &long_option, + size_t char_in_arg) override; + + bool GetShortHelp(std::string &dest) override; + + bool GetLongHelp(std::string &dest) override; + + uint32_t GetFlags() override; + + StructuredData::ObjectSP GetOptionsDefinition() override; + + StructuredData::ObjectSP GetArgumentsDefinition() override; + + void OptionParsingStarted() override; + + bool SetOptionValue(ExecutionContext *exe_ctx, llvm::StringRef long_option, + llvm::StringRef value) override; + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedCommandPythonInterface"; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +private: + lldb::DebuggerSP m_debugger_sp; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDCOMMANDPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 61ceb40dd9d32..e5790ce2ca450 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -33,6 +33,60 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( return result_list.CreateStructuredArray(); } +template <> +std::optional<std::string> +ScriptedPythonInterface::ExtractValueFromPythonObject< + std::optional<std::string>>(python::PythonObject &p, Status &error) { + if (!p.IsAllocated() || p.IsNone()) + return std::nullopt; + return p.Str().GetString().str(); +} + +template <> +std::string ScriptedPythonInterface::ExtractValueFromPythonObject<std::string>( + python::PythonObject &p, Status &error) { + if (!p.IsAllocated() || p.IsNone()) + return {}; + if (!python::PythonString::Check(p.get())) { + error = + Status::FromErrorString("expected a string return value from Python"); + return {}; + } + python::PythonString py_string(python::PyRefType::Borrowed, p.get()); + return py_string.GetString().str(); +} + +template <> +uint32_t ScriptedPythonInterface::ExtractValueFromPythonObject<uint32_t>( + python::PythonObject &p, Status &error) { + if (!p.IsAllocated() || p.IsNone()) + return 0; + if (!python::PythonInteger::Check(p.get())) { + error = + Status::FromErrorString("expected an integer return value from Python"); + return 0; + } + auto value_or_err = p.AsLongLong(); + if (!value_or_err) { + error = Status::FromError(value_or_err.takeError()); + return 0; + } + return static_cast<uint32_t>(*value_or_err); +} + +template <> +bool ScriptedPythonInterface::ExtractValueFromPythonObject<bool>( + python::PythonObject &p, Status &error) { + if (!p.IsAllocated() || p.IsNone()) + return false; + if (!python::PythonBoolean::Check(p.get())) { + error = + Status::FromErrorString("expected a boolean return value from Python"); + return false; + } + return python::PythonBoolean(python::PyRefType::Borrowed, p.get()).GetValue(); +} + template <> StructuredData::DictionarySP ScriptedPythonInterface::ExtractValueFromPythonObject< diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 7d0d4cdd3c6d1..ca59ff954fad6 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -663,6 +663,25 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::DebuggerSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + + python::PythonObject Transform(llvm::StringRef arg) { + return python::PythonString(arg); + } + + python::PythonObject Transform(size_t arg) { + return python::PythonInteger(arg); + } + + python::PythonObject Transform(const std::vector<llvm::StringRef> &arg) { + python::PythonList list(python::PyInitialValue::Empty); + for (llvm::StringRef s : arg) + list.AppendItem(python::PythonString(s)); + return list; + } + template <typename T, typename U> void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { // If U is not a PythonObject, don't touch it! diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 07e0da1dcf70d..97f53b2f435fa 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -143,11 +143,6 @@ class SWIGBridge { const char *session_dictionary_name, const lldb::ValueObjectSP &valobj_sp); - static python::PythonObject - LLDBSwigPythonCreateCommandObject(const char *python_class_name, - const char *session_dictionary_name, - lldb::DebuggerSP debugger_sp); - static size_t LLDBSwigPython_CalculateNumChildren(PyObject *implementor, uint32_t max); @@ -176,30 +171,6 @@ class SWIGBridge { lldb_private::CommandReturnObject &cmd_retobj, lldb::ExecutionContextRefSP exe_ctx_ref_sp); - static bool - LLDBSwigPythonCallCommandObject(PyObject *implementor, - lldb::DebuggerSP debugger, const char *args, - lldb_private::CommandReturnObject &cmd_retobj, - lldb::ExecutionContextRefSP exe_ctx_ref_sp); - static bool LLDBSwigPythonCallParsedCommandObject( - PyObject *implementor, lldb::DebuggerSP debugger, - 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 StructuredData::DictionarySP - LLDBSwigPythonHandleArgumentCompletionForScriptedCommand( - PyObject *implementor, std::vector<llvm::StringRef> &args_impl, - size_t args_pos, size_t pos_in_arg); - - static StructuredData::DictionarySP - LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand( - PyObject *implementor, llvm::StringRef &long_option, size_t pos_in_arg); - static bool LLDBSwigPythonCallModuleInit(const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP debugger); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index a153563b4534b..f63cc5e145aa2 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1644,6 +1644,11 @@ ScriptInterpreterPythonImpl::CreateScriptedStackFrameRecognizerInterface() { return std::make_shared<ScriptedStackFrameRecognizerPythonInterface>(*this); } +ScriptedCommandInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedCommandInterface() { + return std::make_shared<ScriptedCommandPythonInterface>(*this); +} + ScriptedThreadInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); @@ -1758,28 +1763,6 @@ ScriptInterpreterPythonImpl::CreateSyntheticScriptedProvider( new StructuredPythonObject(std::move(ret_val))); } -StructuredData::GenericSP -ScriptInterpreterPythonImpl::CreateScriptCommandObject(const char *class_name) { - DebuggerSP debugger_sp(m_debugger.shared_from_this()); - - if (class_name == nullptr || class_name[0] == '\0') - return StructuredData::GenericSP(); - - if (!debugger_sp.get()) - return StructuredData::GenericSP(); - - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - PythonObject ret_val = SWIGBridge::LLDBSwigPythonCreateCommandObject( - class_name, m_dictionary_name.c_str(), debugger_sp); - - if (ret_val.IsValid()) - return StructuredData::GenericSP( - new StructuredPythonObject(std::move(ret_val))); - else - return {}; -} - bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction( const char *oneliner, std::string &output, const void *name_token) { StringList input; @@ -2628,166 +2611,6 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedCommand( return ret_val; } -bool ScriptInterpreterPythonImpl::RunScriptBasedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef args, - ScriptedCommandSynchronicity synchronicity, - lldb_private::CommandReturnObject &cmd_retobj, Status &error, - const lldb_private::ExecutionContext &exe_ctx) { - if (!impl_obj_sp || !impl_obj_sp->IsValid()) { - error = Status::FromErrorString("no function to execute"); - return false; - } - - lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); - lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); - - if (!debugger_sp.get()) { - error = Status::FromErrorString("invalid Debugger pointer"); - return false; - } - - bool ret_val = false; - - { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | - (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), - Locker::FreeLock | Locker::TearDownSession); - - SynchronicityHandler synch_handler(debugger_sp, synchronicity); - - std::string args_str = args.str(); - ret_val = SWIGBridge::LLDBSwigPythonCallCommandObject( - static_cast<PyObject *>(impl_obj_sp->GetValue()), debugger_sp, - args_str.c_str(), cmd_retobj, exe_ctx_ref_sp); - } - - if (!ret_val) - error = Status::FromErrorString("unable to execute script function"); - else if (cmd_retobj.GetStatus() == eReturnStatusFailed) - return false; - - error.Clear(); - return ret_val; -} - -bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand( - StructuredData::GenericSP impl_obj_sp, Args &args, - ScriptedCommandSynchronicity synchronicity, - lldb_private::CommandReturnObject &cmd_retobj, Status &error, - const lldb_private::ExecutionContext &exe_ctx) { - if (!impl_obj_sp || !impl_obj_sp->IsValid()) { - error = Status::FromErrorString("no function to execute"); - return false; - } - - lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); - lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); - - if (!debugger_sp.get()) { - error = Status::FromErrorString("invalid Debugger pointer"); - return false; - } - - bool ret_val = false; - - { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | - (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), - Locker::FreeLock | Locker::TearDownSession); - - SynchronicityHandler synch_handler(debugger_sp, synchronicity); - - StructuredData::ArraySP args_arr_sp(new StructuredData::Array()); - - for (const Args::ArgEntry &entry : args) { - args_arr_sp->AddStringItem(entry.ref()); - } - StructuredDataImpl args_impl(args_arr_sp); - - ret_val = SWIGBridge::LLDBSwigPythonCallParsedCommandObject( - static_cast<PyObject *>(impl_obj_sp->GetValue()), debugger_sp, - args_impl, cmd_retobj, exe_ctx_ref_sp); - } - - if (!ret_val) - error = Status::FromErrorString("unable to execute script function"); - else if (cmd_retobj.GetStatus() == eReturnStatusFailed) - return false; - - error.Clear(); - 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; -} - -StructuredData::DictionarySP -ScriptInterpreterPythonImpl::HandleArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args, - size_t args_pos, size_t char_in_arg) { - StructuredData::DictionarySP completion_dict_sp; - if (!impl_obj_sp || !impl_obj_sp->IsValid()) - return completion_dict_sp; - - { - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, - Locker::FreeLock); - - completion_dict_sp = - SWIGBridge::LLDBSwigPythonHandleArgumentCompletionForScriptedCommand( - static_cast<PyObject *>(impl_obj_sp->GetValue()), args, args_pos, - char_in_arg); - } - return completion_dict_sp; -} - -StructuredData::DictionarySP -ScriptInterpreterPythonImpl::HandleOptionArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_option, - size_t char_in_arg) { - StructuredData::DictionarySP completion_dict_sp; - if (!impl_obj_sp || !impl_obj_sp->IsValid()) - return completion_dict_sp; - - { - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, - Locker::FreeLock); - - completion_dict_sp = SWIGBridge:: - LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand( - static_cast<PyObject *>(impl_obj_sp->GetValue()), long_option, - char_in_arg); - } - return completion_dict_sp; -} - /// In Python, a special attribute __doc__ contains the docstring for an object /// (function, method, class, ...) if any is defined Otherwise, the attribute's /// value is None. @@ -2821,322 +2644,6 @@ bool ScriptInterpreterPythonImpl::GetDocumentationForItem(const char *item, return false; } -bool ScriptInterpreterPythonImpl::GetShortHelpForCommandObject( - StructuredData::GenericSP cmd_obj_sp, std::string &dest) { - dest.clear(); - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - if (!cmd_obj_sp) - return false; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return false; - - llvm::Expected<PythonObject> expected_py_return = - implementor.CallMethod("get_short_help"); - - if (!expected_py_return) { - llvm::consumeError(expected_py_return.takeError()); - return false; - } - - PythonObject py_return = std::move(expected_py_return.get()); - - if (py_return.IsAllocated() && PythonString::Check(py_return.get())) { - PythonString py_string(PyRefType::Borrowed, py_return.get()); - llvm::StringRef return_data(py_string.GetString()); - dest.assign(return_data.data(), return_data.size()); - return true; - } - - return false; -} - -uint32_t ScriptInterpreterPythonImpl::GetFlagsForCommandObject( - StructuredData::GenericSP cmd_obj_sp) { - uint32_t result = 0; - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - static char callee_name[] = "get_flags"; - - if (!cmd_obj_sp) - return result; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return result; - - PythonObject pmeth(PyRefType::Owned, - PyObject_GetAttrString(implementor.get(), callee_name)); - - if (PyErr_Occurred()) - PyErr_Clear(); - - if (!pmeth.IsAllocated()) - return result; - - if (PyCallable_Check(pmeth.get()) == 0) { - if (PyErr_Occurred()) - PyErr_Clear(); - return result; - } - - if (PyErr_Occurred()) - PyErr_Clear(); - - long long py_return = unwrapOrSetPythonException( - As<long long>(implementor.CallMethod(callee_name))); - - // if it fails, print the error but otherwise go on - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } else { - result = py_return; - } - - return result; -} - -StructuredData::ObjectSP -ScriptInterpreterPythonImpl::GetOptionsForCommandObject( - StructuredData::GenericSP cmd_obj_sp) { - StructuredData::ObjectSP result = {}; - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - static char callee_name[] = "get_options_definition"; - - if (!cmd_obj_sp) - return result; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return result; - - PythonObject pmeth(PyRefType::Owned, - PyObject_GetAttrString(implementor.get(), callee_name)); - - if (PyErr_Occurred()) - PyErr_Clear(); - - if (!pmeth.IsAllocated()) - return result; - - if (PyCallable_Check(pmeth.get()) == 0) { - if (PyErr_Occurred()) - PyErr_Clear(); - return result; - } - - if (PyErr_Occurred()) - PyErr_Clear(); - - PythonDictionary py_return = unwrapOrSetPythonException( - As<PythonDictionary>(implementor.CallMethod(callee_name))); - - // if it fails, print the error but otherwise go on - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - return {}; - } - return py_return.CreateStructuredObject(); -} - -StructuredData::ObjectSP -ScriptInterpreterPythonImpl::GetArgumentsForCommandObject( - StructuredData::GenericSP cmd_obj_sp) { - StructuredData::ObjectSP result = {}; - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - static char callee_name[] = "get_args_definition"; - - if (!cmd_obj_sp) - return result; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return result; - - PythonObject pmeth(PyRefType::Owned, - PyObject_GetAttrString(implementor.get(), callee_name)); - - if (PyErr_Occurred()) - PyErr_Clear(); - - if (!pmeth.IsAllocated()) - return result; - - if (PyCallable_Check(pmeth.get()) == 0) { - if (PyErr_Occurred()) - PyErr_Clear(); - return result; - } - - if (PyErr_Occurred()) - PyErr_Clear(); - - PythonList py_return = unwrapOrSetPythonException( - As<PythonList>(implementor.CallMethod(callee_name))); - - // if it fails, print the error but otherwise go on - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - return {}; - } - return py_return.CreateStructuredObject(); -} - -void ScriptInterpreterPythonImpl::OptionParsingStartedForCommandObject( - StructuredData::GenericSP cmd_obj_sp) { - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - static char callee_name[] = "option_parsing_started"; - - if (!cmd_obj_sp) - return; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return; - - PythonObject pmeth(PyRefType::Owned, - PyObject_GetAttrString(implementor.get(), callee_name)); - - if (PyErr_Occurred()) - PyErr_Clear(); - - if (!pmeth.IsAllocated()) - return; - - if (PyCallable_Check(pmeth.get()) == 0) { - if (PyErr_Occurred()) - PyErr_Clear(); - return; - } - - if (PyErr_Occurred()) - PyErr_Clear(); - - // option_parsing_starting doesn't return anything, ignore anything but - // python errors. - unwrapOrSetPythonException(As<bool>(implementor.CallMethod(callee_name))); - - // if it fails, print the error but otherwise go on - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - return; - } -} - -bool ScriptInterpreterPythonImpl::SetOptionValueForCommandObject( - StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx, - llvm::StringRef long_option, llvm::StringRef value) { - StructuredData::ObjectSP result = {}; - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - static char callee_name[] = "set_option_value"; - - if (!cmd_obj_sp) - return false; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return false; - - PythonObject pmeth(PyRefType::Owned, - PyObject_GetAttrString(implementor.get(), callee_name)); - - if (PyErr_Occurred()) - PyErr_Clear(); - - if (!pmeth.IsAllocated()) - return false; - - if (PyCallable_Check(pmeth.get()) == 0) { - if (PyErr_Occurred()) - PyErr_Clear(); - return false; - } - - if (PyErr_Occurred()) - PyErr_Clear(); - - lldb::ExecutionContextRefSP exe_ctx_ref_sp; - if (exe_ctx) - exe_ctx_ref_sp = std::make_shared<ExecutionContextRef>(exe_ctx); - PythonObject ctx_ref_obj = SWIGBridge::ToSWIGWrapper(exe_ctx_ref_sp); - - bool py_return = unwrapOrSetPythonException(As<bool>( - implementor.CallMethod(callee_name, ctx_ref_obj, - long_option.str().c_str(), value.str().c_str()))); - - // if it fails, print the error but otherwise go on - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - return false; - } - return py_return; -} - -bool ScriptInterpreterPythonImpl::GetLongHelpForCommandObject( - StructuredData::GenericSP cmd_obj_sp, std::string &dest) { - dest.clear(); - - Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - - if (!cmd_obj_sp) - return false; - - PythonObject implementor(PyRefType::Borrowed, - (PyObject *)cmd_obj_sp->GetValue()); - - if (!implementor.IsAllocated()) - return false; - - llvm::Expected<PythonObject> expected_py_return = - implementor.CallMethod("get_long_help"); - - if (!expected_py_return) { - llvm::consumeError(expected_py_return.takeError()); - return false; - } - - PythonObject py_return = std::move(expected_py_return.get()); - - bool got_string = false; - if (py_return.IsAllocated() && PythonString::Check(py_return.get())) { - PythonString str(PyRefType::Borrowed, py_return.get()); - llvm::StringRef str_data(str.GetString()); - dest.assign(str_data.data(), str_data.size()); - got_string = true; - } - - return got_string; -} - std::unique_ptr<ScriptInterpreterLocker> ScriptInterpreterPythonImpl::AcquireInterpreterLock() { std::unique_ptr<ScriptInterpreterLocker> py_lock(new Locker( diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index d8a817198253f..51b373afbb9f4 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -70,9 +70,6 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { CreateSyntheticScriptedProvider(const char *class_name, lldb::ValueObjectSP valobj) override; - StructuredData::GenericSP - CreateScriptCommandObject(const char *class_name) override; - StructuredData::ObjectSP CreateStructuredDataFromScriptObject(ScriptObject obj) override; @@ -86,6 +83,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedStackFrameRecognizerInterfaceSP CreateScriptedStackFrameRecognizerInterface() override; + lldb::ScriptedCommandInterfaceSP CreateScriptedCommandInterface() override; + lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; @@ -137,30 +136,6 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { Status &error, const lldb_private::ExecutionContext &exe_ctx) override; - bool RunScriptBasedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef 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; - - StructuredData::DictionarySP HandleArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args, - size_t args_pos, size_t char_in_arg) override; - - StructuredData::DictionarySP HandleOptionArgumentCompletionForScriptedCommand( - StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_options, - size_t char_in_arg) override; - Status GenerateFunction(const char *signature, const StringList &input, bool is_callback) override; @@ -183,29 +158,6 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { bool GetDocumentationForItem(const char *item, std::string &dest) override; - bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, - std::string &dest) override; - - uint32_t - GetFlagsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; - - bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, - std::string &dest) override; - - StructuredData::ObjectSP - GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; - - StructuredData::ObjectSP - GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; - - bool SetOptionValueForCommandObject(StructuredData::GenericSP cmd_obj_sp, - ExecutionContext *exe_ctx, - llvm::StringRef long_option, - llvm::StringRef value) override; - - void OptionParsingStartedForCommandObject( - StructuredData::GenericSP cmd_obj_sp) override; - bool CheckObjectExists(const char *name) override { if (!name || !name[0]) return false; diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index c9298191ec3c1..72310075a0d81 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -73,13 +73,6 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProvider( return python::PythonObject(); } -python::PythonObject -lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject( - const char *python_class_name, const char *session_dictionary_name, - lldb::DebuggerSP debugger_sp) { - return python::PythonObject(); -} - size_t lldb_private::python::SWIGBridge::LLDBSwigPython_CalculateNumChildren( PyObject *implementor, uint32_t max) { return 0; @@ -207,40 +200,6 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand( return false; } -bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject( - PyObject *implementor, lldb::DebuggerSP debugger, const char *args, - lldb_private::CommandReturnObject &cmd_retobj, - lldb::ExecutionContextRefSP exe_ctx_ref_sp) { - return false; -} - -bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( - PyObject *implementor, lldb::DebuggerSP debugger, - StructuredDataImpl &args_impl, - lldb_private::CommandReturnObject &cmd_retobj, - lldb::ExecutionContextRefSP exe_ctx_ref_sp) { - return false; -} - -std::optional<std::string> -LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, - std::string &command) { - return std::nullopt; -} - -StructuredData::DictionarySP -LLDBSwigPythonHandleArgumentCompletionForScriptedCommand( - PyObject *implementor, std::vector<llvm::StringRef> &args, size_t args_pos, - size_t pos_in_arg) { - return {}; -} - -StructuredData::DictionarySP -LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand( - PyObject *implementor, llvm::StringRef &long_options, size_t char_in_arg) { - return {}; -} - bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit( const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP debugger) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
