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