[Lldb-commits] [lldb] [lldb][api-test] Add API test for SBCommandInterpreter::CommandOverrideCallback (PR #94518)

Chelsea Cassanova via lldb-commits lldb-commits at lists.llvm.org
Wed Jun 5 11:33:15 PDT 2024


https://github.com/chelcassanova created https://github.com/llvm/llvm-project/pull/94518

`SBCommandInterpreter::CommandOverrideCallback` was not being exposed to the Python API has no coverage in the
API test suite, so this commits exposes and adds a test for it. Doing this involves also adding a typemap for the callback used for this function so that it matches the functionality of other callback functions that are exposed to Python.

>From 90602ed1b2f27fecb9e1fc28419587cf6bcb04bf Mon Sep 17 00:00:00 2001
From: Chelsea Cassanova <chelsea_cassanova at apple.com>
Date: Wed, 5 Jun 2024 11:24:01 -0700
Subject: [PATCH] [lldb][api-test] Add API test for
 SBCommandInterpreter::CommandOverrideCallback

`SBCommandInterpreter::CommandOverrideCallback` was not being exposed to
the Python API has no coverage in the
API test suite, so this commits exposes and adds a test for it. Doing
this involves also adding a typemap for the callback used for this
function so that it matches the functionality of other callback
functions that are exposed to Python.
---
 lldb/bindings/python/python-typemaps.swig     | 27 ++++++++++++++++-
 lldb/bindings/python/python-wrapper.swig      | 13 ++++++++
 lldb/include/lldb/API/SBCommandInterpreter.h  |  2 --
 .../TestCommandOverrideCallback.py            | 30 +++++++++++++++++++
 4 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 lldb/test/API/python_api/interpreter/TestCommandOverrideCallback.py

diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 8d4b740e5f35c..1bf93c59fbc7b 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -427,7 +427,6 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
   free($1);
 }
 
-
 // For Log::LogOutputCallback
 %typemap(in) (lldb::LogOutputCallback log_callback, void *baton) {
   if (!($input == Py_None ||
@@ -476,6 +475,32 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
   $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
 }
 
+%typemap(in) (lldb::CommandOverrideCallback callback, void *baton) {
+  if (!($input == Py_None ||
+        PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
+    PyErr_SetString(PyExc_TypeError, "Need a callable object or None!");
+    SWIG_fail;
+  }
+
+  // FIXME (filcab): We can't currently check if our callback is already
+  // LLDBSwigPythonCallPythonSBDebuggerTerminateCallback (to DECREF the previous
+  // baton) nor can we just remove all traces of a callback, if we want to
+  // revert to a file logging mechanism.
+
+  // Don't lose the callback reference
+  Py_INCREF($input);
+  $1 = LLDBSwigPythonCallPythonSBCommandInterpreterSetCommandOverrideCallback;
+  $2 = $input;
+}
+
+%typemap(typecheck) (lldb::CommandOverrideCallback callback,
+
+                                  void *baton) {
+
+  $1 = $input == Py_None;
+  $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
+}
+
 %typemap(in) lldb::FileSP {
   PythonFile py_file(PyRefType::Borrowed, $input);
   if (!py_file) {
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 1370afc885d43..0a332cb0a2ff4 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -1099,6 +1099,19 @@ static void LLDBSwigPythonCallPythonSBDebuggerTerminateCallback(lldb::user_id_t
   }
 }
 
+static bool LLDBSwigPythonCallPythonSBCommandInterpreterSetCommandOverrideCallback(void *baton, const char **argv) {
+  bool b = false;
+  if (baton != Py_None) {
+    SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+    PyObject *result = PyObject_CallFunction(
+      reinterpret_cast<PyObject *>(baton), const_cast<char *>("s"), argv); // WRONG!!!!!
+    b = result ? PyObject_IsTrue(result) : false;
+    Py_XDECREF(result);
+    SWIG_PYTHON_THREAD_END_BLOCK;
+  }
+  return b;
+}
+
 static SBError LLDBSwigPythonCallLocateModuleCallback(
     void *callback_baton, const SBModuleSpec &module_spec_sb,
     SBFileSpec &module_file_spec_sb, SBFileSpec &symbol_file_spec_sb) {
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index 8ac36344b3a79..084b6d9adb703 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -265,11 +265,9 @@ class SBCommandInterpreter {
   // Catch commands before they execute by registering a callback that will get
   // called when the command gets executed. This allows GUI or command line
   // interfaces to intercept a command and stop it from happening
-#ifndef SWIG
   bool SetCommandOverrideCallback(const char *command_name,
                                   lldb::CommandOverrideCallback callback,
                                   void *baton);
-#endif
 
   /// Return true if the command interpreter is the active IO handler.
   ///
diff --git a/lldb/test/API/python_api/interpreter/TestCommandOverrideCallback.py b/lldb/test/API/python_api/interpreter/TestCommandOverrideCallback.py
new file mode 100644
index 0000000000000..067f37f88096d
--- /dev/null
+++ b/lldb/test/API/python_api/interpreter/TestCommandOverrideCallback.py
@@ -0,0 +1,30 @@
+from typing_extensions import override
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class CommandOverrideCallback(TestBase):
+    def setUp(self):
+        TestBase.setUp(self)
+        self.line = line_number("main.c", "Hello world.")
+
+    def test_command_override_callback(self):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Retrieve the associated command interpreter from our debugger.
+        ci = self.dbg.GetCommandInterpreter()
+        self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
+
+        override_string = "what"
+        def foo(string):
+            nonlocal override_string
+            override_string = string
+            return False
+
+        self.assertTrue(ci.SetCommandOverrideCallback("breakpoint", foo))



More information about the lldb-commits mailing list