1 //===-- ValueObjectPrinter.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 "lldb/DataFormatters/ValueObjectPrinter.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/DataFormatters/DataVisualization.h"
18 #include "lldb/Interpreter/CommandInterpreter.h"
19 #include "lldb/Target/Target.h"
20 
21 using namespace lldb;
22 using namespace lldb_private;
23 
24 DumpValueObjectOptions::DumpValueObjectOptions (ValueObject& valobj) :
25 DumpValueObjectOptions()
26 {
27     m_use_dynamic = valobj.GetDynamicValueType();
28     m_use_synthetic = valobj.IsSynthetic();
29 }
30 
31 ValueObjectPrinter::ValueObjectPrinter (ValueObject* valobj,
32                                         Stream* s)
33 {
34     if (valobj)
35     {
36         DumpValueObjectOptions options(*valobj);
37         Init (valobj,s,options,options.m_max_ptr_depth,0);
38     }
39     else
40     {
41         DumpValueObjectOptions options;
42         Init (valobj,s,options,options.m_max_ptr_depth,0);
43     }
44 }
45 
46 ValueObjectPrinter::ValueObjectPrinter (ValueObject* valobj,
47                                         Stream* s,
48                                         const DumpValueObjectOptions& options)
49 {
50     Init(valobj,s,options,options.m_max_ptr_depth,0);
51 }
52 
53 ValueObjectPrinter::ValueObjectPrinter (ValueObject* valobj,
54                                         Stream* s,
55                                         const DumpValueObjectOptions& options,
56                                         const DumpValueObjectOptions::PointerDepth& ptr_depth,
57                                         uint32_t curr_depth)
58 {
59     Init(valobj,s,options,ptr_depth,curr_depth);
60 }
61 
62 void
63 ValueObjectPrinter::Init (ValueObject* valobj,
64                           Stream* s,
65                           const DumpValueObjectOptions& options,
66                           const DumpValueObjectOptions::PointerDepth& ptr_depth,
67                           uint32_t curr_depth)
68 {
69     m_orig_valobj = valobj;
70     m_valobj = nullptr;
71     m_stream = s;
72     this->options = options;
73     m_ptr_depth = ptr_depth;
74     m_curr_depth = curr_depth;
75     assert (m_orig_valobj && "cannot print a NULL ValueObject");
76     assert (m_stream && "cannot print to a NULL Stream");
77     m_should_print = eLazyBoolCalculate;
78     m_is_nil = eLazyBoolCalculate;
79     m_is_ptr = eLazyBoolCalculate;
80     m_is_ref = eLazyBoolCalculate;
81     m_is_aggregate = eLazyBoolCalculate;
82     m_summary_formatter = {nullptr,false};
83     m_value.assign("");
84     m_summary.assign("");
85     m_error.assign("");
86 }
87 
88 bool
89 ValueObjectPrinter::PrintValueObject ()
90 {
91     if (!GetMostSpecializedValue () || m_valobj == nullptr)
92         return false;
93 
94     if (ShouldPrintValueObject())
95     {
96         PrintValidationMarkerIfNeeded();
97 
98         PrintLocationIfNeeded();
99         m_stream->Indent();
100 
101         bool show_type = PrintTypeIfNeeded();
102 
103         PrintNameIfNeeded(show_type);
104     }
105 
106     bool value_printed = false;
107     bool summary_printed = false;
108 
109     bool val_summary_ok = PrintValueAndSummaryIfNeeded (value_printed,summary_printed);
110 
111     if (val_summary_ok)
112         PrintChildrenIfNeeded (value_printed, summary_printed);
113     else
114         m_stream->EOL();
115 
116     PrintValidationErrorIfNeeded();
117 
118     return true;
119 }
120 
121 bool
122 ValueObjectPrinter::GetMostSpecializedValue ()
123 {
124     if (m_valobj)
125         return true;
126     bool update_success = m_orig_valobj->UpdateValueIfNeeded (true);
127     if (!update_success)
128     {
129         m_valobj = m_orig_valobj;
130     }
131     else
132     {
133         if (m_orig_valobj->IsDynamic())
134         {
135             if (options.m_use_dynamic == eNoDynamicValues)
136             {
137                 ValueObject *static_value = m_orig_valobj->GetStaticValue().get();
138                 if (static_value)
139                     m_valobj = static_value;
140                 else
141                     m_valobj = m_orig_valobj;
142             }
143             else
144                 m_valobj = m_orig_valobj;
145         }
146         else
147         {
148             if (options.m_use_dynamic != eNoDynamicValues)
149             {
150                 ValueObject *dynamic_value = m_orig_valobj->GetDynamicValue(options.m_use_dynamic).get();
151                 if (dynamic_value)
152                     m_valobj = dynamic_value;
153                 else
154                     m_valobj = m_orig_valobj;
155             }
156             else
157                 m_valobj = m_orig_valobj;
158         }
159 
160         if (m_valobj->IsSynthetic())
161         {
162             if (options.m_use_synthetic == false)
163             {
164                 ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
165                 if (non_synthetic)
166                     m_valobj = non_synthetic;
167             }
168         }
169         else
170         {
171             if (options.m_use_synthetic == true)
172             {
173                 ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
174                 if (synthetic)
175                     m_valobj = synthetic;
176             }
177         }
178     }
179     m_clang_type = m_valobj->GetClangType();
180     m_type_flags = m_clang_type.GetTypeInfo ();
181     return true;
182 }
183 
184 const char*
185 ValueObjectPrinter::GetDescriptionForDisplay ()
186 {
187     const char* str = m_valobj->GetObjectDescription();
188     if (!str)
189         str = m_valobj->GetSummaryAsCString();
190     if (!str)
191         str = m_valobj->GetValueAsCString();
192     return str;
193 }
194 
195 const char*
196 ValueObjectPrinter::GetRootNameForDisplay (const char* if_fail)
197 {
198     const char *root_valobj_name = options.m_root_valobj_name.empty() ?
199         m_valobj->GetName().AsCString() :
200         options.m_root_valobj_name.c_str();
201     return root_valobj_name ? root_valobj_name : if_fail;
202 }
203 
204 bool
205 ValueObjectPrinter::ShouldPrintValueObject ()
206 {
207     if (m_should_print == eLazyBoolCalculate)
208         m_should_print = (options.m_flat_output == false || m_type_flags.Test (eTypeHasValue)) ? eLazyBoolYes : eLazyBoolNo;
209     return m_should_print == eLazyBoolYes;
210 }
211 
212 bool
213 ValueObjectPrinter::IsNil ()
214 {
215     if (m_is_nil == eLazyBoolCalculate)
216         m_is_nil = m_valobj->IsObjCNil() ? eLazyBoolYes : eLazyBoolNo;
217     return m_is_nil == eLazyBoolYes;
218 }
219 
220 bool
221 ValueObjectPrinter::IsPtr ()
222 {
223     if (m_is_ptr == eLazyBoolCalculate)
224         m_is_ptr = m_type_flags.Test (eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo;
225     return m_is_ptr == eLazyBoolYes;
226 }
227 
228 bool
229 ValueObjectPrinter::IsRef ()
230 {
231     if (m_is_ref == eLazyBoolCalculate)
232         m_is_ref = m_type_flags.Test (eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo;
233     return m_is_ref == eLazyBoolYes;
234 }
235 
236 bool
237 ValueObjectPrinter::IsAggregate ()
238 {
239     if (m_is_aggregate == eLazyBoolCalculate)
240         m_is_aggregate = m_type_flags.Test (eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo;
241     return m_is_aggregate == eLazyBoolYes;
242 }
243 
244 bool
245 ValueObjectPrinter::PrintLocationIfNeeded ()
246 {
247     if (options.m_show_location)
248     {
249         m_stream->Printf("%s: ", m_valobj->GetLocationAsCString());
250         return true;
251     }
252     return false;
253 }
254 
255 bool
256 ValueObjectPrinter::PrintTypeIfNeeded ()
257 {
258     bool show_type = true;
259     // if we are at the root-level and been asked to hide the root's type, then hide it
260     if (m_curr_depth == 0 && options.m_hide_root_type)
261         show_type = false;
262     else
263         // otherwise decide according to the usual rules (asked to show types - always at the root level)
264         show_type = options.m_show_types || (m_curr_depth == 0 && !options.m_flat_output);
265 
266     if (show_type)
267     {
268         // Some ValueObjects don't have types (like registers sets). Only print
269         // the type if there is one to print
270         ConstString type_name;
271         if (options.m_use_type_display_name)
272             type_name = m_valobj->GetDisplayTypeName();
273         else
274             type_name = m_valobj->GetQualifiedTypeName();
275         if (type_name)
276             m_stream->Printf("(%s) ", type_name.GetCString());
277         else
278             show_type = false;
279     }
280     return show_type;
281 }
282 
283 bool
284 ValueObjectPrinter::PrintNameIfNeeded (bool show_type)
285 {
286     if (options.m_flat_output)
287     {
288         // If we are showing types, also qualify the C++ base classes
289         const bool qualify_cxx_base_classes = show_type;
290         if (!options.m_hide_name)
291         {
292             m_valobj->GetExpressionPath(*m_stream, qualify_cxx_base_classes);
293             m_stream->PutCString(" =");
294             return true;
295         }
296     }
297     else if (!options.m_hide_name)
298     {
299         const char *name_cstr = GetRootNameForDisplay("");
300         m_stream->Printf ("%s =", name_cstr);
301         return true;
302     }
303     return false;
304 }
305 
306 bool
307 ValueObjectPrinter::CheckScopeIfNeeded ()
308 {
309     if (options.m_scope_already_checked)
310         return true;
311     return m_valobj->IsInScope();
312 }
313 
314 TypeSummaryImpl*
315 ValueObjectPrinter::GetSummaryFormatter ()
316 {
317     if (m_summary_formatter.second == false)
318     {
319         TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : m_valobj->GetSummaryFormat().get();
320 
321         if (options.m_omit_summary_depth > 0)
322             entry = NULL;
323         m_summary_formatter.first = entry;
324         m_summary_formatter.second = true;
325     }
326     return m_summary_formatter.first;
327 }
328 
329 void
330 ValueObjectPrinter::GetValueSummaryError (std::string& value,
331                                           std::string& summary,
332                                           std::string& error)
333 {
334     if (options.m_format != eFormatDefault && options.m_format != m_valobj->GetFormat())
335     {
336         m_valobj->GetValueAsCString(options.m_format,
337                                     value);
338     }
339     else
340     {
341         const char* val_cstr = m_valobj->GetValueAsCString();
342         if (val_cstr)
343             value.assign(val_cstr);
344     }
345     const char* err_cstr = m_valobj->GetError().AsCString();
346     if (err_cstr)
347         error.assign(err_cstr);
348 
349     if (ShouldPrintValueObject())
350     {
351         if (IsNil())
352             summary.assign("nil");
353         else if (options.m_omit_summary_depth == 0)
354         {
355             TypeSummaryImpl* entry = GetSummaryFormatter();
356             if (entry)
357                 m_valobj->GetSummaryAsCString(entry, summary);
358             else
359             {
360                 const char* sum_cstr = m_valobj->GetSummaryAsCString();
361                 if (sum_cstr)
362                     summary.assign(sum_cstr);
363             }
364         }
365     }
366 }
367 
368 bool
369 ValueObjectPrinter::PrintValueAndSummaryIfNeeded (bool& value_printed,
370                                                   bool& summary_printed)
371 {
372     bool error_printed = false;
373     if (ShouldPrintValueObject())
374     {
375         if (!CheckScopeIfNeeded())
376             m_error.assign("out of scope");
377         if (m_error.empty())
378         {
379             GetValueSummaryError(m_value, m_summary, m_error);
380         }
381         if (m_error.size())
382         {
383             error_printed = true;
384             m_stream->Printf (" <%s>\n", m_error.c_str());
385         }
386         else
387         {
388             // Make sure we have a value and make sure the summary didn't
389             // specify that the value should not be printed - and do not print
390             // the value if this thing is nil
391             // (but show the value if the user passes a format explicitly)
392             TypeSummaryImpl* entry = GetSummaryFormatter();
393             if (!IsNil() && !m_value.empty() && (entry == NULL || (entry->DoesPrintValue(m_valobj) || options.m_format != eFormatDefault) || m_summary.empty()) && !options.m_hide_value)
394             {
395                 m_stream->Printf(" %s", m_value.c_str());
396                 value_printed = true;
397             }
398 
399             if (m_summary.size())
400             {
401                 m_stream->Printf(" %s", m_summary.c_str());
402                 summary_printed = true;
403             }
404         }
405     }
406     return !error_printed;
407 }
408 
409 bool
410 ValueObjectPrinter::PrintObjectDescriptionIfNeeded (bool value_printed,
411                                                     bool summary_printed)
412 {
413     if (ShouldPrintValueObject())
414     {
415         // let's avoid the overly verbose no description error for a nil thing
416         if (options.m_use_objc && !IsNil())
417         {
418             if (!options.m_hide_value || !options.m_hide_name)
419                 m_stream->Printf(" ");
420             const char *object_desc = nullptr;
421             if (value_printed || summary_printed)
422                 object_desc = m_valobj->GetObjectDescription();
423             else
424                 object_desc = GetDescriptionForDisplay();
425             if (object_desc && *object_desc)
426             {
427                 m_stream->Printf("%s\n", object_desc);
428                 return true;
429             }
430             else if (value_printed == false && summary_printed == false)
431                 return true;
432             else
433                 return false;
434         }
435     }
436     return true;
437 }
438 
439 bool
440 DumpValueObjectOptions::PointerDepth::CanAllowExpansion (bool is_root,
441                                                          TypeSummaryImpl* entry,
442                                                          ValueObject *valobj,
443                                                          const std::string& summary)
444 {
445     switch (m_mode)
446     {
447         case Mode::Always:
448             return (m_count > 0);
449         case Mode::Never:
450             return false;
451         case Mode::Default:
452             if (is_root)
453                 m_count = std::min<decltype(m_count)>(m_count,1);
454             return m_count > 0;
455         case Mode::Formatters:
456             if (!entry || entry->DoesPrintChildren(valobj) || summary.empty())
457                 return m_count > 0;
458             return false;
459     }
460 }
461 
462 bool
463 DumpValueObjectOptions::PointerDepth::CanAllowExpansion () const
464 {
465     switch (m_mode)
466     {
467         case Mode::Always:
468         case Mode::Default:
469         case Mode::Formatters:
470             return (m_count > 0);
471         case Mode::Never:
472             return false;
473     }
474 }
475 
476 bool
477 ValueObjectPrinter::ShouldPrintChildren (bool is_failed_description,
478                                          DumpValueObjectOptions::PointerDepth& curr_ptr_depth)
479 {
480     const bool is_ref = IsRef ();
481     const bool is_ptr = IsPtr ();
482 
483     TypeSummaryImpl* entry = GetSummaryFormatter();
484 
485     if (is_failed_description || m_curr_depth < options.m_max_depth)
486     {
487         // We will show children for all concrete types. We won't show
488         // pointer contents unless a pointer depth has been specified.
489         // We won't reference contents unless the reference is the
490         // root object (depth of zero).
491 
492         // Use a new temporary pointer depth in case we override the
493         // current pointer depth below...
494 
495         if (is_ptr || is_ref)
496         {
497             // We have a pointer or reference whose value is an address.
498             // Make sure that address is not NULL
499             AddressType ptr_address_type;
500             if (m_valobj->GetPointerValue (&ptr_address_type) == 0)
501                 return false;
502 
503             const bool is_root_level = m_curr_depth == 0;
504 
505             if (is_ref &&
506                 is_root_level)
507             {
508                 // If this is the root object (depth is zero) that we are showing
509                 // and it is a reference, and no pointer depth has been supplied
510                 // print out what it references. Don't do this at deeper depths
511                 // otherwise we can end up with infinite recursion...
512                 return true;
513             }
514 
515             return curr_ptr_depth.CanAllowExpansion(false, entry, m_valobj, m_summary);
516         }
517 
518         return (!entry || entry->DoesPrintChildren(m_valobj) || m_summary.empty());
519     }
520     return false;
521 }
522 
523 bool
524 ValueObjectPrinter::ShouldExpandEmptyAggregates ()
525 {
526     TypeSummaryImpl* entry = GetSummaryFormatter();
527 
528     if (!entry)
529         return true;
530 
531     return entry->DoesPrintEmptyAggregates();
532 }
533 
534 ValueObject*
535 ValueObjectPrinter::GetValueObjectForChildrenGeneration ()
536 {
537     return m_valobj;
538 }
539 
540 void
541 ValueObjectPrinter::PrintChildrenPreamble ()
542 {
543     if (options.m_flat_output)
544     {
545         if (ShouldPrintValueObject())
546             m_stream->EOL();
547     }
548     else
549     {
550         if (ShouldPrintValueObject())
551             m_stream->PutCString(IsRef () ? ": {\n" : " {\n");
552         m_stream->IndentMore();
553     }
554 }
555 
556 void
557 ValueObjectPrinter::PrintChild (ValueObjectSP child_sp,
558                                 const DumpValueObjectOptions::PointerDepth& curr_ptr_depth)
559 {
560     DumpValueObjectOptions child_options(options);
561     child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName();
562     child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value)
563     .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0);
564     if (child_sp.get())
565     {
566         ValueObjectPrinter child_printer(child_sp.get(),
567                                          m_stream,
568                                          child_options,
569                                          (IsPtr() || IsRef()) ? --curr_ptr_depth : curr_ptr_depth,
570                                          m_curr_depth + 1);
571         child_printer.PrintValueObject();
572     }
573 }
574 
575 uint32_t
576 ValueObjectPrinter::GetMaxNumChildrenToPrint (bool& print_dotdotdot)
577 {
578     ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration();
579 
580     size_t num_children = synth_m_valobj->GetNumChildren();
581     print_dotdotdot = false;
582     if (num_children)
583     {
584         const size_t max_num_children = m_valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
585 
586         if (num_children > max_num_children && !options.m_ignore_cap)
587         {
588             print_dotdotdot = true;
589             return max_num_children;
590         }
591     }
592     return num_children;
593 }
594 
595 void
596 ValueObjectPrinter::PrintChildrenPostamble (bool print_dotdotdot)
597 {
598     if (!options.m_flat_output)
599     {
600         if (print_dotdotdot)
601         {
602             m_valobj->GetTargetSP()->GetDebugger().GetCommandInterpreter().ChildrenTruncated();
603             m_stream->Indent("...\n");
604         }
605         m_stream->IndentLess();
606         m_stream->Indent("}\n");
607     }
608 }
609 
610 void
611 ValueObjectPrinter::PrintChildren (bool value_printed,
612                                    bool summary_printed,
613                                    const DumpValueObjectOptions::PointerDepth& curr_ptr_depth)
614 {
615     ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration();
616 
617     bool print_dotdotdot = false;
618     size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
619     if (num_children)
620     {
621         PrintChildrenPreamble ();
622 
623         for (size_t idx=0; idx<num_children; ++idx)
624         {
625             ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
626             PrintChild (child_sp, curr_ptr_depth);
627         }
628 
629         PrintChildrenPostamble (print_dotdotdot);
630     }
631     else if (IsAggregate())
632     {
633         // Aggregate, no children...
634         if (ShouldPrintValueObject())
635         {
636             // if it has a synthetic value, then don't print {}, the synthetic children are probably only being used to vend a value
637             if (m_valobj->DoesProvideSyntheticValue() || !ShouldExpandEmptyAggregates())
638                 m_stream->PutCString( "\n");
639             else
640                 m_stream->PutCString(" {}\n");
641         }
642     }
643     else
644     {
645         if (ShouldPrintValueObject())
646             m_stream->EOL();
647     }
648 }
649 
650 bool
651 ValueObjectPrinter::PrintChildrenOneLiner (bool hide_names)
652 {
653     if (!GetMostSpecializedValue () || m_valobj == nullptr)
654         return false;
655 
656     ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration();
657 
658     bool print_dotdotdot = false;
659     size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
660 
661     if (num_children)
662     {
663         m_stream->PutChar('(');
664 
665         for (uint32_t idx=0; idx<num_children; ++idx)
666         {
667             lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
668             if (child_sp)
669                 child_sp = child_sp->GetQualifiedRepresentationIfAvailable(options.m_use_dynamic, options.m_use_synthetic);
670             if (child_sp)
671             {
672                 if (idx)
673                     m_stream->PutCString(", ");
674                 if (!hide_names)
675                 {
676                     const char* name = child_sp.get()->GetName().AsCString();
677                     if (name && *name)
678                     {
679                         m_stream->PutCString(name);
680                         m_stream->PutCString(" = ");
681                     }
682                 }
683                 child_sp->DumpPrintableRepresentation(*m_stream,
684                                                       ValueObject::eValueObjectRepresentationStyleSummary,
685                                                       lldb::eFormatInvalid,
686                                                       ValueObject::ePrintableRepresentationSpecialCasesDisable);
687             }
688         }
689 
690         if (print_dotdotdot)
691             m_stream->PutCString(", ...)");
692         else
693             m_stream->PutChar(')');
694     }
695     return true;
696 }
697 
698 void
699 ValueObjectPrinter::PrintChildrenIfNeeded (bool value_printed,
700                                            bool summary_printed)
701 {
702     // this flag controls whether we tried to display a description for this object and failed
703     // if that happens, we want to display the children, if any
704     bool is_failed_description = !PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
705 
706     auto curr_ptr_depth = m_ptr_depth;
707     bool print_children = ShouldPrintChildren (is_failed_description,curr_ptr_depth);
708     bool print_oneline = (curr_ptr_depth.CanAllowExpansion() ||
709                           options.m_show_types ||
710                           !options.m_allow_oneliner_mode ||
711                           options.m_flat_output ||
712                           options.m_show_location) ? false : DataVisualization::ShouldPrintAsOneLiner(*m_valobj);
713 
714     if (print_children)
715     {
716         if (print_oneline)
717         {
718             m_stream->PutChar(' ');
719             PrintChildrenOneLiner (false);
720             m_stream->EOL();
721         }
722         else
723             PrintChildren (value_printed, summary_printed, curr_ptr_depth);
724     }
725     else if (m_curr_depth >= options.m_max_depth && IsAggregate() && ShouldPrintValueObject())
726     {
727             m_stream->PutCString("{...}\n");
728     }
729     else
730         m_stream->EOL();
731 }
732 
733 bool
734 ValueObjectPrinter::ShouldPrintValidation ()
735 {
736     return options.m_run_validator;
737 }
738 
739 bool
740 ValueObjectPrinter::PrintValidationMarkerIfNeeded ()
741 {
742     if (!ShouldPrintValidation())
743         return false;
744 
745     m_validation = m_valobj->GetValidationStatus();
746 
747     if (TypeValidatorResult::Failure == m_validation.first)
748     {
749         m_stream->Printf("! ");
750         return true;
751     }
752 
753     return false;
754 }
755 
756 bool
757 ValueObjectPrinter::PrintValidationErrorIfNeeded ()
758 {
759     if (!ShouldPrintValidation())
760         return false;
761 
762     if (TypeValidatorResult::Success == m_validation.first)
763         return false;
764 
765     if (m_validation.second.empty())
766         m_validation.second.assign("unknown error");
767 
768     m_stream->Printf(" ! validation error: %s", m_validation.second.c_str());
769     m_stream->EOL();
770 
771     return true;
772 }
773