Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- NSString.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 "NSString.h"
10
11
#include "lldb/Core/ValueObject.h"
12
#include "lldb/Core/ValueObjectConstResult.h"
13
#include "lldb/DataFormatters/FormattersHelpers.h"
14
#include "lldb/DataFormatters/StringPrinter.h"
15
#include "lldb/Target/Language.h"
16
#include "lldb/Target/Target.h"
17
#include "lldb/Utility/ConstString.h"
18
#include "lldb/Utility/DataBufferHeap.h"
19
#include "lldb/Utility/Endian.h"
20
#include "lldb/Utility/Status.h"
21
#include "lldb/Utility/Stream.h"
22
23
using namespace lldb;
24
using namespace lldb_private;
25
using namespace lldb_private::formatters;
26
27
std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
28
923
NSString_Additionals::GetAdditionalSummaries() {
29
923
  static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
30
923
  return g_map;
31
923
}
32
33
bool lldb_private::formatters::NSStringSummaryProvider(
34
    ValueObject &valobj, Stream &stream,
35
1.10k
    const TypeSummaryOptions &summary_options) {
36
1.10k
  static constexpr llvm::StringLiteral g_TypeHint("NSString");
37
38
1.10k
  ProcessSP process_sp = valobj.GetProcessSP();
39
1.10k
  if (!process_sp)
40
0
    return false;
41
42
1.10k
  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
43
44
1.10k
  if (!runtime)
45
0
    return false;
46
47
1.10k
  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
48
1.10k
      runtime->GetClassDescriptor(valobj));
49
50
1.10k
  if (!descriptor.get() || 
!descriptor->IsValid()1.09k
)
51
3
    return false;
52
53
1.09k
  uint32_t ptr_size = process_sp->GetAddressByteSize();
54
55
1.09k
  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
56
57
1.09k
  if (!valobj_addr)
58
150
    return false;
59
60
947
  ConstString class_name_cs = descriptor->GetClassName();
61
947
  llvm::StringRef class_name = class_name_cs.GetStringRef();
62
63
947
  if (class_name.empty())
64
0
    return false;
65
66
947
  bool is_tagged_ptr = class_name == "NSTaggedPointerString" &&
67
947
                       
descriptor->GetTaggedPointerInfo()24
;
68
  // for a tagged pointer, the descriptor has everything we need
69
947
  if (is_tagged_ptr)
70
24
    return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
71
24
                                          summary_options);
72
73
923
  auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
74
923
  auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
75
923
  if (iter != end)
76
0
    return iter->second(valobj, stream, summary_options);
77
78
  // if not a tagged pointer that we know about, try the normal route
79
923
  uint64_t info_bits_location = valobj_addr + ptr_size;
80
923
  if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
81
0
    info_bits_location += 3;
82
83
923
  Status error;
84
85
923
  uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
86
923
      info_bits_location, 1, 0, error);
87
923
  if (error.Fail())
88
0
    return false;
89
90
923
  bool is_mutable = (info_bits & 1) == 1;
91
923
  bool is_inline = (info_bits & 0x60) == 0;
92
923
  bool has_explicit_length = (info_bits & (1 | 4)) != 4;
93
923
  bool is_unicode = (info_bits & 0x10) == 0x10;
94
923
  bool is_path_store = class_name == "NSPathStore2";
95
923
  bool has_null = (info_bits & 8) == 8;
96
97
923
  size_t explicit_length = 0;
98
923
  if (!has_null && 
has_explicit_length167
&&
!is_path_store167
) {
99
117
    lldb::addr_t explicit_length_offset = 2 * ptr_size;
100
117
    if (is_mutable && 
!is_inline0
)
101
0
      explicit_length_offset =
102
0
          explicit_length_offset + ptr_size; //  notInlineMutable.length;
103
117
    else if (is_inline)
104
59
      explicit_length = explicit_length + 0; // inline1.length;
105
58
    else if (!is_inline && !is_mutable)
106
58
      explicit_length_offset =
107
58
          explicit_length_offset + ptr_size; // notInlineImmutable1.length;
108
0
    else
109
0
      explicit_length_offset = 0;
110
111
117
    if (explicit_length_offset) {
112
117
      explicit_length_offset = valobj_addr + explicit_length_offset;
113
117
      explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
114
117
          explicit_length_offset, 4, 0, error);
115
117
    }
