blob: 8d0906ed7fa7f612ac4f31396fc9643c8a999802 [file] [log] [blame]
Kate Stoneb9c1b512016-09-06 20:57:50 +00001//===-- NSString.cpp ----------------------------------------------*- C++
2//-*-===//
Enrico Granata7de855c2015-10-02 20:59:58 +00003//
Chandler Carruth2946cd72019-01-19 08:50:56 +00004// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Enrico Granata7de855c2015-10-02 20:59:58 +00007//
8//===----------------------------------------------------------------------===//
9
10#include "NSString.h"
11
Enrico Granata7de855c2015-10-02 20:59:58 +000012#include "lldb/Core/ValueObject.h"
13#include "lldb/Core/ValueObjectConstResult.h"
14#include "lldb/DataFormatters/FormattersHelpers.h"
15#include "lldb/DataFormatters/StringPrinter.h"
Enrico Granata7de855c2015-10-02 20:59:58 +000016#include "lldb/Symbol/ClangASTContext.h"
Enrico Granata675f49b2015-10-07 18:36:53 +000017#include "lldb/Target/Language.h"
Zachary Turner01c32432017-02-14 19:06:07 +000018#include "lldb/Target/ProcessStructReader.h"
Enrico Granata7de855c2015-10-02 20:59:58 +000019#include "lldb/Target/Target.h"
Zachary Turner666cc0b2017-03-04 01:30:05 +000020#include "lldb/Utility/DataBufferHeap.h"
Zachary Turner01c32432017-02-14 19:06:07 +000021#include "lldb/Utility/Endian.h"
Zachary Turner97206d52017-05-12 04:51:55 +000022#include "lldb/Utility/Status.h"
Zachary Turnerbf9a7732017-02-02 21:39:50 +000023#include "lldb/Utility/Stream.h"
Enrico Granata7de855c2015-10-02 20:59:58 +000024
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::formatters;
28
Kate Stoneb9c1b512016-09-06 20:57:50 +000029std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
30NSString_Additionals::GetAdditionalSummaries() {
31 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
32 return g_map;
Enrico Granata7de855c2015-10-02 20:59:58 +000033}
34
Kate Stoneb9c1b512016-09-06 20:57:50 +000035static CompilerType GetNSPathStore2Type(Target &target) {
36 static ConstString g_type_name("__lldb_autogen_nspathstore2");
37
38 ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
39
40 if (!ast_ctx)
41 return CompilerType();
42
43 CompilerType voidstar =
44 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
45 CompilerType uint32 =
46 ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
47
48 return ast_ctx->GetOrCreateStructForIdentifier(
49 g_type_name,
50 {{"isa", voidstar}, {"lengthAndRef", uint32}, {"buffer", voidstar}});
Enrico Granata7de855c2015-10-02 20:59:58 +000051}
52
Kate Stoneb9c1b512016-09-06 20:57:50 +000053bool lldb_private::formatters::NSStringSummaryProvider(
54 ValueObject &valobj, Stream &stream,
55 const TypeSummaryOptions &summary_options) {
56 static ConstString g_TypeHint("NSString");
Enrico Granata7de855c2015-10-02 20:59:58 +000057
Kate Stoneb9c1b512016-09-06 20:57:50 +000058 ProcessSP process_sp = valobj.GetProcessSP();
59 if (!process_sp)
Enrico Granata7de855c2015-10-02 20:59:58 +000060 return false;
Enrico Granata7de855c2015-10-02 20:59:58 +000061
Kate Stoneb9c1b512016-09-06 20:57:50 +000062 ObjCLanguageRuntime *runtime =
63 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
64 lldb::eLanguageTypeObjC);
Enrico Granata7de855c2015-10-02 20:59:58 +000065
Kate Stoneb9c1b512016-09-06 20:57:50 +000066 if (!runtime)
67 return false;
68
69 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
70 runtime->GetClassDescriptor(valobj));
71
72 if (!descriptor.get() || !descriptor->IsValid())
73 return false;
74
75 uint32_t ptr_size = process_sp->GetAddressByteSize();
76
77 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
78
79 if (!valobj_addr)
80 return false;
81
82 ConstString class_name_cs = descriptor->GetClassName();
83 const char *class_name = class_name_cs.GetCString();
84
85 if (!class_name || !*class_name)
86 return false;
87
88 bool is_tagged_ptr = (0 == strcmp(class_name, "NSTaggedPointerString")) &&
89 descriptor->GetTaggedPointerInfo();
90 // for a tagged pointer, the descriptor has everything we need
91 if (is_tagged_ptr)
92 return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
93 summary_options);
94
95 auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
96 auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
97 if (iter != end)
98 return iter->second(valobj, stream, summary_options);
99
100 // if not a tagged pointer that we know about, try the normal route
101 uint64_t info_bits_location = valobj_addr + ptr_size;
102 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
103 info_bits_location += 3;
104
Zachary Turner97206d52017-05-12 04:51:55 +0000105 Status error;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000106
107 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
108 info_bits_location, 1, 0, error);
109 if (error.Fail())
110 return false;
111
112 bool is_mutable = (info_bits & 1) == 1;
113 bool is_inline = (info_bits & 0x60) == 0;
114 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
115 bool is_unicode = (info_bits & 0x10) == 0x10;
116 bool is_path_store = strcmp(class_name, "NSPathStore2") == 0;
117 bool has_null = (info_bits & 8) == 8;
118
119 size_t explicit_length = 0;
120 if (!has_null && has_explicit_length && !is_path_store) {
121 lldb::addr_t explicit_length_offset = 2 * ptr_size;
122 if (is_mutable && !is_inline)
123 explicit_length_offset =
124 explicit_length_offset + ptr_size; // notInlineMutable.length;
125 else if (is_inline)
126 explicit_length = explicit_length + 0; // inline1.length;
127 else if (!is_inline && !is_mutable)
128 explicit_length_offset =
129 explicit_length_offset + ptr_size; // notInlineImmutable1.length;
Enrico Granata7de855c2015-10-02 20:59:58 +0000130 else
Kate Stoneb9c1b512016-09-06 20:57:50 +0000131 explicit_length_offset = 0;
132
133 if (explicit_length_offset) {
134 explicit_length_offset = valobj_addr + explicit_length_offset;
135 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
136 explicit_length_offset, 4, 0, error);
Enrico Granata7de855c2015-10-02 20:59:58 +0000137 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000138 }
139
140 if (strcmp(class_name, "NSString") && strcmp(class_name, "CFStringRef") &&
141 strcmp(class_name, "CFMutableStringRef") &&
142 strcmp(class_name, "__NSCFConstantString") &&
143 strcmp(class_name, "__NSCFString") &&
144 strcmp(class_name, "NSCFConstantString") &&
145 strcmp(class_name, "NSCFString") && strcmp(class_name, "NSPathStore2")) {
146 // not one of us - but tell me class name
147 stream.Printf("class name = %s", class_name);
Enrico Granata7de855c2015-10-02 20:59:58 +0000148 return true;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000149 }
150
151 std::string prefix, suffix;
152 if (Language *language =
153 Language::FindPlugin(summary_options.GetLanguage())) {
154 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
155 suffix)) {
156 prefix.clear();
157 suffix.clear();
158 }
159 }
160
161 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
162 options.SetPrefixToken(prefix);
163 options.SetSuffixToken(suffix);
164
165 if (is_mutable) {
166 uint64_t location = 2 * ptr_size + valobj_addr;
167 location = process_sp->ReadPointerFromMemory(location, error);
168 if (error.Fail())
169 return false;
170 if (has_explicit_length && is_unicode) {
171 options.SetLocation(location);
172 options.SetProcessSP(process_sp);
173 options.SetStream(&stream);
174 options.SetQuote('"');
175 options.SetSourceSize(explicit_length);
176 options.SetNeedsZeroTermination(false);
177 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
178 TypeSummaryCapping::eTypeSummaryUncapped);
179 options.SetBinaryZeroIsTerminator(false);
180 options.SetLanguage(summary_options.GetLanguage());
181 return StringPrinter::ReadStringAndDumpToStream<
182 StringPrinter::StringElementType::UTF16>(options);
183 } else {
184 options.SetLocation(location + 1);
185 options.SetProcessSP(process_sp);
186 options.SetStream(&stream);
187 options.SetSourceSize(explicit_length);
188 options.SetNeedsZeroTermination(false);
189 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
190 TypeSummaryCapping::eTypeSummaryUncapped);
191 options.SetBinaryZeroIsTerminator(false);
192 options.SetLanguage(summary_options.GetLanguage());
193 return StringPrinter::ReadStringAndDumpToStream<
194 StringPrinter::StringElementType::ASCII>(options);
195 }
196 } else if (is_inline && has_explicit_length && !is_unicode &&
197 !is_path_store && !is_mutable) {
198 uint64_t location = 3 * ptr_size + valobj_addr;
199
200 options.SetLocation(location);
201 options.SetProcessSP(process_sp);
202 options.SetStream(&stream);
203 options.SetQuote('"');
204 options.SetSourceSize(explicit_length);
205 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
206 TypeSummaryCapping::eTypeSummaryUncapped);
207 options.SetLanguage(summary_options.GetLanguage());
208 return StringPrinter::ReadStringAndDumpToStream<
209 StringPrinter::StringElementType::ASCII>(options);
210 } else if (is_unicode) {
211 uint64_t location = valobj_addr + 2 * ptr_size;
212 if (is_inline) {
213 if (!has_explicit_length) {
Enrico Granatac8af52d2016-10-20 22:05:21 +0000214 return false;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000215 } else
216 location += ptr_size;
217 } else {
218 location = process_sp->ReadPointerFromMemory(location, error);
219 if (error.Fail())
220 return false;
221 }
222 options.SetLocation(location);
223 options.SetProcessSP(process_sp);
224 options.SetStream(&stream);
225 options.SetQuote('"');
226 options.SetSourceSize(explicit_length);
Jonas Devliegherea6682a42018-12-15 00:15:33 +0000227 options.SetNeedsZeroTermination(!has_explicit_length);
Kate Stoneb9c1b512016-09-06 20:57:50 +0000228 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
229 TypeSummaryCapping::eTypeSummaryUncapped);
Jonas Devliegherea6682a42018-12-15 00:15:33 +0000230 options.SetBinaryZeroIsTerminator(!has_explicit_length);
Kate Stoneb9c1b512016-09-06 20:57:50 +0000231 options.SetLanguage(summary_options.GetLanguage());
232 return StringPrinter::ReadStringAndDumpToStream<
233 StringPrinter::StringElementType::UTF16>(options);
234 } else if (is_path_store) {
235 ProcessStructReader reader(valobj.GetProcessSP().get(),
236 valobj.GetValueAsUnsigned(0),
237 GetNSPathStore2Type(*valobj.GetTargetSP()));
238 explicit_length =
239 reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
240 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
241
242 options.SetLocation(location);
243 options.SetProcessSP(process_sp);
244 options.SetStream(&stream);
245 options.SetQuote('"');
246 options.SetSourceSize(explicit_length);
Jonas Devliegherea6682a42018-12-15 00:15:33 +0000247 options.SetNeedsZeroTermination(!has_explicit_length);
Kate Stoneb9c1b512016-09-06 20:57:50 +0000248 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
249 TypeSummaryCapping::eTypeSummaryUncapped);
Jonas Devliegherea6682a42018-12-15 00:15:33 +0000250 options.SetBinaryZeroIsTerminator(!has_explicit_length);
Kate Stoneb9c1b512016-09-06 20:57:50 +0000251 options.SetLanguage(summary_options.GetLanguage());
252 return StringPrinter::ReadStringAndDumpToStream<
253 StringPrinter::StringElementType::UTF16>(options);
254 } else if (is_inline) {
255 uint64_t location = valobj_addr + 2 * ptr_size;
256 if (!has_explicit_length) {
257 // in this kind of string, the byte before the string content is a length
Adrian Prantl05097242018-04-30 16:49:04 +0000258 // byte so let's try and use it to handle the embedded NUL case
Zachary Turner97206d52017-05-12 04:51:55 +0000259 Status error;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000260 explicit_length =
261 process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
Jonas Devliegherea6682a42018-12-15 00:15:33 +0000262 has_explicit_length = !(error.Fail() || explicit_length == 0);
Kate Stoneb9c1b512016-09-06 20:57:50 +0000263 location++;
264 }
265 options.SetLocation(location);
266 options.SetProcessSP(process_sp);
267 options.SetStream(&stream);
268 options.SetSourceSize(explicit_length);
269 options.SetNeedsZeroTermination(!has_explicit_length);
270 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
271 TypeSummaryCapping::eTypeSummaryUncapped);
272 options.SetBinaryZeroIsTerminator(!has_explicit_length);
273 options.SetLanguage(summary_options.GetLanguage());
274 if (has_explicit_length)
275 return StringPrinter::ReadStringAndDumpToStream<
276 StringPrinter::StringElementType::UTF8>(options);
277 else
278 return StringPrinter::ReadStringAndDumpToStream<
279 StringPrinter::StringElementType::ASCII>(options);
280 } else {
281 uint64_t location = valobj_addr + 2 * ptr_size;
282 location = process_sp->ReadPointerFromMemory(location, error);
283 if (error.Fail())
284 return false;
285 if (has_explicit_length && !has_null)
286 explicit_length++; // account for the fact that there is no NULL and we
287 // need to have one added
288 options.SetLocation(location);
289 options.SetProcessSP(process_sp);
290 options.SetStream(&stream);
291 options.SetSourceSize(explicit_length);
292 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
293 TypeSummaryCapping::eTypeSummaryUncapped);
294 options.SetLanguage(summary_options.GetLanguage());
295 return StringPrinter::ReadStringAndDumpToStream<
296 StringPrinter::StringElementType::ASCII>(options);
297 }
298}
299
300bool lldb_private::formatters::NSAttributedStringSummaryProvider(
301 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
302 TargetSP target_sp(valobj.GetTargetSP());
303 if (!target_sp)
304 return false;
305 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
306 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
307 if (!pointer_value)
308 return false;
309 pointer_value += addr_size;
310 CompilerType type(valobj.GetCompilerType());
311 ExecutionContext exe_ctx(target_sp, false);
312 ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
313 "string_ptr", pointer_value, exe_ctx, type));
314 if (!child_ptr_sp)
315 return false;
316 DataExtractor data;
Zachary Turner97206d52017-05-12 04:51:55 +0000317 Status error;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000318 child_ptr_sp->GetData(data, error);
319 if (error.Fail())
320 return false;
321 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
322 "string_data", data, exe_ctx, type));
323 child_sp->GetValueAsUnsigned(0);
324 if (child_sp)
325 return NSStringSummaryProvider(*child_sp, stream, options);
326 return false;
327}
328
329bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
330 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
331 return NSAttributedStringSummaryProvider(valobj, stream, options);
332}
333
334bool lldb_private::formatters::NSTaggedString_SummaryProvider(
335 ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
336 Stream &stream, const TypeSummaryOptions &summary_options) {
337 static ConstString g_TypeHint("NSString");
338
339 if (!descriptor)
340 return false;
341 uint64_t len_bits = 0, data_bits = 0;
342 if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
343 return false;
344
345 static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
346 static const int g_SixbitMaxLen = 9;
347 static const int g_fiveBitMaxLen = 11;
348
349 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
350 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
351
352 if (len_bits > g_fiveBitMaxLen)
353 return false;
354
355 std::string prefix, suffix;
356 if (Language *language =
357 Language::FindPlugin(summary_options.GetLanguage())) {
358 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
359 suffix)) {
360 prefix.clear();
361 suffix.clear();
362 }
363 }
364
365 // this is a fairly ugly trick - pretend that the numeric value is actually a
Adrian Prantl05097242018-04-30 16:49:04 +0000366 // char* this works under a few assumptions: little endian architecture
Kate Stoneb9c1b512016-09-06 20:57:50 +0000367 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
368 if (len_bits <= g_MaxNonBitmaskedLen) {
369 stream.Printf("%s", prefix.c_str());
370 stream.Printf("\"%s\"", (const char *)&data_bits);
371 stream.Printf("%s", suffix.c_str());
372 return true;
373 }
374
375 // if the data is bitmasked, we need to actually process the bytes
376 uint8_t bitmask = 0;
377 uint8_t shift_offset = 0;
378
379 if (len_bits <= g_SixbitMaxLen) {
380 bitmask = 0x03f;
381 shift_offset = 6;
382 } else {
383 bitmask = 0x01f;
384 shift_offset = 5;
385 }
386
387 std::vector<uint8_t> bytes;
388 bytes.resize(len_bits);
389 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
390 uint8_t packed = data_bits & bitmask;
391 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
392 }
393
394 stream.Printf("%s", prefix.c_str());
395 stream.Printf("\"%s\"", &bytes[0]);
396 stream.Printf("%s", suffix.c_str());
397 return true;
Enrico Granata7de855c2015-10-02 20:59:58 +0000398}