1 //===-- OptionValueProperties.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Interpreter/OptionValueProperties.h"
10 
11 #include "lldb/Utility/Flags.h"
12 
13 #include "lldb/Core/UserSettingsController.h"
14 #include "lldb/Interpreter/OptionValues.h"
15 #include "lldb/Interpreter/Property.h"
16 #include "lldb/Utility/Args.h"
17 #include "lldb/Utility/Stream.h"
18 #include "lldb/Utility/StringList.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 OptionValueProperties::OptionValueProperties(ConstString name) : m_name(name) {}
24 
25 size_t OptionValueProperties::GetNumProperties() const {
26   return m_properties.size();
27 }
28 
29 void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {
30   for (const auto &definition : defs) {
31     Property property(definition);
32     assert(property.IsValid());
33     m_name_to_index.Append(ConstString(property.GetName()), m_properties.size());
34     property.GetValue()->SetParent(shared_from_this());
35     m_properties.push_back(property);
36   }
37   m_name_to_index.Sort();
38 }
39 
40 void OptionValueProperties::SetValueChangedCallback(
41     uint32_t property_idx, std::function<void()> callback) {
42   Property *property = ProtectedGetPropertyAtIndex(property_idx);
43   if (property)
44     property->SetValueChangedCallback(std::move(callback));
45 }
46 
47 void OptionValueProperties::AppendProperty(ConstString name,
48                                            ConstString desc,
49                                            bool is_global,
50                                            const OptionValueSP &value_sp) {
51   Property property(name.GetStringRef(), desc.GetStringRef(), is_global,
52                     value_sp);
53   m_name_to_index.Append(name, m_properties.size());
54   m_properties.push_back(property);
55   value_sp->SetParent(shared_from_this());
56   m_name_to_index.Sort();
57 }
58 
59 // bool
60 // OptionValueProperties::GetQualifiedName (Stream &strm)
61 //{
62 //    bool dumped_something = false;
63 ////    lldb::OptionValuePropertiesSP parent_sp(GetParent ());
64 ////    if (parent_sp)
65 ////    {
66 ////        parent_sp->GetQualifiedName (strm);
67 ////        strm.PutChar('.');
68 ////        dumped_something = true;
69 ////    }
70 //    if (m_name)
71 //    {
72 //        strm << m_name;
73 //        dumped_something = true;
74 //    }
75 //    return dumped_something;
76 //}
77 //
78 lldb::OptionValueSP
79 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
80                                       ConstString key,
81                                       bool will_modify) const {
82   lldb::OptionValueSP value_sp;
83   size_t idx = m_name_to_index.Find(key, SIZE_MAX);
84   if (idx < m_properties.size())
85     value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue();
86   return value_sp;
87 }
88 
89 lldb::OptionValueSP
90 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
91                                    llvm::StringRef name, bool will_modify,
92                                    Status &error) const {
93   lldb::OptionValueSP value_sp;
94   if (name.empty())
95     return OptionValueSP();
96 
97   llvm::StringRef sub_name;
98   ConstString key;
99   size_t key_len = name.find_first_of(".[{");
100   if (key_len != llvm::StringRef::npos) {
101     key.SetString(name.take_front(key_len));
102     sub_name = name.drop_front(key_len);
103   } else
104     key.SetString(name);
105 
106   value_sp = GetValueForKey(exe_ctx, key, will_modify);
107   if (sub_name.empty() || !value_sp)
108     return value_sp;
109 
110   switch (sub_name[0]) {
111   case '.': {
112     lldb::OptionValueSP return_val_sp;
113     return_val_sp =
114         value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error);
115     if (!return_val_sp) {
116       if (Properties::IsSettingExperimental(sub_name.drop_front())) {
117         size_t experimental_len =
118             strlen(Properties::GetExperimentalSettingsName());
119         if (sub_name[experimental_len + 1] == '.')
120           return_val_sp = value_sp->GetSubValue(
121               exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error);
122         // It isn't an error if an experimental setting is not present.
123         if (!return_val_sp)
124           error.Clear();
125       }
126     }
127     return return_val_sp;
128   }
129   case '[':
130     // Array or dictionary access for subvalues like: "[12]"       -- access
131     // 12th array element "['hello']"  -- dictionary access of key named hello
132     return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
133 
134   default:
135     value_sp.reset();
136     break;
137   }
138   return value_sp;
139 }
140 
141 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
142                                           VarSetOperationType op,
143                                           llvm::StringRef name,
144                                           llvm::StringRef value) {
145   Status error;
146   const bool will_modify = true;
147   llvm::SmallVector<llvm::StringRef, 8> components;
148   name.split(components, '.');
149   bool name_contains_experimental = false;
150   for (const auto &part : components)
151     if (Properties::IsSettingExperimental(part))
152       name_contains_experimental = true;
153 
154   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
155   if (value_sp)
156     error = value_sp->SetValueFromString(value, op);
157   else {
158     // Don't set an error if the path contained .experimental. - those are
159     // allowed to be missing and should silently fail.
160     if (!name_contains_experimental && error.AsCString() == nullptr) {
161       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
162     }
163   }
164   return error;
165 }
166 
167 uint32_t
168 OptionValueProperties::GetPropertyIndex(ConstString name) const {
169   return m_name_to_index.Find(name, SIZE_MAX);
170 }
171 
172 const Property *
173 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
174                                    bool will_modify,
175                                    ConstString name) const {
176   return GetPropertyAtIndex(
177       exe_ctx, will_modify,
178       m_name_to_index.Find(name, SIZE_MAX));
179 }
180 
181 const Property *OptionValueProperties::GetPropertyAtIndex(
182     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
183   return ProtectedGetPropertyAtIndex(idx);
184 }
185 
186 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
187     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
188   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
189   if (setting)
190     return setting->GetValue();
191   return OptionValueSP();
192 }
193 
194 OptionValuePathMappings *
195 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
196     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
197   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
198   if (value_sp)
199     return value_sp->GetAsPathMappings();
200   return nullptr;
201 }
202 
203 OptionValueFileSpecList *
204 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
205     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
206   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
207   if (value_sp)
208     return value_sp->GetAsFileSpecList();
209   return nullptr;
210 }
211 
212 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
213     const ExecutionContext *exe_ctx, uint32_t idx) const {
214   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
215   if (property)
216     return property->GetValue()->GetAsArch();
217   return nullptr;
218 }
219 
220 OptionValueLanguage *
221 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
222     const ExecutionContext *exe_ctx, uint32_t idx) const {
223   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
224   if (property)
225     return property->GetValue()->GetAsLanguage();
226   return nullptr;
227 }
228 
229 bool OptionValueProperties::SetPropertyAtIndexAsLanguage(
230     const ExecutionContext *exe_ctx, uint32_t idx, const LanguageType lang) {
231   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
232   if (property) {
233     OptionValue *value = property->GetValue().get();
234     if (value)
235       return value->SetLanguageValue(lang);
236   }
237   return false;
238 }
239 
240 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
241     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
242   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
243   if (!property)
244     return false;
245 
246   OptionValue *value = property->GetValue().get();
247   if (!value)
248     return false;
249 
250   const OptionValueArgs *arguments = value->GetAsArgs();
251   if (arguments) {
252     arguments->GetArgs(args);
253     return true;
254   }
255 
256   const OptionValueArray *array = value->GetAsArray();
257   if (array) {
258     array->GetArgs(args);
259     return true;
260   }
261 
262   const OptionValueDictionary *dict = value->GetAsDictionary();
263   if (dict) {
264     dict->GetArgs(args);
265     return true;
266   }
267 
268   return false;
269 }
270 
271 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
272     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
273   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
274   if (!property)
275     return false;
276 
277   OptionValue *value = property->GetValue().get();
278   if (!value)
279     return false;
280 
281   OptionValueArgs *arguments = value->GetAsArgs();
282   if (arguments)
283     return arguments->SetArgs(args, eVarSetOperationAssign).Success();
284 
285   OptionValueArray *array = value->GetAsArray();
286   if (array)
287     return array->SetArgs(args, eVarSetOperationAssign).Success();
288 
289   OptionValueDictionary *dict = value->GetAsDictionary();
290   if (dict)
291     return dict->SetArgs(args, eVarSetOperationAssign).Success();
292 
293   return false;
294 }
295 
296 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
297     const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
298   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
299   if (property) {
300     OptionValue *value = property->GetValue().get();
301     if (value)
302       return value->GetBooleanValue(fail_value);
303   }
304   return fail_value;
305 }
306 
307 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
308     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
309   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
310   if (property) {
311     OptionValue *value = property->GetValue().get();
312     if (value) {
313       value->SetBooleanValue(new_value);
314       return true;
315     }
316   }
317   return false;
318 }
319 
320 OptionValueDictionary *
321 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
322     const ExecutionContext *exe_ctx, uint32_t idx) const {
323   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
324   if (property)
325     return property->GetValue()->GetAsDictionary();
326   return nullptr;
327 }
328 
329 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
330     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
331   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
332   if (property) {
333     OptionValue *value = property->GetValue().get();
334     if (value)
335       return value->GetEnumerationValue(fail_value);
336   }
337   return fail_value;
338 }
339 
340 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
341     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
342   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
343   if (property) {
344     OptionValue *value = property->GetValue().get();
345     if (value)
346       return value->SetEnumerationValue(new_value);
347   }
348   return false;
349 }
350 
351 const FormatEntity::Entry *
352 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
353     const ExecutionContext *exe_ctx, uint32_t idx) {
354   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
355   if (property) {
356     OptionValue *value = property->GetValue().get();
357     if (value)
358       return value->GetFormatEntity();
359   }
360   return nullptr;
361 }
362 
363 OptionValueFileSpec *
364 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
365     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
366   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
367   if (property) {
368     OptionValue *value = property->GetValue().get();
369     if (value)
370       return value->GetAsFileSpec();
371   }
372   return nullptr;
373 }
374 
375 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
376     const ExecutionContext *exe_ctx, uint32_t idx) const {
377   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
378   if (property) {
379     OptionValue *value = property->GetValue().get();
380     if (value)
381       return value->GetFileSpecValue();
382   }
383   return FileSpec();
384 }
385 
386 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
387     const ExecutionContext *exe_ctx, uint32_t idx,
388     const FileSpec &new_file_spec) {
389   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
390   if (property) {
391     OptionValue *value = property->GetValue().get();
392     if (value)
393       return value->SetFileSpecValue(new_file_spec);
394   }
395   return false;
396 }
397 
398 const RegularExpression *
399 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
400     const ExecutionContext *exe_ctx, uint32_t idx) const {
401   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
402   if (property) {
403     OptionValue *value = property->GetValue().get();
404     if (value)
405       return value->GetRegexValue();
406   }
407   return nullptr;
408 }
409 
410 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
411     const ExecutionContext *exe_ctx, uint32_t idx) const {
412   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
413   if (property) {
414     OptionValue *value = property->GetValue().get();
415     if (value)
416       return value->GetAsSInt64();
417   }
418   return nullptr;
419 }
420 
421 OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64(
422     const ExecutionContext *exe_ctx, uint32_t idx) const {
423   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
424   if (property) {
425     OptionValue *value = property->GetValue().get();
426     if (value)
427       return value->GetAsUInt64();
428   }
429   return nullptr;
430 }
431 
432 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
433     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
434   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
435   if (property) {
436     OptionValue *value = property->GetValue().get();
437     if (value)
438       return value->GetSInt64Value(fail_value);
439   }
440   return fail_value;
441 }
442 
443 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
444     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
445   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
446   if (property) {
447     OptionValue *value = property->GetValue().get();
448     if (value)
449       return value->SetSInt64Value(new_value);
450   }
451   return false;
452 }
453 
454 llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString(
455     const ExecutionContext *exe_ctx, uint32_t idx,
456     llvm::StringRef fail_value) const {
457   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
458   if (property) {
459     OptionValue *value = property->GetValue().get();
460     if (value)
461       return value->GetStringValue(fail_value);
462   }
463   return fail_value;
464 }
465 
466 bool OptionValueProperties::SetPropertyAtIndexAsString(
467     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
468   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
469   if (property) {
470     OptionValue *value = property->GetValue().get();
471     if (value)
472       return value->SetStringValue(new_value);
473   }
474   return false;
475 }
476 
477 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
478     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
479   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
480   if (value_sp)
481     return value_sp->GetAsString();
482   return nullptr;
483 }
484 
485 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
486     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
487   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
488   if (property) {
489     OptionValue *value = property->GetValue().get();
490     if (value)
491       return value->GetUInt64Value(fail_value);
492   }
493   return fail_value;
494 }
495 
496 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
497     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
498   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
499   if (property) {
500     OptionValue *value = property->GetValue().get();
501     if (value)
502       return value->SetUInt64Value(new_value);
503   }
504   return false;
505 }
506 
507 void OptionValueProperties::Clear() {
508   const size_t num_properties = m_properties.size();
509   for (size_t i = 0; i < num_properties; ++i)
510     m_properties[i].GetValue()->Clear();
511 }
512 
513 Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
514                                                  VarSetOperationType op) {
515   Status error;
516 
517   //    Args args(value_cstr);
518   //    const size_t argc = args.GetArgumentCount();
519   switch (op) {
520   case eVarSetOperationClear:
521     Clear();
522     break;
523 
524   case eVarSetOperationReplace:
525   case eVarSetOperationAssign:
526   case eVarSetOperationRemove:
527   case eVarSetOperationInsertBefore:
528   case eVarSetOperationInsertAfter:
529   case eVarSetOperationAppend:
530   case eVarSetOperationInvalid:
531     error = OptionValue::SetValueFromString(value, op);
532     break;
533   }
534 
535   return error;
536 }
537 
538 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
539                                       Stream &strm, uint32_t dump_mask) {
540   const size_t num_properties = m_properties.size();
541   for (size_t i = 0; i < num_properties; ++i) {
542     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
543     if (property) {
544       OptionValue *option_value = property->GetValue().get();
545       assert(option_value);
546       const bool transparent_value = option_value->ValueIsTransparent();
547       property->Dump(exe_ctx, strm, dump_mask);
548       if (!transparent_value)
549         strm.EOL();
550     }
551   }
552 }
553 
554 llvm::json::Value
555 OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) {
556   llvm::json::Object json_properties;
557   const size_t num_properties = m_properties.size();
558   for (size_t i = 0; i < num_properties; ++i) {
559     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
560     if (property) {
561       OptionValue *option_value = property->GetValue().get();
562       assert(option_value);
563       json_properties.try_emplace(property->GetName(),
564                                   option_value->ToJSON(exe_ctx));
565     }
566   }
567   return json_properties;
568 }
569 
570 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
571                                                 Stream &strm,
572                                                 llvm::StringRef property_path,
573                                                 uint32_t dump_mask, bool is_json) {
574   Status error;
575   const bool will_modify = false;
576   lldb::OptionValueSP value_sp(
577       GetSubValue(exe_ctx, property_path, will_modify, error));
578   if (value_sp) {
579     if (!value_sp->ValueIsTransparent()) {
580       if (dump_mask & eDumpOptionName)
581         strm.PutCString(property_path);
582       if (dump_mask & ~eDumpOptionName)
583         strm.PutChar(' ');
584     }
585     if (is_json) {
586       strm.Printf("%s", llvm::formatv("{0:2}", value_sp->ToJSON(exe_ctx)).str().c_str());
587     } else
588       value_sp->DumpValue(exe_ctx, strm, dump_mask);
589   }
590   return error;
591 }
592 
593 OptionValuePropertiesSP
594 OptionValueProperties::CreateLocalCopy(const Properties &global_properties) {
595   auto global_props_sp = global_properties.GetValueProperties();
596   lldbassert(global_props_sp);
597 
598   auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent());
599   return std::static_pointer_cast<OptionValueProperties>(copy_sp);
600 }
601 
602 OptionValueSP
603 OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const {
604   auto copy_sp = OptionValue::DeepCopy(new_parent);
605   // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived
606   // types that override GetType returning a different value.
607   auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get());
608   lldbassert(props_value_ptr);
609 
610   for (auto &property : props_value_ptr->m_properties) {
611     // Duplicate any values that are not global when constructing properties
612     // from a global copy.
613     if (!property.IsGlobal()) {
614       auto value_sp = property.GetValue()->DeepCopy(copy_sp);
615       property.SetOptionValue(value_sp);
616     }
617   }
618   return copy_sp;
619 }
620 
621 const Property *OptionValueProperties::GetPropertyAtPath(
622     const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const {
623   const Property *property = nullptr;
624   if (name.empty())
625     return nullptr;
626   llvm::StringRef sub_name;
627   ConstString key;
628   size_t key_len = name.find_first_of(".[{");
629 
630   if (key_len != llvm::StringRef::npos) {
631     key.SetString(name.take_front(key_len));
632     sub_name = name.drop_front(key_len);
633   } else
634     key.SetString(name);
635 
636   property = GetProperty(exe_ctx, will_modify, key);
637   if (sub_name.empty() || !property)
638     return property;
639 
640   if (sub_name[0] == '.') {
641     OptionValueProperties *sub_properties =
642         property->GetValue()->GetAsProperties();
643     if (sub_properties)
644       return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
645                                                 sub_name.drop_front());
646   }
647   return nullptr;
648 }
649 
650 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
651                                                 Stream &strm) const {
652   size_t max_name_len = 0;
653   const size_t num_properties = m_properties.size();
654   for (size_t i = 0; i < num_properties; ++i) {
655     const Property *property = ProtectedGetPropertyAtIndex(i);
656     if (property)
657       max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
658   }
659   for (size_t i = 0; i < num_properties; ++i) {
660     const Property *property = ProtectedGetPropertyAtIndex(i);
661     if (property)
662       property->DumpDescription(interpreter, strm, max_name_len, false);
663   }
664 }
665 
666 void OptionValueProperties::Apropos(
667     llvm::StringRef keyword,
668     std::vector<const Property *> &matching_properties) const {
669   const size_t num_properties = m_properties.size();
670   StreamString strm;
671   for (size_t i = 0; i < num_properties; ++i) {
672     const Property *property = ProtectedGetPropertyAtIndex(i);
673     if (property) {
674       const OptionValueProperties *properties =
675           property->GetValue()->GetAsProperties();
676       if (properties) {
677         properties->Apropos(keyword, matching_properties);
678       } else {
679         bool match = false;
680         llvm::StringRef name = property->GetName();
681         if (name.contains_insensitive(keyword))
682           match = true;
683         else {
684           llvm::StringRef desc = property->GetDescription();
685           if (desc.contains_insensitive(keyword))
686             match = true;
687         }
688         if (match) {
689           matching_properties.push_back(property);
690         }
691       }
692     }
693   }
694 }
695 
696 lldb::OptionValuePropertiesSP
697 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
698                                       ConstString name) {
699   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
700   if (option_value_sp) {
701     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
702     if (ov_properties)
703       return ov_properties->shared_from_this();
704   }
705   return lldb::OptionValuePropertiesSP();
706 }
707