diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index c8cb54aa18a93..adc34e25766b3 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -253,6 +253,12 @@ Interpreter::Visit(const UnaryOpNode *node) { rhs = dynamic_rhs; lldb::ValueObjectSP child_sp = rhs->Dereference(error); + if (!child_sp && m_use_synthetic) { + if (lldb::ValueObjectSP synth_obj_sp = rhs->GetSyntheticValue()) { + error.Clear(); + child_sp = synth_obj_sp->Dereference(error); + } + } if (error.Fail()) return llvm::make_error(m_expr, error.AsCString(), node->GetLocation()); @@ -280,6 +286,7 @@ Interpreter::Visit(const MemberOfNode *node) { auto base_or_err = Evaluate(node->GetBase()); if (!base_or_err) return base_or_err; + bool expr_is_ptr = node->GetIsArrow(); lldb::ValueObjectSP base = *base_or_err; // Perform some basic type & correctness checking. @@ -319,11 +326,11 @@ Interpreter::Visit(const MemberOfNode *node) { return llvm::make_error( m_expr, errMsg, node->GetLocation(), node->GetFieldName().size()); } + expr_is_ptr = false; } } if (m_check_ptr_vs_member) { - bool expr_is_ptr = node->GetIsArrow(); bool base_is_ptr = base->IsPointerType(); if (expr_is_ptr != base_is_ptr) { diff --git a/lldb/test/API/commands/frame/var-dil/basics/BitField/Makefile b/lldb/test/API/commands/frame/var-dil/basics/BitField/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/BitField/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/BitField/TestFrameVarDILBitField.py b/lldb/test/API/commands/frame/var-dil/basics/BitField/TestFrameVarDILBitField.py new file mode 100644 index 0000000000000..0e35069519093 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/BitField/TestFrameVarDILBitField.py @@ -0,0 +1,43 @@ +""" +Make sure 'frame var' using DIL parser/evaluator works for bit fields. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + +import os +import shutil +import time + + +class TestFrameVarDILBitField(TestBase): + # If your test case doesn't stress debug info, then + # set this to true. That way it won't be run once for + # each debug info format. + NO_DEBUG_INFO_TESTCASE = True + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("bf.a", value="1023") + self.expect_var_path("bf.b", value="9") + self.expect_var_path("bf.c", value="false") + self.expect_var_path("bf.d", value="true") + + self.expect_var_path("abf.a", value="1023") + self.expect_var_path("abf.b", value="'\\x0f'") + self.expect_var_path("abf.c", value="3") + + # Perform an operation to ensure we actually read the value. + # Address-of is not allowed for bit-fields. + self.expect( + "frame variable '&bf.a'", + error=True, + substrs=["'bf.a' doesn't have a valid address"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/BitField/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/BitField/main.cpp new file mode 100644 index 0000000000000..5705cfb2df642 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/BitField/main.cpp @@ -0,0 +1,42 @@ +#include + +int main(int argc, char **argv) { + enum BitFieldEnum : uint32_t { kZero, kOne }; + + struct BitFieldStruct { + uint16_t a : 10; + uint32_t b : 4; + bool c : 1; + bool d : 1; + int32_t e : 32; + uint32_t f : 32; + uint32_t g : 31; + uint64_t h : 31; + uint64_t i : 33; + BitFieldEnum j : 10; + }; + + BitFieldStruct bf; + bf.a = 0b1111111111; + bf.b = 0b1001; + bf.c = 0b0; + bf.d = 0b1; + bf.e = 0b1; + bf.f = 0b1; + bf.g = 0b1; + bf.h = 0b1; + bf.i = 0b1; + bf.j = BitFieldEnum::kOne; + + struct AlignedBitFieldStruct { + uint16_t a : 10; + uint8_t b : 4; + unsigned char : 0; + uint16_t c : 2; + }; + + uint32_t data = ~0; + AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data; + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/Indirection/Makefile b/lldb/test/API/commands/frame/var-dil/basics/Indirection/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/Indirection/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py new file mode 100644 index 0000000000000..38c72131d797c --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py @@ -0,0 +1,46 @@ +""" +Make sure 'frame var' using DIL parser/evaluator works for indirection. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + +import os +import shutil +import time + + +class TestFrameVarDILIndirection(TestBase): + # If your test case doesn't stress debug info, then + # set this to true. That way it won't be run once for + # each debug info format. + NO_DEBUG_INFO_TESTCASE = True + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("*p", value="1") + self.expect_var_path("p", type="int *") + self.expect_var_path("*my_p", value="1") + self.expect_var_path("my_p", type="myp") + self.expect_var_path("*my_pr", type="int *") + self.expect_var_path("my_pr", type="mypr") + + self.expect( + "frame variable '*1'", + error=True, + substrs=["Unexpected token: <'1' (numeric_constant)>"], + ) + self.expect( + "frame variable '*val'", + error=True, + substrs=[ + "dereference failed: not a pointer, reference or array type: (int) val" + ], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/Indirection/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/Indirection/main.cpp new file mode 100644 index 0000000000000..9fbd99b219ce1 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/Indirection/main.cpp @@ -0,0 +1,12 @@ +int main(int argc, char **argv) { + int val = 1; + int *p = &val; + + typedef int *myp; + myp my_p = &val; + + typedef int *&mypr; + mypr my_pr = p; + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/Makefile b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py new file mode 100644 index 0000000000000..ffb447441e982 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py @@ -0,0 +1,48 @@ +""" +Test DIL pointer dereferencing. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + +import os +import shutil +import time + + +class TestFrameVarDILPointerDereference(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("*p_int0", value="0") + self.expect_var_path("*cp_int5", value="5") + self.expect_var_path("&pp_void0[2]", type="void **") + self.expect_var_path("**pp_int0", value="0") + self.expect_var_path("&**pp_int0", type="int *") + self.expect( + "frame variable '&p_void[0]'", + error=True, + substrs=["subscript of pointer to incomplete type 'void'"], + ) + + # Verify some of the returned values. + pp_void0_2_got = self.expect_var_path("&pp_void0[2]", type="void **") + # Initialized in C++ code to point to the same value + pp_void0_2_exp = self.expect_var_path("pp_void0_2", type="void **") + self.assertEqual( + pp_void0_2_got.GetValueAsAddress(), pp_void0_2_exp.GetValueAsAddress() + ) + pp_int0_2stars_got = self.expect_var_path("&**pp_int0", type="int *") + pp_int0_2stars_exp = self.expect_var_path("pp_int0_2stars", type="int *") + self.assertEqual( + pp_int0_2stars_got.GetValueAsAddress(), + pp_int0_2stars_exp.GetValueAsAddress(), + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/main.cpp new file mode 100644 index 0000000000000..0c39609dd591f --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/main.cpp @@ -0,0 +1,34 @@ +#include + +int main(int argc, char **argv) { + int *p_null = nullptr; + const char *p_char1 = "hello"; + + typedef const char *my_char_ptr; + my_char_ptr my_p_char1 = p_char1; + + int offset = 5; + int array[10]; + array[0] = 0; + array[offset] = offset; + + int (&array_ref)[10] = array; + + int *p_int0 = &array[0]; + int **pp_int0 = &p_int0; + const int *cp_int0 = &array[0]; + const int *cp_int5 = &array[offset]; + + typedef int *td_int_ptr_t; + td_int_ptr_t td_int_ptr0 = &array[0]; + + void *p_void = (void *)p_char1; + void **pp_void0 = &p_void; + void **pp_void1 = pp_void0 + 1; + + void **pp_void0_2 = &pp_void0[2]; + int *pp_int0_2stars = &**pp_int0; + std::nullptr_t std_nullptr_t = nullptr; + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/Makefile b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/TestFrameVarDILQualifiedId.py b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/TestFrameVarDILQualifiedId.py new file mode 100644 index 0000000000000..b2ce9602e6a50 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/TestFrameVarDILQualifiedId.py @@ -0,0 +1,31 @@ +""" +Make sure 'frame var' using DIL parser/evaluator works for namespaces. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + +import os +import shutil +import time + + +class TestFrameVarDILQualifiedId(TestBase): + # If your test case doesn't stress debug info, then + # set this to true. That way it won't be run once for + # each debug info format. + NO_DEBUG_INFO_TESTCASE = True + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("::ns::i", value="1") + self.expect_var_path("ns::i", value="1") + self.expect_var_path("::ns::ns::i", value="2") + self.expect_var_path("ns::ns::i", value="2") diff --git a/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/main.cpp new file mode 100644 index 0000000000000..8a5c47a6f364c --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/QualifiedId/main.cpp @@ -0,0 +1,16 @@ +namespace ns { + +int i = 1; + +namespace ns { + +int i = 2; + +} // namespace ns + +} // namespace ns + +int main(int argc, char **argv) { + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/Makefile b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/TestFrameVarDILSyntheticDereference.py b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/TestFrameVarDILSyntheticDereference.py new file mode 100644 index 0000000000000..e67f638461755 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/TestFrameVarDILSyntheticDereference.py @@ -0,0 +1,34 @@ +""" +Test code for dereferencing synthetic wrapped pointers. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class DILSyntheticDereferenceTestCase(TestBase): + # If your test case doesn't stress debug info, then + # set this to true. That way it won't be run once for + # each debug info format. + NO_DEBUG_INFO_TESTCASE = True + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.runCmd("script from wrapPtrSynthProvider import *") + self.runCmd("type synth add -l wrapPtrSynthProvider wrap_ptr") + + self.expect_var_path("ptr_node->value", value="1") + self.expect_var_path("ptr_node->next->value", value="2") + self.expect_var_path("(*ptr_node).value", value="1") + self.expect_var_path("(*(*ptr_node).next).value", value="2") + + self.expect_var_path("ptr_node.ptr", type="NodeS *") + self.expect_var_path("ptr_node.ptr->value", value="1") + self.expect_var_path("ptr_node.ptr->next.ptr->value", value="2") diff --git a/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/main.cpp new file mode 100644 index 0000000000000..dc9c01b46d583 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/main.cpp @@ -0,0 +1,26 @@ +#include + +struct NodeS; + +// Class to wrap pointers. +class wrap_ptr { +public: + NodeS *ptr; + + wrap_ptr(NodeS *n_ptr) : ptr(n_ptr) {} +}; + +struct NodeS { + wrap_ptr next; + int value; + + NodeS(NodeS *n_ptr, int val) : next(wrap_ptr(n_ptr)), value(val) {} +}; + +int main(int argc, char **argv) { + + // Make a short linked list of fake smart pointers. + auto ptr_node = wrap_ptr(new NodeS(new NodeS(nullptr, 2), 1)); + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/wrapPtrSynthProvider.py b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/wrapPtrSynthProvider.py new file mode 100644 index 0000000000000..f89e426c0fd77 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/SyntheticDereference/wrapPtrSynthProvider.py @@ -0,0 +1,29 @@ +import lldb + + +class wrapPtrSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + + def num_children(self): + return 1 + + def get_child_at_index(self, index): + if index == 0: + return self.valobj.GetChildMemberWithName("ptr") + if index == 1: + internal_child = self.valobj.GetChildMemberWithName("ptr") + if not internal_child: + return None + return internal_child.Dereference() + return None + + def get_child_index(self, name): + if name == "ptr": + return 0 + if name == "$$dereference$$": + return 1 + return -1 + + def update(self): + return True diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py index 23011c951e213..61c050b3bfa01 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py @@ -90,3 +90,9 @@ def test_shared_ptr_variables(self): self.expect_var_path("sp_user->id", type="int", value="30") self.expect_var_path("sp_user->name", type="std::string", summary='"steph"') + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("ptr_node->value", value="1") + self.expect_var_path("ptr_node->next->value", value="2") + self.expect_var_path("(*ptr_node).value", value="1") + self.expect_var_path("(*(*ptr_node).next).value", value="2") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/main.cpp index 026bfe24e72e7..84a02ec13df1c 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/main.cpp @@ -6,6 +6,11 @@ struct User { std::string name = "steph"; }; +struct NodeS { + std::shared_ptr next; + int value; +}; + int main() { std::shared_ptr sp_empty; std::shared_ptr sp_int = std::make_shared(10); @@ -13,6 +18,9 @@ int main() { std::shared_ptr &sp_int_ref = sp_int; std::shared_ptr &&sp_int_ref_ref = std::make_shared(10); std::shared_ptr sp_user = std::make_shared(); + std::shared_ptr ptr_node = + std::shared_ptr(new NodeS{nullptr, 2}); + ptr_node = std::shared_ptr(new NodeS{std::move(ptr_node), 1}); return 0; // break here } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py index 6a726e0253482..25a1cd82a4baa 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py @@ -115,3 +115,9 @@ def test_unique_ptr_variables(self): self.expect_var_path("up_user->id", type="int", value="30") self.expect_var_path("up_user->name", type="std::string", summary='"steph"') + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("ptr_node->value", value="1") + self.expect_var_path("ptr_node->next->value", value="2") + self.expect_var_path("(*ptr_node).value", value="1") + self.expect_var_path("(*(*ptr_node).next).value", value="2") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp index ab65d95b248ef..afdddf0bbaf16 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp @@ -6,6 +6,11 @@ struct User { std::string name = "steph"; }; +struct NodeU { + std::unique_ptr next; + int value; +}; + // libc++ stores unique_ptr data in a compressed pair, which has a specialized // representation when the type of the second element is an empty class. So // we need a deleter class with a dummy data member to trigger the other path. @@ -24,6 +29,9 @@ int main() { std::unique_ptr up_user = std::make_unique(); auto up_non_empty_deleter = std::unique_ptr(new int(1234)); + std::unique_ptr ptr_node = + std::unique_ptr(new NodeU{nullptr, 2}); + ptr_node = std::unique_ptr(new NodeU{std::move(ptr_node), 1}); return 0; // break here }