Coverage Report

Created: 2022-01-18 06:27

/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
1.26k
NSString_Additionals::GetAdditionalSummaries() {
29
1.26k
  static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
30
1.26k
  return g_map;
31
1.26k
}
32
33
bool lldb_private::formatters::NSStringSummaryProvider(
34
    ValueObject &valobj, Stream &stream,
35
1.52k
    const TypeSummaryOptions &summary_options) {
36
1.52k
  static ConstString g_TypeHint("NSString");
37
38
1.52k
  ProcessSP process_sp = valobj.GetProcessSP();
39
1.52k
  if (!process_sp)
40
0
    return false;
41
42
1.52k
  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
43
44
1.52k
  if (!runtime)
45
0
    return false;
46
47
1.52k
  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
48
1.52k
      runtime->GetClassDescriptor(valobj));
49
50
1.52k
  if (!descriptor.get() || 
!descriptor->IsValid()1.52k
)
51
3
    return false;
52
53
1.52k
  uint32_t ptr_size = process_sp->GetAddressByteSize();
54
55
1.52k
  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
56
57
1.52k
  if (!valobj_addr)
58
225
    return false;
59
60
1.29k
  ConstString class_name_cs = descriptor->GetClassName();
61
1.29k
  llvm::StringRef class_name = class_name_cs.GetStringRef();
62
63
1.29k
  if (class_name.empty())
64
0
    return false;
65
66
1.29k
  bool is_tagged_ptr = class_name == "NSTaggedPointerString" &&
67
1.29k
                       
descriptor->GetTaggedPointerInfo()33
;
68
  // for a tagged pointer, the descriptor has everything we need
69
1.29k
  if (is_tagged_ptr)
70
33
    return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
71
33
                                          summary_options);
72
73
1.26k
  auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
74
1.26k
  auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
75
1.26k
  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
1.26k
  uint64_t info_bits_location = valobj_addr + ptr_size;
80
1.26k
  if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
81
0
    info_bits_location += 3;
82
83
1.26k
  Status error;
84
85
1.26k
  uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
86
1.26k
      info_bits_location, 1, 0, error);
87
1.26k
  if (error.Fail())
88
0
    return false;
89
90
1.26k
  bool is_mutable = (info_bits & 1) == 1;
91
1.26k
  bool is_inline = (info_bits & 0x60) == 0;
92
1.26k
  bool has_explicit_length = (info_bits & (1 | 4)) != 4;
93
1.26k
  bool is_unicode = (info_bits & 0x10) == 0x10;
94
1.26k
  bool is_path_store = class_name == "NSPathStore2";
95
1.26k
  bool has_null = (info_bits & 8) == 8;
96
97
1.26k
  size_t explicit_length = 0;
98
1.26k
  if (!has_null && 
has_explicit_length243
&&
!is_path_store243
) {
99
171
    lldb::addr_t explicit_length_offset = 2 * ptr_size;
100
171
    if (is_mutable && 
!is_inline0
)
101
0
      explicit_length_offset =
102
0
          explicit_length_offset + ptr_size; //  notInlineMutable.length;
103
171
    else if (is_inline)
104
87
      explicit_length = explicit_length + 0; // inline1.length;
105
84
    else if (!is_inline && !is_mutable)
106
84
      explicit_length_offset =
107
84
          explicit_length_offset + ptr_size; // notInlineImmutable1.length;
108
0
    else
109
0
      explicit_length_offset = 0;
110
111
171
    if (explicit_length_offset) {
112
171
      explicit_length_offset = valobj_addr + explicit_length_offset;
113
171
      explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
114
171
          explicit_length_offset, 4, 0, error);
115
171
    }
116
171
  }
117
118
1.26k
  const llvm::StringSet<> supported_string_classes = {
119
1.26k
      "NSString",     "CFMutableStringRef",
120
1.26k
      "CFStringRef",  "__NSCFConstantString",
121
1.26k
      "__NSCFString", "NSCFConstantString",
122
1.26k
      "NSCFString",   "NSPathStore2"};
123
1.26k
  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
1.26k
  std::string prefix, suffix;
130
1.26k
  if (Language *language =
131
1.26k
          Language::FindPlugin(summary_options.GetLanguage())) {
132
1.23k
    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
133
1.23k
                                            suffix)) {
134
44
      prefix.clear();
135
44
      suffix.clear();
136
44
    }
137
1.23k
  }
