[LLDB] Update DIL handling of array subscripting. (#151605)
This updates the DIL code for handling array subscripting to more closely match and handle all the cases from the original 'frame var' implementation. Also updates the DIL array subscripting test. This particularly fixes some issues with handling synthetic children, objc pointers, and accessing specific bits within scalar data types.
This commit is contained in:
parent
11c2240049
commit
6d3ad9d9fd
@ -330,40 +330,83 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
|
|||||||
return lhs_or_err;
|
return lhs_or_err;
|
||||||
lldb::ValueObjectSP base = *lhs_or_err;
|
lldb::ValueObjectSP base = *lhs_or_err;
|
||||||
|
|
||||||
// Check to see if 'base' has a synthetic value; if so, try using that.
|
StreamString var_expr_path_strm;
|
||||||
uint64_t child_idx = node->GetIndex();
|
uint64_t child_idx = node->GetIndex();
|
||||||
if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) {
|
lldb::ValueObjectSP child_valobj_sp;
|
||||||
llvm::Expected<uint32_t> num_children =
|
bool is_incomplete_array = false;
|
||||||
synthetic->GetNumChildren(child_idx + 1);
|
CompilerType base_type = base->GetCompilerType().GetNonReferenceType();
|
||||||
if (!num_children)
|
base->GetExpressionPath(var_expr_path_strm);
|
||||||
return llvm::make_error<DILDiagnosticError>(
|
if (base_type.IsPointerType()) {
|
||||||
m_expr, toString(num_children.takeError()), node->GetLocation());
|
child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true);
|
||||||
if (child_idx >= *num_children) {
|
if (!child_valobj_sp) {
|
||||||
std::string message = llvm::formatv(
|
std::string err_msg = llvm::formatv(
|
||||||
"array index {0} is not valid for \"({1}) {2}\"", child_idx,
|
"failed to use pointer as array for index {0} for "
|
||||||
base->GetTypeName().AsCString("<invalid type>"),
|
"\"({1}) {2}\"",
|
||||||
base->GetName().AsCString());
|
child_idx, base->GetTypeName().AsCString("<invalid type>"),
|
||||||
return llvm::make_error<DILDiagnosticError>(m_expr, message,
|
var_expr_path_strm.GetData());
|
||||||
|
if (base_type.IsPointerToVoid())
|
||||||
|
err_msg = "subscript of pointer to incomplete type 'void'";
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
node->GetLocation());
|
node->GetLocation());
|
||||||
}
|
}
|
||||||
if (lldb::ValueObjectSP child_valobj_sp =
|
} else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) {
|
||||||
synthetic->GetChildAtIndex(child_idx))
|
child_valobj_sp = base->GetChildAtIndex(child_idx);
|
||||||
return child_valobj_sp;
|
if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic))
|
||||||
|
child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true);
|
||||||
|
if (!child_valobj_sp) {
|
||||||
|
std::string err_msg = llvm::formatv(
|
||||||
|
"array index {0} is not valid for \"({1}) {2}\"", child_idx,
|
||||||
|
base->GetTypeName().AsCString("<invalid type>"),
|
||||||
|
var_expr_path_strm.GetData());
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
|
node->GetLocation());
|
||||||
|
}
|
||||||
|
} else if (base_type.IsScalarType()) {
|
||||||
|
child_valobj_sp =
|
||||||
|
base->GetSyntheticBitFieldChild(child_idx, child_idx, true);
|
||||||
|
if (!child_valobj_sp) {
|
||||||
|
std::string err_msg = llvm::formatv(
|
||||||
|
"bitfield range {0}-{1} is not valid for \"({2}) {3}\"", child_idx,
|
||||||
|
child_idx, base->GetTypeName().AsCString("<invalid type>"),
|
||||||
|
var_expr_path_strm.GetData());
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
|
node->GetLocation(), 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lldb::ValueObjectSP synthetic = base->GetSyntheticValue();
|
||||||
|
if (!m_use_synthetic || !synthetic || synthetic == base) {
|
||||||
|
std::string err_msg =
|
||||||
|
llvm::formatv("\"{0}\" is not an array type",
|
||||||
|
base->GetTypeName().AsCString("<invalid type>"));
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
|
node->GetLocation(), 1);
|
||||||
|
}
|
||||||
|
if (static_cast<uint32_t>(child_idx) >=
|
||||||
|
synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) {
|
||||||
|
std::string err_msg = llvm::formatv(
|
||||||
|
"array index {0} is not valid for \"({1}) {2}\"", child_idx,
|
||||||
|
base->GetTypeName().AsCString("<invalid type>"),
|
||||||
|
var_expr_path_strm.GetData());
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
|
node->GetLocation(), 1);
|
||||||
|
}
|
||||||
|
child_valobj_sp = synthetic->GetChildAtIndex(child_idx);
|
||||||
|
if (!child_valobj_sp) {
|
||||||
|
std::string err_msg = llvm::formatv(
|
||||||
|
"array index {0} is not valid for \"({1}) {2}\"", child_idx,
|
||||||
|
base->GetTypeName().AsCString("<invalid type>"),
|
||||||
|
var_expr_path_strm.GetData());
|
||||||
|
return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
|
||||||
|
node->GetLocation(), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto base_type = base->GetCompilerType().GetNonReferenceType();
|
if (child_valobj_sp) {
|
||||||
if (!base_type.IsPointerType() && !base_type.IsArrayType())
|
if (m_use_dynamic != lldb::eNoDynamicValues) {
|
||||||
return llvm::make_error<DILDiagnosticError>(
|
if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic))
|
||||||
m_expr, "subscripted value is not an array or pointer",
|
child_valobj_sp = std::move(dynamic_sp);
|
||||||
node->GetLocation());
|
}
|
||||||
if (base_type.IsPointerToVoid())
|
return child_valobj_sp;
|
||||||
return llvm::make_error<DILDiagnosticError>(
|
|
||||||
m_expr, "subscript of pointer to incomplete type 'void'",
|
|
||||||
node->GetLocation());
|
|
||||||
|
|
||||||
if (base_type.IsArrayType()) {
|
|
||||||
if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx))
|
|
||||||
return child_valobj_sp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t signed_child_idx = node->GetIndex();
|
int64_t signed_child_idx = node->GetIndex();
|
||||||
|
@ -69,17 +69,18 @@ class TestFrameVarDILArraySubscript(TestBase):
|
|||||||
substrs=["expected 'r_square', got: <'.'"],
|
substrs=["expected 'r_square', got: <'.'"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Base should be a "pointer to T" and index should be of an integral type.
|
# Test accessing bits in scalar types.
|
||||||
self.expect(
|
self.expect_var_path("idx_1[0]", value="1")
|
||||||
"frame var 'idx_1[0]'",
|
self.expect_var_path("idx_1[1]", value="0")
|
||||||
error=True,
|
|
||||||
substrs=["subscripted value is not an array or pointer"],
|
# Bit adcess not valid for a reference.
|
||||||
)
|
|
||||||
self.expect(
|
self.expect(
|
||||||
"frame var 'idx_1_ref[0]'",
|
"frame var 'idx_1_ref[0]'",
|
||||||
error=True,
|
error=True,
|
||||||
substrs=["subscripted value is not an array or pointer"],
|
substrs=["bitfield range 0-0 is not valid"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Base should be a "pointer to T" and index should be of an integral type.
|
||||||
self.expect(
|
self.expect(
|
||||||
"frame var 'int_arr[int_ptr]'",
|
"frame var 'int_arr[int_ptr]'",
|
||||||
error=True,
|
error=True,
|
||||||
@ -105,6 +106,8 @@ class TestFrameVarDILArraySubscript(TestBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.runCmd("settings set target.experimental.use-DIL true")
|
self.runCmd("settings set target.experimental.use-DIL true")
|
||||||
|
self.runCmd("script from myArraySynthProvider import *")
|
||||||
|
self.runCmd("type synth add -l myArraySynthProvider myArray")
|
||||||
|
|
||||||
# Test synthetic value subscription
|
# Test synthetic value subscription
|
||||||
self.expect_var_path("vector[1]", value="2")
|
self.expect_var_path("vector[1]", value="2")
|
||||||
@ -113,3 +116,7 @@ class TestFrameVarDILArraySubscript(TestBase):
|
|||||||
error=True,
|
error=True,
|
||||||
substrs=["array index 100 is not valid"],
|
substrs=["array index 100 is not valid"],
|
||||||
)
|
)
|
||||||
|
self.expect(
|
||||||
|
"frame var 'ma_ptr[0]'",
|
||||||
|
substrs=["(myArray) ma_ptr[0] = ([0] = 7, [1] = 8, [2] = 9, [3] = 10)"],
|
||||||
|
)
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class myArray {
|
||||||
|
public:
|
||||||
|
int m_array[4] = {7, 8, 9, 10};
|
||||||
|
int m_arr_size = 4;
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int int_arr[] = {1, 2, 3};
|
int int_arr[] = {1, 2, 3};
|
||||||
int *int_ptr = int_arr;
|
int *int_ptr = int_arr;
|
||||||
@ -29,5 +35,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
std::vector<int> vector = {1, 2, 3};
|
std::vector<int> vector = {1, 2, 3};
|
||||||
|
|
||||||
|
myArray ma;
|
||||||
|
myArray *ma_ptr = &ma;
|
||||||
|
|
||||||
return 0; // Set a breakpoint here
|
return 0; // Set a breakpoint here
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
import lldb
|
||||||
|
|
||||||
|
|
||||||
|
class myArraySynthProvider:
|
||||||
|
def __init__(self, valobj, dict):
|
||||||
|
self.valobj = valobj
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
size_valobj = self.valobj.GetChildMemberWithName("m_arr_size")
|
||||||
|
if size_valobj:
|
||||||
|
return size_valobj.GetValueAsUnsigned(0)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
size_valobj = self.valobj.GetChildMemberWithName("m_arr_size")
|
||||||
|
arr = self.valobj.GetChildMemberWithName("m_array")
|
||||||
|
if not size_valobj or not arr:
|
||||||
|
return None
|
||||||
|
max_idx = size_valobj.GetValueAsUnsigned(0)
|
||||||
|
if index >= max_idx:
|
||||||
|
return None
|
||||||
|
return arr.GetChildAtIndex(index)
|
||||||
|
|
||||||
|
def get_child_index(self, name):
|
||||||
|
if name == "[0]":
|
||||||
|
return 0
|
||||||
|
if name == "[1]":
|
||||||
|
return
|
||||||
|
if name == "[2]":
|
||||||
|
return 2
|
||||||
|
if name == "[3]":
|
||||||
|
return 3
|
||||||
|
return -1
|
Loading…
x
Reference in New Issue
Block a user