180814287SRaphael Isemann //===-- NSString.cpp ------------------------------------------------------===//
27de855c6SEnrico Granata //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67de855c6SEnrico Granata //
77de855c6SEnrico Granata //===----------------------------------------------------------------------===//
87de855c6SEnrico Granata 
97de855c6SEnrico Granata #include "NSString.h"
107de855c6SEnrico Granata 
117de855c6SEnrico Granata #include "lldb/Core/ValueObject.h"
127de855c6SEnrico Granata #include "lldb/Core/ValueObjectConstResult.h"
137de855c6SEnrico Granata #include "lldb/DataFormatters/FormattersHelpers.h"
147de855c6SEnrico Granata #include "lldb/DataFormatters/StringPrinter.h"
15675f49bbSEnrico Granata #include "lldb/Target/Language.h"
167de855c6SEnrico Granata #include "lldb/Target/Target.h"
17*9d9f3e0eSDave Lee #include "lldb/Utility/ConstString.h"
18666cc0b2SZachary Turner #include "lldb/Utility/DataBufferHeap.h"
1901c3243fSZachary Turner #include "lldb/Utility/Endian.h"
2097206d57SZachary Turner #include "lldb/Utility/Status.h"
21bf9a7730SZachary Turner #include "lldb/Utility/Stream.h"
227de855c6SEnrico Granata 
237de855c6SEnrico Granata using namespace lldb;
247de855c6SEnrico Granata using namespace lldb_private;
257de855c6SEnrico Granata using namespace lldb_private::formatters;
267de855c6SEnrico Granata 
277de855c6SEnrico Granata std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
GetAdditionalSummaries()28b9c1b51eSKate Stone NSString_Additionals::GetAdditionalSummaries() {
297de855c6SEnrico Granata   static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
307de855c6SEnrico Granata   return g_map;
317de855c6SEnrico Granata }
327de855c6SEnrico Granata 
NSStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & summary_options)33b9c1b51eSKate Stone bool lldb_private::formatters::NSStringSummaryProvider(
34b9c1b51eSKate Stone     ValueObject &valobj, Stream &stream,
35b9c1b51eSKate Stone     const TypeSummaryOptions &summary_options) {
36675f49bbSEnrico Granata   static ConstString g_TypeHint("NSString");
37675f49bbSEnrico Granata 
387de855c6SEnrico Granata   ProcessSP process_sp = valobj.GetProcessSP();
397de855c6SEnrico Granata   if (!process_sp)
407de855c6SEnrico Granata     return false;
417de855c6SEnrico Granata 
42e823bbe8SAlex Langford   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
437de855c6SEnrico Granata 
447de855c6SEnrico Granata   if (!runtime)
457de855c6SEnrico Granata     return false;
467de855c6SEnrico Granata 
47b9c1b51eSKate Stone   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
48b9c1b51eSKate Stone       runtime->GetClassDescriptor(valobj));
497de855c6SEnrico Granata 
507de855c6SEnrico Granata   if (!descriptor.get() || !descriptor->IsValid())
517de855c6SEnrico Granata     return false;
527de855c6SEnrico Granata 
537de855c6SEnrico Granata   uint32_t ptr_size = process_sp->GetAddressByteSize();
547de855c6SEnrico Granata 
557de855c6SEnrico Granata   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
567de855c6SEnrico Granata 
577de855c6SEnrico Granata   if (!valobj_addr)
587de855c6SEnrico Granata     return false;
597de855c6SEnrico Granata 
607de855c6SEnrico Granata   ConstString class_name_cs = descriptor->GetClassName();
616bac09afSRaphael Isemann   llvm::StringRef class_name = class_name_cs.GetStringRef();
627de855c6SEnrico Granata 
636bac09afSRaphael Isemann   if (class_name.empty())
647de855c6SEnrico Granata     return false;
657de855c6SEnrico Granata 
666bac09afSRaphael Isemann   bool is_tagged_ptr = class_name == "NSTaggedPointerString" &&
67b9c1b51eSKate Stone                        descriptor->GetTaggedPointerInfo();
687de855c6SEnrico Granata   // for a tagged pointer, the descriptor has everything we need
697de855c6SEnrico Granata   if (is_tagged_ptr)
70b9c1b51eSKate Stone     return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
71b9c1b51eSKate Stone                                           summary_options);
727de855c6SEnrico Granata 
737de855c6SEnrico Granata   auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
747de855c6SEnrico Granata   auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
757de855c6SEnrico Granata   if (iter != end)
767de855c6SEnrico Granata     return iter->second(valobj, stream, summary_options);
777de855c6SEnrico Granata 
787de855c6SEnrico Granata   // if not a tagged pointer that we know about, try the normal route
797de855c6SEnrico Granata   uint64_t info_bits_location = valobj_addr + ptr_size;
807de855c6SEnrico Granata   if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
817de855c6SEnrico Granata     info_bits_location += 3;
827de855c6SEnrico Granata 
8397206d57SZachary Turner   Status error;
847de855c6SEnrico Granata 
85b9c1b51eSKate Stone   uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
86b9c1b51eSKate Stone       info_bits_location, 1, 0, error);
877de855c6SEnrico Granata   if (error.Fail())
887de855c6SEnrico Granata     return false;
897de855c6SEnrico Granata 
907de855c6SEnrico Granata   bool is_mutable = (info_bits & 1) == 1;
917de855c6SEnrico Granata   bool is_inline = (info_bits & 0x60) == 0;
927de855c6SEnrico Granata   bool has_explicit_length = (info_bits & (1 | 4)) != 4;
937de855c6SEnrico Granata   bool is_unicode = (info_bits & 0x10) == 0x10;
946bac09afSRaphael Isemann   bool is_path_store = class_name == "NSPathStore2";
957de855c6SEnrico Granata   bool has_null = (info_bits & 8) == 8;
967de855c6SEnrico Granata 
977de855c6SEnrico Granata   size_t explicit_length = 0;
98b9c1b51eSKate Stone   if (!has_null && has_explicit_length && !is_path_store) {
997de855c6SEnrico Granata     lldb::addr_t explicit_length_offset = 2 * ptr_size;
1007de855c6SEnrico Granata     if (is_mutable && !is_inline)
101b9c1b51eSKate Stone       explicit_length_offset =
102b9c1b51eSKate Stone           explicit_length_offset + ptr_size; //  notInlineMutable.length;
1037de855c6SEnrico Granata     else if (is_inline)
1047de855c6SEnrico Granata       explicit_length = explicit_length + 0; // inline1.length;
1057de855c6SEnrico Granata     else if (!is_inline && !is_mutable)
106b9c1b51eSKate Stone       explicit_length_offset =
107b9c1b51eSKate Stone           explicit_length_offset + ptr_size; // notInlineImmutable1.length;
1087de855c6SEnrico Granata     else
1097de855c6SEnrico Granata       explicit_length_offset = 0;
1107de855c6SEnrico Granata 
111b9c1b51eSKate Stone     if (explicit_length_offset) {
1127de855c6SEnrico Granata       explicit_length_offset = valobj_addr + explicit_length_offset;
113b9c1b51eSKate Stone       explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
114b9c1b51eSKate Stone           explicit_length_offset, 4, 0, error);
1157de855c6SEnrico Granata     }
1167de855c6SEnrico Granata   }
1177de855c6SEnrico Granata 
1186bac09afSRaphael Isemann   const llvm::StringSet<> supported_string_classes = {
1196bac09afSRaphael Isemann       "NSString",     "CFMutableStringRef",
1206bac09afSRaphael Isemann       "CFStringRef",  "__NSCFConstantString",
1216bac09afSRaphael Isemann       "__NSCFString", "NSCFConstantString",
1226bac09afSRaphael Isemann       "NSCFString",   "NSPathStore2"};
1236bac09afSRaphael Isemann   if (supported_string_classes.count(class_name) == 0) {
1247de855c6SEnrico Granata     // not one of us - but tell me class name
1256bac09afSRaphael Isemann     stream.Printf("class name = %s", class_name_cs.GetCString());
1267de855c6SEnrico Granata     return true;
1277de855c6SEnrico Granata   }
1287de855c6SEnrico Granata 
129675f49bbSEnrico Granata   std::string prefix, suffix;
130b9c1b51eSKate Stone   if (Language *language =
131b9c1b51eSKate Stone           Language::FindPlugin(summary_options.GetLanguage())) {
132b9c1b51eSKate Stone     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
133b9c1b51eSKate Stone                                             suffix)) {
134675f49bbSEnrico Granata       prefix.clear();
135675f49bbSEnrico Granata       suffix.clear();
136675f49bbSEnrico Granata     }
137675f49bbSEnrico Granata   }
138675f49bbSEnrico Granata 
139675f49bbSEnrico Granata   StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
140675f49bbSEnrico Granata   options.SetPrefixToken(prefix);
141675f49bbSEnrico Granata   options.SetSuffixToken(suffix);
142675f49bbSEnrico Granata 
143b9c1b51eSKate Stone   if (is_mutable) {
1447de855c6SEnrico Granata     uint64_t location = 2 * ptr_size + valobj_addr;
1457de855c6SEnrico Granata     location = process_sp->ReadPointerFromMemory(location, error);
1467de855c6SEnrico Granata     if (error.Fail())
1477de855c6SEnrico Granata       return false;
148b9c1b51eSKate Stone     if (has_explicit_length && is_unicode) {
1497de855c6SEnrico Granata       options.SetLocation(location);
1504c56f734SPavel Labath       options.SetTargetSP(valobj.GetTargetSP());
1517de855c6SEnrico Granata       options.SetStream(&stream);
1527de855c6SEnrico Granata       options.SetQuote('"');
1537de855c6SEnrico Granata       options.SetSourceSize(explicit_length);
1547b244258SRaphael Isemann       options.SetHasSourceSize(has_explicit_length);
1557de855c6SEnrico Granata       options.SetNeedsZeroTermination(false);
156b9c1b51eSKate Stone       options.SetIgnoreMaxLength(summary_options.GetCapping() ==
157b9c1b51eSKate Stone                                  TypeSummaryCapping::eTypeSummaryUncapped);
1587de855c6SEnrico Granata       options.SetBinaryZeroIsTerminator(false);
159b9c1b51eSKate Stone       return StringPrinter::ReadStringAndDumpToStream<
160b9c1b51eSKate Stone           StringPrinter::StringElementType::UTF16>(options);
161b9c1b51eSKate Stone     } else {
1627de855c6SEnrico Granata       options.SetLocation(location + 1);
1634c56f734SPavel Labath       options.SetTargetSP(valobj.GetTargetSP());
1647de855c6SEnrico Granata       options.SetStream(&stream);
1657de855c6SEnrico Granata       options.SetSourceSize(explicit_length);
1667b244258SRaphael Isemann       options.SetHasSourceSize(has_explicit_length);
1677de855c6SEnrico Granata       options.SetNeedsZeroTermination(false);
168b9c1b51eSKate Stone       options.SetIgnoreMaxLength(summary_options.GetCapping() ==
169b9c1b51eSKate Stone                                  TypeSummaryCapping::eTypeSummaryUncapped);
1707de855c6SEnrico Granata       options.SetBinaryZeroIsTerminator(false);
171b9c1b51eSKate Stone       return StringPrinter::ReadStringAndDumpToStream<
172b9c1b51eSKate Stone           StringPrinter::StringElementType::ASCII>(options);
1737de855c6SEnrico Granata     }
174b9c1b51eSKate Stone   } else if (is_inline && has_explicit_length && !is_unicode &&
175b9c1b51eSKate Stone              !is_path_store && !is_mutable) {
1767de855c6SEnrico Granata     uint64_t location = 3 * ptr_size + valobj_addr;
1777de855c6SEnrico Granata 
1787de855c6SEnrico Granata     options.SetLocation(location);
1794c56f734SPavel Labath     options.SetTargetSP(valobj.GetTargetSP());
1807de855c6SEnrico Granata     options.SetStream(&stream);
1817de855c6SEnrico Granata     options.SetQuote('"');
1827de855c6SEnrico Granata     options.SetSourceSize(explicit_length);
1837b244258SRaphael Isemann     options.SetHasSourceSize(has_explicit_length);
184b9c1b51eSKate Stone     options.SetIgnoreMaxLength(summary_options.GetCapping() ==
185b9c1b51eSKate Stone                                TypeSummaryCapping::eTypeSummaryUncapped);
186b9c1b51eSKate Stone     return StringPrinter::ReadStringAndDumpToStream<
187b9c1b51eSKate Stone         StringPrinter::StringElementType::ASCII>(options);
188b9c1b51eSKate Stone   } else if (is_unicode) {
1897de855c6SEnrico Granata     uint64_t location = valobj_addr + 2 * ptr_size;
190b9c1b51eSKate Stone     if (is_inline) {
191b9c1b51eSKate Stone       if (!has_explicit_length) {
192c8af52dfSEnrico Granata         return false;
193b9c1b51eSKate Stone       } else
1947de855c6SEnrico Granata         location += ptr_size;
195b9c1b51eSKate Stone     } else {
1967de855c6SEnrico Granata       location = process_sp->ReadPointerFromMemory(location, error);
1977de855c6SEnrico Granata       if (error.Fail())
1987de855c6SEnrico Granata         return false;
1997de855c6SEnrico Granata     }
2007de855c6SEnrico Granata     options.SetLocation(location);
2014c56f734SPavel Labath     options.SetTargetSP(valobj.GetTargetSP());
2027de855c6SEnrico Granata     options.SetStream(&stream);
2037de855c6SEnrico Granata     options.SetQuote('"');
2047de855c6SEnrico Granata     options.SetSourceSize(explicit_length);
2057b244258SRaphael Isemann     options.SetHasSourceSize(has_explicit_length);
206a6682a41SJonas Devlieghere     options.SetNeedsZeroTermination(!has_explicit_length);
207b9c1b51eSKate Stone     options.SetIgnoreMaxLength(summary_options.GetCapping() ==
208b9c1b51eSKate Stone                                TypeSummaryCapping::eTypeSummaryUncapped);
209a6682a41SJonas Devlieghere     options.SetBinaryZeroIsTerminator(!has_explicit_length);
210b9c1b51eSKate Stone     return StringPrinter::ReadStringAndDumpToStream<
211b9c1b51eSKate Stone         StringPrinter::StringElementType::UTF16>(options);
212b9c1b51eSKate Stone   } else if (is_path_store) {
213*9d9f3e0eSDave Lee     // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
214*9d9f3e0eSDave Lee     uint64_t length_ivar_offset = 1 * ptr_size;
215*9d9f3e0eSDave Lee     CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
216*9d9f3e0eSDave Lee         lldb::eBasicTypeUnsignedInt);
217*9d9f3e0eSDave Lee     ValueObjectSP length_valobj_sp =
218*9d9f3e0eSDave Lee         valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
219*9d9f3e0eSDave Lee                                          ConstString("_lengthAndRefCount"));
220*9d9f3e0eSDave Lee     if (!length_valobj_sp)
221*9d9f3e0eSDave Lee       return false;
222*9d9f3e0eSDave Lee     // Get the length out of _lengthAndRefCount.
223*9d9f3e0eSDave Lee     explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
2247de855c6SEnrico Granata     lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
2257de855c6SEnrico Granata 
2267de855c6SEnrico Granata     options.SetLocation(location);
2274c56f734SPavel Labath     options.SetTargetSP(valobj.GetTargetSP());
2287de855c6SEnrico Granata     options.SetStream(&stream);
2297de855c6SEnrico Granata     options.SetQuote('"');
2307de855c6SEnrico Granata     options.SetSourceSize(explicit_length);
2317b244258SRaphael Isemann     options.SetHasSourceSize(has_explicit_length);
232a6682a41SJonas Devlieghere     options.SetNeedsZeroTermination(!has_explicit_length);
233b9c1b51eSKate Stone     options.SetIgnoreMaxLength(summary_options.GetCapping() ==
234b9c1b51eSKate Stone                                TypeSummaryCapping::eTypeSummaryUncapped);
235a6682a41SJonas Devlieghere     options.SetBinaryZeroIsTerminator(!has_explicit_length);
236b9c1b51eSKate Stone     return StringPrinter::ReadStringAndDumpToStream<
237b9c1b51eSKate Stone         StringPrinter::StringElementType::UTF16>(options);
238b9c1b51eSKate Stone   } else if (is_inline) {
2397de855c6SEnrico Granata     uint64_t location = valobj_addr + 2 * ptr_size;
240b9c1b51eSKate Stone     if (!has_explicit_length) {
241b9c1b51eSKate Stone       // in this kind of string, the byte before the string content is a length
24205097246SAdrian Prantl       // byte so let's try and use it to handle the embedded NUL case
24397206d57SZachary Turner       Status error;
244b9c1b51eSKate Stone       explicit_length =
245b9c1b51eSKate Stone           process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
246a6682a41SJonas Devlieghere       has_explicit_length = !(error.Fail() || explicit_length == 0);
2477de855c6SEnrico Granata       location++;
2487de855c6SEnrico Granata     }
2497de855c6SEnrico Granata     options.SetLocation(location);
2504c56f734SPavel Labath     options.SetTargetSP(valobj.GetTargetSP());
2517de855c6SEnrico Granata     options.SetStream(&stream);
2527de855c6SEnrico Granata     options.SetSourceSize(explicit_length);
2537b244258SRaphael Isemann     options.SetHasSourceSize(has_explicit_length);
2547de855c6SEnrico Granata     options.SetNeedsZeroTermination(!has_explicit_length);
255b9c1b51eSKate Stone     options.SetIgnoreMaxLength(summary_options.GetCapping() ==
256b9c1b51eSKate Stone                                TypeSummaryCapping::eTypeSummaryUncapped);
2577de855c6SEnrico Granata     options.SetBinaryZeroIsTerminator(!has_explicit_length);
2587de855c6SEnrico Granata     if (has_explicit_length)
259b9c1b51eSKate Stone       return StringPrinter::ReadStringAndDumpToStream<
260b9c1b51eSKate Stone           StringPrinter::StringElementType::UTF8>(options);
2617de855c6SEnrico Granata     else
262b9c1b51eSKate Stone       return StringPrinter::ReadStringAndDumpToStream<
263b9c1b51eSKate Stone           StringPrinter::StringElementType::ASCII>(options);
264b9c1b51eSKate Stone   } else {
2657de855c6SEnrico Granata     uint64_t location = valobj_addr + 2 * ptr_size;
2667de855c6SEnrico Granata     location = process_sp->ReadPointerFromMemory(location, error);
2677de855c6SEnrico Granata     if (error.Fail())
2687de855c6SEnrico Granata       return false;
2697de855c6SEnrico Granata     if (has_explicit_length && !has_null)
270b9c1b51eSKate Stone       explicit_length++; // account for the fact that there is no NULL and we
271b9c1b51eSKate Stone                          // need to have one added
2727de855c6SEnrico Granata     options.SetLocation(location);
2734c56f734SPavel Labath     options.SetTargetSP(valobj.GetTargetSP());
2747de855c6SEnrico Granata     options.SetStream(&stream);
2757de855c6SEnrico Granata     options.SetSourceSize(explicit_length);
2767b244258SRaphael Isemann     options.SetHasSourceSize(has_explicit_length);
277b9c1b51eSKate Stone     options.SetIgnoreMaxLength(summary_options.GetCapping() ==
278b9c1b51eSKate Stone                                TypeSummaryCapping::eTypeSummaryUncapped);
279b9c1b51eSKate Stone     return StringPrinter::ReadStringAndDumpToStream<
280b9c1b51eSKate Stone         StringPrinter::StringElementType::ASCII>(options);
2817de855c6SEnrico Granata   }
2827de855c6SEnrico Granata }
2837de855c6SEnrico Granata 
NSAttributedStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)284b9c1b51eSKate Stone bool lldb_private::formatters::NSAttributedStringSummaryProvider(
285b9c1b51eSKate Stone     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
2867de855c6SEnrico Granata   TargetSP target_sp(valobj.GetTargetSP());
2877de855c6SEnrico Granata   if (!target_sp)
2887de855c6SEnrico Granata     return false;
2897de855c6SEnrico Granata   uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
2907de855c6SEnrico Granata   uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
2917de855c6SEnrico Granata   if (!pointer_value)
2927de855c6SEnrico Granata     return false;
2937de855c6SEnrico Granata   pointer_value += addr_size;
2947de855c6SEnrico Granata   CompilerType type(valobj.GetCompilerType());
2957de855c6SEnrico Granata   ExecutionContext exe_ctx(target_sp, false);
296b9c1b51eSKate Stone   ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
297b9c1b51eSKate Stone       "string_ptr", pointer_value, exe_ctx, type));
2987de855c6SEnrico Granata   if (!child_ptr_sp)
2997de855c6SEnrico Granata     return false;
3007de855c6SEnrico Granata   DataExtractor data;
30197206d57SZachary Turner   Status error;
3027de855c6SEnrico Granata   child_ptr_sp->GetData(data, error);
3037de855c6SEnrico Granata   if (error.Fail())
3047de855c6SEnrico Granata     return false;
305b9c1b51eSKate Stone   ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
306b9c1b51eSKate Stone       "string_data", data, exe_ctx, type));
3077de855c6SEnrico Granata   child_sp->GetValueAsUnsigned(0);
3087de855c6SEnrico Granata   if (child_sp)
3097de855c6SEnrico Granata     return NSStringSummaryProvider(*child_sp, stream, options);
3107de855c6SEnrico Granata   return false;
3117de855c6SEnrico Granata }
3127de855c6SEnrico Granata 
NSMutableAttributedStringSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)313b9c1b51eSKate Stone bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
314b9c1b51eSKate Stone     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
3157de855c6SEnrico Granata   return NSAttributedStringSummaryProvider(valobj, stream, options);
3167de855c6SEnrico Granata }
3177de855c6SEnrico Granata 
NSTaggedString_SummaryProvider(ValueObject & valobj,ObjCLanguageRuntime::ClassDescriptorSP descriptor,Stream & stream,const TypeSummaryOptions & summary_options)318b9c1b51eSKate Stone bool lldb_private::formatters::NSTaggedString_SummaryProvider(
319b9c1b51eSKate Stone     ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
320b9c1b51eSKate Stone     Stream &stream, const TypeSummaryOptions &summary_options) {
321675f49bbSEnrico Granata   static ConstString g_TypeHint("NSString");
322675f49bbSEnrico Granata 
3237de855c6SEnrico Granata   if (!descriptor)
3247de855c6SEnrico Granata     return false;
3257de855c6SEnrico Granata   uint64_t len_bits = 0, data_bits = 0;
3267de855c6SEnrico Granata   if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
3277de855c6SEnrico Granata     return false;
3287de855c6SEnrico Granata 
3297de855c6SEnrico Granata   static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
3307de855c6SEnrico Granata   static const int g_SixbitMaxLen = 9;
3317de855c6SEnrico Granata   static const int g_fiveBitMaxLen = 11;
3327de855c6SEnrico Granata 
333b9c1b51eSKate Stone   static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
334b9c1b51eSKate Stone                                           "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
3357de855c6SEnrico Granata 
3367de855c6SEnrico Granata   if (len_bits > g_fiveBitMaxLen)
3377de855c6SEnrico Granata     return false;
3387de855c6SEnrico Granata 
339675f49bbSEnrico Granata   std::string prefix, suffix;
340b9c1b51eSKate Stone   if (Language *language =
341b9c1b51eSKate Stone           Language::FindPlugin(summary_options.GetLanguage())) {
342b9c1b51eSKate Stone     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
343b9c1b51eSKate Stone                                             suffix)) {
344675f49bbSEnrico Granata       prefix.clear();
345675f49bbSEnrico Granata       suffix.clear();
346675f49bbSEnrico Granata     }
347675f49bbSEnrico Granata   }
348675f49bbSEnrico Granata 
349b9c1b51eSKate Stone   // this is a fairly ugly trick - pretend that the numeric value is actually a
35005097246SAdrian Prantl   // char* this works under a few assumptions: little endian architecture
3517de855c6SEnrico Granata   // sizeof(uint64_t) > g_MaxNonBitmaskedLen
352b9c1b51eSKate Stone   if (len_bits <= g_MaxNonBitmaskedLen) {
353675f49bbSEnrico Granata     stream.Printf("%s", prefix.c_str());
354675f49bbSEnrico Granata     stream.Printf("\"%s\"", (const char *)&data_bits);
355675f49bbSEnrico Granata     stream.Printf("%s", suffix.c_str());
3567de855c6SEnrico Granata     return true;
3577de855c6SEnrico Granata   }
3587de855c6SEnrico Granata 
3597de855c6SEnrico Granata   // if the data is bitmasked, we need to actually process the bytes
3607de855c6SEnrico Granata   uint8_t bitmask = 0;
3617de855c6SEnrico Granata   uint8_t shift_offset = 0;
3627de855c6SEnrico Granata 
363b9c1b51eSKate Stone   if (len_bits <= g_SixbitMaxLen) {
3647de855c6SEnrico Granata     bitmask = 0x03f;
3657de855c6SEnrico Granata     shift_offset = 6;
366b9c1b51eSKate Stone   } else {
3677de855c6SEnrico Granata     bitmask = 0x01f;
3687de855c6SEnrico Granata     shift_offset = 5;
3697de855c6SEnrico Granata   }
3707de855c6SEnrico Granata 
3717de855c6SEnrico Granata   std::vector<uint8_t> bytes;
3727de855c6SEnrico Granata   bytes.resize(len_bits);
373b9c1b51eSKate Stone   for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
3747de855c6SEnrico Granata     uint8_t packed = data_bits & bitmask;
3757de855c6SEnrico Granata     bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
3767de855c6SEnrico Granata   }
3777de855c6SEnrico Granata 
378675f49bbSEnrico Granata   stream.Printf("%s", prefix.c_str());
379675f49bbSEnrico Granata   stream.Printf("\"%s\"", &bytes[0]);
380675f49bbSEnrico Granata   stream.Printf("%s", suffix.c_str());
3817de855c6SEnrico Granata   return true;
3827de855c6SEnrico Granata }
383