1 //===-- NSString.cpp ------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include "lldb/Core/ValueObject.h"
12 #include "lldb/Core/ValueObjectConstResult.h"
13 #include "lldb/DataFormatters/FormattersHelpers.h"
14 #include "lldb/DataFormatters/StringPrinter.h"
15 #include "lldb/Target/Language.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/ConstString.h"
18 #include "lldb/Utility/DataBufferHeap.h"
19 #include "lldb/Utility/Endian.h"
20 #include "lldb/Utility/Status.h"
21 #include "lldb/Utility/Stream.h"
24 using namespace lldb_private
;
25 using namespace lldb_private::formatters
;
27 std::map
<ConstString
, CXXFunctionSummaryFormat::Callback
> &
28 NSString_Additionals::GetAdditionalSummaries() {
29 static std::map
<ConstString
, CXXFunctionSummaryFormat::Callback
> g_map
;
33 bool lldb_private::formatters::NSStringSummaryProvider(
34 ValueObject
&valobj
, Stream
&stream
,
35 const TypeSummaryOptions
&summary_options
) {
36 static constexpr llvm::StringLiteral
g_TypeHint("NSString");
38 ProcessSP process_sp
= valobj
.GetProcessSP();
42 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
47 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
48 runtime
->GetClassDescriptor(valobj
));
50 if (!descriptor
.get() || !descriptor
->IsValid())
53 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
55 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
60 ConstString class_name_cs
= descriptor
->GetClassName();
61 llvm::StringRef class_name
= class_name_cs
.GetStringRef();
63 if (class_name
.empty())
66 bool is_tagged_ptr
= class_name
== "NSTaggedPointerString" &&
67 descriptor
->GetTaggedPointerInfo();
68 // for a tagged pointer, the descriptor has everything we need
70 return NSTaggedString_SummaryProvider(valobj
, descriptor
, stream
,
73 auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
74 auto iter
= additionals_map
.find(class_name_cs
), end
= additionals_map
.end();
76 return iter
->second(valobj
, stream
, summary_options
);
78 // if not a tagged pointer that we know about, try the normal route
79 uint64_t info_bits_location
= valobj_addr
+ ptr_size
;
80 if (process_sp
->GetByteOrder() != lldb::eByteOrderLittle
)
81 info_bits_location
+= 3;
85 uint8_t info_bits
= process_sp
->ReadUnsignedIntegerFromMemory(
86 info_bits_location
, 1, 0, error
);
90 bool is_mutable
= (info_bits
& 1) == 1;
91 bool is_inline
= (info_bits
& 0x60) == 0;
92 bool has_explicit_length
= (info_bits
& (1 | 4)) != 4;
93 bool is_unicode
= (info_bits
& 0x10) == 0x10;
94 bool is_path_store
= class_name
== "NSPathStore2";
95 bool has_null
= (info_bits
& 8) == 8;
97 size_t explicit_length
= 0;
98 if (!has_null
&& has_explicit_length
&& !is_path_store
) {
99 lldb::addr_t explicit_length_offset
= 2 * ptr_size
;
100 if (is_mutable
&& !is_inline
)
101 explicit_length_offset
=
102 explicit_length_offset
+ ptr_size
; // notInlineMutable.length;
104 explicit_length
= explicit_length
+ 0; // inline1.length;
105 else if (!is_inline
&& !is_mutable
)
106 explicit_length_offset
=
107 explicit_length_offset
+ ptr_size
; // notInlineImmutable1.length;
109 explicit_length_offset
= 0;
111 if (explicit_length_offset
) {
112 explicit_length_offset
= valobj_addr
+ explicit_length_offset
;
113 explicit_length
= process_sp
->ReadUnsignedIntegerFromMemory(
114 explicit_length_offset
, 4, 0, error
);
118 const llvm::StringSet
<> supported_string_classes
= {
119 "NSString", "CFMutableStringRef",
120 "CFStringRef", "__NSCFConstantString",
121 "__NSCFString", "NSCFConstantString",
122 "NSCFString", "NSPathStore2"};
123 if (supported_string_classes
.count(class_name
) == 0) {
124 // not one of us - but tell me class name
125 stream
.Printf("class name = %s", class_name_cs
.GetCString());
129 llvm::StringRef prefix
, suffix
;
130 if (Language
*language
= Language::FindPlugin(summary_options
.GetLanguage()))
131 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
133 StringPrinter::ReadStringAndDumpToStreamOptions
options(valobj
);
134 options
.SetPrefixToken(prefix
.str());
135 options
.SetSuffixToken(suffix
.str());
138 uint64_t location
= 2 * ptr_size
+ valobj_addr
;
139 location
= process_sp
->ReadPointerFromMemory(location
, error
);
142 if (has_explicit_length
&& is_unicode
) {
143 options
.SetLocation(location
);
144 options
.SetTargetSP(valobj
.GetTargetSP());
145 options
.SetStream(&stream
);
146 options
.SetQuote('"');
147 options
.SetSourceSize(explicit_length
);
148 options
.SetHasSourceSize(has_explicit_length
);
149 options
.SetNeedsZeroTermination(false);
150 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
151 TypeSummaryCapping::eTypeSummaryUncapped
);
152 options
.SetBinaryZeroIsTerminator(false);
153 return StringPrinter::ReadStringAndDumpToStream
<
154 StringPrinter::StringElementType::UTF16
>(options
);
156 options
.SetLocation(location
+ 1);
157 options
.SetTargetSP(valobj
.GetTargetSP());
158 options
.SetStream(&stream
);
159 options
.SetSourceSize(explicit_length
);
160 options
.SetHasSourceSize(has_explicit_length
);
161 options
.SetNeedsZeroTermination(false);
162 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
163 TypeSummaryCapping::eTypeSummaryUncapped
);
164 options
.SetBinaryZeroIsTerminator(false);
165 return StringPrinter::ReadStringAndDumpToStream
<
166 StringPrinter::StringElementType::ASCII
>(options
);
168 } else if (is_inline
&& has_explicit_length
&& !is_unicode
&&
169 !is_path_store
&& !is_mutable
) {
170 uint64_t location
= 3 * ptr_size
+ valobj_addr
;
172 options
.SetLocation(location
);
173 options
.SetTargetSP(valobj
.GetTargetSP());
174 options
.SetStream(&stream
);
175 options
.SetQuote('"');
176 options
.SetSourceSize(explicit_length
);
177 options
.SetHasSourceSize(has_explicit_length
);
178 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
179 TypeSummaryCapping::eTypeSummaryUncapped
);
180 return StringPrinter::ReadStringAndDumpToStream
<
181 StringPrinter::StringElementType::ASCII
>(options
);
182 } else if (is_unicode
) {
183 uint64_t location
= valobj_addr
+ 2 * ptr_size
;
185 if (!has_explicit_length
) {
188 location
+= ptr_size
;
190 location
= process_sp
->ReadPointerFromMemory(location
, error
);
194 options
.SetLocation(location
);
195 options
.SetTargetSP(valobj
.GetTargetSP());
196 options
.SetStream(&stream
);
197 options
.SetQuote('"');
198 options
.SetSourceSize(explicit_length
);
199 options
.SetHasSourceSize(has_explicit_length
);
200 options
.SetNeedsZeroTermination(!has_explicit_length
);
201 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
202 TypeSummaryCapping::eTypeSummaryUncapped
);
203 options
.SetBinaryZeroIsTerminator(!has_explicit_length
);
204 return StringPrinter::ReadStringAndDumpToStream
<
205 StringPrinter::StringElementType::UTF16
>(options
);
206 } else if (is_path_store
) {
207 // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
208 uint64_t length_ivar_offset
= 1 * ptr_size
;
209 CompilerType length_type
= valobj
.GetCompilerType().GetBasicTypeFromAST(
210 lldb::eBasicTypeUnsignedInt
);
211 ValueObjectSP length_valobj_sp
=
212 valobj
.GetSyntheticChildAtOffset(length_ivar_offset
, length_type
, true,
213 ConstString("_lengthAndRefCount"));
214 if (!length_valobj_sp
)
216 // Get the length out of _lengthAndRefCount.
217 explicit_length
= length_valobj_sp
->GetValueAsUnsigned(0) >> 20;
218 lldb::addr_t location
= valobj
.GetValueAsUnsigned(0) + ptr_size
+ 4;
220 options
.SetLocation(location
);
221 options
.SetTargetSP(valobj
.GetTargetSP());
222 options
.SetStream(&stream
);
223 options
.SetQuote('"');
224 options
.SetSourceSize(explicit_length
);
225 options
.SetHasSourceSize(has_explicit_length
);
226 options
.SetNeedsZeroTermination(!has_explicit_length
);
227 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
228 TypeSummaryCapping::eTypeSummaryUncapped
);
229 options
.SetBinaryZeroIsTerminator(!has_explicit_length
);
230 return StringPrinter::ReadStringAndDumpToStream
<
231 StringPrinter::StringElementType::UTF16
>(options
);
232 } else if (is_inline
) {
233 uint64_t location
= valobj_addr
+ 2 * ptr_size
;
234 if (!has_explicit_length
) {
235 // in this kind of string, the byte before the string content is a length
236 // byte so let's try and use it to handle the embedded NUL case
239 process_sp
->ReadUnsignedIntegerFromMemory(location
, 1, 0, error
);
240 has_explicit_length
= !(error
.Fail() || explicit_length
== 0);
243 options
.SetLocation(location
);
244 options
.SetTargetSP(valobj
.GetTargetSP());
245 options
.SetStream(&stream
);
246 options
.SetSourceSize(explicit_length
);
247 options
.SetHasSourceSize(has_explicit_length
);
248 options
.SetNeedsZeroTermination(!has_explicit_length
);
249 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
250 TypeSummaryCapping::eTypeSummaryUncapped
);
251 options
.SetBinaryZeroIsTerminator(!has_explicit_length
);
252 if (has_explicit_length
)
253 return StringPrinter::ReadStringAndDumpToStream
<
254 StringPrinter::StringElementType::UTF8
>(options
);
256 return StringPrinter::ReadStringAndDumpToStream
<
257 StringPrinter::StringElementType::ASCII
>(options
);
259 uint64_t location
= valobj_addr
+ 2 * ptr_size
;
260 location
= process_sp
->ReadPointerFromMemory(location
, error
);
263 if (has_explicit_length
&& !has_null
)
264 explicit_length
++; // account for the fact that there is no NULL and we
265 // need to have one added
266 options
.SetLocation(location
);
267 options
.SetTargetSP(valobj
.GetTargetSP());
268 options
.SetStream(&stream
);
269 options
.SetSourceSize(explicit_length
);
270 options
.SetHasSourceSize(has_explicit_length
);
271 options
.SetIgnoreMaxLength(summary_options
.GetCapping() ==
272 TypeSummaryCapping::eTypeSummaryUncapped
);
273 return StringPrinter::ReadStringAndDumpToStream
<
274 StringPrinter::StringElementType::ASCII
>(options
);
278 bool lldb_private::formatters::NSAttributedStringSummaryProvider(
279 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
280 TargetSP
target_sp(valobj
.GetTargetSP());
283 uint32_t addr_size
= target_sp
->GetArchitecture().GetAddressByteSize();
284 uint64_t pointer_value
= valobj
.GetValueAsUnsigned(0);
287 pointer_value
+= addr_size
;
288 CompilerType
type(valobj
.GetCompilerType());
289 ExecutionContext
exe_ctx(target_sp
, false);
290 ValueObjectSP
child_ptr_sp(valobj
.CreateValueObjectFromAddress(
291 "string_ptr", pointer_value
, exe_ctx
, type
));
296 child_ptr_sp
->GetData(data
, error
);
299 ValueObjectSP
child_sp(child_ptr_sp
->CreateValueObjectFromData(
300 "string_data", data
, exe_ctx
, type
));
301 child_sp
->GetValueAsUnsigned(0);
303 return NSStringSummaryProvider(*child_sp
, stream
, options
);
307 bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
308 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
309 return NSAttributedStringSummaryProvider(valobj
, stream
, options
);
312 bool lldb_private::formatters::NSTaggedString_SummaryProvider(
313 ValueObject
&valobj
, ObjCLanguageRuntime::ClassDescriptorSP descriptor
,
314 Stream
&stream
, const TypeSummaryOptions
&summary_options
) {
315 static constexpr llvm::StringLiteral
g_TypeHint("NSString");
319 uint64_t len_bits
= 0, data_bits
= 0;
320 if (!descriptor
->GetTaggedPointerInfo(&len_bits
, &data_bits
, nullptr))
323 static const int g_MaxNonBitmaskedLen
= 7; // TAGGED_STRING_UNPACKED_MAXLEN
324 static const int g_SixbitMaxLen
= 9;
325 static const int g_fiveBitMaxLen
= 11;
327 static const char *sixBitToCharLookup
= "eilotrm.apdnsIc ufkMShjTRxgC4013"
328 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
330 if (len_bits
> g_fiveBitMaxLen
)
333 llvm::StringRef prefix
, suffix
;
334 if (Language
*language
= Language::FindPlugin(summary_options
.GetLanguage()))
335 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
337 // this is a fairly ugly trick - pretend that the numeric value is actually a
338 // char* this works under a few assumptions: little endian architecture
339 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
340 if (len_bits
<= g_MaxNonBitmaskedLen
) {
342 stream
.Printf("\"%s\"", (const char *)&data_bits
);
347 // if the data is bitmasked, we need to actually process the bytes
349 uint8_t shift_offset
= 0;
351 if (len_bits
<= g_SixbitMaxLen
) {
359 std::vector
<uint8_t> bytes
;
360 bytes
.resize(len_bits
);
361 for (; len_bits
> 0; data_bits
>>= shift_offset
, --len_bits
) {
362 uint8_t packed
= data_bits
& bitmask
;
363 bytes
.insert(bytes
.begin(), sixBitToCharLookup
[packed
]);
367 stream
.Printf("\"%s\"", &bytes
[0]);