1dda28197Spatrick //===-- Cocoa.cpp ---------------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "Cocoa.h"
10*f6aab3d8Srobert #include "NSString.h"
11*f6aab3d8Srobert #include "ObjCConstants.h"
12061da546Spatrick
13*f6aab3d8Srobert #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
14dda28197Spatrick #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
15061da546Spatrick #include "lldb/Core/Mangled.h"
16061da546Spatrick #include "lldb/Core/ValueObject.h"
17061da546Spatrick #include "lldb/Core/ValueObjectConstResult.h"
18061da546Spatrick #include "lldb/DataFormatters/FormattersHelpers.h"
19061da546Spatrick #include "lldb/DataFormatters/StringPrinter.h"
20061da546Spatrick #include "lldb/DataFormatters/TypeSummary.h"
21061da546Spatrick #include "lldb/Host/Time.h"
22061da546Spatrick #include "lldb/Target/Language.h"
23061da546Spatrick #include "lldb/Target/Process.h"
24061da546Spatrick #include "lldb/Target/ProcessStructReader.h"
25061da546Spatrick #include "lldb/Target/Target.h"
26061da546Spatrick #include "lldb/Utility/DataBufferHeap.h"
27061da546Spatrick #include "lldb/Utility/Endian.h"
28*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
29061da546Spatrick #include "lldb/Utility/Status.h"
30061da546Spatrick #include "lldb/Utility/Stream.h"
31061da546Spatrick
32061da546Spatrick #include "llvm/ADT/APInt.h"
33061da546Spatrick #include "llvm/ADT/bit.h"
34061da546Spatrick
35061da546Spatrick
36061da546Spatrick using namespace lldb;
37061da546Spatrick using namespace lldb_private;
38061da546Spatrick using namespace lldb_private::formatters;
39061da546Spatrick
NSBundleSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)40061da546Spatrick bool lldb_private::formatters::NSBundleSummaryProvider(
41061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
42061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
43061da546Spatrick if (!process_sp)
44061da546Spatrick return false;
45061da546Spatrick
46061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
47061da546Spatrick
48061da546Spatrick if (!runtime)
49061da546Spatrick return false;
50061da546Spatrick
51061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
52061da546Spatrick runtime->GetClassDescriptor(valobj));
53061da546Spatrick
54061da546Spatrick if (!descriptor || !descriptor->IsValid())
55061da546Spatrick return false;
56061da546Spatrick
57061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
58061da546Spatrick
59061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
60061da546Spatrick
61061da546Spatrick if (!valobj_addr)
62061da546Spatrick return false;
63061da546Spatrick
64061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
65061da546Spatrick
66061da546Spatrick if (class_name.empty())
67061da546Spatrick return false;
68061da546Spatrick
69061da546Spatrick if (class_name == "NSBundle") {
70061da546Spatrick uint64_t offset = 5 * ptr_size;
71061da546Spatrick ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
72061da546Spatrick offset,
73061da546Spatrick valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),
74061da546Spatrick true));
75061da546Spatrick
76be691f3bSpatrick if (!text)
77be691f3bSpatrick return false;
78be691f3bSpatrick
79061da546Spatrick StreamString summary_stream;
80061da546Spatrick bool was_nsstring_ok =
81061da546Spatrick NSStringSummaryProvider(*text, summary_stream, options);
82061da546Spatrick if (was_nsstring_ok && summary_stream.GetSize() > 0) {
83061da546Spatrick stream.Printf("%s", summary_stream.GetData());
84061da546Spatrick return true;
85061da546Spatrick }
86061da546Spatrick }
87061da546Spatrick
88061da546Spatrick return false;
89061da546Spatrick }
90061da546Spatrick
NSTimeZoneSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)91061da546Spatrick bool lldb_private::formatters::NSTimeZoneSummaryProvider(
92061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
93061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
94061da546Spatrick if (!process_sp)
95061da546Spatrick return false;
96061da546Spatrick
97061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
98061da546Spatrick
99061da546Spatrick if (!runtime)
100061da546Spatrick return false;
101061da546Spatrick
102061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
103061da546Spatrick runtime->GetClassDescriptor(valobj));
104061da546Spatrick
105061da546Spatrick if (!descriptor || !descriptor->IsValid())
106061da546Spatrick return false;
107061da546Spatrick
108061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
109061da546Spatrick
110061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
111061da546Spatrick
112061da546Spatrick if (!valobj_addr)
113061da546Spatrick return false;
114061da546Spatrick
115061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
116061da546Spatrick
117061da546Spatrick if (class_name.empty())
118061da546Spatrick return false;
119061da546Spatrick
120061da546Spatrick if (class_name == "__NSTimeZone") {
121061da546Spatrick uint64_t offset = ptr_size;
122061da546Spatrick ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
123061da546Spatrick offset, valobj.GetCompilerType(), true));
124be691f3bSpatrick
125be691f3bSpatrick if (!text)
126be691f3bSpatrick return false;
127be691f3bSpatrick
128061da546Spatrick StreamString summary_stream;
129061da546Spatrick bool was_nsstring_ok =
130061da546Spatrick NSStringSummaryProvider(*text, summary_stream, options);
131061da546Spatrick if (was_nsstring_ok && summary_stream.GetSize() > 0) {
132061da546Spatrick stream.Printf("%s", summary_stream.GetData());
133061da546Spatrick return true;
134061da546Spatrick }
135061da546Spatrick }
136061da546Spatrick
137061da546Spatrick return false;
138061da546Spatrick }
139061da546Spatrick
NSNotificationSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)140061da546Spatrick bool lldb_private::formatters::NSNotificationSummaryProvider(
141061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
142061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
143061da546Spatrick if (!process_sp)
144061da546Spatrick return false;
145061da546Spatrick
146061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
147061da546Spatrick
148061da546Spatrick if (!runtime)
149061da546Spatrick return false;
150061da546Spatrick
151061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
152061da546Spatrick runtime->GetClassDescriptor(valobj));
153061da546Spatrick
154061da546Spatrick if (!descriptor || !descriptor->IsValid())
155061da546Spatrick return false;
156061da546Spatrick
157061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
158061da546Spatrick
159061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
160061da546Spatrick
161061da546Spatrick if (!valobj_addr)
162061da546Spatrick return false;
163061da546Spatrick
164061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
165061da546Spatrick
166061da546Spatrick if (class_name.empty())
167061da546Spatrick return false;
168061da546Spatrick
169061da546Spatrick if (class_name == "NSConcreteNotification") {
170061da546Spatrick uint64_t offset = ptr_size;
171061da546Spatrick ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
172061da546Spatrick offset, valobj.GetCompilerType(), true));
173be691f3bSpatrick
174be691f3bSpatrick if (!text)
175be691f3bSpatrick return false;
176be691f3bSpatrick
177061da546Spatrick StreamString summary_stream;
178061da546Spatrick bool was_nsstring_ok =
179061da546Spatrick NSStringSummaryProvider(*text, summary_stream, options);
180061da546Spatrick if (was_nsstring_ok && summary_stream.GetSize() > 0) {
181061da546Spatrick stream.Printf("%s", summary_stream.GetData());
182061da546Spatrick return true;
183061da546Spatrick }
184061da546Spatrick }
185061da546Spatrick
186061da546Spatrick return false;
187061da546Spatrick }
188061da546Spatrick
NSMachPortSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)189061da546Spatrick bool lldb_private::formatters::NSMachPortSummaryProvider(
190061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
191061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
192061da546Spatrick if (!process_sp)
193061da546Spatrick return false;
194061da546Spatrick
195061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
196061da546Spatrick
197061da546Spatrick if (!runtime)
198061da546Spatrick return false;
199061da546Spatrick
200061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
201061da546Spatrick runtime->GetClassDescriptor(valobj));
202061da546Spatrick
203061da546Spatrick if (!descriptor || !descriptor->IsValid())
204061da546Spatrick return false;
205061da546Spatrick
206061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
207061da546Spatrick
208061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
209061da546Spatrick
210061da546Spatrick if (!valobj_addr)
211061da546Spatrick return false;
212061da546Spatrick
213061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
214061da546Spatrick
215061da546Spatrick if (class_name.empty())
216061da546Spatrick return false;
217061da546Spatrick
218061da546Spatrick uint64_t port_number = 0;
219061da546Spatrick
220061da546Spatrick if (class_name == "NSMachPort") {
221061da546Spatrick uint64_t offset = (ptr_size == 4 ? 12 : 20);
222061da546Spatrick Status error;
223061da546Spatrick port_number = process_sp->ReadUnsignedIntegerFromMemory(
224061da546Spatrick offset + valobj_addr, 4, 0, error);
225061da546Spatrick if (error.Success()) {
226061da546Spatrick stream.Printf("mach port: %u",
227061da546Spatrick (uint32_t)(port_number & 0x00000000FFFFFFFF));
228061da546Spatrick return true;
229061da546Spatrick }
230061da546Spatrick }
231061da546Spatrick
232061da546Spatrick return false;
233061da546Spatrick }
234061da546Spatrick
NSIndexSetSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)235061da546Spatrick bool lldb_private::formatters::NSIndexSetSummaryProvider(
236061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
237061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
238061da546Spatrick if (!process_sp)
239061da546Spatrick return false;
240061da546Spatrick
241061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
242061da546Spatrick
243061da546Spatrick if (!runtime)
244061da546Spatrick return false;
245061da546Spatrick
246061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
247061da546Spatrick runtime->GetClassDescriptor(valobj));
248061da546Spatrick
249061da546Spatrick if (!descriptor || !descriptor->IsValid())
250061da546Spatrick return false;
251061da546Spatrick
252061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
253061da546Spatrick
254061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
255061da546Spatrick
256061da546Spatrick if (!valobj_addr)
257061da546Spatrick return false;
258061da546Spatrick
259061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
260061da546Spatrick
261061da546Spatrick if (class_name.empty())
262061da546Spatrick return false;
263061da546Spatrick
264061da546Spatrick uint64_t count = 0;
265061da546Spatrick
266061da546Spatrick do {
267061da546Spatrick if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") {
268061da546Spatrick Status error;
269061da546Spatrick uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(
270061da546Spatrick valobj_addr + ptr_size, 4, 0, error);
271061da546Spatrick if (error.Fail())
272061da546Spatrick return false;
273061da546Spatrick // this means the set is empty - count = 0
274061da546Spatrick if ((mode & 1) == 1) {
275061da546Spatrick count = 0;
276061da546Spatrick break;
277061da546Spatrick }
278061da546Spatrick if ((mode & 2) == 2)
279061da546Spatrick mode = 1; // this means the set only has one range
280061da546Spatrick else
281061da546Spatrick mode = 2; // this means the set has multiple ranges
282061da546Spatrick if (mode == 1) {
283061da546Spatrick count = process_sp->ReadUnsignedIntegerFromMemory(
284061da546Spatrick valobj_addr + 3 * ptr_size, ptr_size, 0, error);
285061da546Spatrick if (error.Fail())
286061da546Spatrick return false;
287061da546Spatrick } else {
288061da546Spatrick // read a pointer to the data at 2*ptr_size
289061da546Spatrick count = process_sp->ReadUnsignedIntegerFromMemory(
290061da546Spatrick valobj_addr + 2 * ptr_size, ptr_size, 0, error);
291061da546Spatrick if (error.Fail())
292061da546Spatrick return false;
293061da546Spatrick // read the data at 2*ptr_size from the first location
294061da546Spatrick count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size,
295061da546Spatrick ptr_size, 0, error);
296061da546Spatrick if (error.Fail())
297061da546Spatrick return false;
298061da546Spatrick }
299061da546Spatrick } else
300061da546Spatrick return false;
301061da546Spatrick } while (false);
302061da546Spatrick stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es"));
303061da546Spatrick return true;
304061da546Spatrick }
305061da546Spatrick
NSNumber_FormatChar(ValueObject & valobj,Stream & stream,char value,lldb::LanguageType lang)306061da546Spatrick static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value,
307061da546Spatrick lldb::LanguageType lang) {
308061da546Spatrick static ConstString g_TypeHint("NSNumber:char");
309061da546Spatrick
310061da546Spatrick std::string prefix, suffix;
311061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
312061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
313061da546Spatrick suffix)) {
314061da546Spatrick prefix.clear();
315061da546Spatrick suffix.clear();
316061da546Spatrick }
317061da546Spatrick }
318061da546Spatrick
319061da546Spatrick stream.Printf("%s%hhd%s", prefix.c_str(), value, suffix.c_str());
320061da546Spatrick }
321061da546Spatrick
NSNumber_FormatShort(ValueObject & valobj,Stream & stream,short value,lldb::LanguageType lang)322061da546Spatrick static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream,
323061da546Spatrick short value, lldb::LanguageType lang) {
324061da546Spatrick static ConstString g_TypeHint("NSNumber:short");
325061da546Spatrick
326061da546Spatrick std::string prefix, suffix;
327061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
328061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
329061da546Spatrick suffix)) {
330061da546Spatrick prefix.clear();
331061da546Spatrick suffix.clear();
332061da546Spatrick }
333061da546Spatrick }
334061da546Spatrick
335061da546Spatrick stream.Printf("%s%hd%s", prefix.c_str(), value, suffix.c_str());
336061da546Spatrick }
337061da546Spatrick
NSNumber_FormatInt(ValueObject & valobj,Stream & stream,int value,lldb::LanguageType lang)338061da546Spatrick static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value,
339061da546Spatrick lldb::LanguageType lang) {
340061da546Spatrick static ConstString g_TypeHint("NSNumber:int");
341061da546Spatrick
342061da546Spatrick std::string prefix, suffix;
343061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
344061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
345061da546Spatrick suffix)) {
346061da546Spatrick prefix.clear();
347061da546Spatrick suffix.clear();
348061da546Spatrick }
349061da546Spatrick }
350061da546Spatrick
351061da546Spatrick stream.Printf("%s%d%s", prefix.c_str(), value, suffix.c_str());
352061da546Spatrick }
353061da546Spatrick
NSNumber_FormatLong(ValueObject & valobj,Stream & stream,int64_t value,lldb::LanguageType lang)354061da546Spatrick static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream,
355be691f3bSpatrick int64_t value, lldb::LanguageType lang) {
356061da546Spatrick static ConstString g_TypeHint("NSNumber:long");
357061da546Spatrick
358061da546Spatrick std::string prefix, suffix;
359061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
360061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
361061da546Spatrick suffix)) {
362061da546Spatrick prefix.clear();
363061da546Spatrick suffix.clear();
364061da546Spatrick }
365061da546Spatrick }
366061da546Spatrick
367061da546Spatrick stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str());
368061da546Spatrick }
369061da546Spatrick
NSNumber_FormatInt128(ValueObject & valobj,Stream & stream,const llvm::APInt & value,lldb::LanguageType lang)370061da546Spatrick static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream,
371061da546Spatrick const llvm::APInt &value,
372061da546Spatrick lldb::LanguageType lang) {
373061da546Spatrick static ConstString g_TypeHint("NSNumber:int128_t");
374061da546Spatrick
375061da546Spatrick std::string prefix, suffix;
376061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
377061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
378061da546Spatrick suffix)) {
379061da546Spatrick prefix.clear();
380061da546Spatrick suffix.clear();
381061da546Spatrick }
382061da546Spatrick }
383061da546Spatrick
384061da546Spatrick stream.PutCString(prefix.c_str());
385061da546Spatrick const int radix = 10;
386061da546Spatrick const bool isSigned = true;
387be691f3bSpatrick std::string str = llvm::toString(value, radix, isSigned);
388061da546Spatrick stream.PutCString(str.c_str());
389061da546Spatrick stream.PutCString(suffix.c_str());
390061da546Spatrick }
391061da546Spatrick
NSNumber_FormatFloat(ValueObject & valobj,Stream & stream,float value,lldb::LanguageType lang)392061da546Spatrick static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream,
393061da546Spatrick float value, lldb::LanguageType lang) {
394061da546Spatrick static ConstString g_TypeHint("NSNumber:float");
395061da546Spatrick
396061da546Spatrick std::string prefix, suffix;
397061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
398061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
399061da546Spatrick suffix)) {
400061da546Spatrick prefix.clear();
401061da546Spatrick suffix.clear();
402061da546Spatrick }
403061da546Spatrick }
404061da546Spatrick
405061da546Spatrick stream.Printf("%s%f%s", prefix.c_str(), value, suffix.c_str());
406061da546Spatrick }
407061da546Spatrick
NSNumber_FormatDouble(ValueObject & valobj,Stream & stream,double value,lldb::LanguageType lang)408061da546Spatrick static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream,
409061da546Spatrick double value, lldb::LanguageType lang) {
410061da546Spatrick static ConstString g_TypeHint("NSNumber:double");
411061da546Spatrick
412061da546Spatrick std::string prefix, suffix;
413061da546Spatrick if (Language *language = Language::FindPlugin(lang)) {
414061da546Spatrick if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
415061da546Spatrick suffix)) {
416061da546Spatrick prefix.clear();
417061da546Spatrick suffix.clear();
418061da546Spatrick }
419061da546Spatrick }
420061da546Spatrick
421061da546Spatrick stream.Printf("%s%g%s", prefix.c_str(), value, suffix.c_str());
422061da546Spatrick }
423061da546Spatrick
NSNumberSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)424061da546Spatrick bool lldb_private::formatters::NSNumberSummaryProvider(
425061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
426061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
427061da546Spatrick if (!process_sp)
428061da546Spatrick return false;
429061da546Spatrick
430*f6aab3d8Srobert Log *log = GetLog(LLDBLog::DataFormatters);
431061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
432061da546Spatrick
433061da546Spatrick if (!runtime)
434061da546Spatrick return false;
435061da546Spatrick
436061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
437061da546Spatrick runtime->GetClassDescriptor(valobj));
438061da546Spatrick
439061da546Spatrick if (!descriptor || !descriptor->IsValid())
440061da546Spatrick return false;
441061da546Spatrick
442061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
443061da546Spatrick
444061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
445061da546Spatrick
446061da546Spatrick if (!valobj_addr)
447061da546Spatrick return false;
448061da546Spatrick
449061da546Spatrick llvm::StringRef class_name(descriptor->GetClassName().GetCString());
450061da546Spatrick
451061da546Spatrick if (class_name.empty())
452061da546Spatrick return false;
453061da546Spatrick
454061da546Spatrick if (class_name == "__NSCFBoolean")
455061da546Spatrick return ObjCBooleanSummaryProvider(valobj, stream, options);
456061da546Spatrick
457061da546Spatrick if (class_name == "NSDecimalNumber")
458061da546Spatrick return NSDecimalNumberSummaryProvider(valobj, stream, options);
459061da546Spatrick
460*f6aab3d8Srobert if (class_name == "NSConstantIntegerNumber") {
461*f6aab3d8Srobert Status error;
462*f6aab3d8Srobert int64_t value = process_sp->ReadSignedIntegerFromMemory(
463*f6aab3d8Srobert valobj_addr + 2 * ptr_size, 8, 0, error);
464*f6aab3d8Srobert if (error.Fail())
465*f6aab3d8Srobert return false;
466*f6aab3d8Srobert uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory(
467*f6aab3d8Srobert valobj_addr + ptr_size, ptr_size, 0, error);
468*f6aab3d8Srobert if (error.Fail())
469*f6aab3d8Srobert return false;
470*f6aab3d8Srobert char encoding =
471*f6aab3d8Srobert process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error);
472*f6aab3d8Srobert if (error.Fail())
473*f6aab3d8Srobert return false;
474*f6aab3d8Srobert
475*f6aab3d8Srobert switch (encoding) {
476*f6aab3d8Srobert case _C_CHR:
477*f6aab3d8Srobert NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
478*f6aab3d8Srobert return true;
479*f6aab3d8Srobert case _C_SHT:
480*f6aab3d8Srobert NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
481*f6aab3d8Srobert return true;
482*f6aab3d8Srobert case _C_INT:
483*f6aab3d8Srobert NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
484*f6aab3d8Srobert return true;
485*f6aab3d8Srobert case _C_LNG:
486*f6aab3d8Srobert case _C_LNG_LNG:
487*f6aab3d8Srobert NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
488*f6aab3d8Srobert return true;
489*f6aab3d8Srobert
490*f6aab3d8Srobert case _C_UCHR:
491*f6aab3d8Srobert case _C_USHT:
492*f6aab3d8Srobert case _C_UINT:
493*f6aab3d8Srobert case _C_ULNG:
494*f6aab3d8Srobert case _C_ULNG_LNG:
495*f6aab3d8Srobert stream.Printf("%" PRIu64, value);
496*f6aab3d8Srobert return true;
497*f6aab3d8Srobert }
498*f6aab3d8Srobert
499*f6aab3d8Srobert return false;
500*f6aab3d8Srobert }
501*f6aab3d8Srobert
502*f6aab3d8Srobert if (class_name == "NSConstantFloatNumber") {
503*f6aab3d8Srobert Status error;
504*f6aab3d8Srobert uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
505*f6aab3d8Srobert valobj_addr + ptr_size, 4, 0, error);
506*f6aab3d8Srobert if (error.Fail())
507*f6aab3d8Srobert return false;
508*f6aab3d8Srobert float flt_value = 0.0f;
509*f6aab3d8Srobert memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
510*f6aab3d8Srobert NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
511*f6aab3d8Srobert return true;
512*f6aab3d8Srobert }
513*f6aab3d8Srobert
514*f6aab3d8Srobert if (class_name == "NSConstantDoubleNumber") {
515*f6aab3d8Srobert Status error;
516*f6aab3d8Srobert uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
517*f6aab3d8Srobert valobj_addr + ptr_size, 8, 0, error);
518*f6aab3d8Srobert if (error.Fail())
519*f6aab3d8Srobert return false;
520*f6aab3d8Srobert double dbl_value = 0.0;
521*f6aab3d8Srobert memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
522*f6aab3d8Srobert NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
523*f6aab3d8Srobert return true;
524*f6aab3d8Srobert }
525*f6aab3d8Srobert
526061da546Spatrick if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
527be691f3bSpatrick int64_t value = 0;
528061da546Spatrick uint64_t i_bits = 0;
529be691f3bSpatrick if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) {
530be691f3bSpatrick // Check for "preserved" numbers. We still don't support them yet.
531be691f3bSpatrick if (i_bits & 0x8) {
532be691f3bSpatrick if (log)
533be691f3bSpatrick log->Printf(
534be691f3bSpatrick "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64,
535be691f3bSpatrick valobj_addr);
536be691f3bSpatrick return false;
537be691f3bSpatrick }
538be691f3bSpatrick
539061da546Spatrick switch (i_bits) {
540061da546Spatrick case 0:
541061da546Spatrick NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
542061da546Spatrick break;
543061da546Spatrick case 1:
544061da546Spatrick case 4:
545061da546Spatrick NSNumber_FormatShort(valobj, stream, (short)value,
546061da546Spatrick options.GetLanguage());
547061da546Spatrick break;
548061da546Spatrick case 2:
549061da546Spatrick case 8:
550061da546Spatrick NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
551061da546Spatrick break;
552061da546Spatrick case 3:
553061da546Spatrick case 12:
554061da546Spatrick NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
555061da546Spatrick break;
556061da546Spatrick default:
557061da546Spatrick return false;
558061da546Spatrick }
559061da546Spatrick return true;
560061da546Spatrick } else {
561061da546Spatrick Status error;
562061da546Spatrick
563061da546Spatrick AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
564061da546Spatrick ObjCLanguageRuntime::Get(*process_sp));
565061da546Spatrick
566061da546Spatrick const bool new_format =
567061da546Spatrick (runtime && runtime->GetFoundationVersion() >= 1400);
568061da546Spatrick
569061da546Spatrick enum class TypeCodes : int {
570061da546Spatrick sint8 = 0x0,
571061da546Spatrick sint16 = 0x1,
572061da546Spatrick sint32 = 0x2,
573061da546Spatrick sint64 = 0x3,
574061da546Spatrick f32 = 0x4,
575061da546Spatrick f64 = 0x5,
576061da546Spatrick sint128 = 0x6
577061da546Spatrick };
578061da546Spatrick
579061da546Spatrick uint64_t data_location = valobj_addr + 2 * ptr_size;
580061da546Spatrick TypeCodes type_code;
581061da546Spatrick
582061da546Spatrick if (new_format) {
583be691f3bSpatrick uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory(
584be691f3bSpatrick valobj_addr + ptr_size, ptr_size, 0, error);
585061da546Spatrick
586061da546Spatrick if (error.Fail())
587061da546Spatrick return false;
588061da546Spatrick
589061da546Spatrick bool is_preserved_number = cfinfoa & 0x8;
590061da546Spatrick if (is_preserved_number) {
591be691f3bSpatrick if (log)
592be691f3bSpatrick log->Printf(
593be691f3bSpatrick "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64,
594be691f3bSpatrick valobj_addr);
595061da546Spatrick return false;
596061da546Spatrick }
597061da546Spatrick
598061da546Spatrick type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
599061da546Spatrick } else {
600be691f3bSpatrick uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory(
601be691f3bSpatrick valobj_addr + ptr_size, 1, 0, error) &
602be691f3bSpatrick 0x1F;
603061da546Spatrick
604061da546Spatrick if (error.Fail())
605061da546Spatrick return false;
606061da546Spatrick
607061da546Spatrick switch (data_type) {
608be691f3bSpatrick case 1:
609be691f3bSpatrick type_code = TypeCodes::sint8;
610be691f3bSpatrick break;
611be691f3bSpatrick case 2:
612be691f3bSpatrick type_code = TypeCodes::sint16;
613be691f3bSpatrick break;
614be691f3bSpatrick case 3:
615be691f3bSpatrick type_code = TypeCodes::sint32;
616be691f3bSpatrick break;
617be691f3bSpatrick case 17:
618be691f3bSpatrick data_location += 8;
619*f6aab3d8Srobert [[fallthrough]];
620be691f3bSpatrick case 4:
621be691f3bSpatrick type_code = TypeCodes::sint64;
622be691f3bSpatrick break;
623be691f3bSpatrick case 5:
624be691f3bSpatrick type_code = TypeCodes::f32;
625be691f3bSpatrick break;
626be691f3bSpatrick case 6:
627be691f3bSpatrick type_code = TypeCodes::f64;
628be691f3bSpatrick break;
629be691f3bSpatrick default:
630be691f3bSpatrick return false;
631061da546Spatrick }
632061da546Spatrick }
633061da546Spatrick
634061da546Spatrick uint64_t value = 0;
635061da546Spatrick bool success = false;
636061da546Spatrick switch (type_code) {
637061da546Spatrick case TypeCodes::sint8:
638061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
639061da546Spatrick error);
640061da546Spatrick if (error.Fail())
641061da546Spatrick return false;
642061da546Spatrick NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
643061da546Spatrick success = true;
644061da546Spatrick break;
645061da546Spatrick case TypeCodes::sint16:
646061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
647061da546Spatrick error);
648061da546Spatrick if (error.Fail())
649061da546Spatrick return false;
650061da546Spatrick NSNumber_FormatShort(valobj, stream, (short)value,
651061da546Spatrick options.GetLanguage());
652061da546Spatrick success = true;
653061da546Spatrick break;
654061da546Spatrick case TypeCodes::sint32:
655061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
656061da546Spatrick error);
657061da546Spatrick if (error.Fail())
658061da546Spatrick return false;
659061da546Spatrick NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
660061da546Spatrick success = true;
661061da546Spatrick break;
662061da546Spatrick case TypeCodes::sint64:
663061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
664061da546Spatrick error);
665061da546Spatrick if (error.Fail())
666061da546Spatrick return false;
667061da546Spatrick NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
668061da546Spatrick success = true;
669061da546Spatrick break;
670be691f3bSpatrick case TypeCodes::f32: {
671061da546Spatrick uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
672061da546Spatrick data_location, 4, 0, error);
673061da546Spatrick if (error.Fail())
674061da546Spatrick return false;
675061da546Spatrick float flt_value = 0.0f;
676061da546Spatrick memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
677061da546Spatrick NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
678061da546Spatrick success = true;
679061da546Spatrick break;
680061da546Spatrick }
681be691f3bSpatrick case TypeCodes::f64: {
682061da546Spatrick uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
683061da546Spatrick data_location, 8, 0, error);
684061da546Spatrick if (error.Fail())
685061da546Spatrick return false;
686061da546Spatrick double dbl_value = 0.0;
687061da546Spatrick memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
688061da546Spatrick NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
689061da546Spatrick success = true;
690061da546Spatrick break;
691061da546Spatrick }
692061da546Spatrick case TypeCodes::sint128: // internally, this is the same
693061da546Spatrick {
694061da546Spatrick uint64_t words[2];
695be691f3bSpatrick words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8,
696be691f3bSpatrick 0, error);
697061da546Spatrick if (error.Fail())
698061da546Spatrick return false;
699be691f3bSpatrick words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8,
700be691f3bSpatrick 8, 0, error);
701061da546Spatrick if (error.Fail())
702061da546Spatrick return false;
703061da546Spatrick llvm::APInt i128_value(128, words);
704be691f3bSpatrick NSNumber_FormatInt128(valobj, stream, i128_value,
705be691f3bSpatrick options.GetLanguage());
706061da546Spatrick success = true;
707061da546Spatrick break;
708061da546Spatrick }
709061da546Spatrick }
710061da546Spatrick return success;
711061da546Spatrick }
712061da546Spatrick }
713061da546Spatrick
714061da546Spatrick return false;
715061da546Spatrick }
716061da546Spatrick
NSDecimalNumberSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)717061da546Spatrick bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
718061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
719061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
720061da546Spatrick if (!process_sp)
721061da546Spatrick return false;
722061da546Spatrick
723061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
724061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
725061da546Spatrick
726061da546Spatrick Status error;
727061da546Spatrick int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
728061da546Spatrick valobj_addr + ptr_size, 1, 0, error);
729061da546Spatrick if (error.Fail())
730061da546Spatrick return false;
731061da546Spatrick
732061da546Spatrick uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
733061da546Spatrick valobj_addr + ptr_size + 1, 1, 0, error);
734061da546Spatrick if (error.Fail())
735061da546Spatrick return false;
736061da546Spatrick
737061da546Spatrick // Fifth bit marks negativity.
738061da546Spatrick const bool is_negative = (length_and_negative >> 4) & 1;
739061da546Spatrick
740061da546Spatrick // Zero length and negative means NaN.
741061da546Spatrick uint8_t length = length_and_negative & 0xf;
742061da546Spatrick const bool is_nan = is_negative && (length == 0);
743061da546Spatrick
744061da546Spatrick if (is_nan) {
745061da546Spatrick stream.Printf("NaN");
746061da546Spatrick return true;
747061da546Spatrick }
748061da546Spatrick
749061da546Spatrick if (length == 0) {
750061da546Spatrick stream.Printf("0");
751061da546Spatrick return true;
752061da546Spatrick }
753061da546Spatrick
754061da546Spatrick uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
755061da546Spatrick valobj_addr + ptr_size + 4, 8, 0, error);
756061da546Spatrick if (error.Fail())
757061da546Spatrick return false;
758061da546Spatrick
759061da546Spatrick if (is_negative)
760061da546Spatrick stream.Printf("-");
761061da546Spatrick
762061da546Spatrick stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
763061da546Spatrick return true;
764061da546Spatrick }
765061da546Spatrick
NSURLSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)766061da546Spatrick bool lldb_private::formatters::NSURLSummaryProvider(
767061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
768061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
769061da546Spatrick if (!process_sp)
770061da546Spatrick return false;
771061da546Spatrick
772061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
773061da546Spatrick
774061da546Spatrick if (!runtime)
775061da546Spatrick return false;
776061da546Spatrick
777061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
778061da546Spatrick runtime->GetClassDescriptor(valobj));
779061da546Spatrick
780061da546Spatrick if (!descriptor || !descriptor->IsValid())
781061da546Spatrick return false;
782061da546Spatrick
783061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
784061da546Spatrick
785061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
786061da546Spatrick
787061da546Spatrick if (!valobj_addr)
788061da546Spatrick return false;
789061da546Spatrick
790061da546Spatrick llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
791061da546Spatrick
792061da546Spatrick if (!class_name.equals("NSURL"))
793061da546Spatrick return false;
794061da546Spatrick
795061da546Spatrick uint64_t offset_text = ptr_size + ptr_size +
796061da546Spatrick 8; // ISA + pointer + 8 bytes of data (even on 32bit)
797061da546Spatrick uint64_t offset_base = offset_text + ptr_size;
798061da546Spatrick CompilerType type(valobj.GetCompilerType());
799061da546Spatrick ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
800061da546Spatrick ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
801061da546Spatrick if (!text || text->GetValueAsUnsigned(0) == 0)
802061da546Spatrick return false;
803061da546Spatrick
804061da546Spatrick StreamString base_summary;
805061da546Spatrick if (base && base->GetValueAsUnsigned(0)) {
806061da546Spatrick if (!NSURLSummaryProvider(*base, base_summary, options))
807061da546Spatrick base_summary.Clear();
808061da546Spatrick }
809061da546Spatrick if (base_summary.Empty())
810061da546Spatrick return NSStringSummaryProvider(*text, stream, options);
811061da546Spatrick
812061da546Spatrick StreamString summary;
813061da546Spatrick if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty())
814061da546Spatrick return false;
815061da546Spatrick
816061da546Spatrick const char quote_char = '"';
817061da546Spatrick std::string prefix, suffix;
818061da546Spatrick if (Language *language = Language::FindPlugin(options.GetLanguage())) {
819061da546Spatrick if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"),
820061da546Spatrick prefix, suffix)) {
821061da546Spatrick prefix.clear();
822061da546Spatrick suffix.clear();
823061da546Spatrick }
824061da546Spatrick }
825061da546Spatrick // @"A" -> @"A
826061da546Spatrick llvm::StringRef summary_str = summary.GetString();
827061da546Spatrick bool back_consumed = summary_str.consume_back(quote_char + suffix);
828061da546Spatrick assert(back_consumed);
829061da546Spatrick UNUSED_IF_ASSERT_DISABLED(back_consumed);
830061da546Spatrick // @"B" -> B"
831061da546Spatrick llvm::StringRef base_summary_str = base_summary.GetString();
832061da546Spatrick bool front_consumed = base_summary_str.consume_front(prefix + quote_char);
833061da546Spatrick assert(front_consumed);
834061da546Spatrick UNUSED_IF_ASSERT_DISABLED(front_consumed);
835061da546Spatrick // @"A -- B"
836061da546Spatrick if (!summary_str.empty() && !base_summary_str.empty()) {
837061da546Spatrick stream.Printf("%s -- %s", summary_str.str().c_str(),
838061da546Spatrick base_summary_str.str().c_str());
839061da546Spatrick return true;
840061da546Spatrick }
841061da546Spatrick
842061da546Spatrick return false;
843061da546Spatrick }
844061da546Spatrick
845061da546Spatrick /// Bias value for tagged pointer exponents.
846061da546Spatrick /// Recommended values:
847061da546Spatrick /// 0x3e3: encodes all dates between distantPast and distantFuture
848061da546Spatrick /// except for the range within about 1e-28 second of the reference date.
849061da546Spatrick /// 0x3ef: encodes all dates for a few million years beyond distantPast and
850061da546Spatrick /// distantFuture, except within about 1e-25 second of the reference date.
851061da546Spatrick const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
852061da546Spatrick
853061da546Spatrick struct DoubleBits {
854061da546Spatrick uint64_t fraction : 52; // unsigned
855061da546Spatrick uint64_t exponent : 11; // signed
856061da546Spatrick uint64_t sign : 1;
857061da546Spatrick };
858061da546Spatrick
859061da546Spatrick struct TaggedDoubleBits {
860061da546Spatrick uint64_t fraction : 52; // unsigned
861061da546Spatrick uint64_t exponent : 7; // signed
862061da546Spatrick uint64_t sign : 1;
863061da546Spatrick uint64_t unused : 4; // placeholder for pointer tag bits
864061da546Spatrick };
865061da546Spatrick
decodeExponent(uint64_t exp)866061da546Spatrick static uint64_t decodeExponent(uint64_t exp) {
867061da546Spatrick // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
868061da546Spatrick // before performing arithmetic.
869061da546Spatrick return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
870061da546Spatrick }
871061da546Spatrick
decodeTaggedTimeInterval(uint64_t encodedTimeInterval)872061da546Spatrick static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
873061da546Spatrick if (encodedTimeInterval == 0)
874061da546Spatrick return 0.0;
875061da546Spatrick if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
876061da546Spatrick return (uint64_t)-0.0;
877061da546Spatrick
878061da546Spatrick TaggedDoubleBits encodedBits =
879061da546Spatrick llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
880061da546Spatrick assert(encodedBits.unused == 0);
881061da546Spatrick
882061da546Spatrick // Sign and fraction are represented exactly.
883061da546Spatrick // Exponent is encoded.
884061da546Spatrick DoubleBits decodedBits;
885061da546Spatrick decodedBits.sign = encodedBits.sign;
886061da546Spatrick decodedBits.fraction = encodedBits.fraction;
887061da546Spatrick decodedBits.exponent = decodeExponent(encodedBits.exponent);
888061da546Spatrick
889061da546Spatrick return llvm::bit_cast<double>(decodedBits);
890061da546Spatrick }
891061da546Spatrick
NSDateSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)892061da546Spatrick bool lldb_private::formatters::NSDateSummaryProvider(
893061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
894061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
895061da546Spatrick if (!process_sp)
896061da546Spatrick return false;
897061da546Spatrick
898061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
899061da546Spatrick
900061da546Spatrick if (!runtime)
901061da546Spatrick return false;
902061da546Spatrick
903061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
904061da546Spatrick runtime->GetClassDescriptor(valobj));
905061da546Spatrick
906061da546Spatrick if (!descriptor || !descriptor->IsValid())
907061da546Spatrick return false;
908061da546Spatrick
909061da546Spatrick uint32_t ptr_size = process_sp->GetAddressByteSize();
910061da546Spatrick
911061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
912061da546Spatrick
913061da546Spatrick if (!valobj_addr)
914061da546Spatrick return false;
915061da546Spatrick
916061da546Spatrick uint64_t date_value_bits = 0;
917061da546Spatrick double date_value = 0.0;
918061da546Spatrick
919061da546Spatrick ConstString class_name = descriptor->GetClassName();
920061da546Spatrick
921061da546Spatrick static const ConstString g_NSDate("NSDate");
922*f6aab3d8Srobert static const ConstString g_dunder_NSDate("__NSDate");
923*f6aab3d8Srobert static const ConstString g_NSTaggedDate("__NSTaggedDate");
924061da546Spatrick static const ConstString g_NSCalendarDate("NSCalendarDate");
925dda28197Spatrick static const ConstString g_NSConstantDate("NSConstantDate");
926061da546Spatrick
927061da546Spatrick if (class_name.IsEmpty())
928061da546Spatrick return false;
929061da546Spatrick
930061da546Spatrick uint64_t info_bits = 0, value_bits = 0;
931*f6aab3d8Srobert if ((class_name == g_NSDate) || (class_name == g_dunder_NSDate) ||
932*f6aab3d8Srobert (class_name == g_NSTaggedDate) || (class_name == g_NSConstantDate)) {
933061da546Spatrick if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
934061da546Spatrick date_value_bits = ((value_bits << 8) | (info_bits << 4));
935061da546Spatrick memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
936061da546Spatrick } else {
937061da546Spatrick llvm::Triple triple(
938061da546Spatrick process_sp->GetTarget().GetArchitecture().GetTriple());
939061da546Spatrick uint32_t delta =
940061da546Spatrick (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
941061da546Spatrick Status error;
942061da546Spatrick date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
943061da546Spatrick valobj_addr + delta, 8, 0, error);
944061da546Spatrick memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
945061da546Spatrick if (error.Fail())
946061da546Spatrick return false;
947061da546Spatrick }
948061da546Spatrick } else if (class_name == g_NSCalendarDate) {
949061da546Spatrick Status error;
950061da546Spatrick date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
951061da546Spatrick valobj_addr + 2 * ptr_size, 8, 0, error);
952061da546Spatrick memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
953061da546Spatrick if (error.Fail())
954061da546Spatrick return false;
955061da546Spatrick } else
956061da546Spatrick return false;
957061da546Spatrick
958dda28197Spatrick // FIXME: It seems old dates are not formatted according to NSDate's calendar
959dda28197Spatrick // so we hardcode distantPast's value so that it looks like LLDB is doing
960dda28197Spatrick // the right thing.
961dda28197Spatrick
962dda28197Spatrick // The relative time in seconds from Cocoa Epoch to [NSDate distantPast].
963dda28197Spatrick const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800;
964dda28197Spatrick if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) {
965dda28197Spatrick stream.Printf("0001-01-01 00:00:00 UTC");
966061da546Spatrick return true;
967061da546Spatrick }
968061da546Spatrick
969061da546Spatrick // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
970*f6aab3d8Srobert if (class_name == g_NSTaggedDate) {
971061da546Spatrick auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
972061da546Spatrick ObjCLanguageRuntime::Get(*process_sp));
973061da546Spatrick if (runtime && runtime->GetFoundationVersion() >= 1600)
974061da546Spatrick date_value = decodeTaggedTimeInterval(value_bits << 4);
975061da546Spatrick }
976061da546Spatrick
977061da546Spatrick // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
978061da546Spatrick // is generally true and POSIXly happy, but might break if a library vendor
979061da546Spatrick // decides to get creative
980061da546Spatrick time_t epoch = GetOSXEpoch();
981dda28197Spatrick epoch = epoch + static_cast<time_t>(std::floor(date_value));
982061da546Spatrick tm *tm_date = gmtime(&epoch);
983061da546Spatrick if (!tm_date)
984061da546Spatrick return false;
985061da546Spatrick std::string buffer(1024, 0);
986061da546Spatrick if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
987061da546Spatrick return false;
988061da546Spatrick stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
989061da546Spatrick tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
990061da546Spatrick tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
991061da546Spatrick return true;
992061da546Spatrick }
993061da546Spatrick
ObjCClassSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)994061da546Spatrick bool lldb_private::formatters::ObjCClassSummaryProvider(
995061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
996061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
997061da546Spatrick if (!process_sp)
998061da546Spatrick return false;
999061da546Spatrick
1000061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1001061da546Spatrick
1002061da546Spatrick if (!runtime)
1003061da546Spatrick return false;
1004061da546Spatrick
1005061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1006061da546Spatrick runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
1007061da546Spatrick
1008061da546Spatrick if (!descriptor || !descriptor->IsValid())
1009061da546Spatrick return false;
1010061da546Spatrick
1011061da546Spatrick ConstString class_name = descriptor->GetClassName();
1012061da546Spatrick
1013061da546Spatrick if (class_name.IsEmpty())
1014061da546Spatrick return false;
1015061da546Spatrick
1016dda28197Spatrick if (ConstString cs = Mangled(class_name).GetDemangledName())
1017061da546Spatrick class_name = cs;
1018061da546Spatrick
1019061da546Spatrick stream.Printf("%s", class_name.AsCString("<unknown class>"));
1020061da546Spatrick return true;
1021061da546Spatrick }
1022061da546Spatrick
1023061da546Spatrick class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
1024061da546Spatrick public:
ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)1025061da546Spatrick ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
1026061da546Spatrick : SyntheticChildrenFrontEnd(*valobj_sp) {}
1027061da546Spatrick
1028061da546Spatrick ~ObjCClassSyntheticChildrenFrontEnd() override = default;
1029061da546Spatrick
CalculateNumChildren()1030061da546Spatrick size_t CalculateNumChildren() override { return 0; }
1031061da546Spatrick
GetChildAtIndex(size_t idx)1032061da546Spatrick lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
1033061da546Spatrick return lldb::ValueObjectSP();
1034061da546Spatrick }
1035061da546Spatrick
Update()1036061da546Spatrick bool Update() override { return false; }
1037061da546Spatrick
MightHaveChildren()1038061da546Spatrick bool MightHaveChildren() override { return false; }
1039061da546Spatrick
GetIndexOfChildWithName(ConstString name)1040061da546Spatrick size_t GetIndexOfChildWithName(ConstString name) override {
1041061da546Spatrick return UINT32_MAX;
1042061da546Spatrick }
1043061da546Spatrick };
1044061da546Spatrick
1045061da546Spatrick SyntheticChildrenFrontEnd *
ObjCClassSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)1046061da546Spatrick lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
1047061da546Spatrick CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
1048061da546Spatrick return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
1049061da546Spatrick }
1050061da546Spatrick
1051061da546Spatrick template <bool needs_at>
NSDataSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)1052061da546Spatrick bool lldb_private::formatters::NSDataSummaryProvider(
1053061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1054061da546Spatrick ProcessSP process_sp = valobj.GetProcessSP();
1055061da546Spatrick if (!process_sp)
1056061da546Spatrick return false;
1057061da546Spatrick
1058061da546Spatrick ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1059061da546Spatrick
1060061da546Spatrick if (!runtime)
1061061da546Spatrick return false;
1062061da546Spatrick
1063061da546Spatrick ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1064061da546Spatrick runtime->GetClassDescriptor(valobj));
1065061da546Spatrick
1066061da546Spatrick if (!descriptor || !descriptor->IsValid())
1067061da546Spatrick return false;
1068061da546Spatrick
1069061da546Spatrick bool is_64bit = (process_sp->GetAddressByteSize() == 8);
1070061da546Spatrick lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
1071061da546Spatrick
1072061da546Spatrick if (!valobj_addr)
1073061da546Spatrick return false;
1074061da546Spatrick
1075061da546Spatrick uint64_t value = 0;
1076061da546Spatrick
1077061da546Spatrick llvm::StringRef class_name = descriptor->GetClassName().GetCString();
1078061da546Spatrick
1079061da546Spatrick if (class_name.empty())
1080061da546Spatrick return false;
1081061da546Spatrick
1082061da546Spatrick bool isNSConcreteData = class_name == "NSConcreteData";
1083061da546Spatrick bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";
1084061da546Spatrick bool isNSCFData = class_name == "__NSCFData";
1085061da546Spatrick if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {
1086061da546Spatrick uint32_t offset;
1087061da546Spatrick if (isNSConcreteData)
1088061da546Spatrick offset = is_64bit ? 8 : 4;
1089061da546Spatrick else
1090061da546Spatrick offset = is_64bit ? 16 : 8;
1091061da546Spatrick
1092061da546Spatrick Status error;
1093061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(
1094061da546Spatrick valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
1095061da546Spatrick if (error.Fail())
1096061da546Spatrick return false;
1097061da546Spatrick } else if (class_name == "_NSInlineData") {
1098061da546Spatrick uint32_t offset = (is_64bit ? 8 : 4);
1099061da546Spatrick Status error;
1100061da546Spatrick value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,
1101061da546Spatrick 0, error);
1102061da546Spatrick if (error.Fail())
1103061da546Spatrick return false;
1104061da546Spatrick } else if (class_name == "_NSZeroData") {
1105061da546Spatrick value = 0;
1106061da546Spatrick } else
1107061da546Spatrick return false;
1108061da546Spatrick
1109061da546Spatrick stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,
1110061da546Spatrick (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));
1111061da546Spatrick
1112061da546Spatrick return true;
1113061da546Spatrick }
1114061da546Spatrick
ObjCBOOLSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)1115061da546Spatrick bool lldb_private::formatters::ObjCBOOLSummaryProvider(
1116061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1117061da546Spatrick const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
1118061da546Spatrick
1119061da546Spatrick ValueObjectSP real_guy_sp = valobj.GetSP();
1120061da546Spatrick
1121061da546Spatrick if (type_info & eTypeIsPointer) {
1122061da546Spatrick Status err;
1123061da546Spatrick real_guy_sp = valobj.Dereference(err);
1124061da546Spatrick if (err.Fail() || !real_guy_sp)
1125061da546Spatrick return false;
1126061da546Spatrick } else if (type_info & eTypeIsReference) {
1127061da546Spatrick real_guy_sp = valobj.GetChildAtIndex(0, true);
1128061da546Spatrick if (!real_guy_sp)
1129061da546Spatrick return false;
1130061da546Spatrick }
1131be691f3bSpatrick int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF);
1132061da546Spatrick switch (value) {
1133061da546Spatrick case 0:
1134061da546Spatrick stream.Printf("NO");
1135061da546Spatrick break;
1136061da546Spatrick case 1:
1137061da546Spatrick stream.Printf("YES");
1138061da546Spatrick break;
1139061da546Spatrick default:
1140be691f3bSpatrick stream.Printf("%d", value);
1141061da546Spatrick break;
1142061da546Spatrick }
1143061da546Spatrick return true;
1144061da546Spatrick }
1145061da546Spatrick
ObjCBooleanSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)1146061da546Spatrick bool lldb_private::formatters::ObjCBooleanSummaryProvider(
1147061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1148061da546Spatrick lldb::addr_t valobj_ptr_value =
1149061da546Spatrick valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1150061da546Spatrick if (valobj_ptr_value == LLDB_INVALID_ADDRESS)
1151061da546Spatrick return false;
1152061da546Spatrick
1153061da546Spatrick ProcessSP process_sp(valobj.GetProcessSP());
1154061da546Spatrick if (!process_sp)
1155061da546Spatrick return false;
1156061da546Spatrick
1157061da546Spatrick if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
1158061da546Spatrick ObjCLanguageRuntime::Get(*process_sp))) {
1159061da546Spatrick lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,
1160061da546Spatrick cf_false = LLDB_INVALID_ADDRESS;
1161061da546Spatrick objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);
1162061da546Spatrick if (valobj_ptr_value == cf_true) {
1163061da546Spatrick stream.PutCString("YES");
1164061da546Spatrick return true;
1165061da546Spatrick }
1166061da546Spatrick if (valobj_ptr_value == cf_false) {
1167061da546Spatrick stream.PutCString("NO");
1168061da546Spatrick return true;
1169061da546Spatrick }
1170061da546Spatrick }
1171061da546Spatrick
1172061da546Spatrick return false;
1173061da546Spatrick }
1174061da546Spatrick
1175061da546Spatrick template <bool is_sel_ptr>
ObjCSELSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)1176061da546Spatrick bool lldb_private::formatters::ObjCSELSummaryProvider(
1177061da546Spatrick ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1178061da546Spatrick lldb::ValueObjectSP valobj_sp;
1179061da546Spatrick
1180061da546Spatrick CompilerType charstar(valobj.GetCompilerType()
1181061da546Spatrick .GetBasicTypeFromAST(eBasicTypeChar)
1182061da546Spatrick .GetPointerType());
1183061da546Spatrick
1184061da546Spatrick if (!charstar)
1185061da546Spatrick return false;
1186061da546Spatrick
1187061da546Spatrick ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
1188061da546Spatrick
1189061da546Spatrick if (is_sel_ptr) {
1190061da546Spatrick lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1191061da546Spatrick if (data_address == LLDB_INVALID_ADDRESS)
1192061da546Spatrick return false;
1193061da546Spatrick valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,
1194061da546Spatrick exe_ctx, charstar);
1195061da546Spatrick } else {
1196061da546Spatrick DataExtractor data;
1197061da546Spatrick Status error;
1198061da546Spatrick valobj.GetData(data, error);
1199061da546Spatrick if (error.Fail())
1200061da546Spatrick return false;
1201061da546Spatrick valobj_sp =
1202061da546Spatrick ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
1203061da546Spatrick }
1204061da546Spatrick
1205061da546Spatrick if (!valobj_sp)
1206061da546Spatrick return false;
1207061da546Spatrick
1208061da546Spatrick stream.Printf("%s", valobj_sp->GetSummaryAsCString());
1209061da546Spatrick return true;
1210061da546Spatrick }
1211061da546Spatrick
1212061da546Spatrick // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
1213061da546Spatrick // this call gives the POSIX equivalent of the Cocoa epoch
GetOSXEpoch()1214061da546Spatrick time_t lldb_private::formatters::GetOSXEpoch() {
1215061da546Spatrick static time_t epoch = 0;
1216061da546Spatrick if (!epoch) {
1217061da546Spatrick #ifndef _WIN32
1218061da546Spatrick tzset();
1219061da546Spatrick tm tm_epoch;
1220061da546Spatrick tm_epoch.tm_sec = 0;
1221061da546Spatrick tm_epoch.tm_hour = 0;
1222061da546Spatrick tm_epoch.tm_min = 0;
1223061da546Spatrick tm_epoch.tm_mon = 0;
1224061da546Spatrick tm_epoch.tm_mday = 1;
1225061da546Spatrick tm_epoch.tm_year = 2001 - 1900;
1226061da546Spatrick tm_epoch.tm_isdst = -1;
1227061da546Spatrick tm_epoch.tm_gmtoff = 0;
1228061da546Spatrick tm_epoch.tm_zone = nullptr;
1229061da546Spatrick epoch = timegm(&tm_epoch);
1230061da546Spatrick #endif
1231061da546Spatrick }
1232061da546Spatrick return epoch;
1233061da546Spatrick }
1234061da546Spatrick
1235061da546Spatrick template bool lldb_private::formatters::NSDataSummaryProvider<true>(
1236061da546Spatrick ValueObject &, Stream &, const TypeSummaryOptions &);
1237061da546Spatrick
1238061da546Spatrick template bool lldb_private::formatters::NSDataSummaryProvider<false>(
1239061da546Spatrick ValueObject &, Stream &, const TypeSummaryOptions &);
1240061da546Spatrick
1241061da546Spatrick template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
1242061da546Spatrick ValueObject &, Stream &, const TypeSummaryOptions &);
1243061da546Spatrick
1244061da546Spatrick template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
1245061da546Spatrick ValueObject &, Stream &, const TypeSummaryOptions &);
1246