https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/210469
>From 547ab0ba7e261272cfd76e44d536eaf67a6f683c Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Fri, 17 Jul 2026 17:24:53 -0700 Subject: [PATCH] [lldb/script] Add class-based summary providers via ScriptedStringSummaryInterface Add `type summary add -L <ClassName>` as a class-based alternative to the existing function-based summary providers, purely additive: nothing about ScriptSummaryFormat/GetScriptedSummary (the existing function-based path) is touched. ScriptedSummaryFormat (TypeSummary.h/.cpp) is a new TypeSummaryImpl backed by a new Kind::eScriptedClass, parallel to ScriptSummaryFormat. Unlike every other scripted extension in this series, its interface object cannot be created eagerly at construction time -- SBTypeSummary::CreateWithClassName can run before any debugger/target context exists -- so CreatePluginObject is deferred to the first FormatObject() call and cached from then on, mirroring how ScriptSummaryFormat already lazily resolves and caches its function object. SBTypeSummary::CreateWithClassName mirrors SBTypeSynthetic::CreateWithClassName's existing shape. The two exhaustive switches over TypeSummaryImpl::Kind (GetSummaryKindName, SBTypeSummary::IsEqualTo) each get a case for eScriptedClass so the new kind does not trip a -Wswitch failure or silently compare unequal. Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/bindings/python/CMakeLists.txt | 1 + lldb/docs/CMakeLists.txt | 1 + .../templates/scripted_string_summary.py | 40 +++++++++ lldb/include/lldb/API/SBTypeSummary.h | 4 + .../include/lldb/DataFormatters/TypeSummary.h | 54 +++++++++++- .../ScriptedStringSummaryInterface.h | 28 ++++++ .../lldb/Interpreter/ScriptInterpreter.h | 5 ++ lldb/include/lldb/lldb-enumerations.h | 3 +- lldb/include/lldb/lldb-forward.h | 3 + lldb/source/API/SBTypeSummary.cpp | 25 ++++++ lldb/source/Commands/CommandObjectType.cpp | 73 ++++++++++++++-- lldb/source/DataFormatters/TypeSummary.cpp | 75 ++++++++++++++++ lldb/source/Interpreter/ScriptInterpreter.cpp | 4 + .../ScriptInterpreter/Python/CMakeLists.txt | 1 + .../ScriptInterpreterPythonInterfaces.cpp | 2 + .../ScriptInterpreterPythonInterfaces.h | 1 + .../ScriptedStringSummaryPythonInterface.cpp | 87 +++++++++++++++++++ .../ScriptedStringSummaryPythonInterface.h | 48 ++++++++++ .../Python/ScriptInterpreterPython.cpp | 7 ++ .../Python/ScriptInterpreterPythonImpl.h | 3 + 20 files changed, 457 insertions(+), 8 deletions(-) create mode 100644 lldb/examples/python/templates/scripted_string_summary.py create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.h diff --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt index d29b143c1408c..902f20d4f0b41 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_string_summary.py" ) if(APPLE) diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt index dd091836dc1aa..760991c5a68b5 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_string_summary.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_string_summary.py b/lldb/examples/python/templates/scripted_string_summary.py new file mode 100644 index 0000000000000..217c3bb6a8e25 --- /dev/null +++ b/lldb/examples/python/templates/scripted_string_summary.py @@ -0,0 +1,40 @@ +from abc import ABCMeta, abstractmethod + +import lldb + + +class ScriptedStringSummary(metaclass=ABCMeta): + """ + The base class for a scripted string summary provider. + + A summary provider produces the one-line string shown next to a value in + `frame variable`/`expression` output. Register it with + `type summary add -l <ClassName> <TypeName>`. + + Most of the base class methods are `@abstractmethod` that need to be + overwritten by the inheriting class. + """ + + def __init__(self): + """Construct a scripted summary provider. + + Summary providers are constructed with no arguments and are shared + across every value they're asked to summarize. + """ + pass + + @abstractmethod + def get_summary( + self, valobj: lldb.SBValue, options: lldb.SBTypeSummaryOptions + ) -> str: + """Get the summary string for a value. + + Args: + valobj (lldb.SBValue): The value to summarize. + options (lldb.SBTypeSummaryOptions): The options to use when + producing the summary. + + Returns: + str: The summary string for `valobj`. + """ + pass diff --git a/lldb/include/lldb/API/SBTypeSummary.h b/lldb/include/lldb/API/SBTypeSummary.h index b6869e53a39e7..9ca26532d8156 100644 --- a/lldb/include/lldb/API/SBTypeSummary.h +++ b/lldb/include/lldb/API/SBTypeSummary.h @@ -81,6 +81,10 @@ class SBTypeSummary { CreateWithScriptCode(const char *data, uint32_t options = 0); // see lldb::eTypeOption values + static SBTypeSummary + CreateWithClassName(const char *data, + uint32_t options = 0); // see lldb::eTypeOption values + #ifndef SWIG static SBTypeSummary CreateWithCallback(FormatCallback cb, uint32_t options = 0, diff --git a/lldb/include/lldb/DataFormatters/TypeSummary.h b/lldb/include/lldb/DataFormatters/TypeSummary.h index a0938556e0174..4abf18c9c39a3 100644 --- a/lldb/include/lldb/DataFormatters/TypeSummary.h +++ b/lldb/include/lldb/DataFormatters/TypeSummary.h @@ -48,7 +48,14 @@ class TypeSummaryOptions { class TypeSummaryImpl { public: - enum class Kind { eSummaryString, eScript, eBytecode, eCallback, eInternal }; + enum class Kind { + eSummaryString, + eScript, + eBytecode, + eCallback, + eInternal, + eScriptedClass + }; virtual ~TypeSummaryImpl() = default; @@ -423,6 +430,51 @@ struct ScriptSummaryFormat : public TypeSummaryImpl { const ScriptSummaryFormat &operator=(const ScriptSummaryFormat &) = delete; }; +// Python-based summaries backed by a class, running an instance's +// `get_summary` method to show data. Unlike ScriptSummaryFormat (a bare +// function resolved once and cached), the Python object here is itself the +// cache: it's created lazily on the first call to FormatObject (since this +// format can be constructed via SBTypeSummary::CreateWithClassName before +// any debugger/target context exists) and then reused across every +// subsequent call, for every value of the matching type. +struct ScriptedSummaryFormat : public TypeSummaryImpl { + std::string m_class_name; + lldb::ScriptedStringSummaryInterfaceSP m_interface_sp; + + ScriptedSummaryFormat(const TypeSummaryImpl::Flags &flags, + const char *class_name, uint32_t ptr_match_depth = 1); + + ~ScriptedSummaryFormat() override = default; + + const char *GetClassName() const { return m_class_name.c_str(); } + + void SetClassName(const char *class_name) { + if (class_name) + m_class_name.assign(class_name); + else + m_class_name.clear(); + m_interface_sp.reset(); + } + + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options) override; + + std::string GetDescription() override; + + std::string GetName() override; + + static bool classof(const TypeSummaryImpl *S) { + return S->GetKind() == Kind::eScriptedClass; + } + + typedef std::shared_ptr<ScriptedSummaryFormat> SharedPointer; + +private: + ScriptedSummaryFormat(const ScriptedSummaryFormat &) = delete; + const ScriptedSummaryFormat & + operator=(const ScriptedSummaryFormat &) = delete; +}; + /// A summary formatter that is defined in LLDB formmater bytecode. /// /// See `BytecodeSyntheticChildren` for the corresponding synthetic formatter. diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h new file mode 100644 index 0000000000000..a0bc59d82f750 --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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_SCRIPTEDSTRINGSUMMARYINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTRINGSUMMARYINTERFACE_H + +#include "ScriptedInterface.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { +class ScriptedStringSummaryInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name) = 0; + + virtual std::optional<std::string> + GetSummary(ValueObject &valobj, const TypeSummaryOptions &options) { + return std::nullopt; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTRINGSUMMARYINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 0e65cb4b8ac4a..98ff3a353d405 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -586,6 +586,11 @@ class ScriptInterpreter : public PluginInterface { return {}; } + virtual lldb::ScriptedStringSummaryInterfaceSP + CreateScriptedStringSummaryInterface() { + 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..773152d1dc13b 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 + eScriptedExtensionScriptedStringSummary, + kLastScriptedExtension = eScriptedExtensionScriptedStringSummary }; /// Register numbering types. diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 157aa5743f016..075a033253b1c 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -199,6 +199,7 @@ class ScriptedProcessInterface; class ScriptedThreadInterface; class ScriptedThreadPlanInterface; class ScriptedStackFrameRecognizerInterface; +class ScriptedStringSummaryInterface; class ScriptedSyntheticChildren; class SearchFilter; class Section; @@ -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::ScriptedStringSummaryInterface> + ScriptedStringSummaryInterfaceSP; 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/API/SBTypeSummary.cpp b/lldb/source/API/SBTypeSummary.cpp index 58ec068ab9600..a424394d2768f 100644 --- a/lldb/source/API/SBTypeSummary.cpp +++ b/lldb/source/API/SBTypeSummary.cpp @@ -135,6 +135,17 @@ SBTypeSummary SBTypeSummary::CreateWithScriptCode(const char *data, TypeSummaryImplSP(new ScriptSummaryFormat(options, "", data))); } +SBTypeSummary SBTypeSummary::CreateWithClassName(const char *data, + uint32_t options) { + LLDB_INSTRUMENT_VA(data, options); + + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary( + TypeSummaryImplSP(new ScriptedSummaryFormat(options, data))); +} + SBTypeSummary SBTypeSummary::CreateWithCallback(FormatCallback cb, uint32_t options, const char *description) { @@ -372,6 +383,16 @@ bool SBTypeSummary::IsEqualTo(lldb::SBTypeSummary &rhs) { return GetOptions() == rhs.GetOptions(); case TypeSummaryImpl::Kind::eInternal: return (m_opaque_sp.get() == rhs.m_opaque_sp.get()); + case TypeSummaryImpl::Kind::eScriptedClass: { + ScriptedSummaryFormat *lhs_ptr = + llvm::dyn_cast<ScriptedSummaryFormat>(m_opaque_sp.get()); + ScriptedSummaryFormat *rhs_ptr = + llvm::dyn_cast<ScriptedSummaryFormat>(rhs.m_opaque_sp.get()); + if (!lhs_ptr || !rhs_ptr) + return false; + return strcmp(lhs_ptr->GetClassName(), rhs_ptr->GetClassName()) == 0 && + GetOptions() == rhs.GetOptions(); + } } return false; @@ -417,6 +438,10 @@ bool SBTypeSummary::CopyOnWrite_Impl() { llvm::dyn_cast<StringSummaryFormat>(m_opaque_sp.get())) { new_sp = TypeSummaryImplSP(new StringSummaryFormat( GetOptions(), current_summary_ptr->GetSummaryString())); + } else if (ScriptedSummaryFormat *current_summary_ptr = + llvm::dyn_cast<ScriptedSummaryFormat>(m_opaque_sp.get())) { + new_sp = TypeSummaryImplSP(new ScriptedSummaryFormat( + GetOptions(), current_summary_ptr->GetClassName())); } SetSP(new_sp); diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index c1dc949a7b815..985bf4709fef0 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -21,6 +21,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueLanguage.h" #include "lldb/Interpreter/OptionValueString.h" @@ -123,9 +124,9 @@ const char *FormatCategoryToString(FormatCategoryItem item, bool long_name) { class CommandObjectTypeSummaryAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { private: - class CommandOptions : public Options { + class CommandOptions : public OptionGroup { public: - CommandOptions(CommandInterpreter &interpreter) {} + CommandOptions() = default; ~CommandOptions() override = default; @@ -151,12 +152,16 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed, uint32_t m_ptr_match_depth = 1; }; + OptionGroupOptions m_option_group; CommandOptions m_options; + OptionGroupPythonClassWithDict m_class_options; - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_option_group; } bool Execute_ScriptSummary(Args &command, CommandReturnObject &result); + bool Execute_PythonClassSummary(Args &command, CommandReturnObject &result); + bool Execute_StringSummary(Args &command, CommandReturnObject &result); public: @@ -1163,7 +1168,8 @@ Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue( uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; - const int short_option = m_getopt_table[option_idx].val; + const int short_option = + g_type_summary_add_options[option_idx].short_option; bool success; switch (short_option) { @@ -1374,6 +1380,48 @@ bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary( return result.Succeeded(); } +bool CommandObjectTypeSummaryAdd::Execute_PythonClassSummary( + Args &command, CommandReturnObject &result) { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1 && !m_options.m_name) { + result.AppendErrorWithFormat("%s takes one or more args", + m_cmd_name.c_str()); + return false; + } + + const std::string &class_name = m_class_options.GetName(); + if (class_name.empty()) { + result.AppendError("must provide a Python class name"); + return false; + } + + TypeSummaryImplSP script_format = std::make_shared<ScriptedSummaryFormat>( + m_options.m_flags, class_name.c_str(), m_options.m_ptr_match_depth); + + Status error; + + for (auto &entry : command.entries()) { + AddSummary(ConstString(entry.ref()), script_format, m_options.m_match_type, + m_options.m_category, &error); + if (error.Fail()) { + result.AppendError(error.AsCString()); + return false; + } + } + + if (m_options.m_name) { + AddNamedSummary(m_options.m_name, script_format, &error); + if (error.Fail()) { + result.AppendError(error.AsCString()); + result.AppendError("added to types, but not given a name"); + return false; + } + } + + return result.Succeeded(); +} + #endif bool CommandObjectTypeSummaryAdd::Execute_StringSummary( @@ -1450,7 +1498,14 @@ CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd( CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "type summary add", "Add a new summary style for a type.", nullptr), - IOHandlerDelegateMultiline("DONE"), m_options(interpreter) { + IOHandlerDelegateMultiline("DONE"), + m_class_options("scripted string summary", /*is_class=*/true, 'L', 'K', + 'V') { + m_option_group.Append(&m_options); + m_option_group.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_ALL); + m_option_group.Finalize(); + AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus); SetHelpLong( @@ -1553,7 +1608,13 @@ void CommandObjectTypeSummaryAdd::DoExecute(Args &command, CommandReturnObject &result) { WarnOnPotentialUnquotedUnsignedType(command, result); - if (m_options.m_is_add_script) { + if (!m_class_options.GetName().empty()) { +#if LLDB_ENABLE_PYTHON + Execute_PythonClassSummary(command, result); +#else + result.AppendError("python is disabled"); +#endif + } else if (m_options.m_is_add_script) { #if LLDB_ENABLE_PYTHON Execute_ScriptSummary(command, result); #else diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp index 4f01466f85148..73ff39db19a81 100644 --- a/lldb/source/DataFormatters/TypeSummary.cpp +++ b/lldb/source/DataFormatters/TypeSummary.cpp @@ -16,6 +16,8 @@ #include "lldb/Core/Debugger.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h" +#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -60,6 +62,8 @@ std::string TypeSummaryImpl::GetSummaryKindName() { return "c++"; case Kind::eBytecode: return "bytecode"; + case Kind::eScriptedClass: + return "python class"; } llvm_unreachable("Unknown type kind name"); } @@ -242,6 +246,77 @@ std::string ScriptSummaryFormat::GetDescription() { std::string ScriptSummaryFormat::GetName() { return m_script_formatter_name; } +ScriptedSummaryFormat::ScriptedSummaryFormat( + const TypeSummaryImpl::Flags &flags, const char *class_name, + uint32_t ptr_match_depth) + : TypeSummaryImpl(Kind::eScriptedClass, flags, ptr_match_depth), + m_class_name(class_name ? class_name : ""), m_interface_sp() {} + +bool ScriptedSummaryFormat::FormatObject(ValueObject *valobj, + std::string &retval, + const TypeSummaryOptions &options) { + if (!valobj) + return false; + + TargetSP target_sp(valobj->GetTargetSP()); + + if (!target_sp) { + retval.assign("error: no target"); + return false; + } + + ScriptInterpreter *script_interpreter = + target_sp->GetDebugger().GetScriptInterpreter(); + + if (!script_interpreter) { + retval.assign("error: no ScriptInterpreter"); + return false; + } + + if (!m_interface_sp) { + m_interface_sp = script_interpreter->CreateScriptedStringSummaryInterface(); + if (!m_interface_sp) { + retval.assign("error: no ScriptedStringSummaryInterface"); + return false; + } + + llvm::Expected<StructuredData::GenericSP> obj_or_err = + m_interface_sp->CreatePluginObject(m_class_name); + if (!obj_or_err) { + retval.assign(llvm::toString(obj_or_err.takeError())); + m_interface_sp.reset(); + return false; + } + } + + std::optional<std::string> summary = + m_interface_sp->GetSummary(*valobj, options); + if (!summary) { + retval.assign("error: script did not provide a summary"); + return false; + } + + retval = std::move(*summary); + return true; +} + +std::string ScriptedSummaryFormat::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s%s%s%s%s ptr-match-depth=%u\n ", + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : "", + GetPtrMatchDepth()); + sstr.PutCString(m_class_name); + return std::string(sstr.GetString()); +} + +std::string ScriptedSummaryFormat::GetName() { return m_class_name; } + BytecodeSummaryFormat::BytecodeSummaryFormat( const TypeSummaryImpl::Flags &flags, std::unique_ptr<llvm::MemoryBuffer> bytecode) diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 4f6095d097d10..33e915951596e 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 eScriptedExtensionScriptedStringSummary: + return "ScriptedStringSummary"; } llvm_unreachable("unhandled ScriptedExtension"); } @@ -241,6 +243,8 @@ ScriptInterpreter::StringToExtension(llvm::StringRef string) { .CaseLower("ScriptedFrame", eScriptedExtensionScriptedFrame) .CaseLower("ScriptedStackFrameRecognizer", eScriptedExtensionScriptedStackFrameRecognizer) + .CaseLower("ScriptedStringSummary", + eScriptedExtensionScriptedStringSummary) .Default(eScriptedExtensionInvalid); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index 201574da72a19..9932d31d26f30 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/ScriptedStringSummaryPythonInterface.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..1636723a7d459 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(); + ScriptedStringSummaryPythonInterface::Initialize(); } void ScriptInterpreterPythonInterfaces::Terminate() { @@ -43,4 +44,5 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedThreadPythonInterface::Terminate(); ScriptedFramePythonInterface::Terminate(); ScriptedStackFrameRecognizerPythonInterface::Terminate(); + ScriptedStringSummaryPythonInterface::Terminate(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 03d747e63a592..686614c5f2da9 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 "ScriptedStringSummaryPythonInterface.h" #include "ScriptedThreadPlanPythonInterface.h" #include "ScriptedThreadPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.cpp new file mode 100644 index 0000000000000..c3fdffdad2f05 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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/DataFormatters/TypeSummary.h" +#include "lldb/ValueObject/ValueObject.h" +#include "lldb/lldb-enumerations.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedStringSummaryPythonInterface.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedStringSummaryPythonInterface::ScriptedStringSummaryPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedStringSummaryInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedStringSummaryPythonInterface::CreatePluginObject( + llvm::StringRef class_name) { + if (class_name.empty()) + return llvm::createStringError("empty class name"); + + return ScriptedPythonInterface::CreatePluginObject( + ScriptedMetadata(class_name, nullptr), nullptr); +} + +std::optional<std::string> ScriptedStringSummaryPythonInterface::GetSummary( + ValueObject &valobj, const TypeSummaryOptions &options) { + if (!m_object_instance_sp) + return std::nullopt; + + Locker py_lock(&m_interpreter, + Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN, + Locker::FreeLock); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + if (!implementor.IsAllocated()) + return std::nullopt; + + llvm::Expected<PythonObject> expected_py_return = implementor.CallMethod( + "get_summary", SWIGBridge::ToSWIGWrapper(valobj.GetSP()), + SWIGBridge::ToSWIGWrapper(options)); + + if (!expected_py_return) { + llvm::consumeError(expected_py_return.takeError()); + return std::nullopt; + } + + PythonObject py_return = std::move(expected_py_return.get()); + if (!py_return.IsAllocated()) + return std::nullopt; + + // Match the legacy function-based summary contract + // (LLDBSwigPythonCallTypeScript): the return value is converted via + // Python's str() regardless of its type, so `get_summary` may return any + // str()-convertible object, not just a literal string. + return py_return.Str().GetString().str(); +} + +void ScriptedStringSummaryPythonInterface::Initialize() { + const std::vector<llvm::StringRef> ci_usages = { + "type summary add -L <ClassName> [-K <key> -V <value> ...] <TypeName>"}; + const std::vector<llvm::StringRef> api_usages = { + "SBTypeSummary.CreateWithClassName"}; + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + "Provide a summary string for a type, used by 'type summary add -l'", + CreateInstance, eScriptedExtensionScriptedStringSummary, + eScriptLanguagePython, {ci_usages, api_usages}); +} + +void ScriptedStringSummaryPythonInterface::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.h new file mode 100644 index 0000000000000..c54c3ad6581bc --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.h @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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_SCRIPTEDSTRINGSUMMARYPYTHONINTERFACE_H +#define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSTRINGSUMMARYPYTHONINTERFACE_H + +#include "lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h" + +#include "ScriptedPythonInterface.h" +namespace lldb_private { + +class ScriptedStringSummaryPythonInterface + : public ScriptedStringSummaryInterface, + public ScriptedPythonInterface, + public PluginInterface { +public: + ScriptedStringSummaryPythonInterface( + ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>({{"get_summary", 3}}); + } + + std::optional<std::string> + GetSummary(ValueObject &valobj, const TypeSummaryOptions &options) override; + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedStringSummaryPythonInterface"; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSTRINGSUMMARYPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 7b1bd9d8411c5..71d607814c238 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 eScriptedExtensionScriptedStringSummary: + return "lldb.plugins.scripted_string_summary"; case eScriptedExtensionInvalid: return llvm::createStringError("invalid extension name"); } @@ -2003,6 +2005,11 @@ ScriptInterpreterPythonImpl::CreateScriptedStackFrameRecognizerInterface() { return std::make_shared<ScriptedStackFrameRecognizerPythonInterface>(*this); } +ScriptedStringSummaryInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedStringSummaryInterface() { + return std::make_shared<ScriptedStringSummaryPythonInterface>(*this); +} + ScriptedThreadInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index d8a817198253f..570be07cc4e3b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -86,6 +86,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedStackFrameRecognizerInterfaceSP CreateScriptedStackFrameRecognizerInterface() override; + lldb::ScriptedStringSummaryInterfaceSP + CreateScriptedStringSummaryInterface() override; + lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
