Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- LibCxxUnorderedMap.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 "LibCxx.h"
10
11
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12
#include "lldb/Core/ValueObject.h"
13
#include "lldb/Core/ValueObjectConstResult.h"
14
#include "lldb/DataFormatters/FormattersHelpers.h"
15
#include "lldb/Target/Target.h"
16
#include "lldb/Utility/ConstString.h"
17
#include "lldb/Utility/DataBufferHeap.h"
18
#include "lldb/Utility/Endian.h"
19
#include "lldb/Utility/Status.h"
20
#include "lldb/Utility/Stream.h"
21
#include "llvm/ADT/StringRef.h"
22
23
using namespace lldb;
24
using namespace lldb_private;
25
using namespace lldb_private::formatters;
26
27
namespace lldb_private {
28
namespace formatters {
29
class LibcxxStdUnorderedMapSyntheticFrontEnd
30
    : public SyntheticChildrenFrontEnd {
31
public:
32
  LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
33
34
14
  ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
35
36
  size_t CalculateNumChildren() override;
37
38
  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
39
40
  bool Update() override;
41
42
  bool MightHaveChildren() override;
43
44
  size_t GetIndexOfChildWithName(ConstString name) override;
45
46
private:
47
  CompilerType m_element_type;
48
  CompilerType m_node_type;
49
  ValueObject *m_tree = nullptr;
50
  size_t m_num_elements = 0;
51
  ValueObject *m_next_element = nullptr;
52
  std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
53
};
54
} // namespace formatters
55
} // namespace lldb_private
56
57
lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
58
    LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
59
14
    : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
60
14
      m_elements_cache() {
61
14
  if (valobj_sp)
62
14
    Update();
63
14
}
64
65
size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
66
78
    CalculateNumChildren() {
67
78
  return m_num_elements;
68
78
}
69
70
4
static void consumeInlineNamespace(llvm::StringRef &name) {
71
  // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
72
4
  auto scratch = name;
73
4
  if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
74
8
    scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
75
4
    if (scratch.consume_front("::")) {
76
      // Successfully consumed a namespace.
77
4
      name = scratch;
78
4
    }
79
4
  }
80
4
}
81
82
26
static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
83
26
  llvm::StringRef name = type_name.GetStringRef();
84
  // The type name may be prefixed with `std::__<inline-namespace>::`.
85
26
  if (name.consume_front("std::"))
86
4
    consumeInlineNamespace(name);
87
26
  return name.consume_front(type) && 
name.startswith("<")8
;
88
26
}
89
90
12
static bool isUnorderedMap(ConstString type_name) {
91
12
  return isStdTemplate(type_name, "unordered_map") ||
92
12
         
isStdTemplate(type_name, "unordered_multimap")10
;
93
12
}
94
95
lldb::ValueObjectSP lldb_private::formatters::
96
64
    LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
97
64
  if (idx >= CalculateNumChildren())
98
0
    return lldb::ValueObjectSP();
99
64
  if (m_tree == nullptr)
100
0
    return lldb::ValueObjectSP();
101
102
128
  
