https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/210845
>From 8d99c8b57a423bbaa8fc87f000e49e907bbe7ef6 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Tue, 28 Jul 2026 23:54:45 -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 <[email protected]> --- lldb/bindings/python/CMakeLists.txt | 1 + lldb/bindings/python/python-swigsafecast.swig | 7 + lldb/bindings/python/python-wrapper.swig | 163 +----------- 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 | 26 +- .../Python/ScriptInterpreterPython.cpp | 240 +----------------- .../Python/ScriptInterpreterPythonImpl.h | 30 +-- .../Python/PythonTestSuite.cpp | 37 +-- 22 files changed, 502 insertions(+), 586 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 6ffdf9ccafabc..517590f51f0c4 100644 --- a/lldb/bindings/python/CMakeLists.txt +++ b/lldb/bindings/python/CMakeLists.txt @@ -121,6 +121,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar "${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" + "${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 c4071b3f7b5b1..868c172880c7d 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -181,15 +181,14 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallTypeScript( return true; } -PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProvider( +PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject( const char *python_class_name, const char *session_dictionary_name, - const lldb::ValueObjectSP &valobj_sp) { + 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>( @@ -198,19 +197,7 @@ PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProv 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(); + return pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger_sp)), dict); } // wrapper that calls an optional instance member of an object taking no @@ -237,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 53647990f7842..d27d1c6f837c8 100644 --- a/lldb/docs/CMakeLists.txt +++ b/lldb/docs/CMakeLists.txt @@ -34,6 +34,7 @@ if (LLDB_ENABLE_PYTHON AND SPHINX_FOUND) 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/" + 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 9d3e7e6f32ff5..a622a726b2b66 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::ObjectSP LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) { return StructuredData::ObjectSP(); @@ -343,43 +337,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, @@ -512,6 +469,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 f73d0085f2320..f7a0641594513 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -270,7 +270,8 @@ enum ScriptedExtension { eScriptedExtensionScriptedStackFrameRecognizer, eScriptedExtensionScriptedCommand, eScriptedExtensionParsedCommand, - kLastScriptedExtension = eScriptedExtensionParsedCommand + eScriptedExtensionScriptedSyntheticChildren, + kLastScriptedExtension = eScriptedExtensionScriptedSyntheticChildren }; /// Register numbering types. diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 2572aa0dc344b..0e0b2fa64b033 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -201,6 +201,7 @@ class ScriptedThreadInterface; class ScriptedThreadPlanInterface; class ScriptedStackFrameRecognizerInterface; class ScriptedSyntheticChildren; +class ScriptedSyntheticChildrenInterface; class SearchFilter; class Section; class SectionList; @@ -441,6 +442,8 @@ typedef std::shared_ptr<lldb_private::ScriptedStackFrameRecognizerInterface> ScriptedStackFrameRecognizerInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedCommandInterface> ScriptedCommandInterfaceSP; +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 04ec3f5421981..705a98847d3fd 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -238,6 +238,8 @@ ScriptInterpreter::ExtensionToString(lldb::ScriptedExtension extension) { return "ScriptedCommand"; case eScriptedExtensionParsedCommand: return "ParsedCommand"; + case eScriptedExtensionScriptedSyntheticChildren: + return "ScriptedSyntheticChildren"; } llvm_unreachable("unhandled ScriptedExtension"); } @@ -260,6 +262,8 @@ ScriptInterpreter::StringToExtension(llvm::StringRef string) { eScriptedExtensionScriptedStackFrameRecognizer) .CaseLower("ScriptedCommand", eScriptedExtensionScriptedCommand) .CaseLower("ParsedCommand", eScriptedExtensionParsedCommand) + .CaseLower("ScriptedSyntheticChildren", + eScriptedExtensionScriptedSyntheticChildren) .Default(eScriptedExtensionInvalid); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index d3dc5773eb0af..8bc0923c4fb1d 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -34,6 +34,7 @@ set(python_plugin_sources Interfaces/ScriptedBreakpointPythonInterface.cpp Interfaces/ScriptedCommandPythonInterface.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 2c8e0ef8bbeb0..066315c520145 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp @@ -31,6 +31,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() { ScriptedFramePythonInterface::Initialize(); ScriptedStackFrameRecognizerPythonInterface::Initialize(); ScriptedCommandPythonInterface::Initialize(); + ScriptedSyntheticChildrenPythonInterface::Initialize(); } void ScriptInterpreterPythonInterfaces::Terminate() { @@ -45,4 +46,5 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedFramePythonInterface::Terminate(); ScriptedStackFrameRecognizerPythonInterface::Terminate(); ScriptedCommandPythonInterface::Terminate(); + ScriptedSyntheticChildrenPythonInterface::Terminate(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 7ddf7ec0e5ae5..7664d086d2baf 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -21,6 +21,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 d82a59738a7db..0e6cfcff32f7b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -111,7 +111,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; @@ -125,18 +125,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) @@ -296,26 +295,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(); @@ -532,15 +512,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)); @@ -720,6 +721,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 9c5391e754396..2530342f74bd3 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); @@ -139,31 +141,13 @@ class SWIGBridge { 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 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); + LLDBSwigPythonCreateCommandObject(const char *python_class_name, + const char *session_dictionary_name, + lldb::DebuggerSP debugger_sp); 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 bb8871e234bc1..ff44721aebb1b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -292,6 +292,8 @@ llvm::Expected<std::string> ScriptInterpreterPython::ExtensionToImportPath( case eScriptedExtensionScriptedCommand: case eScriptedExtensionParsedCommand: return "lldb.plugins.scripted_command"; + case eScriptedExtensionScriptedSyntheticChildren: + return "lldb.plugins.scripted_synthetic_children"; case eScriptedExtensionInvalid: return llvm::createStringError("invalid extension name"); } @@ -2011,6 +2013,11 @@ ScriptInterpreterPythonImpl::CreateScriptedCommandInterface() { return std::make_shared<ScriptedCommandPythonInterface>(*this); } +ScriptedSyntheticChildrenInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedSyntheticChildrenInterface() { + return std::make_shared<ScriptedSyntheticChildrenPythonInterface>(*this); +} + ScriptedThreadInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); @@ -2094,37 +2101,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))); -} - bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction( const char *oneliner, std::string &output, const void *name_token) { StringList input; @@ -2361,208 +2337,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 51b373afbb9f4..5121624ca2030 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::ObjectSP CreateStructuredDataFromScriptObject(ScriptObject obj) override; @@ -85,6 +81,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedCommandInterfaceSP CreateScriptedCommandInterface() override; + lldb::ScriptedSyntheticChildrenInterfaceSP + CreateScriptedSyntheticChildrenInterface() override; + lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; @@ -106,29 +105,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 808bb6157e5b8..9b6c3af3c9563 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -67,28 +67,12 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallTypeScript( } python::PythonObject -lldb_private::python::SWIGBridge::LLDBSwigPythonCreateSyntheticProvider( +lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject( const char *python_class_name, const char *session_dictionary_name, - const lldb::ValueObjectSP &valobj_sp) { + lldb::DebuggerSP debugger_sp) { 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; @@ -185,23 +169,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, _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