138
139
1.26k
  StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
140
1.26k
  options.SetPrefixToken(prefix);
141
1.26k
  options.SetSuffixToken(suffix);
142
143
1.26k
  if (is_mutable) {
144
25
    uint64_t location = 2 * ptr_size + valobj_addr;
145
25
    location = process_sp->ReadPointerFromMemory(location, error);
146
25
    if (error.Fail())
147
0
      return false;
148
25
    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
25
    } else {
162
25
      options.SetLocation(location + 1);
163
25
      options.SetTargetSP(valobj.GetTargetSP());
164
25
      options.SetStream(&stream);
165
25
      options.SetSourceSize(explicit_length);
166
25
      options.SetHasSourceSize(has_explicit_length);
167
25
      options.SetNeedsZeroTermination(false);
168
25
      options.SetIgnoreMaxLength(summary_options.GetCapping() ==
169
25
                                 TypeSummaryCapping::eTypeSummaryUncapped);
170
25
      options.SetBinaryZeroIsTerminator(false);
171
25
      return StringPrinter::ReadStringAndDumpToStream<
172
25
          StringPrinter::StringElementType::ASCII>(options);
173
25
    }
174
1.24k
  } else if (is_inline && 
has_explicit_length387
&&
!is_unicode166
&&
175
1.24k
             
!is_path_store79
&&
!is_mutable7
) {
176
7
    uint64_t location = 3 * ptr_size + valobj_addr;
177
178
7
    options.SetLocation(location);
179
7
    options.SetTargetSP(valobj.GetTargetSP());
180
7
    options.SetStream(&stream);
181
7
    options.SetQuote('"');
182
7
    options.SetSourceSize(explicit_length);
183
7
    options.SetHasSourceSize(has_explicit_length);
184
7
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
185
7
                               TypeSummaryCapping::eTypeSummaryUncapped);
186
7
    return StringPrinter::ReadStringAndDumpToStream<
187
7
        StringPrinter::StringElementType::ASCII>(options);
188
1.23k
  } else if (is_unicode) {
189
140
    uint64_t location = valobj_addr + 2 * ptr_size;
190
140
    if (is_inline) {
191
87
      if (!has_explicit_length) {
192
0
        return false;
193
0
      } else
194
87
        location += ptr_size;
195
87
    } else {
196
53
      location = process_sp->ReadPointerFromMemory(location, error);
197
53
      if (error.Fail())
198
0
        return false;
199
53
    }
200
140
    options.SetLocation(location);
201
140
    options.SetTargetSP(valobj.GetTargetSP());
202
140
    options.SetStream(&stream);
203
140
    options.SetQuote('"');
204
140
    options.SetSourceSize(explicit_length);
205
140
    options.SetHasSourceSize(has_explicit_length);
206
140
    options.SetNeedsZeroTermination(!has_explicit_length);
207
140
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
208
140
                               TypeSummaryCapping::eTypeSummaryUncapped);
209
140
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
210
140
    return StringPrinter::ReadStringAndDumpToStream<
211
140
        StringPrinter::StringElementType::UTF16>(options);
212
1.09k
  } else if (is_path_store) {
213
    // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
214
72
    uint64_t length_ivar_offset = 1 * ptr_size;
215
72
    CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
216
72
        lldb::eBasicTypeUnsignedInt);
217
72
    ValueObjectSP length_valobj_sp =
218
72
        valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
219
72
                                         ConstString("_lengthAndRefCount"));
220
72
    if (!length_valobj_sp)
221
0
      return false;
222
    // Get the length out of _lengthAndRefCount.
223
72
    explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
224
72
    lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
225
226
72
    options.SetLocation(location);
227
72
    options.SetTargetSP(valobj.GetTargetSP());
228
72
    options.SetStream(&stream);
229
72
    options.SetQuote('"');
230
72
    options.SetSourceSize(explicit_length);
231
72
    options.SetHasSourceSize(has_explicit_length);
232
72
    options.SetNeedsZeroTermination(!has_explicit_length);
233
72
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
234
72
                               TypeSummaryCapping::eTypeSummaryUncapped);
235
72
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
236
72
    return StringPrinter::ReadStringAndDumpToStream<
237
72
        StringPrinter::StringElementType::UTF16>(options);