116
117
  }
117
118
923
  const llvm::StringSet<> supported_string_classes = {
119
923
      "NSString",     "CFMutableStringRef",
120
923
      "CFStringRef",  "__NSCFConstantString",
121
923
      "__NSCFString", "NSCFConstantString",
122
923
      "NSCFString",   "NSPathStore2"};
123
923
  if (supported_string_classes.count(class_name) == 0) {
124
    // not one of us - but tell me class name
125
0
    stream.Printf("class name = %s", class_name_cs.GetCString());
126
0
    return true;
127
0
  }
128
129
923
  llvm::StringRef prefix, suffix;
130
923
  if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
131
884
    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
132
133
923
  StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
134
923
  options.SetPrefixToken(prefix.str());
135
923
  options.SetSuffixToken(suffix.str());
136
137
923
  if (is_mutable) {
138
17
    uint64_t location = 2 * ptr_size + valobj_addr;
139
17
    location = process_sp->ReadPointerFromMemory(location, error);
140
17
    if (error.Fail())
141
0
      return false;
142
17
    if (has_explicit_length && is_unicode) {
143
0
      options.SetLocation(location);
144
0
      options.SetTargetSP(valobj.GetTargetSP());
145
0
      options.SetStream(&stream);
146
0
      options.SetQuote('"');
147
0
      options.SetSourceSize(explicit_length);
148
0
      options.SetHasSourceSize(has_explicit_length);
149
0
      options.SetNeedsZeroTermination(false);
150
0
      options.SetIgnoreMaxLength(summary_options.GetCapping() ==
151
0
                                 TypeSummaryCapping::eTypeSummaryUncapped);
152
0
      options.SetBinaryZeroIsTerminator(false);
153
0
      return StringPrinter::ReadStringAndDumpToStream<
154
0
          StringPrinter::StringElementType::UTF16>(options);
155
17
    } else {
156
17
      options.SetLocation(location + 1);
157
17
      options.SetTargetSP(valobj.GetTargetSP());
158
17
      options.SetStream(&stream);
159
17
      options.SetSourceSize(explicit_length);
160
17
      options.SetHasSourceSize(has_explicit_length);
161
17
      options.SetNeedsZeroTermination(false);
162
17
      options.SetIgnoreMaxLength(summary_options.GetCapping() ==
163
17
                                 TypeSummaryCapping::eTypeSummaryUncapped);
164
17
      options.SetBinaryZeroIsTerminator(false);
165
17
      return StringPrinter::ReadStringAndDumpToStream<
166
17
          StringPrinter::StringElementType::ASCII>(options);
167
17
    }
168
906
  } else if (is_inline && 
has_explicit_length266
&&
!is_unicode114
&&
169
906
             
!is_path_store55
&&
!is_mutable5
) {
170
5
    uint64_t location = 3 * ptr_size + valobj_addr;
171
172
5
    options.SetLocation(location);
173
5
    options.SetTargetSP(valobj.GetTargetSP());
174
5
    options.SetStream(&stream);
175
5
    options.SetQuote('"');
176
5
    options.SetSourceSize(explicit_length);
177
5
    options.SetHasSourceSize(has_explicit_length);
178
5
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
179
5
                               TypeSummaryCapping::eTypeSummaryUncapped);
180
5
    return StringPrinter::ReadStringAndDumpToStream<
181
5
        StringPrinter::StringElementType::ASCII>(options);
