https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/204431
>From fb9e6efa387e56ba69d15bdf46e5764621913a9d Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Wed, 17 Jun 2026 21:44:16 +0200 Subject: [PATCH 1/5] [lldb] Treat synthetic variables as locals --- .../Process/scripted/ScriptedFrame.cpp | 5 ++-- .../TestScriptedFrameProvider.py | 25 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp index 72a7169d01188..2bb47dd29c2df 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -307,10 +307,11 @@ void ScriptedFrame::PopulateVariableListFromInterface( VariableSP var = v->GetVariable(); if (!var && include_synthetic_vars) { - // Construct the value type as an synthetic verison of what the value type + // Construct the value type as an synthetic version of what the value type // is. That'll allow the user to tell the scope and the 'synthetic-ness' // of the variable. - lldb::ValueType vt = GetSyntheticValueType(v->GetValueType()); + // Any variable we create here will be treated as a local variable. + lldb::ValueType vt = GetSyntheticValueType(eValueTypeVariableLocal); // Just make up a variable - the frame variable dumper just passes it // back in to GetValueObjectForFrameVariable, so we really just need to diff --git a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py index 597447723047e..b715d99964569 100644 --- a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py +++ b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py @@ -824,26 +824,43 @@ def test_get_values(self): options = lldb.SBVariablesOptions() options.SetIncludeSynthetic(True) variables = frame0.GetVariables(options) + self.assertFalse(variables.IsValid()) + self.assertEqual(variables.GetSize(), 0) + + options.SetIncludeLocals(True) + variables = frame0.GetVariables(options) self.assertTrue(variables.IsValid()) - self.assertTrue(variables.GetValueAtIndex(0).name == "_handler_one") + self.assertEqual(variables.GetSize(), 2) + self.assertEqual(variables.GetValueAtIndex(0).name, "variable_in_main") + self.assertEqual(variables.GetValueAtIndex(1).name, "_handler_one") # Ensure that we get synthetic variables in the other overloads. # (arguments, locals, statics, in_scope_only) variables = frame0.GetVariables(False, False, False, False) + self.assertFalse(variables.IsValid()) + self.assertEqual(variables.GetSize(), 0) + variables = frame0.GetVariables(False, True, False, False) self.assertTrue(variables.IsValid()) - self.assertTrue(variables.GetValueAtIndex(0).name == "_handler_one") + self.assertEqual(variables.GetSize(), 2) + self.assertEqual(variables.GetValueAtIndex(0).name, "variable_in_main") + self.assertEqual(variables.GetValueAtIndex(1).name, "_handler_one") # (arguments, locals, statics, in_scope_only, use_dynamic) variables = frame0.GetVariables( - False, False, False, False, lldb.eNoDynamicValues + False, True, False, False, lldb.eNoDynamicValues ) self.assertTrue(variables.IsValid()) - self.assertTrue(variables.GetValueAtIndex(0).name == "_handler_one") + self.assertEqual(variables.GetSize(), 2) + self.assertEqual(variables.GetValueAtIndex(0).name, "variable_in_main") + self.assertEqual(variables.GetValueAtIndex(1).name, "_handler_one") # FIXME: Synthetic variables are never in scope. variables = frame0.GetVariables(False, False, False, True) self.assertFalse(variables.IsValid()) self.assertEqual(variables.GetSize(), 0) + variables = frame0.GetVariables(False, True, False, True) + self.assertFalse(variables.IsValid()) + self.assertEqual(variables.GetSize(), 0) # Check the `frame variable` command(s) handle synthetic variables the # way we expect by printing them. >From 49965ff92280208c435f224732df48f7d18754fd Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Wed, 24 Jun 2026 17:52:51 +0200 Subject: [PATCH 2/5] refactor: Ask the frame what value type it wants --- .../Interfaces/ScriptedFrameInterface.h | 5 ++ .../Process/scripted/ScriptedFrame.cpp | 50 ++++++++++--------- .../ScriptedFramePythonInterface.cpp | 16 ++++++ .../Interfaces/ScriptedFramePythonInterface.h | 3 ++ .../Interfaces/ScriptedPythonInterface.cpp | 21 ++++++++ .../Interfaces/ScriptedPythonInterface.h | 5 ++ .../test_frame_providers.py | 5 ++ 7 files changed, 82 insertions(+), 23 deletions(-) diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h index 00994d65fd601..43914ef705dbf 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h @@ -53,6 +53,11 @@ class ScriptedFrameInterface : virtual public ScriptedInterface { virtual lldb::ValueObjectListSP GetVariables() { return nullptr; } + virtual std::optional<lldb::ValueType> + GetValueTypeForVariable(lldb::ValueObjectSP value) { + return std::nullopt; + } + virtual lldb::ValueObjectSP GetValueObjectForVariableExpression(llvm::StringRef expr, uint32_t options, Status &error) { diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp index 2bb47dd29c2df..8adc69b1f6cc6 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -300,35 +300,39 @@ void ScriptedFrame::PopulateVariableListFromInterface( // Convert what we can into a variable. m_variable_list_sp = std::make_shared<VariableList>(); + for (uint32_t i = 0, e = value_list_sp->GetSize(); i < e; ++i) { ValueObjectSP v = value_list_sp->GetValueObjectAtIndex(i); if (!v) continue; - VariableSP var = v->GetVariable(); - if (!var && include_synthetic_vars) { - // Construct the value type as an synthetic version of what the value type - // is. That'll allow the user to tell the scope and the 'synthetic-ness' - // of the variable. - // Any variable we create here will be treated as a local variable. - lldb::ValueType vt = GetSyntheticValueType(eValueTypeVariableLocal); - - // Just make up a variable - the frame variable dumper just passes it - // back in to GetValueObjectForFrameVariable, so we really just need to - // make sure the name and type are correct. We create IDs based on - // value_list_sp in order to make sure they're unique. - var = std::make_shared<lldb_private::Variable>( - (lldb::user_id_t)value_list_sp->GetSize() + i, - v->GetName().GetCString(), v->GetName().GetCString(), nullptr, vt, - /*owner_scope=*/nullptr, - /*scope_range=*/Variable::RangeList{}, - /*decl=*/nullptr, DWARFExpressionList{}, /*external=*/false, - /*artificial=*/true, /*location_is_constant_data=*/false); - } + // Ask the interface about the value type of this variable. If it doesn't + // specify any, use the original value type. If we encounter a variable, use + // a non-synthetic type unless it's overwritten. + std::optional<lldb::ValueType> desired_vt = + GetInterface()->GetValueTypeForVariable(v); + lldb::ValueType vt = [&] { + if (desired_vt) + return GetSyntheticValueType(*desired_vt); + if (v->GetVariable()) + return v->GetValueType(); + return GetSyntheticValueType(v->GetValueType()); + }(); + + if (IsSyntheticValueType(vt) && !include_synthetic_vars) + continue; - // Only append the variable if we have one (had already, or just created). - if (var) - m_variable_list_sp->AddVariable(var); + // Just make up a variable - the frame variable dumper just passes it + // back in to GetValueObjectForFrameVariable, so we really just need to + // make sure the name and type are correct. We create IDs based on + // value_list_sp in order to make sure they're unique. + m_variable_list_sp->AddVariable(std::make_shared<lldb_private::Variable>( + (lldb::user_id_t)value_list_sp->GetSize() + i, + v->GetName().GetCString(), v->GetName().GetCString(), nullptr, vt, + /*owner_scope=*/nullptr, + /*scope_range=*/Variable::RangeList{}, + /*decl=*/nullptr, DWARFExpressionList{}, /*external=*/false, + /*artificial=*/true, /*location_is_constant_data=*/false)); } } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp index de987c3773d88..6cc5ebff2c10b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp @@ -11,6 +11,7 @@ #include "lldb/Host/Config.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/Log.h" +#include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-enumerations.h" #include "../SWIGPythonBridge.h" @@ -163,6 +164,21 @@ lldb::ValueObjectListSP ScriptedFramePythonInterface::GetVariables() { return vals; } +std::optional<lldb::ValueType> +ScriptedFramePythonInterface::GetValueTypeForVariable( + lldb::ValueObjectSP value) { + Status error; + auto val = Dispatch<std::optional<lldb::ValueType>>( + "get_value_type_for_variable", error, std::move(value)); + + if (error.Fail()) { + return ErrorWithMessage<std::optional<lldb::ValueType>>( + LLVM_PRETTY_FUNCTION, error.AsCString(), error); + } + + return val; +} + lldb::ValueObjectSP ScriptedFramePythonInterface::GetValueObjectForVariableExpression( llvm::StringRef expr, uint32_t options, Status &status) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h index 2e215bbe6aea2..9320c6762b35f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h @@ -51,6 +51,9 @@ class ScriptedFramePythonInterface : public ScriptedFrameInterface, lldb::ValueObjectListSP GetVariables() override; + std::optional<lldb::ValueType> + GetValueTypeForVariable(lldb::ValueObjectSP value) override; + lldb::ValueObjectSP GetValueObjectForVariableExpression(llvm::StringRef expr, uint32_t options, Status &status) override; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 84dbbbe7270d8..25d0c0dac5be0 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -322,3 +322,24 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>( return out; } + +template <> +std::optional<lldb::ValueType> +ScriptedPythonInterface::ExtractValueFromPythonObject< + std::optional<lldb::ValueType>>(python::PythonObject &p, Status &error) { + if (p.IsNone()) + return std::nullopt; + + llvm::Expected<unsigned long long> val = p.AsUnsignedLongLong(); + if (!val) { + error = Status::FromError(val.takeError()); + return std::nullopt; + } + if (*val > std::numeric_limits<std::underlying_type_t<ValueType>>::max()) { + error = + Status::FromErrorStringWithFormatv("value too large (got {0})", *val); + return std::nullopt; + } + + return static_cast<ValueType>(*val); +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 28ed5745ca55f..7d0d4cdd3c6d1 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -836,6 +836,11 @@ lldb::ValueObjectListSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>( python::PythonObject &p, Status &error); +template <> +std::optional<lldb::ValueType> +ScriptedPythonInterface::ExtractValueFromPythonObject< + std::optional<lldb::ValueType>>(python::PythonObject &p, Status &error); + } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H diff --git a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py index f9f673f80abcf..91b3830fd195f 100644 --- a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py +++ b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py @@ -554,6 +554,11 @@ def is_hidden(self): def get_register_context(self): """No register context.""" return None + + def get_value_type_for_variable(self, var): + if var.GetName() == "_handler_one": + return lldb.eValueTypeVariableLocal + return None # Inherit the value type for others. def get_variables(self): """""" >From e76abb5fa54d6fb33cbd9c7001c046b5c88fe7c5 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Wed, 24 Jun 2026 17:57:39 +0200 Subject: [PATCH 3/5] fix: formatting --- .../scripted_frame_provider/test_frame_providers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py index 91b3830fd195f..13a7d152f87ed 100644 --- a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py +++ b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py @@ -554,11 +554,11 @@ def is_hidden(self): def get_register_context(self): """No register context.""" return None - + def get_value_type_for_variable(self, var): if var.GetName() == "_handler_one": return lldb.eValueTypeVariableLocal - return None # Inherit the value type for others. + return None # Inherit the value type for others. def get_variables(self): """""" >From 0017be2659a048796eea301cc52ccef29799d0a5 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Wed, 24 Jun 2026 21:00:48 +0200 Subject: [PATCH 4/5] fix: use maximum value type --- lldb/include/lldb/lldb-enumerations.h | 2 ++ .../Python/Interfaces/ScriptedPythonInterface.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 32a341b354d9f..0480e115cd771 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -389,6 +389,8 @@ enum ValueType : uint32_t { eValueTypeVTable = 9, /// function pointer in virtual function table eValueTypeVTableEntry = 10, + + kLastValueType = eValueTypeVTableEntry }; /// A mask that we can use to check if the value type is synthetic or not. diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 25d0c0dac5be0..0744dbaed9271 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -335,9 +335,9 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< error = Status::FromError(val.takeError()); return std::nullopt; } - if (*val > std::numeric_limits<std::underlying_type_t<ValueType>>::max()) { - error = - Status::FromErrorStringWithFormatv("value too large (got {0})", *val); + if (*val == eValueTypeInvalid || *val > kLastValueType) { + error = Status::FromErrorStringWithFormatv( + "value type invalid or too large (got {0})", *val); return std::nullopt; } >From 7964d076618a3de7ba3b8784f1dc4cc406f9bea9 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Sat, 27 Jun 2026 13:42:49 +0200 Subject: [PATCH 5/5] refactor: allow python to set the flags --- lldb/include/lldb/Utility/ValueType.h | 6 +++--- lldb/include/lldb/lldb-enumerations.h | 16 +++++++++------- .../Plugins/Process/scripted/ScriptedFrame.cpp | 14 +++----------- .../Interfaces/ScriptedPythonInterface.cpp | 8 +++++--- .../test_frame_providers.py | 7 +++++-- 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lldb/include/lldb/Utility/ValueType.h b/lldb/include/lldb/Utility/ValueType.h index 307be07a3fc81..a96768af14439 100644 --- a/lldb/include/lldb/Utility/ValueType.h +++ b/lldb/include/lldb/Utility/ValueType.h @@ -15,17 +15,17 @@ namespace lldb_private { /// Get the base value type - for when we don't care if the value is synthetic /// or not, or when we've already handled that case. constexpr lldb::ValueType GetBaseValueType(lldb::ValueType vt) { - return lldb::ValueType(vt & ~lldb::ValueTypeSyntheticMask); + return lldb::ValueType(vt & ~lldb::eValueTypeSyntheticFlag); } /// Given a base value type, return a version that carries the synthetic bit. constexpr lldb::ValueType GetSyntheticValueType(lldb::ValueType base) { - return lldb::ValueType(base | lldb::ValueTypeSyntheticMask); + return lldb::ValueType(base | lldb::eValueTypeSyntheticFlag); } /// Return true if vt represents a synthetic value, false if not. constexpr bool IsSyntheticValueType(lldb::ValueType vt) { - return vt & lldb::ValueTypeSyntheticMask; + return vt & lldb::eValueTypeSyntheticFlag; } } // namespace lldb_private diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 0480e115cd771..c246a4bc915ba 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -390,14 +390,16 @@ enum ValueType : uint32_t { /// function pointer in virtual function table eValueTypeVTableEntry = 10, - kLastValueType = eValueTypeVTableEntry -}; + kLastValueType = eValueTypeVTableEntry, + + /// A flag that indicates if the value type is synthetic or not. + // NOTE: This limits the number of value types to 31, but that's 3x more than + // what we currently have now. See lldb/Utility/ValueType.h for helpers for + // working with synthetic value types. + eValueTypeSyntheticFlag = 0x20, -/// A mask that we can use to check if the value type is synthetic or not. -// NOTE: This limits the number of value types to 31, but that's 3x more than -// what we currently have now. See lldb/Utility/ValueType.h for helpers for -// working with synthetic value types. -static constexpr unsigned ValueTypeSyntheticMask = 0x20; + kValueTypeFlagsMask = eValueTypeSyntheticFlag, +}; /// Token size/granularities for Input Readers. diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp index 8adc69b1f6cc6..d6288b6eadb1c 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -307,17 +307,9 @@ void ScriptedFrame::PopulateVariableListFromInterface( continue; // Ask the interface about the value type of this variable. If it doesn't - // specify any, use the original value type. If we encounter a variable, use - // a non-synthetic type unless it's overwritten. - std::optional<lldb::ValueType> desired_vt = - GetInterface()->GetValueTypeForVariable(v); - lldb::ValueType vt = [&] { - if (desired_vt) - return GetSyntheticValueType(*desired_vt); - if (v->GetVariable()) - return v->GetValueType(); - return GetSyntheticValueType(v->GetValueType()); - }(); + // specify any, use the original value type. + lldb::ValueType vt = GetInterface()->GetValueTypeForVariable(v).value_or( + GetSyntheticValueType(v->GetValueType())); if (IsSyntheticValueType(vt) && !include_synthetic_vars) continue; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 0744dbaed9271..4612b1939b5db 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -335,11 +335,13 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< error = Status::FromError(val.takeError()); return std::nullopt; } - if (*val == eValueTypeInvalid || *val > kLastValueType) { + unsigned long long unmasked = *val & ~kValueTypeFlagsMask; + unsigned long long flags = *val & kValueTypeFlagsMask; + if (unmasked == eValueTypeInvalid || unmasked > kLastValueType) { error = Status::FromErrorStringWithFormatv( - "value type invalid or too large (got {0})", *val); + "value type invalid or too large (got {0} | {1:x})", unmasked, flags); return std::nullopt; } - return static_cast<ValueType>(*val); + return static_cast<ValueType>(unmasked | flags); } diff --git a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py index 13a7d152f87ed..a0da39402ca6d 100644 --- a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py +++ b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py @@ -556,9 +556,12 @@ def get_register_context(self): return None def get_value_type_for_variable(self, var): - if var.GetName() == "_handler_one": + name = var.GetName() + if name == "_handler_one": + return lldb.eValueTypeVariableLocal | lldb.eValueTypeSyntheticFlag + if name == "variable_in_main": return lldb.eValueTypeVariableLocal - return None # Inherit the value type for others. + return None def get_variables(self): """""" _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