238
1.02k
  } else if (is_inline) {
239
221
    uint64_t location = valobj_addr + 2 * ptr_size;
240
221
    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
221
      Status error;
244
221
      explicit_length =
245
221
          process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
246
221
      has_explicit_length = !(error.Fail() || explicit_length == 0);
247
221
      location++;
248
221
    }
249
221
    options.SetLocation(location);
250
221
    options.SetTargetSP(valobj.GetTargetSP());
251
221
    options.SetStream(&stream);
252
221
    options.SetSourceSize(explicit_length);
253
221
    options.SetHasSourceSize(has_explicit_length);
254
221
    options.SetNeedsZeroTermination(!has_explicit_length);
255
221
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
256
221
                               TypeSummaryCapping::eTypeSummaryUncapped);
257
221
    options.SetBinaryZeroIsTerminator(!has_explicit_length);
258
221
    if (has_explicit_length)
259
221
      return StringPrinter::ReadStringAndDumpToStream<
260
221
          StringPrinter::StringElementType::UTF8>(options);
261
0
    else
262
0
      return StringPrinter::ReadStringAndDumpToStream<
263
0
          StringPrinter::StringElementType::ASCII>(options);
264
800
  } else {
265
800
    uint64_t location = valobj_addr + 2 * ptr_size;
266
800
    location = process_sp->ReadPointerFromMemory(location, error);
267
800
    if (error.Fail())
268
0
      return false;
269
800
    if (has_explicit_length && !has_null)
270
31
      explicit_length++; // account for the fact that there is no NULL and we
271
                         // need to have one added
272
800
    options.SetLocation(location);
273
800
    options.SetTargetSP(valobj.GetTargetSP());
274
800
    options.SetStream(&stream);
275
800
    options.SetSourceSize(explicit_length);
276
800
    options.SetHasSourceSize(has_explicit_length);
277
800
    options.SetIgnoreMaxLength(summary_options.GetCapping() ==
278
800
                               TypeSummaryCapping::eTypeSummaryUncapped);
279
800
    return StringPrinter::ReadStringAndDumpToStream<
280
800
        StringPrinter::StringElementType::ASCII>(options);
281
800
  }
282
1.26k
}
283
284
bool lldb_private::formatters::NSAttributedStringSummaryProvider(
285
14
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
286
14
  TargetSP target_sp(valobj.GetTargetSP());
287
14
  if (!target_sp)
288
0
    return false;
289
14
  uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
290
14
  uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
291
14
  if (!pointer_value)
292
0
    return false;
293
14
  pointer_value += addr_size;
294
14
  CompilerType type(valobj.GetCompilerType());
295
14
  ExecutionContext exe_ctx(target_sp, false);
296
14
  ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
297
14
      "string_ptr", pointer_value, exe_ctx, type));
298
14
  if (!child_ptr_sp)
299
0
    return false;
300
14
  DataExtractor data;
301
14
  Status error;
302
14
  child_ptr_sp->GetData(data, error);
303
14
  if (error.Fail())
304
0
    return false;
305
14
  ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
306
14
      "string_data", data, exe_ctx, type));
307
14
  child_sp->GetValueAsUnsigned(0);
308
14
  if (child_sp)
309
14
    return NSStringSummaryProvider(*child_sp, stream, options);
310
0
  return false;
311
14
}
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
33
    Stream &stream, const TypeSummaryOptions &summary_options) {
321
33
  static ConstString g_TypeHint("NSString");
322
323
33
  if (!descriptor)
324
0
    return false;
325
33
  uint64_t len_bits = 0, data_bits = 0;
326
33
  if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
327
0
    return false;
328
329
33
  static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
330
33
  static const int g_SixbitMaxLen = 9;
331
33
  static const int g_fiveBitMaxLen = 11;
332
333
33
  static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
334
33
                                          "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
335
336
33
  if (len_bits > g_fiveBitMaxLen)
337
0
    return false;
338
339
33
  std::string prefix, suffix;
340
33
  if (Language *language =
341
33
          Language::FindPlugin(summary_options.GetLanguage())) {
342
33
    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
343
33
                                            suffix)) {
344
0
      prefix.clear();
345
0
      suffix.clear();
346
0
    }
347
33
  }
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
33
  if (len_bits <= g_MaxNonBitmaskedLen) {
353
33
    stream.Printf("%s", prefix.c_str());
354
33
    stream.Printf("\"%s\"", (const char *)&data_bits);
355
33
    stream.Printf("%s", suffix.c_str());
356
33
    return true;
357
33
  }
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
33
}