Skip to content

Commit 6181f4f

Browse files
authored
[lldb] Retcon SBValue::GetChildAtIndex(synthetic=true) (#140065)
The motivation here is being (un)able to treat pointer values as an array consistently. This works for pointers to simple/scalar values, but for aggregates, we get a very surprising result: - GetChildAtIndex(x, ??, true) returns the `x` child of the zeroth array member (the one you get by dereferencing the pointer/array) for all `x` which are smaller than the number of children of that value. - for other values of `x`, we get `v[x]`, where `v` is treated like a (C) pointer This patch reimagines this interface so that the value of `true` always treats (pointer and array) values as pointers. For `false`, we always dereference pointers, while in the case of arrays, we only return the values as far as the array bounds will allow. This has the potential to break existing code, but I have a suspicion that code was already broken to begin with, which is why I think this would be better than introducing a new API and keeping the old (and surprising) behavior. If our own test coverage is any indication, breakage should be minimal.
1 parent 5c1db38 commit 6181f4f

File tree

4 files changed

+63
-32
lines changed

4 files changed

+63
-32
lines changed

lldb/include/lldb/API/SBValue.h

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -160,31 +160,26 @@ class LLDB_API SBValue {
160160
/// members (empty base classes are omitted), and all members of the
161161
/// current class will then follow the base classes.
162162
///
163-
/// Pointers differ depending on what they point to. If the pointer
164-
/// points to a simple type, the child at index zero
165-
/// is the only child value available, unless \a synthetic_allowed
166-
/// is \b true, in which case the pointer will be used as an array
167-
/// and can create 'synthetic' child values using positive or
168-
/// negative indexes. If the pointer points to an aggregate type
169-
/// (an array, class, union, struct), then the pointee is
170-
/// transparently skipped and any children are going to be the indexes
171-
/// of the child values within the aggregate type. For example if
172-
/// we have a 'Point' type and we have a SBValue that contains a
173-
/// pointer to a 'Point' type, then the child at index zero will be
174-
/// the 'x' member, and the child at index 1 will be the 'y' member
175-
/// (the child at index zero won't be a 'Point' instance).
163+
/// For array and pointers the behavior of the function depends on the value
164+
/// of the \a treat_as_array argument. If \b false, the function returns
165+
/// members of the array as given by the array bounds. If the value is a
166+
/// pointer to a simple type, the child at index zero is the only child
167+
/// value available. If the pointer points to an aggregate type (an array,
168+
/// class, union, etc.), then the pointee is transparently skipped and any
169+
/// children are going to be the indexes of the child values within the
170+
/// aggregate type. For example if we have a 'Point' type and we have a
171+
/// SBValue that contains a pointer to a 'Point' type, then the child at
172+
/// index zero will be the 'x' member, and the child at index 1 will be the
173+
/// 'y' member (the child at index zero won't be a 'Point' instance). If \a
174+
/// treat_as_array is \b true, pointer values will be used as a (C) array and
175+
/// and the function will create 'synthetic' child values using positive or
176+
/// negative indexes. In case of arrays, the function will return values
177+
/// which are outside of the array bounds.
176178
///
177179
/// If you actually need an SBValue that represents the type pointed
178180
/// to by a SBValue for which GetType().IsPointeeType() returns true,
179181
/// regardless of the pointee type, you can do that with SBValue::Dereference.
180182
///
181-
/// Arrays have a preset number of children that can be accessed by
182-
/// index and will returns invalid child values for indexes that are
183-
/// out of bounds unless the \a synthetic_allowed is \b true. In this
184-
/// case the array can create 'synthetic' child values for indexes
185-
/// that aren't in the array bounds using positive or negative
186-
/// indexes.
187-
///
188183
/// \param[in] idx
189184
/// The index of the child value to get
190185
///
@@ -193,7 +188,7 @@ class LLDB_API SBValue {
193188
/// and also if the target can be run to figure out the dynamic
194189
/// type of the child value.
195190
///
196-
/// \param[in] can_create_synthetic
191+
/// \param[in] treat_as_array
197192
/// If \b true, then allow child values to be created by index
198193
/// for pointers and arrays for indexes that normally wouldn't
199194
/// be allowed.
@@ -202,7 +197,7 @@ class LLDB_API SBValue {
202197
/// A new SBValue object that represents the child member value.
203198
lldb::SBValue GetChildAtIndex(uint32_t idx,
204199
lldb::DynamicValueType use_dynamic,
205-
bool can_create_synthetic);
200+
bool treat_as_array);
206201

207202
// Matches children of this object only and will match base classes and
208203
// member names if this is a clang typed object.

lldb/source/API/SBValue.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,6 @@ lldb::SBValue SBValue::CreateBoolValue(const char *name, bool value) {
666666
SBValue SBValue::GetChildAtIndex(uint32_t idx) {
667667
LLDB_INSTRUMENT_VA(this, idx);
668668

669-
const bool can_create_synthetic = false;
670669
lldb::DynamicValueType use_dynamic = eNoDynamicValues;
671670
TargetSP target_sp;
672671
if (m_opaque_sp)
@@ -675,24 +674,24 @@ SBValue SBValue::GetChildAtIndex(uint32_t idx) {
675674
if (target_sp)
676675
use_dynamic = target_sp->GetPreferDynamicValue();
677676

678-
return GetChildAtIndex(idx, use_dynamic, can_create_synthetic);
677+
return GetChildAtIndex(idx, use_dynamic, /*treat_as_array=*/false);
679678
}
680679

681680
SBValue SBValue::GetChildAtIndex(uint32_t idx,
682681
lldb::DynamicValueType use_dynamic,
683-
bool can_create_synthetic) {
684-
LLDB_INSTRUMENT_VA(this, idx, use_dynamic, can_create_synthetic);
685-
686-
lldb::ValueObjectSP child_sp;
687-
682+
bool treat_as_array) {
683+
LLDB_INSTRUMENT_VA(this, idx, use_dynamic, treat_as_array);
688684
ValueLocker locker;
689685
lldb::ValueObjectSP value_sp(GetSP(locker));
686+
687+
lldb::ValueObjectSP child_sp;
690688
if (value_sp) {
691689
const bool can_create = true;
692-
child_sp = value_sp->GetChildAtIndex(idx);
693-
if (can_create_synthetic && !child_sp) {
690+
if (treat_as_array &&
691+
(value_sp->IsPointerType() || value_sp->IsArrayType()))
694692
child_sp = value_sp->GetSyntheticArrayMember(idx, can_create);
695-
}
693+
else
694+
child_sp = value_sp->GetChildAtIndex(idx);
696695
}
697696

698697
SBValue sb_value;

lldb/test/API/python_api/sbvalue_synthetic/TestSBValueSynthetic.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,31 @@ def test_str(self):
2121
has_formatted = self.frame().FindVariable("has_foo")
2222
self.expect(str(formatted), exe=False, substrs=["synth_child"])
2323
self.expect(str(has_formatted), exe=False, substrs=["synth_child"])
24+
25+
def test_synth_arr(self):
26+
self.build()
27+
lldbutil.run_to_source_breakpoint(
28+
self, "break here", lldb.SBFileSpec("main.cpp")
29+
)
30+
point_arr = self.frame().FindVariable("point_arr")
31+
point_ptr = self.frame().FindVariable("point_ptr")
32+
for v in [point_arr, point_ptr]:
33+
for i in range(3):
34+
child = v.GetChildAtIndex(i, lldb.eDynamicDontRunTarget, True)
35+
check = ValueCheck(
36+
name=f"[{i}]",
37+
type="Point",
38+
children=[
39+
ValueCheck(name="x", value=str(2 * i + 1)),
40+
ValueCheck(name="y", value=str(2 * i + 2)),
41+
],
42+
)
43+
check.check_value(self, child, f"{child}, child {i} of {v.GetName()}")
44+
45+
int_arr = self.frame().FindVariable("int_arr")
46+
int_ptr = self.frame().FindVariable("int_ptr")
47+
for v in [int_arr, int_ptr]:
48+
for i in range(3):
49+
child = v.GetChildAtIndex(i, lldb.eDynamicDontRunTarget, True)
50+
check = ValueCheck(name=f"[{i}]", type="int", value=str(i + 1))
51+
check.check_value(self, child, f"{child}, child {i} of {v.GetName()}")

lldb/test/API/python_api/sbvalue_synthetic/main.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,17 @@ struct HasFoo {
66
Foo f;
77
};
88

9+
struct Point {
10+
int x;
11+
int y;
12+
};
13+
914
int main() {
1015
Foo foo;
1116
HasFoo has_foo;
17+
Point point_arr[] = {{1, 2}, {3, 4}, {5, 6}};
18+
int int_arr[] = {1, 2, 3, 4, 5, 6};
19+
Point *point_ptr = point_arr;
20+
int *int_ptr = int_arr;
1221
return 0; // break here
1322
}

0 commit comments

Comments
 (0)