1 //===-- LibCxx.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 #include "LibCxx.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Debugger.h"
17 #include "lldb/Core/FormatEntity.h"
18 #include "lldb/Core/ValueObject.h"
19 #include "lldb/Core/ValueObjectConstResult.h"
20 #include "lldb/DataFormatters/FormattersHelpers.h"
21 #include "lldb/DataFormatters/StringPrinter.h"
22 #include "lldb/DataFormatters/TypeSummary.h"
23 #include "lldb/DataFormatters/VectorIterator.h"
24 #include "lldb/Symbol/ClangASTContext.h"
25 #include "lldb/Target/ProcessStructReader.h"
26 #include "lldb/Target/Target.h"
27 #include "lldb/Utility/DataBufferHeap.h"
28 #include "lldb/Utility/Endian.h"
29 #include "lldb/Utility/Status.h"
30 #include "lldb/Utility/Stream.h"
31 
32 using namespace lldb;
33 using namespace lldb_private;
34 using namespace lldb_private::formatters;
35 
36 bool lldb_private::formatters::LibcxxOptionalSummaryProvider(
37     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
38   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
39   if (!valobj_sp)
40     return false;
41 
42   // An optional either contains a value or not, the member __engaged_ is
43   // a bool flag, it is true if the optional has a value and false otherwise.
44   ValueObjectSP engaged_sp(
45       valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true));
46 
47   if (!engaged_sp)
48     return false;
49 
50   llvm::StringRef engaged_as_cstring(
51       engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false");
52 
53   stream.Printf(" Has Value=%s ", engaged_as_cstring.data());
54 
55   return true;
56 }
57 
58 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
59     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
60   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
61   if (!valobj_sp)
62     return false;
63   ValueObjectSP ptr_sp(
64       valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
65   ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath(
66       {ConstString("__cntrl_"), ConstString("__shared_owners_")}));
67   ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath(
68       {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")}));
69 
70   if (!ptr_sp)
71     return false;
72 
73   if (ptr_sp->GetValueAsUnsigned(0) == 0) {
74     stream.Printf("nullptr");
75     return true;
76   } else {
77     bool print_pointee = false;
78     Status error;
79     ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
80     if (pointee_sp && error.Success()) {
81       if (pointee_sp->DumpPrintableRepresentation(
82               stream, ValueObject::eValueObjectRepresentationStyleSummary,
83               lldb::eFormatInvalid,
84               ValueObject::PrintableRepresentationSpecialCases::eDisable,
85               false))
86         print_pointee = true;
87     }
88     if (!print_pointee)
89       stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
90   }
91 
92   if (count_sp)
93     stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
94 
95   if (weakcount_sp)
96     stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
97 
98   return true;
99 }
100 
101 /*
102  (lldb) fr var ibeg --raw --ptr-depth 1
103  (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
104  std::__1::basic_string<char, std::__1::char_traits<char>,
105  std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
106  std::__1::basic_string<char, std::__1::char_traits<char>,
107  std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
108  __i_ = {
109  __ptr_ = 0x0000000100103870 {
110  std::__1::__tree_node_base<void *> = {
111  std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
112  __left_ = 0x0000000000000000
113  }
114  __right_ = 0x0000000000000000
115  __parent_ = 0x00000001001038b0
116  __is_black_ = true
117  }
118  __value_ = {
119  first = 0
120  second = { std::string }
121  */
122 
123 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
124     LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
125     : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
126   if (valobj_sp)
127     Update();
128 }
129 
130 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
131   m_pair_sp.reset();
132   m_pair_ptr = nullptr;
133 
134   ValueObjectSP valobj_sp = m_backend.GetSP();
135   if (!valobj_sp)
136     return false;
137 
138   TargetSP target_sp(valobj_sp->GetTargetSP());
139 
140   if (!target_sp)
141     return false;
142 
143   if (!valobj_sp)
144     return false;
145 
146   static ConstString g___i_("__i_");
147 
148   // this must be a ValueObject* because it is a child of the ValueObject we
149   // are producing children for it if were a ValueObjectSP, we would end up
150   // with a loop (iterator -> synthetic -> child -> parent == iterator) and
151   // that would in turn leak memory by never allowing the ValueObjects to die
152   // and free their memory
153   m_pair_ptr = valobj_sp
154                    ->GetValueForExpressionPath(
155                        ".__i_.__ptr_->__value_", nullptr, nullptr,
156                        ValueObject::GetValueForExpressionPathOptions()
157                            .DontCheckDotVsArrowSyntax()
158                            .SetSyntheticChildrenTraversal(
159                                ValueObject::GetValueForExpressionPathOptions::
160                                    SyntheticChildrenTraversal::None),
161                        nullptr)
162                    .get();
163 
164   if (!m_pair_ptr) {
165     m_pair_ptr = valobj_sp
166                      ->GetValueForExpressionPath(
167                          ".__i_.__ptr_", nullptr, nullptr,
168                          ValueObject::GetValueForExpressionPathOptions()
169                              .DontCheckDotVsArrowSyntax()
170                              .SetSyntheticChildrenTraversal(
171                                  ValueObject::GetValueForExpressionPathOptions::
172                                      SyntheticChildrenTraversal::None),
173                          nullptr)
174                      .get();
175     if (m_pair_ptr) {
176       auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true));
177       if (!__i_) {
178         m_pair_ptr = nullptr;
179         return false;
180       }
181       CompilerType pair_type(__i_->GetCompilerType().GetTypeTemplateArgument(0));
182       std::string name; uint64_t bit_offset_ptr; uint32_t bitfield_bit_size_ptr; bool is_bitfield_ptr;
183       pair_type = pair_type.GetFieldAtIndex(0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
184       if (!pair_type) {
185         m_pair_ptr = nullptr;
186         return false;
187       }
188 
189       auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
190       m_pair_ptr = nullptr;
191       if (addr && addr!=LLDB_INVALID_ADDRESS) {
192         ClangASTContext *ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(pair_type.GetTypeSystem());
193         if (!ast_ctx)
194           return false;
195         CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(ConstString(), {
196           {"ptr0",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
197           {"ptr1",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
198           {"ptr2",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
199           {"cw",ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
200           {"payload",pair_type}
201         });
202         DataBufferSP buffer_sp(new DataBufferHeap(tree_node_type.GetByteSize(nullptr),0));
203         ProcessSP process_sp(target_sp->GetProcessSP());
204         Status error;
205         process_sp->ReadMemory(addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), error);
206         if (error.Fail())
207           return false;
208         DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize());
209         auto pair_sp = CreateValueObjectFromData("pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type);
210         if (pair_sp)
211           m_pair_sp = pair_sp->GetChildAtIndex(4,true);
212       }
213     }
214   }
215 
216   return false;
217 }
218 
219 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
220     CalculateNumChildren() {
221   return 2;
222 }
223 
224 lldb::ValueObjectSP
225 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
226     size_t idx) {
227   if (m_pair_ptr)
228     return m_pair_ptr->GetChildAtIndex(idx, true);
229   if (m_pair_sp)
230     return m_pair_sp->GetChildAtIndex(idx, true);
231   return lldb::ValueObjectSP();
232 }
233 
234 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
235     MightHaveChildren() {
236   return true;
237 }
238 
239 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
240     GetIndexOfChildWithName(const ConstString &name) {
241   if (name == ConstString("first"))
242     return 0;
243   if (name == ConstString("second"))
244     return 1;
245   return UINT32_MAX;
246 }
247 
248 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
249     ~LibCxxMapIteratorSyntheticFrontEnd() {
250   // this will be deleted when its parent dies (since it's a child object)
251   // delete m_pair_ptr;
252 }
253 
254 SyntheticChildrenFrontEnd *
255 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
256     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
257   return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
258                     : nullptr);
259 }
260 
261 /*
262  (lldb) fr var ibeg --raw --ptr-depth 1 -T
263  (std::__1::__wrap_iter<int *>) ibeg = {
264  (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
265  (int) *__i = 1
266  }
267  }
268 */
269 
270 SyntheticChildrenFrontEnd *
271 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
272     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
273   static ConstString g_item_name;
274   if (!g_item_name)
275     g_item_name.SetCString("__i");
276   return (valobj_sp
277               ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
278               : nullptr);
279 }
280 
281 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
282     LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
283     : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(),
284       m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) {
285   if (valobj_sp)
286     Update();
287 }
288 
289 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
290     CalculateNumChildren() {
291   return (m_cntrl ? 1 : 0);
292 }
293 
294 lldb::ValueObjectSP
295 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
296     size_t idx) {
297   if (!m_cntrl)
298     return lldb::ValueObjectSP();
299 
300   ValueObjectSP valobj_sp = m_backend.GetSP();
301   if (!valobj_sp)
302     return lldb::ValueObjectSP();
303 
304   if (idx == 0)
305     return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true);
306 
307   if (idx > 2)
308     return lldb::ValueObjectSP();
309 
310   if (idx == 1) {
311     if (!m_count_sp) {
312       ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName(
313           ConstString("__shared_owners_"), true));
314       if (!shared_owners_sp)
315         return lldb::ValueObjectSP();
316       uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0);
317       DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
318       m_count_sp = CreateValueObjectFromData(
319           "count", data, valobj_sp->GetExecutionContextRef(),
320           shared_owners_sp->GetCompilerType());
321     }
322     return m_count_sp;
323   } else /* if (idx == 2) */
324   {
325     if (!m_weak_count_sp) {
326       ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName(
327           ConstString("__shared_weak_owners_"), true));
328       if (!shared_weak_owners_sp)
329         return lldb::ValueObjectSP();
330       uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0);
331       DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
332       m_weak_count_sp = CreateValueObjectFromData(
333           "count", data, valobj_sp->GetExecutionContextRef(),
334           shared_weak_owners_sp->GetCompilerType());
335     }
336     return m_weak_count_sp;
337   }
338 }
339 
340 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
341   m_count_sp.reset();
342   m_weak_count_sp.reset();
343   m_cntrl = nullptr;
344 
345   ValueObjectSP valobj_sp = m_backend.GetSP();
346   if (!valobj_sp)
347     return false;
348 
349   TargetSP target_sp(valobj_sp->GetTargetSP());
350   if (!target_sp)
351     return false;
352 
353   m_byte_order = target_sp->GetArchitecture().GetByteOrder();
354   m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize();
355 
356   lldb::ValueObjectSP cntrl_sp(
357       valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true));
358 
359   m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
360                             // dependency
361   return false;
362 }
363 
364 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
365     MightHaveChildren() {
366   return true;
367 }
368 
369 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
370     GetIndexOfChildWithName(const ConstString &name) {
371   if (name == ConstString("__ptr_"))
372     return 0;
373   if (name == ConstString("count"))
374     return 1;
375   if (name == ConstString("weak_count"))
376     return 2;
377   return UINT32_MAX;
378 }
379 
380 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
381     ~LibcxxSharedPtrSyntheticFrontEnd() = default;
382 
383 SyntheticChildrenFrontEnd *
384 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
385     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
386   return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
387                     : nullptr);
388 }
389 
390 bool lldb_private::formatters::LibcxxContainerSummaryProvider(
391     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
392   if (valobj.IsPointerType()) {
393     uint64_t value = valobj.GetValueAsUnsigned(0);
394     if (!value)
395       return false;
396     stream.Printf("0x%016" PRIx64 " ", value);
397   }
398   return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
399                                        nullptr, nullptr, &valobj, false, false);
400 }
401 
402 // the field layout in a libc++ string (cap, side, data or data, size, cap)
403 enum LibcxxStringLayoutMode {
404   eLibcxxStringLayoutModeCSD = 0,
405   eLibcxxStringLayoutModeDSC = 1,
406   eLibcxxStringLayoutModeInvalid = 0xffff
407 };
408 
409 // this function abstracts away the layout and mode details of a libc++ string
410 // and returns the address of the data and the size ready for callers to
411 // consume
412 static bool ExtractLibcxxStringInfo(ValueObject &valobj,
413                                     ValueObjectSP &location_sp,
414                                     uint64_t &size) {
415   ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0}));
416   if (!D)
417     return false;
418 
419   ValueObjectSP layout_decider(
420     D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0})));
421 
422   // this child should exist
423   if (!layout_decider)
424     return false;
425 
426   ConstString g_data_name("__data_");
427   ConstString g_size_name("__size_");
428   bool short_mode = false; // this means the string is in short-mode and the
429                            // data is stored inline
430   LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name)
431                                       ? eLibcxxStringLayoutModeDSC
432                                       : eLibcxxStringLayoutModeCSD;
433   uint64_t size_mode_value = 0;
434 
435   if (layout == eLibcxxStringLayoutModeDSC) {
436     ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0}));
437     if (!size_mode)
438       return false;
439 
440     if (size_mode->GetName() != g_size_name) {
441       // we are hitting the padding structure, move along
442       size_mode = D->GetChildAtIndexPath({1, 1, 1});
443       if (!size_mode)
444         return false;
445     }
446 
447     size_mode_value = (size_mode->GetValueAsUnsigned(0));
448     short_mode = ((size_mode_value & 0x80) == 0);
449   } else {
450     ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0}));
451     if (!size_mode)
452       return false;
453 
454     size_mode_value = (size_mode->GetValueAsUnsigned(0));
455     short_mode = ((size_mode_value & 1) == 0);
456   }
457 
458   if (short_mode) {
459     ValueObjectSP s(D->GetChildAtIndex(1, true));
460     if (!s)
461       return false;
462     location_sp = s->GetChildAtIndex(
463         (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true);
464     size = (layout == eLibcxxStringLayoutModeDSC)
465                ? size_mode_value
466                : ((size_mode_value >> 1) % 256);
467     return (location_sp.get() != nullptr);
468   } else {
469     ValueObjectSP l(D->GetChildAtIndex(0, true));
470     if (!l)
471       return false;
472     // we can use the layout_decider object as the data pointer
473     location_sp = (layout == eLibcxxStringLayoutModeDSC)
474                       ? layout_decider
475                       : l->GetChildAtIndex(2, true);
476     ValueObjectSP size_vo(l->GetChildAtIndex(1, true));
477     if (!size_vo || !location_sp)
478       return false;
479     size = size_vo->GetValueAsUnsigned(0);
480     return true;
481   }
482 }
483 
484 bool lldb_private::formatters::LibcxxWStringSummaryProvider(
485     ValueObject &valobj, Stream &stream,
486     const TypeSummaryOptions &summary_options) {
487   uint64_t size = 0;
488   ValueObjectSP location_sp;
489   if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
490     return false;
491   if (size == 0) {
492     stream.Printf("L\"\"");
493     return true;
494   }
495   if (!location_sp)
496     return false;
497 
498   DataExtractor extractor;
499 
500   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
501 
502   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
503     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
504     if (size > max_size) {
505       size = max_size;
506       options.SetIsTruncated(true);
507     }
508   }
509   location_sp->GetPointeeData(extractor, 0, size);
510 
511   // std::wstring::size() is measured in 'characters', not bytes
512   auto wchar_t_size = valobj.GetTargetSP()
513                           ->GetScratchClangASTContext()
514                           ->GetBasicType(lldb::eBasicTypeWChar)
515                           .GetByteSize(nullptr);
516 
517   options.SetData(extractor);
518   options.SetStream(&stream);
519   options.SetPrefixToken("L");
520   options.SetQuote('"');
521   options.SetSourceSize(size);
522   options.SetBinaryZeroIsTerminator(false);
523 
524   switch (wchar_t_size) {
525   case 1:
526     StringPrinter::ReadBufferAndDumpToStream<
527         lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
528         options);
529     break;
530 
531   case 2:
532     lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
533         lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
534         options);
535     break;
536 
537   case 4:
538     lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
539         lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
540         options);
541     break;
542 
543   default:
544     stream.Printf("size for wchar_t is not valid");
545     return true;
546   }
547 
548   return true;
549 }
550 
551 bool lldb_private::formatters::LibcxxStringSummaryProvider(
552     ValueObject &valobj, Stream &stream,
553     const TypeSummaryOptions &summary_options) {
554   uint64_t size = 0;
555   ValueObjectSP location_sp;
556 
557   if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
558     return false;
559 
560   if (size == 0) {
561     stream.Printf("\"\"");
562     return true;
563   }
564 
565   if (!location_sp)
566     return false;
567 
568   StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
569 
570   DataExtractor extractor;
571   if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
572     const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
573     if (size > max_size) {
574       size = max_size;
575       options.SetIsTruncated(true);
576     }
577   }
578   location_sp->GetPointeeData(extractor, 0, size);
579 
580   options.SetData(extractor);
581   options.SetStream(&stream);
582   options.SetPrefixToken(nullptr);
583   options.SetQuote('"');
584   options.SetSourceSize(size);
585   options.SetBinaryZeroIsTerminator(false);
586   StringPrinter::ReadBufferAndDumpToStream<
587       StringPrinter::StringElementType::ASCII>(options);
588 
589   return true;
590 }
591 
592 class LibcxxFunctionFrontEnd : public SyntheticValueProviderFrontEnd {
593 public:
594   LibcxxFunctionFrontEnd(ValueObject &backend)
595       : SyntheticValueProviderFrontEnd(backend) {}
596 
597   lldb::ValueObjectSP GetSyntheticValue() override {
598     static ConstString g___f_("__f_");
599     return m_backend.GetChildMemberWithName(g___f_, true);
600   }
601 };
602 
603 SyntheticChildrenFrontEnd *
604 lldb_private::formatters::LibcxxFunctionFrontEndCreator(
605     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
606   if (valobj_sp)
607     return new LibcxxFunctionFrontEnd(*valobj_sp);
608   return nullptr;
609 }
610