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