while (64
idx >= m_elements_cache.size()) {
103
64
    if (m_next_element == nullptr)
104
0
      return lldb::ValueObjectSP();
105
106
64
    Status error;
107
64
    ValueObjectSP node_sp = m_next_element->Dereference(error);
108
64
    if (!node_sp || error.Fail())
109
0
      return lldb::ValueObjectSP();
110
111
64
    ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_");
112
64
    ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");
113
64
    if (!hash_sp || 
!value_sp0
) {
114
64
      if (!m_element_type) {
115
12
        auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
116
12
        if (!p1_sp)
117
0
          return nullptr;
118
119
12
        ValueObjectSP first_sp = nullptr;
120
12
        switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
121
0
        case 1:
122
          // Assume a pre llvm r300140 __compressed_pair implementation:
123
0
          first_sp = p1_sp->GetChildMemberWithName("__first_");
124
0
          break;
125
12
        case 2: {
126
          // Assume a post llvm r300140 __compressed_pair implementation:
127
12
          ValueObjectSP first_elem_parent_sp =
128
12
            p1_sp->GetChildAtIndex(0);
129
12
          first_sp = p1_sp->GetChildMemberWithName("__value_");
130
12
          break;
131
0
        }
132
0
        default:
133
0
          return nullptr;
134
12
        }
135
136
12
        if (!first_sp)
137
0
          return nullptr;
138
12
        m_element_type = first_sp->GetCompilerType();
139
12
        m_element_type = m_element_type.GetTypeTemplateArgument(0);
140
12
        m_element_type = m_element_type.GetPointeeType();
141
12
        m_node_type = m_element_type;
142
12
        m_element_type = m_element_type.GetTypeTemplateArgument(0);
143
        // This synthetic provider is used for both unordered_(multi)map and
144
        // unordered_(multi)set. For unordered_map, the element type has an
145
        // additional type layer, an internal struct (`__hash_value_type`)
146
        // that wraps a std::pair. Peel away the internal wrapper type - whose
147
        // structure is of no value to users, to expose the std::pair. This
148
        // matches the structure returned by the std::map synthetic provider.
149
12
        if (isUnorderedMap(m_backend.GetTypeName())) {
150
4
          std::string name;
151
4
          CompilerType field_type = m_element_type.GetFieldAtIndex(
152
4
              0, name, nullptr, nullptr, nullptr);
153
4
          CompilerType actual_type = field_type.GetTypedefedType();
154
4
          if (isStdTemplate(actual_type.GetTypeName(), "pair"))
155
4
            m_element_type = actual_type;
156
4
        }
157
12
      }
158
64
      if (!m_node_type)
159
0
        return nullptr;
160
64
      node_sp = m_next_element->Cast(m_node_type.GetPointerType())
161
64
              ->Dereference(error);
162
64
      if (!node_sp || error.Fail())
163
0
          return nullptr;
164
165
64
      value_sp = node_sp->GetChildMemberWithName("__value_");
166
64
      hash_sp = node_sp->GetChildMemberWithName("__hash_");
167
64
      if (!value_sp || !hash_sp)
168
0
        return nullptr;
169
64
    }
170
64
    m_elements_cache.push_back(
171
64
        {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
172
64
    m_next_element = node_sp->GetChildMemberWithName("__next_").get();
173
64
    if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
174
12
      m_next_element = nullptr;
175
64
  }
176
177
64
  std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
178
64
  if (!val_hash.first)
179
0
    return lldb::ValueObjectSP();
180
64
  StreamString stream;
181
64
  stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
182
64
  DataExtractor data;
183
64
  Status error;
184
64
  val_hash.first->GetData(data, error);
185
64
  if (error.Fail())
186
0
    return lldb::ValueObjectSP();
187
64
  const bool thread_and_frame_only_if_stopped = true;
188
64
  ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
189
64
      thread_and_frame_only_if_stopped);
190
64
  return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
191
64
                                   m_element_type);
192
64
}
193
194
bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
195
28
    Update() {
196
28
  m_num_elements = 0;
197
28
  m_next_element = nullptr;
198
28
  m_elements_cache.clear();
199
28
  ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");
200
28
  if (!table_sp)
201
0
    return false;
202
203
28
  ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_");
204
28
  ValueObjectSP num_elements_sp = nullptr;
205
28
  llvm::SmallVector<llvm::StringRef, 3> next_path;
206
28
  switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
207
0
  case 1:
208
    // Assume a pre llvm r300140 __compressed_pair implementation:
209
0
    num_elements_sp = p2_sp->GetChildMemberWithName("__first_");
210
0
    next_path.append({"__p1_", "__first_", "__next_"});
211
0
    break;
212
28
  case 2: {
213
    // Assume a post llvm r300140 __compressed_pair implementation:
214
28
    ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0);
215
28
    num_elements_sp = first_elem_parent->GetChildMemberWithName("__value_");
216
28
    next_path.append({"__p1_", "__value_", "__next_"});
217
28
    break;
218
0
  }
219
0
  default:
220
0
    return false;
221
28
  }
222
223
28
  if (!num_elements_sp)
224
0
    return false;
225
226
28
  m_tree = table_sp->GetChildAtNamePath(next_path).get();
227
28
  if (m_tree == nullptr)
228
0
    return false;
229
230
28
  m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
231
232
28
  if (m_num_elements > 0)
233
24
    m_next_element =
234
24
        table_sp->GetChildAtNamePath(next_path).get();
235
28
  return false;
236
28
}
237
238
bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
239
2
    MightHaveChildren() {
240
2
  return true;
241
2
}
242
243
size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
244
0
    GetIndexOfChildWithName(ConstString name) {
245
0
  return ExtractIndexFromString(name.GetCString());
246
0
}
247
248
SyntheticChildrenFrontEnd *
249
lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
250
14
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
251
14
  return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
252
14
                    : 
nullptr0
);
253
14
}