[Lldb-commits] [lldb] [lldb/script] Migrate synthetic children providers onto ScriptedPythonInterface (PR #210845)
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 28 01:25:48 PDT 2026
https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/210845
>From b3fb3e9e6286050e34d65a72a41b6d5b3e8456c8 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Tue, 28 Jul 2026 01:25:23 -0700
Subject: [PATCH] [lldb/script] Migrate synthetic children providers onto
ScriptedPythonInterface
Give `type synthetic add -l` a formal `ScriptedSyntheticChildrenInterface`,
matching the architecture used elsewhere in this series: a C++ interface
header, a Python-backed implementation, `PluginManager` registration with
CLI/API usages, and a generatable ABC template
(`scripted_synthetic_children.py`) wired into `scripting extension
generate`.
Every method goes through the shared `Dispatch<T>()` machinery instead of
hand-rolling its own Locker/raw-SWIG calls. `Dispatch<T>()` is taught to
introspect the target method's arity via `PythonCallable::GetArgInfo()` and
drop trailing args before calling, so providers that legitimately define an
argument as optional (`num_children(self)` vs. `num_children(self, max_count)`)
still work through the generic dispatch path.
This retires the ad-hoc `LLDBSwigPython_*` synthetic-children bridge
functions entirely.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
lldb/bindings/python/CMakeLists.txt | 1 +
lldb/bindings/python/python-swigsafecast.swig | 7 +
lldb/bindings/python/python-wrapper.swig | 176 -------------
lldb/docs/CMakeLists.txt | 1 +
.../templates/scripted_synthetic_children.py | 109 ++++++++
.../lldb/DataFormatters/TypeSynthetic.h | 3 +-
.../ScriptedSyntheticChildrenInterface.h | 44 ++++
.../lldb/Interpreter/ScriptInterpreter.h | 48 +---
lldb/include/lldb/lldb-enumerations.h | 3 +-
lldb/include/lldb/lldb-forward.h | 3 +
lldb/source/DataFormatters/TypeSynthetic.cpp | 66 ++---
lldb/source/Interpreter/ScriptInterpreter.cpp | 4 +
.../ScriptInterpreter/Python/CMakeLists.txt | 1 +
.../ScriptInterpreterPythonInterfaces.cpp | 2 +
.../ScriptInterpreterPythonInterfaces.h | 1 +
.../Interfaces/ScriptedPythonInterface.h | 95 ++++---
...riptedSyntheticChildrenPythonInterface.cpp | 141 ++++++++++
...ScriptedSyntheticChildrenPythonInterface.h | 63 +++++
.../Python/SWIGPythonBridge.h | 25 +-
.../Python/ScriptInterpreterPython.cpp | 240 +-----------------
.../Python/ScriptInterpreterPythonImpl.h | 30 +--
.../Python/PythonTestSuite.cpp | 40 ---
22 files changed, 494 insertions(+), 609 deletions(-)
create mode 100644 lldb/examples/python/templates/scripted_synthetic_children.py
create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h
create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.cpp
create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.h
diff --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt
index d29b143c1408c..ce6201396bb4f 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_synthetic_children.py"
)
if(APPLE)
diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index a86dc44ce4106..c5003c019aae2 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -17,6 +17,13 @@ PythonObject SWIGBridge::ToSWIGWrapper(lldb::ValueObjectSP value_sp) {
return ToSWIGWrapper(std::unique_ptr<lldb::SBValue>(new lldb::SBValue(value_sp)));
}
+PythonObject SWIGBridge::ToSWIGWrapper(lldb::ValueObjectSP value_sp,
+ bool use_synthetic) {
+ auto sb_value = std::unique_ptr<lldb::SBValue>(new lldb::SBValue(value_sp));
+ sb_value->SetPreferSyntheticValue(use_synthetic);
+ return ToSWIGWrapper(std::move(sb_value));
+}
+
PythonObject SWIGBridge::ToSWIGWrapper(lldb::TargetSP target_sp) {
return ToSWIGHelper(new lldb::SBTarget(std::move(target_sp)),
SWIGTYPE_p_lldb__SBTarget);
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 2392737402e20..a902c53c74ee6 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -181,38 +181,6 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallTypeScript(
return true;
}
-PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProvider(
- const char *python_class_name, const char *session_dictionary_name,
- const lldb::ValueObjectSP &valobj_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();
-
- auto sb_value = std::unique_ptr<lldb::SBValue>(new lldb::SBValue(valobj_sp));
- sb_value->SetPreferSyntheticValue(false);
-
- PythonObject val_arg = SWIGBridge::ToSWIGWrapper(std::move(sb_value));
- if (!val_arg.IsAllocated())
- return PythonObject();
-
- PythonObject result = pfunc(val_arg, dict);
-
- if (result.IsAllocated())
- return result;
-
- return PythonObject();
-}
-
PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject(
const char *python_class_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger_sp) {
@@ -256,150 +224,6 @@ static PyObject *LLDBSwigPython_CallOptionalMember(
return result.release();
}
-size_t lldb_private::python::SWIGBridge::LLDBSwigPython_CalculateNumChildren(PyObject * implementor,
- uint32_t max) {
- PythonObject self(PyRefType::Borrowed, implementor);
- auto pfunc = self.ResolveName<PythonCallable>("num_children");
-
- if (!pfunc.IsAllocated())
- return 0;
-
- auto arg_info = pfunc.GetArgInfo();
- if (!arg_info) {
- llvm::consumeError(arg_info.takeError());
- return 0;
- }
-
- size_t ret_val;
- if (arg_info.get().max_positional_args < 1)
- ret_val = unwrapOrSetPythonException(As<long long>(pfunc.Call()));
- else
- ret_val = unwrapOrSetPythonException(
- As<long long>(pfunc.Call(PythonInteger(max))));
-
- if (PyErr_Occurred()) {
- PyErr_Print();
- PyErr_Clear();
- return 0;
- }
-
- if (arg_info.get().max_positional_args < 1)
- ret_val = std::min(ret_val, static_cast<size_t>(max));
-
- return ret_val;
-}
-
-PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetChildAtIndex(PyObject * implementor,
- uint32_t idx) {
- PyErr_Cleaner py_err_cleaner(true);
-
- PythonObject self(PyRefType::Borrowed, implementor);
- auto pfunc = self.ResolveName<PythonCallable>("get_child_at_index");
-
- if (!pfunc.IsAllocated())
- return nullptr;
-
- PythonObject result = pfunc(PythonInteger(idx));
-
- if (!result.IsAllocated())
- return nullptr;
-
- lldb::SBValue *sbvalue_ptr = nullptr;
- if (SWIG_ConvertPtr(result.get(), (void **)&sbvalue_ptr,
- SWIGTYPE_p_lldb__SBValue, 0) == -1)
- return nullptr;
-
- if (sbvalue_ptr == nullptr)
- return nullptr;
-
- return result.release();
-}
-
-uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName(
- PyObject * implementor, const char *child_name) {
- PyErr_Cleaner py_err_cleaner(true);
-
- PythonObject self(PyRefType::Borrowed, implementor);
- auto pfunc = self.ResolveName<PythonCallable>("get_child_index");
-
- if (!pfunc.IsAllocated())
- return UINT32_MAX;
-
- llvm::Expected<PythonObject> result = pfunc.Call(PythonString(child_name));
-
- long long retval =
- unwrapOrSetPythonException(As<long long>(std::move(result)));
-
- if (PyErr_Occurred()) {
- PyErr_Clear(); // FIXME print this? do something else
- return UINT32_MAX;
- }
-
- if (retval >= 0)
- return (uint32_t)retval;
-
- return UINT32_MAX;
-}
-
-bool lldb_private::python::SWIGBridge::LLDBSwigPython_UpdateSynthProviderInstance(PyObject *
- implementor) {
- bool ret_val = false;
-
- static char callee_name[] = "update";
-
- PyObject *py_return =
- LLDBSwigPython_CallOptionalMember(implementor, callee_name);
-
- if (py_return == Py_True)
- ret_val = true;
-
- Py_XDECREF(py_return);
-
- return ret_val;
-}
-
-bool lldb_private::python::SWIGBridge::LLDBSwigPython_MightHaveChildrenSynthProviderInstance(
- PyObject * implementor) {
- bool ret_val = false;
-
- static char callee_name[] = "has_children";
-
- PyObject *py_return =
- LLDBSwigPython_CallOptionalMember(implementor, callee_name, Py_True);
-
- if (py_return == Py_True)
- ret_val = true;
-
- Py_XDECREF(py_return);
-
- return ret_val;
-}
-
-PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetValueSynthProviderInstance(
- PyObject * implementor) {
- PyObject *ret_val = nullptr;
-
- static char callee_name[] = "get_value";
-
- PyObject *py_return =
- LLDBSwigPython_CallOptionalMember(implementor, callee_name, Py_None);
-
- if (py_return == Py_None || py_return == nullptr)
- ret_val = nullptr;
-
- lldb::SBValue *sbvalue_ptr = NULL;
-
- if (SWIG_ConvertPtr(py_return, (void **)&sbvalue_ptr,
- SWIGTYPE_p_lldb__SBValue, 0) == -1)
- ret_val = nullptr;
- else if (sbvalue_ptr == NULL)
- ret_val = nullptr;
- else
- ret_val = py_return;
-
- Py_XDECREF(py_return);
- return ret_val;
-}
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBData(PyObject * data) {
lldb::SBData *sb_ptr = nullptr;
diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt
index dd091836dc1aa..d57415ef1e975 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_synthetic_children.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_synthetic_children.py b/lldb/examples/python/templates/scripted_synthetic_children.py
new file mode 100644
index 0000000000000..d4d0f993a521f
--- /dev/null
+++ b/lldb/examples/python/templates/scripted_synthetic_children.py
@@ -0,0 +1,109 @@
+from abc import ABCMeta, abstractmethod
+from typing import Optional
+
+import lldb
+
+
+class ScriptedSyntheticChildren(metaclass=ABCMeta):
+ """
+ The base class for a scripted synthetic children provider.
+
+ A synthetic children provider allows you to customize how a value is
+ expanded into children when displayed (e.g. `frame variable`, `bt`).
+ Register it with `type synthetic add -l <ClassName> ...`.
+
+ Most of the base class methods are `@abstractmethod` that need to be
+ overwritten by the inheriting class.
+ """
+
+ valobj: lldb.SBValue
+
+ def __init__(self, valobj: lldb.SBValue):
+ """Construct a scripted synthetic children provider.
+
+ Args:
+ valobj (lldb.SBValue): The value this provider generates children
+ for.
+ """
+ self.valobj = valobj
+
+ @abstractmethod
+ def num_children(self) -> int:
+ """The number of children this value has.
+
+ This can optionally take a second `max_count` parameter (i.e.
+ `def num_children(self, max_count)`) if computing the exact count is
+ expensive; in that case return `max_count` once at least that many
+ children are known to exist.
+
+ Returns:
+ int: The number of children.
+ """
+ pass
+
+ @abstractmethod
+ def get_child_at_index(self, index: int) -> Optional[lldb.SBValue]:
+ """Get the child at the given index.
+
+ Args:
+ index (int): The index of the child to return.
+
+ Returns:
+ lldb.SBValue: The value for the child at this index, or `None` if
+ there is no child at this index.
+ """
+ pass
+
+ def get_child_index(self, name: str) -> Optional[int]:
+ """Get the index of the child with the given name.
+
+ Args:
+ name (str): The name of the child to look up.
+
+ Returns:
+ int: The index of the child with this name, or `None`/a negative
+ value if no such child exists. Defaults to a linear search over
+ `get_child_at_index`/`num_children`.
+ """
+ pass
+
+ def update(self) -> bool:
+ """Called when the value backing this provider may have changed
+ (e.g. after a `continue`), giving the provider a chance to refresh
+ any cached state.
+
+ Returns:
+ bool: `True` if the previously computed children can be reused,
+ `False` if they should be recomputed. Defaults to `False`.
+ """
+ return False
+
+ def has_children(self) -> bool:
+ """Whether this value might have children, without necessarily
+ computing them. Used as a cheap check to decide whether to show an
+ expansion arrow in graphical frontends, for example.
+
+ Returns:
+ bool: `True` if this value might have children, `False`
+ otherwise. Defaults to `True`.
+ """
+ return True
+
+ def get_value(self) -> Optional[lldb.SBValue]:
+ """Override the value shown for this synthetic value itself,
+ alongside its children.
+
+ Returns:
+ lldb.SBValue: The value to display, or `None` to keep the
+ default. Defaults to `None`.
+ """
+ return None
+
+ def get_type_name(self) -> Optional[str]:
+ """Override the type name shown for this synthetic value.
+
+ Returns:
+ str: The type name to display, or `None`/empty to keep the
+ default. Defaults to `None`.
+ """
+ pass
diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
index 9c17adbde7465..6eb06ba8dda7f 100644
--- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h
+++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
@@ -477,8 +477,7 @@ class ScriptedSyntheticChildren : public SyntheticChildren {
private:
std::string m_python_class;
- StructuredData::ObjectSP m_wrapper_sp;
- ScriptInterpreter *m_interpreter;
+ lldb::ScriptedSyntheticChildrenInterfaceSP m_interface_sp;
FrontEnd(const FrontEnd &) = delete;
const FrontEnd &operator=(const FrontEnd &) = delete;
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h
new file mode 100644
index 0000000000000..c73018c779e1e
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SCRIPTEDSYNTHETICCHILDRENINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYNTHETICCHILDRENINTERFACE_H
+
+#include "ScriptedInterface.h"
+#include "lldb/lldb-private.h"
+#include "llvm/Support/ErrorExtras.h"
+
+namespace lldb_private {
+class ScriptedSyntheticChildrenInterface : virtual public ScriptedInterface {
+public:
+ virtual llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, ValueObject &backend) = 0;
+
+ virtual llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) {
+ return 0;
+ }
+
+ virtual lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) {
+ return lldb::ValueObjectSP();
+ }
+
+ virtual llvm::Expected<uint32_t> GetIndexOfChildWithName(ConstString name) {
+ return llvm::createStringErrorV("type has no child named '{0}'", name);
+ }
+
+ virtual lldb::ChildCacheState Update() { return lldb::eRefetch; }
+
+ virtual bool MightHaveChildren() { return true; }
+
+ virtual lldb::ValueObjectSP GetSyntheticValue() { return nullptr; }
+
+ virtual ConstString GetSyntheticTypeName() { return ConstString(); }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYNTHETICCHILDRENINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 0e65cb4b8ac4a..015c042004e63 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -257,12 +257,6 @@ class ScriptInterpreter : public PluginInterface {
return false;
}
- virtual StructuredData::ObjectSP
- CreateSyntheticScriptedProvider(const char *class_name,
- lldb::ValueObjectSP valobj) {
- return StructuredData::ObjectSP();
- }
-
virtual StructuredData::GenericSP
CreateScriptCommandObject(const char *class_name) {
return StructuredData::GenericSP();
@@ -348,43 +342,6 @@ class ScriptInterpreter : public PluginInterface {
// Clean up any ref counts to SBObjects that might be in global variables
}
- virtual size_t
- CalculateNumChildren(const StructuredData::ObjectSP &implementor,
- uint32_t max) {
- return 0;
- }
-
- virtual lldb::ValueObjectSP
- GetChildAtIndex(const StructuredData::ObjectSP &implementor, uint32_t idx) {
- return lldb::ValueObjectSP();
- }
-
- virtual llvm::Expected<uint32_t>
- GetIndexOfChildWithName(const StructuredData::ObjectSP &implementor,
- const char *child_name) {
- return llvm::createStringError("Type has no child named '%s'", child_name);
- }
-
- virtual bool
- UpdateSynthProviderInstance(const StructuredData::ObjectSP &implementor) {
- return false;
- }
-
- virtual bool MightHaveChildrenSynthProviderInstance(
- const StructuredData::ObjectSP &implementor) {
- return true;
- }
-
- virtual lldb::ValueObjectSP
- GetSyntheticValue(const StructuredData::ObjectSP &implementor) {
- return nullptr;
- }
-
- virtual ConstString
- GetSyntheticTypeName(const StructuredData::ObjectSP &implementor) {
- return ConstString();
- }
-
virtual bool
RunScriptBasedCommand(const char *impl_function, llvm::StringRef args,
ScriptedCommandSynchronicity synchronicity,
@@ -586,6 +543,11 @@ class ScriptInterpreter : public PluginInterface {
return {};
}
+ virtual lldb::ScriptedSyntheticChildrenInterfaceSP
+ CreateScriptedSyntheticChildrenInterface() {
+ 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..9351ed14c0524 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -268,7 +268,8 @@ enum ScriptedExtension {
eScriptedExtensionScriptedThread,
eScriptedExtensionScriptedFrame,
eScriptedExtensionScriptedStackFrameRecognizer,
- kLastScriptedExtension = eScriptedExtensionScriptedStackFrameRecognizer
+ eScriptedExtensionScriptedSyntheticChildren,
+ kLastScriptedExtension = eScriptedExtensionScriptedSyntheticChildren
};
/// Register numbering types.
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 157aa5743f016..a1292ed538145 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -200,6 +200,7 @@ class ScriptedThreadInterface;
class ScriptedThreadPlanInterface;
class ScriptedStackFrameRecognizerInterface;
class ScriptedSyntheticChildren;
+class ScriptedSyntheticChildrenInterface;
class SearchFilter;
class Section;
class SectionList;
@@ -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::ScriptedSyntheticChildrenInterface>
+ ScriptedSyntheticChildrenInterfaceSP;
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/DataFormatters/TypeSynthetic.cpp b/lldb/source/DataFormatters/TypeSynthetic.cpp
index 2200ccf3b092d..66bcd310ef770 100644
--- a/lldb/source/DataFormatters/TypeSynthetic.cpp
+++ b/lldb/source/DataFormatters/TypeSynthetic.cpp
@@ -16,6 +16,7 @@
#include "lldb/DataFormatters/FormatterBytecode.h"
#include "lldb/DataFormatters/TypeSynthetic.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Target/Target.h"
@@ -164,8 +165,7 @@ lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateChildValueObjectFromData(
ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass,
ValueObject &backend)
- : SyntheticChildrenFrontEnd(backend), m_python_class(pclass),
- m_wrapper_sp(), m_interpreter(nullptr) {
+ : SyntheticChildrenFrontEnd(backend), m_python_class(pclass) {
if (backend.GetID() == LLDB_INVALID_UID)
return;
@@ -174,87 +174,95 @@ ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass,
if (!target_sp)
return;
- m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
+ ScriptInterpreter *interpreter =
+ target_sp->GetDebugger().GetScriptInterpreter();
- if (m_interpreter != nullptr)
- m_wrapper_sp = m_interpreter->CreateSyntheticScriptedProvider(
- m_python_class.c_str(), backend.GetSP());
+ if (!interpreter)
+ return;
+
+ m_interface_sp = interpreter->CreateScriptedSyntheticChildrenInterface();
+ if (!m_interface_sp)
+ return;
+
+ auto obj_or_err = m_interface_sp->CreatePluginObject(m_python_class, backend);
+ if (!obj_or_err) {
+ llvm::consumeError(obj_or_err.takeError());
+ m_interface_sp.reset();
+ }
}
ScriptedSyntheticChildren::FrontEnd::~FrontEnd() = default;
lldb::ValueObjectSP
ScriptedSyntheticChildren::FrontEnd::GetChildAtIndex(uint32_t idx) {
- if (!m_wrapper_sp || !m_interpreter)
+ if (!m_interface_sp)
return lldb::ValueObjectSP();
- return m_interpreter->GetChildAtIndex(m_wrapper_sp, idx);
+ return m_interface_sp->GetChildAtIndex(idx);
}
bool ScriptedSyntheticChildren::FrontEnd::IsValid() {
- return (m_wrapper_sp && m_wrapper_sp->IsValid() && m_interpreter);
+ return m_interface_sp != nullptr;
}
llvm::Expected<uint32_t>
ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return 0;
- return m_interpreter->CalculateNumChildren(m_wrapper_sp, UINT32_MAX);
+ return m_interface_sp->CalculateNumChildren(UINT32_MAX);
}
llvm::Expected<uint32_t>
ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren(uint32_t max) {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return 0;
- return m_interpreter->CalculateNumChildren(m_wrapper_sp, max);
+ return m_interface_sp->CalculateNumChildren(max);
}
lldb::ChildCacheState ScriptedSyntheticChildren::FrontEnd::Update() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return lldb::ChildCacheState::eRefetch;
- return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp)
- ? lldb::ChildCacheState::eReuse
- : lldb::ChildCacheState::eRefetch;
+ return m_interface_sp->Update();
}
bool ScriptedSyntheticChildren::FrontEnd::MightHaveChildren() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return false;
- return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp);
+ return m_interface_sp->MightHaveChildren();
}
llvm::Expected<size_t>
ScriptedSyntheticChildren::FrontEnd::GetIndexOfChildWithName(ConstString name) {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return llvm::createStringErrorV("type has no child named '{0}'", name);
- return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp,
- name.GetCString());
+ return m_interface_sp->GetIndexOfChildWithName(name);
}
lldb::ValueObjectSP ScriptedSyntheticChildren::FrontEnd::GetSyntheticValue() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return nullptr;
- return m_interpreter->GetSyntheticValue(m_wrapper_sp);
+ return m_interface_sp->GetSyntheticValue();
}
ConstString ScriptedSyntheticChildren::FrontEnd::GetSyntheticTypeName() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return ConstString();
- return m_interpreter->GetSyntheticTypeName(m_wrapper_sp);
+ return m_interface_sp->GetSyntheticTypeName();
}
void *ScriptedSyntheticChildren::FrontEnd::GetImplementation() {
- if (!m_wrapper_sp || m_interpreter == nullptr)
+ if (!m_interface_sp)
return nullptr;
- if (m_wrapper_sp->GetType() != eStructuredDataTypeGeneric)
+ StructuredData::GenericSP obj = m_interface_sp->GetScriptObjectInstance();
+ if (!obj)
return nullptr;
- return m_wrapper_sp->GetAsGeneric()->GetValue();
+ return obj->GetValue();
}
std::string ScriptedSyntheticChildren::GetDescription() {
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 4f6095d097d10..873bcb2410055 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -221,6 +221,8 @@ ScriptInterpreter::ExtensionToString(lldb::ScriptedExtension extension) {
return "ScriptedFrame";
case eScriptedExtensionScriptedStackFrameRecognizer:
return "ScriptedStackFrameRecognizer";
+ case eScriptedExtensionScriptedSyntheticChildren:
+ return "ScriptedSyntheticChildren";
}
llvm_unreachable("unhandled ScriptedExtension");
}
@@ -241,6 +243,8 @@ ScriptInterpreter::StringToExtension(llvm::StringRef string) {
.CaseLower("ScriptedFrame", eScriptedExtensionScriptedFrame)
.CaseLower("ScriptedStackFrameRecognizer",
eScriptedExtensionScriptedStackFrameRecognizer)
+ .CaseLower("ScriptedSyntheticChildren",
+ eScriptedExtensionScriptedSyntheticChildren)
.Default(eScriptedExtensionInvalid);
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
index 201574da72a19..314e092d5fb47 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
@@ -33,6 +33,7 @@ set(python_plugin_sources
Interfaces/ScriptedHookPythonInterface.cpp
Interfaces/ScriptedBreakpointPythonInterface.cpp
Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
+ Interfaces/ScriptedSyntheticChildrenPythonInterface.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..cff975d99c0fb 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();
+ ScriptedSyntheticChildrenPythonInterface::Initialize();
}
void ScriptInterpreterPythonInterfaces::Terminate() {
@@ -43,4 +44,5 @@ void ScriptInterpreterPythonInterfaces::Terminate() {
ScriptedThreadPythonInterface::Terminate();
ScriptedFramePythonInterface::Terminate();
ScriptedStackFrameRecognizerPythonInterface::Terminate();
+ ScriptedSyntheticChildrenPythonInterface::Terminate();
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 03d747e63a592..5267cffbadbc9 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -20,6 +20,7 @@
#include "ScriptedPlatformPythonInterface.h"
#include "ScriptedProcessPythonInterface.h"
#include "ScriptedStackFrameRecognizerPythonInterface.h"
+#include "ScriptedSyntheticChildrenPythonInterface.h"
#include "ScriptedThreadPlanPythonInterface.h"
#include "ScriptedThreadPythonInterface.h"
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index aaa0b6a0f7a59..460f10dae2cee 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -110,7 +110,7 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerPayload>>
CheckAbstractMethodImplementation(
- const python::PythonDictionary &class_dict) const {
+ const python::PythonObject &obj_class) const {
using namespace python;
@@ -124,18 +124,17 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
for (const AbstractMethodRequirement &requirement :
GetAbstractMethodRequirements()) {
llvm::StringLiteral method_name = requirement.name;
- if (!class_dict.HasKey(method_name))
+ // Look up via attribute access so inherited methods are found; the
+ // class's own __dict__ omits anything defined on a base class.
+ if (!obj_class.HasAttribute(method_name))
SET_CASE_AND_CONTINUE(method_name,
AbstractMethodCheckerCases::eNotImplemented)
- llvm::Expected<PythonObject> callable_or_err =
- class_dict.GetItem(method_name);
- if (!callable_or_err) {
- llvm::consumeError(callable_or_err.takeError());
+ PythonObject attr = obj_class.GetAttributeValue(method_name);
+ if (!attr.IsAllocated())
SET_CASE_AND_CONTINUE(method_name,
AbstractMethodCheckerCases::eNotAllocated)
- }
- PythonCallable callable = callable_or_err->AsType<PythonCallable>();
+ PythonCallable callable = attr.AsType<PythonCallable>();
if (!callable)
SET_CASE_AND_CONTINUE(method_name,
AbstractMethodCheckerCases::eNotCallable)
@@ -295,26 +294,7 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
PythonString obj_class_name =
obj_class.GetAttributeValue("__name__").AsType<PythonString>();
- PythonObject object_class_mapping_proxy =
- obj_class.GetAttributeValue("__dict__");
- if (!obj_class.HasAttribute("__dict__"))
- return create_error(
- "Resulting object class doesn't have '__dict__' member.");
-
- PythonCallable dict_converter = PythonModule::BuiltinsModule()
- .ResolveName("dict")
- .AsType<PythonCallable>();
- if (!dict_converter.IsAllocated())
- return create_error(
- "Python 'builtins' module doesn't have 'dict' class.");
-
- PythonDictionary object_class_dict =
- dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();
- if (!object_class_dict.IsAllocated())
- return create_error("Coudn't create dictionary from resulting object "
- "class mapping proxy object.");
-
- auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict);
+ auto checker_or_err = CheckAbstractMethodImplementation(obj_class);
if (!checker_or_err)
return checker_or_err.takeError();
@@ -531,15 +511,36 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
std::tuple<Args...> original_args = std::forward_as_tuple(args...);
auto transformed_args = TransformArgs(original_args);
+ // Trim trailing args if the Python method accepts fewer positional
+ // parameters than we're passing (e.g. `num_children(self)` vs.
+ // `num_children(self, max_count)`).
+ size_t call_arity = sizeof...(Args);
+ if (PythonObject py_method = implementor.GetAttributeValue(method_name);
+ py_method.IsAllocated()) {
+ PythonCallable callable = py_method.AsType<PythonCallable>();
+ if (callable.IsAllocated()) {
+ if (llvm::Expected<PythonCallable::ArgInfo> arg_info =
+ callable.GetArgInfo()) {
+ if (arg_info->max_positional_args !=
+ PythonCallable::ArgInfo::UNBOUNDED &&
+ arg_info->max_positional_args < call_arity)
+ call_arity = arg_info->max_positional_args;
+ } else {
+ llvm::consumeError(arg_info.takeError());
+ }
+ }
+ }
+
llvm::Expected<PythonObject> expected_return_object =
llvm::createStringError("not initialized");
- std::apply(
- [&implementor, &method_name, &expected_return_object](auto &&...args) {
- llvm::consumeError(expected_return_object.takeError());
- expected_return_object =
- implementor.CallMethod(method_name.data(), args...);
- },
- transformed_args);
+ CallWithArity(call_arity, transformed_args,
+ std::make_index_sequence<sizeof...(Args) + 1>{},
+ [&implementor, &method_name,
+ &expected_return_object](auto &&...call_args) {
+ llvm::consumeError(expected_return_object.takeError());
+ expected_return_object = implementor.CallMethod(
+ method_name.data(), call_args...);
+ });
if (llvm::Error e = expected_return_object.takeError()) {
error = Status::FromError(std::move(e));
@@ -703,6 +704,30 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());
}
+ // Apply `fn` with the first `N` elements of `t`, for compile-time `N`.
+ template <std::size_t N, typename Tuple, typename Fn, std::size_t... I>
+ static void ApplyPrefixImpl(Tuple &&t, Fn &&fn, std::index_sequence<I...>) {
+ std::forward<Fn>(fn)(std::get<I>(std::forward<Tuple>(t))...);
+ }
+
+ template <std::size_t N, typename Tuple, typename Fn>
+ static void ApplyPrefix(Tuple &&t, Fn &&fn) {
+ ApplyPrefixImpl<N>(std::forward<Tuple>(t), std::forward<Fn>(fn),
+ std::make_index_sequence<N>{});
+ }
+
+ // Call `fn` with a runtime-selected prefix of `t`: exactly `call_arity`
+ // leading elements. `Is...` enumerates every compile-time count in
+ // `[0, sizeof...(Args)]`; the runtime check picks the matching one.
+ template <typename Tuple, std::size_t... Is, typename Fn>
+ static void CallWithArity(size_t call_arity, Tuple &&t,
+ std::index_sequence<Is...>, Fn &&fn) {
+ (void)std::initializer_list<int>{(
+ Is == call_arity
+ ? (ApplyPrefix<Is>(std::forward<Tuple>(t), std::forward<Fn>(fn)), 0)
+ : 0)...};
+ }
+
template <typename T, typename U>
void TransformBack(T &original_arg, U transformed_arg, Status &error) {
ReverseTransform(original_arg, transformed_arg, error);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.cpp
new file mode 100644
index 0000000000000..ad86406c0d78f
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.cpp
@@ -0,0 +1,141 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Core/PluginManager.h"
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedSyntheticChildrenPythonInterface.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedSyntheticChildrenPythonInterface::
+ ScriptedSyntheticChildrenPythonInterface(
+ ScriptInterpreterPythonImpl &interpreter)
+ : ScriptedSyntheticChildrenInterface(),
+ ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedSyntheticChildrenPythonInterface::CreatePluginObject(
+ llvm::StringRef class_name, ValueObject &backend) {
+ if (class_name.empty())
+ return llvm::createStringError("empty class name");
+
+ ValueObjectSP valobj_sp = backend.GetSP();
+ if (!valobj_sp)
+ return llvm::createStringError("invalid backing value");
+
+ Locker py_lock(&m_interpreter,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN,
+ Locker::FreeLock | Locker::TearDownSession);
+
+ // Hand the provider's __init__ a fresh SBValue view of the backing value
+ // with synthetic children disabled, so introspecting it doesn't recursively
+ // re-enter this provider. `SetPreferSyntheticValue` lives on the SBValue's
+ // ValueImpl, so this override doesn't affect the caller's original view.
+ PythonObject val_arg =
+ SWIGBridge::ToSWIGWrapper(valobj_sp, /*use_synthetic=*/false);
+
+ ScriptedMetadata scripted_metadata(class_name,
+ StructuredData::DictionarySP());
+ return ScriptedPythonInterface::CreatePluginObject(
+ scripted_metadata, /*script_obj=*/nullptr, std::move(val_arg));
+}
+
+llvm::Expected<uint32_t>
+ScriptedSyntheticChildrenPythonInterface::CalculateNumChildren(uint32_t max) {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("num_children", error, max);
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return 0;
+ // Cap at max in case the provider ignores the argument (e.g. defines
+ // `num_children(self)`) and returns an unbounded count.
+ return std::min<uint32_t>(obj->GetUnsignedIntegerValue(), max);
+}
+
+lldb::ValueObjectSP
+ScriptedSyntheticChildrenPythonInterface::GetChildAtIndex(uint32_t idx) {
+ Status error;
+ return Dispatch<lldb::ValueObjectSP>("get_child_at_index", error, idx);
+}
+
+llvm::Expected<uint32_t>
+ScriptedSyntheticChildrenPythonInterface::GetIndexOfChildWithName(
+ ConstString name) {
+ Status error;
+ StructuredData::ObjectSP obj =
+ Dispatch("get_child_index", error, name.GetCString());
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return llvm::createStringErrorV("type has no child named '{0}'", name);
+
+ int64_t retval = obj->GetSignedIntegerValue(-1);
+ if (retval < 0)
+ return llvm::createStringErrorV("type has no child named '{0}'", name);
+ return static_cast<uint32_t>(retval);
+}
+
+lldb::ChildCacheState ScriptedSyntheticChildrenPythonInterface::Update() {
+ Status error;
+ // update() is optional; a missing method means "always refetch".
+ StructuredData::ObjectSP obj = Dispatch("update", error);
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return lldb::eRefetch;
+ return obj->GetBooleanValue() ? lldb::eReuse : lldb::eRefetch;
+}
+
+bool ScriptedSyntheticChildrenPythonInterface::MightHaveChildren() {
+ Status error;
+ // has_children() is optional and defaults to True when missing.
+ StructuredData::ObjectSP obj = Dispatch("has_children", error);
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return true;
+ return obj->GetBooleanValue();
+}
+
+lldb::ValueObjectSP
+ScriptedSyntheticChildrenPythonInterface::GetSyntheticValue() {
+ Status error;
+ return Dispatch<lldb::ValueObjectSP>("get_value", error);
+}
+
+ConstString ScriptedSyntheticChildrenPythonInterface::GetSyntheticTypeName() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_type_name", error);
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+ return ConstString(obj->GetStringValue());
+}
+
+void ScriptedSyntheticChildrenPythonInterface::Initialize() {
+ const std::vector<llvm::StringRef> ci_usages = {
+ "type synthetic add -l <ClassName> <TypeName>"};
+ const std::vector<llvm::StringRef> api_usages = {
+ "SBTypeSynthetic.CreateWithClassName"};
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(),
+ "Provide synthetic children for a type, used by 'type synthetic add -l'",
+ CreateInstance, eScriptedExtensionScriptedSyntheticChildren,
+ eScriptLanguagePython, {ci_usages, api_usages});
+}
+
+void ScriptedSyntheticChildrenPythonInterface::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.h
new file mode 100644
index 0000000000000..0cb2ffc13415f
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.h
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SCRIPTEDSYNTHETICCHILDRENPYTHONINTERFACE_H
+#define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYNTHETICCHILDRENPYTHONINTERFACE_H
+
+#include "lldb/Interpreter/Interfaces/ScriptedSyntheticChildrenInterface.h"
+
+#include "ScriptedPythonInterface.h"
+namespace lldb_private {
+
+class ScriptedSyntheticChildrenPythonInterface
+ : public ScriptedSyntheticChildrenInterface,
+ public ScriptedPythonInterface,
+ public PluginInterface {
+public:
+ ScriptedSyntheticChildrenPythonInterface(
+ ScriptInterpreterPythonImpl &interpreter);
+
+ llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, ValueObject &backend) override;
+
+ llvm::SmallVector<AbstractMethodRequirement>
+ GetAbstractMethodRequirements() const override {
+ // Providers that never expose children (num_children == 0 / has_children ==
+ // False) legitimately don't implement get_child_at_index; LLDB simply
+ // won't call it. Treating any single method as required here is stricter
+ // than the pre-migration behavior and would reject those providers.
+ return {};
+ }
+
+ llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) override;
+
+ lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+ llvm::Expected<uint32_t> GetIndexOfChildWithName(ConstString name) override;
+
+ lldb::ChildCacheState Update() override;
+
+ bool MightHaveChildren() override;
+
+ lldb::ValueObjectSP GetSyntheticValue() override;
+
+ ConstString GetSyntheticTypeName() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static llvm::StringRef GetPluginNameStatic() {
+ return "ScriptedSyntheticChildrenPythonInterface";
+ }
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+};
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYNTHETICCHILDRENPYTHONINTERFACE_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 07e0da1dcf70d..3880e31dfa7ce 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -79,6 +79,8 @@ class SWIGBridge {
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBCommandReturnObject> result_up);
static PythonObject ToSWIGWrapper(lldb::ValueObjectSP value_sp);
+ static PythonObject ToSWIGWrapper(lldb::ValueObjectSP value_sp,
+ bool use_synthetic);
static PythonObject ToSWIGWrapper(lldb::TargetSP target_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessSP process_sp);
static PythonObject ToSWIGWrapper(lldb::ModuleSP module_sp);
@@ -138,37 +140,14 @@ class SWIGBridge {
const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,
const lldb::TypeSummaryOptionsSP &options_sp, std::string &retval);
- static python::PythonObject
- LLDBSwigPythonCreateSyntheticProvider(const char *python_class_name,
- 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);
-
- static PyObject *LLDBSwigPython_GetChildAtIndex(PyObject *implementor,
- uint32_t idx);
-
- static uint32_t
- LLDBSwigPython_GetIndexOfChildWithName(PyObject *implementor,
- const char *child_name);
-
static lldb::ValueObjectSP
LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data);
- static bool LLDBSwigPython_UpdateSynthProviderInstance(PyObject *implementor);
-
- static bool
- LLDBSwigPython_MightHaveChildrenSynthProviderInstance(PyObject *implementor);
-
- static PyObject *
- LLDBSwigPython_GetValueSynthProviderInstance(PyObject *implementor);
-
static bool
LLDBSwigPythonCallCommand(const char *python_function_name,
const char *session_dictionary_name,
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 7b1bd9d8411c5..6df6d5a071bf1 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -289,6 +289,8 @@ llvm::Expected<std::string> ScriptInterpreterPython::ExtensionToImportPath(
return "lldb.plugins.scripted_process";
case eScriptedExtensionScriptedStackFrameRecognizer:
return "lldb.plugins.scripted_stackframe_recognizer";
+ case eScriptedExtensionScriptedSyntheticChildren:
+ return "lldb.plugins.scripted_synthetic_children";
case eScriptedExtensionInvalid:
return llvm::createStringError("invalid extension name");
}
@@ -2003,6 +2005,11 @@ ScriptInterpreterPythonImpl::CreateScriptedStackFrameRecognizerInterface() {
return std::make_shared<ScriptedStackFrameRecognizerPythonInterface>(*this);
}
+ScriptedSyntheticChildrenInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedSyntheticChildrenInterface() {
+ return std::make_shared<ScriptedSyntheticChildrenPythonInterface>(*this);
+}
+
ScriptedThreadInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
return std::make_shared<ScriptedThreadPythonInterface>(*this);
@@ -2086,37 +2093,6 @@ StructuredData::DictionarySP ScriptInterpreterPythonImpl::GetDynamicSettings(
return py_dict.CreateStructuredDictionary();
}
-StructuredData::ObjectSP
-ScriptInterpreterPythonImpl::CreateSyntheticScriptedProvider(
- const char *class_name, lldb::ValueObjectSP valobj) {
- if (class_name == nullptr || class_name[0] == '\0')
- return StructuredData::ObjectSP();
-
- if (!valobj.get())
- return StructuredData::ObjectSP();
-
- ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
- Target *target = exe_ctx.GetTargetPtr();
-
- if (!target)
- return StructuredData::ObjectSP();
-
- Debugger &debugger = target->GetDebugger();
- ScriptInterpreterPythonImpl *python_interpreter =
- GetPythonInterpreter(debugger);
-
- if (!python_interpreter)
- return StructuredData::ObjectSP();
-
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- PythonObject ret_val = SWIGBridge::LLDBSwigPythonCreateSyntheticProvider(
- class_name, python_interpreter->m_dictionary_name.c_str(), valobj);
-
- return StructuredData::ObjectSP(
- new StructuredPythonObject(std::move(ret_val)));
-}
-
StructuredData::GenericSP
ScriptInterpreterPythonImpl::CreateScriptCommandObject(const char *class_name) {
DebuggerSP debugger_sp(m_debugger.shared_from_this());
@@ -2375,208 +2351,6 @@ bool ScriptInterpreterPythonImpl::WatchpointCallbackFunction(
return true;
}
-size_t ScriptInterpreterPythonImpl::CalculateNumChildren(
- const StructuredData::ObjectSP &implementor_sp, uint32_t max) {
- if (!implementor_sp)
- return 0;
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return 0;
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return 0;
-
- size_t ret_val = 0;
-
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- ret_val = SWIGBridge::LLDBSwigPython_CalculateNumChildren(implementor, max);
- }
-
- return ret_val;
-}
-
-lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetChildAtIndex(
- const StructuredData::ObjectSP &implementor_sp, uint32_t idx) {
- if (!implementor_sp)
- return lldb::ValueObjectSP();
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return lldb::ValueObjectSP();
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return lldb::ValueObjectSP();
-
- lldb::ValueObjectSP ret_val;
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- PyObject *child_ptr =
- SWIGBridge::LLDBSwigPython_GetChildAtIndex(implementor, idx);
- if (child_ptr != nullptr && child_ptr != Py_None) {
- lldb::SBValue *sb_value_ptr =
- (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
- if (sb_value_ptr == nullptr)
- Py_XDECREF(child_ptr);
- else
- ret_val = SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(
- sb_value_ptr);
- } else {
- Py_XDECREF(child_ptr);
- }
- }
-
- return ret_val;
-}
-
-llvm::Expected<uint32_t> ScriptInterpreterPythonImpl::GetIndexOfChildWithName(
- const StructuredData::ObjectSP &implementor_sp, const char *child_name) {
- if (!implementor_sp)
- return llvm::createStringErrorV("type has no child named '{0}'",
- child_name);
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return llvm::createStringErrorV("type has no child named '{0}'",
- child_name);
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return llvm::createStringErrorV("type has no child named '{0}'",
- child_name);
-
- uint32_t ret_val = UINT32_MAX;
-
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- ret_val = SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName(implementor,
- child_name);
- }
-
- if (ret_val == UINT32_MAX)
- return llvm::createStringErrorV("type has no child named '{0}'",
- child_name);
- return ret_val;
-}
-
-bool ScriptInterpreterPythonImpl::UpdateSynthProviderInstance(
- const StructuredData::ObjectSP &implementor_sp) {
- bool ret_val = false;
-
- if (!implementor_sp)
- return ret_val;
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return ret_val;
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return ret_val;
-
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- ret_val =
- SWIGBridge::LLDBSwigPython_UpdateSynthProviderInstance(implementor);
- }
-
- return ret_val;
-}
-
-bool ScriptInterpreterPythonImpl::MightHaveChildrenSynthProviderInstance(
- const StructuredData::ObjectSP &implementor_sp) {
- bool ret_val = false;
-
- if (!implementor_sp)
- return ret_val;
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return ret_val;
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return ret_val;
-
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- ret_val = SWIGBridge::LLDBSwigPython_MightHaveChildrenSynthProviderInstance(
- implementor);
- }
-
- return ret_val;
-}
-
-lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetSyntheticValue(
- const StructuredData::ObjectSP &implementor_sp) {
- lldb::ValueObjectSP ret_val(nullptr);
-
- if (!implementor_sp)
- return ret_val;
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return ret_val;
- auto *implementor = static_cast<PyObject *>(generic->GetValue());
- if (!implementor)
- return ret_val;
-
- {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
- PyObject *child_ptr =
- SWIGBridge::LLDBSwigPython_GetValueSynthProviderInstance(implementor);
- if (child_ptr != nullptr && child_ptr != Py_None) {
- lldb::SBValue *sb_value_ptr =
- (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
- if (sb_value_ptr == nullptr)
- Py_XDECREF(child_ptr);
- else
- ret_val = SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(
- sb_value_ptr);
- } else {
- Py_XDECREF(child_ptr);
- }
- }
-
- return ret_val;
-}
-
-ConstString ScriptInterpreterPythonImpl::GetSyntheticTypeName(
- const StructuredData::ObjectSP &implementor_sp) {
- Locker py_lock(this,
- Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
-
- if (!implementor_sp)
- return {};
-
- StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
- if (!generic)
- return {};
-
- PythonObject implementor(PyRefType::Borrowed,
- (PyObject *)generic->GetValue());
- if (!implementor.IsAllocated())
- return {};
-
- llvm::Expected<PythonObject> expected_py_return =
- implementor.CallMethod("get_type_name");
-
- if (!expected_py_return) {
- llvm::consumeError(expected_py_return.takeError());
- return {};
- }
-
- PythonObject py_return = std::move(expected_py_return.get());
- if (!py_return.IsAllocated() || !PythonString::Check(py_return.get()))
- return {};
-
- PythonString type_name(PyRefType::Borrowed, py_return.get());
- return ConstString(type_name.GetString());
-}
-
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, Process *process, std::string &output,
Status &error) {
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index d8a817198253f..d32e9dde4c6f1 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -66,10 +66,6 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
bool GenerateScriptAliasFunction(StringList &input,
std::string &output) override;
- StructuredData::ObjectSP
- CreateSyntheticScriptedProvider(const char *class_name,
- lldb::ValueObjectSP valobj) override;
-
StructuredData::GenericSP
CreateScriptCommandObject(const char *class_name) override;
@@ -86,6 +82,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
lldb::ScriptedStackFrameRecognizerInterfaceSP
CreateScriptedStackFrameRecognizerInterface() override;
+ lldb::ScriptedSyntheticChildrenInterfaceSP
+ CreateScriptedSyntheticChildrenInterface() override;
+
lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
@@ -107,29 +106,6 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
const char *setting_name,
lldb_private::Status &error) override;
- size_t CalculateNumChildren(const StructuredData::ObjectSP &implementor,
- uint32_t max) override;
-
- lldb::ValueObjectSP
- GetChildAtIndex(const StructuredData::ObjectSP &implementor,
- uint32_t idx) override;
-
- llvm::Expected<uint32_t>
- GetIndexOfChildWithName(const StructuredData::ObjectSP &implementor,
- const char *child_name) override;
-
- bool UpdateSynthProviderInstance(
- const StructuredData::ObjectSP &implementor) override;
-
- bool MightHaveChildrenSynthProviderInstance(
- const StructuredData::ObjectSP &implementor) override;
-
- lldb::ValueObjectSP
- GetSyntheticValue(const StructuredData::ObjectSP &implementor) override;
-
- ConstString
- GetSyntheticTypeName(const StructuredData::ObjectSP &implementor) override;
-
bool
RunScriptBasedCommand(const char *impl_function, llvm::StringRef args,
ScriptedCommandSynchronicity synchronicity,
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index c9298191ec3c1..7a5eb7aae548e 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -66,13 +66,6 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallTypeScript(
return false;
}
-python::PythonObject
-lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProvider(
- const char *python_class_name, const char *session_dictionary_name,
- const lldb::ValueObjectSP &valobj_sp) {
- return python::PythonObject();
-}
-
python::PythonObject
lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject(
const char *python_class_name, const char *session_dictionary_name,
@@ -80,22 +73,6 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject(
return python::PythonObject();
}
-size_t lldb_private::python::SWIGBridge::LLDBSwigPython_CalculateNumChildren(
- PyObject *implementor, uint32_t max) {
- return 0;
-}
-
-PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetChildAtIndex(
- PyObject *implementor, uint32_t idx) {
- return nullptr;
-}
-
-uint32_t
-lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName(
- PyObject *implementor, const char *child_name) {
- return 0;
-}
-
void *
lldb_private::python::LLDBSWIGPython_CastPyObjectToSBData(PyObject *data) {
return nullptr;
@@ -182,23 +159,6 @@ lldb_private::python::SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(
return nullptr;
}
-bool lldb_private::python::SWIGBridge::
- LLDBSwigPython_UpdateSynthProviderInstance(PyObject *implementor) {
- return false;
-}
-
-bool lldb_private::python::SWIGBridge::
- LLDBSwigPython_MightHaveChildrenSynthProviderInstance(
- PyObject *implementor) {
- return false;
-}
-
-PyObject *
-lldb_private::python::SWIGBridge::LLDBSwigPython_GetValueSynthProviderInstance(
- PyObject *implementor) {
- return nullptr;
-}
-
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand(
const char *python_function_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger, const char *args,
More information about the lldb-commits
mailing list