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