blob: 0717d6dcff905725f4b9f3c0af308c340797adf4 [file] [log] [blame]
Enrico Granata7de855c2015-10-02 20:59:58 +00001//===-- NSString.cpp ----------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "NSString.h"
11
12#include "lldb/Core/DataBufferHeap.h"
13#include "lldb/Core/Error.h"
14#include "lldb/Core/Stream.h"
15#include "lldb/Core/ValueObject.h"
16#include "lldb/Core/ValueObjectConstResult.h"
17#include "lldb/DataFormatters/FormattersHelpers.h"
18#include "lldb/DataFormatters/StringPrinter.h"
19#include "lldb/Host/Endian.h"
20#include "lldb/Symbol/ClangASTContext.h"
21#include "lldb/Target/Target.h"
22#include "lldb/Utility/ProcessStructReader.h"
23
24using namespace lldb;
25using namespace lldb_private;
26using namespace lldb_private::formatters;
27
28std::map<ConstString, CXXFunctionSummaryFormat::Callback>&
29NSString_Additionals::GetAdditionalSummaries ()
30{
31 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
32 return g_map;
33}
34
35static CompilerType
36GetNSPathStore2Type (Target &target)
37{
38 static ConstString g_type_name("__lldb_autogen_nspathstore2");
39
40 ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
41
42 if (!ast_ctx)
43 return CompilerType();
44
45 CompilerType voidstar = ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
46 CompilerType uint32 = ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
47
48 return ast_ctx->GetOrCreateStructForIdentifier(g_type_name, {
49 {"isa",voidstar},
50 {"lengthAndRef",uint32},
51 {"buffer",voidstar}
52 });
53}
54
55bool
56lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& summary_options)
57{
58 ProcessSP process_sp = valobj.GetProcessSP();
59 if (!process_sp)
60 return false;
61
62 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
63
64 if (!runtime)
65 return false;
66
67 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
68
69 if (!descriptor.get() || !descriptor->IsValid())
70 return false;
71
72 uint32_t ptr_size = process_sp->GetAddressByteSize();
73
74 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
75
76 if (!valobj_addr)
77 return false;
78
79 ConstString class_name_cs = descriptor->GetClassName();
80 const char* class_name = class_name_cs.GetCString();
81
82 if (!class_name || !*class_name)
83 return false;
84
85 bool is_tagged_ptr = (0 == strcmp(class_name,"NSTaggedPointerString")) && descriptor->GetTaggedPointerInfo();
86 // for a tagged pointer, the descriptor has everything we need
87 if (is_tagged_ptr)
88 return NSTaggedString_SummaryProvider(descriptor, stream);
89
90 auto& additionals_map(NSString_Additionals::GetAdditionalSummaries());
91 auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
92 if (iter != end)
93 return iter->second(valobj, stream, summary_options);
94
95 // if not a tagged pointer that we know about, try the normal route
96 uint64_t info_bits_location = valobj_addr + ptr_size;
97 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
98 info_bits_location += 3;
99
100 Error error;
101
102 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(info_bits_location, 1, 0, error);
103 if (error.Fail())
104 return false;
105
106 bool is_mutable = (info_bits & 1) == 1;
107 bool is_inline = (info_bits & 0x60) == 0;
108 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
109 bool is_unicode = (info_bits & 0x10) == 0x10;
110 bool is_path_store = strcmp(class_name,"NSPathStore2") == 0;
111 bool has_null = (info_bits & 8) == 8;
112
113 size_t explicit_length = 0;
114 if (!has_null && has_explicit_length && !is_path_store)
115 {
116 lldb::addr_t explicit_length_offset = 2*ptr_size;
117 if (is_mutable && !is_inline)
118 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineMutable.length;
119 else if (is_inline)
120 explicit_length = explicit_length + 0; // inline1.length;
121 else if (!is_inline && !is_mutable)
122 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineImmutable1.length;
123 else
124 explicit_length_offset = 0;
125
126 if (explicit_length_offset)
127 {
128 explicit_length_offset = valobj_addr + explicit_length_offset;
129 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(explicit_length_offset, 4, 0, error);
130 }
131 }
132
133 if (strcmp(class_name,"NSString") &&
134 strcmp(class_name,"CFStringRef") &&
135 strcmp(class_name,"CFMutableStringRef") &&
136 strcmp(class_name,"__NSCFConstantString") &&
137 strcmp(class_name,"__NSCFString") &&
138 strcmp(class_name,"NSCFConstantString") &&
139 strcmp(class_name,"NSCFString") &&
140 strcmp(class_name,"NSPathStore2"))
141 {
142 // not one of us - but tell me class name
143 stream.Printf("class name = %s",class_name);
144 return true;
145 }
146
147 if (is_mutable)
148 {
149 uint64_t location = 2 * ptr_size + valobj_addr;
150 location = process_sp->ReadPointerFromMemory(location, error);
151 if (error.Fail())
152 return false;
153 if (has_explicit_length && is_unicode)
154 {
155 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
156 options.SetLocation(location);
157 options.SetProcessSP(process_sp);
158 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000159 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000160 options.SetQuote('"');
161 options.SetSourceSize(explicit_length);
162 options.SetNeedsZeroTermination(false);
163 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
164 options.SetBinaryZeroIsTerminator(false);
165 options.SetLanguage(summary_options.GetLanguage());
166 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16>(options);
167 }
168 else
169 {
170 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
171 options.SetLocation(location+1);
172 options.SetProcessSP(process_sp);
173 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000174 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000175 options.SetSourceSize(explicit_length);
176 options.SetNeedsZeroTermination(false);
177 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
178 options.SetBinaryZeroIsTerminator(false);
179 options.SetLanguage(summary_options.GetLanguage());
180 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
181 }
182 }
183 else if (is_inline && has_explicit_length && !is_unicode && !is_path_store && !is_mutable)
184 {
185 uint64_t location = 3 * ptr_size + valobj_addr;
186
187 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
188 options.SetLocation(location);
189 options.SetProcessSP(process_sp);
190 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000191 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000192 options.SetQuote('"');
193 options.SetSourceSize(explicit_length);
194 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
195 options.SetLanguage(summary_options.GetLanguage());
196 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII> (options);
197 }
198 else if (is_unicode)
199 {
200 uint64_t location = valobj_addr + 2*ptr_size;
201 if (is_inline)
202 {
203 if (!has_explicit_length)
204 {
205 stream.Printf("found new combo");
206 return true;
207 }
208 else
209 location += ptr_size;
210 }
211 else
212 {
213 location = process_sp->ReadPointerFromMemory(location, error);
214 if (error.Fail())
215 return false;
216 }
217 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
218 options.SetLocation(location);
219 options.SetProcessSP(process_sp);
220 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000221 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000222 options.SetQuote('"');
223 options.SetSourceSize(explicit_length);
224 options.SetNeedsZeroTermination(has_explicit_length == false);
225 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
226 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
227 options.SetLanguage(summary_options.GetLanguage());
228 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16> (options);
229 }
230 else if (is_path_store)
231 {
232 ProcessStructReader reader(valobj.GetProcessSP().get(), valobj.GetValueAsUnsigned(0), GetNSPathStore2Type(*valobj.GetTargetSP()));
233 explicit_length = reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
234 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
235
236 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
237 options.SetLocation(location);
238 options.SetProcessSP(process_sp);
239 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000240 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000241 options.SetQuote('"');
242 options.SetSourceSize(explicit_length);
243 options.SetNeedsZeroTermination(has_explicit_length == false);
244 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
245 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
246 options.SetLanguage(summary_options.GetLanguage());
247 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16> (options);
248 }
249 else if (is_inline)
250 {
251 uint64_t location = valobj_addr + 2*ptr_size;
252 if (!has_explicit_length)
253 {
254 // in this kind of string, the byte before the string content is a length byte
255 // so let's try and use it to handle the embedded NUL case
256 Error error;
257 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
258 if (error.Fail() || explicit_length == 0)
259 has_explicit_length = false;
260 else
261 has_explicit_length = true;
262 location++;
263 }
264 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
265 options.SetLocation(location);
266 options.SetProcessSP(process_sp);
267 options.SetStream(&stream);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000268 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000269 options.SetSourceSize(explicit_length);
270 options.SetNeedsZeroTermination(!has_explicit_length);
271 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
272 options.SetBinaryZeroIsTerminator(!has_explicit_length);
273 options.SetLanguage(summary_options.GetLanguage());
274 if (has_explicit_length)
275 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF8>(options);
276 else
277 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
278 }
279 else
280 {
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 need to have one added
287 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
288 options.SetLocation(location);
289 options.SetProcessSP(process_sp);
Enrico Granatad54f7fb2015-10-07 02:06:48 +0000290 options.SetPrefixToken("@");
Enrico Granata7de855c2015-10-02 20:59:58 +0000291 options.SetStream(&stream);
292 options.SetSourceSize(explicit_length);
293 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
294 options.SetLanguage(summary_options.GetLanguage());
295 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
296 }
297}
298
299bool
300lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
301{
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("string_ptr", pointer_value, exe_ctx, type));
313 if (!child_ptr_sp)
314 return false;
315 DataExtractor data;
316 Error error;
317 child_ptr_sp->GetData(data, error);
318 if (error.Fail())
319 return false;
320 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData("string_data", data, exe_ctx, type));
321 child_sp->GetValueAsUnsigned(0);
322 if (child_sp)
323 return NSStringSummaryProvider(*child_sp, stream, options);
324 return false;
325}
326
327bool
328lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
329{
330 return NSAttributedStringSummaryProvider(valobj, stream, options);
331}
332
333bool
334lldb_private::formatters::NSTaggedString_SummaryProvider (ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream& stream)
335{
336 if (!descriptor)
337 return false;
338 uint64_t len_bits = 0, data_bits = 0;
339 if (!descriptor->GetTaggedPointerInfo(&len_bits,&data_bits,nullptr))
340 return false;
341
342 static const int g_MaxNonBitmaskedLen = 7; //TAGGED_STRING_UNPACKED_MAXLEN
343 static const int g_SixbitMaxLen = 9;
344 static const int g_fiveBitMaxLen = 11;
345
346 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
347
348 if (len_bits > g_fiveBitMaxLen)
349 return false;
350
351 // this is a fairly ugly trick - pretend that the numeric value is actually a char*
352 // this works under a few assumptions:
353 // little endian architecture
354 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
355 if (len_bits <= g_MaxNonBitmaskedLen)
356 {
357 stream.Printf("@\"%s\"",(const char*)&data_bits);
358 return true;
359 }
360
361 // if the data is bitmasked, we need to actually process the bytes
362 uint8_t bitmask = 0;
363 uint8_t shift_offset = 0;
364
365 if (len_bits <= g_SixbitMaxLen)
366 {
367 bitmask = 0x03f;
368 shift_offset = 6;
369 }
370 else
371 {
372 bitmask = 0x01f;
373 shift_offset = 5;
374 }
375
376 std::vector<uint8_t> bytes;
377 bytes.resize(len_bits);
378 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits)
379 {
380 uint8_t packed = data_bits & bitmask;
381 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
382 }
383
384 stream.Printf("@\"%s\"",&bytes[0]);
385 return true;
386}