Coverage Report

Created: 2023-03-28 09:12

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