1 //===-- NSArray.cpp ---------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "clang/AST/ASTContext.h"
14 
15 // Project includes
16 #include "Cocoa.h"
17 
18 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
19 #include "lldb/Core/DataBufferHeap.h"
20 #include "lldb/Core/Error.h"
21 #include "lldb/Core/Stream.h"
22 #include "lldb/Core/ValueObject.h"
23 #include "lldb/Core/ValueObjectConstResult.h"
24 #include "lldb/DataFormatters/FormattersHelpers.h"
25 #include "lldb/Expression/FunctionCaller.h"
26 #include "lldb/Host/Endian.h"
27 #include "lldb/Symbol/ClangASTContext.h"
28 #include "lldb/Target/Language.h"
29 #include "lldb/Target/ObjCLanguageRuntime.h"
30 #include "lldb/Target/Target.h"
31 
32 using namespace lldb;
33 using namespace lldb_private;
34 using namespace lldb_private::formatters;
35 
36 namespace lldb_private {
37 namespace formatters {
38 std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
39 NSArray_Additionals::GetAdditionalSummaries() {
40   static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
41   return g_map;
42 }
43 
44 std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
45 NSArray_Additionals::GetAdditionalSynthetics() {
46   static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
47       g_map;
48   return g_map;
49 }
50 
51 class NSArrayMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
52 public:
53   NSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
54 
55   ~NSArrayMSyntheticFrontEnd() override = default;
56 
57   size_t CalculateNumChildren() override;
58 
59   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
60 
61   bool Update() override = 0;
62 
63   bool MightHaveChildren() override;
64 
65   size_t GetIndexOfChildWithName(const ConstString &name) override;
66 
67 protected:
68   virtual lldb::addr_t GetDataAddress() = 0;
69 
70   virtual uint64_t GetUsedCount() = 0;
71 
72   virtual uint64_t GetOffset() = 0;
73 
74   virtual uint64_t GetSize() = 0;
75 
76   ExecutionContextRef m_exe_ctx_ref;
77   uint8_t m_ptr_size;
78   CompilerType m_id_type;
79 };
80 
81 class NSArrayMSyntheticFrontEnd_109 : public NSArrayMSyntheticFrontEnd {
82 public:
83   NSArrayMSyntheticFrontEnd_109(lldb::ValueObjectSP valobj_sp);
84 
85   ~NSArrayMSyntheticFrontEnd_109() override;
86 
87   bool Update() override;
88 
89 protected:
90   lldb::addr_t GetDataAddress() override;
91 
92   uint64_t GetUsedCount() override;
93 
94   uint64_t GetOffset() override;
95 
96   uint64_t GetSize() override;
97 
98 private:
99   struct DataDescriptor_32 {
100     uint32_t _used;
101     uint32_t _priv1 : 2;
102     uint32_t _size : 30;
103     uint32_t _priv2 : 2;
104     uint32_t _offset : 30;
105     uint32_t _priv3;
106     uint32_t _data;
107   };
108 
109   struct DataDescriptor_64 {
110     uint64_t _used;
111     uint64_t _priv1 : 2;
112     uint64_t _size : 62;
113     uint64_t _priv2 : 2;
114     uint64_t _offset : 62;
115     uint32_t _priv3;
116     uint64_t _data;
117   };
118 
119   DataDescriptor_32 *m_data_32;
120   DataDescriptor_64 *m_data_64;
121 };
122 
123 class NSArrayMSyntheticFrontEnd_1010 : public NSArrayMSyntheticFrontEnd {
124 public:
125   NSArrayMSyntheticFrontEnd_1010(lldb::ValueObjectSP valobj_sp);
126 
127   ~NSArrayMSyntheticFrontEnd_1010() override;
128 
129   bool Update() override;
130 
131 protected:
132   lldb::addr_t GetDataAddress() override;
133 
134   uint64_t GetUsedCount() override;
135 
136   uint64_t GetOffset() override;
137 
138   uint64_t GetSize() override;
139 
140 private:
141   struct DataDescriptor_32 {
142     uint32_t _used;
143     uint32_t _offset;
144     uint32_t _size : 28;
145     uint64_t _priv1 : 4;
146     uint32_t _priv2;
147     uint32_t _data;
148   };
149 
150   struct DataDescriptor_64 {
151     uint64_t _used;
152     uint64_t _offset;
153     uint64_t _size : 60;
154     uint64_t _priv1 : 4;
155     uint32_t _priv2;
156     uint64_t _data;
157   };
158 
159   DataDescriptor_32 *m_data_32;
160   DataDescriptor_64 *m_data_64;
161 };
162 
163 class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
164 public:
165   NSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
166 
167   ~NSArrayISyntheticFrontEnd() override = default;
168 
169   size_t CalculateNumChildren() override;
170 
171   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
172 
173   bool Update() override;
174 
175   bool MightHaveChildren() override;
176 
177   size_t GetIndexOfChildWithName(const ConstString &name) override;
178 
179 private:
180   ExecutionContextRef m_exe_ctx_ref;
181   uint8_t m_ptr_size;
182   uint64_t m_items;
183   lldb::addr_t m_data_ptr;
184   CompilerType m_id_type;
185 };
186 
187 class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
188 public:
189   NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
190 
191   ~NSArray0SyntheticFrontEnd() override = default;
192 
193   size_t CalculateNumChildren() override;
194 
195   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
196 
197   bool Update() override;
198 
199   bool MightHaveChildren() override;
200 
201   size_t GetIndexOfChildWithName(const ConstString &name) override;
202 };
203 
204 class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
205 public:
206   NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
207 
208   ~NSArray1SyntheticFrontEnd() override = default;
209 
210   size_t CalculateNumChildren() override;
211 
212   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
213 
214   bool Update() override;
215 
216   bool MightHaveChildren() override;
217 
218   size_t GetIndexOfChildWithName(const ConstString &name) override;
219 };
220 } // namespace formatters
221 } // namespace lldb_private
222 
223 bool lldb_private::formatters::NSArraySummaryProvider(
224     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
225   static ConstString g_TypeHint("NSArray");
226 
227   ProcessSP process_sp = valobj.GetProcessSP();
228   if (!process_sp)
229     return false;
230 
231   ObjCLanguageRuntime *runtime =
232       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
233           lldb::eLanguageTypeObjC);
234 
235   if (!runtime)
236     return false;
237 
238   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
239       runtime->GetClassDescriptor(valobj));
240 
241   if (!descriptor || !descriptor->IsValid())
242     return false;
243 
244   uint32_t ptr_size = process_sp->GetAddressByteSize();
245 
246   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
247 
248   if (!valobj_addr)
249     return false;
250 
251   uint64_t value = 0;
252 
253   ConstString class_name(descriptor->GetClassName());
254 
255   static const ConstString g_NSArrayI("__NSArrayI");
256   static const ConstString g_NSArrayM("__NSArrayM");
257   static const ConstString g_NSArray0("__NSArray0");
258   static const ConstString g_NSArray1("__NSSingleObjectArrayI");
259   static const ConstString g_NSArrayCF("__NSCFArray");
260 
261   if (class_name.IsEmpty())
262     return false;
263 
264   if (class_name == g_NSArrayI) {
265     Error error;
266     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
267                                                       ptr_size, 0, error);
268     if (error.Fail())
269       return false;
270   } else if (class_name == g_NSArrayM) {
271     Error error;
272     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
273                                                       ptr_size, 0, error);
274     if (error.Fail())
275       return false;
276   } else if (class_name == g_NSArray0) {
277     value = 0;
278   } else if (class_name == g_NSArray1) {
279     value = 1;
280   } else if (class_name == g_NSArrayCF) {
281     Error error;
282     value = process_sp->ReadUnsignedIntegerFromMemory(
283         valobj_addr + 2 * ptr_size, ptr_size, 0, error);
284     if (error.Fail())
285       return false;
286   } else {
287     auto &map(NSArray_Additionals::GetAdditionalSummaries());
288     auto iter = map.find(class_name), end = map.end();
289     if (iter != end)
290       return iter->second(valobj, stream, options);
291     else
292       return false;
293   }
294 
295   std::string prefix, suffix;
296   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
297     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
298                                             suffix)) {
299       prefix.clear();
300       suffix.clear();
301     }
302   }
303 
304   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
305                 value == 1 ? "" : "s", suffix.c_str());
306   return true;
307 }
308 
309 lldb_private::formatters::NSArrayMSyntheticFrontEnd::NSArrayMSyntheticFrontEnd(
310     lldb::ValueObjectSP valobj_sp)
311     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
312       m_id_type() {
313   if (valobj_sp) {
314     clang::ASTContext *ast = valobj_sp->GetExecutionContextRef()
315                                  .GetTargetSP()
316                                  ->GetScratchClangASTContext()
317                                  ->getASTContext();
318     if (ast)
319       m_id_type = CompilerType(ast, ast->ObjCBuiltinIdTy);
320     if (valobj_sp->GetProcessSP())
321       m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize();
322   }
323 }
324 
325 lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::
326     NSArrayMSyntheticFrontEnd_109(lldb::ValueObjectSP valobj_sp)
327     : NSArrayMSyntheticFrontEnd(valobj_sp), m_data_32(nullptr),
328       m_data_64(nullptr) {}
329 
330 lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::
331     NSArrayMSyntheticFrontEnd_1010(lldb::ValueObjectSP valobj_sp)
332     : NSArrayMSyntheticFrontEnd(valobj_sp), m_data_32(nullptr),
333       m_data_64(nullptr) {}
334 
335 size_t
336 lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren() {
337   return GetUsedCount();
338 }
339 
340 lldb::ValueObjectSP
341 lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex(
342     size_t idx) {
343   if (idx >= CalculateNumChildren())
344     return lldb::ValueObjectSP();
345   lldb::addr_t object_at_idx = GetDataAddress();
346   size_t pyhs_idx = idx;
347   pyhs_idx += GetOffset();
348   if (GetSize() <= pyhs_idx)
349     pyhs_idx -= GetSize();
350   object_at_idx += (pyhs_idx * m_ptr_size);
351   StreamString idx_name;
352   idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
353   return CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx,
354                                       m_exe_ctx_ref, m_id_type);
355 }
356 
357 bool lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::Update() {
358   ValueObjectSP valobj_sp = m_backend.GetSP();
359   m_ptr_size = 0;
360   delete m_data_32;
361   m_data_32 = nullptr;
362   delete m_data_64;
363   m_data_64 = nullptr;
364   if (!valobj_sp)
365     return false;
366   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
367   Error error;
368   error.Clear();
369   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
370   if (!process_sp)
371     return false;
372   m_ptr_size = process_sp->GetAddressByteSize();
373   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
374   if (m_ptr_size == 4) {
375     m_data_32 = new DataDescriptor_32();
376     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
377                            error);
378   } else {
379     m_data_64 = new DataDescriptor_64();
380     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
381                            error);
382   }
383   if (error.Fail())
384     return false;
385   return false;
386 }
387 
388 bool lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::Update() {
389   ValueObjectSP valobj_sp = m_backend.GetSP();
390   m_ptr_size = 0;
391   delete m_data_32;
392   m_data_32 = nullptr;
393   delete m_data_64;
394   m_data_64 = nullptr;
395   if (!valobj_sp)
396     return false;
397   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
398   Error error;
399   error.Clear();
400   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
401   if (!process_sp)
402     return false;
403   m_ptr_size = process_sp->GetAddressByteSize();
404   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
405   if (m_ptr_size == 4) {
406     m_data_32 = new DataDescriptor_32();
407     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
408                            error);
409   } else {
410     m_data_64 = new DataDescriptor_64();
411     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
412                            error);
413   }
414   if (error.Fail())
415     return false;
416   return false;
417 }
418 
419 bool lldb_private::formatters::NSArrayMSyntheticFrontEnd::MightHaveChildren() {
420   return true;
421 }
422 
423 size_t
424 lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName(
425     const ConstString &name) {
426   const char *item_name = name.GetCString();
427   uint32_t idx = ExtractIndexFromString(item_name);
428   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
429     return UINT32_MAX;
430   return idx;
431 }
432 
433 lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::
434     ~NSArrayMSyntheticFrontEnd_109() {
435   delete m_data_32;
436   m_data_32 = nullptr;
437   delete m_data_64;
438   m_data_64 = nullptr;
439 }
440 
441 lldb::addr_t
442 lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetDataAddress() {
443   if (!m_data_32 && !m_data_64)
444     return LLDB_INVALID_ADDRESS;
445   return m_data_32 ? m_data_32->_data : m_data_64->_data;
446 }
447 
448 uint64_t
449 lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetUsedCount() {
450   if (!m_data_32 && !m_data_64)
451     return 0;
452   return m_data_32 ? m_data_32->_used : m_data_64->_used;
453 }
454 
455 uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetOffset() {
456   if (!m_data_32 && !m_data_64)
457     return 0;
458   return m_data_32 ? m_data_32->_offset : m_data_64->_offset;
459 }
460 
461 uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_109::GetSize() {
462   if (!m_data_32 && !m_data_64)
463     return 0;
464   return m_data_32 ? m_data_32->_size : m_data_64->_size;
465 }
466 
467 lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::
468     ~NSArrayMSyntheticFrontEnd_1010() {
469   delete m_data_32;
470   m_data_32 = nullptr;
471   delete m_data_64;
472   m_data_64 = nullptr;
473 }
474 
475 lldb::addr_t
476 lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetDataAddress() {
477   if (!m_data_32 && !m_data_64)
478     return LLDB_INVALID_ADDRESS;
479   return m_data_32 ? m_data_32->_data : m_data_64->_data;
480 }
481 
482 uint64_t
483 lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetUsedCount() {
484   if (!m_data_32 && !m_data_64)
485     return 0;
486   return m_data_32 ? m_data_32->_used : m_data_64->_used;
487 }
488 
489 uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetOffset() {
490   if (!m_data_32 && !m_data_64)
491     return 0;
492   return m_data_32 ? m_data_32->_offset : m_data_64->_offset;
493 }
494 
495 uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetSize() {
496   if (!m_data_32 && !m_data_64)
497     return 0;
498   return m_data_32 ? m_data_32->_size : m_data_64->_size;
499 }
500 
501 lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd(
502     lldb::ValueObjectSP valobj_sp)
503     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
504       m_items(0), m_data_ptr(0) {
505   if (valobj_sp) {
506     CompilerType type = valobj_sp->GetCompilerType();
507     if (type) {
508       ClangASTContext *ast = valobj_sp->GetExecutionContextRef()
509                                  .GetTargetSP()
510                                  ->GetScratchClangASTContext();
511       if (ast)
512         m_id_type = CompilerType(ast->getASTContext(),
513                                  ast->getASTContext()->ObjCBuiltinIdTy);
514     }
515   }
516 }
517 
518 size_t
519 lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName(
520     const ConstString &name) {
521   const char *item_name = name.GetCString();
522   uint32_t idx = ExtractIndexFromString(item_name);
523   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
524     return UINT32_MAX;
525   return idx;
526 }
527 
528 size_t
529 lldb_private::formatters::NSArrayISyntheticFrontEnd::CalculateNumChildren() {
530   return m_items;
531 }
532 
533 bool lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() {
534   m_ptr_size = 0;
535   m_items = 0;
536   m_data_ptr = 0;
537   ValueObjectSP valobj_sp = m_backend.GetSP();
538   if (!valobj_sp)
539     return false;
540   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
541   Error error;
542   error.Clear();
543   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
544   if (!process_sp)
545     return false;
546   m_ptr_size = process_sp->GetAddressByteSize();
547   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
548   m_items = process_sp->ReadPointerFromMemory(data_location, error);
549   if (error.Fail())
550     return false;
551   m_data_ptr = data_location + m_ptr_size;
552   return false;
553 }
554 
555 bool lldb_private::formatters::NSArrayISyntheticFrontEnd::MightHaveChildren() {
556   return true;
557 }
558 
559 lldb::ValueObjectSP
560 lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex(
561     size_t idx) {
562   if (idx >= CalculateNumChildren())
563     return lldb::ValueObjectSP();
564   lldb::addr_t object_at_idx = m_data_ptr;
565   object_at_idx += (idx * m_ptr_size);
566   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
567   if (!process_sp)
568     return lldb::ValueObjectSP();
569   Error error;
570   if (error.Fail())
571     return lldb::ValueObjectSP();
572   StreamString idx_name;
573   idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
574   return CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx,
575                                       m_exe_ctx_ref, m_id_type);
576 }
577 
578 lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd(
579     lldb::ValueObjectSP valobj_sp)
580     : SyntheticChildrenFrontEnd(*valobj_sp) {}
581 
582 size_t
583 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName(
584     const ConstString &name) {
585   return UINT32_MAX;
586 }
587 
588 size_t
589 lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() {
590   return 0;
591 }
592 
593 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() {
594   return false;
595 }
596 
597 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() {
598   return false;
599 }
600 
601 lldb::ValueObjectSP
602 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex(
603     size_t idx) {
604   return lldb::ValueObjectSP();
605 }
606 
607 lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd(
608     lldb::ValueObjectSP valobj_sp)
609     : SyntheticChildrenFrontEnd(*valobj_sp.get()) {}
610 
611 size_t
612 lldb_private::formatters::NSArray1SyntheticFrontEnd::GetIndexOfChildWithName(
613     const ConstString &name) {
614   static const ConstString g_zero("[0]");
615 
616   if (name == g_zero)
617     return 0;
618 
619   return UINT32_MAX;
620 }
621 
622 size_t
623 lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() {
624   return 1;
625 }
626 
627 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() {
628   return false;
629 }
630 
631 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() {
632   return true;
633 }
634 
635 lldb::ValueObjectSP
636 lldb_private::formatters::NSArray1SyntheticFrontEnd::GetChildAtIndex(
637     size_t idx) {
638   static const ConstString g_zero("[0]");
639 
640   if (idx == 0) {
641     CompilerType id_type(
642         m_backend.GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
643             lldb::eBasicTypeObjCID));
644     return m_backend.GetSyntheticChildAtOffset(
645         m_backend.GetProcessSP()->GetAddressByteSize(), id_type, true, g_zero);
646   }
647   return lldb::ValueObjectSP();
648 }
649 
650 SyntheticChildrenFrontEnd *
651 lldb_private::formatters::NSArraySyntheticFrontEndCreator(
652     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
653   if (!valobj_sp)
654     return nullptr;
655 
656   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
657   if (!process_sp)
658     return nullptr;
659   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
660       process_sp->GetObjCLanguageRuntime());
661   if (!runtime)
662     return nullptr;
663 
664   CompilerType valobj_type(valobj_sp->GetCompilerType());
665   Flags flags(valobj_type.GetTypeInfo());
666 
667   if (flags.IsClear(eTypeIsPointer)) {
668     Error error;
669     valobj_sp = valobj_sp->AddressOf(error);
670     if (error.Fail() || !valobj_sp)
671       return nullptr;
672   }
673 
674   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
675       runtime->GetClassDescriptor(*valobj_sp));
676 
677   if (!descriptor || !descriptor->IsValid())
678     return nullptr;
679 
680   ConstString class_name(descriptor->GetClassName());
681 
682   static const ConstString g_NSArrayI("__NSArrayI");
683   static const ConstString g_NSArrayM("__NSArrayM");
684   static const ConstString g_NSArray0("__NSArray0");
685   static const ConstString g_NSArray1("__NSSingleObjectArrayI");
686 
687   if (class_name.IsEmpty())
688     return nullptr;
689 
690   if (class_name == g_NSArrayI) {
691     return (new NSArrayISyntheticFrontEnd(valobj_sp));
692   } else if (class_name == g_NSArray0) {
693     return (new NSArray0SyntheticFrontEnd(valobj_sp));
694   } else if (class_name == g_NSArray1) {
695     return (new NSArray1SyntheticFrontEnd(valobj_sp));
696   } else if (class_name == g_NSArrayM) {
697     if (runtime->GetFoundationVersion() >= 1100)
698       return (new NSArrayMSyntheticFrontEnd_1010(valobj_sp));
699     else
700       return (new NSArrayMSyntheticFrontEnd_109(valobj_sp));
701   } else {
702     auto &map(NSArray_Additionals::GetAdditionalSynthetics());
703     auto iter = map.find(class_name), end = map.end();
704     if (iter != end)
705       return iter->second(synth, valobj_sp);
706   }
707 
708   return nullptr;
709 }
710