https://github.com/mentlerd created 
https://github.com/llvm/llvm-project/pull/209056

Allow manipulating the scripted synthetic children frontend selection process 
for individual `SBValue` instances.

The motivating use-case is supporting custom data structures, type erasure 
systems, and facilitating debugging handle based C APIs. Below is an example of 
such a use-case of an anonymized 3rd party C library:
```C
using ErrorCode = int;
using Handle = int;

using FooHandle = Handle;
using BarHandle = Handle;

struct ArrayOfHandles {
  int size;
  Handle* data;
};

struct AllHandles {
  ArrayOfHandles foos; 
  ArrayOfHandles bars;
};

ErrorCode GetAllHandles(AllHandles* into);
```

The current LLDB feature-set allows attaching a synthetic children provider to 
`AllHandles`, but the scripted frontend has no chance to select an appropriate 
alternative frontend implementation class for `foos` and `bars` based on type 
information it is aware of.

> [!NOTE]
> I am aware the state of this PR doesn't reach contributing standards yet due 
> to lacking tests! I am raising it to ask for feedback on the API implications 
> and implementation direction which I largely borrowed from `SBProcess`'s 
> scripted implementation.

>From 9cf04299d36c7e917d99214a0b8ac247de659a10 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sun, 12 Jul 2026 22:14:17 +0200
Subject: [PATCH 1/2] SBValue::SetTypeSynthetic

---
 lldb/include/lldb/API/SBValue.h |  8 ++++++++
 lldb/source/API/SBValue.cpp     | 11 +++++++++++
 2 files changed, 19 insertions(+)

diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 16a9f0837454c..a2aef2434a01b 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -132,6 +132,14 @@ class LLDB_API SBValue {
 
   lldb::SBTypeSynthetic GetTypeSynthetic();
 
+  /// Override the `SBTypeSynthetic` chosen by the DataFormatter system for 
this
+  /// instance.
+  ///
+  /// This can be used to great effect in scripted synthetic children providers
+  /// where a child's underlying type can only be figured out by inspecting the
+  /// containing object's other members.
+  void SetTypeSynthetic(lldb::SBTypeSynthetic &synthetic);
+
   lldb::SBValue GetChildAtIndex(uint32_t idx);
 
   lldb::SBValue CreateChildAtOffset(const char *name, uint32_t offset,
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index de660577b3a9b..1b7fdb9b09078 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -393,6 +393,17 @@ lldb::SBTypeSynthetic SBValue::GetTypeSynthetic() {
   return synthetic;
 }
 
+void SBValue::SetTypeSynthetic(lldb::SBTypeSynthetic &synthetic) {
+  LLDB_INSTRUMENT_VA(this);
+
+  ValueLocker locker;
+  lldb::ValueObjectSP value_sp(GetSP(locker));
+  lldb::ScriptedSyntheticChildrenSP synthetic_sp(synthetic.GetSP());
+  if (value_sp && synthetic_sp) {
+    value_sp->SetSyntheticChildren(synthetic_sp);
+  }
+}
+
 lldb::SBValue SBValue::CreateChildAtOffset(const char *name, uint32_t offset,
                                            SBType type) {
   LLDB_INSTRUMENT_VA(this, name, offset, type);

>From 37cce473899be58b336a6b17dd6b604ad11e4160 Mon Sep 17 00:00:00 2001
From: mentlerd <[email protected]>
Date: Sun, 12 Jul 2026 22:16:18 +0200
Subject: [PATCH 2/2] SBValue::GetTypeSyntheticImplementation

---
 lldb/include/lldb/API/SBValue.h                    |  8 ++++++++
 lldb/include/lldb/DataFormatters/TypeSynthetic.h   |  4 ++++
 lldb/include/lldb/ValueObject/ValueObject.h        |  6 ++++++
 .../lldb/ValueObject/ValueObjectSynthetic.h        |  2 ++
 lldb/source/API/SBValue.cpp                        | 14 ++++++++++++++
 lldb/source/DataFormatters/TypeSynthetic.cpp       | 10 ++++++++++
 lldb/source/ValueObject/ValueObjectSynthetic.cpp   |  5 +++++
 7 files changed, 49 insertions(+)

diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index a2aef2434a01b..9d746f74c9b09 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -140,6 +140,14 @@ class LLDB_API SBValue {
   /// containing object's other members.
   void SetTypeSynthetic(lldb::SBTypeSynthetic &synthetic);
 
+  /// This function's primary use is to ease inspecting the internal state of
+  /// scripted synthetic children providers for debugging purposes.
+  ///
+  /// An other alternative usecase is for parent synthetic children providers 
to
+  /// imbue their children's synthetic children providers with additional
+  /// context after creation.
+  lldb::SBScriptObject GetTypeSyntheticImplementation();
+
   lldb::SBValue GetChildAtIndex(uint32_t idx);
 
   lldb::SBValue CreateChildAtOffset(const char *name, uint32_t offset,
diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h 
b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
index ae44d7e8f96eb..9c17adbde7465 100644
--- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h
+++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
@@ -83,6 +83,8 @@ class SyntheticChildrenFrontEnd {
   // display purposes
   virtual ConstString GetSyntheticTypeName() { return ConstString(); }
 
+  virtual void *GetImplementation() { return nullptr; }
+
   typedef std::shared_ptr<SyntheticChildrenFrontEnd> SharedPointer;
   typedef std::unique_ptr<SyntheticChildrenFrontEnd> UniquePointer;
 
@@ -469,6 +471,8 @@ class ScriptedSyntheticChildren : public SyntheticChildren {
 
     ConstString GetSyntheticTypeName() override;
 
+    void *GetImplementation() override;
+
     typedef std::shared_ptr<SyntheticChildrenFrontEnd> SharedPointer;
 
   private:
diff --git a/lldb/include/lldb/ValueObject/ValueObject.h 
b/lldb/include/lldb/ValueObject/ValueObject.h
index 6e4d93b815761..583dd3faac482 100644
--- a/lldb/include/lldb/ValueObject/ValueObject.h
+++ b/lldb/include/lldb/ValueObject/ValueObject.h
@@ -917,6 +917,10 @@ class ValueObject {
     return m_synthetic_children_sp;
   }
 
+  virtual SyntheticChildrenFrontEnd *GetSyntheticChildrenFrontEnd() {
+    return nullptr;
+  }
+
   // Use GetParent for display purposes, but if you want to tell the parent to
   // update itself then use m_parent.  The ValueObjectDynamicValue's parent is
   // not the correct parent for displaying, they are really siblings, so for
@@ -982,6 +986,8 @@ class ValueObject {
 
   lldb::ValueObjectSP CheckValueObjectOwnership(ValueObject *child);
 
+  virtual void *GetImplementation() { return nullptr; }
+
 protected:
   typedef ClusterManager<ValueObject> ValueObjectManager;
 
diff --git a/lldb/include/lldb/ValueObject/ValueObjectSynthetic.h 
b/lldb/include/lldb/ValueObject/ValueObjectSynthetic.h
index 60803697ee020..32475aa02a5e8 100644
--- a/lldb/include/lldb/ValueObject/ValueObjectSynthetic.h
+++ b/lldb/include/lldb/ValueObject/ValueObjectSynthetic.h
@@ -128,6 +128,8 @@ class ValueObjectSynthetic : public ValueObject {
                     GetExpressionPathFormat epformat =
                         eGetExpressionPathFormatDereferencePointers) override;
 
+  SyntheticChildrenFrontEnd *GetSyntheticChildrenFrontEnd() override;
+
 protected:
   bool UpdateValue() override;
 
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 1b7fdb9b09078..c2509e89633df 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -404,6 +404,20 @@ void SBValue::SetTypeSynthetic(lldb::SBTypeSynthetic 
&synthetic) {
   }
 }
 
+lldb::SBScriptObject SBValue::GetTypeSyntheticImplementation() {
+  LLDB_INSTRUMENT_VA(this);
+
+  ValueLocker locker;
+  lldb::ValueObjectSP value_sp(GetSP(locker));
+
+  auto frontend = value_sp->GetSyntheticChildrenFrontEnd();
+  if (!frontend) {
+    return lldb::SBScriptObject(nullptr, eScriptLanguageDefault);
+  }
+  return lldb::SBScriptObject(frontend->GetImplementation(),
+                              eScriptLanguageDefault);
+}
+
 lldb::SBValue SBValue::CreateChildAtOffset(const char *name, uint32_t offset,
                                            SBType type) {
   LLDB_INSTRUMENT_VA(this, name, offset, type);
diff --git a/lldb/source/DataFormatters/TypeSynthetic.cpp 
b/lldb/source/DataFormatters/TypeSynthetic.cpp
index 76eb61b92e446..53852fc291980 100644
--- a/lldb/source/DataFormatters/TypeSynthetic.cpp
+++ b/lldb/source/DataFormatters/TypeSynthetic.cpp
@@ -247,6 +247,16 @@ ConstString 
ScriptedSyntheticChildren::FrontEnd::GetSyntheticTypeName() {
   return m_interpreter->GetSyntheticTypeName(m_wrapper_sp);
 }
 
+void *ScriptedSyntheticChildren::FrontEnd::GetImplementation() {
+  if (!m_wrapper_sp || m_interpreter == nullptr)
+    return nullptr;
+
+  if (m_wrapper_sp->GetType() != eStructuredDataTypeGeneric)
+    return nullptr;
+
+  return m_wrapper_sp->GetAsGeneric()->GetValue();
+}
+
 std::string ScriptedSyntheticChildren::GetDescription() {
   StreamString sstr;
   sstr.Printf("%s%s%s Python class %s", Cascades() ? "" : " (not cascading)",
diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp 
b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
index 1485dc1827480..74a3cfa4cc0f5 100644
--- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp
+++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
@@ -484,3 +484,8 @@ void ValueObjectSynthetic::GetExpressionPath(Stream &stream,
   }
   return ValueObject::GetExpressionPath(stream, epformat);
 }
+
+SyntheticChildrenFrontEnd *
+ValueObjectSynthetic::GetSyntheticChildrenFrontEnd() {
+  return m_synth_filter_up.get();
+}

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to