1 | //===-- GenericOptional.cpp ----------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===---------------------------------------------------------------------===// |
8 | |
9 | #include "Generic.h" |
10 | #include "LibCxx.h" |
11 | #include "LibStdcpp.h" |
12 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
13 | #include "lldb/DataFormatters/FormattersHelpers.h" |
14 | #include "lldb/Target/Target.h" |
15 | |
16 | using namespace lldb; |
17 | using namespace lldb_private; |
18 | |
19 | bool lldb_private::formatters::GenericOptionalSummaryProvider( |
20 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
21 | stream.Printf(format: " Has Value=%s " , |
22 | valobj.GetNumChildrenIgnoringErrors() == 0 ? "false" : "true" ); |
23 | |
24 | return true; |
25 | } |
26 | |
27 | // Synthetic Children Provider |
28 | namespace { |
29 | |
30 | class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { |
31 | public: |
32 | enum class StdLib { |
33 | LibCxx, |
34 | LibStdcpp, |
35 | }; |
36 | |
37 | GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib); |
38 | |
39 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { |
40 | if (name == "$$dereference$$" ) |
41 | return 0; |
42 | auto optional_idx = formatters::ExtractIndexFromString(item_name: name.GetCString()); |
43 | if (!optional_idx) { |
44 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
45 | Vals: name.AsCString()); |
46 | } |
47 | return *optional_idx; |
48 | } |
49 | |
50 | llvm::Expected<uint32_t> CalculateNumChildren() override { |
51 | return m_has_value ? 1U : 0U; |
52 | } |
53 | |
54 | ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
55 | lldb::ChildCacheState Update() override; |
56 | |
57 | private: |
58 | bool m_has_value = false; |
59 | StdLib m_stdlib; |
60 | }; |
61 | |
62 | } // namespace |
63 | |
64 | GenericOptionalFrontend::GenericOptionalFrontend(ValueObject &valobj, |
65 | StdLib stdlib) |
66 | : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { |
67 | if (auto target_sp = m_backend.GetTargetSP()) { |
68 | Update(); |
69 | } |
70 | } |
71 | |
72 | lldb::ChildCacheState GenericOptionalFrontend::Update() { |
73 | ValueObjectSP engaged_sp; |
74 | |
75 | if (m_stdlib == StdLib::LibCxx) |
76 | engaged_sp = m_backend.GetChildMemberWithName(name: "__engaged_" ); |
77 | else if (m_stdlib == StdLib::LibStdcpp) { |
78 | if (ValueObjectSP payload = m_backend.GetChildMemberWithName(name: "_M_payload" )) |
79 | engaged_sp = payload->GetChildMemberWithName(name: "_M_engaged" ); |
80 | } |
81 | |
82 | if (!engaged_sp) |
83 | return lldb::ChildCacheState::eRefetch; |
84 | |
85 | // _M_engaged/__engaged is a bool flag and is true if the optional contains a |
86 | // value. Converting it to unsigned gives us a size of 1 if it contains a |
87 | // value and 0 if not. |
88 | m_has_value = engaged_sp->GetValueAsUnsigned(fail_value: 0) != 0; |
89 | |
90 | return lldb::ChildCacheState::eRefetch; |
91 | } |
92 | |
93 | ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(uint32_t _idx) { |
94 | if (!m_has_value) |
95 | return ValueObjectSP(); |
96 | |
97 | ValueObjectSP val_sp; |
98 | |
99 | if (m_stdlib == StdLib::LibCxx) |
100 | // __val_ contains the underlying value of an optional if it has one. |
101 | // Currently because it is part of an anonymous union |
102 | // GetChildMemberWithName() does not peer through and find it unless we are |
103 | // at the parent itself. We can obtain the parent through __engaged_. |
104 | val_sp = m_backend.GetChildMemberWithName(name: "__engaged_" ) |
105 | ->GetParent() |
106 | ->GetChildAtIndex(idx: 0) |
107 | ->GetChildMemberWithName(name: "__val_" ); |
108 | else if (m_stdlib == StdLib::LibStdcpp) { |
109 | val_sp = m_backend.GetChildMemberWithName(name: "_M_payload" ) |
110 | ->GetChildMemberWithName(name: "_M_payload" ); |
111 | |
112 | // In some implementations, _M_value contains the underlying value of an |
113 | // optional, and in other versions, it's in the payload member. |
114 | ValueObjectSP candidate = val_sp->GetChildMemberWithName(name: "_M_value" ); |
115 | if (candidate) |
116 | val_sp = candidate; |
117 | } |
118 | |
119 | if (!val_sp) |
120 | return ValueObjectSP(); |
121 | |
122 | CompilerType holder_type = val_sp->GetCompilerType(); |
123 | |
124 | if (!holder_type) |
125 | return ValueObjectSP(); |
126 | |
127 | return val_sp->Clone(new_name: ConstString("Value" )); |
128 | } |
129 | |
130 | SyntheticChildrenFrontEnd * |
131 | formatters::LibStdcppOptionalSyntheticFrontEndCreator( |
132 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
133 | if (valobj_sp) |
134 | return new GenericOptionalFrontend( |
135 | *valobj_sp, GenericOptionalFrontend::StdLib::LibStdcpp); |
136 | return nullptr; |
137 | } |
138 | |
139 | SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator( |
140 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
141 | if (valobj_sp) |
142 | return new GenericOptionalFrontend(*valobj_sp, |
143 | GenericOptionalFrontend::StdLib::LibCxx); |
144 | return nullptr; |
145 | } |
146 | |