182
901
  } else if (is_unicode) {
183
96
    uint64_t location = valobj_addr + 2 * ptr_size;
184
96
    if (is_inline) {
185
59
      if (!has_explicit_length) {
186
0
        return false;
187
0
      } else
188
59
        location += ptr_size;
189
59
    } else {
190
37
      location = process_sp->ReadPointerFromMemory(location, error);
191
37
      if (error.Fail())
192
0
        return false;
193
37
    }
194
96
    options.SetLocation(location);
195
96
    options.SetTargetSP(valobj.GetTargetSP());
196
96
    options.SetStream(&stream);
197
96
    options.SetQuote('"');
198
96
    options.SetSourceSize(explicit_length);
199
96
    options.SetHasSourceSize(has_explicit_length);
200
96
    options.SetNeedsZeroTermination(!has_explicit_length);
201
96
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
202
96
                               TypeSummaryCapping::eTypeSummaryUncapped);
203
96
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
204
96
    return StringPrinter::ReadStringAndDumpToStream<
205
96
        StringPrinter::StringElementType::UTF16>(options);
206
805
  } else if (is_path_store) {
207
    // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
208
50
    uint64_t length_ivar_offset = 1 * ptr_size;
209
50
    CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
210
50
        lldb::eBasicTypeUnsignedInt);
211
50
    ValueObjectSP length_valobj_sp =
212
50
        valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
213
50
                                         ConstString("_lengthAndRefCount"));
214
50
    if (!length_valobj_sp)
215
0
      return false;
216
    // Get the length out of _lengthAndRefCount.
217
50
    explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
218
50
    lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
219
220
50
    options.SetLocation(location);
221
50
    options.SetTargetSP(valobj.GetTargetSP());
222
50
    options.SetStream(&stream);
223
50
    options.SetQuote('"');
224
50
    options.SetSourceSize(explicit_length);
225
50
    options.SetHasSourceSize(has_explicit_length);
226
50
    options.SetNeedsZeroTermination(!has_explicit_length);
227
50
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
228
50
                               TypeSummaryCapping::eTypeSummaryUncapped);
229
50
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
230
50
    return StringPrinter::ReadStringAndDumpToStream<
231
50
        StringPrinter::StringElementType::UTF16>(options);
232
755
  } else if (is_inline) {
233
152
    uint64_t location = valobj_addr + 2 * ptr_size;
234
152
    if (!has_explicit_length) {
235
      // in this kind of string, the byte before the string content is a length
236
      // byte so let's try and use it to handle the embedded NUL case
237
152
      Status error;
238
152
      explicit_length =
239
152
          process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
240
152
      has_explicit_length = !(error.Fail() || explicit_length == 0);
241
152
      location++;
242
152
    }
243
152
    options.SetLocation(location);
244
152
    options.SetTargetSP(valobj.GetTargetSP());
245
152
    options.SetStream(&stream);
246
152
    options.SetSourceSize(explicit_length);
247
152
    options.SetHasSourceSize(has_explicit_length);
248
152
    options.SetNeedsZeroTermination(!has_explicit_length);
249
152
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
250
152
                               TypeSummaryCapping::eTypeSummaryUncapped);
251
152
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
252
152
    if (has_explicit_length)
253
152
      return StringPrinter::ReadStringAndDumpToStream<
254
152
          StringPrinter::StringElementType::UTF8>(options);
255
0
    else
256
0
      return StringPrinter::ReadStringAndDumpToStream<
257
0
          StringPrinter::StringElementType::ASCII>(options);
258
603
  } else {
259
603
    uint64_t location = valobj_addr + 2 * ptr_size;
260
603
    location = process_sp->ReadPointerFromMemory(location, error);
261
603
    if (error.Fail())
262
0
      return false;
263
603
    if (has_explicit_length && !has_null)
264
21
      explicit_length++; // account for the fact that there is no NULL and we
265
                         // need to have one added
266
603
    options.SetLocation(location);
267
603
    options.SetTargetSP(valobj.GetTargetSP());
268
603
    options.SetStream(&stream);
269
603
    options.SetSourceSize(explicit_length);
270
603
    options.SetHasSourceSize(has_explicit_length);
271
603
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
272
603
                               TypeSummaryCapping::eTypeSummaryUncapped);
