https://github.com/medismailben created https://github.com/llvm/llvm-project/pull/210845
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`. Teach `Dispatch<T>()` 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)`) work through the generic dispatch path. `CalculateNumChildren` now uses `Dispatch<T>()` and the standalone `LLDBSwigPython_CalculateNumChildren` bridge is removed. Only `CreatePluginObject` takes a full `InitSession`/`TearDownSession` session; per-call methods just acquire the GIL. >From c6c12414c6ee48bd6609ab55b48e04200c7959af Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Mon, 20 Jul 2026 16:07:52 -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`. `CreatePluginObject` goes through the generic `ScriptedPythonInterface::CreatePluginObject` template, so `LLDBSwigPythonCreateSyntheticProvider` is removed. A new `SWIGBridge::ToSWIGWrapper(ValueObjectSP, bool use_synthetic)` overload preserves the pre-migration `SetPreferSyntheticValue(false)` on the SBValue view handed to `__init__`, so the provider does not recursively re-enter its own synthetic children while introspecting its backing value. Teach `Dispatch<T>()` 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)`) work through the generic dispatch path. `CalculateNumChildren` now uses `Dispatch<T>()` and the standalone `LLDBSwigPython_CalculateNumChildren` bridge is removed. Only `CreatePluginObject` takes a full `InitSession`/`TearDownSession` session; per-call methods just acquire the GIL. 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 | 64 ----- lldb/docs/CMakeLists.txt | 1 + .../templates/scripted_synthetic_children.py | 111 ++++++++ .../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 | 96 ++++--- ...riptedSyntheticChildrenPythonInterface.cpp | 233 +++++++++++++++++ ...ScriptedSyntheticChildrenPythonInterface.h | 63 +++++ .../Python/SWIGPythonBridge.h | 10 +- .../Python/ScriptInterpreterPython.cpp | 240 +----------------- .../Python/ScriptInterpreterPythonImpl.h | 30 +-- .../Python/PythonTestSuite.cpp | 12 - 22 files changed, 589 insertions(+), 454 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..0beac24568c84 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,38 +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) { 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..61ce038b7f58b --- /dev/null +++ b/lldb/examples/python/templates/scripted_synthetic_children.py @@ -0,0 +1,111 @@ +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, internal_dict: dict): + """Construct a scripted synthetic children provider. + + Args: + valobj (lldb.SBValue): The value this provider generates children + for. + internal_dict (dict): The session dictionary for the embedded + interpreter, unused in most implementations. + """ + 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..0e2b874b95e91 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,31 @@ 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..46f7afd72748f --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSyntheticChildrenPythonInterface.cpp @@ -0,0 +1,233 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../lldb-python.h" + +#include "lldb/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) { + if (!m_object_instance_sp) + return lldb::ValueObjectSP(); + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return lldb::ValueObjectSP(); + + PyObject *child_ptr = + SWIGBridge::LLDBSwigPython_GetChildAtIndex(implementor.get(), idx); + if (child_ptr == nullptr || child_ptr == Py_None) { + Py_XDECREF(child_ptr); + return lldb::ValueObjectSP(); + } + + lldb::SBValue *sb_value_ptr = + (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr); + if (sb_value_ptr == nullptr) { + Py_XDECREF(child_ptr); + return lldb::ValueObjectSP(); + } + + return SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr); +} + +llvm::Expected<uint32_t> +ScriptedSyntheticChildrenPythonInterface::GetIndexOfChildWithName( + ConstString name) { + if (!m_object_instance_sp) + return llvm::createStringErrorV("type has no child named '{0}'", name); + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return llvm::createStringErrorV("type has no child named '{0}'", name); + + uint32_t ret_val = SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName( + implementor.get(), name.GetCString()); + + if (ret_val == UINT32_MAX) + return llvm::createStringErrorV("type has no child named '{0}'", name); + return ret_val; +} + +lldb::ChildCacheState ScriptedSyntheticChildrenPythonInterface::Update() { + if (!m_object_instance_sp) + return lldb::eRefetch; + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return lldb::eRefetch; + + // update() is optional; a missing method means "always refetch", matching + // LLDBSwigPython_UpdateSynthProviderInstance's behavior. + return SWIGBridge::LLDBSwigPython_UpdateSynthProviderInstance( + implementor.get()) + ? lldb::eReuse + : lldb::eRefetch; +} + +bool ScriptedSyntheticChildrenPythonInterface::MightHaveChildren() { + if (!m_object_instance_sp) + return true; + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return true; + + // has_children() is optional and defaults to True when missing, matching + // LLDBSwigPython_MightHaveChildrenSynthProviderInstance's behavior. + return SWIGBridge::LLDBSwigPython_MightHaveChildrenSynthProviderInstance( + implementor.get()); +} + +lldb::ValueObjectSP +ScriptedSyntheticChildrenPythonInterface::GetSyntheticValue() { + if (!m_object_instance_sp) + return nullptr; + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return nullptr; + + PyObject *child_ptr = + SWIGBridge::LLDBSwigPython_GetValueSynthProviderInstance( + implementor.get()); + if (child_ptr == nullptr || child_ptr == Py_None) { + Py_XDECREF(child_ptr); + return nullptr; + } + + lldb::SBValue *sb_value_ptr = + (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr); + if (sb_value_ptr == nullptr) { + Py_XDECREF(child_ptr); + return nullptr; + } + + return SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr); +} + +ConstString ScriptedSyntheticChildrenPythonInterface::GetSyntheticTypeName() { + if (!m_object_instance_sp) + return {}; + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->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()); +} + +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..97e06d06ed52c 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,19 +140,11 @@ 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); 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..4aa8992b6aed6 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,11 +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; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
