Skip to content

[lldb/script] Add class-based summary providers via ScriptedStringSummaryInterface#210469

Open
medismailben wants to merge 1 commit into
llvm:mainfrom
medismailben:scripted-extension-summary
Open

[lldb/script] Add class-based summary providers via ScriptedStringSummaryInterface#210469
medismailben wants to merge 1 commit into
llvm:mainfrom
medismailben:scripted-extension-summary

Conversation

@medismailben

@medismailben medismailben commented Jul 18, 2026

Copy link
Copy Markdown
Member

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 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.

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lldb

Author: Med Ismail Bennani (medismailben)

Changes

Add type summary add -l &lt;ClassName&gt; 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 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.


Patch is 30.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/210469.diff

21 Files Affected:

  • (modified) lldb/bindings/python/CMakeLists.txt (+1)
  • (modified) lldb/docs/CMakeLists.txt (+1)
  • (added) lldb/examples/python/templates/scripted_string_summary.py (+40)
  • (modified) lldb/include/lldb/API/SBTypeSummary.h (+4)
  • (modified) lldb/include/lldb/DataFormatters/TypeSummary.h (+53-1)
  • (added) lldb/include/lldb/Interpreter/Interfaces/ScriptedStringSummaryInterface.h (+28)
  • (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+5)
  • (modified) lldb/include/lldb/lldb-enumerations.h (+2-1)
  • (modified) lldb/include/lldb/lldb-forward.h (+3)
  • (modified) lldb/source/API/SBTypeSummary.cpp (+25)
  • (modified) lldb/source/Commands/CommandObjectType.cpp (+59-1)
  • (modified) lldb/source/Commands/Options.td (+5)
  • (modified) lldb/source/DataFormatters/TypeSummary.cpp (+75)
  • (modified) lldb/source/Interpreter/ScriptInterpreter.cpp (+4)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt (+1)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp (+2)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h (+1)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.cpp (+87)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStringSummaryPythonInterface.h (+48)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (+7)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h (+3)
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..e7c4f55bef04d 100644
--- a/lldb/source/Commands/CommandObjectType.cpp
+++ b/lldb/source/Commands/CommandObjectType.cpp
@@ -146,7 +146,9 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
     ConstString m_name;
     std::string m_python_script;
     std::string m_python_function;
+    std::string m_class_name;
     bool m_is_add_script = false;
+    bool m_is_class_based = false;
     std::string m_category;
     uint32_t m_ptr_match_depth = 1;
   };
@@ -157,6 +159,8 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
 
   bool Execute_ScriptSummary(Args &command, CommandReturnObject &result);
 
+  bool Execute_PythonClassSummary(Args &command, CommandReturnObject &result);
+
   bool Execute_StringSummary(Args &command, CommandReturnObject &result);
 
 public:
@@ -1229,6 +1233,10 @@ Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue(
   case 'P':
     m_is_add_script = true;
     break;
+  case 'l':
+    m_class_name = std::string(option_arg);
+    m_is_class_based = true;
+    break;
   case 'w':
     m_category = std::string(option_arg);
     break;
@@ -1254,8 +1262,10 @@ void CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting(
   m_name.Clear();
   m_python_script = "";
   m_python_function = "";
+  m_class_name = "";
   m_format_string = "";
   m_is_add_script = false;
+  m_is_class_based = false;
   m_category = "default";
 }
 
@@ -1374,6 +1384,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;
+  }
+
+  if (m_options.m_class_name.empty()) {
+    result.AppendError("must provide a Python class name");
+    return false;
+  }
+
+  TypeSummaryImplSP script_format = std::make_shared<ScriptedSummaryFormat>(
+      m_options.m_flags, m_options.m_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(
@@ -1553,7 +1605,13 @@ void CommandObjectTypeSummaryAdd::DoExecute(Args &command,
                                             CommandReturnObject &result) {
   WarnOnPotentialUnquotedUnsignedType(command, result);
 
-  if (m_options.m_is_add_script) {
+  if (m_options.m_is_class_based) {
+#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/Commands/Options.td b/lldb/source/Commands/Options.td
index ab851725979ef..7ed7ed492913a 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -2434,6 +2434,11 @@ let Command = "type summary add" in {
       : Option<"input-python", "P">,
         Group<3>,
         Desc<"Input Python code to use for this type manually.">;
+  def type_summary_add_python_class
+      : Option<"python-class", "l">,
+        Group<3>,
+        Arg<"PythonClass">,
+        Desc<"Use this Python class to produce a summary.">;
   def type_summary_add_expand
       : Option<"expand", "e">,
         Groups<[2, 3]>,
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 ...
[truncated]

@medismailben
medismailben force-pushed the scripted-extension-summary branch from 74d1c32 to 547ab0b Compare July 18, 2026 00:49
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

…erface

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 <ismail@bennani.ma>
@medismailben
medismailben force-pushed the scripted-extension-summary branch from 547ab0b to 6156b3e Compare July 18, 2026 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant