Skip to content

[lldb/script] Migrate ParsedCommand & raw commands onto ScriptedPythonInterface#210430

Open
medismailben wants to merge 1 commit into
llvm:mainfrom
medismailben:scripted-extension-command
Open

[lldb/script] Migrate ParsedCommand & raw commands onto ScriptedPythonInterface#210430
medismailben wants to merge 1 commit into
llvm:mainfrom
medismailben:scripted-extension-command

Conversation

@medismailben

@medismailben medismailben commented Jul 17, 2026

Copy link
Copy Markdown
Member

Give both command script add -c (raw class) and -P (ParsedCommand) a single, shared ScriptedCommandInterface/ScriptedCommandPythonInterface, built on the existing ScriptedPythonInterface machinery: CreatePluginObject delegates to the base template, and every other method goes through Dispatch().

Add a lighter ScriptedCommand ABC template (scripted_command.py) for raw mode; the existing ParsedCommand/LLDBOptionValueParser classes are kept as-is.

Extend ScriptedPythonInterface with a few Transform overloads (DebuggerSP, std::vector<StringRef>, CommandReturnObject &) so those argument types route through Dispatch without extra plumbing.

Delete the standalone SWIG bridge functions this plugin was the sole caller of. CommandObjectScriptingObjectRaw and CommandObjectScriptingObjectParsed now hold a ScriptedCommandInterfaceSP instead of a raw StructuredData::GenericSP; command script add's DoExecute constructs the interface once via CreateScriptedCommandInterface()->CreatePluginObject for either flag.

@medismailben medismailben changed the title [lldb/script] Migrate ParsedCommand and raw class commands onto Scrip… [lldb/script] Migrate ParsedCommand and raw class commands onto ScriptedPythonInterface Jul 17, 2026
@medismailben
medismailben requested a review from jimingham July 17, 2026 20:51
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lldb

Author: Med Ismail Bennani (medismailben)

Changes

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&lt;T&gt;(). 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()-&gt;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.


Patch is 81.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/210430.diff

21 Files Affected:

  • (modified) lldb/bindings/python/CMakeLists.txt (+1)
  • (modified) lldb/bindings/python/python-wrapper.swig (-158)
  • (modified) lldb/docs/CMakeLists.txt (+1)
  • (added) lldb/examples/python/templates/scripted_command.py (+86)
  • (added) lldb/include/lldb/Interpreter/Interfaces/ScriptedCommandInterface.h (+83)
  • (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+4-78)
  • (modified) lldb/include/lldb/lldb-enumerations.h (+3-1)
  • (modified) lldb/include/lldb/lldb-forward.h (+3)
  • (modified) lldb/source/Commands/CommandObjectCommands.cpp (+88-110)
  • (modified) lldb/source/Interpreter/ScriptInterpreter.cpp (+6)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt (+1)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp (+2)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h (+1)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.cpp (+233)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedCommandPythonInterface.h (+81)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp (+54)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (+19)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h (-29)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (+5-498)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h (+2-50)
  • (modified) lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp (-41)
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/Op...
[truncated]

@medismailben
medismailben force-pushed the scripted-extension-command branch 4 times, most recently from 45cf0e7 to 76f2a10 Compare July 17, 2026 23:42
@medismailben medismailben changed the title [lldb/script] Migrate ParsedCommand and raw class commands onto ScriptedPythonInterface [lldb/script] Migrate ParsedCommand & raw commands onto ScriptedPythonInterface Jul 17, 2026
…nInterface

Give both `command script add -c` (raw class) and `-P` (ParsedCommand) a
single, shared `ScriptedCommandInterface`/`ScriptedCommandPythonInterface`,
built on the existing `ScriptedPythonInterface` machinery: `CreatePluginObject`
delegates to the base template, and every other method goes through
`Dispatch()`.

Add a lighter `ScriptedCommand` ABC template (`scripted_command.py`) for raw
mode; the existing `ParsedCommand`/`LLDBOptionValueParser` classes are kept
as-is.

Extend `ScriptedPythonInterface` with a few `Transform` overloads
(`DebuggerSP`, `std::vector<StringRef>`, `CommandReturnObject &`) so those
argument types route through `Dispatch` without extra plumbing.

Delete the standalone SWIG bridge functions this plugin was the sole caller
of. `CommandObjectScriptingObjectRaw` and `CommandObjectScriptingObjectParsed`
now hold a `ScriptedCommandInterfaceSP` instead of a raw
`StructuredData::GenericSP`; `command script add`'s `DoExecute` constructs
the interface once via `CreateScriptedCommandInterface()->CreatePluginObject`
for either flag.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
@medismailben
medismailben force-pushed the scripted-extension-command branch from 76f2a10 to 445cc9f Compare July 18, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant