blob: 80896631baf3c38bdbc1369c8bdf541b75e7b302 [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"
Enrico Granata675f49b2015-10-07 18:36:53 +000021#include "lldb/Target/Language.h"
Enrico Granata7de855c2015-10-02 20:59:58 +000022#include "lldb/Target/Target.h"
23#include "lldb/Utility/ProcessStructReader.h"
24
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::formatters;
28
29std::map<ConstString, CXXFunctionSummaryFormat::Callback>&
30NSString_Additionals::GetAdditionalSummaries ()
31{
32 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
33 return g_map;
34}
35
36static CompilerType
37GetNSPathStore2Type (Target &target)
38{
39 static ConstString g_type_name("__lldb_autogen_nspathstore2");
40
41 ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
42
43 if (!ast_ctx)
44 return CompilerType();
45
46 CompilerType voidstar = ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
47 CompilerType uint32 = ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
48
49 return ast_ctx->GetOrCreateStructForIdentifier(g_type_name, {
50 {"isa",voidstar},
51 {"lengthAndRef",uint32},
52 {"buffer",voidstar}
53 });
54}
55
56bool
57lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& summary_options)
58{
Enrico Granata675f49b2015-10-07 18:36:53 +000059 static ConstString g_TypeHint("NSString");
60
Enrico Granata7de855c2015-10-02 20:59:58 +000061 ProcessSP process_sp = valobj.GetProcessSP();
62 if (!process_sp)
63 return false;
64
65 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
66
67 if (!runtime)
68 return false;
69
70 ObjCLanguageRuntime::ClassDescriptorSP descriptor(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")) && descriptor->GetTaggedPointerInfo();
89 // for a tagged pointer, the descriptor has everything we need
90 if (is_tagged_ptr)
Enrico Granata675f49b2015-10-07 18:36:53 +000091 return NSTaggedString_SummaryProvider(valobj, descriptor, stream, summary_options);
Enrico Granata7de855c2015-10-02 20:59:58 +000092
93 auto& additionals_map(NSString_Additionals::GetAdditionalSummaries());
94 auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
95 if (iter != end)
96 return iter->second(valobj, stream, summary_options);
97
98 // if not a tagged pointer that we know about, try the normal route
99 uint64_t info_bits_location = valobj_addr + ptr_size;
100 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
101 info_bits_location += 3;
102
103 Error error;
104
105 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(info_bits_location, 1, 0, error);
106 if (error.Fail())
107 return false;
108
109 bool is_mutable = (info_bits & 1) == 1;
110 bool is_inline = (info_bits & 0x60) == 0;
111 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
112 bool is_unicode = (info_bits & 0x10) == 0x10;
113 bool is_path_store = strcmp(class_name,"NSPathStore2") == 0;
114 bool has_null = (info_bits & 8) == 8;
115
116 size_t explicit_length = 0;
117 if (!has_null && has_explicit_length && !is_path_store)
118 {
119 lldb::addr_t explicit_length_offset = 2*ptr_size;
120 if (is_mutable && !is_inline)
121 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineMutable.length;
122 else if (is_inline)
123 explicit_length = explicit_length + 0; // inline1.length;
124 else if (!is_inline && !is_mutable)
125 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineImmutable1.length;
126 else
127 explicit_length_offset = 0;
128
129 if (explicit_length_offset)
130 {
131 explicit_length_offset = valobj_addr + explicit_length_offset;
132 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(explicit_length_offset, 4, 0, error);
133 }
134 }
135
136 if (strcmp(class_name,"NSString") &&
137 strcmp(class_name,"CFStringRef") &&
138 strcmp(class_name,"CFMutableStringRef") &&
139 strcmp(class_name,"__NSCFConstantString") &&
140 strcmp(class_name,"__NSCFString") &&
141 strcmp(class_name,"NSCFConstantString") &&
142 strcmp(class_name,"NSCFString") &&
143 strcmp(class_name,"NSPathStore2"))
144 {
145 // not one of us - but tell me class name
146 stream.Printf("class name = %s",class_name);
147 return true;
148 }
149
Enrico Granata675f49b2015-10-07 18:36:53 +0000150 std::string prefix,suffix;
151 if (Language* language = Language::FindPlugin(summary_options.GetLanguage()))
152 {
153 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
154 {
155 prefix.clear();
156 suffix.clear();
157 }
158 }
159
160 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
161 options.SetPrefixToken(prefix);
162 options.SetSuffixToken(suffix);
163
Enrico Granata7de855c2015-10-02 20:59:58 +0000164 if (is_mutable)
165 {
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 {
Enrico Granata7de855c2015-10-02 20:59:58 +0000172 options.SetLocation(location);
173 options.SetProcessSP(process_sp);
174 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000175 options.SetQuote('"');
176 options.SetSourceSize(explicit_length);
177 options.SetNeedsZeroTermination(false);
178 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
179 options.SetBinaryZeroIsTerminator(false);
180 options.SetLanguage(summary_options.GetLanguage());
181 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16>(options);
182 }
183 else
184 {
Enrico Granata7de855c2015-10-02 20:59:58 +0000185 options.SetLocation(location+1);
186 options.SetProcessSP(process_sp);
187 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000188 options.SetSourceSize(explicit_length);
189 options.SetNeedsZeroTermination(false);
190 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
191 options.SetBinaryZeroIsTerminator(false);
192 options.SetLanguage(summary_options.GetLanguage());
193 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
194 }
195 }
196 else if (is_inline && has_explicit_length && !is_unicode && !is_path_store && !is_mutable)
197 {
198 uint64_t location = 3 * ptr_size + valobj_addr;
199
Enrico Granata7de855c2015-10-02 20:59:58 +0000200 options.SetLocation(location);
201 options.SetProcessSP(process_sp);
202 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000203 options.SetQuote('"');
204 options.SetSourceSize(explicit_length);
205 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
206 options.SetLanguage(summary_options.GetLanguage());
207 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII> (options);
208 }
209 else if (is_unicode)
210 {
211 uint64_t location = valobj_addr + 2*ptr_size;
212 if (is_inline)
213 {
214 if (!has_explicit_length)
215 {
216 stream.Printf("found new combo");
217 return true;
218 }
219 else
220 location += ptr_size;
221 }
222 else
223 {
224 location = process_sp->ReadPointerFromMemory(location, error);
225 if (error.Fail())
226 return false;
227 }
Enrico Granata7de855c2015-10-02 20:59:58 +0000228 options.SetLocation(location);
229 options.SetProcessSP(process_sp);
230 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000231 options.SetQuote('"');
232 options.SetSourceSize(explicit_length);
233 options.SetNeedsZeroTermination(has_explicit_length == false);
234 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
235 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
236 options.SetLanguage(summary_options.GetLanguage());
237 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16> (options);
238 }
239 else if (is_path_store)
240 {
241 ProcessStructReader reader(valobj.GetProcessSP().get(), valobj.GetValueAsUnsigned(0), GetNSPathStore2Type(*valobj.GetTargetSP()));
242 explicit_length = reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
243 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
244
Enrico Granata7de855c2015-10-02 20:59:58 +0000245 options.SetLocation(location);
246 options.SetProcessSP(process_sp);
247 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000248 options.SetQuote('"');
249 options.SetSourceSize(explicit_length);
250 options.SetNeedsZeroTermination(has_explicit_length == false);
251 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
252 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
253 options.SetLanguage(summary_options.GetLanguage());
254 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16> (options);
255 }
256 else if (is_inline)
257 {
258 uint64_t location = valobj_addr + 2*ptr_size;
259 if (!has_explicit_length)
260 {
261 // in this kind of string, the byte before the string content is a length byte
262 // so let's try and use it to handle the embedded NUL case
263 Error error;
264 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
265 if (error.Fail() || explicit_length == 0)
266 has_explicit_length = false;
267 else
268 has_explicit_length = true;
269 location++;
270 }
Enrico Granata7de855c2015-10-02 20:59:58 +0000271 options.SetLocation(location);
272 options.SetProcessSP(process_sp);
273 options.SetStream(&stream);
Enrico Granata7de855c2015-10-02 20:59:58 +0000274 options.SetSourceSize(explicit_length);
275 options.SetNeedsZeroTermination(!has_explicit_length);
276 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
277 options.SetBinaryZeroIsTerminator(!has_explicit_length);
278 options.SetLanguage(summary_options.GetLanguage());
279 if (has_explicit_length)
280 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF8>(options);
281 else
282 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
283 }
284 else
285 {
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 need to have one added
Enrico Granata7de855c2015-10-02 20:59:58 +0000292 options.SetLocation(location);
293 options.SetProcessSP(process_sp);
Enrico Granata7de855c2015-10-02 20:59:58 +0000294 options.SetStream(&stream);
295 options.SetSourceSize(explicit_length);
296 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
297 options.SetLanguage(summary_options.GetLanguage());
298 return StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII>(options);
299 }
300}
301
302bool
303lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
304{
305 TargetSP target_sp(valobj.GetTargetSP());
306 if (!target_sp)
307 return false;
308 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
309 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
310 if (!pointer_value)
311 return false;
312 pointer_value += addr_size;
313 CompilerType type(valobj.GetCompilerType());
314 ExecutionContext exe_ctx(target_sp,false);
315 ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress("string_ptr", pointer_value, exe_ctx, type));
316 if (!child_ptr_sp)
317 return false;
318 DataExtractor data;
319 Error error;
320 child_ptr_sp->GetData(data, error);
321 if (error.Fail())
322 return false;
323 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData("string_data", data, exe_ctx, type));
324 child_sp->GetValueAsUnsigned(0);
325 if (child_sp)
326 return NSStringSummaryProvider(*child_sp, stream, options);
327 return false;
328}
329
330bool
331lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
332{
333 return NSAttributedStringSummaryProvider(valobj, stream, options);
334}
335
336bool
Enrico Granata675f49b2015-10-07 18:36:53 +0000337lldb_private::formatters::NSTaggedString_SummaryProvider (ValueObject& valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream& stream, const TypeSummaryOptions& summary_options)
Enrico Granata7de855c2015-10-02 20:59:58 +0000338{
Enrico Granata675f49b2015-10-07 18:36:53 +0000339 static ConstString g_TypeHint("NSString");
340
Enrico Granata7de855c2015-10-02 20:59:58 +0000341 if (!descriptor)
342 return false;
343 uint64_t len_bits = 0, data_bits = 0;
344 if (!descriptor->GetTaggedPointerInfo(&len_bits,&data_bits,nullptr))
345 return false;
346
347 static const int g_MaxNonBitmaskedLen = 7; //TAGGED_STRING_UNPACKED_MAXLEN
348 static const int g_SixbitMaxLen = 9;
349 static const int g_fiveBitMaxLen = 11;
350
351 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
352
353 if (len_bits > g_fiveBitMaxLen)
354 return false;
355
Enrico Granata675f49b2015-10-07 18:36:53 +0000356 std::string prefix,suffix;
357 if (Language* language = Language::FindPlugin(summary_options.GetLanguage()))
358 {
359 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
360 {
361 prefix.clear();
362 suffix.clear();
363 }
364 }
365
Enrico Granata7de855c2015-10-02 20:59:58 +0000366 // this is a fairly ugly trick - pretend that the numeric value is actually a char*
367 // this works under a few assumptions:
368 // little endian architecture
369 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
370 if (len_bits <= g_MaxNonBitmaskedLen)
371 {
Enrico Granata675f49b2015-10-07 18:36:53 +0000372 stream.Printf("%s",prefix.c_str());
373 stream.Printf("\"%s\"",(const char*)&data_bits);
374 stream.Printf("%s",suffix.c_str());
Enrico Granata7de855c2015-10-02 20:59:58 +0000375 return true;
376 }
377
378 // if the data is bitmasked, we need to actually process the bytes
379 uint8_t bitmask = 0;
380 uint8_t shift_offset = 0;
381
382 if (len_bits <= g_SixbitMaxLen)
383 {
384 bitmask = 0x03f;
385 shift_offset = 6;
386 }
387 else
388 {
389 bitmask = 0x01f;
390 shift_offset = 5;
391 }
392
393 std::vector<uint8_t> bytes;
394 bytes.resize(len_bits);
395 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits)
396 {
397 uint8_t packed = data_bits & bitmask;
398 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
399 }
400
Enrico Granata675f49b2015-10-07 18:36:53 +0000401 stream.Printf("%s",prefix.c_str());
402 stream.Printf("\"%s\"",&bytes[0]);
403 stream.Printf("%s",suffix.c_str());
Enrico Granata7de855c2015-10-02 20:59:58 +0000404 return true;
405}