273
603
    return StringPrinter::ReadStringAndDumpToStream<
274
603
        StringPrinter::StringElementType::ASCII>(options);
275
603
  }
276
923
}
277
278
bool lldb_private::formatters::NSAttributedStringSummaryProvider(
279
10
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
280
10
  TargetSP target_sp(valobj.GetTargetSP());
281
10
  if (!target_sp)
282
0
    return false;
283
10
  uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
284
10
  uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
285
10
  if (!pointer_value)
286
0
    return false;
287
10
  pointer_value += addr_size;
288
10
  CompilerType type(valobj.GetCompilerType());
289
10
  ExecutionContext exe_ctx(target_sp, false);
290
10
  ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
291
10
      "string_ptr", pointer_value, exe_ctx, type));
292
10
  if (!child_ptr_sp)
293
0
    return false;
294
10
  DataExtractor data;
295
10
  Status error;
296
10
  child_ptr_sp->GetData(data, error);
297
10
  if (error.Fail())
298
0
    return false;
299
10
  ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
300
10
      "string_data", data, exe_ctx, type));
301
10
  child_sp->GetValueAsUnsigned(0);
302
10
  if (child_sp)
303
10
    return NSStringSummaryProvider(*child_sp, stream, options);
304
0
  return false;
305
10
}
306
307
bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
308
0
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
309
0
  return NSAttributedStringSummaryProvider(valobj, stream, options);
310
0
}
311
312
bool lldb_private::formatters::NSTaggedString_SummaryProvider(
313
    ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
314
24
    Stream &stream, const TypeSummaryOptions &summary_options) {
315
24
  static constexpr llvm::StringLiteral g_TypeHint("NSString");
316
317
24
  if (!descriptor)
318
0
    return false;
319
24
  uint64_t len_bits = 0, data_bits = 0;
320
24
  if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
321
0
    return false;
322
323
24
  static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
324
24
  static const int g_SixbitMaxLen = 9;
325
24
  static const int g_fiveBitMaxLen = 11;
326
327
24
  static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
328
24
                                          "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
329
330
24
  if (len_bits > g_fiveBitMaxLen)
331
0
    return false;
332
333
24
  llvm::StringRef prefix, suffix;
334
24
  if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))
335
24
    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
336
337
  // this is a fairly ugly trick - pretend that the numeric value is actually a
338
  // char* this works under a few assumptions: little endian architecture
339
  // sizeof(uint64_t) > g_MaxNonBitmaskedLen
340
24
  if (len_bits <= g_MaxNonBitmaskedLen) {
341
22
    stream << prefix;
342
22
    stream.Printf("\"%s\"", (const char *)&data_bits);
343
22
    stream << suffix;
344
22
    return true;
345
22
  }
346
347
  // if the data is bitmasked, we need to actually process the bytes
348
2
  uint8_t bitmask = 0;
349
2
  uint8_t shift_offset = 0;
350
351
2
  if (len_bits <= g_SixbitMaxLen) {
352
2
    bitmask = 0x03f;
353
2
    shift_offset = 6;
354
2
  } else {
355
0
    bitmask = 0x01f;
356
0
    shift_offset = 5;
357
0
  }
358
359
2
  std::vector<uint8_t> bytes;
360
2
  bytes.resize(len_bits);
361
18
  for (; len_bits > 0; 
data_bits >>= shift_offset, --len_bits16
) {
362
16
    uint8_t packed = data_bits & bitmask;
363
16
    bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
364
16
  }
365
366
2
  stream << prefix;
367
2
  stream.Printf("\"%s\"", &bytes[0]);
368
2
  stream << suffix;
369
2
  return true;
370
24
}