xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- LibCxxUnorderedMap.cpp --------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "LibCxx.h"
100b57cec5SDimitry Andric 
115ffd83dbSDimitry Andric #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
120b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h"
130b57cec5SDimitry Andric #include "lldb/Core/ValueObjectConstResult.h"
140b57cec5SDimitry Andric #include "lldb/DataFormatters/FormattersHelpers.h"
150b57cec5SDimitry Andric #include "lldb/Target/Target.h"
16bdd1243dSDimitry Andric #include "lldb/Utility/ConstString.h"
170b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
180b57cec5SDimitry Andric #include "lldb/Utility/Endian.h"
190b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
200b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
21bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h"
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric using namespace lldb;
240b57cec5SDimitry Andric using namespace lldb_private;
250b57cec5SDimitry Andric using namespace lldb_private::formatters;
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric namespace lldb_private {
280b57cec5SDimitry Andric namespace formatters {
290b57cec5SDimitry Andric class LibcxxStdUnorderedMapSyntheticFrontEnd
300b57cec5SDimitry Andric     : public SyntheticChildrenFrontEnd {
310b57cec5SDimitry Andric public:
320b57cec5SDimitry Andric   LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric   ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
350b57cec5SDimitry Andric 
36*0fca6ea1SDimitry Andric   llvm::Expected<uint32_t> CalculateNumChildren() override;
370b57cec5SDimitry Andric 
38*0fca6ea1SDimitry Andric   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
390b57cec5SDimitry Andric 
40*0fca6ea1SDimitry Andric   lldb::ChildCacheState Update() override;
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric   bool MightHaveChildren() override;
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   size_t GetIndexOfChildWithName(ConstString name) override;
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric private:
470b57cec5SDimitry Andric   CompilerType m_element_type;
480b57cec5SDimitry Andric   CompilerType m_node_type;
4981ad6265SDimitry Andric   ValueObject *m_tree = nullptr;
5081ad6265SDimitry Andric   size_t m_num_elements = 0;
5181ad6265SDimitry Andric   ValueObject *m_next_element = nullptr;
520b57cec5SDimitry Andric   std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
530b57cec5SDimitry Andric };
54*0fca6ea1SDimitry Andric 
55*0fca6ea1SDimitry Andric class LibCxxUnorderedMapIteratorSyntheticFrontEnd
56*0fca6ea1SDimitry Andric     : public SyntheticChildrenFrontEnd {
57*0fca6ea1SDimitry Andric public:
58*0fca6ea1SDimitry Andric   LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
59*0fca6ea1SDimitry Andric 
60*0fca6ea1SDimitry Andric   ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default;
61*0fca6ea1SDimitry Andric 
62*0fca6ea1SDimitry Andric   llvm::Expected<uint32_t> CalculateNumChildren() override;
63*0fca6ea1SDimitry Andric 
64*0fca6ea1SDimitry Andric   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
65*0fca6ea1SDimitry Andric 
66*0fca6ea1SDimitry Andric   lldb::ChildCacheState Update() override;
67*0fca6ea1SDimitry Andric 
68*0fca6ea1SDimitry Andric   bool MightHaveChildren() override;
69*0fca6ea1SDimitry Andric 
70*0fca6ea1SDimitry Andric   size_t GetIndexOfChildWithName(ConstString name) override;
71*0fca6ea1SDimitry Andric 
72*0fca6ea1SDimitry Andric private:
73*0fca6ea1SDimitry Andric   lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair
74*0fca6ea1SDimitry Andric                                  ///< that the iterator currently points
75*0fca6ea1SDimitry Andric                                  ///< to.
76*0fca6ea1SDimitry Andric };
77*0fca6ea1SDimitry Andric 
780b57cec5SDimitry Andric } // namespace formatters
790b57cec5SDimitry Andric } // namespace lldb_private
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
820b57cec5SDimitry Andric     LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
8381ad6265SDimitry Andric     : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
8481ad6265SDimitry Andric       m_elements_cache() {
850b57cec5SDimitry Andric   if (valobj_sp)
860b57cec5SDimitry Andric     Update();
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric 
89*0fca6ea1SDimitry Andric llvm::Expected<uint32_t> lldb_private::formatters::
90*0fca6ea1SDimitry Andric     LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() {
910b57cec5SDimitry Andric   return m_num_elements;
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric 
94bdd1243dSDimitry Andric static void consumeInlineNamespace(llvm::StringRef &name) {
95bdd1243dSDimitry Andric   // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
96bdd1243dSDimitry Andric   auto scratch = name;
97bdd1243dSDimitry Andric   if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
98bdd1243dSDimitry Andric     scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
99bdd1243dSDimitry Andric     if (scratch.consume_front("::")) {
100bdd1243dSDimitry Andric       // Successfully consumed a namespace.
101bdd1243dSDimitry Andric       name = scratch;
102bdd1243dSDimitry Andric     }
103bdd1243dSDimitry Andric   }
104bdd1243dSDimitry Andric }
105bdd1243dSDimitry Andric 
106bdd1243dSDimitry Andric static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
107bdd1243dSDimitry Andric   llvm::StringRef name = type_name.GetStringRef();
108bdd1243dSDimitry Andric   // The type name may be prefixed with `std::__<inline-namespace>::`.
109bdd1243dSDimitry Andric   if (name.consume_front("std::"))
110bdd1243dSDimitry Andric     consumeInlineNamespace(name);
1115f757f3fSDimitry Andric   return name.consume_front(type) && name.starts_with("<");
112bdd1243dSDimitry Andric }
113bdd1243dSDimitry Andric 
114bdd1243dSDimitry Andric static bool isUnorderedMap(ConstString type_name) {
115bdd1243dSDimitry Andric   return isStdTemplate(type_name, "unordered_map") ||
116bdd1243dSDimitry Andric          isStdTemplate(type_name, "unordered_multimap");
117bdd1243dSDimitry Andric }
118bdd1243dSDimitry Andric 
1190b57cec5SDimitry Andric lldb::ValueObjectSP lldb_private::formatters::
120*0fca6ea1SDimitry Andric     LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
121*0fca6ea1SDimitry Andric   if (idx >= CalculateNumChildrenIgnoringErrors())
1220b57cec5SDimitry Andric     return lldb::ValueObjectSP();
1230b57cec5SDimitry Andric   if (m_tree == nullptr)
1240b57cec5SDimitry Andric     return lldb::ValueObjectSP();
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric   while (idx >= m_elements_cache.size()) {
1270b57cec5SDimitry Andric     if (m_next_element == nullptr)
1280b57cec5SDimitry Andric       return lldb::ValueObjectSP();
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric     Status error;
1310b57cec5SDimitry Andric     ValueObjectSP node_sp = m_next_element->Dereference(error);
1320b57cec5SDimitry Andric     if (!node_sp || error.Fail())
1330b57cec5SDimitry Andric       return lldb::ValueObjectSP();
1340b57cec5SDimitry Andric 
13506c3fb27SDimitry Andric     ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_");
13606c3fb27SDimitry Andric     ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");
1370b57cec5SDimitry Andric     if (!hash_sp || !value_sp) {
1380b57cec5SDimitry Andric       if (!m_element_type) {
13906c3fb27SDimitry Andric         auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
1400b57cec5SDimitry Andric         if (!p1_sp)
1410b57cec5SDimitry Andric           return nullptr;
1420b57cec5SDimitry Andric 
143*0fca6ea1SDimitry Andric         ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
1440b57cec5SDimitry Andric         if (!first_sp)
1450b57cec5SDimitry Andric           return nullptr;
146*0fca6ea1SDimitry Andric 
1470b57cec5SDimitry Andric         m_element_type = first_sp->GetCompilerType();
1480b57cec5SDimitry Andric         m_element_type = m_element_type.GetTypeTemplateArgument(0);
1490b57cec5SDimitry Andric         m_element_type = m_element_type.GetPointeeType();
1500b57cec5SDimitry Andric         m_node_type = m_element_type;
1510b57cec5SDimitry Andric         m_element_type = m_element_type.GetTypeTemplateArgument(0);
152bdd1243dSDimitry Andric         // This synthetic provider is used for both unordered_(multi)map and
153bdd1243dSDimitry Andric         // unordered_(multi)set. For unordered_map, the element type has an
154bdd1243dSDimitry Andric         // additional type layer, an internal struct (`__hash_value_type`)
155bdd1243dSDimitry Andric         // that wraps a std::pair. Peel away the internal wrapper type - whose
156bdd1243dSDimitry Andric         // structure is of no value to users, to expose the std::pair. This
157bdd1243dSDimitry Andric         // matches the structure returned by the std::map synthetic provider.
158bdd1243dSDimitry Andric         if (isUnorderedMap(m_backend.GetTypeName())) {
1590b57cec5SDimitry Andric           std::string name;
160bdd1243dSDimitry Andric           CompilerType field_type = m_element_type.GetFieldAtIndex(
161bdd1243dSDimitry Andric               0, name, nullptr, nullptr, nullptr);
162bdd1243dSDimitry Andric           CompilerType actual_type = field_type.GetTypedefedType();
163bdd1243dSDimitry Andric           if (isStdTemplate(actual_type.GetTypeName(), "pair"))
164bdd1243dSDimitry Andric             m_element_type = actual_type;
165bdd1243dSDimitry Andric         }
1660b57cec5SDimitry Andric       }
1670b57cec5SDimitry Andric       if (!m_node_type)
1680b57cec5SDimitry Andric         return nullptr;
16906c3fb27SDimitry Andric       node_sp = m_next_element->Cast(m_node_type.GetPointerType())
17006c3fb27SDimitry Andric               ->Dereference(error);
17106c3fb27SDimitry Andric       if (!node_sp || error.Fail())
17206c3fb27SDimitry Andric           return nullptr;
17306c3fb27SDimitry Andric 
17406c3fb27SDimitry Andric       hash_sp = node_sp->GetChildMemberWithName("__hash_");
1755f757f3fSDimitry Andric       if (!hash_sp)
1760b57cec5SDimitry Andric         return nullptr;
1775f757f3fSDimitry Andric 
1785f757f3fSDimitry Andric       value_sp = node_sp->GetChildMemberWithName("__value_");
1795f757f3fSDimitry Andric       if (!value_sp) {
1805f757f3fSDimitry Andric         // clang-format off
1815f757f3fSDimitry Andric         // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
1825f757f3fSDimitry Andric         // anonymous union.
1835f757f3fSDimitry Andric         // Child 0: __hash_node_base base class
1845f757f3fSDimitry Andric         // Child 1: __hash_
1855f757f3fSDimitry Andric         // Child 2: anonymous union
1865f757f3fSDimitry Andric         // clang-format on
1875f757f3fSDimitry Andric         auto anon_union_sp = node_sp->GetChildAtIndex(2);
1885f757f3fSDimitry Andric         if (!anon_union_sp)
1895f757f3fSDimitry Andric           return nullptr;
1905f757f3fSDimitry Andric 
1915f757f3fSDimitry Andric         value_sp = anon_union_sp->GetChildMemberWithName("__value_");
1925f757f3fSDimitry Andric         if (!value_sp)
1935f757f3fSDimitry Andric           return nullptr;
1945f757f3fSDimitry Andric       }
1950b57cec5SDimitry Andric     }
1960b57cec5SDimitry Andric     m_elements_cache.push_back(
1970b57cec5SDimitry Andric         {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
19806c3fb27SDimitry Andric     m_next_element = node_sp->GetChildMemberWithName("__next_").get();
1990b57cec5SDimitry Andric     if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
2000b57cec5SDimitry Andric       m_next_element = nullptr;
2010b57cec5SDimitry Andric   }
2020b57cec5SDimitry Andric 
2030b57cec5SDimitry Andric   std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
2040b57cec5SDimitry Andric   if (!val_hash.first)
2050b57cec5SDimitry Andric     return lldb::ValueObjectSP();
2060b57cec5SDimitry Andric   StreamString stream;
2070b57cec5SDimitry Andric   stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
2080b57cec5SDimitry Andric   DataExtractor data;
2090b57cec5SDimitry Andric   Status error;
2100b57cec5SDimitry Andric   val_hash.first->GetData(data, error);
2110b57cec5SDimitry Andric   if (error.Fail())
2120b57cec5SDimitry Andric     return lldb::ValueObjectSP();
2130b57cec5SDimitry Andric   const bool thread_and_frame_only_if_stopped = true;
2140b57cec5SDimitry Andric   ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
2150b57cec5SDimitry Andric       thread_and_frame_only_if_stopped);
2160b57cec5SDimitry Andric   return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
217bdd1243dSDimitry Andric                                    m_element_type);
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric 
220*0fca6ea1SDimitry Andric lldb::ChildCacheState
221*0fca6ea1SDimitry Andric lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {
2224824e7fdSDimitry Andric   m_num_elements = 0;
2230b57cec5SDimitry Andric   m_next_element = nullptr;
2240b57cec5SDimitry Andric   m_elements_cache.clear();
22506c3fb27SDimitry Andric   ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");
2260b57cec5SDimitry Andric   if (!table_sp)
227*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
2280b57cec5SDimitry Andric 
22906c3fb27SDimitry Andric   ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_");
230*0fca6ea1SDimitry Andric   if (!p2_sp)
231*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
2320b57cec5SDimitry Andric 
233*0fca6ea1SDimitry Andric   ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);
2340b57cec5SDimitry Andric   if (!num_elements_sp)
235*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
2364824e7fdSDimitry Andric 
237*0fca6ea1SDimitry Andric   ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_");
238*0fca6ea1SDimitry Andric   if (!p1_sp)
239*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
240*0fca6ea1SDimitry Andric 
241*0fca6ea1SDimitry Andric   ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
242*0fca6ea1SDimitry Andric   if (!value_sp)
243*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
244*0fca6ea1SDimitry Andric 
245*0fca6ea1SDimitry Andric   m_tree = value_sp->GetChildMemberWithName("__next_").get();
2464824e7fdSDimitry Andric   if (m_tree == nullptr)
247*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
2484824e7fdSDimitry Andric 
2494824e7fdSDimitry Andric   m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
2504824e7fdSDimitry Andric 
2510b57cec5SDimitry Andric   if (m_num_elements > 0)
252*0fca6ea1SDimitry Andric     m_next_element = m_tree;
253*0fca6ea1SDimitry Andric 
254*0fca6ea1SDimitry Andric   return lldb::ChildCacheState::eRefetch;
2550b57cec5SDimitry Andric }
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
2580b57cec5SDimitry Andric     MightHaveChildren() {
2590b57cec5SDimitry Andric   return true;
2600b57cec5SDimitry Andric }
2610b57cec5SDimitry Andric 
2620b57cec5SDimitry Andric size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
2630b57cec5SDimitry Andric     GetIndexOfChildWithName(ConstString name) {
2640b57cec5SDimitry Andric   return ExtractIndexFromString(name.GetCString());
2650b57cec5SDimitry Andric }
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric SyntheticChildrenFrontEnd *
2680b57cec5SDimitry Andric lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
2690b57cec5SDimitry Andric     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
2700b57cec5SDimitry Andric   return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
2710b57cec5SDimitry Andric                     : nullptr);
2720b57cec5SDimitry Andric }
273*0fca6ea1SDimitry Andric 
274*0fca6ea1SDimitry Andric lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
275*0fca6ea1SDimitry Andric     LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
276*0fca6ea1SDimitry Andric     : SyntheticChildrenFrontEnd(*valobj_sp) {
277*0fca6ea1SDimitry Andric   if (valobj_sp)
278*0fca6ea1SDimitry Andric     Update();
279*0fca6ea1SDimitry Andric }
280*0fca6ea1SDimitry Andric 
281*0fca6ea1SDimitry Andric lldb::ChildCacheState lldb_private::formatters::
282*0fca6ea1SDimitry Andric     LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() {
283*0fca6ea1SDimitry Andric   m_pair_sp.reset();
284*0fca6ea1SDimitry Andric 
285*0fca6ea1SDimitry Andric   ValueObjectSP valobj_sp = m_backend.GetSP();
286*0fca6ea1SDimitry Andric   if (!valobj_sp)
287*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
288*0fca6ea1SDimitry Andric 
289*0fca6ea1SDimitry Andric   TargetSP target_sp(valobj_sp->GetTargetSP());
290*0fca6ea1SDimitry Andric 
291*0fca6ea1SDimitry Andric   if (!target_sp)
292*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
293*0fca6ea1SDimitry Andric 
294*0fca6ea1SDimitry Andric   // Get the unordered_map::iterator
295*0fca6ea1SDimitry Andric   // m_backend is an 'unordered_map::iterator', aka a
296*0fca6ea1SDimitry Andric   // '__hash_map_iterator<__hash_table::iterator>'
297*0fca6ea1SDimitry Andric   //
298*0fca6ea1SDimitry Andric   // __hash_map_iterator::__i_ is a __hash_table::iterator (aka
299*0fca6ea1SDimitry Andric   // __hash_iterator<__node_pointer>)
300*0fca6ea1SDimitry Andric   auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
301*0fca6ea1SDimitry Andric   if (!hash_iter_sp)
302*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
303*0fca6ea1SDimitry Andric 
304*0fca6ea1SDimitry Andric   // Type is '__hash_iterator<__node_pointer>'
305*0fca6ea1SDimitry Andric   auto hash_iter_type = hash_iter_sp->GetCompilerType();
306*0fca6ea1SDimitry Andric   if (!hash_iter_type.IsValid())
307*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
308*0fca6ea1SDimitry Andric 
309*0fca6ea1SDimitry Andric   // Type is '__node_pointer'
310*0fca6ea1SDimitry Andric   auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0);
311*0fca6ea1SDimitry Andric   if (!node_pointer_type.IsValid())
312*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
313*0fca6ea1SDimitry Andric 
314*0fca6ea1SDimitry Andric   // Cast the __hash_iterator to a __node_pointer (which stores our key/value
315*0fca6ea1SDimitry Andric   // pair)
316*0fca6ea1SDimitry Andric   auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type);
317*0fca6ea1SDimitry Andric   if (!hash_node_sp)
318*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
319*0fca6ea1SDimitry Andric 
320*0fca6ea1SDimitry Andric   auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_");
321*0fca6ea1SDimitry Andric   if (!key_value_sp) {
322*0fca6ea1SDimitry Andric     // clang-format off
323*0fca6ea1SDimitry Andric     // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
324*0fca6ea1SDimitry Andric     // anonymous union.
325*0fca6ea1SDimitry Andric     // Child 0: __hash_node_base base class
326*0fca6ea1SDimitry Andric     // Child 1: __hash_
327*0fca6ea1SDimitry Andric     // Child 2: anonymous union
328*0fca6ea1SDimitry Andric     // clang-format on
329*0fca6ea1SDimitry Andric     auto anon_union_sp = hash_node_sp->GetChildAtIndex(2);
330*0fca6ea1SDimitry Andric     if (!anon_union_sp)
331*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
332*0fca6ea1SDimitry Andric 
333*0fca6ea1SDimitry Andric     key_value_sp = anon_union_sp->GetChildMemberWithName("__value_");
334*0fca6ea1SDimitry Andric     if (!key_value_sp)
335*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
336*0fca6ea1SDimitry Andric   }
337*0fca6ea1SDimitry Andric 
338*0fca6ea1SDimitry Andric   // Create the synthetic child, which is a pair where the key and value can be
339*0fca6ea1SDimitry Andric   // retrieved by querying the synthetic frontend for
340*0fca6ea1SDimitry Andric   // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
341*0fca6ea1SDimitry Andric   // respectively.
342*0fca6ea1SDimitry Andric   //
343*0fca6ea1SDimitry Andric   // std::unordered_map stores the actual key/value pair in
344*0fca6ea1SDimitry Andric   // __hash_value_type::__cc_ (or previously __cc).
345*0fca6ea1SDimitry Andric   auto potential_child_sp = key_value_sp->Clone(ConstString("pair"));
346*0fca6ea1SDimitry Andric   if (potential_child_sp)
347*0fca6ea1SDimitry Andric     if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1)
348*0fca6ea1SDimitry Andric       if (auto child0_sp = potential_child_sp->GetChildAtIndex(0);
349*0fca6ea1SDimitry Andric           child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")
350*0fca6ea1SDimitry Andric         potential_child_sp = child0_sp->Clone(ConstString("pair"));
351*0fca6ea1SDimitry Andric 
352*0fca6ea1SDimitry Andric   m_pair_sp = potential_child_sp;
353*0fca6ea1SDimitry Andric 
354*0fca6ea1SDimitry Andric   return lldb::ChildCacheState::eRefetch;
355*0fca6ea1SDimitry Andric }
356*0fca6ea1SDimitry Andric 
357*0fca6ea1SDimitry Andric llvm::Expected<uint32_t> lldb_private::formatters::
358*0fca6ea1SDimitry Andric     LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
359*0fca6ea1SDimitry Andric   return 2;
360*0fca6ea1SDimitry Andric }
361*0fca6ea1SDimitry Andric 
362*0fca6ea1SDimitry Andric lldb::ValueObjectSP lldb_private::formatters::
363*0fca6ea1SDimitry Andric     LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
364*0fca6ea1SDimitry Andric   if (m_pair_sp)
365*0fca6ea1SDimitry Andric     return m_pair_sp->GetChildAtIndex(idx);
366*0fca6ea1SDimitry Andric   return lldb::ValueObjectSP();
367*0fca6ea1SDimitry Andric }
368*0fca6ea1SDimitry Andric 
369*0fca6ea1SDimitry Andric bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
370*0fca6ea1SDimitry Andric     MightHaveChildren() {
371*0fca6ea1SDimitry Andric   return true;
372*0fca6ea1SDimitry Andric }
373*0fca6ea1SDimitry Andric 
374*0fca6ea1SDimitry Andric size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
375*0fca6ea1SDimitry Andric     GetIndexOfChildWithName(ConstString name) {
376*0fca6ea1SDimitry Andric   if (name == "first")
377*0fca6ea1SDimitry Andric     return 0;
378*0fca6ea1SDimitry Andric   if (name == "second")
379*0fca6ea1SDimitry Andric     return 1;
380*0fca6ea1SDimitry Andric   return UINT32_MAX;
381*0fca6ea1SDimitry Andric }
382*0fca6ea1SDimitry Andric 
383*0fca6ea1SDimitry Andric SyntheticChildrenFrontEnd *
384*0fca6ea1SDimitry Andric lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(
385*0fca6ea1SDimitry Andric     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
386*0fca6ea1SDimitry Andric   return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp)
387*0fca6ea1SDimitry Andric                     : nullptr);
388*0fca6ea1SDimitry Andric }
389