1 //===-- NSString.cpp ----------------------------------------------*- C++
4 // The LLVM Compiler Infrastructure
6 // This file is distributed under the University of Illinois Open Source
7 // License. See LICENSE.TXT for details.
9 //===----------------------------------------------------------------------===//
13 #include "lldb/Core/ValueObject.h"
14 #include "lldb/Core/ValueObjectConstResult.h"
15 #include "lldb/DataFormatters/FormattersHelpers.h"
16 #include "lldb/DataFormatters/StringPrinter.h"
17 #include "lldb/Symbol/ClangASTContext.h"
18 #include "lldb/Target/Language.h"
19 #include "lldb/Target/ProcessStructReader.h"
20 #include "lldb/Target/Target.h"
21 #include "lldb/Utility/DataBufferHeap.h"
22 #include "lldb/Utility/Endian.h"
23 #include "lldb/Utility/Status.h"
24 #include "lldb/Utility/Stream.h"
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
30 std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
31 NSString_Additionals::GetAdditionalSummaries() {
32 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
36 static CompilerType GetNSPathStore2Type(Target &target) {
37 static ConstString g_type_name("__lldb_autogen_nspathstore2");
39 ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
42 return CompilerType();
44 CompilerType voidstar =
45 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
47 ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
49 return ast_ctx->GetOrCreateStructForIdentifier(
51 {{"isa", voidstar}, {"lengthAndRef", uint32}, {"buffer", voidstar}});
54 bool lldb_private::formatters::NSStringSummaryProvider(
55 ValueObject &valobj, Stream &stream,
56 const TypeSummaryOptions &summary_options) {
57 static ConstString g_TypeHint("NSString");
59 ProcessSP process_sp = valobj.GetProcessSP();
63 ObjCLanguageRuntime *runtime =
64 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
65 lldb::eLanguageTypeObjC);
70 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
71 runtime->GetClassDescriptor(valobj));
73 if (!descriptor.get() || !descriptor->IsValid())
76 uint32_t ptr_size = process_sp->GetAddressByteSize();
78 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
83 ConstString class_name_cs = descriptor->GetClassName();
84 const char *class_name = class_name_cs.GetCString();
86 if (!class_name || !*class_name)
89 bool is_tagged_ptr = (0 == strcmp(class_name, "NSTaggedPointerString")) &&
90 descriptor->GetTaggedPointerInfo();
91 // for a tagged pointer, the descriptor has everything we need
93 return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
96 auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
97 auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
99 return iter->second(valobj, stream, summary_options);
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;
108 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
109 info_bits_location, 1, 0, error);
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;
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;
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;
132 explicit_length_offset = 0;
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);
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);
152 std::string prefix, suffix;
153 if (Language *language =
154 Language::FindPlugin(summary_options.GetLanguage())) {
155 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
162 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
163 options.SetPrefixToken(prefix);
164 options.SetSuffixToken(suffix);
167 uint64_t location = 2 * ptr_size + valobj_addr;
168 location = process_sp->ReadPointerFromMemory(location, error);
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);
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);
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;
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;
214 if (!has_explicit_length) {
217 location += ptr_size;
219 location = process_sp->ReadPointerFromMemory(location, error);
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()));
240 reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
241 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
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 so let's try and use it to handle the embedded NUL case
262 process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
263 if (error.Fail() || explicit_length == 0)
264 has_explicit_length = false;
266 has_explicit_length = true;
269 options.SetLocation(location);
270 options.SetProcessSP(process_sp);
271 options.SetStream(&stream);
272 options.SetSourceSize(explicit_length);
273 options.SetNeedsZeroTermination(!has_explicit_length);
274 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
275 TypeSummaryCapping::eTypeSummaryUncapped);
276 options.SetBinaryZeroIsTerminator(!has_explicit_length);
277 options.SetLanguage(summary_options.GetLanguage());
278 if (has_explicit_length)
279 return StringPrinter::ReadStringAndDumpToStream<
280 StringPrinter::StringElementType::UTF8>(options);
282 return StringPrinter::ReadStringAndDumpToStream<
283 StringPrinter::StringElementType::ASCII>(options);
285 uint64_t location = valobj_addr + 2 * ptr_size;
286 location = process_sp->ReadPointerFromMemory(location, error);
289 if (has_explicit_length && !has_null)
290 explicit_length++; // account for the fact that there is no NULL and we
291 // need to have one added
292 options.SetLocation(location);
293 options.SetProcessSP(process_sp);
294 options.SetStream(&stream);
295 options.SetSourceSize(explicit_length);
296 options.SetIgnoreMaxLength(summary_options.GetCapping() ==
297 TypeSummaryCapping::eTypeSummaryUncapped);
298 options.SetLanguage(summary_options.GetLanguage());
299 return StringPrinter::ReadStringAndDumpToStream<
300 StringPrinter::StringElementType::ASCII>(options);
304 bool lldb_private::formatters::NSAttributedStringSummaryProvider(
305 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
306 TargetSP target_sp(valobj.GetTargetSP());
309 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
310 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
313 pointer_value += addr_size;
314 CompilerType type(valobj.GetCompilerType());
315 ExecutionContext exe_ctx(target_sp, false);
316 ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
317 "string_ptr", pointer_value, exe_ctx, type));
322 child_ptr_sp->GetData(data, error);
325 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
326 "string_data", data, exe_ctx, type));
327 child_sp->GetValueAsUnsigned(0);
329 return NSStringSummaryProvider(*child_sp, stream, options);
333 bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
334 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
335 return NSAttributedStringSummaryProvider(valobj, stream, options);
338 bool lldb_private::formatters::NSTaggedString_SummaryProvider(
339 ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
340 Stream &stream, const TypeSummaryOptions &summary_options) {
341 static ConstString g_TypeHint("NSString");
345 uint64_t len_bits = 0, data_bits = 0;
346 if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
349 static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
350 static const int g_SixbitMaxLen = 9;
351 static const int g_fiveBitMaxLen = 11;
353 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
354 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
356 if (len_bits > g_fiveBitMaxLen)
359 std::string prefix, suffix;
360 if (Language *language =
361 Language::FindPlugin(summary_options.GetLanguage())) {
362 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
369 // this is a fairly ugly trick - pretend that the numeric value is actually a
370 // char* this works under a few assumptions: little endian architecture
371 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
372 if (len_bits <= g_MaxNonBitmaskedLen) {
373 stream.Printf("%s", prefix.c_str());
374 stream.Printf("\"%s\"", (const char *)&data_bits);
375 stream.Printf("%s", suffix.c_str());
379 // if the data is bitmasked, we need to actually process the bytes
381 uint8_t shift_offset = 0;
383 if (len_bits <= g_SixbitMaxLen) {
391 std::vector<uint8_t> bytes;
392 bytes.resize(len_bits);
393 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
394 uint8_t packed = data_bits & bitmask;
395 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
398 stream.Printf("%s", prefix.c_str());
399 stream.Printf("\"%s\"", &bytes[0]);
400 stream.Printf("%s", suffix.c_str());